X-Git-Url: https://arthur.barton.de/gitweb/?p=netatalk.git;a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fcmd_dbd.c;h=266b986ba00da9905c56777b4ac5dff6f50b4625;hp=2805b6fcf5c91423f36dd7e7c43784943c74300f;hb=980548c882a297d26e67397d0f72e0b991f36bf2;hpb=25ca059b795321076af8d8d422ba6a473ef04cf6 diff --git a/etc/cnid_dbd/cmd_dbd.c b/etc/cnid_dbd/cmd_dbd.c index 2805b6fc..266b986b 100644 --- a/etc/cnid_dbd/cmd_dbd.c +++ b/etc/cnid_dbd/cmd_dbd.c @@ -12,49 +12,6 @@ GNU General Public License for more details. */ -/* - dbd specs and implementation progress - ===================================== - - St := Status - - Force option - ------------ - - St Spec - -- ---- - OK If -f is requested, ensure -e is too. - Check if volumes is using AFPVOL_CACHE, then wipe db from disk. Rebuild from ad-files. - - 1st pass: Scan volume - -------------------- - - St Type Check - -- ---- ----- - OK F/D Make sure ad file exists - OK D Make sure .AppleDouble dir exist, create if missing. Error creating - it is fatal as that shouldn't happen as root. - OK F/D Delete orphaned ad-files, log dirs in ad-dir - OK F/D Check name encoding by roundtripping, log on error - OK F/D try: read CNID from ad file (if cnid caching is on) - try: fetch CNID from database - -> on mismatch: use CNID from file, update database (deleting both found CNIDs first) - -> if no CNID in ad file: write CNID from database to ad file - -> if no CNID in database: add CNID from ad file to database - -> on no CNID at all: create one and store in both places - OK F/D Add found CNID, DID, filename, dev/inode, stamp to rebuild database - OK F/D Check/update stamp (implicitly done while checking CNIDs) - - - 2nd pass: Delete unused CNIDs - ----------------------------- - - St Spec - -- ---- - OK Step through dbd (the one on disk) and rebuild-db from pass 1 and delete any CNID from - dbd not in rebuild db. This in only done in exclusive mode. -*/ - #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ @@ -70,63 +27,29 @@ #include #include -#include -#include +#include +#include +#include +#include + #include "cmd_dbd.h" -#include "dbd.h" -#include "dbif.h" -#include "db_param.h" - -#define LOCKFILENAME "lock" -#define DBOPTIONS (DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) - -int nocniddb = 0; /* Dont open CNID database, only scan filesystem */ -volatile sig_atomic_t alarmed; -struct volinfo volinfo; /* needed by pack.c:idxname() */ - -static DBD *dbd; -static int verbose; /* Logging flag */ -static int exclusive; /* Exclusive volume access */ -static struct db_param db_param = { - NULL, /* Volume dirpath */ - 1, /* bdb logfile autoremove */ - 64 * 1024, /* bdb cachesize (64 MB) */ - DEFAULT_MAXLOCKS, /* maxlocks */ - DEFAULT_MAXLOCKOBJS, /* maxlockobjs */ - 0, /* flush_interval */ - 0, /* flush_frequency */ - 1000, /* txn_frequency */ - 0, /* usock_file */ - -1, /* fd_table_size */ - -1, /* idle_timeout */ - -1 /* max_vols */ -}; -static char dbpath[MAXPATHLEN+1]; /* Path to the dbd database */ -/* - Provide some logging - */ -void dbd_log(enum logtype lt, char *fmt, ...) -{ - int len; - static char logbuffer[1024]; - va_list args; +enum dbd_cmd {dbd_scan, dbd_rebuild}; - if ( (lt == LOGSTD) || (verbose == 1)) { - va_start(args, fmt); - len = vsnprintf(logbuffer, 1023, fmt, args); - va_end(args); - logbuffer[1023] = 0; +/* Global variables */ +volatile sig_atomic_t alarmed; /* flags for signals */ - printf("%s\n", logbuffer); - } -} +/* Local variables */ +static dbd_flags_t flags; -/* - SIGNAL handling: - catch SIGINT and SIGTERM which cause clean exit. Ignore anything else. - */ +/*************************************************************************** + * Local functions + ***************************************************************************/ +/* + * SIGNAL handling: + * catch SIGINT and SIGTERM which cause clean exit. Ignore anything else. + */ static void sig_handler(int signo) { alarmed = 1; @@ -167,172 +90,81 @@ static void set_signal(void) } } -static int get_lock(const char *dbpath) -{ - int lockfd; - char lockpath[PATH_MAX]; - struct flock lock; - struct stat st; - - if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) { - dbd_log( LOGSTD, ".AppleDB pathname too long"); - exit(EXIT_FAILURE); - } - strncpy(lockpath, dbpath, PATH_MAX - 1); - strcat(lockpath, "/"); - strcat(lockpath, LOCKFILENAME); - - if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) { - dbd_log( LOGSTD, "Error opening lockfile: %s", strerror(errno)); - exit(EXIT_FAILURE); - } - - if ((stat(dbpath, &st)) != 0) { - dbd_log( LOGSTD, "Error statting lockfile: %s", strerror(errno)); - exit(EXIT_FAILURE); - } - - if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) { - dbd_log( LOGSTD, "Error inheriting lockfile permissions: %s", strerror(errno)); - exit(EXIT_FAILURE); - } - - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; - lock.l_type = F_WRLCK; - - if (fcntl(lockfd, F_SETLK, &lock) < 0) { - if (errno == EACCES || errno == EAGAIN) { - if (exclusive) { - dbd_log( LOGSTD, "Database is in use and exlusive was requested", strerror(errno)); - exit(EXIT_FAILURE); - }; - } else { - dbd_log( LOGSTD, "Error getting fcntl F_WRLCK on lockfile: %s", strerror(errno)); - exit(EXIT_FAILURE); - } - } - - return lockfd; -} - -static void free_lock(int lockfd) -{ - struct flock lock; - - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; - lock.l_type = F_UNLCK; - fcntl(lockfd, F_SETLK, &lock); - close(lockfd); -} - static void usage (void) { - printf("Usage: dbd [-e|-t|-v|-x] -d [-i] | -s [-c|-n]| -r [-c|-f] | -u \n" - "dbd can dump, scan, reindex and rebuild Netatalk dbd CNID databases.\n" - "dbd must be run with appropiate permissions i.e. as root.\n\n" - "Main commands are:\n" - " -d Dump CNID database\n" - " Option: -i dump indexes too\n\n" - " -s Scan volume:\n" - " 1. Compare CNIDs in database with volume\n" - " 2. Check if .AppleDouble dirs exist\n" - " 3. Check if AppleDouble file exist\n" - " 4. Report orphaned AppleDouble files\n" - " 5. Check for directories inside AppleDouble directories\n" - " 6. Check name encoding by roundtripping, log on error\n" - " 7. Check for orphaned CNIDs in database (requires -e)\n" - " 8. Open and close adouble files\n" - " Options: -c Don't check .AppleDouble stuff, only ckeck orphaned.\n" - " -n Don't open CNID database, skip CNID checks\n\n" - " -r Rebuild volume:\n" - " 1. Sync CNIDSs in database with volume\n" - " 2. Make sure .AppleDouble dir exist, create if missing\n" - " 3. Make sure AppleDouble file exists, create if missing\n" - " 4. Delete orphaned AppleDouble files\n" - " 5. Check for directories inside AppleDouble directories\n" - " 6. Check name encoding by roundtripping, log on error\n" - " 7. Check for orphaned CNIDs in database (requires -e)\n" - " 8. Open and close adouble files\n" - " Options: -c Don't create .AppleDouble stuff, only cleanup orphaned.\n" - " -f wipe database and rebuild from IDs stored in AppleDouble files,\n" - " only available for volumes without 'nocnidcache' option. Implies -e.\n\n" - " -u Prepare upgrade:\n" - " Before installing an upgraded version of Netatalk that is linked against\n" - " a newer BerkeleyDB lib, run `dbd -u ...` from the OLD Netatalk pior to\n" - " upgrading on all volumes. This removes the BerkleyDB environment.\n" - " On exit cnid_dbd does this automatically, so normally calling dbd -u should not be necessary.\n\n" - "General options:\n" - " -e only work on inactive volumes and lock them (exclusive)\n" - " -x rebuild indexes (just for completeness, mostly useless!)\n" + printf("Usage: dbd [-cfFstvV] \n\n" + "dbd scans all file and directories of AFP volumes, updating the\n" + "CNID database of the volume. dbd must be run with appropiate\n" + "permissions i.e. as root.\n\n" + "Options:\n" + " -s scan volume: treat the volume as read only and don't\n" + " perform any filesystem modifications\n" + " -c convert from adouble:v2 to adouble:ea\n" + " -F location of the afp.conf config file\n" + " -f delete and recreate CNID database\n" " -t show statistics while running\n" - " -v verbose\n\n" - "WARNING:\n" - "For -r -f restore of the CNID database from the adouble files, the CNID must of course\n" - "be synched to them files first with a plain -r rebuild !\n" + " -v verbose\n" + " -V show version info\n\n" ); } -int main(int argc, char **argv) +/*************************************************************************** + * Global functions + ***************************************************************************/ + +void dbd_log(enum logtype lt, char *fmt, ...) { - int c, lockfd, ret = -1; - int dump=0, scan=0, rebuild=0, prep_upgrade=0, rebuildindexes=0, dumpindexes=0, force=0; - dbd_flags_t flags = 0; - char *volpath; - int cdir; + int len; + static char logbuffer[1024]; + va_list args; - if (geteuid() != 0) { - usage(); - exit(EXIT_FAILURE); + if ( (lt == LOGSTD) || (flags & DBD_FLAGS_VERBOSE)) { + va_start(args, fmt); + len = vsnprintf(logbuffer, 1023, fmt, args); + va_end(args); + logbuffer[1023] = 0; + + printf("%s\n", logbuffer); } - /* Inhereting perms in ad_mkdir etc requires this */ - ad_setfuid(0); +} - while ((c = getopt(argc, argv, ":cdefinrstuvx")) != -1) { +int main(int argc, char **argv) +{ + EC_INIT; + int dbd_cmd = dbd_rebuild; + int cdir = -1; + AFPObj obj = { 0 }; + struct vol *vol = NULL; + const char *volpath = NULL; + + int c; + while ((c = getopt(argc, argv, ":cfF:rstvV")) != -1) { switch(c) { case 'c': - flags |= DBD_FLAGS_CLEANUP; + flags |= DBD_FLAGS_V2TOEA; + break; + case 'f': + flags |= DBD_FLAGS_FORCE; break; - case 'd': - dump = 1; + case 'F': + obj.cmdlineconfigfile = strdup(optarg); break; - case 'i': - dumpindexes = 1; + case 'r': + /* the default */ break; case 's': - scan = 1; + dbd_cmd = dbd_scan; flags |= DBD_FLAGS_SCAN; break; - case 'n': - nocniddb = 1; /* FIXME: this could/should be a flag too for consistency */ - break; - case 'r': - rebuild = 1; - break; case 't': flags |= DBD_FLAGS_STATS; break; - case 'u': - prep_upgrade = 1; - break; case 'v': - verbose = 1; - break; - case 'e': - exclusive = 1; - flags |= DBD_FLAGS_EXCL; - break; - case 'x': - rebuildindexes = 1; - break; - case 'f': - force = 1; - exclusive = 1; - flags |= DBD_FLAGS_FORCE | DBD_FLAGS_EXCL; + flags |= DBD_FLAGS_VERBOSE; break; + case 'V': + printf("dbd %s\n", VERSION); + exit(0); case ':': case '?': usage(); @@ -341,16 +173,18 @@ int main(int argc, char **argv) } } - if ((dump + scan + rebuild + prep_upgrade) != 1) { + if ( (optind + 1) != argc ) { usage(); exit(EXIT_FAILURE); } + volpath = argv[optind]; - if ( (optind + 1) != argc ) { + if (geteuid() != 0) { usage(); exit(EXIT_FAILURE); } - volpath = argv[optind]; + /* Inhereting perms in ad_mkdir etc requires this */ + ad_setfuid(0); setvbuf(stdout, (char *) NULL, _IONBF, 0); @@ -363,141 +197,96 @@ int main(int argc, char **argv) /* Setup signal handling */ set_signal(); + /* Load config */ + if (afp_config_parse(&obj, "dbd") != 0) { + dbd_log( LOGSTD, "Couldn't load afp.conf"); + exit(EXIT_FAILURE); + } + + /* Initialize CNID subsystem */ + cnid_init(); + /* Setup logging. Should be portable among *NIXes */ - if (!verbose) - setuplog("default log_info /dev/tty"); + if (flags & DBD_FLAGS_VERBOSE) + setuplog("default:note, cnid:debug", "/dev/tty"); else - setuplog("default log_debug /dev/tty"); + setuplog("default:note", "/dev/tty"); - /* Load .volinfo file */ - if (loadvolinfo(volpath, &volinfo) == -1) { - dbd_log( LOGSTD, "Not a Netatalk volume at '%s', no .volinfo file at '%s/.AppleDesktop/.volinfo' or unknown volume options", volpath, volpath); - exit(EXIT_FAILURE); - } - if (vol_load_charsets(&volinfo) == -1) { - dbd_log( LOGSTD, "Error loading charsets!"); + if (load_volumes(&obj, lv_all) != 0) { + dbd_log( LOGSTD, "Couldn't load volumes"); exit(EXIT_FAILURE); } - /* Sanity checks to ensure we can touch this volume */ - if (volinfo.v_vfs_ea != AFPVOL_EA_AD && volinfo.v_vfs_ea != AFPVOL_EA_SYS) { - dbd_log( LOGSTD, "Unknown Extended Attributes option: %u", volinfo.v_vfs_ea); - exit(EXIT_FAILURE); + if ((vol = getvolbypath(&obj, volpath)) == NULL) { + dbd_log( LOGSTD, "Couldn't find volume for '%s'", volpath); + exit(EXIT_FAILURE); } - /* Enuser dbpath is there, create if necessary */ - struct stat st; - if (stat(volinfo.v_dbpath, &st) != 0) { - if (errno != ENOENT) { - dbd_log( LOGSTD, "Can't stat dbpath \"%s\": %s", volinfo.v_dbpath, strerror(errno)); - exit(EXIT_FAILURE); - } - if ((mkdir(volinfo.v_dbpath, 0755)) != 0) { - dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno)); - exit(EXIT_FAILURE); - } + if (load_charset(vol) != 0) { + dbd_log( LOGSTD, "Couldn't load charsets for '%s'", volpath); + exit(EXIT_FAILURE); } - /* Put "/.AppleDB" at end of volpath, get path from volinfo file */ - if ( (strlen(volinfo.v_dbpath) + strlen("/.AppleDB")) > MAXPATHLEN ) { - dbd_log( LOGSTD, "Volume pathname too long"); - exit(EXIT_FAILURE); + /* open volume */ + if (STRCMP(vol->v_cnidscheme, != , "dbd") && STRCMP(vol->v_cnidscheme, != , "mysql")) { + dbd_log(LOGSTD, "\"%s\" isn't a \"dbd\" CNID volume", vol->v_path); + exit(EXIT_FAILURE); } - strncpy(dbpath, volinfo.v_dbpath, MAXPATHLEN - strlen("/.AppleDB")); - strcat(dbpath, "/.AppleDB"); - - /* Check or create dbpath */ - int dbdirfd = open(dbpath, O_RDONLY); - if (dbdirfd == -1 && errno == ENOENT) { - if (errno == ENOENT) { - if ((mkdir(dbpath, 0755)) != 0) { - dbd_log( LOGSTD, "Can't create .AppleDB for \"%s\": %s", dbpath, strerror(errno)); - exit(EXIT_FAILURE); - } - } else { - dbd_log( LOGSTD, "Somethings wrong with .AppleDB for \"%s\", giving up: %s", dbpath, strerror(errno)); - exit(EXIT_FAILURE); - } - } else { - close(dbdirfd); + if ((vol->v_cdb = cnid_open(vol->v_path, + 0000, + vol->v_cnidscheme, + vol->v_flags & AFPVOL_NODEV ? CNID_FLAG_NODEV : 0, + vol->v_cnidserver, + vol->v_cnidport, + &obj, + vol->v_uuid)) == NULL) { + dbd_log(LOGSTD, "Cant initialize CNID database connection for %s", vol->v_path); + exit(EXIT_FAILURE); } - /* - Before we do anything else, check if there is an instance of cnid_dbd - running already and silently exit if yes. - */ - lockfd = get_lock(dbpath); - - /* Prepare upgrade ? */ - if (prep_upgrade) { - if (dbif_env_remove(dbpath)) - goto exit_failure; - goto exit_success; - } - - /* Check if -f is requested and wipe db if yes */ - if ((flags & DBD_FLAGS_FORCE) && rebuild && (volinfo.v_flags & AFPVOL_CACHE)) { - char cmd[8 + MAXPATHLEN]; - close(lockfd); - snprintf(cmd, 8 + MAXPATHLEN, "rm -rf \"%s\"", dbpath); - dbd_log( LOGDEBUG, "Removing old database of volume: '%s'", volpath); - system(cmd); - if ((mkdir(dbpath, 0755)) != 0) { - dbd_log( LOGSTD, "Can't create dbpath \"%s\": %s", dbpath, strerror(errno)); - exit(EXIT_FAILURE); - } - dbd_log( LOGDEBUG, "Removed old database."); - lockfd = get_lock(dbpath); + if (vol->v_adouble == AD_VERSION_EA) + dbd_log( LOGDEBUG, "adouble:ea volume"); + else if (vol->v_adouble == AD_VERSION2) + dbd_log( LOGDEBUG, "adouble:v2 volume"); + else { + dbd_log( LOGSTD, "unknown adouble volume"); + exit(EXIT_FAILURE); } - /* - Lets start with the BerkeleyDB stuff - */ - if ( ! nocniddb) { - if ((dbd = dbif_init(dbpath, "cnid2.db")) == NULL) - goto exit_failure; - - if (dbif_env_open(dbd, &db_param, exclusive ? (DBOPTIONS | DB_RECOVER) : DBOPTIONS) < 0) { - dbd_log( LOGSTD, "error opening database!"); - goto exit_failure; - } + /* -C v2 to ea conversion only on adouble:ea volumes */ + if ((flags & DBD_FLAGS_V2TOEA) && (vol->v_adouble!= AD_VERSION_EA)) { + dbd_log( LOGSTD, "Can't run adouble:v2 to adouble:ea conversion because not an adouble:ea volume"); + exit(EXIT_FAILURE); + } - if (exclusive) - dbd_log( LOGDEBUG, "Finished recovery."); + /* Sanity checks to ensure we can touch this volume */ + if (vol->v_vfs_ea != AFPVOL_EA_AD && vol->v_vfs_ea != AFPVOL_EA_SYS) { + dbd_log( LOGSTD, "Unknown Extended Attributes option: %u", vol->v_vfs_ea); + exit(EXIT_FAILURE); + } - if (dbif_open(dbd, NULL, rebuildindexes) < 0) { - dbif_close(dbd); - goto exit_failure; + if (flags & DBD_FLAGS_FORCE) { + if (cnid_wipe(vol->v_cdb) != 0) { + dbd_log( LOGSTD, "Failed to wipe CNID db"); + EC_FAIL; } } /* Now execute given command scan|rebuild|dump */ - if (dump && ! nocniddb) { - if (dbif_dump(dbd, dumpindexes) < 0) { - dbd_log( LOGSTD, "Error dumping database"); - } - } else if ((rebuild && ! nocniddb) || scan) { - if (cmd_dbd_scanvol(dbd, &volinfo, flags) < 0) { + switch (dbd_cmd) { + case dbd_scan: + case dbd_rebuild: + if (cmd_dbd_scanvol(vol, flags) < 0) { dbd_log( LOGSTD, "Error repairing database."); } + break; } - /* Cleanup */ - dbd_log(LOGDEBUG, "Closing db"); - if (! nocniddb) { - if (dbif_close(dbd) < 0) { - dbd_log( LOGSTD, "Error closing database"); - goto exit_failure; - } - } - -exit_success: - ret = 0; +EC_CLEANUP: + if (vol) + cnid_close(vol->v_cdb); -exit_failure: - free_lock(lockfd); - - if ((fchdir(cdir)) < 0) + if (cdir != -1 && (fchdir(cdir) < 0)) dbd_log(LOGSTD, "fchdir: %s", strerror(errno)); if (ret == 0)