2 $Id: cmd_dbd_scanvol.c,v 1.21 2010-04-11 07:01:23 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);
160 Check if "name" is pointing to
161 a) an object inside the current volume (return 0)
162 b) an object outside the current volume (return 1)
163 Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags
164 Return -1 on any serious error.
166 static int check_symlink(const char *name, int *adflags)
170 char pathbuf[MAXPATHLEN + 1];
174 if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) {
175 dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s",
176 cwdbuf, name, strerror(errno));
181 if ((stat(pathbuf, &st)) != 0) {
182 dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno));
186 if ((cwd = open(".", O_RDONLY)) < 0) {
187 dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno));
191 if (S_ISDIR(st.st_mode)) {
192 *adflags |= ADFLAGS_DIR;
194 /* get basename from path */
195 if ((sep = strrchr(pathbuf, '/')) == NULL)
196 /* just a file at the same level */
198 sep = 0; /* pathbuf now contains the directory*/
201 /* fchdir() to pathbuf so we can easily get its path with getcwd() */
202 if ((chdir(pathbuf)) != 0) {
203 dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno));
207 if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) {
208 dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno));
209 if ((fchdir(cwd)) != 0)
211 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
215 if ((fchdir(cwd)) != 0)
217 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
220 We now have the symlink target dir as absoulte path in pathbuf
221 and can compare it with the currents volume path
224 while (volinfo->v_path[i]) {
225 if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
226 dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
232 dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name);
238 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
239 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
241 static int check_name_encoding(char *uname)
245 roundtripped = mtoupath(utompath(uname));
247 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
251 if ( STRCMP(uname, !=, roundtripped)) {
252 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
260 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
261 Returns pointer to name or NULL.
263 static const char *check_netatalk_dirs(const char *name)
267 for (c=0; netatalk_dirs[c]; c++) {
268 if ((strcmp(name, netatalk_dirs[c])) == 0)
269 return netatalk_dirs[c];
275 Check for .AppleDouble file, create if missing
277 static int check_adfile(const char *fname, const struct stat *st)
283 if (dbd_flags & DBD_FLAGS_CLEANUP)
286 if (S_ISREG(st->st_mode))
289 adflags = ADFLAGS_DIR;
291 adname = volinfo->ad_path(fname, adflags);
293 if ((ret = access( adname, F_OK)) != 0) {
294 if (errno != ENOENT) {
295 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
296 cwdbuf, adname, strerror(errno));
299 /* Missing. Log and create it */
300 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
302 if (dbd_flags & DBD_FLAGS_SCAN)
303 /* Scan only requested, dont change anything */
307 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
309 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
310 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
311 cwdbuf, adname, strerror(errno));
316 /* Set name in ad-file */
317 ad_setname(&ad, utompath((char *)fname));
319 ad_close_metadata(&ad);
321 chown(adname, st->st_uid, st->st_gid);
322 /* FIXME: should we inherit mode too here ? */
324 chmod(adname, st->st_mode);
327 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
328 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
329 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
332 ad_close_metadata(&ad);
338 Remove all files with file::EA* from adouble dir
340 static void remove_eafiles(const char *name, struct ea *ea)
344 char eaname[MAXPATHLEN];
346 strlcpy(eaname, name, sizeof(eaname));
347 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
348 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
352 if ((chdir(ADv2_DIRNAME)) != 0) {
353 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
354 cwdbuf, ADv2_DIRNAME, strerror(errno));
358 if ((dp = opendir(".")) == NULL) {
359 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
360 cwdbuf, ADv2_DIRNAME, strerror(errno));
364 while ((ep = readdir(dp))) {
365 if (strstr(ep->d_name, eaname) != NULL) {
366 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
367 cwdbuf, ADv2_DIRNAME, ep->d_name);
368 if ((unlink(ep->d_name)) != 0) {
369 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
370 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
381 Check Extended Attributes files
383 static int check_eafiles(const char *fname)
385 unsigned int count = 0;
391 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
394 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
395 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
396 remove_eafiles(fname, &ea);
401 while (count < ea.ea_count) {
402 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
405 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
407 if (lstat(eaname, &st) != 0) {
409 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
411 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
413 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
414 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
416 if ((unlink(eaname)) != 0)
417 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
418 cwdbuf, eaname, strerror(errno));
422 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
423 free((*ea.ea_entries)[count].ea_name);
424 (*ea.ea_entries)[count].ea_name = NULL;
435 Check for .AppleDouble folder and .Parent, create if missing
437 static int check_addir(int volroot)
439 int addir_ok, adpar_ok;
444 if (dbd_flags & DBD_FLAGS_CLEANUP)
447 /* Check for ad-dir */
448 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
449 if (errno != ENOENT) {
450 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
453 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
456 /* Check for ".Parent" */
457 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
458 if (errno != ENOENT) {
459 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
460 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
463 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
466 /* Is one missing ? */
467 if ((addir_ok != 0) || (adpar_ok != 0)) {
468 /* Yes, but are we only scanning ? */
469 if (dbd_flags & DBD_FLAGS_SCAN) {
470 /* Yes: missing .Parent is not a problem, but missing ad-dir
471 causes later checking of ad-files to fail. So we have to return appropiately */
474 else /* (adpar_ok != 0) */
478 /* Create ad dir and set name */
479 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
481 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
482 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
486 /* Get basename of cwd from cwdbuf */
487 mname = utompath(strrchr(cwdbuf, '/') + 1);
489 /* Update name in ad file */
490 ad_setname(&ad, mname);
492 ad_close_metadata(&ad);
494 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
495 if ((lstat(".", &st)) != 0) {
496 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
499 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
500 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
507 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
509 0 = name is not an EA file
510 1 = name is an EA file and no problem was found
511 -1 = name is an EA file and data fork is gone
513 static int check_eafile_in_adouble(const char *name)
516 char *namep, *namedup = NULL;
518 /* Check if this is an AFPVOL_EA_AD vol */
519 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
520 /* Does the filename contain "::EA" ? */
521 namedup = strdup(name);
522 if ((namep = strstr(namedup, "::EA")) == NULL) {
526 /* File contains "::EA" so it's an EA file. Check for data file */
528 /* Get string before "::EA" from EA filename */
530 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
532 if ((access( pname, F_OK)) == 0) {
537 if (errno != ENOENT) {
538 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
539 cwdbuf, name, strerror(errno));
543 /* Orphaned EA file*/
544 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
545 cwdbuf, ADv2_DIRNAME, name);
547 if (dbd_flags & DBD_FLAGS_SCAN)
548 /* Scan only requested, dont change anything */
551 if ((unlink(name)) != 0) {
552 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
553 cwdbuf, ADv2_DIRNAME, name);
557 } /* if AFPVOL_EA_AD */
567 Check files and dirs inside .AppleDouble folder:
568 - remove orphaned files
571 static int read_addir(void)
577 if ((chdir(ADv2_DIRNAME)) != 0) {
578 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
579 cwdbuf, ADv2_DIRNAME, strerror(errno));
583 if ((dp = opendir(".")) == NULL) {
584 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
585 cwdbuf, ADv2_DIRNAME, strerror(errno));
589 while ((ep = readdir(dp))) {
590 /* Check if its "." or ".." */
591 if (DIR_DOT_OR_DOTDOT(ep->d_name))
594 if (STRCMP(ep->d_name, ==, ".Parent"))
597 if ((lstat(ep->d_name, &st)) < 0) {
598 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
599 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
604 if (S_ISDIR(st.st_mode)) {
605 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
606 ep->d_name, cwdbuf, ADv2_DIRNAME);
610 /* Check if for orphaned and corrupt Extended Attributes file */
611 if (check_eafile_in_adouble(ep->d_name) != 0)
614 /* Check for data file */
615 strcpy(pname + 3, ep->d_name);
616 if ((access( pname, F_OK)) != 0) {
617 if (errno != ENOENT) {
618 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
619 cwdbuf, pname, strerror(errno));
622 /* Orphaned ad-file*/
623 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
624 cwdbuf, ADv2_DIRNAME, ep->d_name);
626 if (dbd_flags & DBD_FLAGS_SCAN)
627 /* Scan only requested, dont change anything */
630 if ((unlink(ep->d_name)) != 0) {
631 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
632 cwdbuf, ADv2_DIRNAME, ep->d_name);
638 if ((chdir("..")) != 0) {
639 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
640 cwdbuf, strerror(errno));
641 /* This really is EOT! */
642 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
651 Check CNID for a file/dir, both from db and from ad-file.
652 For detailed specs see intro.
654 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
657 cnid_t db_cnid, ad_cnid;
660 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
662 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
663 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
664 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
666 if (dbd_flags & DBD_FLAGS_CLEANUP)
669 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
673 if (dbd_flags & DBD_FLAGS_FORCE) {
674 ad_cnid = ad_forcegetid(&ad);
675 /* This ensures the changed stamp is written */
676 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
680 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
683 dbd_log( LOGSTD, "Incorrect CNID data in .AppleDouble data for '%s/%s' (bad stamp?)", cwdbuf, name);
685 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
687 ad_close_metadata(&ad);
690 /* Get CNID from database */
692 /* Prepare request data */
693 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
694 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
697 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
698 rqst.dev = st->st_dev;
699 rqst.ino = st->st_ino;
700 rqst.type = S_ISDIR(st->st_mode)?1:0;
701 rqst.name = (char *)name;
702 rqst.namelen = strlen(name);
704 /* Query the database */
705 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
706 dbif_txn_close(dbd, ret);
707 if (rply.result == CNID_DBD_RES_OK) {
709 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
710 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
711 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
714 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
718 /* Compare results from both CNID searches */
719 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
720 /* Everything is fine */
722 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
723 /* Mismatch ? Delete both from db and re-add data from file */
724 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
725 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
727 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
728 dbif_txn_close(dbd, ret);
731 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
732 dbif_txn_close(dbd, ret);
734 ret = dbd_rebuild_add(dbd, &rqst, &rply);
735 dbif_txn_close(dbd, ret);
738 } else if (ad_cnid && (db_cnid == 0)) {
739 /* in ad-file but not in db */
740 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
741 dbd_log( LOGDEBUG, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
743 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
744 dbif_txn_close(dbd, ret);
745 ret = dbd_rebuild_add(dbd, &rqst, &rply);
746 dbif_txn_close(dbd, ret);
749 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
750 /* No CNID at all, we clearly have to allocate a fresh one... */
751 /* Note: the next test will use this new CNID too! */
752 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
754 ret = dbd_add(dbd, &rqst, &rply, 1);
755 dbif_txn_close(dbd, ret);
757 dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
761 if ((ad_cnid == 0) && db_cnid) {
762 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
763 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
764 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
765 dbd_log( LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", cwdbuf, name, ntohl(db_cnid));
766 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
767 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
768 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
771 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
773 ad_close_metadata(&ad);
783 This is called recursively for all dirs.
784 volroot=1 means we're in the volume root dir, 0 means we aren't.
785 We use this when checking for netatalk private folders like .AppleDB.
786 did is our parents CNID.
788 static int dbd_readdir(int volroot, cnid_t did)
790 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
795 static struct stat st; /* Save some stack space */
797 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
798 if ((addir_ok = check_addir(volroot)) != 0)
799 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
800 /* Fatal on rebuild run, continue if only scanning ! */
803 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
805 if ((read_addir()) != 0)
806 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
807 /* Fatal on rebuild run, continue if only scanning ! */
810 if ((dp = opendir (".")) == NULL) {
811 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
815 while ((ep = readdir (dp))) {
816 /* Check if we got a termination signal */
818 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
820 /* Check if its "." or ".." */
821 if (DIR_DOT_OR_DOTDOT(ep->d_name))
824 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
825 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
827 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
831 /* Skip .AppleDouble dir in this loop */
832 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
835 if ((ret = lstat(ep->d_name, &st)) < 0) {
836 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
840 switch (st.st_mode & S_IFMT) {
845 adflags = ADFLAGS_DIR;
848 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
850 ret = check_symlink(ep->d_name, &adflags);
854 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
858 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
859 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
860 if ((unlink(ep->d_name)) != 0) {
861 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
867 /**************************************************************************
869 **************************************************************************/
872 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
873 /* If its a file: skipp all other tests now ! */
874 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
878 /* Check for appledouble file, create if missing, but only if we have addir */
881 adfile_ok = check_adfile(ep->d_name, &st);
885 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
887 /* Now add this object to our rebuild dbd */
889 rqst.cnid = rply.cnid;
890 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
891 if (rply.result != CNID_DBD_RES_OK) {
892 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
893 cnid, cwdbuf, ep->d_name);
894 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
900 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
901 check_eafiles(ep->d_name);
903 /**************************************************************************
905 **************************************************************************/
906 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
908 strcat(cwdbuf, ep->d_name);
909 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
910 if (-1 == (cwd = open(".", O_RDONLY))) {
911 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
914 if (0 != chdir(ep->d_name)) {
915 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
920 ret = dbd_readdir(0, cnid);
924 *(strrchr(cwdbuf, '/')) = 0;
931 Use results of previous checks
938 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
940 /* Dont scanvol on no-appledouble vols */
941 if (vi->v_flags & AFPVOL_NOADOUBLE) {
942 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
946 /* Make this stuff accessible from all funcs easily */
950 /* Init a fake struct vol with just enough so we can call ea_open and friends */
951 volume.v_adouble = AD_VERSION2;
952 volume.v_vfs_ea = volinfo->v_vfs_ea;
953 initvol_vfs(&volume);
955 /* Run with umask 0 */
958 /* Remove trailing slash from volume, chdir to vol */
959 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
960 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
961 strcpy(cwdbuf, volinfo->v_path);
962 chdir(volinfo->v_path);
964 /* Start recursion */
965 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
972 Remove all CNIDs from dbd that are not in dbd_rebuild
974 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
976 int ret, deleted = 0;
977 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
978 struct cnid_dbd_rqst rqst;
979 struct cnid_dbd_rply rply;
981 /* jump over rootinfo key */
982 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
984 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
987 /* Get first id from dbd_rebuild */
988 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
991 /* Start main loop through dbd: get CNID from dbd */
992 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
996 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
997 dbd_log(LOGSTD, "Error checkpointing!");
1002 /* This should be the normal case: CNID is in both dbs */
1003 if (dbd_cnid == rebuild_cnid) {
1004 /* Get next CNID from rebuild db */
1005 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1008 } else if (ret == 0) {
1009 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1010 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1011 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1012 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1013 rqst.cnid = htonl(dbd_cnid);
1014 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1015 dbif_txn_close(dbd, ret);
1021 /* Normal case (ret=1): continue while loop */
1025 if (dbd_cnid < rebuild_cnid) {
1026 /* CNID is orphaned -> delete */
1027 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1028 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1029 rqst.cnid = htonl(dbd_cnid);
1030 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1031 dbif_txn_close(dbd, ret);
1037 if (dbd_cnid > rebuild_cnid) {
1038 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1039 dbif_dump(dbd_rebuild, 0);
1040 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1041 dbif_txn_close(dbd, ret);
1042 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1043 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1049 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1050 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1055 Main func called from cmd_dbd.c
1057 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1060 struct db_param db_param = { 0 };
1062 /* Set cachesize for in-memory rebuild db */
1063 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1065 /* Make it accessible for all funcs */
1068 /* We only support unicode volumes ! */
1069 if ( volinfo->v_volcharset != CH_UTF8) {
1070 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1075 /* Get volume stamp */
1076 dbd_getstamp(dbd, &rqst, &rply);
1077 if (rply.result != CNID_DBD_RES_OK)
1079 memcpy(stamp, rply.name, CNID_DEV_LEN);
1081 /* open/create rebuild dbd, copy rootinfo key */
1082 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1084 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1086 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1090 if (setjmp(jmp) != 0)
1091 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1094 if ( (scanvol(volinfo, flags)) != 0)
1098 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1099 other clients in between our pass 1 and 2 */
1100 if (flags & DBD_FLAGS_EXCL)
1101 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1106 dbif_close(dbd_rebuild);