]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/cmd_dbd_scanvol.c
On the hunt for fixing all warnings
[netatalk.git] / etc / cnid_dbd / cmd_dbd_scanvol.c
1 /*
2   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <setjmp.h>
28
29 #include <atalk/adouble.h>
30 #include <atalk/unicode.h>
31 #include <atalk/netatalk_conf.h>
32 #include <atalk/volume.h>
33 #include <atalk/ea.h>
34 #include <atalk/util.h>
35 #include <atalk/acl.h>
36 #include <atalk/compat.h>
37 #include <atalk/cnid.h>
38 #include <atalk/errchk.h>
39
40 #include "cmd_dbd.h"
41 #include "dbif.h"
42 #include "db_param.h"
43 #include "dbd.h"
44
45 /* Some defines to ease code parsing */
46 #define ADDIR_OK (addir_ok == 0)
47 #define ADFILE_OK (adfile_ok == 0)
48
49
50 static char           cwdbuf[MAXPATHLEN+1];
51 static struct vol     *vol;
52 static DBD            *dbd_rebuild;
53 static dbd_flags_t    dbd_flags;
54 static char           stamp[CNID_DEV_LEN];
55 static char           *netatalk_dirs[] = {
56     ".AppleDB",
57     ".AppleDesktop",
58     NULL
59 };
60 static char           *special_dirs[] = {
61     ".zfs",
62     NULL
63 };
64 static struct cnid_dbd_rqst rqst;
65 static struct cnid_dbd_rply rply;
66 static jmp_buf jmp;
67 static char pname[MAXPATHLEN] = "../";
68 static char cnidResBuf[12 + MAXPATHLEN + 1];
69
70 /*
71   Taken form afpd/desktop.c
72 */
73 static char *utompath(char *upath)
74 {
75     static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
76     char         *m, *u;
77     uint16_t     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
78     size_t       outlen;
79
80     if (!upath)
81         return NULL;
82
83     m = mpath;
84     u = upath;
85     outlen = strlen(upath);
86
87     if ((vol->v_casefold & AFPVOL_UTOMUPPER))
88         flags |= CONV_TOUPPER;
89     else if ((vol->v_casefold & AFPVOL_UTOMLOWER))
90         flags |= CONV_TOLOWER;
91
92     if ((vol->v_flags & AFPVOL_EILSEQ)) {
93         flags |= CONV__EILSEQ;
94     }
95
96     /* convert charsets */
97     if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset,
98                                                  CH_UTF8_MAC,
99                                                  vol->v_maccharset,
100                                                  u, outlen, mpath, MAXPATHLEN, &flags)) ) {
101         dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
102                  vol->v_volcodepage, vol->v_maccodepage, u);
103         return NULL;
104     }
105
106     return(m);
107 }
108
109 /*
110   Taken form afpd/desktop.c
111 */
112 static char *mtoupath(char *mpath)
113 {
114     static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
115     char    *m, *u;
116     size_t       inplen;
117     size_t       outlen;
118     u_int16_t    flags = 0;
119
120     if (!mpath)
121         return NULL;
122
123     if ( *mpath == '\0' ) {
124         return( "." );
125     }
126
127     /* set conversion flags */
128     if ((vol->v_casefold & AFPVOL_MTOUUPPER))
129         flags |= CONV_TOUPPER;
130     else if ((vol->v_casefold & AFPVOL_MTOULOWER))
131         flags |= CONV_TOLOWER;
132
133     if ((vol->v_flags & AFPVOL_EILSEQ)) {
134         flags |= CONV__EILSEQ;
135     }
136
137     m = mpath;
138     u = upath;
139
140     inplen = strlen(m);
141     outlen = MAXPATHLEN;
142
143     if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
144                                                 vol->v_volcharset,
145                                                 vol->v_maccharset,
146                                                 m, inplen, u, outlen, &flags)) ) {
147         dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
148                  vol->v_volcodepage, mpath);
149         return NULL;
150     }
151
152     return( upath );
153 }
154
155 /*
156   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
157   Returns pointer to name or NULL.
158 */
159 static const char *check_netatalk_dirs(const char *name)
160 {
161     int c;
162
163     for (c=0; netatalk_dirs[c]; c++) {
164         if ((strcmp(name, netatalk_dirs[c])) == 0)
165             return netatalk_dirs[c];
166     }
167     return NULL;
168 }
169
170 /*
171   Check for special names
172   Returns pointer to name or NULL.
173 */
174 static const char *check_special_dirs(const char *name)
175 {
176     int c;
177
178     for (c=0; special_dirs[c]; c++) {
179         if ((strcmp(name, special_dirs[c])) == 0)
180             return special_dirs[c];
181     }
182     return NULL;
183 }
184
185 /*
186  * We unCAPed a name, update CNID db
187  */
188 static int update_cnid(cnid_t did, const struct stat *sp, const char *oldname, const char *newname)
189 {
190     int ret;
191     cnid_t id;
192
193     /* Query the database */
194     if ((id = cnid_lookup(vol->v_cdb, sp, did, (char *)oldname, strlen(oldname))) == CNID_INVALID)
195         /* not in db, no need to update */
196         return 0;
197
198     /* Update the database */
199     if (cnid_update(vol->v_cdb, id, sp, did, (char *)newname, strlen(newname)) < 0)
200         return -1;
201
202     return 0;
203 }
204
205 /*
206   Check for .AppleDouble file, create if missing
207 */
208 static int check_adfile(const char *fname, const struct stat *st, const char **newname)
209 {
210     int ret;
211     int adflags = ADFLAGS_HF;
212     struct adouble ad;
213     const char *adname;
214
215     *newname = NULL;
216
217     if (vol->v_adouble == AD_VERSION_EA) {
218         if (!(dbd_flags & DBD_FLAGS_V2TOEA))
219             return 0;
220         if (ad_convert(fname, st, vol, newname) != 0) {
221             switch (errno) {
222             case ENOENT:
223                 break;
224             default:
225                 dbd_log(LOGSTD, "Conversion error for \"%s/%s\": %s", cwdbuf, fname, strerror(errno));
226                 break;
227             }
228         }
229         return 0;
230     }
231     
232     if (S_ISDIR(st->st_mode))
233         adflags |= ADFLAGS_DIR;
234
235     adname = vol->ad_path(fname, adflags);
236
237     if ((ret = access( adname, F_OK)) != 0) {
238         if (errno != ENOENT) {
239             dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
240                     cwdbuf, adname, strerror(errno));
241             return -1;
242         }
243         /* Missing. Log and create it */
244         dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
245
246         if (dbd_flags & DBD_FLAGS_SCAN)
247             /* Scan only requested, dont change anything */
248             return -1;
249
250         /* Create ad file */
251         ad_init(&ad, vol);
252
253         if ((ret = ad_open(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 0) {
254             dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
255                      cwdbuf, adname, strerror(errno));
256
257             return -1;
258         }
259
260         /* Set name in ad-file */
261         ad_setname(&ad, utompath((char *)fname));
262         ad_flush(&ad);
263         ad_close(&ad, ADFLAGS_HF);
264
265         chown(adname, st->st_uid, st->st_gid);
266         /* FIXME: should we inherit mode too here ? */
267 #if 0
268         chmod(adname, st->st_mode);
269 #endif
270     } else {
271         ad_init(&ad, vol);
272         if (ad_open(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) {
273             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
274             return -1;
275         }
276         ad_close(&ad, ADFLAGS_HF);
277     }
278     return 0;
279 }
280
281 /* 
282    Remove all files with file::EA* from adouble dir
283 */
284 static void remove_eafiles(const char *name, struct ea *ea)
285 {
286     DIR *dp = NULL;
287     struct dirent *ep;
288     char eaname[MAXPATHLEN];
289
290     strlcpy(eaname, name, sizeof(eaname));
291     if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
292         dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
293         return;
294     }
295
296     if ((chdir(ADv2_DIRNAME)) != 0) {
297         dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
298                 cwdbuf, ADv2_DIRNAME, strerror(errno));
299         return;
300     }
301
302     if ((dp = opendir(".")) == NULL) {
303         dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
304                 cwdbuf, ADv2_DIRNAME, strerror(errno));
305         goto exit;
306     }
307
308     while ((ep = readdir(dp))) {
309         if (strstr(ep->d_name, eaname) != NULL) {
310             dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
311                     cwdbuf, ADv2_DIRNAME, ep->d_name);
312             if ((unlink(ep->d_name)) != 0) {
313                 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
314                         cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
315             }
316         } /* if */
317     } /* while */
318
319 exit:
320     if (dp)
321         closedir(dp);
322     if ((chdir("..")) != 0) {
323         dbd_log(LOGSTD, "Couldn't chdir to '%s': %s", cwdbuf, strerror(errno));
324         /* we can't proceed */
325         longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
326     }    
327 }
328
329 /*
330   Check Extended Attributes files
331 */
332 static int check_eafiles(const char *fname)
333 {
334     unsigned int  count = 0;
335     int ret = 0, remove;
336     struct ea ea;
337     struct stat st;
338     char *eaname;
339
340     if ((ret = ea_open(vol, fname, EA_RDWR, &ea)) != 0) {
341         if (errno == ENOENT)
342             return 0;
343         dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
344         if ( ! (dbd_flags & DBD_FLAGS_SCAN))
345             remove_eafiles(fname, &ea);
346         return -1;
347     }
348
349     /* Check all EAs */
350     while (count < ea.ea_count) {        
351         dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
352         remove = 0;
353
354         eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
355
356         if (lstat(eaname, &st) != 0) {
357             if (errno == ENOENT)
358                 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
359             else
360                 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
361             remove = 1;
362         } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
363             dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
364             remove = 1;
365             if ((unlink(eaname)) != 0)
366                 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
367                         cwdbuf, eaname, strerror(errno));
368         }
369
370         if (remove) {
371             /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
372             free((*ea.ea_entries)[count].ea_name);
373             (*ea.ea_entries)[count].ea_name = NULL;
374         }
375
376         count++;
377     } /* while */
378
379     ea_close(&ea);
380     return ret;
381 }
382
383 /*
384   Check for .AppleDouble folder and .Parent, create if missing
385 */
386 static int check_addir(int volroot)
387 {
388     int addir_ok, adpar_ok;
389     struct stat st;
390     struct adouble ad;
391     char *mname = NULL;
392
393     if (vol->v_adouble == AD_VERSION_EA)
394         return 0;
395
396     /* Check for ad-dir */
397     if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
398         if (errno != ENOENT) {
399             dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
400             return -1;
401         }
402         dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
403     }
404
405     /* Check for ".Parent" */
406     if ( (adpar_ok = access(vol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
407         if (errno != ENOENT) {
408             dbd_log(LOGSTD, "Access error on '%s/%s': %s",
409                     cwdbuf, vol->ad_path(".", ADFLAGS_DIR), strerror(errno));
410             return -1;
411         }
412         dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
413     }
414
415     /* Is one missing ? */
416     if ((addir_ok != 0) || (adpar_ok != 0)) {
417         /* Yes, but are we only scanning ? */
418         if (dbd_flags & DBD_FLAGS_SCAN) {
419             /* Yes:  missing .Parent is not a problem, but missing ad-dir
420                causes later checking of ad-files to fail. So we have to return appropiately */
421             if (addir_ok != 0)
422                 return -1;
423             else  /* (adpar_ok != 0) */
424                 return 0;
425         }
426
427         /* Create ad dir and set name */
428         ad_init(&ad, vol);
429
430         if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
431             dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
432             return -1;
433         }
434
435         /* Get basename of cwd from cwdbuf */
436         mname = utompath(strrchr(cwdbuf, '/') + 1);
437
438         /* Update name in ad file */
439         ad_setname(&ad, mname);
440         ad_flush(&ad);
441         ad_close(&ad, ADFLAGS_HF);
442
443         /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
444         if ((lstat(".", &st)) != 0) {
445             dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
446             return -1;
447         }
448         chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
449         chown(vol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
450     }
451
452     return 0;
453 }
454
455 /*
456   Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
457   Returns:
458   0 = name is not an EA file
459   1 = name is an EA file and no problem was found
460   -1 = name is an EA file and data fork is gone
461  */
462 static int check_eafile_in_adouble(const char *name)
463 {
464     int ret = 0;
465     char *namep, *namedup = NULL;
466
467     /* Check if this is an AFPVOL_EA_AD vol */
468     if (vol->v_vfs_ea == AFPVOL_EA_AD) {
469         /* Does the filename contain "::EA" ? */
470         namedup = strdup(name);
471         if ((namep = strstr(namedup, "::EA")) == NULL) {
472             ret = 0;
473             goto ea_check_done;
474         } else {
475             /* File contains "::EA" so it's an EA file. Check for data file  */
476
477             /* Get string before "::EA" from EA filename */
478             namep[0] = 0;
479             strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
480
481             if ((access( pname, F_OK)) == 0) {
482                 ret = 1;
483                 goto ea_check_done;
484             } else {
485                 ret = -1;
486                 if (errno != ENOENT) {
487                     dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
488                             cwdbuf, name, strerror(errno));
489                     goto ea_check_done;
490                 }
491
492                 /* Orphaned EA file*/
493                 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
494                         cwdbuf, ADv2_DIRNAME, name);
495
496                 if (dbd_flags & DBD_FLAGS_SCAN)
497                     /* Scan only requested, dont change anything */
498                     goto ea_check_done;
499
500                 if ((unlink(name)) != 0) {
501                     dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
502                             cwdbuf, ADv2_DIRNAME, name);
503                 }
504             } /* if (access) */
505         } /* if strstr */
506     } /* if AFPVOL_EA_AD */
507
508 ea_check_done:
509     if (namedup)
510         free(namedup);
511
512     return ret;
513 }
514
515 /*
516   Check files and dirs inside .AppleDouble folder:
517   - remove orphaned files
518   - bail on dirs
519 */
520 static int read_addir(void)
521 {
522     DIR *dp;
523     struct dirent *ep;
524     struct stat st;
525
526     if ((chdir(ADv2_DIRNAME)) != 0) {
527         if (vol->v_adouble == AD_VERSION_EA) {
528             return 0;
529         }
530         dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
531                 cwdbuf, ADv2_DIRNAME, strerror(errno));
532         return -1;
533     }
534
535     if ((dp = opendir(".")) == NULL) {
536         dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
537                 cwdbuf, ADv2_DIRNAME, strerror(errno));
538         return -1;
539     }
540
541     while ((ep = readdir(dp))) {
542         /* Check if its "." or ".." */
543         if (DIR_DOT_OR_DOTDOT(ep->d_name))
544             continue;
545
546         /* Skip ".Parent" */
547         if (STRCMP(ep->d_name, ==, ".Parent"))
548             continue;
549
550         if ((lstat(ep->d_name, &st)) < 0) {
551             dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
552                      cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
553             continue;
554         }
555
556         /* Check for dirs */
557         if (S_ISDIR(st.st_mode)) {
558             dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
559                      ep->d_name, cwdbuf, ADv2_DIRNAME);
560             continue;
561         }
562
563         /* Check if for orphaned and corrupt Extended Attributes file */
564         if (check_eafile_in_adouble(ep->d_name) != 0)
565             continue;
566
567         /* Check for data file */
568         strcpy(pname + 3, ep->d_name);
569         if ((access( pname, F_OK)) != 0) {
570             if (errno != ENOENT) {
571                 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
572                         cwdbuf, pname, strerror(errno));
573                 continue;
574             }
575             /* Orphaned ad-file*/
576             dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
577                     cwdbuf, ADv2_DIRNAME, ep->d_name);
578
579             if (dbd_flags & DBD_FLAGS_SCAN)
580                 /* Scan only requested, dont change anything */
581                 continue;;
582
583             if ((unlink(ep->d_name)) != 0) {
584                 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
585                         cwdbuf, ADv2_DIRNAME, ep->d_name);
586
587             }
588         }
589     }
590
591     if ((chdir("..")) != 0) {
592         dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
593                 cwdbuf, strerror(errno));
594         /* This really is EOT! */
595         longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
596     }
597
598     closedir(dp);
599
600     return 0;
601 }
602
603 /*
604   Check CNID for a file/dir, both from db and from ad-file.
605   For detailed specs see intro.
606
607   @return Correct CNID of object or CNID_INVALID (ie 0) on error
608 */
609 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok)
610 {
611     int ret, adflags = ADFLAGS_HF;
612     cnid_t db_cnid, ad_cnid, tmpid;
613     struct adouble ad;
614
615     adflags = ADFLAGS_HF | (S_ISDIR(st->st_mode) ? ADFLAGS_DIR : 0);
616
617     /* Get CNID from ad-file */
618     ad_cnid = CNID_INVALID;
619     if (ADFILE_OK) {
620         ad_init(&ad, vol);
621         if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
622             
623             if (vol->v_adouble != AD_VERSION_EA) {
624                 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
625                 return CNID_INVALID;
626             }
627             dbd_log( LOGDEBUG, "File without meta EA: \"%s/%s\"", cwdbuf, name);
628             adfile_ok = 1;
629         } else {
630             ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
631             if (ad_cnid == CNID_INVALID)
632                 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
633             else
634                 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
635             ad_close(&ad, ADFLAGS_HF);
636         }
637     }
638
639     /* Get CNID from database */
640     if ((db_cnid = cnid_add(vol->v_cdb, st, did, (char *)name, strlen(name), ad_cnid)) == CNID_INVALID)
641         return CNID_INVALID;
642
643     /* Compare CNID from db and adouble file */
644     if (ad_cnid != db_cnid && adfile_ok == 0) {
645         /* Mismatch, overwrite ad file with value from db */
646         dbd_log(LOGSTD, "CNID mismatch for '%s/%s', CNID db: %u, ad-file: %u",
647                 cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
648         ad_init(&ad, vol);
649         if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
650             dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
651                     cwdbuf, name, strerror(errno));
652             return CNID_INVALID;
653         }
654         ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
655         ad_flush(&ad);
656         ad_close(&ad, ADFLAGS_HF);
657     }
658
659     return db_cnid;
660 }
661
662 /*
663   This is called recursively for all dirs.
664   volroot=1 means we're in the volume root dir, 0 means we aren't.
665   We use this when checking for netatalk private folders like .AppleDB.
666   did is our parents CNID.
667 */
668 static int dbd_readdir(int volroot, cnid_t did)
669 {
670     int cwd, ret = 0, adfile_ok, addir_ok, encoding_ok;
671     cnid_t cnid = 0;
672     const char *name;
673     DIR *dp;
674     struct dirent *ep;
675     static struct stat st;      /* Save some stack space */
676
677     /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
678     if ((addir_ok = check_addir(volroot)) != 0)
679         if ( ! (dbd_flags & DBD_FLAGS_SCAN))
680             /* Fatal on rebuild run, continue if only scanning ! */
681             return -1;
682
683     /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
684     if (ADDIR_OK)
685         if ((read_addir()) != 0)
686             if ( ! (dbd_flags & DBD_FLAGS_SCAN))
687                 /* Fatal on rebuild run, continue if only scanning ! */
688                 return -1;
689
690     if ((dp = opendir (".")) == NULL) {
691         dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
692         return -1;
693     }
694
695     while ((ep = readdir (dp))) {
696         /* Check if we got a termination signal */
697         if (alarmed)
698             longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
699
700         /* Check if its "." or ".." */
701         if (DIR_DOT_OR_DOTDOT(ep->d_name))
702             continue;
703
704         /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
705         if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
706             if (! volroot)
707                 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
708             continue;
709         }
710
711         /* Check for special folders in volume root e.g. ".zfs" */
712         if (volroot) {
713             if ((name = check_special_dirs(ep->d_name)) != NULL) {
714                 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
715                 continue;
716             }
717         }
718
719         /* Skip .AppleDouble dir in this loop */
720         if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
721             continue;
722
723         if (!vol->vfs->vfs_validupath(vol, ep->d_name)) {
724             dbd_log(LOGDEBUG, "Ignoring \"%s\"", ep->d_name);
725             continue;
726         }
727
728         if ((ret = lstat(ep->d_name, &st)) < 0) {
729             dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
730                      cwdbuf, ep->d_name, strerror(errno));
731             continue;
732         }
733         
734         switch (st.st_mode & S_IFMT) {
735         case S_IFREG:
736         case S_IFDIR:
737             break;
738         case S_IFLNK:
739             dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
740             continue;
741         default:
742             dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
743             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
744                 if ((unlink(ep->d_name)) != 0) {
745                     dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
746                 }
747             }
748             continue;
749         }
750
751         /**************************************************************************
752            Statistics
753          **************************************************************************/
754         static unsigned long long statcount = 0;
755         static time_t t = 0;
756
757         if (t == 0)
758             t = time(NULL);
759
760         statcount++;
761         if ((statcount % 10000) == 0) {
762             if (dbd_flags & DBD_FLAGS_STATS)            
763                 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
764                         statcount, (unsigned long long)(time(NULL) - t));
765         }
766
767         /**************************************************************************
768            Tests
769         **************************************************************************/
770
771         /* Check for appledouble file, create if missing, but only if we have addir */
772         const char *name = NULL;
773         adfile_ok = -1;
774         if (ADDIR_OK)
775             adfile_ok = check_adfile(ep->d_name, &st, &name);
776
777         if (name == NULL) {
778             name = ep->d_name;
779         } else {
780             update_cnid(did, &st, ep->d_name, name);
781         }
782
783         /* Check CNIDs */
784         cnid = check_cnid(name, did, &st, adfile_ok);
785
786         /* Now add this object to our rebuild dbd */
787         if (cnid && dbd_rebuild) {
788             static uint count = 0;
789             rqst.cnid = rply.cnid;
790             ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
791             if (dbif_txn_close(dbd_rebuild, ret) != 0)
792                 return -1;
793             if (rply.result != CNID_DBD_RES_OK) {
794                 dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
795                          cnid, cwdbuf, name);
796                 return -1;
797             }
798             count++;
799             if (count == 10000) {
800                 if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
801                     dbd_log(LOGSTD, "Error checkpointing!");
802                     return -1;
803                 }
804                 count = 0;
805             }
806         }
807
808         /* Check EA files */
809         if (vol->v_vfs_ea == AFPVOL_EA_AD)
810             check_eafiles(name);
811
812         /**************************************************************************
813           Recursion
814         **************************************************************************/
815         if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant enter recursion */
816             strcat(cwdbuf, "/");
817             strcat(cwdbuf, name);
818             dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
819             if (-1 == (cwd = open(".", O_RDONLY))) {
820                 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
821                 continue;
822             }
823             if (0 != chdir(name)) {
824                 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
825                 close(cwd);
826                 continue;
827             }
828
829             ret = dbd_readdir(0, cnid);
830
831             fchdir(cwd);
832             close(cwd);
833             *(strrchr(cwdbuf, '/')) = 0;
834             if (ret < 0)
835                 return -1;
836         }
837     }
838
839     /*
840       Use results of previous checks
841     */
842     if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
843         if (rmdir(ADv2_DIRNAME) != 0) {
844             switch (errno) {
845             case ENOENT:
846                 break;
847             default:
848                 dbd_log(LOGSTD, "Error removing adouble dir \"%s/%s\": %s", cwdbuf, ADv2_DIRNAME, strerror(errno));
849                 break;
850             }
851         }
852     }
853     closedir(dp);
854     return ret;
855 }
856
857 /*
858   Main func called from cmd_dbd.c
859 */
860 int cmd_dbd_scanvol(struct vol *vol_in, dbd_flags_t flags)
861 {
862     EC_INIT;
863     struct stat st;
864
865     /* Run with umask 0 */
866     umask(0);
867
868     /* Make vol accessible for all funcs */
869     vol = vol_in;
870     dbd_flags = flags;
871
872     /* We only support unicode volumes ! */
873     if (vol->v_volcharset != CH_UTF8) {
874         dbd_log(LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8);
875         EC_FAIL;
876     }
877
878     /*
879      * Get CNID database stamp, cnid_getstamp() passes the buffer,
880      * then cnid_resolve() actually gets the value from the db
881      */
882     cnid_getstamp(vol->v_cdb, stamp, sizeof(stamp));
883
884     if (setjmp(jmp) != 0) {
885         EC_EXIT_STATUS(0); /* Got signal, jump from dbd_readdir */
886     }
887
888     strcpy(cwdbuf, vol->v_path);
889     chdir(vol->v_path);
890
891     if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
892         if (lstat(".", &st) != 0)
893             EC_FAIL;
894         if (ad_convert(".", &st, vol, NULL) != 0) {
895             switch (errno) {
896             case ENOENT:
897                 break;
898             default:
899                 dbd_log(LOGSTD, "Conversion error for \"%s\": %s", vol->v_path, strerror(errno));
900                 break;
901             }
902         }
903     }
904
905     /* Start recursion */
906     EC_NEG1( dbd_readdir(1, htonl(2)) );  /* 2 = volumeroot CNID */
907
908 EC_CLEANUP:
909     EC_EXIT;
910 }