2 $Id: cmd_dbd_scanvol.c,v 1.20 2010-02-15 13:58:38 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 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
678 ad_close_metadata(&ad);
681 /* Get CNID from database */
683 /* Prepare request data */
684 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
685 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
688 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
689 rqst.dev = st->st_dev;
690 rqst.ino = st->st_ino;
691 rqst.type = S_ISDIR(st->st_mode)?1:0;
692 rqst.name = (char *)name;
693 rqst.namelen = strlen(name);
695 /* Query the database */
696 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
697 dbif_txn_close(dbd, ret);
698 if (rply.result == CNID_DBD_RES_OK) {
700 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
701 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
702 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
705 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
709 /* Compare results from both CNID searches */
710 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
711 /* Everything is fine */
713 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
714 /* Mismatch ? Delete both from db and re-add data from file */
715 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
716 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
718 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
719 dbif_txn_close(dbd, ret);
722 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
723 dbif_txn_close(dbd, ret);
725 ret = dbd_rebuild_add(dbd, &rqst, &rply);
726 dbif_txn_close(dbd, ret);
729 } else if (ad_cnid && (db_cnid == 0)) {
730 /* in ad-file but not in db */
731 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
732 dbd_log( LOGDEBUG, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
734 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
735 dbif_txn_close(dbd, ret);
736 ret = dbd_rebuild_add(dbd, &rqst, &rply);
737 dbif_txn_close(dbd, ret);
740 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
741 /* No CNID at all, we clearly have to allocate a fresh one... */
742 /* Note: the next test will use this new CNID too! */
743 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
745 ret = dbd_add(dbd, &rqst, &rply, 1);
746 dbif_txn_close(dbd, ret);
748 dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
752 if ((ad_cnid == 0) && db_cnid) {
753 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
754 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
755 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
756 dbd_log( LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", cwdbuf, name, ntohl(db_cnid));
757 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
758 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
759 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
762 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
764 ad_close_metadata(&ad);
774 This is called recursively for all dirs.
775 volroot=1 means we're in the volume root dir, 0 means we aren't.
776 We use this when checking for netatalk private folders like .AppleDB.
777 did is our parents CNID.
779 static int dbd_readdir(int volroot, cnid_t did)
781 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
786 static struct stat st; /* Save some stack space */
788 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
789 if ((addir_ok = check_addir(volroot)) != 0)
790 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
791 /* Fatal on rebuild run, continue if only scanning ! */
794 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
796 if ((read_addir()) != 0)
797 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
798 /* Fatal on rebuild run, continue if only scanning ! */
801 if ((dp = opendir (".")) == NULL) {
802 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
806 while ((ep = readdir (dp))) {
807 /* Check if we got a termination signal */
809 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
811 /* Check if its "." or ".." */
812 if (DIR_DOT_OR_DOTDOT(ep->d_name))
815 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
816 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
818 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
822 /* Skip .AppleDouble dir in this loop */
823 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
826 if ((ret = lstat(ep->d_name, &st)) < 0) {
827 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
831 switch (st.st_mode & S_IFMT) {
836 adflags = ADFLAGS_DIR;
839 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
841 ret = check_symlink(ep->d_name, &adflags);
845 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
849 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
850 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
851 if ((unlink(ep->d_name)) != 0) {
852 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
858 /**************************************************************************
860 **************************************************************************/
863 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
864 /* If its a file: skipp all other tests now ! */
865 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
869 /* Check for appledouble file, create if missing, but only if we have addir */
872 adfile_ok = check_adfile(ep->d_name, &st);
876 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
878 /* Now add this object to our rebuild dbd */
880 rqst.cnid = rply.cnid;
881 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
882 if (rply.result != CNID_DBD_RES_OK) {
883 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
884 cnid, cwdbuf, ep->d_name);
885 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
891 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
892 check_eafiles(ep->d_name);
894 /**************************************************************************
896 **************************************************************************/
897 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
899 strcat(cwdbuf, ep->d_name);
900 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
901 if (-1 == (cwd = open(".", O_RDONLY))) {
902 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
905 if (0 != chdir(ep->d_name)) {
906 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
911 ret = dbd_readdir(0, cnid);
915 *(strrchr(cwdbuf, '/')) = 0;
922 Use results of previous checks
929 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
931 /* Dont scanvol on no-appledouble vols */
932 if (vi->v_flags & AFPVOL_NOADOUBLE) {
933 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
937 /* Make this stuff accessible from all funcs easily */
941 /* Init a fake struct vol with just enough so we can call ea_open and friends */
942 volume.v_adouble = AD_VERSION2;
943 volume.v_vfs_ea = volinfo->v_vfs_ea;
944 initvol_vfs(&volume);
946 /* Run with umask 0 */
949 /* Remove trailing slash from volume, chdir to vol */
950 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
951 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
952 strcpy(cwdbuf, volinfo->v_path);
953 chdir(volinfo->v_path);
955 /* Start recursion */
956 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
963 Remove all CNIDs from dbd that are not in dbd_rebuild
965 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
967 int ret, deleted = 0;
968 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
969 struct cnid_dbd_rqst rqst;
970 struct cnid_dbd_rply rply;
972 /* jump over rootinfo key */
973 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
975 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
978 /* Get first id from dbd_rebuild */
979 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
982 /* Start main loop through dbd: get CNID from dbd */
983 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
987 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
988 dbd_log(LOGSTD, "Error checkpointing!");
993 /* This should be the normal case: CNID is in both dbs */
994 if (dbd_cnid == rebuild_cnid) {
995 /* Get next CNID from rebuild db */
996 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
999 } else if (ret == 0) {
1000 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1001 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1002 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1003 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1004 rqst.cnid = htonl(dbd_cnid);
1005 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1006 dbif_txn_close(dbd, ret);
1012 /* Normal case (ret=1): continue while loop */
1016 if (dbd_cnid < rebuild_cnid) {
1017 /* CNID is orphaned -> delete */
1018 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1019 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1020 rqst.cnid = htonl(dbd_cnid);
1021 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1022 dbif_txn_close(dbd, ret);
1028 if (dbd_cnid > rebuild_cnid) {
1029 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1030 dbif_dump(dbd_rebuild, 0);
1031 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1032 dbif_txn_close(dbd, ret);
1033 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1034 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1040 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1041 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1046 Main func called from cmd_dbd.c
1048 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1051 struct db_param db_param = { 0 };
1053 /* Set cachesize for in-memory rebuild db */
1054 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1056 /* Make it accessible for all funcs */
1059 /* We only support unicode volumes ! */
1060 if ( volinfo->v_volcharset != CH_UTF8) {
1061 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1066 /* Get volume stamp */
1067 dbd_getstamp(dbd, &rqst, &rply);
1068 if (rply.result != CNID_DBD_RES_OK)
1070 memcpy(stamp, rply.name, CNID_DEV_LEN);
1072 /* open/create rebuild dbd, copy rootinfo key */
1073 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1075 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1077 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1081 if (setjmp(jmp) != 0)
1082 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1085 if ( (scanvol(volinfo, flags)) != 0)
1089 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1090 other clients in between our pass 1 and 2 */
1091 if (flags & DBD_FLAGS_EXCL)
1092 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1097 dbif_close(dbd_rebuild);