]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/cmd_dbd_scanvol.c
Merge 2-1
[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/volinfo.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
38 #include "cmd_dbd.h"
39 #include "dbif.h"
40 #include "db_param.h"
41 #include "dbd.h"
42
43 /* Some defines to ease code parsing */
44 #define ADDIR_OK (addir_ok == 0)
45 #define ADFILE_OK (adfile_ok == 0)
46
47
48 static struct volinfo *myvolinfo;
49 static char           cwdbuf[MAXPATHLEN+1];
50 static DBD            *dbd;
51 static DBD            *dbd_rebuild;
52 static dbd_flags_t    dbd_flags;
53 static char           stamp[CNID_DEV_LEN];
54 static char           *netatalk_dirs[] = {
55     ".AppleDB",
56     ".AppleDesktop",
57     NULL
58 };
59 static char           *special_dirs[] = {
60     ".zfs",
61     NULL
62 };
63 static struct cnid_dbd_rqst rqst;
64 static struct cnid_dbd_rply rply;
65 static jmp_buf jmp;
66 static struct vol volume; /* fake it for ea_open */
67 static char pname[MAXPATHLEN] = "../";
68
69 /*
70   Taken form afpd/desktop.c
71 */
72 static char *utompath(char *upath)
73 {
74     static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
75     char         *m, *u;
76     uint16_t     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
77     size_t       outlen;
78
79     if (!upath)
80         return NULL;
81
82     m = mpath;
83     u = upath;
84     outlen = strlen(upath);
85
86     if ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER))
87         flags |= CONV_TOUPPER;
88     else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER))
89         flags |= CONV_TOLOWER;
90
91     if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
92         flags |= CONV__EILSEQ;
93     }
94
95     /* convert charsets */
96     if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset,
97                                                  CH_UTF8_MAC,
98                                                  myvolinfo->v_maccharset,
99                                                  u, outlen, mpath, MAXPATHLEN, &flags)) ) {
100         dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
101                  myvolinfo->v_volcodepage, myvolinfo->v_maccodepage, u);
102         return NULL;
103     }
104
105     return(m);
106 }
107
108 /*
109   Taken form afpd/desktop.c
110 */
111 static char *mtoupath(char *mpath)
112 {
113     static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
114     char    *m, *u;
115     size_t       inplen;
116     size_t       outlen;
117     u_int16_t    flags = 0;
118
119     if (!mpath)
120         return NULL;
121
122     if ( *mpath == '\0' ) {
123         return( "." );
124     }
125
126     /* set conversion flags */
127     if (!(myvolinfo->v_flags & AFPVOL_NOHEX))
128         flags |= CONV_ESCAPEHEX;
129     if (!(myvolinfo->v_flags & AFPVOL_USEDOTS))
130         flags |= CONV_ESCAPEDOTS;
131
132     if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER))
133         flags |= CONV_TOUPPER;
134     else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER))
135         flags |= CONV_TOLOWER;
136
137     if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
138         flags |= CONV__EILSEQ;
139     }
140
141     m = mpath;
142     u = upath;
143
144     inplen = strlen(m);
145     outlen = MAXPATHLEN;
146
147     if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
148                                                 myvolinfo->v_volcharset,
149                                                 myvolinfo->v_maccharset,
150                                                 m, inplen, u, outlen, &flags)) ) {
151         dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
152                  myvolinfo->v_volcodepage, mpath);
153         return NULL;
154     }
155
156     return( upath );
157 }
158
159 #if 0
160 /* 
161    Check if "name" is pointing to
162    a) an object inside the current volume (return 0)
163    b) an object outside the current volume (return 1)
164    Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags
165    Return -1 on any serious error.
166  */
167 static int check_symlink(const char *name, int *adflags)
168 {
169     int cwd;
170     ssize_t len;
171     char pathbuf[MAXPATHLEN + 1];
172     char *sep;
173     struct stat st;
174
175     if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) {
176         dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s", 
177                 cwdbuf, name, strerror(errno));
178         return -1;
179     }
180     pathbuf[len] = 0;
181
182     if ((stat(pathbuf, &st)) != 0) {
183         dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno));
184     }
185
186     /* Remember cwd */
187     if ((cwd = open(".", O_RDONLY)) < 0) {
188         dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno));
189         return -1;
190     }
191
192     if (S_ISDIR(st.st_mode)) {
193         *adflags |= ADFLAGS_DIR;
194     } else { /* file */
195         /* get basename from path */
196         if ((sep = strrchr(pathbuf, '/')) == NULL)
197             /* just a file at the same level */
198             return 0;
199         sep = 0; /* pathbuf now contains the directory*/
200     }
201
202     /* fchdir() to pathbuf so we can easily get its path with getcwd() */
203     if ((chdir(pathbuf)) != 0) {
204         dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno));
205         return -1;
206     }
207
208     if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) {
209         dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno));
210         if ((fchdir(cwd)) != 0)
211             /* We're foobared */
212             longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
213         return -1;
214     }
215
216     if ((fchdir(cwd)) != 0)
217         /* We're foobared */
218         longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
219
220     /*
221       We now have the symlink target dir as absoulte path in pathbuf
222       and can compare it with the currents volume path
223     */
224     int i = 0;
225     while (myvolinfo->v_path[i]) {
226         if ((pathbuf[i] == 0) || (myvolinfo->v_path[i] != pathbuf[i])) {
227             dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
228             return 1;
229         }
230         i++;
231     }
232
233     dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name);
234     return 0;
235 }
236 #endif
237
238 /*
239   Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
240   We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
241 */
242 static int check_name_encoding(char *uname)
243 {
244     char *roundtripped;
245
246     roundtripped = mtoupath(utompath(uname));
247     if (!roundtripped) {
248         dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
249         return -1;
250     }
251
252     if ( STRCMP(uname, !=, roundtripped)) {
253         dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
254         return -1;
255     }
256
257     return 0;
258 }
259
260 /*
261   Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
262   Returns pointer to name or NULL.
263 */
264 static const char *check_netatalk_dirs(const char *name)
265 {
266     int c;
267
268     for (c=0; netatalk_dirs[c]; c++) {
269         if ((strcmp(name, netatalk_dirs[c])) == 0)
270             return netatalk_dirs[c];
271     }
272     return NULL;
273 }
274
275 /*
276   Check for special names
277   Returns pointer to name or NULL.
278 */
279 static const char *check_special_dirs(const char *name)
280 {
281     int c;
282
283     for (c=0; special_dirs[c]; c++) {
284         if ((strcmp(name, special_dirs[c])) == 0)
285             return special_dirs[c];
286     }
287     return NULL;
288 }
289
290 /*
291   Check for .AppleDouble file, create if missing
292 */
293 static int check_adfile(const char *fname, const struct stat *st)
294 {
295     int ret, adflags;
296     struct adouble ad;
297     char *adname;
298
299     if (dbd_flags & DBD_FLAGS_CLEANUP)
300         return 0;
301
302     if (S_ISREG(st->st_mode))
303         adflags = 0;
304     else
305         adflags = ADFLAGS_DIR;
306
307     adname = myvolinfo->ad_path(fname, adflags);
308
309     if ((ret = access( adname, F_OK)) != 0) {
310         if (errno != ENOENT) {
311             dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
312                     cwdbuf, adname, strerror(errno));
313             return -1;
314         }
315         /* Missing. Log and create it */
316         dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
317
318         if (dbd_flags & DBD_FLAGS_SCAN)
319             /* Scan only requested, dont change anything */
320             return -1;
321
322         /* Create ad file */
323         ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
324
325         if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
326             dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
327                      cwdbuf, adname, strerror(errno));
328
329             return -1;
330         }
331
332         /* Set name in ad-file */
333         ad_setname(&ad, utompath((char *)fname));
334         ad_flush(&ad);
335         ad_close_metadata(&ad);
336
337         chown(adname, st->st_uid, st->st_gid);
338         /* FIXME: should we inherit mode too here ? */
339 #if 0
340         chmod(adname, st->st_mode);
341 #endif
342     } else {
343         ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
344         if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
345             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
346             return -1;
347         }
348         ad_close_metadata(&ad);
349     }
350     return 0;
351 }
352
353 /* 
354    Remove all files with file::EA* from adouble dir
355 */
356 static void remove_eafiles(const char *name, struct ea *ea)
357 {
358     DIR *dp = NULL;
359     struct dirent *ep;
360     char eaname[MAXPATHLEN];
361
362     strlcpy(eaname, name, sizeof(eaname));
363     if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
364         dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
365         return;
366     }
367
368     if ((chdir(ADv2_DIRNAME)) != 0) {
369         dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
370                 cwdbuf, ADv2_DIRNAME, strerror(errno));
371         return;
372     }
373
374     if ((dp = opendir(".")) == NULL) {
375         dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
376                 cwdbuf, ADv2_DIRNAME, strerror(errno));
377         return;
378     }
379
380     while ((ep = readdir(dp))) {
381         if (strstr(ep->d_name, eaname) != NULL) {
382             dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
383                     cwdbuf, ADv2_DIRNAME, ep->d_name);
384             if ((unlink(ep->d_name)) != 0) {
385                 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
386                         cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
387             }
388         } /* if */
389     } /* while */
390
391     if (dp)
392         closedir(dp);
393
394 }
395
396 /*
397   Check Extended Attributes files
398 */
399 static int check_eafiles(const char *fname)
400 {
401     unsigned int  count = 0;
402     int ret = 0, remove;
403     struct ea ea;
404     struct stat st;
405     char *eaname;
406
407     if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
408         if (errno == ENOENT)
409             return 0;
410         dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
411         if ( ! (dbd_flags & DBD_FLAGS_SCAN))
412             remove_eafiles(fname, &ea);
413         return -1;
414     }
415
416     /* Check all EAs */
417     while (count < ea.ea_count) {        
418         dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
419         remove = 0;
420
421         eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
422
423         if (lstat(eaname, &st) != 0) {
424             if (errno == ENOENT)
425                 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
426             else
427                 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
428             remove = 1;
429         } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
430             dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
431             remove = 1;
432             if ((unlink(eaname)) != 0)
433                 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
434                         cwdbuf, eaname, strerror(errno));
435         }
436
437         if (remove) {
438             /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
439             free((*ea.ea_entries)[count].ea_name);
440             (*ea.ea_entries)[count].ea_name = NULL;
441         }
442
443         count++;
444     } /* while */
445
446     ea_close(&ea);
447     return ret;
448 }
449
450 /*
451   Check for .AppleDouble folder and .Parent, create if missing
452 */
453 static int check_addir(int volroot)
454 {
455     int addir_ok, adpar_ok;
456     struct stat st;
457     struct adouble ad;
458     char *mname = NULL;
459
460     if (dbd_flags & DBD_FLAGS_CLEANUP)
461         return 0;
462
463     /* Check for ad-dir */
464     if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
465         if (errno != ENOENT) {
466             dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
467             return -1;
468         }
469         dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
470     }
471
472     /* Check for ".Parent" */
473     if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
474         if (errno != ENOENT) {
475             dbd_log(LOGSTD, "Access error on '%s/%s': %s",
476                     cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
477             return -1;
478         }
479         dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
480     }
481
482     /* Is one missing ? */
483     if ((addir_ok != 0) || (adpar_ok != 0)) {
484         /* Yes, but are we only scanning ? */
485         if (dbd_flags & DBD_FLAGS_SCAN) {
486             /* Yes:  missing .Parent is not a problem, but missing ad-dir
487                causes later checking of ad-files to fail. So we have to return appropiately */
488             if (addir_ok != 0)
489                 return -1;
490             else  /* (adpar_ok != 0) */
491                 return 0;
492         }
493
494         /* Create ad dir and set name */
495         ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
496
497         if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
498             dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
499             return -1;
500         }
501
502         /* Get basename of cwd from cwdbuf */
503         mname = utompath(strrchr(cwdbuf, '/') + 1);
504
505         /* Update name in ad file */
506         ad_setname(&ad, mname);
507         ad_flush(&ad);
508         ad_close_metadata(&ad);
509
510         /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
511         if ((lstat(".", &st)) != 0) {
512             dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
513             return -1;
514         }
515         chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
516         chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
517     }
518
519     return 0;
520 }
521
522 /*
523   Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
524   Returns:
525   0 = name is not an EA file
526   1 = name is an EA file and no problem was found
527   -1 = name is an EA file and data fork is gone
528  */
529 static int check_eafile_in_adouble(const char *name)
530 {
531     int ret = 0;
532     char *namep, *namedup = NULL;
533
534     /* Check if this is an AFPVOL_EA_AD vol */
535     if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) {
536         /* Does the filename contain "::EA" ? */
537         namedup = strdup(name);
538         if ((namep = strstr(namedup, "::EA")) == NULL) {
539             ret = 0;
540             goto ea_check_done;
541         } else {
542             /* File contains "::EA" so it's an EA file. Check for data file  */
543
544             /* Get string before "::EA" from EA filename */
545             namep[0] = 0;
546             strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
547
548             if ((access( pname, F_OK)) == 0) {
549                 ret = 1;
550                 goto ea_check_done;
551             } else {
552                 ret = -1;
553                 if (errno != ENOENT) {
554                     dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
555                             cwdbuf, name, strerror(errno));
556                     goto ea_check_done;
557                 }
558
559                 /* Orphaned EA file*/
560                 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
561                         cwdbuf, ADv2_DIRNAME, name);
562
563                 if (dbd_flags & DBD_FLAGS_SCAN)
564                     /* Scan only requested, dont change anything */
565                     goto ea_check_done;
566
567                 if ((unlink(name)) != 0) {
568                     dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
569                             cwdbuf, ADv2_DIRNAME, name);
570                 }
571             } /* if (access) */
572         } /* if strstr */
573     } /* if AFPVOL_EA_AD */
574
575 ea_check_done:
576     if (namedup)
577         free(namedup);
578
579     return ret;
580 }
581
582 /*
583   Check files and dirs inside .AppleDouble folder:
584   - remove orphaned files
585   - bail on dirs
586 */
587 static int read_addir(void)
588 {
589     DIR *dp;
590     struct dirent *ep;
591     struct stat st;
592
593     if ((chdir(ADv2_DIRNAME)) != 0) {
594         dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
595                 cwdbuf, ADv2_DIRNAME, strerror(errno));
596         return -1;
597     }
598
599     if ((dp = opendir(".")) == NULL) {
600         dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
601                 cwdbuf, ADv2_DIRNAME, strerror(errno));
602         return -1;
603     }
604
605     while ((ep = readdir(dp))) {
606         /* Check if its "." or ".." */
607         if (DIR_DOT_OR_DOTDOT(ep->d_name))
608             continue;
609         /* Skip ".Parent" */
610         if (STRCMP(ep->d_name, ==, ".Parent"))
611             continue;
612
613         if ((lstat(ep->d_name, &st)) < 0) {
614             dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
615                      cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
616             continue;
617         }
618
619         /* Check for dirs */
620         if (S_ISDIR(st.st_mode)) {
621             dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
622                      ep->d_name, cwdbuf, ADv2_DIRNAME);
623             continue;
624         }
625
626         /* Check if for orphaned and corrupt Extended Attributes file */
627         if (check_eafile_in_adouble(ep->d_name) != 0)
628             continue;
629
630         /* Check for data file */
631         strcpy(pname + 3, ep->d_name);
632         if ((access( pname, F_OK)) != 0) {
633             if (errno != ENOENT) {
634                 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
635                         cwdbuf, pname, strerror(errno));
636                 continue;
637             }
638             /* Orphaned ad-file*/
639             dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
640                     cwdbuf, ADv2_DIRNAME, ep->d_name);
641
642             if (dbd_flags & DBD_FLAGS_SCAN)
643                 /* Scan only requested, dont change anything */
644                 continue;;
645
646             if ((unlink(ep->d_name)) != 0) {
647                 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
648                         cwdbuf, ADv2_DIRNAME, ep->d_name);
649
650             }
651         }
652     }
653
654     if ((chdir("..")) != 0) {
655         dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
656                 cwdbuf, strerror(errno));
657         /* This really is EOT! */
658         longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
659     }
660
661     closedir(dp);
662
663     return 0;
664 }
665
666 /*
667   Check CNID for a file/dir, both from db and from ad-file.
668   For detailed specs see intro.
669
670   @return Correct CNID of object or CNID_INVALID (ie 0) on error
671 */
672 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
673 {
674     int ret;
675     cnid_t db_cnid, ad_cnid;
676     struct adouble ad;
677
678     /* Force checkout every X items */
679     static int cnidcount = 0;
680     cnidcount++;
681     if (cnidcount > 10000) {
682         cnidcount = 0;
683         if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
684             dbd_log(LOGSTD, "Error checkpointing!");
685             return CNID_INVALID;
686         }
687     }
688
689     /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
690     ad_cnid = 0;
691     if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
692         ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
693         if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
694             
695             if (dbd_flags & DBD_FLAGS_CLEANUP)
696                 return CNID_INVALID;
697
698             dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
699             return CNID_INVALID;
700         }
701
702         if (dbd_flags & DBD_FLAGS_FORCE) {
703             ad_cnid = ad_forcegetid(&ad);
704             /* This ensures the changed stamp is written */
705             ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
706             ad_flush(&ad);
707         }
708         else
709             ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
710
711         if (ad_cnid == 0)
712             dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
713         else
714             dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
715
716         ad_close_metadata(&ad);
717     }
718
719     /* Get CNID from database */
720
721     /* Prepare request data */
722     memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
723     memset(&rply, 0, sizeof(struct cnid_dbd_rply));
724     rqst.did = did;
725     rqst.cnid = ad_cnid;
726     if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
727         rqst.dev = st->st_dev;
728     rqst.ino = st->st_ino;
729     rqst.type = S_ISDIR(st->st_mode)?1:0;
730     rqst.name = (char *)name;
731     rqst.namelen = strlen(name);
732
733     /* Query the database */
734     ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
735     if (dbif_txn_close(dbd, ret) != 0)
736         return CNID_INVALID;
737     if (rply.result == CNID_DBD_RES_OK) {
738         db_cnid = rply.cnid;
739     } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
740         if ( ! (dbd_flags & DBD_FLAGS_FORCE))
741             dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
742         db_cnid = 0;
743     } else {
744         dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
745         db_cnid = 0;
746     }
747
748     /* Compare results from both CNID searches */
749     if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
750         /* Everything is fine */
751         return db_cnid;
752     } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
753         /* Mismatch ? Delete both from db and re-add data from file */
754         dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
755         if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
756             rqst.cnid = db_cnid;
757             ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
758             if (dbif_txn_close(dbd, ret) != 0)
759                 return CNID_INVALID;
760
761             rqst.cnid = ad_cnid;
762             ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
763             if (dbif_txn_close(dbd, ret) != 0)
764                 return CNID_INVALID;
765
766             ret = dbd_rebuild_add(dbd, &rqst, &rply);
767             if (dbif_txn_close(dbd, ret) != 0)
768                 return CNID_INVALID;
769         }
770         return ad_cnid;
771     } else if (ad_cnid && (db_cnid == 0)) {
772         /* in ad-file but not in db */
773         if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
774             /* Ensure the cnid from the ad-file is not already occupied by another file */
775             dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
776                     ntohl(ad_cnid));
777
778             rqst.cnid = ad_cnid;
779             ret = dbd_resolve(dbd, &rqst, &rply);
780             if (ret == CNID_DBD_RES_OK) {
781                 /* Occupied! Choose another, update ad-file */
782                 ret = dbd_add(dbd, &rqst, &rply, 1);
783                 if (dbif_txn_close(dbd, ret) != 0)
784                     return CNID_INVALID;
785                 db_cnid = rply.cnid;
786                 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
787
788                 if ((myvolinfo->v_flags & AFPVOL_CACHE)
789                     && ADFILE_OK
790                     && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
791                     dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
792                             cwdbuf, name, ntohl(db_cnid));
793                     ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
794                     if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
795                         dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
796                                 cwdbuf, name, strerror(errno));
797                         return CNID_INVALID;
798                     }
799                     ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
800                     ad_flush(&ad);
801                     ad_close_metadata(&ad);
802                 }
803                 return db_cnid;
804             }
805
806             dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
807                     cwdbuf, name, ntohl(ad_cnid));
808             rqst.cnid = ad_cnid;
809             ret = dbd_rebuild_add(dbd, &rqst, &rply);
810             if (dbif_txn_close(dbd, ret) != 0)
811                 return CNID_INVALID;
812         }
813         return ad_cnid;
814     } else if ((db_cnid == 0) && (ad_cnid == 0)) {
815         /* No CNID at all, we clearly have to allocate a fresh one... */
816         /* Note: the next test will use this new CNID too! */
817         if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
818             /* add to db */
819             ret = dbd_add(dbd, &rqst, &rply, 1);
820             if (dbif_txn_close(dbd, ret) != 0)
821                 return CNID_INVALID;
822             db_cnid = rply.cnid;
823             dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
824         }
825     }
826
827     if ((ad_cnid == 0) && db_cnid) {
828         /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
829         if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
830             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
831                 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
832                         cwdbuf, name, ntohl(db_cnid));
833                 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
834                 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
835                     dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
836                             cwdbuf, name, strerror(errno));
837                     return CNID_INVALID;
838                 }
839                 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
840                 ad_flush(&ad);
841                 ad_close_metadata(&ad);
842             }
843         }
844         return db_cnid;
845     }
846
847     return CNID_INVALID;
848 }
849
850 /*
851   This is called recursively for all dirs.
852   volroot=1 means we're in the volume root dir, 0 means we aren't.
853   We use this when checking for netatalk private folders like .AppleDB.
854   did is our parents CNID.
855 */
856 static int dbd_readdir(int volroot, cnid_t did)
857 {
858     int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
859     cnid_t cnid = 0;
860     const char *name;
861     DIR *dp;
862     struct dirent *ep;
863     static struct stat st;      /* Save some stack space */
864
865     /* keep trying to get the lock */
866     if (!db_locked)
867         if ((db_locked = get_lock(1, NULL)) == -1)
868             return -1;
869
870     /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
871     if ((addir_ok = check_addir(volroot)) != 0)
872         if ( ! (dbd_flags & DBD_FLAGS_SCAN))
873             /* Fatal on rebuild run, continue if only scanning ! */
874             return -1;
875
876     /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
877     if (ADDIR_OK)
878         if ((read_addir()) != 0)
879             if ( ! (dbd_flags & DBD_FLAGS_SCAN))
880                 /* Fatal on rebuild run, continue if only scanning ! */
881                 return -1;
882
883     if ((dp = opendir (".")) == NULL) {
884         dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
885         return -1;
886     }
887
888     while ((ep = readdir (dp))) {
889         /* Check if we got a termination signal */
890         if (alarmed)
891             longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
892
893         /* Check if its "." or ".." */
894         if (DIR_DOT_OR_DOTDOT(ep->d_name))
895             continue;
896
897         /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
898         if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
899             if (! volroot)
900                 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
901             continue;
902         }
903
904         /* Check for special folders in volume root e.g. ".zfs" */
905         if (volroot) {
906             if ((name = check_special_dirs(ep->d_name)) != NULL) {
907                 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
908                 continue;
909             }
910         }
911
912         /* Skip .AppleDouble dir in this loop */
913         if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
914             continue;
915
916         if ((ret = lstat(ep->d_name, &st)) < 0) {
917             dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
918                      cwdbuf, ep->d_name, strerror(errno));
919             continue;
920         }
921         
922         switch (st.st_mode & S_IFMT) {
923         case S_IFREG:
924             adflags = 0;
925             break;
926         case S_IFDIR:
927             adflags = ADFLAGS_DIR;
928             break;
929         case S_IFLNK:
930             dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
931 #if 0
932             ret = check_symlink(ep->d_name, &adflags);
933             if (ret == 1)
934                 break;
935             if (ret == -1)
936                 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
937 #endif
938             continue;
939         default:
940             dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
941             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
942                 if ((unlink(ep->d_name)) != 0) {
943                     dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
944                 }
945             }
946             continue;
947         }
948
949         /**************************************************************************
950            Statistics
951          **************************************************************************/
952         static unsigned long long statcount = 0;
953         static time_t t = 0;
954
955         if (t == 0)
956             t = time(NULL);
957
958         statcount++;
959         if ((statcount % 10000) == 0) {
960             if (dbd_flags & DBD_FLAGS_STATS)            
961                 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
962                         statcount, (unsigned long long)(time(NULL) - t));
963         }
964
965         /**************************************************************************
966            Tests
967         **************************************************************************/
968
969         /* Check encoding */
970         if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
971             /* If its a file: skipp all other tests now ! */
972             /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
973             continue;
974         }
975
976         /* Check for appledouble file, create if missing, but only if we have addir */
977         adfile_ok = -1;
978         if (ADDIR_OK)
979             adfile_ok = check_adfile(ep->d_name, &st);
980
981         if ( ! nocniddb) {
982             /* Check CNIDs */
983             cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
984
985             /* Now add this object to our rebuild dbd */
986             if (cnid && dbd_rebuild) {
987                 static uint count = 0;
988                 rqst.cnid = rply.cnid;
989                 ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
990                 if (dbif_txn_close(dbd_rebuild, ret) != 0)
991                     return -1;
992                 if (rply.result != CNID_DBD_RES_OK) {
993                     dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
994                              cnid, cwdbuf, ep->d_name);
995                     return -1;
996                 }
997                 count++;
998                 if (count == 10000) {
999                     if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
1000                         dbd_log(LOGSTD, "Error checkpointing!");
1001                         return -1;
1002                     }
1003                     count = 0;
1004                 }
1005             }
1006         }
1007
1008         /* Check EA files */
1009         if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD)
1010             check_eafiles(ep->d_name);
1011
1012         /**************************************************************************
1013           Recursion
1014         **************************************************************************/
1015         if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
1016             strcat(cwdbuf, "/");
1017             strcat(cwdbuf, ep->d_name);
1018             dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
1019             if (-1 == (cwd = open(".", O_RDONLY))) {
1020                 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
1021                 continue;
1022             }
1023             if (0 != chdir(ep->d_name)) {
1024                 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
1025                 close(cwd);
1026                 continue;
1027             }
1028
1029             ret = dbd_readdir(0, cnid);
1030
1031             fchdir(cwd);
1032             close(cwd);
1033             *(strrchr(cwdbuf, '/')) = 0;
1034             if (ret < 0)
1035                 return -1;
1036         }
1037     }
1038
1039     /*
1040       Use results of previous checks
1041     */
1042
1043     closedir(dp);
1044     return ret;
1045 }
1046
1047 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
1048 {
1049     /* Dont scanvol on no-appledouble vols */
1050     if (vi->v_flags & AFPVOL_NOADOUBLE) {
1051         dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1052         return 0;
1053     }
1054
1055     /* Make this stuff accessible from all funcs easily */
1056     myvolinfo = vi;
1057     dbd_flags = flags;
1058
1059     /* Init a fake struct vol with just enough so we can call ea_open and friends */
1060     volume.v_adouble = AD_VERSION2;
1061     volume.v_vfs_ea = myvolinfo->v_vfs_ea;
1062     initvol_vfs(&volume);
1063
1064     /* Run with umask 0 */
1065     umask(0);
1066
1067     /* Remove trailing slash from volume, chdir to vol */
1068     if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
1069         myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
1070     strcpy(cwdbuf, myvolinfo->v_path);
1071     chdir(myvolinfo->v_path);
1072
1073     /* Start recursion */
1074     if (dbd_readdir(1, htonl(2)) < 0)  /* 2 = volumeroot CNID */
1075         return -1;
1076
1077     return 0;
1078 }
1079
1080 /*
1081   Remove all CNIDs from dbd that are not in dbd_rebuild
1082 */
1083 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1084 {
1085     int ret = 0, deleted = 0;
1086     cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1087     struct cnid_dbd_rqst rqst;
1088     struct cnid_dbd_rply rply;
1089
1090     /* jump over rootinfo key */
1091     if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1092         return;
1093     if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1094         return;
1095
1096     /* Get first id from dbd_rebuild */
1097     if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1098         return;
1099
1100     /* Start main loop through dbd: get CNID from dbd */
1101     while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1102         /* Check if we got a termination signal */
1103         if (alarmed)
1104             longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1105
1106         if (deleted > 1000) {
1107             deleted = 0;
1108             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1109                 dbd_log(LOGSTD, "Error checkpointing!");
1110                 goto cleanup;
1111             }
1112         }
1113
1114         /* This should be the normal case: CNID is in both dbs */
1115         if (dbd_cnid == rebuild_cnid) {
1116             /* Get next CNID from rebuild db */
1117             if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1118                 /* Some error */
1119                 goto cleanup;
1120             } else if (ret == 0) {
1121                 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1122                 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1123                     dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1124                     if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1125                         rqst.cnid = htonl(dbd_cnid);
1126                         if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1127                             dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1128                             (void)dbif_txn_abort(dbd);
1129                             goto cleanup;
1130                         }
1131                         
1132                         if (dbif_txn_close(dbd, ret) != 0)
1133                             return;
1134                         deleted++;
1135                     }
1136                     /* Check if we got a termination signal */
1137                     if (alarmed)
1138                         longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1139                 }
1140                 return;
1141             } else
1142                 /* Normal case (ret=1): continue while loop */
1143                 continue;
1144         }
1145
1146         if (dbd_cnid < rebuild_cnid) {
1147             /* CNID is orphaned -> delete */
1148             dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
1149             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1150                 rqst.cnid = htonl(dbd_cnid);
1151                 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1152                     dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1153                     (void)dbif_txn_abort(dbd);
1154                     goto cleanup;
1155                 }
1156                 if (dbif_txn_close(dbd, ret) != 0)
1157                     return;
1158                 deleted++;
1159             }
1160             continue;
1161         }
1162
1163         if (dbd_cnid > rebuild_cnid) {
1164             dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1165             dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1166             (void)dbif_txn_close(dbd, 2);
1167             (void)dbif_txn_close(dbd_rebuild, 2);                
1168             dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1169             dbif_dump(dbd_rebuild, 0);
1170             dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1171             goto cleanup;
1172         }
1173     } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
1174
1175 cleanup:
1176     dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1177     dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1178     return;
1179 }
1180
1181 static const char *get_tmpdb_path(void)
1182 {
1183     pid_t pid = getpid();
1184     static char path[MAXPATHLEN];
1185     snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
1186     if (mkdir(path, 0755) != 0)
1187         return NULL;
1188     return path;
1189 }
1190
1191 /*
1192   Main func called from cmd_dbd.c
1193 */
1194 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
1195 {
1196     int ret = 0;
1197     struct db_param db_param = { 0 };
1198     const char *tmpdb_path = NULL;
1199
1200     /* Set cachesize for in-memory rebuild db */
1201     db_param.cachesize = 64 * 1024;         /* 64 MB */
1202     db_param.maxlocks = DEFAULT_MAXLOCKS;
1203     db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS;
1204     db_param.logfile_autoremove = 1;
1205
1206     /* Make it accessible for all funcs */
1207     dbd = dbd_ref;
1208
1209     /* We only support unicode volumes ! */
1210     if ( vi->v_volcharset != CH_UTF8) {
1211         dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
1212         return -1;
1213     }
1214
1215     /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
1216     if (! nocniddb && !(flags & DBD_FLAGS_FORCE)) {
1217         /* Get volume stamp */
1218         dbd_getstamp(dbd, &rqst, &rply);
1219         if (rply.result != CNID_DBD_RES_OK)
1220             goto exit;
1221         memcpy(stamp, rply.name, CNID_DEV_LEN);
1222
1223         /* open/create rebuild dbd, copy rootinfo key */
1224         tmpdb_path = get_tmpdb_path();
1225         if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
1226             ret = -1;
1227             goto exit;
1228         }
1229
1230         if (dbif_env_open(dbd_rebuild,
1231                           &db_param,
1232                           DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
1233             dbd_log(LOGSTD, "error opening tmp database!");
1234             goto exit;
1235         }
1236
1237         if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
1238             ret = -1;
1239             goto exit;
1240         }
1241
1242         if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
1243             ret = -1;
1244             goto exit;
1245         }
1246     }
1247
1248     if (setjmp(jmp) != 0) {
1249         ret = 0;                /* Got signal, jump from dbd_readdir */
1250         goto exit;
1251     }
1252
1253     /* scanvol */
1254     if ( (scanvol(vi, flags)) != 0) {
1255         ret = -1;
1256         goto exit;
1257     }
1258
1259 exit:
1260     if (! nocniddb) {
1261         if (dbif_txn_close(dbd, 2) != 0)
1262             ret = -1;
1263         if (dbd_rebuild)
1264             if (dbif_txn_close(dbd_rebuild, 2) != 0)
1265                 ret = -1;
1266         if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
1267             /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1268                other clients in between our pass 1 and 2 */
1269             delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1270     }
1271
1272     if (dbd_rebuild) {
1273         dbd_log(LOGDEBUG, "Closing tmp db");
1274         dbif_close(dbd_rebuild);
1275
1276         if (tmpdb_path) {
1277             char cmd[8 + MAXPATHLEN];
1278             snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
1279             dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
1280             system(cmd);
1281             snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);
1282             system(cmd);
1283         }        
1284     }
1285     return ret;
1286 }