2 $Id: cmd_dbd_scanvol.c,v 1.17 2009-12-21 14:41:00 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.
167 static int check_symlink(const char *name, int *adflags)
171 char pathbuf[MAXPATHLEN + 1];
175 if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) {
176 dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s",
177 cwdbuf, name, strerror(errno));
182 if ((stat(pathbuf, &st)) != 0) {
183 dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno));
187 if ((cwd = open(".", O_RDONLY)) < 0) {
188 dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno));
192 if (S_ISDIR(st.st_mode)) {
193 *adflags |= ADFLAGS_DIR;
195 /* get basename from path */
196 if ((sep = strrchr(pathbuf, '/')) == NULL)
197 /* just a file at the same level */
199 sep = 0; /* pathbuf now contains the directory*/
202 /* fchdir() to pathbuf so we can easily get its path with getcwd() */
203 if ((chdir(pathbuf)) != 0) {
204 dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno));
208 if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) {
209 dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno));
210 if ((fchdir(cwd)) != 0)
212 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
216 if ((fchdir(cwd)) != 0)
218 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
221 We now have the symlink target dir as absoulte path in pathbuf
222 and can compare it with the currents volume path
225 while (volinfo->v_path[i]) {
226 if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
227 dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
233 dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name);
239 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
240 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
242 static int check_name_encoding(char *uname)
246 roundtripped = mtoupath(utompath(uname));
248 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
252 if ( STRCMP(uname, !=, roundtripped)) {
253 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
261 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
262 Returns pointer to name or NULL.
264 static const char *check_netatalk_dirs(const char *name)
268 for (c=0; netatalk_dirs[c]; c++) {
269 if ((strcmp(name, netatalk_dirs[c])) == 0)
270 return netatalk_dirs[c];
276 Check for .AppleDouble file, create if missing
278 static int check_adfile(const char *fname, const struct stat *st)
284 if (S_ISREG(st->st_mode))
287 adflags = ADFLAGS_DIR;
289 adname = volinfo->ad_path(fname, adflags);
291 if ((ret = access( adname, F_OK)) != 0) {
292 if (errno != ENOENT) {
293 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
294 cwdbuf, adname, strerror(errno));
297 /* Missing. Log and create it */
298 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
300 if (dbd_flags & DBD_FLAGS_SCAN)
301 /* Scan only requested, dont change anything */
305 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
307 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
308 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
309 cwdbuf, adname, strerror(errno));
314 /* Set name in ad-file */
315 ad_setname(&ad, utompath((char *)fname));
317 ad_close_metadata(&ad);
319 chown(adname, st->st_uid, st->st_gid);
320 /* FIXME: should we inherit mode too here ? */
322 chmod(adname, st->st_mode);
325 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
326 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
327 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
330 ad_close_metadata(&ad);
336 Remove all files with file::EA* from adouble dir
338 static void remove_eafiles(const char *name, struct ea *ea)
342 char eaname[MAXPATHLEN];
344 strlcpy(eaname, name, sizeof(eaname));
345 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
346 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
350 if ((chdir(ADv2_DIRNAME)) != 0) {
351 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
352 cwdbuf, ADv2_DIRNAME, strerror(errno));
356 if ((dp = opendir(".")) == NULL) {
357 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
358 cwdbuf, ADv2_DIRNAME, strerror(errno));
362 while ((ep = readdir(dp))) {
363 if (strstr(ep->d_name, eaname) != NULL) {
364 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
365 cwdbuf, ADv2_DIRNAME, ep->d_name);
366 if ((unlink(ep->d_name)) != 0) {
367 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
368 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
379 Check Extended Attributes files
381 static int check_eafiles(const char *fname)
383 unsigned int count = 0;
389 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
392 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files",
394 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
395 remove_eafiles(fname, &ea);
400 while (count < ea.ea_count) {
401 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
404 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
406 if (lstat(eaname, &st) != 0) {
408 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
410 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
412 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
413 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
415 if ((unlink(eaname)) != 0)
416 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
417 cwdbuf, eaname, strerror(errno));
421 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
422 free((*ea.ea_entries)[count].ea_name);
423 (*ea.ea_entries)[count].ea_name = NULL;
434 Check for .AppleDouble folder and .Parent, create if missing
436 static int check_addir(int volroot)
438 int addir_ok, adpar_ok;
443 /* Check for ad-dir */
444 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
445 if (errno != ENOENT) {
446 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
449 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
452 /* Check for ".Parent" */
453 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
454 if (errno != ENOENT) {
455 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
456 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
459 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
462 /* Is one missing ? */
463 if ((addir_ok != 0) || (adpar_ok != 0)) {
464 /* Yes, but are we only scanning ? */
465 if (dbd_flags & DBD_FLAGS_SCAN) {
466 /* Yes: missing .Parent is not a problem, but missing ad-dir
467 causes later checking of ad-files to fail. So we have to return appropiately */
470 else /* (adpar_ok != 0) */
474 /* Create ad dir and set name */
475 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
477 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
478 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
482 /* Get basename of cwd from cwdbuf */
483 mname = utompath(strrchr(cwdbuf, '/') + 1);
485 /* Update name in ad file */
486 ad_setname(&ad, mname);
488 ad_close_metadata(&ad);
490 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
491 if ((lstat(".", &st)) != 0) {
492 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
495 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
496 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
503 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
505 0 = name is not an EA file
506 1 = name is an EA file and no problem was found
507 -1 = name is an EA file and data fork is gone
509 static int check_eafile_in_adouble(const char *name)
512 char *namep, *namedup = NULL;
514 /* Check if this is an AFPVOL_EA_AD vol */
515 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
516 /* Does the filename contain "::EA" ? */
517 namedup = strdup(name);
518 if ((namep = strstr(namedup, "::EA")) == NULL) {
522 /* File contains "::EA" so it's an EA file. Check for data file */
524 /* Get string before "::EA" from EA filename */
526 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
528 if ((access( pname, F_OK)) == 0) {
533 if (errno != ENOENT) {
534 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
535 cwdbuf, name, strerror(errno));
539 /* Orphaned EA file*/
540 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
541 cwdbuf, ADv2_DIRNAME, name);
543 if (dbd_flags & DBD_FLAGS_SCAN)
544 /* Scan only requested, dont change anything */
547 if ((unlink(name)) != 0) {
548 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
549 cwdbuf, ADv2_DIRNAME, name);
553 } /* if AFPVOL_EA_AD */
563 Check files and dirs inside .AppleDouble folder:
564 - remove orphaned files
567 static int read_addir(void)
573 if ((chdir(ADv2_DIRNAME)) != 0) {
574 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
575 cwdbuf, ADv2_DIRNAME, strerror(errno));
579 if ((dp = opendir(".")) == NULL) {
580 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
581 cwdbuf, ADv2_DIRNAME, strerror(errno));
585 while ((ep = readdir(dp))) {
586 /* Check if its "." or ".." */
587 if (DIR_DOT_OR_DOTDOT(ep->d_name))
590 if (STRCMP(ep->d_name, ==, ".Parent"))
593 if ((lstat(ep->d_name, &st)) < 0) {
594 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
595 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
600 if (S_ISDIR(st.st_mode)) {
601 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
602 ep->d_name, cwdbuf, ADv2_DIRNAME);
606 /* Check if for orphaned and corrupt Extended Attributes file */
607 if (check_eafile_in_adouble(ep->d_name) != 0)
610 /* Check for data file */
611 strcpy(pname + 3, ep->d_name);
612 if ((access( pname, F_OK)) != 0) {
613 if (errno != ENOENT) {
614 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
615 cwdbuf, pname, strerror(errno));
618 /* Orphaned ad-file*/
619 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
620 cwdbuf, ADv2_DIRNAME, ep->d_name);
622 if (dbd_flags & DBD_FLAGS_SCAN)
623 /* Scan only requested, dont change anything */
626 if ((unlink(ep->d_name)) != 0) {
627 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
628 cwdbuf, ADv2_DIRNAME, ep->d_name);
634 if ((chdir("..")) != 0) {
635 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
636 cwdbuf, strerror(errno));
637 /* This really is EOT! */
638 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
647 Check CNID for a file/dir, both from db and from ad-file.
648 For detailed specs see intro.
650 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
653 cnid_t db_cnid, ad_cnid;
656 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
658 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
659 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
660 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
661 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
665 if (dbd_flags & DBD_FLAGS_FORCE) {
666 ad_cnid = ad_forcegetid(&ad);
667 /* This ensures the changed stamp is written */
668 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
672 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
675 dbd_log( LOGSTD, "Incorrect CNID data in .AppleDouble data for '%s/%s' (bad stamp?)", cwdbuf, name);
677 ad_close_metadata(&ad);
680 /* Get CNID from database */
682 /* Prepare request data */
683 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
684 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
687 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
688 rqst.dev = st->st_dev;
689 rqst.ino = st->st_ino;
690 rqst.type = S_ISDIR(st->st_mode)?1:0;
691 rqst.name = (char *)name;
692 rqst.namelen = strlen(name);
694 /* Query the database */
695 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
696 dbif_txn_close(dbd, ret);
697 if (rply.result == CNID_DBD_RES_OK) {
699 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
700 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
701 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
704 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
708 /* Compare results from both CNID searches */
709 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
710 /* Everything is fine */
712 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
713 /* Mismatch ? Delete both from db and re-add data from file */
714 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
715 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
717 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
718 dbif_txn_close(dbd, ret);
721 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
722 dbif_txn_close(dbd, ret);
724 ret = dbd_rebuild_add(dbd, &rqst, &rply);
725 dbif_txn_close(dbd, ret);
728 } else if (ad_cnid && (db_cnid == 0)) {
729 /* in ad-file but not in db */
730 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
731 dbd_log( LOGDEBUG, "CNID rebuild add for '%s/%s', adding with CNID from ad-file: %u", cwdbuf, name, ntohl(ad_cnid));
733 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
734 dbif_txn_close(dbd, ret);
735 ret = dbd_rebuild_add(dbd, &rqst, &rply);
736 dbif_txn_close(dbd, ret);
739 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
740 /* No CNID at all, we clearly have to allocate a fresh one... */
741 /* Note: the next test will use this new CNID too! */
742 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
744 ret = dbd_add(dbd, &rqst, &rply, 1);
745 dbif_txn_close(dbd, ret);
747 dbd_log( LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
751 if ((ad_cnid == 0) && db_cnid) {
752 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
753 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
754 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
755 dbd_log( LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", cwdbuf, name, ntohl(db_cnid));
756 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
757 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
758 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
761 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
763 ad_close_metadata(&ad);
773 This is called recursively for all dirs.
774 volroot=1 means we're in the volume root dir, 0 means we aren't.
775 We use this when checking for netatalk private folders like .AppleDB.
776 did is our parents CNID.
778 static int dbd_readdir(int volroot, cnid_t did)
780 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
785 static struct stat st; /* Save some stack space */
787 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
788 if ((addir_ok = check_addir(volroot)) != 0)
789 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
790 /* Fatal on rebuild run, continue if only scanning ! */
793 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
795 if ((read_addir()) != 0)
796 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
797 /* Fatal on rebuild run, continue if only scanning ! */
800 if ((dp = opendir (".")) == NULL) {
801 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
805 while ((ep = readdir (dp))) {
806 /* Check if we got a termination signal */
808 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
810 /* Check if its "." or ".." */
811 if (DIR_DOT_OR_DOTDOT(ep->d_name))
814 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
815 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
817 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
821 /* Skip .AppleDouble dir in this loop */
822 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
825 if ((ret = lstat(ep->d_name, &st)) < 0) {
826 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
830 switch (st.st_mode & S_IFMT) {
835 adflags = ADFLAGS_DIR;
838 dbd_log(LOGDEBUG, "Checking 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);
846 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
847 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
848 if ((unlink(ep->d_name)) != 0) {
849 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
855 /**************************************************************************
857 **************************************************************************/
860 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
861 /* If its a file: skipp all other tests now ! */
862 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
866 /* Check for appledouble file, create if missing, but only if we have addir */
869 adfile_ok = check_adfile(ep->d_name, &st);
873 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
875 /* Now add this object to our rebuild dbd */
877 rqst.cnid = rply.cnid;
878 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
879 if (rply.result != CNID_DBD_RES_OK) {
880 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
881 cnid, cwdbuf, ep->d_name);
882 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
888 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
889 check_eafiles(ep->d_name);
891 /**************************************************************************
893 **************************************************************************/
894 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
896 strcat(cwdbuf, ep->d_name);
897 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
898 if (-1 == (cwd = open(".", O_RDONLY))) {
899 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
902 if (0 != chdir(ep->d_name)) {
903 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
908 ret = dbd_readdir(0, cnid);
912 *(strrchr(cwdbuf, '/')) = 0;
919 Use results of previous checks
926 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
928 /* Dont scanvol on no-appledouble vols */
929 if (vi->v_flags & AFPVOL_NOADOUBLE) {
930 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
934 /* Make this stuff accessible from all funcs easily */
938 /* Init a fake struct vol with just enough so we can call ea_open and friends */
939 volume.v_adouble = AD_VERSION2;
940 volume.v_vfs_ea = volinfo->v_vfs_ea;
941 initvol_vfs(&volume);
943 /* Run with umask 0 */
946 /* Remove trailing slash from volume, chdir to vol */
947 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
948 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
949 strcpy(cwdbuf, volinfo->v_path);
950 chdir(volinfo->v_path);
952 /* Start recursion */
953 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
960 Remove all CNIDs from dbd that are not in dbd_rebuild
962 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
964 int ret, deleted = 0;
965 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
966 struct cnid_dbd_rqst rqst;
967 struct cnid_dbd_rply rply;
969 /* jump over rootinfo key */
970 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
972 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
975 /* Get first id from dbd_rebuild */
976 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
979 /* Start main loop through dbd: get CNID from dbd */
980 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
984 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
985 dbd_log(LOGSTD, "Error checkpointing!");
990 /* This should be the normal case: CNID is in both dbs */
991 if (dbd_cnid == rebuild_cnid) {
992 /* Get next CNID from rebuild db */
993 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
996 } else if (ret == 0) {
997 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
998 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
999 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1000 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1001 rqst.cnid = htonl(dbd_cnid);
1002 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1003 dbif_txn_close(dbd, ret);
1009 /* Normal case (ret=1): continue while loop */
1013 if (dbd_cnid < rebuild_cnid) {
1014 /* CNID is orphaned -> delete */
1015 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1016 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1017 rqst.cnid = htonl(dbd_cnid);
1018 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1019 dbif_txn_close(dbd, ret);
1025 if (dbd_cnid > rebuild_cnid) {
1026 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1027 dbif_dump(dbd_rebuild, 0);
1028 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1029 dbif_txn_close(dbd, ret);
1030 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1031 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1037 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1038 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1043 Main func called from cmd_dbd.c
1045 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1048 struct db_param db_param = { 0 };
1050 /* Set cachesize for in-memory rebuild db */
1051 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1053 /* Make it accessible for all funcs */
1056 /* We only support unicode volumes ! */
1057 if ( volinfo->v_volcharset != CH_UTF8) {
1058 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1063 /* Get volume stamp */
1064 dbd_getstamp(dbd, &rqst, &rply);
1065 if (rply.result != CNID_DBD_RES_OK)
1067 memcpy(stamp, rply.name, CNID_DEV_LEN);
1069 /* open/create rebuild dbd, copy rootinfo key */
1070 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1072 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1074 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1078 if (setjmp(jmp) != 0)
1079 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1082 if ( (scanvol(volinfo, flags)) != 0)
1086 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1087 other clients in between our pass 1 and 2 */
1088 if (flags & DBD_FLAGS_EXCL)
1089 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1094 dbif_close(dbd_rebuild);