2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
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.
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.
17 #endif /* HAVE_CONFIG_H */
21 #include <sys/types.h>
29 #include <atalk/adouble.h>
30 #include <atalk/unicode.h>
31 #include <atalk/netatalk_conf.h>
32 #include <atalk/volume.h>
34 #include <atalk/util.h>
35 #include <atalk/acl.h>
36 #include <atalk/compat.h>
37 #include <atalk/cnid.h>
38 #include <atalk/errchk.h>
45 /* Some defines to ease code parsing */
46 #define ADDIR_OK (addir_ok == 0)
47 #define ADFILE_OK (adfile_ok == 0)
50 static char cwdbuf[MAXPATHLEN+1];
51 static struct vol *vol;
52 static DBD *dbd_rebuild;
53 static dbd_flags_t dbd_flags;
54 static char stamp[CNID_DEV_LEN];
55 static char *netatalk_dirs[] = {
60 static char *special_dirs[] = {
64 static struct cnid_dbd_rqst rqst;
65 static struct cnid_dbd_rply rply;
67 static char pname[MAXPATHLEN] = "../";
68 static char cnidResBuf[12 + MAXPATHLEN + 1];
71 Taken form afpd/desktop.c
73 static char *utompath(char *upath)
75 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
77 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
85 outlen = strlen(upath);
87 if ((vol->v_casefold & AFPVOL_UTOMUPPER))
88 flags |= CONV_TOUPPER;
89 else if ((vol->v_casefold & AFPVOL_UTOMLOWER))
90 flags |= CONV_TOLOWER;
92 if ((vol->v_flags & AFPVOL_EILSEQ)) {
93 flags |= CONV__EILSEQ;
96 /* convert charsets */
97 if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset,
100 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
101 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
102 vol->v_volcodepage, vol->v_maccodepage, u);
110 Taken form afpd/desktop.c
112 static char *mtoupath(char *mpath)
114 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
123 if ( *mpath == '\0' ) {
127 /* set conversion flags */
128 if ((vol->v_casefold & AFPVOL_MTOUUPPER))
129 flags |= CONV_TOUPPER;
130 else if ((vol->v_casefold & AFPVOL_MTOULOWER))
131 flags |= CONV_TOLOWER;
133 if ((vol->v_flags & AFPVOL_EILSEQ)) {
134 flags |= CONV__EILSEQ;
143 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
146 m, inplen, u, outlen, &flags)) ) {
147 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
148 vol->v_volcodepage, mpath);
156 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
157 Returns pointer to name or NULL.
159 static const char *check_netatalk_dirs(const char *name)
163 for (c=0; netatalk_dirs[c]; c++) {
164 if ((strcmp(name, netatalk_dirs[c])) == 0)
165 return netatalk_dirs[c];
171 Check for special names
172 Returns pointer to name or NULL.
174 static const char *check_special_dirs(const char *name)
178 for (c=0; special_dirs[c]; c++) {
179 if ((strcmp(name, special_dirs[c])) == 0)
180 return special_dirs[c];
186 * We unCAPed a name, update CNID db
188 static int update_cnid(cnid_t did, const struct stat *sp, const char *oldname, const char *newname)
193 /* Query the database */
194 if ((id = cnid_lookup(vol->v_cdb, sp, did, (char *)oldname, strlen(oldname))) == CNID_INVALID)
195 /* not in db, no need to update */
198 /* Update the database */
199 if (cnid_update(vol->v_cdb, id, sp, did, (char *)newname, strlen(newname)) < 0)
206 Check for .AppleDouble file, create if missing
208 static int check_adfile(const char *fname, const struct stat *st, const char **newname)
211 int adflags = ADFLAGS_HF;
217 if (vol->v_adouble == AD_VERSION_EA) {
218 if (!(dbd_flags & DBD_FLAGS_V2TOEA))
220 if (ad_convert(fname, st, vol, newname) != 0) {
225 dbd_log(LOGSTD, "Conversion error for \"%s/%s\": %s", cwdbuf, fname, strerror(errno));
232 if (S_ISDIR(st->st_mode))
233 adflags |= ADFLAGS_DIR;
235 adname = vol->ad_path(fname, adflags);
237 if ((ret = access( adname, F_OK)) != 0) {
238 if (errno != ENOENT) {
239 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
240 cwdbuf, adname, strerror(errno));
243 /* Missing. Log and create it */
244 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
246 if (dbd_flags & DBD_FLAGS_SCAN)
247 /* Scan only requested, dont change anything */
253 if ((ret = ad_open(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 0) {
254 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
255 cwdbuf, adname, strerror(errno));
260 /* Set name in ad-file */
261 ad_setname(&ad, utompath((char *)fname));
263 ad_close(&ad, ADFLAGS_HF);
265 chown(adname, st->st_uid, st->st_gid);
266 /* FIXME: should we inherit mode too here ? */
268 chmod(adname, st->st_mode);
272 if (ad_open(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) {
273 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
276 ad_close(&ad, ADFLAGS_HF);
282 Remove all files with file::EA* from adouble dir
284 static void remove_eafiles(const char *name, struct ea *ea)
288 char eaname[MAXPATHLEN];
290 strlcpy(eaname, name, sizeof(eaname));
291 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
292 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
296 if ((chdir(ADv2_DIRNAME)) != 0) {
297 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
298 cwdbuf, ADv2_DIRNAME, strerror(errno));
302 if ((dp = opendir(".")) == NULL) {
303 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
304 cwdbuf, ADv2_DIRNAME, strerror(errno));
308 while ((ep = readdir(dp))) {
309 if (strstr(ep->d_name, eaname) != NULL) {
310 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
311 cwdbuf, ADv2_DIRNAME, ep->d_name);
312 if ((unlink(ep->d_name)) != 0) {
313 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
314 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
322 if ((chdir("..")) != 0) {
323 dbd_log(LOGSTD, "Couldn't chdir to '%s': %s", cwdbuf, strerror(errno));
324 /* we can't proceed */
325 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
330 Check Extended Attributes files
332 static int check_eafiles(const char *fname)
334 unsigned int count = 0;
340 if ((ret = ea_open(vol, fname, EA_RDWR, &ea)) != 0) {
343 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
344 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
345 remove_eafiles(fname, &ea);
350 while (count < ea.ea_count) {
351 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
354 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
356 if (lstat(eaname, &st) != 0) {
358 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
360 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
362 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
363 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
365 if ((unlink(eaname)) != 0)
366 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
367 cwdbuf, eaname, strerror(errno));
371 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
372 free((*ea.ea_entries)[count].ea_name);
373 (*ea.ea_entries)[count].ea_name = NULL;
384 Check for .AppleDouble folder and .Parent, create if missing
386 static int check_addir(int volroot)
388 int addir_ok, adpar_ok;
393 if (vol->v_adouble == AD_VERSION_EA)
396 /* Check for ad-dir */
397 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
398 if (errno != ENOENT) {
399 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
402 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
405 /* Check for ".Parent" */
406 if ( (adpar_ok = access(vol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
407 if (errno != ENOENT) {
408 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
409 cwdbuf, vol->ad_path(".", ADFLAGS_DIR), strerror(errno));
412 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
415 /* Is one missing ? */
416 if ((addir_ok != 0) || (adpar_ok != 0)) {
417 /* Yes, but are we only scanning ? */
418 if (dbd_flags & DBD_FLAGS_SCAN) {
419 /* Yes: missing .Parent is not a problem, but missing ad-dir
420 causes later checking of ad-files to fail. So we have to return appropiately */
423 else /* (adpar_ok != 0) */
427 /* Create ad dir and set name */
430 if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
431 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
435 /* Get basename of cwd from cwdbuf */
436 mname = utompath(strrchr(cwdbuf, '/') + 1);
438 /* Update name in ad file */
439 ad_setname(&ad, mname);
441 ad_close(&ad, ADFLAGS_HF);
443 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
444 if ((lstat(".", &st)) != 0) {
445 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
448 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
449 chown(vol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
456 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
458 0 = name is not an EA file
459 1 = name is an EA file and no problem was found
460 -1 = name is an EA file and data fork is gone
462 static int check_eafile_in_adouble(const char *name)
465 char *namep, *namedup = NULL;
467 /* Check if this is an AFPVOL_EA_AD vol */
468 if (vol->v_vfs_ea == AFPVOL_EA_AD) {
469 /* Does the filename contain "::EA" ? */
470 namedup = strdup(name);
471 if ((namep = strstr(namedup, "::EA")) == NULL) {
475 /* File contains "::EA" so it's an EA file. Check for data file */
477 /* Get string before "::EA" from EA filename */
479 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
481 if ((access( pname, F_OK)) == 0) {
486 if (errno != ENOENT) {
487 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
488 cwdbuf, name, strerror(errno));
492 /* Orphaned EA file*/
493 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
494 cwdbuf, ADv2_DIRNAME, name);
496 if (dbd_flags & DBD_FLAGS_SCAN)
497 /* Scan only requested, dont change anything */
500 if ((unlink(name)) != 0) {
501 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
502 cwdbuf, ADv2_DIRNAME, name);
506 } /* if AFPVOL_EA_AD */
516 Check files and dirs inside .AppleDouble folder:
517 - remove orphaned files
520 static int read_addir(void)
526 if ((chdir(ADv2_DIRNAME)) != 0) {
527 if (vol->v_adouble == AD_VERSION_EA) {
530 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
531 cwdbuf, ADv2_DIRNAME, strerror(errno));
535 if ((dp = opendir(".")) == NULL) {
536 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
537 cwdbuf, ADv2_DIRNAME, strerror(errno));
541 while ((ep = readdir(dp))) {
542 /* Check if its "." or ".." */
543 if (DIR_DOT_OR_DOTDOT(ep->d_name))
547 if (STRCMP(ep->d_name, ==, ".Parent"))
550 if ((lstat(ep->d_name, &st)) < 0) {
551 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
552 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
557 if (S_ISDIR(st.st_mode)) {
558 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
559 ep->d_name, cwdbuf, ADv2_DIRNAME);
563 /* Check if for orphaned and corrupt Extended Attributes file */
564 if (check_eafile_in_adouble(ep->d_name) != 0)
567 /* Check for data file */
568 strcpy(pname + 3, ep->d_name);
569 if ((access( pname, F_OK)) != 0) {
570 if (errno != ENOENT) {
571 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
572 cwdbuf, pname, strerror(errno));
575 /* Orphaned ad-file*/
576 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
577 cwdbuf, ADv2_DIRNAME, ep->d_name);
579 if (dbd_flags & DBD_FLAGS_SCAN)
580 /* Scan only requested, dont change anything */
583 if ((unlink(ep->d_name)) != 0) {
584 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
585 cwdbuf, ADv2_DIRNAME, ep->d_name);
591 if ((chdir("..")) != 0) {
592 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
593 cwdbuf, strerror(errno));
594 /* This really is EOT! */
595 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
604 Check CNID for a file/dir, both from db and from ad-file.
605 For detailed specs see intro.
607 @return Correct CNID of object or CNID_INVALID (ie 0) on error
609 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok)
611 int ret, adflags = ADFLAGS_HF;
612 cnid_t db_cnid, ad_cnid, tmpid;
615 adflags = ADFLAGS_HF | (S_ISDIR(st->st_mode) ? ADFLAGS_DIR : 0);
617 /* Get CNID from ad-file */
618 ad_cnid = CNID_INVALID;
621 if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
623 if (vol->v_adouble != AD_VERSION_EA) {
624 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
627 dbd_log( LOGDEBUG, "File without meta EA: \"%s/%s\"", cwdbuf, name);
630 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
631 if (ad_cnid == CNID_INVALID)
632 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
634 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
635 ad_close(&ad, ADFLAGS_HF);
639 /* Get CNID from database */
640 if ((db_cnid = cnid_add(vol->v_cdb, st, did, (char *)name, strlen(name), ad_cnid)) == CNID_INVALID)
643 /* Compare CNID from db and adouble file */
644 if (ad_cnid != db_cnid && adfile_ok == 0) {
645 /* Mismatch, overwrite ad file with value from db */
646 dbd_log(LOGSTD, "CNID mismatch for '%s/%s', CNID db: %u, ad-file: %u",
647 cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
649 if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
650 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
651 cwdbuf, name, strerror(errno));
654 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
656 ad_close(&ad, ADFLAGS_HF);
663 This is called recursively for all dirs.
664 volroot=1 means we're in the volume root dir, 0 means we aren't.
665 We use this when checking for netatalk private folders like .AppleDB.
666 did is our parents CNID.
668 static int dbd_readdir(int volroot, cnid_t did)
670 int cwd, ret = 0, adfile_ok, addir_ok, encoding_ok;
675 static struct stat st; /* Save some stack space */
677 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
678 if ((addir_ok = check_addir(volroot)) != 0)
679 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
680 /* Fatal on rebuild run, continue if only scanning ! */
683 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
685 if ((read_addir()) != 0)
686 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
687 /* Fatal on rebuild run, continue if only scanning ! */
690 if ((dp = opendir (".")) == NULL) {
691 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
695 while ((ep = readdir (dp))) {
696 /* Check if we got a termination signal */
698 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
700 /* Check if its "." or ".." */
701 if (DIR_DOT_OR_DOTDOT(ep->d_name))
704 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
705 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
707 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
711 /* Check for special folders in volume root e.g. ".zfs" */
713 if ((name = check_special_dirs(ep->d_name)) != NULL) {
714 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
719 /* Skip .AppleDouble dir in this loop */
720 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
723 if (!vol->vfs->vfs_validupath(vol, ep->d_name)) {
724 dbd_log(LOGDEBUG, "Ignoring \"%s\"", ep->d_name);
728 if ((ret = lstat(ep->d_name, &st)) < 0) {
729 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
730 cwdbuf, ep->d_name, strerror(errno));
734 switch (st.st_mode & S_IFMT) {
739 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
742 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
743 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
744 if ((unlink(ep->d_name)) != 0) {
745 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
751 /**************************************************************************
753 **************************************************************************/
754 static unsigned long long statcount = 0;
761 if ((statcount % 10000) == 0) {
762 if (dbd_flags & DBD_FLAGS_STATS)
763 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
764 statcount, (unsigned long long)(time(NULL) - t));
767 /**************************************************************************
769 **************************************************************************/
771 /* Check for appledouble file, create if missing, but only if we have addir */
772 const char *name = NULL;
775 adfile_ok = check_adfile(ep->d_name, &st, &name);
780 update_cnid(did, &st, ep->d_name, name);
784 cnid = check_cnid(name, did, &st, adfile_ok);
786 /* Now add this object to our rebuild dbd */
787 if (cnid && dbd_rebuild) {
788 static uint count = 0;
789 rqst.cnid = rply.cnid;
790 ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
791 if (dbif_txn_close(dbd_rebuild, ret) != 0)
793 if (rply.result != CNID_DBD_RES_OK) {
794 dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
799 if (count == 10000) {
800 if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
801 dbd_log(LOGSTD, "Error checkpointing!");
809 if (vol->v_vfs_ea == AFPVOL_EA_AD)
812 /**************************************************************************
814 **************************************************************************/
815 if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant enter recursion */
817 strcat(cwdbuf, name);
818 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
819 if (-1 == (cwd = open(".", O_RDONLY))) {
820 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
823 if (0 != chdir(name)) {
824 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
829 ret = dbd_readdir(0, cnid);
833 *(strrchr(cwdbuf, '/')) = 0;
840 Use results of previous checks
842 if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
843 if (rmdir(ADv2_DIRNAME) != 0) {
848 dbd_log(LOGSTD, "Error removing adouble dir \"%s/%s\": %s", cwdbuf, ADv2_DIRNAME, strerror(errno));
858 Main func called from cmd_dbd.c
860 int cmd_dbd_scanvol(struct vol *vol_in, dbd_flags_t flags)
865 /* Run with umask 0 */
868 /* Make vol accessible for all funcs */
872 /* We only support unicode volumes ! */
873 if (vol->v_volcharset != CH_UTF8) {
874 dbd_log(LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8);
879 * Get CNID database stamp, cnid_getstamp() passes the buffer,
880 * then cnid_resolve() actually gets the value from the db
882 cnid_getstamp(vol->v_cdb, stamp, sizeof(stamp));
884 if (setjmp(jmp) != 0) {
885 EC_EXIT_STATUS(0); /* Got signal, jump from dbd_readdir */
888 strcpy(cwdbuf, vol->v_path);
891 if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) {
892 if (lstat(".", &st) != 0)
894 if (ad_convert(".", &st, vol, NULL) != 0) {
899 dbd_log(LOGSTD, "Conversion error for \"%s\": %s", vol->v_path, strerror(errno));
905 /* Start recursion */
906 EC_NEG1( dbd_readdir(1, htonl(2)) ); /* 2 = volumeroot CNID */