]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/cnid/cnid_add.c
Tru64 does not define timeval in time.h. Make sure we check for time.h and
[netatalk.git] / libatalk / cnid / cnid_add.c
index b159c813d9580949092c1d92369fa12c35f82c52..c0ff281117a1f76e36990afc26b67697fd6b21cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: cnid_add.c,v 1.11 2001-10-18 02:28:56 jmarcus Exp $
+ * $Id: cnid_add.c,v 1.16 2001-12-03 15:46:30 jmarcus Exp $
  *
  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
  * All Rights Reserved. See COPYRIGHT.
 #endif /* HAVE_FCNTL_H */
 #include <errno.h>
 #include <syslog.h>
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif /* HAVE_TIME_H */
+#ifdef HAVE_CMA_H
+#include <cma.h>
+#endif /* HAVE_CMA_H */
 
 #include <db.h>
 #include <netatalk/endian.h>
 
 #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)
-{
-  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 = 0;
-
-  memset(&altkey, 0, sizeof(altkey));
-  memset(&altdata, 0, sizeof(altdata));
-  
+static int add_cnid(CNID_private *db, DB_TXN *ptid, 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;
+
+    memset(&altkey, 0, sizeof(altkey));
+    memset(&altdata, 0, sizeof(altdata));
+
+    if (0) {
 retry:
-  if ((rc = txn_begin(db->dbenv, ptid, &tid,0))) {
-    return rc;
-  }
+        if ((rc = txn_abort(tid)) != 0) {
+            return rc;
+        }
+        if (++aborts > MAX_ABORTS) {
+            return DB_LOCK_DEADLOCK;
+        }
+    }
 
-  /* main database */
-  if ((rc = db->db_cnid->put(db->db_cnid, tid,
-                               key, data, DB_NOOVERWRITE))) {
-    txn_abort(tid);
-    if (rc == DB_LOCK_DEADLOCK)
-      goto retry;
+    if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) {
+        return rc;
+    }
 
-    return rc;
-  }
+    /* main database */
+    if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
+        if (rc == DB_LOCK_DEADLOCK) {
+            goto retry;
+        }
 
-  /* 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))) {
-    txn_abort(tid);
-    if (rc == DB_LOCK_DEADLOCK)
-      goto retry;
+        goto abort;
+    }
 
-    return rc;
-  }
-
-  /* dev/ino database */
-  altkey.data = data->data;
-  altkey.size = CNID_DEVINO_LEN;
-  altdata.data = key->data;
-  altdata.size = key->size;
-  if ((rc = db->db_devino->put(db->db_devino, tid,
-                                 &altkey, &altdata, 0))) {
-    txn_abort(tid);
-    if (rc == DB_LOCK_DEADLOCK)
-      goto retry;
 
-    return rc;
-  }
+    /* dev/ino database */
+    altkey.data = data->data;
+    altkey.size = CNID_DEVINO_LEN;
+    altdata.data = key->data;
+    altdata.size = key->size;
+    if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) {
+        if (rc == DB_LOCK_DEADLOCK) {
+            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) {
+            goto retry;
+        }
+
+        goto abort;
+    }
+
+    if ((rc = txn_commit(tid, 0)) != 0) {
+        syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s",
+               db_strerror(rc));
+        return rc;
+    }
+    return 0;
+
+abort:
+    if ((ret = txn_abort(tid)) != 0) {
+        return ret;
+    }
+    return rc;
 
-  return txn_commit(tid, 0);
 }
 
-/* 0 is not a valid cnid. this will do a cnid_lookup beforehand and
-   return that cnid if it exists.  */
 cnid_t cnid_add(void *CNID, const struct stat *st,
-               const cnid_t did, const char *name, const int len,
-               cnid_t hint)
+                const cnid_t did, const char *name, const int len,
+                cnid_t hint)
 {
-  CNID_private *db;
-  DBT key, data;
-  DBT rootinfo_key, rootinfo_data;
-  DB_TXN *tid;
-  cnid_t id, save;
-  int rc = 0;
+    CNID_private *db;
+    DBT key, data, rootinfo_key, rootinfo_data;
+    DB_TXN *tid;
+    struct timeval t;
+    cnid_t id, save;
+    int rc;
+
+    if (!(db = CNID) || !st || !name) {
+        return 0;
+    }
 
-  int debug = 0;
+    /* Do a lookup. */
+    id = cnid_lookup(db, st, did, name, len);
+    /* ... 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));
+#endif
+        return id;
+    }
 
+    /* Initialize our DBT data structures. */
+    memset(&key, 0, sizeof(key));
+    memset(&data, 0, sizeof(data));
 
-  if (!(db = CNID) || !st || !name) {
-    return 0;
-  }
-
-  /* do a lookup... */
-  id = cnid_lookup(db, st, did, name, len);
-  /* ...return id if it is valid or if RootInfo is read-only. */
-  if (id || (db->flags & CNIDFLAG_DB_RO)) {
-    if (debug)
-      syslog(LOG_ERR, "cnid_add: looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
-    return id;
-  }
-
-  /* initialize everything */
-  memset(&key, 0, sizeof(key));
-  memset(&data, 0, sizeof(data));
-
-  /* just set hint, and the key will change. */
-  key.data = &hint;
-  key.size = sizeof(hint);
-
-  if ((data.data =
-       make_cnid_data(st, did, name, len)) == NULL) {
-    syslog(LOG_ERR, "cnid_add: path name too long.");
-    goto cleanup_err;
-  }
-  data.size = CNID_HEADER_LEN + len + 1;
-
-  /* start off with the hint. it should be in network byte order.
-   * we need to make sure that somebody doesn't add in restricted
-   * 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);
-    switch (rc) {
-    case DB_KEYEXIST: /* need to use RootInfo after all. */
-      break;
-    default:
-      syslog(LOG_ERR, "cnid_add: unable to add CNID %u (%d)", ntohl(hint), rc);
-      goto cleanup_err;
+    /* Just tickle hint, and the key will change (gotta love pointers). */
+    key.data = &hint;
+    key.size = sizeof(hint);
+
+    if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
+        syslog(LOG_ERR, "cnid_add: Path name is too long");
+        goto cleanup_err;
+    }
+
+    data.size = CNID_HEADER_LEN + len + 1;
+
+    /* Start off with the hint.  It should be in network byte order.
+     * We need to make sure that somebody doesn't add in restricted
+     * 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);
+        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;
+        case 0:
+#ifdef DEBUG
+            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;
+
+    /* Get the key. */
+retry_get:
+    switch (rc = db->db_didname->get(db->db_didname, NULL, &rootinfo_key,
+                                     &rootinfo_data, 0)) {
+    case 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_get;
     case 0:
-      if (debug)
-        syslog(LOG_ERR, "cnid_add: used hint for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
-      return hint;
-    }
-  }
-
-
-  memset(&rootinfo_key, 0, sizeof(rootinfo_key));
-  memset(&rootinfo_data, 0, sizeof(rootinfo_data));
-
-  /* just set hint, and the key will change. */
-  rootinfo_key.data = ROOTINFO_KEY;
-  rootinfo_key.size = ROOTINFO_KEYLEN;
-
-  /* Get the key. */
-  switch (rc = db->db_didname->get(db->db_didname, NULL, &rootinfo_key, &rootinfo_data, 0)) {
-  case DB_LOCK_DEADLOCK:
-          goto retry;
-  case 0:
-          memcpy (&hint, rootinfo_data.data, sizeof(hint));
-          if (debug)
-            syslog(LOG_ERR, "cnid_add: found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
-          break;
-  case DB_NOTFOUND:
-          hint = htonl(CNID_START);
-          if (debug)
-            syslog(LOG_ERR, "cnid_add: using CNID_START for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
-          break;
-  default:
-          syslog(LOG_ERR, "cnid_add: unable to lookup rootinfo (%d)", rc);
-                 goto cleanup_err;
- }
-
-  /* Abort and retry the modification. */
-  if (0) {
-retry:    if ((rc = txn_abort(tid)) != 0)
-              syslog(LOG_ERR, "cnid_add: txn_abort failed (%d)", rc);
-          /* FALLTHROUGH */
-  }
-
-  /* Begin the transaction. */
-  if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
-    syslog(LOG_ERR, "cnid_add: txn_begin failed (%d)", 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))) {
-    /* don't use any of the special CNIDs */
-    if (++id < CNID_START)
-      id = CNID_START;
-
-    if ((rc != DB_KEYEXIST) || (save == id)) {
-      syslog(LOG_ERR, "cnid_add: unable to add CNID %u (%d)", ntohl(hint), rc);
-      hint = 0;
-      goto cleanup_abort;
-    }
-    hint = htonl(id);
-  }
-
-  /* update RootInfo with the next id. */
-  rootinfo_data.data = &hint;
-  rootinfo_data.size = sizeof(hint);
-
-  switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
-  case DB_LOCK_DEADLOCK:
-          goto retry;
-  case 0:
-          break;
-  default:
-          syslog(LOG_ERR, "cnid_add: unable to update rootinfo (%d)", rc);
-          goto cleanup_abort;
-  }
+        memcpy(&hint, rootinfo_data.data, sizeof(hint));
+#ifdef DEBUG
+        syslog(LOG_INFO, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
+#endif
+        break;
+    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);
+#endif
+        break;
+    default:
+        syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s",
+               db_strerror(rc));
+        goto cleanup_err;
+    }
+
+
+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);
+    }
+
+    rootinfo_data.data = &hint;
+    rootinfo_data.size = sizeof(hint);
 
+    switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
+    case 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;
+    case 0:
+        break;
+    default:
+        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: txn_commit failed (%d)", rc);
-    goto cleanup_err;
-  }
+    /* 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;
+    }
 
-  if (debug)
-    syslog(LOG_ERR, "cnid_add: returned cnid for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
-  return hint;
+#ifdef DEBUG
+    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);
+    txn_abort(tid);
 
 cleanup_err:
-  return 0;
+    return 0;
 }
 #endif /* CNID_DB */
+