/*
- * $Id: cnid_add.c,v 1.14.2.3 2001-12-15 06:35:28 jmarcus Exp $
+ * $Id: cnid_add.c,v 1.14.2.4 2002-02-09 20:29:02 jmarcus Exp $
*
* Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
* All Rights Reserved. See COPYRIGHT.
#include "cnid_private.h"
-#define MAX_ABORTS 255
-
/* add an entry to the CNID databases. we do this as a transaction
* to prevent messiness. */
-static int add_cnid(CNID_private *db, DB_TXN *ptid, DBT *key, DBT *data) {
+static int add_cnid(CNID_private *db, DBT *key, DBT *data) {
DBT altkey, altdata;
DB_TXN *tid;
- /* We create rc here because using errno is bad. Why? Well, if you
- * use errno once, then call another function which resets it, you're
- * screwed. */
- int rc, ret, aborts = 0;
+ int rc, ret;
memset(&altkey, 0, sizeof(altkey));
memset(&altdata, 0, sizeof(altdata));
- if (0) {
retry:
- if ((rc = txn_abort(tid)) != 0) {
- return rc;
- }
- if (++aborts > MAX_ABORTS) {
- return DB_LOCK_DEADLOCK;
- }
- }
-
- if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) {
+ if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
return rc;
}
/* main database */
if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
if (rc == DB_LOCK_DEADLOCK) {
+ if ((ret = txn_abort(tid)) != 0) {
+ return ret;
+ }
goto retry;
}
-
goto abort;
}
-
/* dev/ino database */
altkey.data = data->data;
altkey.size = CNID_DEVINO_LEN;
altdata.size = key->size;
if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) {
if (rc == DB_LOCK_DEADLOCK) {
+ if ((ret = txn_abort(tid)) != 0) {
+ return ret;
+ }
goto retry;
}
-
goto abort;
}
-
/* did/name database */
altkey.data = (char *) data->data + CNID_DEVINO_LEN;
altkey.size = data->size - CNID_DEVINO_LEN;
if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) {
if (rc == DB_LOCK_DEADLOCK) {
+ if ((ret = txn_abort(tid)) != 0) {
+ return ret;
+ }
goto retry;
}
-
goto abort;
}
if ((rc = txn_commit(tid, 0)) != 0) {
- syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s",
- db_strerror(rc));
+ syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s", db_strerror(rc));
return rc;
}
+
return 0;
abort:
return ret;
}
return rc;
-
}
cnid_t cnid_add(void *CNID, const struct stat *st,
int rc;
if (!(db = CNID) || !st || !name) {
- return 0;
+ errno = CNID_ERR_PARAM;
+ return CNID_INVALID;
}
/* Do a lookup. */
/* ... Return id if it is valid, or if Rootinfo is read-only. */
if (id || (db->flags & CNIDFLAG_DB_RO)) {
#ifdef DEBUG
- syslog(LOG_INFO, "cnid_add: Looked up did %u, name %s as %u",
- ntohl(did), name, ntohl(id));
+ syslog(LOG_INFO, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
#endif
return id;
}
if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
syslog(LOG_ERR, "cnid_add: Path name is too long");
- goto cleanup_err;
+ errno = CNID_ERR_PATH;
+ return CNID_INVALID;
}
data.size = CNID_HEADER_LEN + len + 1;
* cnid's to the database. */
if (ntohl(hint) >= CNID_START) {
/* If the key doesn't exist, add it in. Don't fiddle with nextID. */
- rc = add_cnid(db, NULL, &key, &data);
+ rc = add_cnid(db, &key, &data);
switch (rc) {
case DB_KEYEXIST: /* Need to use RootInfo after all. */
break;
default:
- syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
- ntohl(hint), db_strerror(rc));
- goto cleanup_err;
+ syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s", ntohl(hint), db_strerror(rc));
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
case 0:
#ifdef DEBUG
- syslog(LOG_INFO, "cnid_add: Used hint for did %u, name %s as %u",
- ntohl(did), name, ntohl(hint));
+ syslog(LOG_INFO, "cnid_add: Used hint for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
#endif
return hint;
}
}
- /* We need to create a random sleep interval to prevent deadlocks. */
- (void)srand(getpid() ^ time(NULL));
- t.tv_sec = 0;
-
memset(&rootinfo_key, 0, sizeof(rootinfo_key));
memset(&rootinfo_data, 0, sizeof(rootinfo_data));
rootinfo_key.data = ROOTINFO_KEY;
rootinfo_key.size = ROOTINFO_KEYLEN;
+retry:
+ if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
+ syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s", db_strerror(rc));
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
+ }
+
/* Get the key. */
-retry_get:
- switch (rc = db->db_didname->get(db->db_didname, NULL, &rootinfo_key,
- &rootinfo_data, 0)) {
+ switch (rc = db->db_didname->get(db->db_didname, tid, &rootinfo_key,
+ &rootinfo_data, DB_RMW)) {
case DB_LOCK_DEADLOCK:
if ((rc = txn_abort(tid)) != 0) {
syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
- goto cleanup_err;
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
}
- goto retry_get;
+ goto retry;
case 0:
memcpy(&hint, rootinfo_data.data, sizeof(hint));
+ id = ntohl(hint);
+ /* If we've hit the max CNID allowed, we return a fatal error. CNID
+ * needs to be recycled before proceding. */
+ if (++id == CNID_INVALID) {
+ txn_abort(tid);
+ syslog(LOG_ERR, "cnid_add: FATAL: Cannot add CNID for %s. CNID database has reached its limit.", name);
+ errno = CNID_ERR_MAX;
+ return CNID_INVALID;
+ }
+ hint = htonl(id);
#ifdef DEBUG
syslog(LOG_INFO, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
#endif
case DB_NOTFOUND:
hint = htonl(CNID_START);
#ifdef DEBUG
- syslog(LOG_INFO, "cnid_add: Using CNID_START for did %u, name %s",
- ntohl(did), name);
+ syslog(LOG_INFO, "cnid_add: Using CNID_START for did %u, name %s", ntohl(did), name);
#endif
break;
default:
- syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s",
- db_strerror(rc));
- goto cleanup_err;
- }
-
-
- if (0) {
-retry:
- t.tv_usec = rand() % 1000000;
-#ifdef DEBUG
- syslog(LOG_INFO, "cnid_add: Hitting MAX_ABORTS, sleeping");
-#endif
- (void)select(0, NULL, NULL, NULL, &t);
- }
- if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
- syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s",
- db_strerror(rc));
- goto cleanup_err;
- }
-
- /* Search for a new id. We keep the first id around to check for
- * wrap-around. NOTE: I do it this way so that we can go back and
- * fill in holes. */
- save = id = ntohl(hint);
- while ((rc = add_cnid(db, tid, &key, &data)) != 0) {
- /* Don't use any special CNIDs. */
- if (++id < CNID_START) {
- id = CNID_START;
- }
- if (rc == DB_LOCK_DEADLOCK) {
- if ((rc = txn_abort(tid)) != 0) {
- syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
- goto cleanup_err;
- }
- goto retry;
- }
-
- if ((rc != DB_KEYEXIST) || (save == id)) {
- syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
- ntohl(hint), db_strerror(rc));
- goto cleanup_abort;
- }
- hint = htonl(id);
+ syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
+ goto cleanup_abort;
}
rootinfo_data.data = &hint;
case DB_LOCK_DEADLOCK:
if ((rc = txn_abort(tid)) != 0) {
syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
- goto cleanup_err;
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
}
goto retry;
case 0:
+ /* The transaction finished, commit it. */
+ if ((rc = txn_commit(tid, 0)) != 0) {
+ syslog(LOG_ERR, "cnid_add: Unable to commit transaction: %s", db_strerror(rc));
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
+ }
break;
default:
- syslog(LOG_ERR, "cnid_add: Unable to update rootinfo: %s",
- db_strerror(rc));
+ syslog(LOG_ERR, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
goto cleanup_abort;
}
-cleanup_commit:
- /* The transaction finished, commit it. */
- if ((rc = txn_commit(tid, 0)) != 0) {
- syslog(LOG_ERR, "cnid_add: Unable to commit transaction: %s",
- db_strerror(rc));
- goto cleanup_err;
+ /* Now we need to add the CNID data to the databases. */
+ rc = add_cnid(db, &key, &data);
+ if (rc) {
+ syslog(LOG_ERR, "cnid_add: Failed to add CNID for %s to database using hint %u: %s", name, ntohl(hint), db_strerror(rc));
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
}
#ifdef DEBUG
- syslog(LOG_INFO, "cnid_add: Returned CNID for did %u, name %s as %u",
- ntohl(did), name, ntohl(hint));
+ syslog(LOG_INFO, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
#endif
+
return hint;
cleanup_abort:
txn_abort(tid);
-cleanup_err:
- return 0;
+ errno = CNID_ERR_DB;
+ return CNID_INVALID;
}
#endif /* CNID_DB */