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