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