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)
47 /* These must be accessible for cmd_dbd_* funcs */
48 struct volinfo *volinfo;
49 char cwdbuf[MAXPATHLEN+1];
51 /* Some static vars */
53 static DBD *dbd_rebuild;
54 static dbd_flags_t dbd_flags;
55 static char stamp[CNID_DEV_LEN];
56 static char *netatalk_dirs[] = {
61 static char *special_dirs[] = {
65 static struct cnid_dbd_rqst rqst;
66 static struct cnid_dbd_rply rply;
68 static struct vol volume; /* fake it for ea_open */
69 static char pname[MAXPATHLEN] = "../";
72 Taken form afpd/desktop.c
74 static char *utompath(char *upath)
76 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
78 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
86 outlen = strlen(upath);
88 if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
89 flags |= CONV_TOUPPER;
90 else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
91 flags |= CONV_TOLOWER;
93 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
94 flags |= CONV__EILSEQ;
97 /* convert charsets */
98 if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
100 volinfo->v_maccharset,
101 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
102 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
103 volinfo->v_volcodepage, volinfo->v_maccodepage, u);
111 Taken form afpd/desktop.c
113 static char *mtoupath(char *mpath)
115 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
124 if ( *mpath == '\0' ) {
128 /* set conversion flags */
129 if (!(volinfo->v_flags & AFPVOL_NOHEX))
130 flags |= CONV_ESCAPEHEX;
131 if (!(volinfo->v_flags & AFPVOL_USEDOTS))
132 flags |= CONV_ESCAPEDOTS;
134 if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
135 flags |= CONV_TOUPPER;
136 else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
137 flags |= CONV_TOLOWER;
139 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
140 flags |= CONV__EILSEQ;
149 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
150 volinfo->v_volcharset,
151 volinfo->v_maccharset,
152 m, inplen, u, outlen, &flags)) ) {
153 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
154 volinfo->v_volcodepage, mpath);
163 Check if "name" is pointing to
164 a) an object inside the current volume (return 0)
165 b) an object outside the current volume (return 1)
166 Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags
167 Return -1 on any serious error.
169 static int check_symlink(const char *name, int *adflags)
173 char pathbuf[MAXPATHLEN + 1];
177 if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) {
178 dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s",
179 cwdbuf, name, strerror(errno));
184 if ((stat(pathbuf, &st)) != 0) {
185 dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno));
189 if ((cwd = open(".", O_RDONLY)) < 0) {
190 dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno));
194 if (S_ISDIR(st.st_mode)) {
195 *adflags |= ADFLAGS_DIR;
197 /* get basename from path */
198 if ((sep = strrchr(pathbuf, '/')) == NULL)
199 /* just a file at the same level */
201 sep = 0; /* pathbuf now contains the directory*/
204 /* fchdir() to pathbuf so we can easily get its path with getcwd() */
205 if ((chdir(pathbuf)) != 0) {
206 dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno));
210 if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) {
211 dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno));
212 if ((fchdir(cwd)) != 0)
214 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
218 if ((fchdir(cwd)) != 0)
220 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
223 We now have the symlink target dir as absoulte path in pathbuf
224 and can compare it with the currents volume path
227 while (volinfo->v_path[i]) {
228 if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
229 dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
235 dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name);
241 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
242 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
244 static int check_name_encoding(char *uname)
248 roundtripped = mtoupath(utompath(uname));
250 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
254 if ( STRCMP(uname, !=, roundtripped)) {
255 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
263 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
264 Returns pointer to name or NULL.
266 static const char *check_netatalk_dirs(const char *name)
270 for (c=0; netatalk_dirs[c]; c++) {
271 if ((strcmp(name, netatalk_dirs[c])) == 0)
272 return netatalk_dirs[c];
278 Check for special names
279 Returns pointer to name or NULL.
281 static const char *check_special_dirs(const char *name)
285 for (c=0; special_dirs[c]; c++) {
286 if ((strcmp(name, special_dirs[c])) == 0)
287 return special_dirs[c];
293 Check for .AppleDouble file, create if missing
295 static int check_adfile(const char *fname, const struct stat *st)
301 if (dbd_flags & DBD_FLAGS_CLEANUP)
304 if (S_ISREG(st->st_mode))
307 adflags = ADFLAGS_DIR;
309 adname = volinfo->ad_path(fname, adflags);
311 if ((ret = access( adname, F_OK)) != 0) {
312 if (errno != ENOENT) {
313 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
314 cwdbuf, adname, strerror(errno));
317 /* Missing. Log and create it */
318 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
320 if (dbd_flags & DBD_FLAGS_SCAN)
321 /* Scan only requested, dont change anything */
325 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
327 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
328 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
329 cwdbuf, adname, strerror(errno));
334 /* Set name in ad-file */
335 ad_setname(&ad, utompath((char *)fname));
337 ad_close_metadata(&ad);
339 chown(adname, st->st_uid, st->st_gid);
340 /* FIXME: should we inherit mode too here ? */
342 chmod(adname, st->st_mode);
345 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
346 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
347 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
350 ad_close_metadata(&ad);
356 Remove all files with file::EA* from adouble dir
358 static void remove_eafiles(const char *name, struct ea *ea)
362 char eaname[MAXPATHLEN];
364 strlcpy(eaname, name, sizeof(eaname));
365 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
366 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
370 if ((chdir(ADv2_DIRNAME)) != 0) {
371 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
372 cwdbuf, ADv2_DIRNAME, strerror(errno));
376 if ((dp = opendir(".")) == NULL) {
377 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
378 cwdbuf, ADv2_DIRNAME, strerror(errno));
382 while ((ep = readdir(dp))) {
383 if (strstr(ep->d_name, eaname) != NULL) {
384 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
385 cwdbuf, ADv2_DIRNAME, ep->d_name);
386 if ((unlink(ep->d_name)) != 0) {
387 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
388 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
399 Check Extended Attributes files
401 static int check_eafiles(const char *fname)
403 unsigned int count = 0;
409 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
412 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
413 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
414 remove_eafiles(fname, &ea);
419 while (count < ea.ea_count) {
420 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
423 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
425 if (lstat(eaname, &st) != 0) {
427 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
429 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
431 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
432 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
434 if ((unlink(eaname)) != 0)
435 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
436 cwdbuf, eaname, strerror(errno));
440 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
441 free((*ea.ea_entries)[count].ea_name);
442 (*ea.ea_entries)[count].ea_name = NULL;
453 Check for .AppleDouble folder and .Parent, create if missing
455 static int check_addir(int volroot)
457 int addir_ok, adpar_ok;
462 if (dbd_flags & DBD_FLAGS_CLEANUP)
465 /* Check for ad-dir */
466 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
467 if (errno != ENOENT) {
468 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
471 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
474 /* Check for ".Parent" */
475 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
476 if (errno != ENOENT) {
477 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
478 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
481 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
484 /* Is one missing ? */
485 if ((addir_ok != 0) || (adpar_ok != 0)) {
486 /* Yes, but are we only scanning ? */
487 if (dbd_flags & DBD_FLAGS_SCAN) {
488 /* Yes: missing .Parent is not a problem, but missing ad-dir
489 causes later checking of ad-files to fail. So we have to return appropiately */
492 else /* (adpar_ok != 0) */
496 /* Create ad dir and set name */
497 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
499 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
500 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
504 /* Get basename of cwd from cwdbuf */
505 mname = utompath(strrchr(cwdbuf, '/') + 1);
507 /* Update name in ad file */
508 ad_setname(&ad, mname);
510 ad_close_metadata(&ad);
512 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
513 if ((lstat(".", &st)) != 0) {
514 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
517 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
518 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
525 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
527 0 = name is not an EA file
528 1 = name is an EA file and no problem was found
529 -1 = name is an EA file and data fork is gone
531 static int check_eafile_in_adouble(const char *name)
534 char *namep, *namedup = NULL;
536 /* Check if this is an AFPVOL_EA_AD vol */
537 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
538 /* Does the filename contain "::EA" ? */
539 namedup = strdup(name);
540 if ((namep = strstr(namedup, "::EA")) == NULL) {
544 /* File contains "::EA" so it's an EA file. Check for data file */
546 /* Get string before "::EA" from EA filename */
548 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
550 if ((access( pname, F_OK)) == 0) {
555 if (errno != ENOENT) {
556 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
557 cwdbuf, name, strerror(errno));
561 /* Orphaned EA file*/
562 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
563 cwdbuf, ADv2_DIRNAME, name);
565 if (dbd_flags & DBD_FLAGS_SCAN)
566 /* Scan only requested, dont change anything */
569 if ((unlink(name)) != 0) {
570 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
571 cwdbuf, ADv2_DIRNAME, name);
575 } /* if AFPVOL_EA_AD */
585 Check files and dirs inside .AppleDouble folder:
586 - remove orphaned files
589 static int read_addir(void)
595 if ((chdir(ADv2_DIRNAME)) != 0) {
596 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
597 cwdbuf, ADv2_DIRNAME, strerror(errno));
601 if ((dp = opendir(".")) == NULL) {
602 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
603 cwdbuf, ADv2_DIRNAME, strerror(errno));
607 while ((ep = readdir(dp))) {
608 /* Check if its "." or ".." */
609 if (DIR_DOT_OR_DOTDOT(ep->d_name))
612 if (STRCMP(ep->d_name, ==, ".Parent"))
615 if ((lstat(ep->d_name, &st)) < 0) {
616 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
617 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
622 if (S_ISDIR(st.st_mode)) {
623 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
624 ep->d_name, cwdbuf, ADv2_DIRNAME);
628 /* Check if for orphaned and corrupt Extended Attributes file */
629 if (check_eafile_in_adouble(ep->d_name) != 0)
632 /* Check for data file */
633 strcpy(pname + 3, ep->d_name);
634 if ((access( pname, F_OK)) != 0) {
635 if (errno != ENOENT) {
636 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
637 cwdbuf, pname, strerror(errno));
640 /* Orphaned ad-file*/
641 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
642 cwdbuf, ADv2_DIRNAME, ep->d_name);
644 if (dbd_flags & DBD_FLAGS_SCAN)
645 /* Scan only requested, dont change anything */
648 if ((unlink(ep->d_name)) != 0) {
649 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
650 cwdbuf, ADv2_DIRNAME, ep->d_name);
656 if ((chdir("..")) != 0) {
657 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
658 cwdbuf, strerror(errno));
659 /* This really is EOT! */
660 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
669 Check CNID for a file/dir, both from db and from ad-file.
670 For detailed specs see intro.
672 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
675 cnid_t db_cnid, ad_cnid;
678 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
680 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
681 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
682 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
684 if (dbd_flags & DBD_FLAGS_CLEANUP)
687 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
691 if (dbd_flags & DBD_FLAGS_FORCE) {
692 ad_cnid = ad_forcegetid(&ad);
693 /* This ensures the changed stamp is written */
694 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
698 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
701 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
703 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
705 ad_close_metadata(&ad);
708 /* Get CNID from database */
710 /* Prepare request data */
711 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
712 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
715 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
716 rqst.dev = st->st_dev;
717 rqst.ino = st->st_ino;
718 rqst.type = S_ISDIR(st->st_mode)?1:0;
719 rqst.name = (char *)name;
720 rqst.namelen = strlen(name);
722 /* Query the database */
723 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
724 dbif_txn_close(dbd, ret);
725 if (rply.result == CNID_DBD_RES_OK) {
727 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
728 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
729 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
732 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
736 /* Compare results from both CNID searches */
737 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
738 /* Everything is fine */
740 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
741 /* Mismatch ? Delete both from db and re-add data from file */
742 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
743 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
745 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
746 dbif_txn_close(dbd, ret);
749 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
750 dbif_txn_close(dbd, ret);
752 ret = dbd_rebuild_add(dbd, &rqst, &rply);
753 dbif_txn_close(dbd, ret);
756 } else if (ad_cnid && (db_cnid == 0)) {
757 /* in ad-file but not in db */
758 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
759 /* Ensure the cnid from the ad-file is not already occupied by another file */
760 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
764 ret = dbd_resolve(dbd, &rqst, &rply);
765 if (ret == CNID_DBD_RES_OK) {
766 /* Occupied! Choose another, update ad-file */
767 ret = dbd_add(dbd, &rqst, &rply, 1);
768 dbif_txn_close(dbd, ret);
770 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
772 if ((volinfo->v_flags & AFPVOL_CACHE)
774 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
775 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
776 cwdbuf, name, ntohl(db_cnid));
777 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
778 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
779 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
780 cwdbuf, name, strerror(errno));
783 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
785 ad_close_metadata(&ad);
790 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
791 cwdbuf, name, ntohl(ad_cnid));
793 ret = dbd_rebuild_add(dbd, &rqst, &rply);
794 dbif_txn_close(dbd, ret);
797 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
798 /* No CNID at all, we clearly have to allocate a fresh one... */
799 /* Note: the next test will use this new CNID too! */
800 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
802 ret = dbd_add(dbd, &rqst, &rply, 1);
803 dbif_txn_close(dbd, ret);
805 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
809 if ((ad_cnid == 0) && db_cnid) {
810 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
811 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
812 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
813 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
814 cwdbuf, name, ntohl(db_cnid));
815 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
816 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
817 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
818 cwdbuf, name, strerror(errno));
821 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
823 ad_close_metadata(&ad);
833 This is called recursively for all dirs.
834 volroot=1 means we're in the volume root dir, 0 means we aren't.
835 We use this when checking for netatalk private folders like .AppleDB.
836 did is our parents CNID.
838 static int dbd_readdir(int volroot, cnid_t did)
840 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
845 static struct stat st; /* Save some stack space */
847 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
848 if ((addir_ok = check_addir(volroot)) != 0)
849 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
850 /* Fatal on rebuild run, continue if only scanning ! */
853 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
855 if ((read_addir()) != 0)
856 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
857 /* Fatal on rebuild run, continue if only scanning ! */
860 if ((dp = opendir (".")) == NULL) {
861 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
865 while ((ep = readdir (dp))) {
866 /* Check if we got a termination signal */
868 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
870 /* Check if its "." or ".." */
871 if (DIR_DOT_OR_DOTDOT(ep->d_name))
874 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
875 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
877 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
881 /* Check for special folders in volume root e.g. ".zfs" */
883 if ((name = check_special_dirs(ep->d_name)) != NULL) {
884 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name);
889 /* Skip .AppleDouble dir in this loop */
890 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
893 if ((ret = lstat(ep->d_name, &st)) < 0) {
894 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s",
895 cwdbuf, ep->d_name, strerror(errno));
899 switch (st.st_mode & S_IFMT) {
904 adflags = ADFLAGS_DIR;
907 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
909 ret = check_symlink(ep->d_name, &adflags);
913 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
917 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
918 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
919 if ((unlink(ep->d_name)) != 0) {
920 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
926 /**************************************************************************
928 **************************************************************************/
931 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
932 /* If its a file: skipp all other tests now ! */
933 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
937 /* Check for appledouble file, create if missing, but only if we have addir */
940 adfile_ok = check_adfile(ep->d_name, &st);
944 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
946 /* Now add this object to our rebuild dbd */
948 rqst.cnid = rply.cnid;
949 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
950 if (rply.result != CNID_DBD_RES_OK) {
951 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
952 cnid, cwdbuf, ep->d_name);
953 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
959 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
960 check_eafiles(ep->d_name);
962 /**************************************************************************
964 **************************************************************************/
965 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
967 strcat(cwdbuf, ep->d_name);
968 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
969 if (-1 == (cwd = open(".", O_RDONLY))) {
970 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
973 if (0 != chdir(ep->d_name)) {
974 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
979 ret = dbd_readdir(0, cnid);
983 *(strrchr(cwdbuf, '/')) = 0;
990 Use results of previous checks
997 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
999 /* Dont scanvol on no-appledouble vols */
1000 if (vi->v_flags & AFPVOL_NOADOUBLE) {
1001 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
1005 /* Make this stuff accessible from all funcs easily */
1009 /* Init a fake struct vol with just enough so we can call ea_open and friends */
1010 volume.v_adouble = AD_VERSION2;
1011 volume.v_vfs_ea = volinfo->v_vfs_ea;
1012 initvol_vfs(&volume);
1014 /* Run with umask 0 */
1017 /* Remove trailing slash from volume, chdir to vol */
1018 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
1019 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
1020 strcpy(cwdbuf, volinfo->v_path);
1021 chdir(volinfo->v_path);
1023 /* Start recursion */
1024 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
1031 Remove all CNIDs from dbd that are not in dbd_rebuild
1033 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1035 int ret, deleted = 0;
1036 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1037 struct cnid_dbd_rqst rqst;
1038 struct cnid_dbd_rply rply;
1040 /* jump over rootinfo key */
1041 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1043 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1046 /* Get first id from dbd_rebuild */
1047 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1050 /* Start main loop through dbd: get CNID from dbd */
1051 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1052 /* Check if we got a termination signal */
1054 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1058 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1059 dbd_log(LOGSTD, "Error checkpointing!");
1064 /* This should be the normal case: CNID is in both dbs */
1065 if (dbd_cnid == rebuild_cnid) {
1066 /* Get next CNID from rebuild db */
1067 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1070 } else if (ret == 0) {
1071 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1072 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1073 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1074 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1075 rqst.cnid = htonl(dbd_cnid);
1076 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1077 dbif_txn_close(dbd, ret);
1080 /* Check if we got a termination signal */
1082 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
1086 /* Normal case (ret=1): continue while loop */
1090 if (dbd_cnid < rebuild_cnid) {
1091 /* CNID is orphaned -> delete */
1092 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1093 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1094 rqst.cnid = htonl(dbd_cnid);
1095 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1096 dbif_txn_close(dbd, ret);
1102 if (dbd_cnid > rebuild_cnid) {
1103 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1104 dbif_dump(dbd_rebuild, 0);
1105 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1106 dbif_txn_close(dbd, ret);
1107 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1108 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1114 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1115 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1120 Main func called from cmd_dbd.c
1122 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1125 struct db_param db_param = { 0 };
1127 /* Set cachesize for in-memory rebuild db */
1128 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1130 /* Make it accessible for all funcs */
1133 /* We only support unicode volumes ! */
1134 if ( volinfo->v_volcharset != CH_UTF8) {
1135 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1140 /* Get volume stamp */
1141 dbd_getstamp(dbd, &rqst, &rply);
1142 if (rply.result != CNID_DBD_RES_OK)
1144 memcpy(stamp, rply.name, CNID_DEV_LEN);
1146 /* open/create rebuild dbd, copy rootinfo key */
1147 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1149 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1151 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1155 if (setjmp(jmp) != 0)
1156 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1159 if ( (scanvol(volinfo, flags)) != 0)
1163 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1164 other clients in between our pass 1 and 2 */
1165 if (flags & DBD_FLAGS_EXCL)
1166 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1171 dbif_close(dbd_rebuild);