2 $Id: cmd_dbd_scanvol.c,v 1.15 2009-12-21 06:41:09 franklahm Exp $
4 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
19 #endif /* HAVE_CONFIG_H */
23 #include <sys/types.h>
31 #include <atalk/adouble.h>
32 #include <atalk/unicode.h>
33 #include <atalk/volinfo.h>
34 #include <atalk/cnid_dbd_private.h>
35 #include <atalk/volume.h>
37 #include <atalk/util.h>
44 /* Some defines to ease code parsing */
45 #define ADDIR_OK (addir_ok == 0)
46 #define ADFILE_OK (adfile_ok == 0)
48 /* These must be accessible for cmd_dbd_* funcs */
49 struct volinfo *volinfo;
50 char cwdbuf[MAXPATHLEN+1];
52 /* Some static vars */
54 static DBD *dbd_rebuild;
55 static dbd_flags_t dbd_flags;
56 static char stamp[CNID_DEV_LEN];
57 static char *netatalk_dirs[] = {
62 static struct cnid_dbd_rqst rqst;
63 static struct cnid_dbd_rply rply;
65 static struct vol volume; /* fake it for ea_open */
66 static char pname[MAXPATHLEN] = "../";
69 Taken form afpd/desktop.c
71 static char *utompath(char *upath)
73 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
75 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
83 outlen = strlen(upath);
85 if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
86 flags |= CONV_TOUPPER;
87 else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
88 flags |= CONV_TOLOWER;
90 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
91 flags |= CONV__EILSEQ;
94 /* convert charsets */
95 if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
97 volinfo->v_maccharset,
98 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
99 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
100 volinfo->v_volcodepage, volinfo->v_maccodepage, u);
108 Taken form afpd/desktop.c
110 static char *mtoupath(char *mpath)
112 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
121 if ( *mpath == '\0' ) {
125 /* set conversion flags */
126 if (!(volinfo->v_flags & AFPVOL_NOHEX))
127 flags |= CONV_ESCAPEHEX;
128 if (!(volinfo->v_flags & AFPVOL_USEDOTS))
129 flags |= CONV_ESCAPEDOTS;
131 if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
132 flags |= CONV_TOUPPER;
133 else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
134 flags |= CONV_TOLOWER;
136 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
137 flags |= CONV__EILSEQ;
146 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
147 volinfo->v_volcharset,
148 volinfo->v_maccharset,
149 m, inplen, u, outlen, &flags)) ) {
150 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
151 volinfo->v_volcodepage, mpath);
159 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
160 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
162 static int check_name_encoding(char *uname)
166 roundtripped = mtoupath(utompath(uname));
168 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
172 if ( STRCMP(uname, !=, roundtripped)) {
173 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
181 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
182 Returns pointer to name or NULL.
184 static const char *check_netatalk_dirs(const char *name)
188 for (c=0; netatalk_dirs[c]; c++) {
189 if ((strcmp(name, netatalk_dirs[c])) == 0)
190 return netatalk_dirs[c];
196 Check for .AppleDouble file, create if missing
198 static int check_adfile(const char *fname, const struct stat *st)
204 if (S_ISREG(st->st_mode))
207 adflags = ADFLAGS_DIR;
209 adname = volinfo->ad_path(fname, adflags);
211 if ((ret = access( adname, F_OK)) != 0) {
212 if (errno != ENOENT) {
213 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
214 cwdbuf, adname, strerror(errno));
217 /* Missing. Log and create it */
218 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
220 if (dbd_flags & DBD_FLAGS_SCAN)
221 /* Scan only requested, dont change anything */
225 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
227 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
228 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
229 cwdbuf, adname, strerror(errno));
234 /* Set name in ad-file */
235 ad_setname(&ad, utompath((char *)fname));
237 ad_close_metadata(&ad);
239 chown(adname, st->st_uid, st->st_gid);
240 /* FIXME: should we inherit mode too here ? */
242 chmod(adname, st->st_mode);
245 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
246 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
247 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
250 ad_close_metadata(&ad);
256 Remove all files with file::EA* from adouble dir
258 static void remove_eafiles(const char *name, struct ea *ea)
262 char eaname[MAXPATHLEN];
264 strlcpy(eaname, name, sizeof(eaname));
265 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
266 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
270 if ((chdir(ADv2_DIRNAME)) != 0) {
271 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
272 cwdbuf, ADv2_DIRNAME, strerror(errno));
276 if ((dp = opendir(".")) == NULL) {
277 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
278 cwdbuf, ADv2_DIRNAME, strerror(errno));
282 while ((ep = readdir(dp))) {
283 if (strstr(ep->d_name, eaname) != NULL) {
284 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
285 cwdbuf, ADv2_DIRNAME, ep->d_name);
286 if ((unlink(ep->d_name)) != 0) {
287 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
288 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
299 Check Extended Attributes files
301 static int check_eafiles(const char *fname)
303 unsigned int count = 0;
309 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
312 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files",
314 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
315 remove_eafiles(fname, &ea);
320 while (count < ea.ea_count) {
321 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
324 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
326 if (stat(eaname, &st) != 0) {
328 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
330 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
332 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
333 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
335 if ((unlink(eaname)) != 0)
336 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
337 cwdbuf, eaname, strerror(errno));
341 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
342 free((*ea.ea_entries)[count].ea_name);
343 (*ea.ea_entries)[count].ea_name = NULL;
354 Check for .AppleDouble folder and .Parent, create if missing
356 static int check_addir(int volroot)
358 int addir_ok, adpar_ok;
363 /* Check for ad-dir */
364 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
365 if (errno != ENOENT) {
366 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
369 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
372 /* Check for ".Parent" */
373 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
374 if (errno != ENOENT) {
375 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
376 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
379 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
382 /* Is one missing ? */
383 if ((addir_ok != 0) || (adpar_ok != 0)) {
384 /* Yes, but are we only scanning ? */
385 if (dbd_flags & DBD_FLAGS_SCAN) {
386 /* Yes: missing .Parent is not a problem, but missing ad-dir
387 causes later checking of ad-files to fail. So we have to return appropiately */
390 else /* (adpar_ok != 0) */
394 /* Create ad dir and set name */
395 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
397 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
398 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
402 /* Get basename of cwd from cwdbuf */
403 mname = utompath(strrchr(cwdbuf, '/') + 1);
405 /* Update name in ad file */
406 ad_setname(&ad, mname);
408 ad_close_metadata(&ad);
410 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
411 if ((stat(".", &st)) != 0) {
412 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
415 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
416 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
423 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
425 0 = name is not an EA file
426 1 = name is an EA file and no problem was found
427 -1 = name is an EA file and data fork is gone
429 static int check_eafile_in_adouble(const char *name)
432 char *namep, *namedup = NULL;
434 /* Check if this is an AFPVOL_EA_AD vol */
435 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
436 /* Does the filename contain "::EA" ? */
437 namedup = strdup(name);
438 if ((namep = strstr(namedup, "::EA")) == NULL) {
442 /* File contains "::EA" so it's an EA file. Check for data file */
444 /* Get string before "::EA" from EA filename */
446 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
448 if ((access( pname, F_OK)) == 0) {
453 if (errno != ENOENT) {
454 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
455 cwdbuf, name, strerror(errno));
459 /* Orphaned EA file*/
460 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
461 cwdbuf, ADv2_DIRNAME, name);
463 if (dbd_flags & DBD_FLAGS_SCAN)
464 /* Scan only requested, dont change anything */
467 if ((unlink(name)) != 0) {
468 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
469 cwdbuf, ADv2_DIRNAME, name);
473 } /* if AFPVOL_EA_AD */
483 Check files and dirs inside .AppleDouble folder:
484 - remove orphaned files
487 static int read_addir(void)
493 if ((chdir(ADv2_DIRNAME)) != 0) {
494 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
495 cwdbuf, ADv2_DIRNAME, strerror(errno));
499 if ((dp = opendir(".")) == NULL) {
500 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
501 cwdbuf, ADv2_DIRNAME, strerror(errno));
505 while ((ep = readdir(dp))) {
506 /* Check if its "." or ".." */
507 if (DIR_DOT_OR_DOTDOT(ep->d_name))
510 if (STRCMP(ep->d_name, ==, ".Parent"))
513 if ((stat(ep->d_name, &st)) < 0) {
514 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
515 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
520 if (S_ISDIR(st.st_mode)) {
521 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
522 ep->d_name, cwdbuf, ADv2_DIRNAME);
526 /* Check if for orphaned and corrupt Extended Attributes file */
527 if (check_eafile_in_adouble(ep->d_name) != 0)
530 /* Check for data file */
531 strcpy(pname + 3, ep->d_name);
532 if ((access( pname, F_OK)) != 0) {
533 if (errno != ENOENT) {
534 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
535 cwdbuf, pname, strerror(errno));
538 /* Orphaned ad-file*/
539 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
540 cwdbuf, ADv2_DIRNAME, ep->d_name);
542 if (dbd_flags & DBD_FLAGS_SCAN)
543 /* Scan only requested, dont change anything */
546 if ((unlink(ep->d_name)) != 0) {
547 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
548 cwdbuf, ADv2_DIRNAME, ep->d_name);
554 if ((chdir("..")) != 0) {
555 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
556 cwdbuf, strerror(errno));
557 /* This really is EOT! */
558 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
567 Check CNID for a file/dir, both from db and from ad-file.
568 For detailed specs see intro.
570 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
573 cnid_t db_cnid, ad_cnid;
576 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
578 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
579 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
580 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
581 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
585 if (dbd_flags & DBD_FLAGS_FORCE) {
586 ad_cnid = ad_forcegetid(&ad);
587 /* This ensures the changed stamp is written */
588 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
592 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
595 dbd_log( LOGSTD, "Incorrect CNID data in .AppleDouble data for '%s/%s' (bad stamp?)", cwdbuf, name);
597 ad_close_metadata(&ad);
600 /* Get CNID from database */
602 /* Prepare request data */
603 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
604 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
607 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
608 rqst.dev = st->st_dev;
609 rqst.ino = st->st_ino;
610 rqst.type = S_ISDIR(st->st_mode)?1:0;
611 rqst.name = (char *)name;
612 rqst.namelen = strlen(name);
614 /* Query the database */
615 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
616 dbif_txn_close(dbd, ret);
617 if (rply.result == CNID_DBD_RES_OK) {
619 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
620 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
621 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
624 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
628 /* Compare results from both CNID searches */
629 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
630 /* Everything is fine */
632 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
633 /* Mismatch ? Delete both from db and re-add data from file */
634 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
635 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
637 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
638 dbif_txn_close(dbd, ret);
641 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
642 dbif_txn_close(dbd, ret);
644 ret = dbd_rebuild_add(dbd, &rqst, &rply);
645 dbif_txn_close(dbd, ret);
648 } else if (ad_cnid && (db_cnid == 0)) {
649 /* in ad-file but not in db */
650 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
651 dbd_log( LOGDEBUG, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
653 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
654 dbif_txn_close(dbd, ret);
655 ret = dbd_rebuild_add(dbd, &rqst, &rply);
656 dbif_txn_close(dbd, ret);
659 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
660 /* No CNID at all, we clearly have to allocate a fresh one... */
661 /* Note: the next test will use this new CNID too! */
662 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
664 ret = dbd_add(dbd, &rqst, &rply, 1);
665 dbif_txn_close(dbd, ret);
667 dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
671 if ((ad_cnid == 0) && db_cnid) {
672 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
673 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
674 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
675 dbd_log( LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", cwdbuf, name, ntohl(db_cnid));
676 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
677 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
678 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
681 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
683 ad_close_metadata(&ad);
693 This is called recursively for all dirs.
694 volroot=1 means we're in the volume root dir, 0 means we aren't.
695 We use this when checking for netatalk private folders like .AppleDB.
696 did is our parents CNID.
698 static int dbd_readdir(int volroot, cnid_t did)
700 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
705 static struct stat st; /* Save some stack space */
707 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
708 if ((addir_ok = check_addir(volroot)) != 0)
709 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
710 /* Fatal on rebuild run, continue if only scanning ! */
713 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
715 if ((read_addir()) != 0)
716 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
717 /* Fatal on rebuild run, continue if only scanning ! */
720 if ((dp = opendir (".")) == NULL) {
721 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
725 while ((ep = readdir (dp))) {
726 /* Check if we got a termination signal */
728 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
730 /* Check if its "." or ".." */
731 if (DIR_DOT_OR_DOTDOT(ep->d_name))
734 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
735 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
737 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
741 /* Skip .AppleDouble dir in this loop */
742 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
745 if ((ret = stat(ep->d_name, &st)) < 0) {
746 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
749 if (S_ISREG(st.st_mode))
752 adflags = ADFLAGS_DIR;
754 /**************************************************************************
756 **************************************************************************/
759 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
760 /* If its a file: skipp all other tests now ! */
761 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
765 /* Check for appledouble file, create if missing, but only if we have addir */
768 adfile_ok = check_adfile(ep->d_name, &st);
772 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
774 /* Now add this object to our rebuild dbd */
776 rqst.cnid = rply.cnid;
777 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
778 if (rply.result != CNID_DBD_RES_OK) {
779 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
780 cnid, cwdbuf, ep->d_name);
781 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
787 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
788 check_eafiles(ep->d_name);
790 /**************************************************************************
792 **************************************************************************/
793 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
795 strcat(cwdbuf, ep->d_name);
796 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
797 if (-1 == (cwd = open(".", O_RDONLY))) {
798 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
801 if (0 != chdir(ep->d_name)) {
802 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
807 ret = dbd_readdir(0, cnid);
811 *(strrchr(cwdbuf, '/')) = 0;
818 Use results of previous checks
825 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
827 /* Dont scanvol on no-appledouble vols */
828 if (vi->v_flags & AFPVOL_NOADOUBLE) {
829 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
833 /* Make this stuff accessible from all funcs easily */
837 /* Init a fake struct vol with just enough so we can call ea_open and friends */
838 volume.v_adouble = AD_VERSION2;
839 volume.v_vfs_ea = volinfo->v_vfs_ea;
840 initvol_vfs(&volume);
842 /* Run with umask 0 */
846 strcpy(cwdbuf, volinfo->v_path);
847 if (cwdbuf[strlen(cwdbuf) - 1] == '/')
848 cwdbuf[strlen(cwdbuf) - 1] = 0;
849 chdir(volinfo->v_path);
851 /* Start recursion */
852 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
859 Remove all CNIDs from dbd that are not in dbd_rebuild
861 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
863 int ret, deleted = 0;
864 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
865 struct cnid_dbd_rqst rqst;
866 struct cnid_dbd_rply rply;
868 /* jump over rootinfo key */
869 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
871 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
874 /* Get first id from dbd_rebuild */
875 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
878 /* Start main loop through dbd: get CNID from dbd */
879 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
883 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
884 dbd_log(LOGSTD, "Error checkpointing!");
889 /* This should be the normal case: CNID is in both dbs */
890 if (dbd_cnid == rebuild_cnid) {
891 /* Get next CNID from rebuild db */
892 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
895 } else if (ret == 0) {
896 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
897 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
898 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
899 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
900 rqst.cnid = htonl(dbd_cnid);
901 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
902 dbif_txn_close(dbd, ret);
908 /* Normal case (ret=1): continue while loop */
912 if (dbd_cnid < rebuild_cnid) {
913 /* CNID is orphaned -> delete */
914 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
915 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
916 rqst.cnid = htonl(dbd_cnid);
917 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
918 dbif_txn_close(dbd, ret);
924 if (dbd_cnid > rebuild_cnid) {
925 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
926 dbif_dump(dbd_rebuild, 0);
927 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
928 dbif_txn_close(dbd, ret);
929 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
930 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
936 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
937 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
942 Main func called from cmd_dbd.c
944 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
947 struct db_param db_param = { 0 };
949 /* Set cachesize for in-memory rebuild db */
950 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
952 /* Make it accessible for all funcs */
955 /* We only support unicode volumes ! */
956 if ( volinfo->v_volcharset != CH_UTF8) {
957 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
962 /* Get volume stamp */
963 dbd_getstamp(dbd, &rqst, &rply);
964 if (rply.result != CNID_DBD_RES_OK)
966 memcpy(stamp, rply.name, CNID_DEV_LEN);
968 /* open/create rebuild dbd, copy rootinfo key */
969 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
971 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
973 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
977 if (setjmp(jmp) != 0)
978 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
981 if ( (scanvol(volinfo, flags)) != 0)
985 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
986 other clients in between our pass 1 and 2 */
987 if (flags & DBD_FLAGS_EXCL)
988 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
993 dbif_close(dbd_rebuild);