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