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 **************************************************************************/
927 static unsigned long long statcount = 0;
934 if ((statcount % 10000) == 0) {
935 if (dbd_flags & DBD_FLAGS_STATS)
936 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
937 statcount, (unsigned long long)(time(NULL) - t));
940 /**************************************************************************
942 **************************************************************************/
945 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
946 /* If its a file: skipp all other tests now ! */
947 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
951 /* Check for appledouble file, create if missing, but only if we have addir */
954 adfile_ok = check_adfile(ep->d_name, &st);
958 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
960 /* Now add this object to our rebuild dbd */
962 rqst.cnid = rply.cnid;
963 ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
964 dbif_txn_close(dbd_rebuild, ret);
965 if (rply.result != CNID_DBD_RES_OK) {
966 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
967 cnid, cwdbuf, ep->d_name);
968 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
974 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD)
975 check_eafiles(ep->d_name);
977 /**************************************************************************
979 **************************************************************************/
980 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
982 strcat(cwdbuf, ep->d_name);
983 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
984 if (-1 == (cwd = open(".", O_RDONLY))) {
985 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
988 if (0 != chdir(ep->d_name)) {
989 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
994 ret = dbd_readdir(0, cnid);
998 *(strrchr(cwdbuf, '/')) = 0;
1005 Use results of previous checks
1012 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
1014 /* Dont scanvol on no-appledouble vols */
1015 if (vi->v_flags & AFPVOL_NOADOUBLE) {
1016 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1020 /* Make this stuff accessible from all funcs easily */
1024 /* Init a fake struct vol with just enough so we can call ea_open and friends */
1025 volume.v_adouble = AD_VERSION2;
1026 volume.v_vfs_ea = myvolinfo->v_vfs_ea;
1027 initvol_vfs(&volume);
1029 /* Run with umask 0 */
1032 /* Remove trailing slash from volume, chdir to vol */
1033 if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
1034 myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
1035 strcpy(cwdbuf, myvolinfo->v_path);
1036 chdir(myvolinfo->v_path);
1038 /* Start recursion */
1039 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
1046 Remove all CNIDs from dbd that are not in dbd_rebuild
1048 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1050 int ret = 0, deleted = 0;
1051 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1052 struct cnid_dbd_rqst rqst;
1053 struct cnid_dbd_rply rply;
1055 /* jump over rootinfo key */
1056 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1058 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1061 /* Get first id from dbd_rebuild */
1062 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1065 /* Start main loop through dbd: get CNID from dbd */
1066 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1067 /* Check if we got a termination signal */
1069 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1071 if (deleted > 1000) {
1073 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1074 dbd_log(LOGSTD, "Error checkpointing!");
1079 /* This should be the normal case: CNID is in both dbs */
1080 if (dbd_cnid == rebuild_cnid) {
1081 /* Get next CNID from rebuild db */
1082 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1085 } else if (ret == 0) {
1086 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1087 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1088 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1089 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1090 rqst.cnid = htonl(dbd_cnid);
1091 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1092 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1093 (void)dbif_txn_abort(dbd);
1097 dbif_txn_close(dbd, ret);
1100 /* Check if we got a termination signal */
1102 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1106 /* Normal case (ret=1): continue while loop */
1110 if (dbd_cnid < rebuild_cnid) {
1111 /* CNID is orphaned -> delete */
1112 dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
1113 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1114 rqst.cnid = htonl(dbd_cnid);
1115 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1116 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1117 (void)dbif_txn_abort(dbd);
1120 dbif_txn_close(dbd, ret);
1126 if (dbd_cnid > rebuild_cnid) {
1127 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1128 dbif_dump(dbd_rebuild, 0);
1129 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1130 dbif_txn_close(dbd, ret);
1131 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1132 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1135 } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
1138 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1139 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1143 static const char *get_tmpdb_path(void)
1145 pid_t pid = getpid();
1146 static char path[MAXPATHLEN];
1147 snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
1148 if (mkdir(path, 0755) != 0)
1154 Main func called from cmd_dbd.c
1156 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
1159 struct db_param db_param = { 0 };
1160 const char *tmpdb_path = NULL;
1162 /* Set cachesize for in-memory rebuild db */
1163 db_param.cachesize = 256 * 1024 * 1024; /* 128 MB */
1165 /* Make it accessible for all funcs */
1168 /* We only support unicode volumes ! */
1169 if ( vi->v_volcharset != CH_UTF8) {
1170 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
1175 /* Get volume stamp */
1176 dbd_getstamp(dbd, &rqst, &rply);
1177 if (rply.result != CNID_DBD_RES_OK)
1179 memcpy(stamp, rply.name, CNID_DEV_LEN);
1181 /* open/create rebuild dbd, copy rootinfo key */
1182 tmpdb_path = get_tmpdb_path();
1183 if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
1188 if (dbif_env_open(dbd_rebuild,
1190 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
1191 dbd_log(LOGSTD, "error opening tmp database!");
1195 if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
1200 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
1206 if (setjmp(jmp) != 0) {
1207 ret = 0; /* Got signal, jump from dbd_readdir */
1212 if ( (scanvol(vi, flags)) != 0) {
1218 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1219 other clients in between our pass 1 and 2 */
1220 if (flags & DBD_FLAGS_EXCL)
1221 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1226 dbd_log(LOGDEBUG, "Closing tmp db");
1227 dbif_close(dbd_rebuild);
1230 char cmd[8 + MAXPATHLEN];
1231 snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
1232 dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
1234 snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);