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)
296 int adflags = ADFLAGS_HF;
300 if (dbd_flags & DBD_FLAGS_CLEANUP)
303 if (S_ISREG(st->st_mode))
304 adflags |= ADFLAGS_DIR;
306 adname = myvolinfo->ad_path(fname, adflags);
308 if ((ret = access( adname, F_OK)) != 0) {
309 if (errno != ENOENT) {
310 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
311 cwdbuf, adname, strerror(errno));
314 /* Missing. Log and create it */
315 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
317 if (dbd_flags & DBD_FLAGS_SCAN)
318 /* Scan only requested, dont change anything */
322 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
324 if ((ret = ad_open(&ad, fname, adflags, O_CREAT | O_RDWR, 0666)) != 0) {
325 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
326 cwdbuf, adname, strerror(errno));
331 /* Set name in ad-file */
332 ad_setname(&ad, utompath((char *)fname));
334 ad_close_metadata(&ad);
336 chown(adname, st->st_uid, st->st_gid);
337 /* FIXME: should we inherit mode too here ? */
339 chmod(adname, st->st_mode);
342 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
343 if (ad_open(&ad, fname, adflags, O_RDONLY) != 0) {
344 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
347 ad_close_metadata(&ad);
353 Remove all files with file::EA* from adouble dir
355 static void remove_eafiles(const char *name, struct ea *ea)
359 char eaname[MAXPATHLEN];
361 strlcpy(eaname, name, sizeof(eaname));
362 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
363 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
367 if ((chdir(ADv2_DIRNAME)) != 0) {
368 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
369 cwdbuf, ADv2_DIRNAME, strerror(errno));
373 if ((dp = opendir(".")) == NULL) {
374 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
375 cwdbuf, ADv2_DIRNAME, strerror(errno));
379 while ((ep = readdir(dp))) {
380 if (strstr(ep->d_name, eaname) != NULL) {
381 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
382 cwdbuf, ADv2_DIRNAME, ep->d_name);
383 if ((unlink(ep->d_name)) != 0) {
384 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
385 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
396 Check Extended Attributes files
398 static int check_eafiles(const char *fname)
400 unsigned int count = 0;
406 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
409 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
410 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
411 remove_eafiles(fname, &ea);
416 while (count < ea.ea_count) {
417 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
420 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
422 if (lstat(eaname, &st) != 0) {
424 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
426 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
428 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
429 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
431 if ((unlink(eaname)) != 0)
432 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
433 cwdbuf, eaname, strerror(errno));
437 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
438 free((*ea.ea_entries)[count].ea_name);
439 (*ea.ea_entries)[count].ea_name = NULL;
450 Check for .AppleDouble folder and .Parent, create if missing
452 static int check_addir(int volroot)
454 int addir_ok, adpar_ok;
459 if (dbd_flags & DBD_FLAGS_CLEANUP)
462 /* Check for ad-dir */
463 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
464 if (errno != ENOENT) {
465 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
468 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
471 /* Check for ".Parent" */
472 if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
473 if (errno != ENOENT) {
474 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
475 cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
478 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
481 /* Is one missing ? */
482 if ((addir_ok != 0) || (adpar_ok != 0)) {
483 /* Yes, but are we only scanning ? */
484 if (dbd_flags & DBD_FLAGS_SCAN) {
485 /* Yes: missing .Parent is not a problem, but missing ad-dir
486 causes later checking of ad-files to fail. So we have to return appropiately */
489 else /* (adpar_ok != 0) */
493 /* Create ad dir and set name */
494 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
496 if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR, O_CREAT | O_RDWR, 0777) != 0) {
497 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
501 /* Get basename of cwd from cwdbuf */
502 mname = utompath(strrchr(cwdbuf, '/') + 1);
504 /* Update name in ad file */
505 ad_setname(&ad, mname);
507 ad_close_metadata(&ad);
509 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
510 if ((lstat(".", &st)) != 0) {
511 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
514 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
515 chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
522 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
524 0 = name is not an EA file
525 1 = name is an EA file and no problem was found
526 -1 = name is an EA file and data fork is gone
528 static int check_eafile_in_adouble(const char *name)
531 char *namep, *namedup = NULL;
533 /* Check if this is an AFPVOL_EA_AD vol */
534 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) {
535 /* Does the filename contain "::EA" ? */
536 namedup = strdup(name);
537 if ((namep = strstr(namedup, "::EA")) == NULL) {
541 /* File contains "::EA" so it's an EA file. Check for data file */
543 /* Get string before "::EA" from EA filename */
545 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
547 if ((access( pname, F_OK)) == 0) {
552 if (errno != ENOENT) {
553 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
554 cwdbuf, name, strerror(errno));
558 /* Orphaned EA file*/
559 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
560 cwdbuf, ADv2_DIRNAME, name);
562 if (dbd_flags & DBD_FLAGS_SCAN)
563 /* Scan only requested, dont change anything */
566 if ((unlink(name)) != 0) {
567 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
568 cwdbuf, ADv2_DIRNAME, name);
572 } /* if AFPVOL_EA_AD */
582 Check files and dirs inside .AppleDouble folder:
583 - remove orphaned files
586 static int read_addir(void)
592 if ((chdir(ADv2_DIRNAME)) != 0) {
593 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
594 cwdbuf, ADv2_DIRNAME, strerror(errno));
598 if ((dp = opendir(".")) == NULL) {
599 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
600 cwdbuf, ADv2_DIRNAME, strerror(errno));
604 while ((ep = readdir(dp))) {
605 /* Check if its "." or ".." */
606 if (DIR_DOT_OR_DOTDOT(ep->d_name))
609 if (STRCMP(ep->d_name, ==, ".Parent"))
612 if ((lstat(ep->d_name, &st)) < 0) {
613 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
614 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
619 if (S_ISDIR(st.st_mode)) {
620 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
621 ep->d_name, cwdbuf, ADv2_DIRNAME);
625 /* Check if for orphaned and corrupt Extended Attributes file */
626 if (check_eafile_in_adouble(ep->d_name) != 0)
629 /* Check for data file */
630 strcpy(pname + 3, ep->d_name);
631 if ((access( pname, F_OK)) != 0) {
632 if (errno != ENOENT) {
633 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
634 cwdbuf, pname, strerror(errno));
637 /* Orphaned ad-file*/
638 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
639 cwdbuf, ADv2_DIRNAME, ep->d_name);
641 if (dbd_flags & DBD_FLAGS_SCAN)
642 /* Scan only requested, dont change anything */
645 if ((unlink(ep->d_name)) != 0) {
646 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
647 cwdbuf, ADv2_DIRNAME, ep->d_name);
653 if ((chdir("..")) != 0) {
654 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
655 cwdbuf, strerror(errno));
656 /* This really is EOT! */
657 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
666 Check CNID for a file/dir, both from db and from ad-file.
667 For detailed specs see intro.
669 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
672 cnid_t db_cnid, ad_cnid;
675 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
677 if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
678 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
679 if (ad_open(&ad, name, adflags, O_RDWR) != 0) {
681 if (dbd_flags & DBD_FLAGS_CLEANUP)
684 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
688 if (dbd_flags & DBD_FLAGS_FORCE) {
689 ad_cnid = ad_forcegetid(&ad);
690 /* This ensures the changed stamp is written */
691 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
695 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
698 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
700 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
702 ad_close_metadata(&ad);
705 /* Get CNID from database */
707 /* Prepare request data */
708 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
709 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
712 if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
713 rqst.dev = st->st_dev;
714 rqst.ino = st->st_ino;
715 rqst.type = S_ISDIR(st->st_mode)?1:0;
716 rqst.name = (char *)name;
717 rqst.namelen = strlen(name);
719 /* Query the database */
720 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
721 dbif_txn_close(dbd, ret);
722 if (rply.result == CNID_DBD_RES_OK) {
724 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
725 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
726 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
729 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
733 /* Compare results from both CNID searches */
734 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
735 /* Everything is fine */
737 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
738 /* Mismatch ? Delete both from db and re-add data from file */
739 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
740 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
742 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
743 dbif_txn_close(dbd, ret);
746 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
747 dbif_txn_close(dbd, ret);
749 ret = dbd_rebuild_add(dbd, &rqst, &rply);
750 dbif_txn_close(dbd, ret);
753 } else if (ad_cnid && (db_cnid == 0)) {
754 /* in ad-file but not in db */
755 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
756 /* Ensure the cnid from the ad-file is not already occupied by another file */
757 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
761 ret = dbd_resolve(dbd, &rqst, &rply);
762 if (ret == CNID_DBD_RES_OK) {
763 /* Occupied! Choose another, update ad-file */
764 ret = dbd_add(dbd, &rqst, &rply, 1);
765 dbif_txn_close(dbd, ret);
767 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
769 if ((myvolinfo->v_flags & AFPVOL_CACHE)
771 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
772 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
773 cwdbuf, name, ntohl(db_cnid));
774 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
775 if (ad_open(&ad, name, adflags, O_RDWR) != 0) {
776 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
777 cwdbuf, name, strerror(errno));
780 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
782 ad_close_metadata(&ad);
787 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
788 cwdbuf, name, ntohl(ad_cnid));
790 ret = dbd_rebuild_add(dbd, &rqst, &rply);
791 dbif_txn_close(dbd, ret);
794 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
795 /* No CNID at all, we clearly have to allocate a fresh one... */
796 /* Note: the next test will use this new CNID too! */
797 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
799 ret = dbd_add(dbd, &rqst, &rply, 1);
800 dbif_txn_close(dbd, ret);
802 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
806 if ((ad_cnid == 0) && db_cnid) {
807 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
808 if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
809 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
810 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
811 cwdbuf, name, ntohl(db_cnid));
812 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
813 if (ad_open(&ad, name, adflags, O_RDWR) != 0) {
814 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
815 cwdbuf, name, strerror(errno));
818 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
820 ad_close_metadata(&ad);
830 This is called recursively for all dirs.
831 volroot=1 means we're in the volume root dir, 0 means we aren't.
832 We use this when checking for netatalk private folders like .AppleDB.
833 did is our parents CNID.
835 static int dbd_readdir(int volroot, cnid_t did)
837 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
842 static struct stat st; /* Save some stack space */
844 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
845 if ((addir_ok = check_addir(volroot)) != 0)
846 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
847 /* Fatal on rebuild run, continue if only scanning ! */
850 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
852 if ((read_addir()) != 0)
853 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
854 /* Fatal on rebuild run, continue if only scanning ! */
857 if ((dp = opendir (".")) == NULL) {
858 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
862 while ((ep = readdir (dp))) {
863 /* Check if we got a termination signal */
865 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
867 /* Check if its "." or ".." */
868 if (DIR_DOT_OR_DOTDOT(ep->d_name))
871 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
872 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
874 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
878 /* Check for special folders in volume root e.g. ".zfs" */
880 if ((name = check_special_dirs(ep->d_name)) != NULL) {
881 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
886 /* Skip .AppleDouble dir in this loop */
887 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
890 if ((ret = lstat(ep->d_name, &st)) < 0) {
891 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
892 cwdbuf, ep->d_name, strerror(errno));
896 switch (st.st_mode & S_IFMT) {
901 adflags = ADFLAGS_DIR;
904 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
906 ret = check_symlink(ep->d_name, &adflags);
910 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
914 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
915 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
916 if ((unlink(ep->d_name)) != 0) {
917 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
923 /**************************************************************************
925 **************************************************************************/
928 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
929 /* If its a file: skipp all other tests now ! */
930 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
934 /* Check for appledouble file, create if missing, but only if we have addir */
937 adfile_ok = check_adfile(ep->d_name, &st);
941 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
943 /* Now add this object to our rebuild dbd */
945 rqst.cnid = rply.cnid;
946 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
947 if (rply.result != CNID_DBD_RES_OK) {
948 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
949 cnid, cwdbuf, ep->d_name);
950 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
956 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD)
957 check_eafiles(ep->d_name);
959 /**************************************************************************
961 **************************************************************************/
962 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
964 strcat(cwdbuf, ep->d_name);
965 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
966 if (-1 == (cwd = open(".", O_RDONLY))) {
967 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
970 if (0 != chdir(ep->d_name)) {
971 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
976 ret = dbd_readdir(0, cnid);
980 *(strrchr(cwdbuf, '/')) = 0;
987 Use results of previous checks
994 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
996 /* Dont scanvol on no-appledouble vols */
997 if (vi->v_flags & AFPVOL_NOADOUBLE) {
998 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1002 /* Make this stuff accessible from all funcs easily */
1006 /* Init a fake struct vol with just enough so we can call ea_open and friends */
1007 volume.v_adouble = AD_VERSION2;
1008 volume.v_vfs_ea = myvolinfo->v_vfs_ea;
1009 initvol_vfs(&volume);
1011 /* Run with umask 0 */
1014 /* Remove trailing slash from volume, chdir to vol */
1015 if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
1016 myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
1017 strcpy(cwdbuf, myvolinfo->v_path);
1018 chdir(myvolinfo->v_path);
1020 /* Start recursion */
1021 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
1028 Remove all CNIDs from dbd that are not in dbd_rebuild
1030 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1032 int ret, deleted = 0;
1033 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1034 struct cnid_dbd_rqst rqst;
1035 struct cnid_dbd_rply rply;
1037 /* jump over rootinfo key */
1038 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1040 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1043 /* Get first id from dbd_rebuild */
1044 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1047 /* Start main loop through dbd: get CNID from dbd */
1048 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1049 /* Check if we got a termination signal */
1051 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1055 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1056 dbd_log(LOGSTD, "Error checkpointing!");
1061 /* This should be the normal case: CNID is in both dbs */
1062 if (dbd_cnid == rebuild_cnid) {
1063 /* Get next CNID from rebuild db */
1064 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1067 } else if (ret == 0) {
1068 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1069 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1070 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1071 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1072 rqst.cnid = htonl(dbd_cnid);
1073 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1074 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1075 (void)dbif_txn_abort(dbd);
1079 dbif_txn_close(dbd, ret);
1082 /* Check if we got a termination signal */
1084 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1088 /* Normal case (ret=1): continue while loop */
1092 if (dbd_cnid < rebuild_cnid) {
1093 /* CNID is orphaned -> delete */
1094 dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
1095 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1096 rqst.cnid = htonl(dbd_cnid);
1097 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1098 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1099 (void)dbif_txn_abort(dbd);
1102 dbif_txn_close(dbd, ret);
1108 if (dbd_cnid > rebuild_cnid) {
1109 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1110 dbif_dump(dbd_rebuild, 0);
1111 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1112 dbif_txn_close(dbd, ret);
1113 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1114 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1117 } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
1120 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1121 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1126 Main func called from cmd_dbd.c
1128 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
1131 struct db_param db_param = { 0 };
1133 /* Set cachesize for in-memory rebuild db */
1134 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1136 /* Make it accessible for all funcs */
1139 /* We only support unicode volumes ! */
1140 if ( vi->v_volcharset != CH_UTF8) {
1141 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
1146 /* Get volume stamp */
1147 dbd_getstamp(dbd, &rqst, &rply);
1148 if (rply.result != CNID_DBD_RES_OK)
1150 memcpy(stamp, rply.name, CNID_DEV_LEN);
1152 /* open/create rebuild dbd, copy rootinfo key */
1153 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1155 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1157 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1161 if (setjmp(jmp) != 0)
1162 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1165 if ( (scanvol(vi, flags)) != 0)
1169 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1170 other clients in between our pass 1 and 2 */
1171 if (flags & DBD_FLAGS_EXCL)
1172 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1177 dbif_close(dbd_rebuild);