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>
37 #include <atalk/compat.h>
44 /* Some defines to ease code parsing */
45 #define ADDIR_OK (addir_ok == 0)
46 #define ADFILE_OK (adfile_ok == 0)
49 static struct volinfo *myvolinfo;
50 static char cwdbuf[MAXPATHLEN+1];
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 ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER))
88 flags |= CONV_TOUPPER;
89 else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER))
90 flags |= CONV_TOLOWER;
92 if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
93 flags |= CONV__EILSEQ;
96 /* convert charsets */
97 if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset,
99 myvolinfo->v_maccharset,
100 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
101 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
102 myvolinfo->v_volcodepage, myvolinfo->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 (!(myvolinfo->v_flags & AFPVOL_NOHEX))
129 flags |= CONV_ESCAPEHEX;
130 if (!(myvolinfo->v_flags & AFPVOL_USEDOTS))
131 flags |= CONV_ESCAPEDOTS;
133 if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER))
134 flags |= CONV_TOUPPER;
135 else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER))
136 flags |= CONV_TOLOWER;
138 if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) {
139 flags |= CONV__EILSEQ;
148 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
149 myvolinfo->v_volcharset,
150 myvolinfo->v_maccharset,
151 m, inplen, u, outlen, &flags)) ) {
152 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
153 myvolinfo->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 (myvolinfo->v_path[i]) {
227 if ((pathbuf[i] == 0) || (myvolinfo->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)
297 int adflags = ADFLAGS_HF;
301 if (dbd_flags & DBD_FLAGS_CLEANUP)
304 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(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 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(&ad, ADFLAGS_HF);
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(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) {
345 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
348 ad_close(&ad, ADFLAGS_HF);
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));
394 if ((chdir("..")) != 0) {
395 dbd_log(LOGSTD, "Couldn't chdir to '%s': %s", cwdbuf, strerror(errno));
396 /* we can't proceed */
397 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
402 Check Extended Attributes files
404 static int check_eafiles(const char *fname)
406 unsigned int count = 0;
412 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
415 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
416 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
417 remove_eafiles(fname, &ea);
422 while (count < ea.ea_count) {
423 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
426 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
428 if (lstat(eaname, &st) != 0) {
430 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
432 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
434 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
435 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
437 if ((unlink(eaname)) != 0)
438 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
439 cwdbuf, eaname, strerror(errno));
443 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
444 free((*ea.ea_entries)[count].ea_name);
445 (*ea.ea_entries)[count].ea_name = NULL;
456 Check for .AppleDouble folder and .Parent, create if missing
458 static int check_addir(int volroot)
460 int addir_ok, adpar_ok;
465 if (dbd_flags & DBD_FLAGS_CLEANUP)
468 /* Check for ad-dir */
469 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
470 if (errno != ENOENT) {
471 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
474 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
477 /* Check for ".Parent" */
478 if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
479 if (errno != ENOENT) {
480 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
481 cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
484 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
487 /* Is one missing ? */
488 if ((addir_ok != 0) || (adpar_ok != 0)) {
489 /* Yes, but are we only scanning ? */
490 if (dbd_flags & DBD_FLAGS_SCAN) {
491 /* Yes: missing .Parent is not a problem, but missing ad-dir
492 causes later checking of ad-files to fail. So we have to return appropiately */
495 else /* (adpar_ok != 0) */
499 /* Create ad dir and set name */
500 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
502 if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
503 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
507 /* Get basename of cwd from cwdbuf */
508 mname = utompath(strrchr(cwdbuf, '/') + 1);
510 /* Update name in ad file */
511 ad_setname(&ad, mname);
513 ad_close(&ad, ADFLAGS_HF);
515 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
516 if ((lstat(".", &st)) != 0) {
517 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
520 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
521 chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
528 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
530 0 = name is not an EA file
531 1 = name is an EA file and no problem was found
532 -1 = name is an EA file and data fork is gone
534 static int check_eafile_in_adouble(const char *name)
537 char *namep, *namedup = NULL;
539 /* Check if this is an AFPVOL_EA_AD vol */
540 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) {
541 /* Does the filename contain "::EA" ? */
542 namedup = strdup(name);
543 if ((namep = strstr(namedup, "::EA")) == NULL) {
547 /* File contains "::EA" so it's an EA file. Check for data file */
549 /* Get string before "::EA" from EA filename */
551 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
553 if ((access( pname, F_OK)) == 0) {
558 if (errno != ENOENT) {
559 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
560 cwdbuf, name, strerror(errno));
564 /* Orphaned EA file*/
565 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
566 cwdbuf, ADv2_DIRNAME, name);
568 if (dbd_flags & DBD_FLAGS_SCAN)
569 /* Scan only requested, dont change anything */
572 if ((unlink(name)) != 0) {
573 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
574 cwdbuf, ADv2_DIRNAME, name);
578 } /* if AFPVOL_EA_AD */
588 Check files and dirs inside .AppleDouble folder:
589 - remove orphaned files
592 static int read_addir(void)
598 if ((chdir(ADv2_DIRNAME)) != 0) {
599 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
600 cwdbuf, ADv2_DIRNAME, strerror(errno));
604 if ((dp = opendir(".")) == NULL) {
605 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
606 cwdbuf, ADv2_DIRNAME, strerror(errno));
610 while ((ep = readdir(dp))) {
611 /* Check if its "." or ".." */
612 if (DIR_DOT_OR_DOTDOT(ep->d_name))
615 if (STRCMP(ep->d_name, ==, ".Parent"))
618 if ((lstat(ep->d_name, &st)) < 0) {
619 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
620 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
625 if (S_ISDIR(st.st_mode)) {
626 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
627 ep->d_name, cwdbuf, ADv2_DIRNAME);
631 /* Check if for orphaned and corrupt Extended Attributes file */
632 if (check_eafile_in_adouble(ep->d_name) != 0)
635 /* Check for data file */
636 strcpy(pname + 3, ep->d_name);
637 if ((access( pname, F_OK)) != 0) {
638 if (errno != ENOENT) {
639 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
640 cwdbuf, pname, strerror(errno));
643 /* Orphaned ad-file*/
644 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
645 cwdbuf, ADv2_DIRNAME, ep->d_name);
647 if (dbd_flags & DBD_FLAGS_SCAN)
648 /* Scan only requested, dont change anything */
651 if ((unlink(ep->d_name)) != 0) {
652 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
653 cwdbuf, ADv2_DIRNAME, ep->d_name);
659 if ((chdir("..")) != 0) {
660 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
661 cwdbuf, strerror(errno));
662 /* This really is EOT! */
663 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
672 Check CNID for a file/dir, both from db and from ad-file.
673 For detailed specs see intro.
675 @return Correct CNID of object or CNID_INVALID (ie 0) on error
677 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
680 cnid_t db_cnid, ad_cnid;
683 /* Force checkout every X items */
684 static int cnidcount = 0;
686 if (cnidcount > 10000) {
688 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
689 dbd_log(LOGSTD, "Error checkpointing!");
694 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
696 if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
697 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
698 if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
700 if (dbd_flags & DBD_FLAGS_CLEANUP)
703 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
707 if (dbd_flags & DBD_FLAGS_FORCE) {
708 ad_cnid = ad_forcegetid(&ad);
709 /* This ensures the changed stamp is written */
710 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
714 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp);
717 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
719 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
721 ad_close(&ad, ADFLAGS_HF);
724 /* Get CNID from database */
726 /* Prepare request data */
727 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
728 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
731 if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
732 rqst.dev = st->st_dev;
733 rqst.ino = st->st_ino;
734 rqst.type = S_ISDIR(st->st_mode)?1:0;
735 rqst.name = (char *)name;
736 rqst.namelen = strlen(name);
738 /* Query the database */
739 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
740 if (dbif_txn_close(dbd, ret) != 0)
742 if (rply.result == CNID_DBD_RES_OK) {
744 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
745 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
746 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
749 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
753 /* Compare results from both CNID searches */
754 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
755 /* Everything is fine */
757 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
758 /* Mismatch, overwrite ad file with value from db */
759 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
760 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
761 dbd_log(LOGSTD, "Updating AppleDouble file for '%s/%s' with CNID: %u from database",
762 cwdbuf, name, ntohl(db_cnid));
763 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
764 if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) {
765 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
766 cwdbuf, name, strerror(errno));
769 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
771 ad_close(&ad, ADFLAGS_HF);
774 } else if (ad_cnid && (db_cnid == 0)) {
775 /* in ad-file but not in db */
776 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
777 /* Ensure the cnid from the ad-file is not already occupied by another file */
778 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
782 ret = dbd_resolve(dbd, &rqst, &rply);
783 if (rply.result == CNID_DBD_RES_OK) {
784 /* Occupied! Choose another, update ad-file */
785 ret = dbd_add(dbd, &rqst, &rply, 1);
786 if (dbif_txn_close(dbd, ret) != 0)
789 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
791 if ((myvolinfo->v_flags & AFPVOL_CACHE)
793 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
794 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
795 cwdbuf, name, ntohl(db_cnid));
796 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
797 if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
798 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
799 cwdbuf, name, strerror(errno));
802 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
804 ad_close(&ad, ADFLAGS_HF);
809 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
810 cwdbuf, name, ntohl(ad_cnid));
812 ret = dbd_rebuild_add(dbd, &rqst, &rply);
813 if (dbif_txn_close(dbd, ret) != 0)
817 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
818 /* No CNID at all, we clearly have to allocate a fresh one... */
819 /* Note: the next test will use this new CNID too! */
820 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
822 ret = dbd_add(dbd, &rqst, &rply, 1);
823 if (dbif_txn_close(dbd, ret) != 0)
826 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
830 if ((ad_cnid == 0) && db_cnid) {
831 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
832 if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
833 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
834 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
835 cwdbuf, name, ntohl(db_cnid));
836 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
837 if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) {
838 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
839 cwdbuf, name, strerror(errno));
842 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
844 ad_close(&ad, ADFLAGS_HF);
854 This is called recursively for all dirs.
855 volroot=1 means we're in the volume root dir, 0 means we aren't.
856 We use this when checking for netatalk private folders like .AppleDB.
857 did is our parents CNID.
859 static int dbd_readdir(int volroot, cnid_t did)
861 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
866 static struct stat st; /* Save some stack space */
868 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
869 if ((addir_ok = check_addir(volroot)) != 0)
870 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
871 /* Fatal on rebuild run, continue if only scanning ! */
874 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
876 if ((read_addir()) != 0)
877 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
878 /* Fatal on rebuild run, continue if only scanning ! */
881 if ((dp = opendir (".")) == NULL) {
882 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
886 while ((ep = readdir (dp))) {
887 /* Check if we got a termination signal */
889 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
891 /* Check if its "." or ".." */
892 if (DIR_DOT_OR_DOTDOT(ep->d_name))
895 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
896 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
898 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
902 /* Check for special folders in volume root e.g. ".zfs" */
904 if ((name = check_special_dirs(ep->d_name)) != NULL) {
905 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
910 /* Skip .AppleDouble dir in this loop */
911 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
914 if ((ret = lstat(ep->d_name, &st)) < 0) {
915 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
916 cwdbuf, ep->d_name, strerror(errno));
920 switch (st.st_mode & S_IFMT) {
925 adflags = ADFLAGS_DIR;
928 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
930 ret = check_symlink(ep->d_name, &adflags);
934 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
938 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
939 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
940 if ((unlink(ep->d_name)) != 0) {
941 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
947 /**************************************************************************
949 **************************************************************************/
950 static unsigned long long statcount = 0;
957 if ((statcount % 10000) == 0) {
958 if (dbd_flags & DBD_FLAGS_STATS)
959 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s",
960 statcount, (unsigned long long)(time(NULL) - t));
963 /**************************************************************************
965 **************************************************************************/
968 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
969 /* If its a file: skipp all other tests now ! */
970 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
974 /* Check for appledouble file, create if missing, but only if we have addir */
977 adfile_ok = check_adfile(ep->d_name, &st);
981 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
983 /* Now add this object to our rebuild dbd */
984 if (cnid && dbd_rebuild) {
985 static uint count = 0;
986 rqst.cnid = rply.cnid;
987 ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
988 if (dbif_txn_close(dbd_rebuild, ret) != 0)
990 if (rply.result != CNID_DBD_RES_OK) {
991 dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
992 cnid, cwdbuf, ep->d_name);
996 if (count == 10000) {
997 if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) {
998 dbd_log(LOGSTD, "Error checkpointing!");
1006 /* Check EA files */
1007 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD)
1008 check_eafiles(ep->d_name);
1010 /**************************************************************************
1012 **************************************************************************/
1013 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
1014 strcat(cwdbuf, "/");
1015 strcat(cwdbuf, ep->d_name);
1016 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
1017 if (-1 == (cwd = open(".", O_RDONLY))) {
1018 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
1021 if (0 != chdir(ep->d_name)) {
1022 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
1027 ret = dbd_readdir(0, cnid);
1031 *(strrchr(cwdbuf, '/')) = 0;
1038 Use results of previous checks
1045 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
1047 /* Dont scanvol on no-appledouble vols */
1048 if (vi->v_flags & AFPVOL_NOADOUBLE) {
1049 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1053 /* Make this stuff accessible from all funcs easily */
1057 /* Init a fake struct vol with just enough so we can call ea_open and friends */
1058 volume.v_adouble = AD_VERSION2;
1059 volume.v_vfs_ea = myvolinfo->v_vfs_ea;
1060 initvol_vfs(&volume);
1062 /* Run with umask 0 */
1065 /* Remove trailing slash from volume, chdir to vol */
1066 if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/')
1067 myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0;
1068 strcpy(cwdbuf, myvolinfo->v_path);
1069 chdir(myvolinfo->v_path);
1071 /* Start recursion */
1072 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
1079 Remove all CNIDs from dbd that are not in dbd_rebuild
1081 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1083 int ret = 0, deleted = 0;
1084 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1085 struct cnid_dbd_rqst rqst;
1086 struct cnid_dbd_rply rply;
1088 /* jump over rootinfo key */
1089 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1091 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1094 /* Get first id from dbd_rebuild */
1095 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1098 /* Start main loop through dbd: get CNID from dbd */
1099 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1100 /* Check if we got a termination signal */
1102 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1104 if (deleted > 1000) {
1106 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1107 dbd_log(LOGSTD, "Error checkpointing!");
1112 /* This should be the normal case: CNID is in both dbs */
1113 if (dbd_cnid == rebuild_cnid) {
1114 /* Get next CNID from rebuild db */
1115 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1118 } else if (ret == 0) {
1119 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1120 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1121 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1122 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1123 rqst.cnid = htonl(dbd_cnid);
1124 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1125 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1126 (void)dbif_txn_abort(dbd);
1130 if (dbif_txn_close(dbd, ret) != 0)
1134 /* Check if we got a termination signal */
1136 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1140 /* Normal case (ret=1): continue while loop */
1144 if (dbd_cnid < rebuild_cnid) {
1145 /* CNID is orphaned -> delete */
1146 dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid);
1147 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1148 rqst.cnid = htonl(dbd_cnid);
1149 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) {
1150 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid);
1151 (void)dbif_txn_abort(dbd);
1154 if (dbif_txn_close(dbd, ret) != 0)
1161 if (dbd_cnid > rebuild_cnid) {
1162 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1163 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1164 (void)dbif_txn_close(dbd, 2);
1165 (void)dbif_txn_close(dbd_rebuild, 2);
1166 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1167 dbif_dump(dbd_rebuild, 0);
1168 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1171 } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */
1174 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1175 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1179 static const char *get_tmpdb_path(void)
1181 pid_t pid = getpid();
1182 static char path[MAXPATHLEN];
1183 snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid);
1184 if (mkdir(path, 0755) != 0)
1190 Main func called from cmd_dbd.c
1192 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags)
1195 struct db_param db_param = { 0 };
1196 const char *tmpdb_path = NULL;
1198 /* Set cachesize for in-memory rebuild db */
1199 db_param.cachesize = 64 * 1024; /* 64 MB */
1200 db_param.maxlocks = DEFAULT_MAXLOCKS;
1201 db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS;
1202 db_param.logfile_autoremove = 1;
1204 /* Make it accessible for all funcs */
1207 /* We only support unicode volumes ! */
1208 if ( vi->v_volcharset != CH_UTF8) {
1209 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8);
1213 /* Get volume stamp */
1214 dbd_getstamp(dbd, &rqst, &rply);
1215 if (rply.result != CNID_DBD_RES_OK) {
1219 memcpy(stamp, rply.name, CNID_DEV_LEN);
1221 /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */
1222 if (! nocniddb && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) {
1223 tmpdb_path = get_tmpdb_path();
1224 if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) {
1229 if (dbif_env_open(dbd_rebuild,
1231 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) {
1232 dbd_log(LOGSTD, "error opening tmp database!");
1236 if (0 != (dbif_open(dbd_rebuild, NULL, 0))) {
1241 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) {
1247 if (setjmp(jmp) != 0) {
1248 ret = 0; /* Got signal, jump from dbd_readdir */
1253 if ( (scanvol(vi, flags)) != 0) {
1260 if (dbif_txn_close(dbd, ret == 0 ? 1 : 0) != 0)
1263 if (dbif_txn_close(dbd_rebuild, ret == 0 ? 1 : 0) != 0)
1265 if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE))
1266 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1267 other clients in between our pass 1 and 2 */
1268 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1272 dbd_log(LOGDEBUG, "Closing tmp db");
1273 dbif_close(dbd_rebuild);
1276 char cmd[8 + MAXPATHLEN];
1277 snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path);
1278 dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path);
1280 snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path);