+
+/*!
+ * 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
+ */
+static int get_lock(int cmd, const char *dbpath)
+{
+ static int lockfd = -1;
+ int ret;
+ char lockpath[PATH_MAX];
+ struct stat st;
+
+ LOG(log_debug, logtype_cnid, "get_lock(%s, \"%s\")",
+ cmd == LOCK_EXCL ? "LOCK_EXCL" :
+ cmd == LOCK_SHRD ? "LOCK_SHRD" :
+ cmd == LOCK_FREE ? "LOCK_FREE" :
+ cmd == LOCK_UNLOCK ? "LOCK_UNLOCK" : "UNKNOWN",
+ dbpath ? dbpath : "");
+
+ switch (cmd) {
+ case LOCK_FREE:
+ if (lockfd == -1)
+ return -1;
+ close(lockfd);
+ lockfd = -1;
+ return 0;
+
+ case LOCK_UNLOCK:
+ if (lockfd == -1)
+ return -1;
+ return unlock(lockfd, 0, SEEK_SET, 0);
+
+ 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 ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) {
+ LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno));
+ return -1;
+ }
+
+ if ((stat(dbpath, &st)) != 0) {
+ LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno));
+ return -1;
+ }
+
+ 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;
+
+ default:
+ return -1;
+ } /* switch(cmd) */
+
+ /* deadc0de, never get here */
+ return -1;
+}
+
+static int open_db(void)
+{
+ EC_INIT;
+
+ /* Get db lock */
+ if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+ LOG(log_error, logtype_cnid, "main: fatal db lock error");
+ EC_FAIL;
+ }
+
+ if (NULL == (dbd = dbif_init(bdata(dbpath), "cnid2.db")))
+ EC_FAIL;
+
+ /* Only recover if we got the lock */
+ if (dbif_env_open(dbd, dbp, DBOPTIONS | DB_RECOVER) < 0)
+ EC_FAIL;
+
+ LOG(log_debug, logtype_cnid, "Finished initializing BerkeleyDB environment");
+
+ if (dbif_open(dbd, dbp, 0) < 0)
+ EC_FAIL;
+
+ LOG(log_debug, logtype_cnid, "Finished opening BerkeleyDB databases");
+
+EC_CLEANUP:
+ if (ret != 0) {
+ if (dbd) {
+ (void)dbif_close(dbd);
+ dbd = NULL;
+ }
+ }
+
+ EC_EXIT;
+}
+
+static int delete_db(void)
+{
+ EC_INIT;
+ int cwd = -1;
+
+ EC_ZERO( get_lock(LOCK_FREE, bdata(dbpath)) );
+ EC_NEG1( cwd = open(".", O_RDONLY) );
+ chdir(cfrombstr(dbpath));
+ system("rm -f cnid2.db lock log.* __db.*");
+
+ if ((db_locked = get_lock(LOCK_EXCL, bdata(dbpath))) != LOCK_EXCL) {
+ LOG(log_error, logtype_cnid, "main: fatal db lock error");
+ EC_FAIL;
+ }
+
+ LOG(log_warning, logtype_cnid, "Recreated CNID BerkeleyDB databases of volume \"%s\"", vol->v_localname);
+
+EC_CLEANUP:
+ if (cwd != -1) {
+ fchdir(cwd);
+ close(cwd);
+ }
+ EC_EXIT;
+}
+
+
+/**
+ * Close dbd if open, delete it, reopen
+ *
+ * Also tries to copy the rootinfo key, that would allow for keeping the db stamp
+ * and last used CNID
+ **/
+static int reinit_db(void)
+{
+ EC_INIT;
+ DBT key, data;
+ bool copyRootInfo = false;
+
+ if (dbd) {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+
+ if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) <= 0) {
+ LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record");
+ copyRootInfo = false;
+ } else {
+ copyRootInfo = true;
+ }
+ (void)dbif_close(dbd);
+ }
+
+ EC_ZERO_LOG( delete_db() );
+ EC_ZERO_LOG( open_db() );
+
+ if (copyRootInfo == true) {
+ memset(&key, 0, sizeof(key));
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+
+ if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) != 0) {
+ LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key");
+ EC_FAIL;
+ }
+ }
+
+EC_CLEANUP:
+ EC_EXIT;
+}