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