• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/etc/cnid_dbd/
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
48static struct volinfo *myvolinfo;
49static char           cwdbuf[MAXPATHLEN+1];
50static DBD            *dbd;
51static DBD            *dbd_rebuild;
52static dbd_flags_t    dbd_flags;
53static char           stamp[CNID_DEV_LEN];
54static char           *netatalk_dirs[] = {
55    ".AppleDB",
56    ".AppleDesktop",
57    NULL
58};
59static char           *special_dirs[] = {
60    ".zfs",
61    NULL
62};
63static struct cnid_dbd_rqst rqst;
64static struct cnid_dbd_rply rply;
65static jmp_buf jmp;
66static struct vol volume; /* fake it for ea_open */
67static char pname[MAXPATHLEN] = "../";
68
69/*
70  Taken form afpd/desktop.c
71*/
72static 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*/
111static 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 */
167static 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*/
242static 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*/
264static 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*/
279static 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*/
293static 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*/
356static 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
391exit:
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*/
404static 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*/
458static 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_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 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_metadata(&ad);
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 */
534static 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
580ea_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*/
592static 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*/
677static 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_metadata( name, adflags, O_RDWR, &ad) != 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_metadata(&ad);
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_metadata( name, adflags, O_RDWR, &ad) != 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_metadata(&ad);
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_metadata( name, adflags, O_RDWR, &ad) != 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_metadata(&ad);
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_metadata( name, adflags, O_RDWR, &ad) != 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_metadata(&ad);
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*/
859static 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
1045static 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*/
1081static 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
1173cleanup:
1174    dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1175    dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1176    return;
1177}
1178
1179static 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*/
1192int 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
1258exit:
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}
1286