2 $Id: cmd_dbd_scanvol.c,v 1.18 2009-12-22 09:43:15 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 (S_ISREG(st->st_mode))
286 adflags = ADFLAGS_DIR;
288 adname = volinfo->ad_path(fname, adflags);
290 if ((ret = access( adname, F_OK)) != 0) {
291 if (errno != ENOENT) {
292 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
293 cwdbuf, adname, strerror(errno));
296 /* Missing. Log and create it */
297 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
299 if (dbd_flags & DBD_FLAGS_SCAN)
300 /* Scan only requested, dont change anything */
304 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
306 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
307 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
308 cwdbuf, adname, strerror(errno));
313 /* Set name in ad-file */
314 ad_setname(&ad, utompath((char *)fname));
316 ad_close_metadata(&ad);
318 chown(adname, st->st_uid, st->st_gid);
319 /* FIXME: should we inherit mode too here ? */
321 chmod(adname, st->st_mode);
324 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
325 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
326 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
329 ad_close_metadata(&ad);
335 Remove all files with file::EA* from adouble dir
337 static void remove_eafiles(const char *name, struct ea *ea)
341 char eaname[MAXPATHLEN];
343 strlcpy(eaname, name, sizeof(eaname));
344 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
345 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
349 if ((chdir(ADv2_DIRNAME)) != 0) {
350 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
351 cwdbuf, ADv2_DIRNAME, strerror(errno));
355 if ((dp = opendir(".")) == NULL) {
356 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
357 cwdbuf, ADv2_DIRNAME, strerror(errno));
361 while ((ep = readdir(dp))) {
362 if (strstr(ep->d_name, eaname) != NULL) {
363 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
364 cwdbuf, ADv2_DIRNAME, ep->d_name);
365 if ((unlink(ep->d_name)) != 0) {
366 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
367 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
378 Check Extended Attributes files
380 static int check_eafiles(const char *fname)
382 unsigned int count = 0;
388 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
391 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files",
393 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
394 remove_eafiles(fname, &ea);
399 while (count < ea.ea_count) {
400 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
403 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
405 if (lstat(eaname, &st) != 0) {
407 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
409 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
411 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
412 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
414 if ((unlink(eaname)) != 0)
415 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
416 cwdbuf, eaname, strerror(errno));
420 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
421 free((*ea.ea_entries)[count].ea_name);
422 (*ea.ea_entries)[count].ea_name = NULL;
433 Check for .AppleDouble folder and .Parent, create if missing
435 static int check_addir(int volroot)
437 int addir_ok, adpar_ok;
442 /* Check for ad-dir */
443 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
444 if (errno != ENOENT) {
445 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
448 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
451 /* Check for ".Parent" */
452 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
453 if (errno != ENOENT) {
454 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
455 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
458 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
461 /* Is one missing ? */
462 if ((addir_ok != 0) || (adpar_ok != 0)) {
463 /* Yes, but are we only scanning ? */
464 if (dbd_flags & DBD_FLAGS_SCAN) {
465 /* Yes: missing .Parent is not a problem, but missing ad-dir
466 causes later checking of ad-files to fail. So we have to return appropiately */
469 else /* (adpar_ok != 0) */
473 /* Create ad dir and set name */
474 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
476 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
477 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
481 /* Get basename of cwd from cwdbuf */
482 mname = utompath(strrchr(cwdbuf, '/') + 1);
484 /* Update name in ad file */
485 ad_setname(&ad, mname);
487 ad_close_metadata(&ad);
489 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
490 if ((lstat(".", &st)) != 0) {
491 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
494 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
495 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
502 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
504 0 = name is not an EA file
505 1 = name is an EA file and no problem was found
506 -1 = name is an EA file and data fork is gone
508 static int check_eafile_in_adouble(const char *name)
511 char *namep, *namedup = NULL;
513 /* Check if this is an AFPVOL_EA_AD vol */
514 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
515 /* Does the filename contain "::EA" ? */
516 namedup = strdup(name);
517 if ((namep = strstr(namedup, "::EA")) == NULL) {
521 /* File contains "::EA" so it's an EA file. Check for data file */
523 /* Get string before "::EA" from EA filename */
525 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
527 if ((access( pname, F_OK)) == 0) {
532 if (errno != ENOENT) {
533 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
534 cwdbuf, name, strerror(errno));
538 /* Orphaned EA file*/
539 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
540 cwdbuf, ADv2_DIRNAME, name);
542 if (dbd_flags & DBD_FLAGS_SCAN)
543 /* Scan only requested, dont change anything */
546 if ((unlink(name)) != 0) {
547 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
548 cwdbuf, ADv2_DIRNAME, name);
552 } /* if AFPVOL_EA_AD */
562 Check files and dirs inside .AppleDouble folder:
563 - remove orphaned files
566 static int read_addir(void)
572 if ((chdir(ADv2_DIRNAME)) != 0) {
573 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
574 cwdbuf, ADv2_DIRNAME, strerror(errno));
578 if ((dp = opendir(".")) == NULL) {
579 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
580 cwdbuf, ADv2_DIRNAME, strerror(errno));
584 while ((ep = readdir(dp))) {
585 /* Check if its "." or ".." */
586 if (DIR_DOT_OR_DOTDOT(ep->d_name))
589 if (STRCMP(ep->d_name, ==, ".Parent"))
592 if ((lstat(ep->d_name, &st)) < 0) {
593 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
594 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
599 if (S_ISDIR(st.st_mode)) {
600 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
601 ep->d_name, cwdbuf, ADv2_DIRNAME);
605 /* Check if for orphaned and corrupt Extended Attributes file */
606 if (check_eafile_in_adouble(ep->d_name) != 0)
609 /* Check for data file */
610 strcpy(pname + 3, ep->d_name);
611 if ((access( pname, F_OK)) != 0) {
612 if (errno != ENOENT) {
613 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
614 cwdbuf, pname, strerror(errno));
617 /* Orphaned ad-file*/
618 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
619 cwdbuf, ADv2_DIRNAME, ep->d_name);
621 if (dbd_flags & DBD_FLAGS_SCAN)
622 /* Scan only requested, dont change anything */
625 if ((unlink(ep->d_name)) != 0) {
626 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
627 cwdbuf, ADv2_DIRNAME, ep->d_name);
633 if ((chdir("..")) != 0) {
634 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
635 cwdbuf, strerror(errno));
636 /* This really is EOT! */
637 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
646 Check CNID for a file/dir, both from db and from ad-file.
647 For detailed specs see intro.
649 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
652 cnid_t db_cnid, ad_cnid;
655 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
657 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
658 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
659 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
660 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
664 if (dbd_flags & DBD_FLAGS_FORCE) {
665 ad_cnid = ad_forcegetid(&ad);
666 /* This ensures the changed stamp is written */
667 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
671 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
674 dbd_log( LOGSTD, "Incorrect CNID data in .AppleDouble data for '%s/%s' (bad stamp?)", cwdbuf, name);
676 ad_close_metadata(&ad);
679 /* Get CNID from database */
681 /* Prepare request data */
682 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
683 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
686 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
687 rqst.dev = st->st_dev;
688 rqst.ino = st->st_ino;
689 rqst.type = S_ISDIR(st->st_mode)?1:0;
690 rqst.name = (char *)name;
691 rqst.namelen = strlen(name);
693 /* Query the database */
694 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
695 dbif_txn_close(dbd, ret);
696 if (rply.result == CNID_DBD_RES_OK) {
698 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
699 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
700 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
703 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
707 /* Compare results from both CNID searches */
708 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
709 /* Everything is fine */
711 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
712 /* Mismatch ? Delete both from db and re-add data from file */
713 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
714 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
716 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
717 dbif_txn_close(dbd, ret);
720 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
721 dbif_txn_close(dbd, ret);
723 ret = dbd_rebuild_add(dbd, &rqst, &rply);
724 dbif_txn_close(dbd, ret);
727 } else if (ad_cnid && (db_cnid == 0)) {
728 /* in ad-file but not in db */
729 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
730 dbd_log( LOGDEBUG, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
732 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
733 dbif_txn_close(dbd, ret);
734 ret = dbd_rebuild_add(dbd, &rqst, &rply);
735 dbif_txn_close(dbd, ret);
738 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
739 /* No CNID at all, we clearly have to allocate a fresh one... */
740 /* Note: the next test will use this new CNID too! */
741 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
743 ret = dbd_add(dbd, &rqst, &rply, 1);
744 dbif_txn_close(dbd, ret);
746 dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
750 if ((ad_cnid == 0) && db_cnid) {
751 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
752 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
753 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
754 dbd_log( LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", cwdbuf, name, ntohl(db_cnid));
755 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
756 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
757 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
760 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
762 ad_close_metadata(&ad);
772 This is called recursively for all dirs.
773 volroot=1 means we're in the volume root dir, 0 means we aren't.
774 We use this when checking for netatalk private folders like .AppleDB.
775 did is our parents CNID.
777 static int dbd_readdir(int volroot, cnid_t did)
779 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
784 static struct stat st; /* Save some stack space */
786 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
787 if ((addir_ok = check_addir(volroot)) != 0)
788 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
789 /* Fatal on rebuild run, continue if only scanning ! */
792 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
794 if ((read_addir()) != 0)
795 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
796 /* Fatal on rebuild run, continue if only scanning ! */
799 if ((dp = opendir (".")) == NULL) {
800 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
804 while ((ep = readdir (dp))) {
805 /* Check if we got a termination signal */
807 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
809 /* Check if its "." or ".." */
810 if (DIR_DOT_OR_DOTDOT(ep->d_name))
813 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
814 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
816 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
820 /* Skip .AppleDouble dir in this loop */
821 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
824 if ((ret = lstat(ep->d_name, &st)) < 0) {
825 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
829 switch (st.st_mode & S_IFMT) {
834 adflags = ADFLAGS_DIR;
837 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
839 ret = check_symlink(ep->d_name, &adflags);
843 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
847 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
848 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
849 if ((unlink(ep->d_name)) != 0) {
850 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
856 /**************************************************************************
858 **************************************************************************/
861 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
862 /* If its a file: skipp all other tests now ! */
863 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
867 /* Check for appledouble file, create if missing, but only if we have addir */
870 adfile_ok = check_adfile(ep->d_name, &st);
874 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
876 /* Now add this object to our rebuild dbd */
878 rqst.cnid = rply.cnid;
879 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
880 if (rply.result != CNID_DBD_RES_OK) {
881 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
882 cnid, cwdbuf, ep->d_name);
883 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
889 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
890 check_eafiles(ep->d_name);
892 /**************************************************************************
894 **************************************************************************/
895 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
897 strcat(cwdbuf, ep->d_name);
898 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
899 if (-1 == (cwd = open(".", O_RDONLY))) {
900 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
903 if (0 != chdir(ep->d_name)) {
904 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
909 ret = dbd_readdir(0, cnid);
913 *(strrchr(cwdbuf, '/')) = 0;
920 Use results of previous checks
927 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
929 /* Dont scanvol on no-appledouble vols */
930 if (vi->v_flags & AFPVOL_NOADOUBLE) {
931 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
935 /* Make this stuff accessible from all funcs easily */
939 /* Init a fake struct vol with just enough so we can call ea_open and friends */
940 volume.v_adouble = AD_VERSION2;
941 volume.v_vfs_ea = volinfo->v_vfs_ea;
942 initvol_vfs(&volume);
944 /* Run with umask 0 */
947 /* Remove trailing slash from volume, chdir to vol */
948 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
949 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
950 strcpy(cwdbuf, volinfo->v_path);
951 chdir(volinfo->v_path);
953 /* Start recursion */
954 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
961 Remove all CNIDs from dbd that are not in dbd_rebuild
963 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
965 int ret, deleted = 0;
966 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
967 struct cnid_dbd_rqst rqst;
968 struct cnid_dbd_rply rply;
970 /* jump over rootinfo key */
971 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
973 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
976 /* Get first id from dbd_rebuild */
977 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
980 /* Start main loop through dbd: get CNID from dbd */
981 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
985 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
986 dbd_log(LOGSTD, "Error checkpointing!");
991 /* This should be the normal case: CNID is in both dbs */
992 if (dbd_cnid == rebuild_cnid) {
993 /* Get next CNID from rebuild db */
994 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
997 } else if (ret == 0) {
998 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
999 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1000 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1001 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1002 rqst.cnid = htonl(dbd_cnid);
1003 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1004 dbif_txn_close(dbd, ret);
1010 /* Normal case (ret=1): continue while loop */
1014 if (dbd_cnid < rebuild_cnid) {
1015 /* CNID is orphaned -> delete */
1016 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1017 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1018 rqst.cnid = htonl(dbd_cnid);
1019 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1020 dbif_txn_close(dbd, ret);
1026 if (dbd_cnid > rebuild_cnid) {
1027 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1028 dbif_dump(dbd_rebuild, 0);
1029 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1030 dbif_txn_close(dbd, ret);
1031 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1032 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1038 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1039 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1044 Main func called from cmd_dbd.c
1046 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1049 struct db_param db_param = { 0 };
1051 /* Set cachesize for in-memory rebuild db */
1052 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1054 /* Make it accessible for all funcs */
1057 /* We only support unicode volumes ! */
1058 if ( volinfo->v_volcharset != CH_UTF8) {
1059 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1064 /* Get volume stamp */
1065 dbd_getstamp(dbd, &rqst, &rply);
1066 if (rply.result != CNID_DBD_RES_OK)
1068 memcpy(stamp, rply.name, CNID_DEV_LEN);
1070 /* open/create rebuild dbd, copy rootinfo key */
1071 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1073 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1075 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1079 if (setjmp(jmp) != 0)
1080 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1083 if ( (scanvol(volinfo, flags)) != 0)
1087 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1088 other clients in between our pass 1 and 2 */
1089 if (flags & DBD_FLAGS_EXCL)
1090 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1095 dbif_close(dbd_rebuild);