]> arthur.barton.de Git - netatalk.git/blob - 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
1 /*
2  * $Id: cnid_add.c,v 1.16 2001-12-03 15:46:30 jmarcus Exp $
3  *
4  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
5  * All Rights Reserved. See COPYRIGHT.
6  *
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.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif /* HAVE_CONFIG_H */
15
16 #ifdef CNID_DB
17 #include <stdio.h>
18 #include <sys/param.h>
19 #include <sys/stat.h>
20 #include <string.h>
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif /* HAVE_UNISTD_H */
24 #ifdef HAVE_FCNTL_H
25 #include <fcntl.h>
26 #endif /* HAVE_FCNTL_H */
27 #include <errno.h>
28 #include <syslog.h>
29 #ifdef HAVE_TIME_H
30 #include <time.h>
31 #endif /* HAVE_TIME_H */
32 #ifdef HAVE_CMA_H
33 #include <cma.h>
34 #endif /* HAVE_CMA_H */
35
36 #include <db.h>
37 #include <netatalk/endian.h>
38
39 #include <atalk/adouble.h>
40 #include <atalk/cnid.h>
41 #include <atalk/util.h>
42
43 #include "cnid_private.h"
44
45 #define MAX_ABORTS 255
46
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) {
50     DBT altkey, altdata;
51     DB_TXN *tid;
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
54      * screwed. */
55     int rc, ret, aborts = 0;
56
57     memset(&altkey, 0, sizeof(altkey));
58     memset(&altdata, 0, sizeof(altdata));
59
60     if (0) {
61 retry:
62         if ((rc = txn_abort(tid)) != 0) {
63             return rc;
64         }
65         if (++aborts > MAX_ABORTS) {
66             return DB_LOCK_DEADLOCK;
67         }
68     }
69
70     if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) {
71         return rc;
72     }
73
74     /* main database */
75     if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
76         if (rc == DB_LOCK_DEADLOCK) {
77             goto retry;
78         }
79
80         goto abort;
81     }
82
83
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) {
91             goto retry;
92         }
93
94         goto abort;
95     }
96
97
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) {
103             goto retry;
104         }
105
106         goto abort;
107     }
108
109     if ((rc = txn_commit(tid, 0)) != 0) {
110         syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s",
111                db_strerror(rc));
112         return rc;
113     }
114     return 0;
115
116 abort:
117     if ((ret = txn_abort(tid)) != 0) {
118         return ret;
119     }
120     return rc;
121
122 }
123
124 cnid_t cnid_add(void *CNID, const struct stat *st,
125                 const cnid_t did, const char *name, const int len,
126                 cnid_t hint)
127 {
128     CNID_private *db;
129     DBT key, data, rootinfo_key, rootinfo_data;
130     DB_TXN *tid;
131     struct timeval t;
132     cnid_t id, save;
133     int rc;
134
135     if (!(db = CNID) || !st || !name) {
136         return 0;
137     }
138
139     /* Do a lookup. */
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)) {
143 #ifdef DEBUG
144         syslog(LOG_INFO, "cnid_add: Looked up did %u, name %s as %u",
145                ntohl(did), name, ntohl(id));
146 #endif
147         return id;
148     }
149
150     /* Initialize our DBT data structures. */
151     memset(&key, 0, sizeof(key));
152     memset(&data, 0, sizeof(data));
153
154     /* Just tickle hint, and the key will change (gotta love pointers). */
155     key.data = &hint;
156     key.size = sizeof(hint);
157
158     if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
159         syslog(LOG_ERR, "cnid_add: Path name is too long");
160         goto cleanup_err;
161     }
162
163     data.size = CNID_HEADER_LEN + len + 1;
164
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);
171         switch (rc) {
172         case DB_KEYEXIST: /* Need to use RootInfo after all. */
173             break;
174         default:
175             syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
176                    ntohl(hint), db_strerror(rc));
177             goto cleanup_err;
178         case 0:
179 #ifdef DEBUG
180             syslog(LOG_INFO, "cnid_add: Used hint for did %u, name %s as %u",
181                    ntohl(did), name, ntohl(hint));
182 #endif
183             return hint;
184         }
185     }
186
187     /* We need to create a random sleep interval to prevent deadlocks. */
188     (void)srand(getpid() ^ time(NULL));
189     t.tv_sec = 0;
190
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;
195
196     /* Get the key. */
197 retry_get:
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));
203             goto cleanup_err;
204         }
205         goto retry_get;
206     case 0:
207         memcpy(&hint, rootinfo_data.data, sizeof(hint));
208 #ifdef DEBUG
209         syslog(LOG_INFO, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
210 #endif
211         break;
212     case DB_NOTFOUND:
213         hint = htonl(CNID_START);
214 #ifdef DEBUG
215         syslog(LOG_INFO, "cnid_add: Using CNID_START for did %u, name %s",
216                ntohl(did), name);
217 #endif
218         break;
219     default:
220         syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s",
221                db_strerror(rc));
222         goto cleanup_err;
223     }
224
225
226 retry:
227     t.tv_usec = rand() % 1000000;
228 #ifdef DEBUG
229     syslog(LOG_INFO, "cnid_add: Hitting MAX_ABORTS, sleeping");
230 #endif
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",
234                db_strerror(rc));
235         goto cleanup_err;
236     }
237
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
240      * fill in holes. */
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) {
245             id = CNID_START;
246         }
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));
250                 goto cleanup_err;
251             }
252             goto retry;
253         }
254
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));
258             goto cleanup_abort;
259         }
260         hint = htonl(id);
261     }
262
263     rootinfo_data.data = &hint;
264     rootinfo_data.size = sizeof(hint);
265
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));
270             goto cleanup_err;
271         }
272         goto retry;
273     case 0:
274         break;
275     default:
276         syslog(LOG_ERR, "cnid_add: Unable to update rootinfo: %s",
277                db_strerror(rc));
278         goto cleanup_abort;
279     }
280
281 cleanup_commit:
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",
285                db_strerror(rc));
286         goto cleanup_err;
287     }
288
289 #ifdef DEBUG
290     syslog(LOG_INFO, "cnid_add: Returned CNID for did %u, name %s as %u",
291            ntohl(did), name, ntohl(hint));
292 #endif
293     return hint;
294
295 cleanup_abort:
296     txn_abort(tid);
297
298 cleanup_err:
299     return 0;
300 }
301 #endif /* CNID_DB */
302