]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/cnid/cnid_add.c
Merge latest CNID DB code from HEAD.
[netatalk.git] / libatalk / cnid / cnid_add.c
index 6591088a7d91f2c2b42ba55090f422fdb748c25b..e6a725544d205d049cd4ce9dbfea40e76498bfc6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $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;
@@ -85,29 +72,32 @@ retry:
     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:
@@ -115,7 +105,6 @@ abort:
         return ret;
     }
     return rc;
-
 }
 
 cnid_t cnid_add(void *CNID, const struct stat *st,
@@ -130,7 +119,8 @@ 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. */
@@ -138,8 +128,7 @@ cnid_t cnid_add(void *CNID, const struct stat *st,
     /* ... 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;
     }
@@ -154,7 +143,8 @@ cnid_t cnid_add(void *CNID, const struct stat *st,
 
     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;
@@ -164,44 +154,56 @@ cnid_t cnid_add(void *CNID, const struct stat *st,
      * 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
@@ -209,54 +211,12 @@ retry_get:
     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;
@@ -266,36 +226,42 @@ retry:
     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 */