X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fdbif.c;h=217fdd6d73d7255e6e1a16acc8c9acc3f7ada82b;hb=056d3ef4c88ba09eabb1fcbf06bdd9fe6e7af4cf;hp=549277da31329ca307c88bdde1722c8dbd183241;hpb=f4753a76a913c64fb6cdeb480564777e35c14e37;p=netatalk.git diff --git a/etc/cnid_dbd/dbif.c b/etc/cnid_dbd/dbif.c index 549277da..217fdd6d 100644 --- a/etc/cnid_dbd/dbif.c +++ b/etc/cnid_dbd/dbif.c @@ -11,83 +11,210 @@ #include #include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif /* HAVE_SYS_TYPES_H */ #include #include #include -#include #include -#include +#include + #include + +#include +#include +#include + #include "db_param.h" #include "dbif.h" #include "pack.h" #define DB_ERRLOGFILE "db_errlog" -static char *old_dbfiles[] = {"cnid.db", NULL}; - -/* --------------- */ -static int upgrade_required(const DBD *dbd) +/*! + * Get the db stamp which is the st_ctime of the file "cnid2.db" and store it in buffer + */ +static int dbif_stamp(DBD *dbd, void *buffer, int size) { - int i; - int cwd = -1; - int ret = 0; - int found = 0; + EC_INIT; struct stat st; + int cwd = -1; - if ( ! dbd->db_filename) - /* in memory db */ - return 0; + if (size < 8) + EC_FAIL; /* Remember cwd */ if ((cwd = open(".", O_RDONLY)) < 0) { LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno)); - return -1; + EC_FAIL; } /* chdir to db_envhome */ if ((chdir(dbd->db_envhome)) != 0) { LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno)); - ret = -1; - goto exit; + EC_FAIL; } - for (i = 0; old_dbfiles[i] != NULL; i++) { - if ( !(stat(old_dbfiles[i], &st) < 0) ) { - found++; - continue; - } - if (errno != ENOENT) { - LOG(log_error, logtype_cnid, "cnid_open: Checking %s gave %s", old_dbfiles[i], strerror(errno)); - found++; - } + if (stat(dbd->db_table[DBIF_CNID].name, &st) < 0) { + LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno)); + EC_FAIL; } -exit: + LOG(log_maxdebug, logtype_cnid,"stamp: %s", asctime(localtime(&st.st_ctime))); + + memset(buffer, 0, size); + memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime)); + +EC_CLEANUP: if (cwd != -1) { - if ((fchdir(cwd)) != 0) { + if (fchdir(cwd) != 0) { LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno)); - ret = -1; + EC_STATUS(-1); } close(cwd); } - return (ret < 0 ? ret : found); + EC_EXIT; +} + +/*! + * Inititialize rootinfo key (which has CNID 0 as key) + * + * This also "stamps" the database, which means storing st.st_ctime of the + * "cnid2.db" file in the rootinfo data at the DEV offset + * + * @param dbd (rw) database handle + * @param version (r) database version number + * + * @returns -1 on error, 0 on success + */ +static int dbif_init_rootinfo(DBD *dbd, int version) +{ + DBT key, data; + uint32_t v; + char buf[ROOTINFO_DATALEN]; + + LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version); + + v = version; + v = htonl(v); + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = ROOTINFO_KEY; + key.size = ROOTINFO_KEYLEN; + data.data = buf; + data.size = ROOTINFO_DATALEN; + + memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN); + memcpy(buf + CNID_DID_OFS, &v, sizeof(v)); + if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0) + return -1; + + if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0) + return -1; + if (dbif_txn_commit(dbd) != 1) { + LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn"); + return -1; + } + + return 0; } /*! - * Upgrade CNID database versions + * Return CNID database version number + * + * Returns version in *version + * + * @returns -1 on error, 0 if theres no rootinfo key yet, 1 if *version is returned + */ +static int dbif_getversion(DBD *dbd, uint32_t *version) +{ + DBT key, data; + int ret; + + LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info"); + + *version = -1; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = ROOTINFO_KEY; + key.size = ROOTINFO_KEYLEN; + + switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) { + case 1: /* found */ + memcpy(version, (char *)data.data + CNID_DID_OFS, sizeof(uint32_t)); + *version = ntohl(*version); + LOG(log_debug, logtype_cnid, "CNID database version %u", *version); + ret = 1; + break; + case 0: /* not found */ + LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found"); + ret = 0; + break; + default: + LOG(log_error, logtype_cnid, "dbif_getversion: database error"); + ret = -1; + break; + } + + return ret; +} + +/*! + * Set CNID database version number + * + * Initializes rootinfo key as neccessary + * @returns -1 on error, 0 on success + */ +static int dbif_setversion(DBD *dbd, uint32_t version) +{ + int ret; + DBT key, data; + uint32_t v; + + LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version); + + v = version; + v = htonl(v); + + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = ROOTINFO_KEY; + key.size = ROOTINFO_KEYLEN; + + if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1) + return -1; + if (ret == 0) { + /* No rootinfo key yet, init it */ + if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0) + return -1; + /* Now try again */ + if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1) + return -1; + } + memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v)); + data.size = ROOTINFO_DATALEN; + if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0) + return -1; + + return 0; +} + +/*! + * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion() * * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open */ +#define UNINTIALIZED_DB UINT32_MAX static int dbif_upgrade(DBD *dbd) { - int version; + uint32_t version = CNID_VERSION_UNINTIALIZED_DB; - if ((version = dbif_getversion(dbd)) == -1) + if (dbif_getversion(dbd, &version) == -1) return -1; + if (version == CNID_VERSION_UNINTIALIZED_DB) { + version = CNID_VERSION; + if (dbif_setversion(dbd, CNID_VERSION) != 0) + return -1; + } /* * Do upgrade stuff ... @@ -199,45 +326,89 @@ exit: return ret; } -#include - -/* --------------- */ -int dbif_stamp(DBD *dbd, void *buffer, int size) +/*! + * Get lock on db lock file + * + * @args cmd (r) lock command: + * LOCK_FREE: close lockfd + * LOCK_UNLOCK: unlock lockm keep lockfd open + * LOCK_EXCL: F_WRLCK on lockfd + * LOCK_SHRD: F_RDLCK on lockfd + * @args dbpath (r) path to lockfile, only used on first call, + * later the stored fd is used + * @returns LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error + * LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on + * success, 0 if the lock couldn't be acquired, -1 on other errors + */ +int get_lock(int cmd, const char *dbpath) { + static int lockfd = -1; + int ret; + char lockpath[PATH_MAX]; struct stat st; - int rc,cwd; - if (size < 8) - return -1; + switch (cmd) { + case LOCK_FREE: + if (lockfd == -1) + return -1; + close(lockfd); + lockfd = -1; + return 0; - /* Remember cwd */ - if ((cwd = open(".", O_RDONLY)) < 0) { - LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno)); - return -1; - } + case LOCK_UNLOCK: + if (lockfd == -1) + return -1; + return unlock(lockfd, 0, SEEK_SET, 0); - /* chdir to db_envhome */ - if ((chdir(dbd->db_envhome)) != 0) { - LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno)); - return -1; - } + case LOCK_EXCL: + case LOCK_SHRD: + if (lockfd == -1) { + if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) { + LOG(log_error, logtype_cnid, ".AppleDB pathname too long"); + return -1; + } + strncpy(lockpath, dbpath, PATH_MAX - 1); + strcat(lockpath, "/"); + strcat(lockpath, LOCKFILENAME); - if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) { - LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno)); - return -1; - } + if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) { + LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno)); + return -1; + } - LOG(log_maxdebug, logtype_cnid,"stamp: %s", asctime(localtime(&st.st_ctime))); + if ((stat(dbpath, &st)) != 0) { + LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno)); + return -1; + } - memset(buffer, 0, size); - memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime)); + if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) { + LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s", + strerror(errno)); + return -1; + } + } + + if (cmd == LOCK_EXCL) + ret = write_lock(lockfd, 0, SEEK_SET, 0); + else + ret = read_lock(lockfd, 0, SEEK_SET, 0); + + if (ret != 0) { + if (cmd == LOCK_SHRD) + LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again..."); + return 0; + } + + LOG(log_debug, logtype_cnid, "get_lock: got %s lock", + cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD"); + return cmd; - if ((fchdir(cwd)) != 0) { - LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno)); + default: return -1; - } + } /* switch(cmd) */ - return 0; + /* deadc0de, never get here */ + return -1; } /* --------------- */ @@ -290,7 +461,7 @@ DBD *dbif_init(const char *envhome, const char *filename) /* We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which breaks e.g. bdb logfile-rotation with relative pathnames. - But still we use relative paths with upgrade_required() and the DB_ERRLOGFILE + But still we use relative paths with DB_ERRLOGFILE in order to avoid creating absolute paths by copying. Both have no problem with a relative path. */ @@ -298,12 +469,6 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags) { int ret; - /* Refuse to do anything if this is an old version of the CNID database */ - if (upgrade_required(dbd)) { - LOG(log_error, logtype_cnid, "Found version 1 of the CNID database. Please upgrade to version 2"); - return -1; - } - if ((ret = db_env_create(&dbd->db_env, 0))) { LOG(log_error, logtype_cnid, "error creating DB environment: %s", db_strerror(ret)); @@ -311,6 +476,8 @@ int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags) return -1; } + dbd->db_param = *dbp; + if ((dbif_openlog(dbd)) != 0) return -1; @@ -552,9 +719,13 @@ int dbif_open(DBD *dbd, struct db_param *dbp, int reindex) if (reindex) LOG(log_info, logtype_cnid, "Reindexing name index..."); - int version = CNID_VERSION; + /* + * Upgrading from version 0 to 1 requires adding the name index below which + * must be done by specifying the DB_CREATE flag + */ + uint32_t version = CNID_VERSION; if (dbd->db_envhome && !reindex) { - if ((version = dbif_getversion(dbd)) == -1) + if (dbif_getversion(dbd, &version) == -1) return -1; } @@ -797,7 +968,7 @@ int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags) flags); if (ret == DB_NOTFOUND) { - LOG(log_info, logtype_cnid, "key not found"); + LOG(log_debug, logtype_cnid, "key not found"); return 0; } if (ret) { @@ -808,122 +979,6 @@ int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags) return 1; } -/*! - * Inititialize rootinfo key (which has CNID 0 as key) - * - * This also "stamps" the database, which means storing st.st_ctime of the - * "cnid2.db" file in the rootinfo data at the DEV offset - * - * @param dbd (rw) database handle - * @param version (r) database version number - * - * @returns -1 on error, 0 on success - */ -static int dbif_init_rootinfo(DBD *dbd, int version) -{ - DBT key, data; - uint32_t v; - char buf[ROOTINFO_DATALEN]; - - LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version); - - v = version; - v = htonl(v); - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = ROOTINFO_KEY; - key.size = ROOTINFO_KEYLEN; - data.data = buf; - data.size = ROOTINFO_DATALEN; - - memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN); - memcpy(buf + CNID_DID_OFS, &v, sizeof(v)); - if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0) - return -1; - - if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0) - return -1; - - return 0; -} - -/*! - * Initialize or return CNID database version number - * - * Calls dbif_init_rootinfo if the rootinfo key does not exist yet - * - * @returns -1 on error, version number otherwise - */ -int dbif_getversion(DBD *dbd) -{ - DBT key, data; - uint32_t version; - int ret; - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = ROOTINFO_KEY; - key.size = ROOTINFO_KEYLEN; - - switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) { - case 1: /* found */ - memcpy(&version, (char *)data.data + CNID_DID_OFS, sizeof(version)); - version = ntohl(version); - LOG(log_debug, logtype_cnid, "CNID database version %u", version); - ret = version; - break; - case 0: /* not found */ - if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0) - return -1; - ret = CNID_VERSION; - break; - default: - return -1; - } - return ret; -} - -/*! - * Set CNID database version number - * - * Initializes rootinfo key as neccessary, as does dbif_getversion - * @returns -1 on error, version number otherwise - */ -int dbif_setversion(DBD *dbd, int version) -{ - int ret; - DBT key, data; - uint32_t v; - - LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version); - - v = version; - v = htonl(v); - - memset(&key, 0, sizeof(key)); - memset(&data, 0, sizeof(data)); - key.data = ROOTINFO_KEY; - key.size = ROOTINFO_KEYLEN; - - if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1) - return -1; - if (ret == 0) { - /* No rootinfo key yet, init it */ - if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0) - return -1; - /* Now try again */ - if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1) - return -1; - } - memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v)); - data.size = ROOTINFO_DATALEN; - if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0) - return -1; - - return 0; -} - /*! * Search the database by name * @@ -1042,25 +1097,30 @@ int dbif_txn_abort(DBD *dbd) } /* - ret = 1 -> commit txn - ret = 0 -> abort txn -> exit! + ret = 1 -> commit txn if db_param.txn_frequency + ret = 0 -> abort txn db_param.txn_frequency -> exit! anything else -> exit! + + @returns 0 on success (abort or commit), -1 on error */ -void dbif_txn_close(DBD *dbd, int ret) +int dbif_txn_close(DBD *dbd, int ret) { if (ret == 0) { if (dbif_txn_abort(dbd) < 0) { LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!"); - exit(EXIT_FAILURE); + return -1; } } else if (ret == 1) { ret = dbif_txn_commit(dbd); if ( ret < 0) { LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!"); - exit(EXIT_FAILURE); + return -1; } - } else - exit(EXIT_FAILURE); + } else { + return -1; + } + + return 0; } int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags)