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