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>
42 /* Some defines to ease code parsing */
43 #define ADDIR_OK (addir_ok == 0)
44 #define ADFILE_OK (adfile_ok == 0)
46 /* These must be accessible for cmd_dbd_* funcs */
47 struct volinfo *volinfo;
48 char cwdbuf[MAXPATHLEN+1];
50 /* Some static vars */
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 struct cnid_dbd_rqst rqst;
61 static struct cnid_dbd_rply rply;
63 static struct vol volume; /* fake it for ea_open */
64 static char pname[MAXPATHLEN] = "../";
67 Taken form afpd/desktop.c
69 static char *utompath(char *upath)
71 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
73 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
81 outlen = strlen(upath);
83 if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
84 flags |= CONV_TOUPPER;
85 else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
86 flags |= CONV_TOLOWER;
88 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
89 flags |= CONV__EILSEQ;
92 /* convert charsets */
93 if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
95 volinfo->v_maccharset,
96 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
97 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
98 volinfo->v_volcodepage, volinfo->v_maccodepage, u);
106 Taken form afpd/desktop.c
108 static char *mtoupath(char *mpath)
110 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
119 if ( *mpath == '\0' ) {
123 /* set conversion flags */
124 if (!(volinfo->v_flags & AFPVOL_NOHEX))
125 flags |= CONV_ESCAPEHEX;
126 if (!(volinfo->v_flags & AFPVOL_USEDOTS))
127 flags |= CONV_ESCAPEDOTS;
129 if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
130 flags |= CONV_TOUPPER;
131 else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
132 flags |= CONV_TOLOWER;
134 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
135 flags |= CONV__EILSEQ;
144 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
145 volinfo->v_volcharset,
146 volinfo->v_maccharset,
147 m, inplen, u, outlen, &flags)) ) {
148 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
149 volinfo->v_volcodepage, mpath);
158 Check if "name" is pointing to
159 a) an object inside the current volume (return 0)
160 b) an object outside the current volume (return 1)
161 Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags
162 Return -1 on any serious error.
164 static int check_symlink(const char *name, int *adflags)
168 char pathbuf[MAXPATHLEN + 1];
172 if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) {
173 dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s",
174 cwdbuf, name, strerror(errno));
179 if ((stat(pathbuf, &st)) != 0) {
180 dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno));
184 if ((cwd = open(".", O_RDONLY)) < 0) {
185 dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno));
189 if (S_ISDIR(st.st_mode)) {
190 *adflags |= ADFLAGS_DIR;
192 /* get basename from path */
193 if ((sep = strrchr(pathbuf, '/')) == NULL)
194 /* just a file at the same level */
196 sep = 0; /* pathbuf now contains the directory*/
199 /* fchdir() to pathbuf so we can easily get its path with getcwd() */
200 if ((chdir(pathbuf)) != 0) {
201 dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno));
205 if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) {
206 dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno));
207 if ((fchdir(cwd)) != 0)
209 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
213 if ((fchdir(cwd)) != 0)
215 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
218 We now have the symlink target dir as absoulte path in pathbuf
219 and can compare it with the currents volume path
222 while (volinfo->v_path[i]) {
223 if ((pathbuf[i] == 0) || (volinfo->v_path[i] != pathbuf[i])) {
224 dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name);
230 dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name);
236 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
237 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
239 static int check_name_encoding(char *uname)
243 roundtripped = mtoupath(utompath(uname));
245 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname);
249 if ( STRCMP(uname, !=, roundtripped)) {
250 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname);
258 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
259 Returns pointer to name or NULL.
261 static const char *check_netatalk_dirs(const char *name)
265 for (c=0; netatalk_dirs[c]; c++) {
266 if ((strcmp(name, netatalk_dirs[c])) == 0)
267 return netatalk_dirs[c];
273 Check for .AppleDouble file, create if missing
275 static int check_adfile(const char *fname, const struct stat *st)
281 if (dbd_flags & DBD_FLAGS_CLEANUP)
284 if (S_ISREG(st->st_mode))
287 adflags = ADFLAGS_DIR;
289 adname = volinfo->ad_path(fname, adflags);
291 if ((ret = access( adname, F_OK)) != 0) {
292 if (errno != ENOENT) {
293 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
294 cwdbuf, adname, strerror(errno));
297 /* Missing. Log and create it */
298 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname);
300 if (dbd_flags & DBD_FLAGS_SCAN)
301 /* Scan only requested, dont change anything */
305 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
307 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) {
308 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
309 cwdbuf, adname, strerror(errno));
314 /* Set name in ad-file */
315 ad_setname(&ad, utompath((char *)fname));
317 ad_close_metadata(&ad);
319 chown(adname, st->st_uid, st->st_gid);
320 /* FIXME: should we inherit mode too here ? */
322 chmod(adname, st->st_mode);
325 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
326 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) {
327 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname);
330 ad_close_metadata(&ad);
336 Remove all files with file::EA* from adouble dir
338 static void remove_eafiles(const char *name, struct ea *ea)
342 char eaname[MAXPATHLEN];
344 strlcpy(eaname, name, sizeof(eaname));
345 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) {
346 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name);
350 if ((chdir(ADv2_DIRNAME)) != 0) {
351 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
352 cwdbuf, ADv2_DIRNAME, strerror(errno));
356 if ((dp = opendir(".")) == NULL) {
357 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
358 cwdbuf, ADv2_DIRNAME, strerror(errno));
362 while ((ep = readdir(dp))) {
363 if (strstr(ep->d_name, eaname) != NULL) {
364 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'",
365 cwdbuf, ADv2_DIRNAME, ep->d_name);
366 if ((unlink(ep->d_name)) != 0) {
367 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s",
368 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
379 Check Extended Attributes files
381 static int check_eafiles(const char *fname)
383 unsigned int count = 0;
389 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) {
392 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname);
393 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
394 remove_eafiles(fname, &ea);
399 while (count < ea.ea_count) {
400 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name);
403 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0);
405 if (lstat(eaname, &st) != 0) {
407 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname);
409 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname);
411 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) {
412 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname);
414 if ((unlink(eaname)) != 0)
415 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s",
416 cwdbuf, eaname, strerror(errno));
420 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/
421 free((*ea.ea_entries)[count].ea_name);
422 (*ea.ea_entries)[count].ea_name = NULL;
433 Check for .AppleDouble folder and .Parent, create if missing
435 static int check_addir(int volroot)
437 int addir_ok, adpar_ok;
442 if (dbd_flags & DBD_FLAGS_CLEANUP)
445 /* Check for ad-dir */
446 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) {
447 if (errno != ENOENT) {
448 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
451 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf);
454 /* Check for ".Parent" */
455 if ( (adpar_ok = access(volinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) {
456 if (errno != ENOENT) {
457 dbd_log(LOGSTD, "Access error on '%s/%s': %s",
458 cwdbuf, volinfo->ad_path(".", ADFLAGS_DIR), strerror(errno));
461 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf);
464 /* Is one missing ? */
465 if ((addir_ok != 0) || (adpar_ok != 0)) {
466 /* Yes, but are we only scanning ? */
467 if (dbd_flags & DBD_FLAGS_SCAN) {
468 /* Yes: missing .Parent is not a problem, but missing ad-dir
469 causes later checking of ad-files to fail. So we have to return appropiately */
472 else /* (adpar_ok != 0) */
476 /* Create ad dir and set name */
477 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
479 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) {
480 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
484 /* Get basename of cwd from cwdbuf */
485 mname = utompath(strrchr(cwdbuf, '/') + 1);
487 /* Update name in ad file */
488 ad_setname(&ad, mname);
490 ad_close_metadata(&ad);
492 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
493 if ((lstat(".", &st)) != 0) {
494 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
497 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
498 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
505 Check if file cotains "::EA" and if it does check if its correspondig data fork exists.
507 0 = name is not an EA file
508 1 = name is an EA file and no problem was found
509 -1 = name is an EA file and data fork is gone
511 static int check_eafile_in_adouble(const char *name)
514 char *namep, *namedup = NULL;
516 /* Check if this is an AFPVOL_EA_AD vol */
517 if (volinfo->v_vfs_ea == AFPVOL_EA_AD) {
518 /* Does the filename contain "::EA" ? */
519 namedup = strdup(name);
520 if ((namep = strstr(namedup, "::EA")) == NULL) {
524 /* File contains "::EA" so it's an EA file. Check for data file */
526 /* Get string before "::EA" from EA filename */
528 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */
530 if ((access( pname, F_OK)) == 0) {
535 if (errno != ENOENT) {
536 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
537 cwdbuf, name, strerror(errno));
541 /* Orphaned EA file*/
542 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'",
543 cwdbuf, ADv2_DIRNAME, name);
545 if (dbd_flags & DBD_FLAGS_SCAN)
546 /* Scan only requested, dont change anything */
549 if ((unlink(name)) != 0) {
550 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'",
551 cwdbuf, ADv2_DIRNAME, name);
555 } /* if AFPVOL_EA_AD */
565 Check files and dirs inside .AppleDouble folder:
566 - remove orphaned files
569 static int read_addir(void)
575 if ((chdir(ADv2_DIRNAME)) != 0) {
576 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
577 cwdbuf, ADv2_DIRNAME, strerror(errno));
581 if ((dp = opendir(".")) == NULL) {
582 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
583 cwdbuf, ADv2_DIRNAME, strerror(errno));
587 while ((ep = readdir(dp))) {
588 /* Check if its "." or ".." */
589 if (DIR_DOT_OR_DOTDOT(ep->d_name))
592 if (STRCMP(ep->d_name, ==, ".Parent"))
595 if ((lstat(ep->d_name, &st)) < 0) {
596 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s",
597 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
602 if (S_ISDIR(st.st_mode)) {
603 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
604 ep->d_name, cwdbuf, ADv2_DIRNAME);
608 /* Check if for orphaned and corrupt Extended Attributes file */
609 if (check_eafile_in_adouble(ep->d_name) != 0)
612 /* Check for data file */
613 strcpy(pname + 3, ep->d_name);
614 if ((access( pname, F_OK)) != 0) {
615 if (errno != ENOENT) {
616 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
617 cwdbuf, pname, strerror(errno));
620 /* Orphaned ad-file*/
621 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
622 cwdbuf, ADv2_DIRNAME, ep->d_name);
624 if (dbd_flags & DBD_FLAGS_SCAN)
625 /* Scan only requested, dont change anything */
628 if ((unlink(ep->d_name)) != 0) {
629 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
630 cwdbuf, ADv2_DIRNAME, ep->d_name);
636 if ((chdir("..")) != 0) {
637 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
638 cwdbuf, strerror(errno));
639 /* This really is EOT! */
640 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
649 Check CNID for a file/dir, both from db and from ad-file.
650 For detailed specs see intro.
652 static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
655 cnid_t db_cnid, ad_cnid;
658 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
660 if ( (volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
661 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
662 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
664 if (dbd_flags & DBD_FLAGS_CLEANUP)
667 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
671 if (dbd_flags & DBD_FLAGS_FORCE) {
672 ad_cnid = ad_forcegetid(&ad);
673 /* This ensures the changed stamp is written */
674 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
678 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
681 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
683 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
685 ad_close_metadata(&ad);
688 /* Get CNID from database */
690 /* Prepare request data */
691 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
692 memset(&rply, 0, sizeof(struct cnid_dbd_rply));
695 if ( ! (volinfo->v_flags & AFPVOL_NODEV))
696 rqst.dev = st->st_dev;
697 rqst.ino = st->st_ino;
698 rqst.type = S_ISDIR(st->st_mode)?1:0;
699 rqst.name = (char *)name;
700 rqst.namelen = strlen(name);
702 /* Query the database */
703 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
704 dbif_txn_close(dbd, ret);
705 if (rply.result == CNID_DBD_RES_OK) {
707 } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
708 if ( ! (dbd_flags & DBD_FLAGS_FORCE))
709 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
712 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
716 /* Compare results from both CNID searches */
717 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
718 /* Everything is fine */
720 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
721 /* Mismatch ? Delete both from db and re-add data from file */
722 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
723 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
725 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
726 dbif_txn_close(dbd, ret);
729 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
730 dbif_txn_close(dbd, ret);
732 ret = dbd_rebuild_add(dbd, &rqst, &rply);
733 dbif_txn_close(dbd, ret);
736 } else if (ad_cnid && (db_cnid == 0)) {
737 /* in ad-file but not in db */
738 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
739 /* Ensure the cnid from the ad-file is not already occupied by another file */
740 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
744 ret = dbd_resolve(dbd, &rqst, &rply);
745 if (ret == CNID_DBD_RES_OK) {
746 /* Occupied! Choose another, update ad-file */
747 ret = dbd_add(dbd, &rqst, &rply, 1);
748 dbif_txn_close(dbd, ret);
750 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
752 if ((volinfo->v_flags & AFPVOL_CACHE)
754 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
755 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
756 cwdbuf, name, ntohl(db_cnid));
757 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
758 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
759 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
760 cwdbuf, name, strerror(errno));
763 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
765 ad_close_metadata(&ad);
770 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
771 cwdbuf, name, ntohl(ad_cnid));
773 ret = dbd_rebuild_add(dbd, &rqst, &rply);
774 dbif_txn_close(dbd, ret);
777 } else if ((db_cnid == 0) && (ad_cnid == 0)) {
778 /* No CNID at all, we clearly have to allocate a fresh one... */
779 /* Note: the next test will use this new CNID too! */
780 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
782 ret = dbd_add(dbd, &rqst, &rply, 1);
783 dbif_txn_close(dbd, ret);
785 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
789 if ((ad_cnid == 0) && db_cnid) {
790 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
791 if ((volinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
792 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
793 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
794 cwdbuf, name, ntohl(db_cnid));
795 ad_init(&ad, volinfo->v_adouble, volinfo->v_ad_options);
796 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
797 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
798 cwdbuf, name, strerror(errno));
801 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
803 ad_close_metadata(&ad);
813 This is called recursively for all dirs.
814 volroot=1 means we're in the volume root dir, 0 means we aren't.
815 We use this when checking for netatalk private folders like .AppleDB.
816 did is our parents CNID.
818 static int dbd_readdir(int volroot, cnid_t did)
820 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok;
825 static struct stat st; /* Save some stack space */
827 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */
828 if ((addir_ok = check_addir(volroot)) != 0)
829 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
830 /* Fatal on rebuild run, continue if only scanning ! */
833 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */
835 if ((read_addir()) != 0)
836 if ( ! (dbd_flags & DBD_FLAGS_SCAN))
837 /* Fatal on rebuild run, continue if only scanning ! */
840 if ((dp = opendir (".")) == NULL) {
841 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
845 while ((ep = readdir (dp))) {
846 /* Check if we got a termination signal */
848 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
850 /* Check if its "." or ".." */
851 if (DIR_DOT_OR_DOTDOT(ep->d_name))
854 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
855 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
857 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
861 /* Skip .AppleDouble dir in this loop */
862 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
865 if ((ret = lstat(ep->d_name, &st)) < 0) {
866 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
870 switch (st.st_mode & S_IFMT) {
875 adflags = ADFLAGS_DIR;
878 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name);
880 ret = check_symlink(ep->d_name, &adflags);
884 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name);
888 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name);
889 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
890 if ((unlink(ep->d_name)) != 0) {
891 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno));
897 /**************************************************************************
899 **************************************************************************/
902 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
903 /* If its a file: skipp all other tests now ! */
904 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */
908 /* Check for appledouble file, create if missing, but only if we have addir */
911 adfile_ok = check_adfile(ep->d_name, &st);
915 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags);
917 /* Now add this object to our rebuild dbd */
919 rqst.cnid = rply.cnid;
920 dbd_rebuild_add(dbd_rebuild, &rqst, &rply);
921 if (rply.result != CNID_DBD_RES_OK) {
922 dbd_log( LOGDEBUG, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db",
923 cnid, cwdbuf, ep->d_name);
924 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */
930 if (volinfo->v_vfs_ea == AFPVOL_EA_AD)
931 check_eafiles(ep->d_name);
933 /**************************************************************************
935 **************************************************************************/
936 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */
938 strcat(cwdbuf, ep->d_name);
939 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
940 if (-1 == (cwd = open(".", O_RDONLY))) {
941 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
944 if (0 != chdir(ep->d_name)) {
945 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
950 ret = dbd_readdir(0, cnid);
954 *(strrchr(cwdbuf, '/')) = 0;
961 Use results of previous checks
968 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
970 /* Dont scanvol on no-appledouble vols */
971 if (vi->v_flags & AFPVOL_NOADOUBLE) {
972 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
976 /* Make this stuff accessible from all funcs easily */
980 /* Init a fake struct vol with just enough so we can call ea_open and friends */
981 volume.v_adouble = AD_VERSION2;
982 volume.v_vfs_ea = volinfo->v_vfs_ea;
983 initvol_vfs(&volume);
985 /* Run with umask 0 */
988 /* Remove trailing slash from volume, chdir to vol */
989 if (volinfo->v_path[strlen(volinfo->v_path) - 1] == '/')
990 volinfo->v_path[strlen(volinfo->v_path) - 1] = 0;
991 strcpy(cwdbuf, volinfo->v_path);
992 chdir(volinfo->v_path);
994 /* Start recursion */
995 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */
1002 Remove all CNIDs from dbd that are not in dbd_rebuild
1004 static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags)
1006 int ret, deleted = 0;
1007 cnid_t dbd_cnid = 0, rebuild_cnid = 0;
1008 struct cnid_dbd_rqst rqst;
1009 struct cnid_dbd_rply rply;
1011 /* jump over rootinfo key */
1012 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1)
1014 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1)
1017 /* Get first id from dbd_rebuild */
1018 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1)
1021 /* Start main loop through dbd: get CNID from dbd */
1022 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1026 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
1027 dbd_log(LOGSTD, "Error checkpointing!");
1032 /* This should be the normal case: CNID is in both dbs */
1033 if (dbd_cnid == rebuild_cnid) {
1034 /* Get next CNID from rebuild db */
1035 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) {
1038 } else if (ret == 0) {
1039 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */
1040 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) {
1041 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid);
1042 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1043 rqst.cnid = htonl(dbd_cnid);
1044 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1045 dbif_txn_close(dbd, ret);
1051 /* Normal case (ret=1): continue while loop */
1055 if (dbd_cnid < rebuild_cnid) {
1056 /* CNID is orphaned -> delete */
1057 dbd_log(LOGSTD, "Orphaned CNID in database: %u.", dbd_cnid);
1058 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
1059 rqst.cnid = htonl(dbd_cnid);
1060 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
1061 dbif_txn_close(dbd, ret);
1067 if (dbd_cnid > rebuild_cnid) {
1068 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid);
1069 dbif_dump(dbd_rebuild, 0);
1070 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!");
1071 dbif_txn_close(dbd, ret);
1072 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1073 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1079 dbif_idwalk(dbd, NULL, 1); /* Close cursor */
1080 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */
1085 Main func called from cmd_dbd.c
1087 int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *volinfo, dbd_flags_t flags)
1090 struct db_param db_param = { 0 };
1092 /* Set cachesize for in-memory rebuild db */
1093 db_param.cachesize = 128 * 1024 * 1024; /* 128 MB */
1095 /* Make it accessible for all funcs */
1098 /* We only support unicode volumes ! */
1099 if ( volinfo->v_volcharset != CH_UTF8) {
1100 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
1105 /* Get volume stamp */
1106 dbd_getstamp(dbd, &rqst, &rply);
1107 if (rply.result != CNID_DBD_RES_OK)
1109 memcpy(stamp, rply.name, CNID_DEV_LEN);
1111 /* open/create rebuild dbd, copy rootinfo key */
1112 if (NULL == (dbd_rebuild = dbif_init(NULL, NULL)))
1114 if (0 != (dbif_open(dbd_rebuild, &db_param, 0)))
1116 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild)))
1120 if (setjmp(jmp) != 0)
1121 goto exit_cleanup; /* Got signal, jump from dbd_readdir */
1124 if ( (scanvol(volinfo, flags)) != 0)
1128 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from
1129 other clients in between our pass 1 and 2 */
1130 if (flags & DBD_FLAGS_EXCL)
1131 delete_orphaned_cnids(dbd, dbd_rebuild, flags);
1136 dbif_close(dbd_rebuild);