2 * $Id: cnid_add.c,v 1.15 2001-11-27 23:38:17 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 */
32 #include <netatalk/endian.h>
34 #include <atalk/adouble.h>
35 #include <atalk/cnid.h>
36 #include <atalk/util.h>
38 #include "cnid_private.h"
40 #define MAX_ABORTS 255
42 /* add an entry to the CNID databases. we do this as a transaction
43 * to prevent messiness. */
44 static int add_cnid(CNID_private *db, DB_TXN *ptid, DBT *key, DBT *data) {
47 /* We create rc here because using errno is bad. Why? Well, if you
48 * use errno once, then call another function which resets it, you're
50 int rc, ret, aborts = 0;
52 memset(&altkey, 0, sizeof(altkey));
53 memset(&altdata, 0, sizeof(altdata));
57 if ((rc = txn_abort(tid)) != 0) {
60 if (++aborts > MAX_ABORTS) {
61 return DB_LOCK_DEADLOCK;
65 if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) {
70 if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
71 if (rc == DB_LOCK_DEADLOCK) {
79 /* dev/ino database */
80 altkey.data = data->data;
81 altkey.size = CNID_DEVINO_LEN;
82 altdata.data = key->data;
83 altdata.size = key->size;
84 if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) {
85 if (rc == DB_LOCK_DEADLOCK) {
93 /* did/name database */
94 altkey.data = (char *) data->data + CNID_DEVINO_LEN;
95 altkey.size = data->size - CNID_DEVINO_LEN;
96 if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) {
97 if (rc == DB_LOCK_DEADLOCK) {
104 if ((rc = txn_commit(tid, 0)) != 0) {
105 syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s",
112 if ((ret = txn_abort(tid)) != 0) {
119 cnid_t cnid_add(void *CNID, const struct stat *st,
120 const cnid_t did, const char *name, const int len,
124 DBT key, data, rootinfo_key, rootinfo_data;
130 if (!(db = CNID) || !st || !name) {
135 id = cnid_lookup(db, st, did, name, len);
136 /* ... Return id if it is valid, or if Rootinfo is read-only. */
137 if (id || (db->flags & CNIDFLAG_DB_RO)) {
139 syslog(LOG_INFO, "cnid_add: Looked up did %u, name %s as %u",
140 ntohl(did), name, ntohl(id));
145 /* Initialize our DBT data structures. */
146 memset(&key, 0, sizeof(key));
147 memset(&data, 0, sizeof(data));
149 /* Just tickle hint, and the key will change (gotta love pointers). */
151 key.size = sizeof(hint);
153 if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
154 syslog(LOG_ERR, "cnid_add: Path name is too long");
158 data.size = CNID_HEADER_LEN + len + 1;
160 /* Start off with the hint. It should be in network byte order.
161 * We need to make sure that somebody doesn't add in restricted
162 * cnid's to the database. */
163 if (ntohl(hint) >= CNID_START) {
164 /* If the key doesn't exist, add it in. Don't fiddle with nextID. */
165 rc = add_cnid(db, NULL, &key, &data);
167 case DB_KEYEXIST: /* Need to use RootInfo after all. */
170 syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
171 ntohl(hint), db_strerror(rc));
175 syslog(LOG_INFO, "cnid_add: Used hint for did %u, name %s as %u",
176 ntohl(did), name, ntohl(hint));
182 /* We need to create a random sleep interval to prevent deadlocks. */
183 (void)srand(getpid() ^ time(NULL));
186 memset(&rootinfo_key, 0, sizeof(rootinfo_key));
187 memset(&rootinfo_data, 0, sizeof(rootinfo_data));
188 rootinfo_key.data = ROOTINFO_KEY;
189 rootinfo_key.size = ROOTINFO_KEYLEN;
193 switch (rc = db->db_didname->get(db->db_didname, NULL, &rootinfo_key,
194 &rootinfo_data, 0)) {
195 case DB_LOCK_DEADLOCK:
196 if ((rc = txn_abort(tid)) != 0) {
197 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
202 memcpy(&hint, rootinfo_data.data, sizeof(hint));
204 syslog(LOG_INFO, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
208 hint = htonl(CNID_START);
210 syslog(LOG_INFO, "cnid_add: Using CNID_START for did %u, name %s",
215 syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s",
222 t.tv_usec = rand() % 1000000;
224 syslog(LOG_INFO, "cnid_add: Hitting MAX_ABORTS, sleeping");
226 (void)select(0, NULL, NULL, NULL, &t);
227 if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
228 syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s",
233 /* Search for a new id. We keep the first id around to check for
234 * wrap-around. NOTE: I do it this way so that we can go back and
236 save = id = ntohl(hint);
237 while ((rc = add_cnid(db, tid, &key, &data)) != 0) {
238 /* Don't use any special CNIDs. */
239 if (++id < CNID_START) {
242 if (rc == DB_LOCK_DEADLOCK) {
243 if ((rc = txn_abort(tid)) != 0) {
244 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
250 if ((rc != DB_KEYEXIST) || (save == id)) {
251 syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
252 ntohl(hint), db_strerror(rc));
258 rootinfo_data.data = &hint;
259 rootinfo_data.size = sizeof(hint);
261 switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
262 case DB_LOCK_DEADLOCK:
263 if ((rc = txn_abort(tid)) != 0) {
264 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
271 syslog(LOG_ERR, "cnid_add: Unable to update rootinfo: %s",
277 /* The transaction finished, commit it. */
278 if ((rc = txn_commit(tid, 0)) != 0) {
279 syslog(LOG_ERR, "cnid_add: Unable to commit transaction: %s",
285 syslog(LOG_INFO, "cnid_add: Returned CNID for did %u, name %s as %u",
286 ntohl(did), name, ntohl(hint));