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>
36 #include <atalk/acl.h>
43 /* Some defines to ease code parsing */
44 #define ADDIR_OK (addir_ok == 0)
45 #define ADFILE_OK (adfile_ok == 0)
48 static struct volinfo *myvolinfo;
49 static char cwdbuf[MAXPATHLEN+1];
51 static DBD *dbd_rebuild;
52 static dbd_flags_t dbd_flags;
53 static char stamp[CNID_DEV_LEN];
54 static char *netatalk_dirs[] = {
59 static char *special_dirs[] = {
63 static struct cnid_dbd_rqst rqst;
64 static struct cnid_dbd_rply rply;
66 static struct vol volume; /* fake it for ea_open */
67 static char pname[MAXPATHLEN] = "../";
70 Taken form afpd/desktop.c
72 static char *utompath(char *upath)
74 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
76 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
84 outlen = strlen(upath);
86 if ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER))
87 flags |= CONV_TOUPPER;
88 else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER))
89 flags |= CONV_TOLOWER;
91 if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
92 flags |= CONV__EILSEQ;
95 /* convert charsets */
96 if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset,
98 myvolinfo->v_maccharset,
99 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
100 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
101 myvolinfo->v_volcodepage, myvolinfo->v_maccodepage, u);
109 Taken form afpd/desktop.c
111 static char *mtoupath(char *mpath)
113 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
122 if ( *mpath == '\0' ) {
126 /* set conversion flags */
127 if (!(myvolinfo->v_flags & AFPVOL_NOHEX))
128 flags |= CONV_ESCAPEHEX;
129 if (!(myvolinfo->v_flags & AFPVOL_USEDOTS))
130 flags |= CONV_ESCAPEDOTS;
132 if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER))
133 flags |= CONV_TOUPPER;
134 else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER))
135 flags |= CONV_TOLOWER;
137 if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
138 flags |= CONV__EILSEQ;
147 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
148 myvolinfo->v_volcharset,
149 myvolinfo->v_maccharset,
150 m, inplen, u, outlen, &flags)) ) {
151 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
152 myvolinfo->v_volcodepage, mpath);
161 Check if "name" is pointing to
162 a) an object inside the current volume (return 0)
163 b) an object outside the current volume (return 1)
164 Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags
165 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 (myvolinfo->v_path[i]) {
226 if ((pathbuf[i] == 0) || (myvolinfo->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 special names
277 Returns pointer to name or NULL.
279 static const char *check_special_dirs(const char *name)
283 for (c=0; special_dirs[c]; c++) {
284 if ((strcmp(name, special_dirs[c])) == 0)
285 return special_dirs[c];
291 Check for .AppleDouble file, create if missing
293 static int check_adfile(const char *fname, const struct stat *st)
299 if (dbd_flags & DBD_FLAGS_CLEANUP)
302 if (S_ISREG(st->st_mode))
305 adflags = ADFLAGS_DIR;
307 adname = myvolinfo->ad_path(fname, adflags);
309 if ((ret = access( adname, F_OK)) != 0) {
310 if (errno != ENOENT) {
311 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
312 cwdbuf, adname, strerror(errno));
315 /* Missing. Log and create it */
316 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
318 if (dbd_flags & DBD_FLAGS_SCAN)
319 /* Scan only requested, dont change anything */
323 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
325 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
326 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
327 cwdbuf, adname, strerror(errno));
332 /* Set name in ad-file */
333 ad_setname(&ad, utompath((char *)fname));
335 ad_close_metadata(&ad);
337 chown(adname, st->st_uid, st->st_gid);
338 /* FIXME: should we inherit mode too here ? */
340 chmod(adname, st->st_mode);
343 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
344 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
345 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
348 ad_close_metadata(&ad);
354 Remove all files with file::EA* from adouble dir
356 static void remove_eafiles(const char *name, struct ea *ea)
360 char eaname[MAXPATHLEN];
362 strlcpy(eaname, name, sizeof(eaname));
363 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
364 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
368 if ((chdir(ADv2_DIRNAME)) != 0) {
369 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
370 cwdbuf, ADv2_DIRNAME, strerror(errno));
374 if ((dp = opendir(".")) == NULL) {
375 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
376 cwdbuf, ADv2_DIRNAME, strerror(errno));
380 while ((ep = readdir(dp))) {
381 if (strstr(ep->d_name, eaname) != NULL) {
382 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
383 cwdbuf, ADv2_DIRNAME, ep->d_name);
384 if ((unlink(ep->d_name)) != 0) {
385 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
386 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
397 Check Extended Attributes files
399 static int check_eafiles(const char *fname)
401 unsigned int count = 0;
407 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
410 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
411 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
412 remove_eafiles(fname, &ea);
417 while (count < ea.ea_count) {
418 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
421 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
423 if (lstat(eaname, &st) != 0) {
425 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
427 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
429 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
430 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
432 if ((unlink(eaname)) != 0)
433 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
434 cwdbuf, eaname, strerror(errno));
438 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
439 free((*ea.ea_entries)[count].ea_name);
440 (*ea.ea_entries)[count].ea_name = NULL;
451 Check for .AppleDouble folder and .Parent, create if missing
453 static int check_addir(int volroot)
455 int addir_ok, adpar_ok;
460 if (dbd_flags & DBD_FLAGS_CLEANUP)
463 /* Check for ad-dir */
464 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
465 if (errno != ENOENT) {
466 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
469 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
472 /* Check for ".Parent" */
473 if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
474 if (errno != ENOENT) {
475 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
476 cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
479 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
482 /* Is one missing ? */
483 if ((addir_ok != 0) || (adpar_ok != 0)) {
484 /* Yes, but are we only scanning ? */
485 if (dbd_flags & DBD_FLAGS_SCAN) {
486 /* Yes: missing .Parent is not a problem, but missing ad-dir
487 causes later checking of ad-files to fail. So we have to return appropiately */
490 else /* (adpar_ok != 0) */
494 /* Create ad dir and set name */
495 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
497 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
498 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
502 /* Get basename of cwd from cwdbuf */
503 mname = utompath(strrchr(cwdbuf, '/') + 1);
505 /* Update name in ad file */
506 ad_setname(&ad, mname);
508 ad_close_metadata(&ad);
510 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
511 if ((lstat(".", &st)) != 0) {
512 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
515 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
516 chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
523 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
525 0 = name is not an EA file
526 1 = name is an EA file and no problem was found
527 -1 = name is an EA file and data fork is gone
529 static int check_eafile_in_adouble(const char *name)
532 char *namep, *namedup = NULL;
534 /* Check if this is an AFPVOL_EA_AD vol */
535 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) {
536 /* Does the filename contain "::EA" ? */
537 namedup = strdup(name);
538 if ((namep = strstr(namedup, "::EA")) == NULL) {
542 /* File contains "::EA" so it's an EA file. Check for data file */
544 /* Get string before "::EA" from EA filename */
546 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
548 if ((access( pname, F_OK)) == 0) {
553 if (errno != ENOENT) {
554 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
555 cwdbuf, name, strerror(errno));
559 /* Orphaned EA file*/
560 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
561 cwdbuf, ADv2_DIRNAME, name);
563 if (dbd_flags & DBD_FLAGS_SCAN)
564 /* Scan only requested, dont change anything */
567 if ((unlink(name)) != 0) {
568 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
569 cwdbuf, ADv2_DIRNAME, name);
573 } /* if AFPVOL_EA_AD */
583 Check files and dirs inside .AppleDouble folder:
584 - remove orphaned files
587 static int read_addir(void)
593 if ((chdir(ADv2_DIRNAME)) != 0) {
594 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
595 cwdbuf, ADv2_DIRNAME, strerror(errno));
599 if ((dp = opendir(".")) == NULL) {
600 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
601 cwdbuf, ADv2_DIRNAME, strerror(errno));
605 while ((ep = readdir(dp))) {
606 /* Check if its "." or ".." */
607 if (DIR_DOT_OR_DOTDOT(ep->d_name))
610 if (STRCMP(ep->d_name, ==, ".Parent"))
613 if ((lstat(ep->d_name, &st)) < 0) {
614 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
615 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
620 if (S_ISDIR(st.st_mode)) {
621 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
622 ep->d_name, cwdbuf, ADv2_DIRNAME);
626 /* Check if for orphaned and corrupt Extended Attributes file */
627 if (check_eafile_in_adouble(ep->d_name) != 0)
630 /* Check for data file */
631 strcpy(pname + 3, ep->d_name);
632 if ((access( pname, F_OK)) != 0) {
633 if (errno != ENOENT) {
634 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
635 cwdbuf, pname, strerror(errno));
638 /* Orphaned ad-file*/
639 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
640 cwdbuf, ADv2_DIRNAME, ep->d_name);
642 if (dbd_flags & DBD_FLAGS_SCAN)
643 /* Scan only requested, dont change anything */
646 if ((unlink(ep->d_name)) != 0) {
647 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
648 cwdbuf, ADv2_DIRNAME, ep->d_name);
654 if ((chdir("..")) != 0) {
655 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
656 cwdbuf, strerror(errno));
657 /* This really is EOT! */
658 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
667 Check CNID for a file/dir, both from db and from ad-file.
668 For detailed specs see intro.
670 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
673 cnid_t db_cnid, ad_cnid;
676 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
678 if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
679 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
680 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
682 if (dbd_flags & DBD_FLAGS_CLEANUP)
685 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
689 if (dbd_flags & DBD_FLAGS_FORCE) {
690 ad_cnid = ad_forcegetid(&ad);
691 /* This ensures the changed stamp is written */
692 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
696 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
699 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
701 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
703 ad_close_metadata(&ad);
706 /* Get CNID from database */
708 /* Prepare request data */
709 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
710 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
713 if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
714 rqst.dev = st->st_dev;
715 rqst.ino = st->st_ino;
716 rqst.type = S_ISDIR(st->st_mode)?1:0;
717 rqst.name = (char *)name;
718 rqst.namelen = strlen(name);
720 /* Query the database */
721 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
722 dbif_txn_close(dbd, ret);
723 if (rply.result == CNID_DBD_RES_OK) {
725 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
726 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
727 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
730 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
734 /* Compare results from both CNID searches */
735 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
736 /* Everything is fine */
738 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
739 /* Mismatch ? Delete both from db and re-add data from file */
740 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
741 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
743 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
744 dbif_txn_close(dbd, ret);
747 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
748 dbif_txn_close(dbd, ret);
750 ret = dbd_rebuild_add(dbd, &rqst, &rply);
751 dbif_txn_close(dbd, ret);
754 } else if (ad_cnid && (db_cnid == 0)) {
755 /* in ad-file but not in db */
756 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
757 /* Ensure the cnid from the ad-file is not already occupied by another file */
758 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
762 ret = dbd_resolve(dbd, &rqst, &rply);
763 if (ret == CNID_DBD_RES_OK) {
764 /* Occupied! Choose another, update ad-file */
765 ret = dbd_add(dbd, &rqst, &rply, 1);
766 dbif_txn_close(dbd, ret);
768 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
770 if ((myvolinfo->v_flags & AFPVOL_CACHE)
772 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
773 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
774 cwdbuf, name, ntohl(db_cnid));
775 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
776 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
777 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
778 cwdbuf, name, strerror(errno));
781 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
783 ad_close_metadata(&ad);
788 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
789 cwdbuf, name, ntohl(ad_cnid));
791 ret = dbd_rebuild_add(dbd, &rqst, &rply);
792 dbif_txn_close(dbd, ret);
795 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
796 /* No CNID at all, we clearly have to allocate a fresh one... */
797 /* Note: the next test will use this new CNID too! */
798 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
800 ret = dbd_add(dbd, &rqst, &rply, 1);
801 dbif_txn_close(dbd, ret);
803 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
807 if ((ad_cnid == 0) && db_cnid) {
808 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
809 if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
810 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
811 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
812 cwdbuf, name, ntohl(db_cnid));
813 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
814 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
815 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
816 cwdbuf, name, strerror(errno));
819 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
821 ad_close_metadata(&ad);
831 This is called recursively for all dirs.
832 volroot=1 means we're in the volume root dir, 0 means we aren't.
833 We use this when checking for netatalk private folders like .AppleDB.
834 did is our parents CNID.
836 static int dbd_readdir(int volroot, cnid_t did)
838 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
843 static struct stat st; /* Save some stack space */
845 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
846 if ((addir_ok = check_addir(volroot)) != 0)
847 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
848 /* Fatal on rebuild run, continue if only scanning ! */
851 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
853 if ((read_addir()) != 0)
854 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
855 /* Fatal on rebuild run, continue if only scanning ! */
858 if ((dp = opendir (".")) == NULL) {
859 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
863 while ((ep = readdir (dp))) {
864 /* Check if we got a termination signal */
866 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
868 /* Check if its "." or ".." */
869 if (DIR_DOT_OR_DOTDOT(ep->d_name))
872 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
873 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
875 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
879 /* Check for special folders in volume root e.g. ".zfs" */
881 if ((name = check_special_dirs(ep->d_name)) != NULL) {
882 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 (myvolinfo->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 = myvolinfo->v_vfs_ea;
1010 initvol_vfs(&volume);
1012 /* Run with umask 0 */
1015 /* Remove trailing slash from volume, chdir to vol */
1016 if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
1017 myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
1018 strcpy(cwdbuf, myvolinfo->v_path);
1019 chdir(myvolinfo->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) {
1050 /* Check if we got a termination signal */
1052 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1056 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1057 dbd_log(LOGSTD, "Error checkpointing!");
1062 /* This should be the normal case: CNID is in both dbs */
1063 if (dbd_cnid == rebuild_cnid) {
1064 /* Get next CNID from rebuild db */
1065 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1068 } else if (ret == 0) {
1069 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1070 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1071 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1072 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1073 rqst.cnid = htonl(dbd_cnid);
1074 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1075 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1076 (void)dbif_txn_abort(dbd);
1080 dbif_txn_close(dbd, ret);
1083 /* Check if we got a termination signal */
1085 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1089 /* Normal case (ret=1): continue while loop */
1093 if (dbd_cnid < rebuild_cnid) {
1094 /* CNID is orphaned -> delete */
1095 dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
1096 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1097 rqst.cnid = htonl(dbd_cnid);
1098 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1099 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1100 (void)dbif_txn_abort(dbd);
1103 dbif_txn_close(dbd, ret);
1109 if (dbd_cnid > rebuild_cnid) {
1110 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1111 dbif_dump(dbd_rebuild, 0);
1112 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1113 dbif_txn_close(dbd, ret);
1114 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1115 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1118 } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
1121 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1122 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1127 Main func called from cmd_dbd.c
1129 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
1132 struct db_param db_param = { 0 };
1134 /* Set cachesize for in-memory rebuild db */
1135 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1137 /* Make it accessible for all funcs */
1140 /* We only support unicode volumes ! */
1141 if ( vi->v_volcharset != CH_UTF8) {
1142 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
1147 /* Get volume stamp */
1148 dbd_getstamp(dbd, &rqst, &rply);
1149 if (rply.result != CNID_DBD_RES_OK)
1151 memcpy(stamp, rply.name, CNID_DEV_LEN);
1153 /* open/create rebuild dbd, copy rootinfo key */
1154 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1156 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1158 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1162 if (setjmp(jmp) != 0)
1163 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1166 if ( (scanvol(vi, flags)) != 0)
1170 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1171 other clients in between our pass 1 and 2 */
1172 if (flags & DBD_FLAGS_EXCL)
1173 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1178 dbif_close(dbd_rebuild);