2 * $Id: cnid_add.c,v 1.16 2001-12-03 15:46:30 jmarcus Exp $
4 * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
5 * All Rights Reserved. See COPYRIGHT.
7 * cnid_add (db, dev, ino, did, name, hint):
8 * add a name to the CNID database. we use both dev/ino and did/name
9 * to keep track of things.
14 #endif /* HAVE_CONFIG_H */
18 #include <sys/param.h>
23 #endif /* HAVE_UNISTD_H */
26 #endif /* HAVE_FCNTL_H */
31 #endif /* HAVE_TIME_H */
34 #endif /* HAVE_CMA_H */
37 #include <netatalk/endian.h>
39 #include <atalk/adouble.h>
40 #include <atalk/cnid.h>
41 #include <atalk/util.h>
43 #include "cnid_private.h"
45 #define MAX_ABORTS 255
47 /* add an entry to the CNID databases. we do this as a transaction
48 * to prevent messiness. */
49 static int add_cnid(CNID_private *db, DB_TXN *ptid, DBT *key, DBT *data) {
52 /* We create rc here because using errno is bad. Why? Well, if you
53 * use errno once, then call another function which resets it, you're
55 int rc, ret, aborts = 0;
57 memset(&altkey, 0, sizeof(altkey));
58 memset(&altdata, 0, sizeof(altdata));
62 if ((rc = txn_abort(tid)) != 0) {
65 if (++aborts > MAX_ABORTS) {
66 return DB_LOCK_DEADLOCK;
70 if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) {
75 if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
76 if (rc == DB_LOCK_DEADLOCK) {
84 /* dev/ino database */
85 altkey.data = data->data;
86 altkey.size = CNID_DEVINO_LEN;
87 altdata.data = key->data;
88 altdata.size = key->size;
89 if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) {
90 if (rc == DB_LOCK_DEADLOCK) {
98 /* did/name database */
99 altkey.data = (char *) data->data + CNID_DEVINO_LEN;
100 altkey.size = data->size - CNID_DEVINO_LEN;
101 if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) {
102 if (rc == DB_LOCK_DEADLOCK) {
109 if ((rc = txn_commit(tid, 0)) != 0) {
110 syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s",
117 if ((ret = txn_abort(tid)) != 0) {
124 cnid_t cnid_add(void *CNID, const struct stat *st,
125 const cnid_t did, const char *name, const int len,
129 DBT key, data, rootinfo_key, rootinfo_data;
135 if (!(db = CNID) || !st || !name) {
140 id = cnid_lookup(db, st, did, name, len);
141 /* ... Return id if it is valid, or if Rootinfo is read-only. */
142 if (id || (db->flags & CNIDFLAG_DB_RO)) {
144 syslog(LOG_INFO, "cnid_add: Looked up did %u, name %s as %u",
145 ntohl(did), name, ntohl(id));
150 /* Initialize our DBT data structures. */
151 memset(&key, 0, sizeof(key));
152 memset(&data, 0, sizeof(data));
154 /* Just tickle hint, and the key will change (gotta love pointers). */
156 key.size = sizeof(hint);
158 if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
159 syslog(LOG_ERR, "cnid_add: Path name is too long");
163 data.size = CNID_HEADER_LEN + len + 1;
165 /* Start off with the hint. It should be in network byte order.
166 * We need to make sure that somebody doesn't add in restricted
167 * cnid's to the database. */
168 if (ntohl(hint) >= CNID_START) {
169 /* If the key doesn't exist, add it in. Don't fiddle with nextID. */
170 rc = add_cnid(db, NULL, &key, &data);
172 case DB_KEYEXIST: /* Need to use RootInfo after all. */
175 syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
176 ntohl(hint), db_strerror(rc));
180 syslog(LOG_INFO, "cnid_add: Used hint for did %u, name %s as %u",
181 ntohl(did), name, ntohl(hint));
187 /* We need to create a random sleep interval to prevent deadlocks. */
188 (void)srand(getpid() ^ time(NULL));
191 memset(&rootinfo_key, 0, sizeof(rootinfo_key));
192 memset(&rootinfo_data, 0, sizeof(rootinfo_data));
193 rootinfo_key.data = ROOTINFO_KEY;
194 rootinfo_key.size = ROOTINFO_KEYLEN;
198 switch (rc = db->db_didname->get(db->db_didname, NULL, &rootinfo_key,
199 &rootinfo_data, 0)) {
200 case DB_LOCK_DEADLOCK:
201 if ((rc = txn_abort(tid)) != 0) {
202 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
207 memcpy(&hint, rootinfo_data.data, sizeof(hint));
209 syslog(LOG_INFO, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
213 hint = htonl(CNID_START);
215 syslog(LOG_INFO, "cnid_add: Using CNID_START for did %u, name %s",
220 syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s",
227 t.tv_usec = rand() % 1000000;
229 syslog(LOG_INFO, "cnid_add: Hitting MAX_ABORTS, sleeping");
231 (void)select(0, NULL, NULL, NULL, &t);
232 if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
233 syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s",
238 /* Search for a new id. We keep the first id around to check for
239 * wrap-around. NOTE: I do it this way so that we can go back and
241 save = id = ntohl(hint);
242 while ((rc = add_cnid(db, tid, &key, &data)) != 0) {
243 /* Don't use any special CNIDs. */
244 if (++id < CNID_START) {
247 if (rc == DB_LOCK_DEADLOCK) {
248 if ((rc = txn_abort(tid)) != 0) {
249 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
255 if ((rc != DB_KEYEXIST) || (save == id)) {
256 syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
257 ntohl(hint), db_strerror(rc));
263 rootinfo_data.data = &hint;
264 rootinfo_data.size = sizeof(hint);
266 switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
267 case DB_LOCK_DEADLOCK:
268 if ((rc = txn_abort(tid)) != 0) {
269 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
276 syslog(LOG_ERR, "cnid_add: Unable to update rootinfo: %s",
282 /* The transaction finished, commit it. */
283 if ((rc = txn_commit(tid, 0)) != 0) {
284 syslog(LOG_ERR, "cnid_add: Unable to commit transaction: %s",
290 syslog(LOG_INFO, "cnid_add: Returned CNID for did %u, name %s as %u",
291 ntohl(did), name, ntohl(hint));