2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
17 #endif /* HAVE_CONFIG_H */
21 #include <sys/types.h>
29 #include <atalk/adouble.h>
30 #include <atalk/unicode.h>
31 #include <atalk/volinfo.h>
32 #include <atalk/cnid_dbd_private.h>
33 #include <atalk/volume.h>
35 #include <atalk/util.h>
42 /* Some defines to ease code parsing */
43 #define ADDIR_OK (addir_ok == 0)
44 #define ADFILE_OK (adfile_ok == 0)
46 /* These must be accessible for cmd_dbd_* funcs */
47 struct volinfo *volinfo;
48 char cwdbuf[MAXPATHLEN+1];
50 /* Some static vars */
52 static DBD *dbd_rebuild;
53 static dbd_flags_t dbd_flags;
54 static char stamp[CNID_DEV_LEN];
55 static char *netatalk_dirs[] = {
60 static char *special_dirs[] = {
64 static struct cnid_dbd_rqst rqst;
65 static struct cnid_dbd_rply rply;
67 static struct vol volume; /* fake it for ea_open */
68 static char pname[MAXPATHLEN] = "../";
71 Taken form afpd/desktop.c
73 static char *utompath(char *upath)
75 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
77 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
85 outlen = strlen(upath);
87 if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
88 flags |= CONV_TOUPPER;
89 else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
90 flags |= CONV_TOLOWER;
92 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
93 flags |= CONV__EILSEQ;
96 /* convert charsets */
97 if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
99 volinfo->v_maccharset,
100 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
101 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
102 volinfo->v_volcodepage, volinfo->v_maccodepage, u);
110 Taken form afpd/desktop.c
112 static char *mtoupath(char *mpath)
114 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
123 if ( *mpath == '\0' ) {
127 /* set conversion flags */
128 if (!(volinfo->v_flags & AFPVOL_NOHEX))
129 flags |= CONV_ESCAPEHEX;
130 if (!(volinfo->v_flags & AFPVOL_USEDOTS))
131 flags |= CONV_ESCAPEDOTS;
133 if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
134 flags |= CONV_TOUPPER;
135 else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
136 flags |= CONV_TOLOWER;
138 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
139 flags |= CONV__EILSEQ;
148 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
149 volinfo->v_volcharset,
150 volinfo->v_maccharset,
151 m, inplen, u, outlen, &flags)) ) {
152 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
153 volinfo->v_volcodepage, mpath);
162 Check if "name" is pointing to
163 a) an object inside the current volume (return 0)
164 b) an object outside the current volume (return 1)
165 Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags
166 Return -1 on any serious error.
168 static int check_symlink(const char *name, int *adflags)
172 char pathbuf[MAXPATHLEN + 1];
176 if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) {
177 dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s",
178 cwdbuf, name, strerror(errno));
183 if ((stat(pathbuf, &st)) != 0) {
184 dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno));
188 if ((cwd = open(".", O_RDONLY)) < 0) {
189 dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno));
193 if (S_ISDIR(st.st_mode)) {
194 *adflags |= ADFLAGS_DIR;
196 /* get basename from path */
197 if ((sep = strrchr(pathbuf, '/')) == NULL)
198 /* just a file at the same level */
200 sep = 0; /* pathbuf now contains the directory*/
203 /* fchdir() to pathbuf so we can easily get its path with getcwd() */
204 if ((chdir(pathbuf)) != 0) {
205 dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno));
209 if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) {
210 dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno));
211 if ((fchdir(cwd)) != 0)
213 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
217 if ((fchdir(cwd)) != 0)
219 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
222 We now have the symlink target dir as absoulte path in pathbuf
223 and can compare it with the currents volume path
226 while (volinfo->v_path[i]) {
227 if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
228 dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
234 dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name);
240 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
241 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
243 static int check_name_encoding(char *uname)
247 roundtripped = mtoupath(utompath(uname));
249 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
253 if ( STRCMP(uname, !=, roundtripped)) {
254 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
262 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
263 Returns pointer to name or NULL.
265 static const char *check_netatalk_dirs(const char *name)
269 for (c=0; netatalk_dirs[c]; c++) {
270 if ((strcmp(name, netatalk_dirs[c])) == 0)
271 return netatalk_dirs[c];
277 Check for special names
278 Returns pointer to name or NULL.
280 static const char *check_special_dirs(const char *name)
284 for (c=0; special_dirs[c]; c++) {
285 if ((strcmp(name, special_dirs[c])) == 0)
286 return special_dirs[c];
292 Check for .AppleDouble file, create if missing
294 static int check_adfile(const char *fname, const struct stat *st)
300 if (dbd_flags & DBD_FLAGS_CLEANUP)
303 if (S_ISREG(st->st_mode))
306 adflags = ADFLAGS_DIR;
308 adname = volinfo->ad_path(fname, adflags);
310 if ((ret = access( adname, F_OK)) != 0) {
311 if (errno != ENOENT) {
312 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
313 cwdbuf, adname, strerror(errno));
316 /* Missing. Log and create it */
317 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
319 if (dbd_flags & DBD_FLAGS_SCAN)
320 /* Scan only requested, dont change anything */
324 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
326 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
327 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
328 cwdbuf, adname, strerror(errno));
333 /* Set name in ad-file */
334 ad_setname(&ad, utompath((char *)fname));
336 ad_close_metadata(&ad);
338 chown(adname, st->st_uid, st->st_gid);
339 /* FIXME: should we inherit mode too here ? */
341 chmod(adname, st->st_mode);
344 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
345 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
346 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
349 ad_close_metadata(&ad);
355 Remove all files with file::EA* from adouble dir
357 static void remove_eafiles(const char *name, struct ea *ea)
361 char eaname[MAXPATHLEN];
363 strlcpy(eaname, name, sizeof(eaname));
364 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
365 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
369 if ((chdir(ADv2_DIRNAME)) != 0) {
370 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
371 cwdbuf, ADv2_DIRNAME, strerror(errno));
375 if ((dp = opendir(".")) == NULL) {
376 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
377 cwdbuf, ADv2_DIRNAME, strerror(errno));
381 while ((ep = readdir(dp))) {
382 if (strstr(ep->d_name, eaname) != NULL) {
383 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
384 cwdbuf, ADv2_DIRNAME, ep->d_name);
385 if ((unlink(ep->d_name)) != 0) {
386 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
387 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
398 Check Extended Attributes files
400 static int check_eafiles(const char *fname)
402 unsigned int count = 0;
408 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
411 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
412 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
413 remove_eafiles(fname, &ea);
418 while (count < ea.ea_count) {
419 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
422 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
424 if (lstat(eaname, &st) != 0) {
426 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
428 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
430 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
431 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
433 if ((unlink(eaname)) != 0)
434 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
435 cwdbuf, eaname, strerror(errno));
439 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
440 free((*ea.ea_entries)[count].ea_name);
441 (*ea.ea_entries)[count].ea_name = NULL;
452 Check for .AppleDouble folder and .Parent, create if missing
454 static int check_addir(int volroot)
456 int addir_ok, adpar_ok;
461 if (dbd_flags & DBD_FLAGS_CLEANUP)
464 /* Check for ad-dir */
465 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
466 if (errno != ENOENT) {
467 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
470 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
473 /* Check for ".Parent" */
474 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
475 if (errno != ENOENT) {
476 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
477 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
480 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
483 /* Is one missing ? */
484 if ((addir_ok != 0) || (adpar_ok != 0)) {
485 /* Yes, but are we only scanning ? */
486 if (dbd_flags & DBD_FLAGS_SCAN) {
487 /* Yes: missing .Parent is not a problem, but missing ad-dir
488 causes later checking of ad-files to fail. So we have to return appropiately */
491 else /* (adpar_ok != 0) */
495 /* Create ad dir and set name */
496 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
498 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
499 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
503 /* Get basename of cwd from cwdbuf */
504 mname = utompath(strrchr(cwdbuf, '/') + 1);
506 /* Update name in ad file */
507 ad_setname(&ad, mname);
509 ad_close_metadata(&ad);
511 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
512 if ((lstat(".", &st)) != 0) {
513 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
516 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
517 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
524 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
526 0 = name is not an EA file
527 1 = name is an EA file and no problem was found
528 -1 = name is an EA file and data fork is gone
530 static int check_eafile_in_adouble(const char *name)
533 char *namep, *namedup = NULL;
535 /* Check if this is an AFPVOL_EA_AD vol */
536 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
537 /* Does the filename contain "::EA" ? */
538 namedup = strdup(name);
539 if ((namep = strstr(namedup, "::EA")) == NULL) {
543 /* File contains "::EA" so it's an EA file. Check for data file */
545 /* Get string before "::EA" from EA filename */
547 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
549 if ((access( pname, F_OK)) == 0) {
554 if (errno != ENOENT) {
555 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
556 cwdbuf, name, strerror(errno));
560 /* Orphaned EA file*/
561 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
562 cwdbuf, ADv2_DIRNAME, name);
564 if (dbd_flags & DBD_FLAGS_SCAN)
565 /* Scan only requested, dont change anything */
568 if ((unlink(name)) != 0) {
569 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
570 cwdbuf, ADv2_DIRNAME, name);
574 } /* if AFPVOL_EA_AD */
584 Check files and dirs inside .AppleDouble folder:
585 - remove orphaned files
588 static int read_addir(void)
594 if ((chdir(ADv2_DIRNAME)) != 0) {
595 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
596 cwdbuf, ADv2_DIRNAME, strerror(errno));
600 if ((dp = opendir(".")) == NULL) {
601 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
602 cwdbuf, ADv2_DIRNAME, strerror(errno));
606 while ((ep = readdir(dp))) {
607 /* Check if its "." or ".." */
608 if (DIR_DOT_OR_DOTDOT(ep->d_name))
611 if (STRCMP(ep->d_name, ==, ".Parent"))
614 if ((lstat(ep->d_name, &st)) < 0) {
615 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
616 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
621 if (S_ISDIR(st.st_mode)) {
622 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
623 ep->d_name, cwdbuf, ADv2_DIRNAME);
627 /* Check if for orphaned and corrupt Extended Attributes file */
628 if (check_eafile_in_adouble(ep->d_name) != 0)
631 /* Check for data file */
632 strcpy(pname + 3, ep->d_name);
633 if ((access( pname, F_OK)) != 0) {
634 if (errno != ENOENT) {
635 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
636 cwdbuf, pname, strerror(errno));
639 /* Orphaned ad-file*/
640 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
641 cwdbuf, ADv2_DIRNAME, ep->d_name);
643 if (dbd_flags & DBD_FLAGS_SCAN)
644 /* Scan only requested, dont change anything */
647 if ((unlink(ep->d_name)) != 0) {
648 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
649 cwdbuf, ADv2_DIRNAME, ep->d_name);
655 if ((chdir("..")) != 0) {
656 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
657 cwdbuf, strerror(errno));
658 /* This really is EOT! */
659 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
668 Check CNID for a file/dir, both from db and from ad-file.
669 For detailed specs see intro.
671 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
674 cnid_t db_cnid, ad_cnid;
677 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
679 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
680 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
681 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
683 if (dbd_flags & DBD_FLAGS_CLEANUP)
686 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
690 if (dbd_flags & DBD_FLAGS_FORCE) {
691 ad_cnid = ad_forcegetid(&ad);
692 /* This ensures the changed stamp is written */
693 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
697 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
700 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
702 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
704 ad_close_metadata(&ad);
707 /* Get CNID from database */
709 /* Prepare request data */
710 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
711 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
714 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
715 rqst.dev = st->st_dev;
716 rqst.ino = st->st_ino;
717 rqst.type = S_ISDIR(st->st_mode)?1:0;
718 rqst.name = (char *)name;
719 rqst.namelen = strlen(name);
721 /* Query the database */
722 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
723 dbif_txn_close(dbd, ret);
724 if (rply.result == CNID_DBD_RES_OK) {
726 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
727 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
728 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
731 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
735 /* Compare results from both CNID searches */
736 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
737 /* Everything is fine */
739 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
740 /* Mismatch ? Delete both from db and re-add data from file */
741 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
742 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
744 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
745 dbif_txn_close(dbd, ret);
748 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
749 dbif_txn_close(dbd, ret);
751 ret = dbd_rebuild_add(dbd, &rqst, &rply);
752 dbif_txn_close(dbd, ret);
755 } else if (ad_cnid && (db_cnid == 0)) {
756 /* in ad-file but not in db */
757 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
758 /* Ensure the cnid from the ad-file is not already occupied by another file */
759 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
763 ret = dbd_resolve(dbd, &rqst, &rply);
764 if (ret == CNID_DBD_RES_OK) {
765 /* Occupied! Choose another, update ad-file */
766 ret = dbd_add(dbd, &rqst, &rply, 1);
767 dbif_txn_close(dbd, ret);
769 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
771 if ((volinfo->v_flags & AFPVOL_CACHE)
773 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
774 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
775 cwdbuf, name, ntohl(db_cnid));
776 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
777 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
778 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
779 cwdbuf, name, strerror(errno));
782 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
784 ad_close_metadata(&ad);
789 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
790 cwdbuf, name, ntohl(ad_cnid));
792 ret = dbd_rebuild_add(dbd, &rqst, &rply);
793 dbif_txn_close(dbd, ret);
796 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
797 /* No CNID at all, we clearly have to allocate a fresh one... */
798 /* Note: the next test will use this new CNID too! */
799 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
801 ret = dbd_add(dbd, &rqst, &rply, 1);
802 dbif_txn_close(dbd, ret);
804 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
808 if ((ad_cnid == 0) && db_cnid) {
809 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
810 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
811 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
812 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
813 cwdbuf, name, ntohl(db_cnid));
814 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
815 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
816 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
817 cwdbuf, name, strerror(errno));
820 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
822 ad_close_metadata(&ad);
832 This is called recursively for all dirs.
833 volroot=1 means we're in the volume root dir, 0 means we aren't.
834 We use this when checking for netatalk private folders like .AppleDB.
835 did is our parents CNID.
837 static int dbd_readdir(int volroot, cnid_t did)
839 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
844 static struct stat st; /* Save some stack space */
846 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
847 if ((addir_ok = check_addir(volroot)) != 0)
848 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
849 /* Fatal on rebuild run, continue if only scanning ! */
852 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
854 if ((read_addir()) != 0)
855 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
856 /* Fatal on rebuild run, continue if only scanning ! */
859 if ((dp = opendir (".")) == NULL) {
860 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
864 while ((ep = readdir (dp))) {
865 /* Check if we got a termination signal */
867 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
869 /* Check if its "." or ".." */
870 if (DIR_DOT_OR_DOTDOT(ep->d_name))
873 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
874 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
876 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
880 /* Check for special folders in volume root e.g. ".zfs" */
882 if ((name = check_special_dirs(ep->d_name)) != NULL)
883 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
887 /* Skip .AppleDouble dir in this loop */
888 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
891 if ((ret = lstat(ep->d_name, &st)) < 0) {
892 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
893 cwdbuf, ep->d_name, strerror(errno));
897 switch (st.st_mode & S_IFMT) {
902 adflags = ADFLAGS_DIR;
905 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
907 ret = check_symlink(ep->d_name, &adflags);
911 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
915 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
916 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
917 if ((unlink(ep->d_name)) != 0) {
918 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
924 /**************************************************************************
926 **************************************************************************/
929 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
930 /* If its a file: skipp all other tests now ! */
931 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
935 /* Check for appledouble file, create if missing, but only if we have addir */
938 adfile_ok = check_adfile(ep->d_name, &st);
942 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
944 /* Now add this object to our rebuild dbd */
946 rqst.cnid = rply.cnid;
947 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
948 if (rply.result != CNID_DBD_RES_OK) {
949 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
950 cnid, cwdbuf, ep->d_name);
951 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
957 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
958 check_eafiles(ep->d_name);
960 /**************************************************************************
962 **************************************************************************/
963 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
965 strcat(cwdbuf, ep->d_name);
966 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
967 if (-1 == (cwd = open(".", O_RDONLY))) {
968 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
971 if (0 != chdir(ep->d_name)) {
972 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
977 ret = dbd_readdir(0, cnid);
981 *(strrchr(cwdbuf, '/')) = 0;
988 Use results of previous checks
995 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
997 /* Dont scanvol on no-appledouble vols */
998 if (vi->v_flags & AFPVOL_NOADOUBLE) {
999 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1003 /* Make this stuff accessible from all funcs easily */
1007 /* Init a fake struct vol with just enough so we can call ea_open and friends */
1008 volume.v_adouble = AD_VERSION2;
1009 volume.v_vfs_ea = volinfo->v_vfs_ea;
1010 initvol_vfs(&volume);
1012 /* Run with umask 0 */
1015 /* Remove trailing slash from volume, chdir to vol */
1016 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
1017 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
1018 strcpy(cwdbuf, volinfo->v_path);
1019 chdir(volinfo->v_path);
1021 /* Start recursion */
1022 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
1029 Remove all CNIDs from dbd that are not in dbd_rebuild
1031 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1033 int ret, deleted = 0;
1034 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1035 struct cnid_dbd_rqst rqst;
1036 struct cnid_dbd_rply rply;
1038 /* jump over rootinfo key */
1039 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1041 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1044 /* Get first id from dbd_rebuild */
1045 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1048 /* Start main loop through dbd: get CNID from dbd */
1049 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1053 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1054 dbd_log(LOGSTD, "Error checkpointing!");
1059 /* This should be the normal case: CNID is in both dbs */
1060 if (dbd_cnid == rebuild_cnid) {
1061 /* Get next CNID from rebuild db */
1062 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1065 } else if (ret == 0) {
1066 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1067 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1068 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1069 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1070 rqst.cnid = htonl(dbd_cnid);
1071 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1072 dbif_txn_close(dbd, ret);
1078 /* Normal case (ret=1): continue while loop */
1082 if (dbd_cnid < rebuild_cnid) {
1083 /* CNID is orphaned -> delete */
1084 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1085 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1086 rqst.cnid = htonl(dbd_cnid);
1087 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1088 dbif_txn_close(dbd, ret);
1094 if (dbd_cnid > rebuild_cnid) {
1095 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1096 dbif_dump(dbd_rebuild, 0);
1097 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1098 dbif_txn_close(dbd, ret);
1099 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1100 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1106 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1107 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1112 Main func called from cmd_dbd.c
1114 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1117 struct db_param db_param = { 0 };
1119 /* Set cachesize for in-memory rebuild db */
1120 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1122 /* Make it accessible for all funcs */
1125 /* We only support unicode volumes ! */
1126 if ( volinfo->v_volcharset != CH_UTF8) {
1127 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1132 /* Get volume stamp */
1133 dbd_getstamp(dbd, &rqst, &rply);
1134 if (rply.result != CNID_DBD_RES_OK)
1136 memcpy(stamp, rply.name, CNID_DEV_LEN);
1138 /* open/create rebuild dbd, copy rootinfo key */
1139 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1141 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1143 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1147 if (setjmp(jmp) != 0)
1148 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1151 if ( (scanvol(volinfo, flags)) != 0)
1155 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1156 other clients in between our pass 1 and 2 */
1157 if (flags & DBD_FLAGS_EXCL)
1158 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1163 dbif_close(dbd_rebuild);