X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fcmd_dbd_scanvol.c;h=ba1cd623e0b54dd778ccf2b5a80cdfc3df0a72d4;hp=3c61f920fa888f0218246b584763ae122d221478;hb=e421da9fdfa2026757b60277a83614fe4a7db2ab;hpb=7da5be70fe636491c5cb45a7d1cd9d2ada48ef1e diff --git a/etc/cnid_dbd/cmd_dbd_scanvol.c b/etc/cnid_dbd/cmd_dbd_scanvol.c index 3c61f920..ba1cd623 100644 --- a/etc/cnid_dbd/cmd_dbd_scanvol.c +++ b/etc/cnid_dbd/cmd_dbd_scanvol.c @@ -28,13 +28,14 @@ #include #include -#include -#include +#include #include #include #include #include #include +#include +#include #include "cmd_dbd.h" #include "dbif.h" @@ -46,10 +47,8 @@ #define ADFILE_OK (adfile_ok == 0) -static struct volinfo *myvolinfo; static char cwdbuf[MAXPATHLEN+1]; -static DBD *dbd; -static DBD *dbd_rebuild; +static struct vol *vol; static dbd_flags_t dbd_flags; static char stamp[CNID_DEV_LEN]; static char *netatalk_dirs[] = { @@ -64,7 +63,6 @@ static char *special_dirs[] = { static struct cnid_dbd_rqst rqst; static struct cnid_dbd_rply rply; static jmp_buf jmp; -static struct vol volume; /* fake it for ea_open */ static char pname[MAXPATHLEN] = "../"; /* @@ -84,180 +82,28 @@ static char *utompath(char *upath) u = upath; outlen = strlen(upath); - if ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER)) + if ((vol->v_casefold & AFPVOL_UTOMUPPER)) flags |= CONV_TOUPPER; - else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER)) + else if ((vol->v_casefold & AFPVOL_UTOMLOWER)) flags |= CONV_TOLOWER; - if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) { + if ((vol->v_flags & AFPVOL_EILSEQ)) { flags |= CONV__EILSEQ; } /* convert charsets */ - if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset, + if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset, CH_UTF8_MAC, - myvolinfo->v_maccharset, + vol->v_maccharset, u, outlen, mpath, MAXPATHLEN, &flags)) ) { dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.", - myvolinfo->v_volcodepage, myvolinfo->v_maccodepage, u); + vol->v_volcodepage, vol->v_maccodepage, u); return NULL; } return(m); } -/* - Taken form afpd/desktop.c -*/ -static char *mtoupath(char *mpath) -{ - static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ - char *m, *u; - size_t inplen; - size_t outlen; - u_int16_t flags = 0; - - if (!mpath) - return NULL; - - if ( *mpath == '\0' ) { - return( "." ); - } - - /* set conversion flags */ - if (!(myvolinfo->v_flags & AFPVOL_NOHEX)) - flags |= CONV_ESCAPEHEX; - if (!(myvolinfo->v_flags & AFPVOL_USEDOTS)) - flags |= CONV_ESCAPEDOTS; - - if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER)) - flags |= CONV_TOUPPER; - else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER)) - flags |= CONV_TOLOWER; - - if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) { - flags |= CONV__EILSEQ; - } - - m = mpath; - u = upath; - - inplen = strlen(m); - outlen = MAXPATHLEN; - - if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC, - myvolinfo->v_volcharset, - myvolinfo->v_maccharset, - m, inplen, u, outlen, &flags)) ) { - dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.", - myvolinfo->v_volcodepage, mpath); - return NULL; - } - - return( upath ); -} - -#if 0 -/* - Check if "name" is pointing to - a) an object inside the current volume (return 0) - b) an object outside the current volume (return 1) - Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags - Return -1 on any serious error. - */ -static int check_symlink(const char *name, int *adflags) -{ - int cwd; - ssize_t len; - char pathbuf[MAXPATHLEN + 1]; - char *sep; - struct stat st; - - if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) { - dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s", - cwdbuf, name, strerror(errno)); - return -1; - } - pathbuf[len] = 0; - - if ((stat(pathbuf, &st)) != 0) { - dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno)); - } - - /* Remember cwd */ - if ((cwd = open(".", O_RDONLY)) < 0) { - dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno)); - return -1; - } - - if (S_ISDIR(st.st_mode)) { - *adflags |= ADFLAGS_DIR; - } else { /* file */ - /* get basename from path */ - if ((sep = strrchr(pathbuf, '/')) == NULL) - /* just a file at the same level */ - return 0; - sep = 0; /* pathbuf now contains the directory*/ - } - - /* fchdir() to pathbuf so we can easily get its path with getcwd() */ - if ((chdir(pathbuf)) != 0) { - dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno)); - return -1; - } - - if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) { - dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno)); - if ((fchdir(cwd)) != 0) - /* We're foobared */ - longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ - return -1; - } - - if ((fchdir(cwd)) != 0) - /* We're foobared */ - longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ - - /* - We now have the symlink target dir as absoulte path in pathbuf - and can compare it with the currents volume path - */ - int i = 0; - while (myvolinfo->v_path[i]) { - if ((pathbuf[i] == 0) || (myvolinfo->v_path[i] != pathbuf[i])) { - dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name); - return 1; - } - i++; - } - - dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name); - return 0; -} -#endif - -/* - Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS. - We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result. -*/ -static int check_name_encoding(char *uname) -{ - char *roundtripped; - - roundtripped = mtoupath(utompath(uname)); - if (!roundtripped) { - dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname); - return -1; - } - - if ( STRCMP(uname, !=, roundtripped)) { - dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname); - return -1; - } - - return 0; -} - /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" Returns pointer to name or NULL. @@ -288,23 +134,56 @@ static const char *check_special_dirs(const char *name) return NULL; } +/* + * We unCAPed a name, update CNID db + */ +static int update_cnid(cnid_t did, const struct stat *sp, const char *oldname, const char *newname) +{ + cnid_t id; + + /* Query the database */ + if ((id = cnid_lookup(vol->v_cdb, sp, did, (char *)oldname, strlen(oldname))) == CNID_INVALID) + /* not in db, no need to update */ + return 0; + + /* Update the database */ + if (cnid_update(vol->v_cdb, id, sp, did, (char *)newname, strlen(newname)) < 0) + return -1; + + return 0; +} + /* Check for .AppleDouble file, create if missing */ -static int check_adfile(const char *fname, const struct stat *st) +static int check_adfile(const char *fname, const struct stat *st, const char **newname) { int ret; int adflags = ADFLAGS_HF; struct adouble ad; const char *adname; - if (dbd_flags & DBD_FLAGS_CLEANUP) - return 0; + *newname = NULL; - if (S_ISREG(st->st_mode)) + if (vol->v_adouble == AD_VERSION_EA) { + if (!(dbd_flags & DBD_FLAGS_V2TOEA)) + return 0; + if (ad_convert(fname, st, vol, newname) != 0) { + switch (errno) { + case ENOENT: + break; + default: + dbd_log(LOGSTD, "Conversion error for \"%s/%s\": %s", cwdbuf, fname, strerror(errno)); + break; + } + } + return 0; + } + + if (S_ISDIR(st->st_mode)) adflags |= ADFLAGS_DIR; - adname = myvolinfo->ad_path(fname, adflags); + adname = vol->ad_path(fname, adflags); if ((ret = access( adname, F_OK)) != 0) { if (errno != ENOENT) { @@ -320,7 +199,7 @@ static int check_adfile(const char *fname, const struct stat *st) return -1; /* Create ad file */ - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); + ad_init(&ad, vol); if ((ret = ad_open(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 0) { dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s", @@ -340,7 +219,7 @@ static int check_adfile(const char *fname, const struct stat *st) chmod(adname, st->st_mode); #endif } else { - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); + ad_init(&ad, vol); if (ad_open(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) { dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname); return -1; @@ -409,7 +288,7 @@ static int check_eafiles(const char *fname) struct stat st; char *eaname; - if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) { + if ((ret = ea_open(vol, fname, EA_RDWR, &ea)) != 0) { if (errno == ENOENT) return 0; dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname); @@ -462,7 +341,7 @@ static int check_addir(int volroot) struct adouble ad; char *mname = NULL; - if (dbd_flags & DBD_FLAGS_CLEANUP) + if (vol->v_adouble == AD_VERSION_EA) return 0; /* Check for ad-dir */ @@ -475,10 +354,10 @@ static int check_addir(int volroot) } /* Check for ".Parent" */ - if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) { + if ( (adpar_ok = access(vol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) { if (errno != ENOENT) { dbd_log(LOGSTD, "Access error on '%s/%s': %s", - cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno)); + cwdbuf, vol->ad_path(".", ADFLAGS_DIR), strerror(errno)); return -1; } dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf); @@ -497,7 +376,7 @@ static int check_addir(int volroot) } /* Create ad dir and set name */ - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); + ad_init(&ad, vol); if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) { dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno)); @@ -518,7 +397,7 @@ static int check_addir(int volroot) return -1; } chown(ADv2_DIRNAME, st.st_uid, st.st_gid); - chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid); + chown(vol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid); } return 0; @@ -537,7 +416,7 @@ static int check_eafile_in_adouble(const char *name) char *namep, *namedup = NULL; /* Check if this is an AFPVOL_EA_AD vol */ - if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) { + if (vol->v_vfs_ea == AFPVOL_EA_AD) { /* Does the filename contain "::EA" ? */ namedup = strdup(name); if ((namep = strstr(namedup, "::EA")) == NULL) { @@ -596,6 +475,9 @@ static int read_addir(void) struct stat st; if ((chdir(ADv2_DIRNAME)) != 0) { + if (vol->v_adouble == AD_VERSION_EA) { + return 0; + } dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s", cwdbuf, ADv2_DIRNAME, strerror(errno)); return -1; @@ -611,6 +493,7 @@ static int read_addir(void) /* Check if its "." or ".." */ if (DIR_DOT_OR_DOTDOT(ep->d_name)) continue; + /* Skip ".Parent" */ if (STRCMP(ep->d_name, ==, ".Parent")) continue; @@ -674,180 +557,73 @@ static int read_addir(void) @return Correct CNID of object or CNID_INVALID (ie 0) on error */ -static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags) +static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok) { - int ret; + int adflags = ADFLAGS_HF; cnid_t db_cnid, ad_cnid; struct adouble ad; - /* Force checkout every X items */ - static int cnidcount = 0; - cnidcount++; - if (cnidcount > 10000) { - cnidcount = 0; - if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) { - dbd_log(LOGSTD, "Error checkpointing!"); - return CNID_INVALID; - } - } + adflags = ADFLAGS_HF | (S_ISDIR(st->st_mode) ? ADFLAGS_DIR : 0); - /* Get CNID from ad-file if volume is using AFPVOL_CACHE */ - ad_cnid = 0; - if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) { - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); + /* Get CNID from ad-file */ + ad_cnid = CNID_INVALID; + if (ADFILE_OK) { + ad_init(&ad, vol); if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) { - if (dbd_flags & DBD_FLAGS_CLEANUP) + if (vol->v_adouble != AD_VERSION_EA) { + dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno)); return CNID_INVALID; - - dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno)); - return CNID_INVALID; - } - - if (dbd_flags & DBD_FLAGS_FORCE) { - ad_cnid = ad_forcegetid(&ad); - /* This ensures the changed stamp is written */ - ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp); - ad_flush(&ad); - } - else + } + dbd_log( LOGDEBUG, "File without meta EA: \"%s/%s\"", cwdbuf, name); + adfile_ok = 1; + } else { ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp); - - if (ad_cnid == 0) - dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name); - else - dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid)); - - ad_close(&ad, ADFLAGS_HF); + if (ad_cnid == CNID_INVALID) + dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name); + else + dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid)); + ad_close(&ad, ADFLAGS_HF); + } } /* Get CNID from database */ - - /* Prepare request data */ - memset(&rqst, 0, sizeof(struct cnid_dbd_rqst)); - memset(&rply, 0, sizeof(struct cnid_dbd_rply)); - rqst.did = did; - rqst.cnid = ad_cnid; - if ( ! (myvolinfo->v_flags & AFPVOL_NODEV)) - rqst.dev = st->st_dev; - rqst.ino = st->st_ino; - rqst.type = S_ISDIR(st->st_mode)?1:0; - rqst.name = (char *)name; - rqst.namelen = strlen(name); - - /* Query the database */ - ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0); - if (dbif_txn_close(dbd, ret) != 0) + if ((db_cnid = cnid_add(vol->v_cdb, st, did, (char *)name, strlen(name), ad_cnid)) == CNID_INVALID) return CNID_INVALID; - if (rply.result == CNID_DBD_RES_OK) { - db_cnid = rply.cnid; - } else if (rply.result == CNID_DBD_RES_NOTFOUND) { - if ( ! (dbd_flags & DBD_FLAGS_FORCE)) - dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name); - db_cnid = 0; - } else { - dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name); - db_cnid = 0; - } - /* Compare results from both CNID searches */ - if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) { - /* Everything is fine */ - return db_cnid; - } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) { + /* Compare CNID from db and adouble file */ + if (ad_cnid != db_cnid && adfile_ok == 0) { /* Mismatch, overwrite ad file with value from db */ - dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid)); - if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { - dbd_log(LOGSTD, "Updating AppleDouble file for '%s/%s' with CNID: %u from database", - cwdbuf, name, ntohl(db_cnid)); - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); - if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) { - dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", - cwdbuf, name, strerror(errno)); - return CNID_INVALID; - } - ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); - ad_flush(&ad); - ad_close(&ad, ADFLAGS_HF); - } - return db_cnid; - } else if (ad_cnid && (db_cnid == 0)) { - /* in ad-file but not in db */ - if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { - /* Ensure the cnid from the ad-file is not already occupied by another file */ - dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied", - ntohl(ad_cnid)); - - rqst.cnid = ad_cnid; - ret = dbd_resolve(dbd, &rqst, &rply); - if (rply.result == CNID_DBD_RES_OK) { - /* Occupied! Choose another, update ad-file */ - ret = dbd_add(dbd, &rqst, &rply, 1); - if (dbif_txn_close(dbd, ret) != 0) - return CNID_INVALID; - db_cnid = rply.cnid; - dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid)); - - if ((myvolinfo->v_flags & AFPVOL_CACHE) - && ADFILE_OK - && ( ! (dbd_flags & DBD_FLAGS_SCAN))) { - dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", - cwdbuf, name, ntohl(db_cnid)); - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); - if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) { - dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", - cwdbuf, name, strerror(errno)); - return CNID_INVALID; - } - ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); - ad_flush(&ad); - ad_close(&ad, ADFLAGS_HF); - } - return db_cnid; - } - - dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u", - cwdbuf, name, ntohl(ad_cnid)); - rqst.cnid = ad_cnid; - ret = dbd_rebuild_add(dbd, &rqst, &rply); - if (dbif_txn_close(dbd, ret) != 0) - return CNID_INVALID; - } - return ad_cnid; - } else if ((db_cnid == 0) && (ad_cnid == 0)) { - /* No CNID at all, we clearly have to allocate a fresh one... */ - /* Note: the next test will use this new CNID too! */ - if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { - /* add to db */ - ret = dbd_add(dbd, &rqst, &rply, 1); - if (dbif_txn_close(dbd, ret) != 0) - return CNID_INVALID; - db_cnid = rply.cnid; - dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid)); + dbd_log(LOGSTD, "CNID mismatch for '%s/%s', CNID db: %u, ad-file: %u", + cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid)); + ad_init(&ad, vol); + if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) { + dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", + cwdbuf, name, strerror(errno)); + return CNID_INVALID; } + ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); + ad_flush(&ad); + ad_close(&ad, ADFLAGS_HF); } - if ((ad_cnid == 0) && db_cnid) { - /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */ - if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) { - if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { - dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", - cwdbuf, name, ntohl(db_cnid)); - ad_init_old(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); - if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) { - dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", - cwdbuf, name, strerror(errno)); - return CNID_INVALID; - } - ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); - ad_flush(&ad); - ad_close(&ad, ADFLAGS_HF); - } - } - return db_cnid; - } + return db_cnid; +} + +static void check_orphaned(const char *name) +{ + int rc; + struct stat sb; - return CNID_INVALID; + if (strlen(name) < 3) + return; + + rc = lstat(&name[2], &sb); + + if (rc != 0 && errno == ENOENT) { + dbd_log(LOGSTD, "Removing orphaned AppleDouble \"%s/%s\"", cwdbuf, name); + unlink(name); + } } /* @@ -858,7 +634,7 @@ static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfi */ static int dbd_readdir(int volroot, cnid_t did) { - int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok; + int cwd, ret = 0, adfile_ok, addir_ok; cnid_t cnid = 0; const char *name; DIR *dp; @@ -911,6 +687,11 @@ static int dbd_readdir(int volroot, cnid_t did) if (STRCMP(ep->d_name, == , ADv2_DIRNAME)) continue; + if (!vol->vfs->vfs_validupath(vol, ep->d_name)) { + dbd_log(LOGDEBUG, "Ignoring \"%s\"", ep->d_name); + continue; + } + if ((ret = lstat(ep->d_name, &st)) < 0) { dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno)); @@ -919,21 +700,9 @@ static int dbd_readdir(int volroot, cnid_t did) switch (st.st_mode & S_IFMT) { case S_IFREG: - adflags = 0; - break; case S_IFDIR: - adflags = ADFLAGS_DIR; - break; case S_IFLNK: - dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name); -#if 0 - ret = check_symlink(ep->d_name, &adflags); - if (ret == 1) - break; - if (ret == -1) - dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name); -#endif - continue; + break; default: dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name); if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { @@ -964,61 +733,48 @@ static int dbd_readdir(int volroot, cnid_t did) Tests **************************************************************************/ - /* Check encoding */ - if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) { - /* If its a file: skipp all other tests now ! */ - /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */ + /* Check for invalid names and orphaned ._ files */ + if (S_ISREG(st.st_mode) && (strncmp(ep->d_name, "._", strlen("._")) == 0)) + check_orphaned(ep->d_name); + + if (!vol->vfs->vfs_validupath(vol, ep->d_name)) { + dbd_log(LOGSTD, "Ignoring \"%s/%s\"", cwdbuf, ep->d_name); continue; } /* Check for appledouble file, create if missing, but only if we have addir */ + const char *name = NULL; adfile_ok = -1; if (ADDIR_OK) - adfile_ok = check_adfile(ep->d_name, &st); + adfile_ok = check_adfile(ep->d_name, &st, &name); - if ( ! nocniddb) { - /* Check CNIDs */ - cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags); - - /* Now add this object to our rebuild dbd */ - if (cnid && dbd_rebuild) { - static uint count = 0; - rqst.cnid = rply.cnid; - ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply); - if (dbif_txn_close(dbd_rebuild, ret) != 0) - return -1; - if (rply.result != CNID_DBD_RES_OK) { - dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db", - cnid, cwdbuf, ep->d_name); - return -1; - } - count++; - if (count == 10000) { - if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) { - dbd_log(LOGSTD, "Error checkpointing!"); - return -1; - } - count = 0; - } + if (!S_ISLNK(st.st_mode)) { + if (name == NULL) { + name = ep->d_name; + } else { + update_cnid(did, &st, ep->d_name, name); } - } - /* Check EA files */ - if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) - check_eafiles(ep->d_name); + /* Check CNIDs */ + cnid = check_cnid(name, did, &st, adfile_ok); + + /* Check EA files */ + if (vol->v_vfs_ea == AFPVOL_EA_AD) + check_eafiles(name); + } /************************************************************************** Recursion **************************************************************************/ - if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */ + if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant enter recursion */ strcat(cwdbuf, "/"); - strcat(cwdbuf, ep->d_name); + strcat(cwdbuf, name); dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf); if (-1 == (cwd = open(".", O_RDONLY))) { dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno)); continue; } - if (0 != chdir(ep->d_name)) { + if (0 != chdir(name)) { dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno)); close(cwd); continue; @@ -1037,249 +793,72 @@ static int dbd_readdir(int volroot, cnid_t did) /* Use results of previous checks */ - - closedir(dp); - return ret; -} - -static int scanvol(struct volinfo *vi, dbd_flags_t flags) -{ - /* Dont scanvol on no-appledouble vols */ - if (vi->v_flags & AFPVOL_NOADOUBLE) { - dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning."); - return 0; - } - - /* Make this stuff accessible from all funcs easily */ - myvolinfo = vi; - dbd_flags = flags; - - /* Init a fake struct vol with just enough so we can call ea_open and friends */ - volume.v_adouble = AD_VERSION2; - volume.v_vfs_ea = myvolinfo->v_vfs_ea; - initvol_vfs(&volume); - - /* Run with umask 0 */ - umask(0); - - /* Remove trailing slash from volume, chdir to vol */ - if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/') - myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0; - strcpy(cwdbuf, myvolinfo->v_path); - chdir(myvolinfo->v_path); - - /* Start recursion */ - if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */ - return -1; - - return 0; -} - -/* - Remove all CNIDs from dbd that are not in dbd_rebuild -*/ -static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags) -{ - int ret = 0, deleted = 0; - cnid_t dbd_cnid = 0, rebuild_cnid = 0; - struct cnid_dbd_rqst rqst; - struct cnid_dbd_rply rply; - - /* jump over rootinfo key */ - if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1) - return; - if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1) - return; - - /* Get first id from dbd_rebuild */ - if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) - return; - - /* Start main loop through dbd: get CNID from dbd */ - while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) { - /* Check if we got a termination signal */ - if (alarmed) - longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ - - if (deleted > 1000) { - deleted = 0; - if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) { - dbd_log(LOGSTD, "Error checkpointing!"); - goto cleanup; - } - } - - /* This should be the normal case: CNID is in both dbs */ - if (dbd_cnid == rebuild_cnid) { - /* Get next CNID from rebuild db */ - if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) { - /* Some error */ - goto cleanup; - } else if (ret == 0) { - /* end of rebuild_cnid, delete all remaining CNIDs from dbd */ - while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) { - dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid); - if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { - rqst.cnid = htonl(dbd_cnid); - if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) { - dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid); - (void)dbif_txn_abort(dbd); - goto cleanup; - } - - if (dbif_txn_close(dbd, ret) != 0) - return; - deleted++; - } - /* Check if we got a termination signal */ - if (alarmed) - longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ - } - return; - } else - /* Normal case (ret=1): continue while loop */ - continue; - } - - if (dbd_cnid < rebuild_cnid) { - /* CNID is orphaned -> delete */ - dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid); - if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { - rqst.cnid = htonl(dbd_cnid); - if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) { - dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid); - (void)dbif_txn_abort(dbd); - goto cleanup; - } - if (dbif_txn_close(dbd, ret) != 0) - return; - deleted++; + if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) { + if (rmdir(ADv2_DIRNAME) != 0) { + switch (errno) { + case ENOENT: + break; + default: + dbd_log(LOGSTD, "Error removing adouble dir \"%s/%s\": %s", cwdbuf, ADv2_DIRNAME, strerror(errno)); + break; } - continue; - } - - if (dbd_cnid > rebuild_cnid) { - dbif_idwalk(dbd, NULL, 1); /* Close cursor */ - dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */ - (void)dbif_txn_close(dbd, 2); - (void)dbif_txn_close(dbd_rebuild, 2); - dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid); - dbif_dump(dbd_rebuild, 0); - dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!"); - goto cleanup; } - } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */ - -cleanup: - dbif_idwalk(dbd, NULL, 1); /* Close cursor */ - dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */ - return; -} - -static const char *get_tmpdb_path(void) -{ - pid_t pid = getpid(); - static char path[MAXPATHLEN]; - snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid); - if (mkdir(path, 0755) != 0) - return NULL; - return path; + } + closedir(dp); + return ret; } /* Main func called from cmd_dbd.c */ -int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags) +int cmd_dbd_scanvol(struct vol *vol_in, dbd_flags_t flags) { - int ret = 0; - struct db_param db_param = { 0 }; - const char *tmpdb_path = NULL; + EC_INIT; + struct stat st; - /* Set cachesize for in-memory rebuild db */ - db_param.cachesize = 64 * 1024; /* 64 MB */ - db_param.maxlocks = DEFAULT_MAXLOCKS; - db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS; - db_param.logfile_autoremove = 1; + /* Run with umask 0 */ + umask(0); - /* Make it accessible for all funcs */ - dbd = dbd_ref; + /* Make vol accessible for all funcs */ + vol = vol_in; + dbd_flags = flags; /* We only support unicode volumes ! */ - if ( vi->v_volcharset != CH_UTF8) { - dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8); - return -1; - } - - /* Get volume stamp */ - dbd_getstamp(dbd, &rqst, &rply); - if (rply.result != CNID_DBD_RES_OK) { - ret = -1; - goto exit; + if (vol->v_volcharset != CH_UTF8) { + dbd_log(LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8); + EC_FAIL; } - memcpy(stamp, rply.name, CNID_DEV_LEN); - - /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */ - if (! nocniddb && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) { - tmpdb_path = get_tmpdb_path(); - if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) { - ret = -1; - goto exit; - } - - if (dbif_env_open(dbd_rebuild, - &db_param, - DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) { - dbd_log(LOGSTD, "error opening tmp database!"); - goto exit; - } - - if (0 != (dbif_open(dbd_rebuild, NULL, 0))) { - ret = -1; - goto exit; - } - if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) { - ret = -1; - goto exit; - } - } + /* + * Get CNID database stamp, cnid_getstamp() passes the buffer, + * then cnid_resolve() actually gets the value from the db + */ + cnid_getstamp(vol->v_cdb, stamp, sizeof(stamp)); if (setjmp(jmp) != 0) { - ret = 0; /* Got signal, jump from dbd_readdir */ - goto exit; + EC_EXIT_STATUS(0); /* Got signal, jump from dbd_readdir */ } - /* scanvol */ - if ( (scanvol(vi, flags)) != 0) { - ret = -1; - goto exit; - } + strcpy(cwdbuf, vol->v_path); + chdir(vol->v_path); -exit: - if (! nocniddb) { - if (dbif_txn_close(dbd, ret == 0 ? 1 : 0) != 0) - ret = -1; - if (dbd_rebuild) - if (dbif_txn_close(dbd_rebuild, ret == 0 ? 1 : 0) != 0) - ret = -1; - if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) - /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from - other clients in between our pass 1 and 2 */ - delete_orphaned_cnids(dbd, dbd_rebuild, flags); + if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) { + if (lstat(".", &st) != 0) + EC_FAIL; + if (ad_convert(".", &st, vol, NULL) != 0) { + switch (errno) { + case ENOENT: + break; + default: + dbd_log(LOGSTD, "Conversion error for \"%s\": %s", vol->v_path, strerror(errno)); + break; + } + } } - if (dbd_rebuild) { - dbd_log(LOGDEBUG, "Closing tmp db"); - dbif_close(dbd_rebuild); - - if (tmpdb_path) { - char cmd[8 + MAXPATHLEN]; - snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path); - dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path); - system(cmd); - snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path); - system(cmd); - } - } - return ret; + /* Start recursion */ + EC_NEG1( dbd_readdir(1, htonl(2)) ); /* 2 = volumeroot CNID */ + +EC_CLEANUP: + EC_EXIT; }