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