]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/cmd_dbd_scanvol.c
Fixes for new rebuild db
[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     /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
859     if ((addir_ok = check_addir(volroot)) != 0)
860         if ( ! (dbd_flags & DBD_FLAGS_SCAN))
861             /* Fatal on rebuild run, continue if only scanning ! */
862             return -1;
863
864     /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
865     if (ADDIR_OK)
866         if ((read_addir()) != 0)
867             if ( ! (dbd_flags & DBD_FLAGS_SCAN))
868                 /* Fatal on rebuild run, continue if only scanning ! */
869                 return -1;
870
871     if ((dp = opendir (".")) == NULL) {
872         dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
873         return -1;
874     }
875
876     while ((ep = readdir (dp))) {
877         /* Check if we got a termination signal */
878         if (alarmed)
879             longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
880
881         /* Check if its "." or ".." */
882         if (DIR_DOT_OR_DOTDOT(ep->d_name))
883             continue;
884
885         /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
886         if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
887             if (! volroot)
888                 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
889             continue;
890         }
891
892         /* Check for special folders in volume root e.g. ".zfs" */
893         if (volroot) {
894             if ((name = check_special_dirs(ep->d_name)) != NULL) {
895                 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
896                 continue;
897             }
898         }
899
900         /* Skip .AppleDouble dir in this loop */
901         if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
902             continue;
903
904         if ((ret = lstat(ep->d_name, &st)) < 0) {
905             dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
906                      cwdbuf, ep->d_name, strerror(errno));
907             continue;
908         }
909         
910         switch (st.st_mode & S_IFMT) {
911         case S_IFREG:
912             adflags = 0;
913             break;
914         case S_IFDIR:
915             adflags = ADFLAGS_DIR;
916             break;
917         case S_IFLNK:
918             dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
919 #if 0
920             ret = check_symlink(ep->d_name, &adflags);
921             if (ret == 1)
922                 break;
923             if (ret == -1)
924                 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
925 #endif
926             continue;
927         default:
928             dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
929             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
930                 if ((unlink(ep->d_name)) != 0) {
931                     dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
932                 }
933             }
934             continue;
935         }
936
937         /**************************************************************************
938            Statistics
939          **************************************************************************/
940         static unsigned long long statcount = 0;
941         static time_t t = 0;
942
943         if (t == 0)
944             t = time(NULL);
945
946         statcount++;
947         if ((statcount % 10000) == 0) {
948             if (dbd_flags & DBD_FLAGS_STATS)            
949                 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
950                         statcount, (unsigned long long)(time(NULL) - t));
951         }
952
953         /**************************************************************************
954            Tests
955         **************************************************************************/
956
957         /* Check encoding */
958         if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
959             /* If its a file: skipp all other tests now ! */
960             /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
961             continue;
962         }
963
964         /* Check for appledouble file, create if missing, but only if we have addir */
965         adfile_ok = -1;
966         if (ADDIR_OK)
967             adfile_ok = check_adfile(ep->d_name, &st);
968
969         if ( ! nocniddb) {
970             /* Check CNIDs */
971             cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
972
973             /* Now add this object to our rebuild dbd */
974             if (cnid) {
975                 static uint count = 0;
976                 rqst.cnid = rply.cnid;
977                 ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
978                 dbif_txn_close(dbd_rebuild, ret);
979                 if (rply.result != CNID_DBD_RES_OK) {
980                     dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
981                              cnid, cwdbuf, ep->d_name);
982                     longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
983                 }
984                 count++;
985                 if (count == 10000) {
986                     if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
987                         dbd_log(LOGSTD, "Error checkpointing!");
988                         return -1;
989                     }
990                     count = 0;
991                 }
992             }
993         }
994
995         /* Check EA files */
996         if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
997             check_eafiles(ep->d_name);
998
999         /**************************************************************************
1000           Recursion
1001         **************************************************************************/
1002         if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
1003             strcat(cwdbuf, "/");
1004             strcat(cwdbuf, ep->d_name);
1005             dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
1006             if (-1 == (cwd = open(".", O_RDONLY))) {
1007                 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
1008                 continue;
1009             }
1010             if (0 != chdir(ep->d_name)) {
1011                 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
1012                 close(cwd);
1013                 continue;
1014             }
1015
1016             ret = dbd_readdir(0, cnid);
1017
1018             fchdir(cwd);
1019             close(cwd);
1020             *(strrchr(cwdbuf, '/')) = 0;
1021             if (ret < 0)
1022                 continue;
1023         }
1024     }
1025
1026     /*
1027       Use results of previous checks
1028     */
1029
1030     closedir(dp);
1031     return ret;
1032 }
1033
1034 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
1035 {
1036     /* Dont scanvol on no-appledouble vols */
1037     if (vi->v_flags & AFPVOL_NOADOUBLE) {
1038         dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1039         return 0;
1040     }
1041
1042     /* Make this stuff accessible from all funcs easily */
1043     volinfo = vi;
1044     dbd_flags = flags;
1045
1046     /* Init a fake struct vol with just enough so we can call ea_open and friends */
1047     volume.v_adouble = AD_VERSION2;
1048     volume.v_vfs_ea = volinfo->v_vfs_ea;
1049     initvol_vfs(&volume);
1050
1051     /* Run with umask 0 */
1052     umask(0);
1053
1054     /* Remove trailing slash from volume, chdir to vol */
1055     if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
1056         volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
1057     strcpy(cwdbuf, volinfo->v_path);
1058     chdir(volinfo->v_path);
1059
1060     /* Start recursion */
1061     if (dbd_readdir(1, htonl(2)) < 0)  /* 2 = volumeroot CNID */
1062         return -1;
1063
1064     return 0;
1065 }
1066
1067 /*
1068   Remove all CNIDs from dbd that are not in dbd_rebuild
1069 */
1070 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1071 {
1072     int ret = 0, deleted = 0;
1073     cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1074     struct cnid_dbd_rqst rqst;
1075     struct cnid_dbd_rply rply;
1076
1077     dbd->db_param.txn_frequency = 0;
1078
1079     /* jump over rootinfo key */
1080     if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1081         return;
1082     if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1083         return;
1084
1085     /* Get first id from dbd_rebuild */
1086     if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1087         return;
1088
1089     /* Start main loop through dbd: get CNID from dbd */
1090     while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1091         /* Check if we got a termination signal */
1092         if (alarmed)
1093             longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1094
1095         if (deleted > 1000) {
1096             deleted = 0;
1097             if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1098                 dbd_log(LOGSTD, "Error checkpointing!");
1099                 goto cleanup;
1100             }
1101         }
1102
1103         /* This should be the normal case: CNID is in both dbs */
1104         if (dbd_cnid == rebuild_cnid) {
1105             /* Get next CNID from rebuild db */
1106             if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1107                 /* Some error */
1108                 goto cleanup;
1109             } else if (ret == 0) {
1110                 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1111                 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1112                     dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1113                     if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1114                         rqst.cnid = htonl(dbd_cnid);
1115                         ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1116                         dbif_txn_close(dbd, ret);
1117                         deleted++;
1118                     }
1119                     /* Check if we got a termination signal */
1120                     if (alarmed)
1121                         longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1122                 }
1123                 return;
1124             } else
1125                 /* Normal case (ret=1): continue while loop */
1126                 continue;
1127         }
1128
1129         if (dbd_cnid < rebuild_cnid) {
1130             /* CNID is orphaned -> delete */
1131             dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1132             if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1133                 rqst.cnid = htonl(dbd_cnid);
1134                 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1135                 dbif_txn_close(dbd, ret);
1136                 deleted++;
1137             }
1138             continue;
1139         }
1140
1141         if (dbd_cnid > rebuild_cnid) {
1142             dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1143             dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1144             dbif_txn_close(dbd, 2);
1145             dbif_txn_close(dbd_rebuild, 2);
1146             dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1147             dbif_dump(dbd_rebuild, 0);
1148             dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1149             goto cleanup;
1150         }
1151     }
1152
1153 cleanup:
1154     dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1155     dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1156     return;
1157 }
1158
1159 static const char *get_tmpdb_path(void)
1160 {
1161     pid_t pid = getpid();
1162     static char path[MAXPATHLEN];
1163     snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
1164     if (mkdir(path, 0755) != 0)
1165         return NULL;
1166     return path;
1167 }
1168
1169 /*
1170   Main func called from cmd_dbd.c
1171 */
1172 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1173 {
1174     int ret = 0;
1175     struct db_param db_param = { 0 };
1176     const char *tmpdb_path = NULL;
1177
1178     /* Set cachesize for in-memory rebuild db */
1179     db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1180     db_param.txn_frequency = 1000;          /* close txn every 1000 objects */
1181     db_param.logfile_autoremove = 1;
1182
1183     /* Make it accessible for all funcs */
1184     dbd = dbd_ref;
1185
1186     /* We only support unicode volumes ! */
1187     if ( volinfo->v_volcharset != CH_UTF8) {
1188         dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1189         return -1;
1190     }
1191
1192     if (! nocniddb) {
1193         /* Get volume stamp */
1194         dbd_getstamp(dbd, &rqst, &rply);
1195         if (rply.result != CNID_DBD_RES_OK)
1196             goto exit;
1197         memcpy(stamp, rply.name, CNID_DEV_LEN);
1198
1199         /* open/create rebuild dbd, copy rootinfo key */
1200         tmpdb_path = get_tmpdb_path();
1201         if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
1202             ret = -1;
1203             goto exit;
1204         }
1205
1206         if (dbif_env_open(dbd_rebuild,
1207                           &db_param,
1208                           DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
1209             dbd_log(LOGSTD, "error opening tmp database!");
1210             goto exit;
1211         }
1212
1213         if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
1214             ret = -1;
1215             goto exit;
1216         }
1217
1218         if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
1219             ret = -1;
1220             goto exit;
1221         }
1222     }
1223
1224     if (setjmp(jmp) != 0) {
1225         ret = 0;                /* Got signal, jump from dbd_readdir */
1226         goto exit_cleanup;              
1227     }
1228
1229     /* scanvol */
1230     if ( (scanvol(volinfo, flags)) != 0) {
1231         ret = -1;
1232         goto exit;
1233     }
1234
1235 exit_cleanup:
1236     if (! nocniddb) {
1237         dbif_txn_close(dbd, 2);
1238         dbif_txn_close(dbd_rebuild, 2);
1239         if ((flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
1240             /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1241                other clients in between our pass 1 and 2 */
1242             delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1243     }
1244
1245 exit:
1246     if (dbd_rebuild) {
1247         dbd_log(LOGDEBUG, "Closing tmp db");
1248         dbif_close(dbd_rebuild);
1249
1250         if (tmpdb_path) {
1251             char cmd[8 + MAXPATHLEN];
1252             snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
1253             dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
1254             system(cmd);
1255             snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);
1256             system(cmd);
1257         }        
1258     }
1259     return ret;
1260 }