]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cnid_add.c
apply parts of the netbsd build patch by Patrick Welche <prlw1@newn.cam.ac.uk>, mostl...
[netatalk.git] / libatalk / cnid / cnid_add.c
1 /*
2  * $Id: cnid_add.c,v 1.35 2003-06-06 21:22:43 srittau 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 <atalk/logger.h>
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif /* HAVE_SYS_TIME_H */
32
33 #include <db.h>
34 #include <netatalk/endian.h>
35
36 #include <atalk/adouble.h>
37 #include <atalk/cnid.h>
38 #include <atalk/util.h>
39
40 #define use_make_cnid_data
41 #include "cnid_private.h"
42
43 #ifdef CNID_DB_CDB
44     #define tid    NULL
45 #endif /* CNID_DB_CDB */
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, DBT *key, DBT *data) {
50     DBT altkey, altdata;
51 #ifndef CNID_DB_CDB
52     DB_TXN *tid;
53 #endif /* CNID_DB_CDB */
54     int rc;
55
56     memset(&altkey, 0, sizeof(altkey));
57     memset(&altdata, 0, sizeof(altdata));
58
59 #ifndef CNID_DB_CDB
60 retry:
61     if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
62         return rc;
63     }
64 #endif /* CNID_DB_CDB */
65
66     /* main database */
67     if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
68 #ifndef CNID_DB_CDB
69         if (rc == DB_LOCK_DEADLOCK) {
70             if ((ret = txn_abort(tid)) != 0) {
71                 return ret;
72             }
73             goto retry;
74         }
75 #endif /* CNID_DB_CDB */
76         goto abort;
77     }
78
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 #ifndef CNID_DB_CDB
86         if (rc == DB_LOCK_DEADLOCK) {
87             if ((ret = txn_abort(tid)) != 0) {
88                 return ret;
89             }
90             goto retry;
91         }
92 #endif /* CNID_DB_CDB */
93         goto abort;
94     }
95
96     /* did/name database */
97     altkey.data = (char *) data->data + CNID_DEVINO_LEN;
98     altkey.size = data->size - CNID_DEVINO_LEN;
99     if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) {
100 #ifndef CNID_DB_CDB
101         if (rc == DB_LOCK_DEADLOCK) {
102             if ((ret = txn_abort(tid)) != 0) {
103                 return ret;
104             }
105             goto retry;
106         }
107 #endif /* CNID_DB_CDB */
108         goto abort;
109     }
110
111 #ifndef CNID_DB_CDB
112     if ((rc = txn_commit(tid, 0)) != 0) {
113         LOG(log_error, logtype_default, "add_cnid: Failed to commit transaction: %s", db_strerror(rc));
114         return rc;
115     }
116 #endif /* CNID_DB_CDB */
117
118     return 0;
119
120 abort:
121 #ifndef CNID_DB_CDB
122     if ((ret = txn_abort(tid)) != 0) {
123         return ret;
124     }
125 #endif    
126     return rc;
127 }
128
129 /* ---------------------- */
130 #ifndef CNID_DB_CDB
131 static cnid_t get_cnid(CNID_private *db)
132 {
133     DBT rootinfo_key, rootinfo_data;
134     DB_TXN *tid;
135     int rc;
136     int flag;
137     cnid_t hint,id;
138
139     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
140     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
141     rootinfo_key.data = ROOTINFO_KEY;
142     rootinfo_key.size = ROOTINFO_KEYLEN;
143
144 retry:
145     if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
146         LOG(log_error, logtype_default, "cnid_add: Failed to begin transaction: %s", db_strerror(rc));
147         errno = CNID_ERR_DB;
148         return CNID_INVALID;
149     }
150     switch (rc = db->db_didname->get(db->db_didname, tid, &rootinfo_key,
151                                      &rootinfo_data, DB_RMW)) {
152     case DB_LOCK_DEADLOCK:
153         if ((rc = txn_abort(tid)) != 0) {
154             LOG(log_error, logtype_default, "cnid_add: txn_abort: %s", db_strerror(rc));
155             errno = CNID_ERR_DB;
156             return CNID_INVALID;
157         }
158         goto retry;
159     case 0:
160         memcpy(&hint, rootinfo_data.data, sizeof(hint));
161         id = ntohl(hint);
162         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
163          * needs to be recycled before proceding. */
164         if (++id == CNID_INVALID) {
165             txn_abort(tid);
166             LOG(log_error, logtype_default, "cnid_add: FATAL: Cannot add CNID for %s.  CNID database has reached its limit.", name);
167             errno = CNID_ERR_MAX;
168             return CNID_INVALID;
169         }
170         hint = htonl(id);
171 #ifdef DEBUG
172         LOG(log_info, logtype_default, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
173 #endif
174         break;
175     case DB_NOTFOUND:
176         hint = htonl(CNID_START);
177 #ifdef DEBUG
178         LOG(log_info, logtype_default, "cnid_add: Using CNID_START for did %u, name %s", ntohl(did), name);
179 #endif
180         break;
181     default:
182         LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
183         goto cleanup_abort;
184     }
185
186     rootinfo_data.data = &hint;
187     rootinfo_data.size = sizeof(hint);
188
189     switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
190     case DB_LOCK_DEADLOCK:
191         if ((rc = txn_abort(tid)) != 0) {
192             LOG(log_error, logtype_default, "cnid_add: txn_abort: %s", db_strerror(rc));
193             errno = CNID_ERR_DB;
194             return CNID_INVALID;
195         }
196         goto retry;
197     case 0:
198         /* The transaction finished, commit it. */
199         if ((rc = txn_commit(tid, 0)) != 0) {
200             LOG(log_error, logtype_default, "cnid_add: Unable to commit transaction: %s", db_strerror(rc));
201             errno = CNID_ERR_DB;
202             return CNID_INVALID;
203         }
204         break;
205     default:
206         LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
207         goto cleanup_abort;
208     }
209     return hint;
210     
211 cleanup_abort:
212     txn_abort(tid);
213     errno = CNID_ERR_DB;
214     return CNID_INVALID;
215 }
216 #else
217 static cnid_t get_cnid(CNID_private *db)
218 {
219     DBT rootinfo_key, rootinfo_data;
220     DBC  *cursor;
221     int rc;
222     int flag;
223     cnid_t hint,id;
224     
225     if ((rc = db->db_didname->cursor(db->db_didname, NULL, &cursor, DB_WRITECURSOR) ) != 0) {
226         LOG(log_error, logtype_default, "get_cnid: Unable to get a cursor: %s", db_strerror(rc));
227         return CNID_INVALID;
228     }
229
230     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
231     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
232     rootinfo_key.data = ROOTINFO_KEY;
233     rootinfo_key.size = ROOTINFO_KEYLEN;
234
235     switch (rc = cursor->c_get(cursor, &rootinfo_key, &rootinfo_data, DB_SET)) {
236     case 0:
237         memcpy(&hint, rootinfo_data.data, sizeof(hint));
238         id = ntohl(hint);
239         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
240          * needs to be recycled before proceding. */
241         if (++id == CNID_INVALID) {
242             LOG(log_error, logtype_default, "cnid_add: FATAL: CNID database has reached its limit.");
243             errno = CNID_ERR_MAX;
244             goto cleanup;
245         }
246         hint = htonl(id);
247         flag = DB_CURRENT;
248         break;
249     case DB_NOTFOUND:
250         hint = htonl(CNID_START);
251         flag = DB_KEYFIRST;
252         break;
253     default:
254         LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
255         errno = CNID_ERR_DB; 
256         goto cleanup;
257     }
258     
259     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
260     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
261     rootinfo_data.data = &hint;
262     rootinfo_data.size = sizeof(hint);
263     rootinfo_key.data = ROOTINFO_KEY;
264     rootinfo_key.size = ROOTINFO_KEYLEN;
265
266     switch (rc = cursor->c_put(cursor, &rootinfo_key, &rootinfo_data, flag)) {
267     case 0:
268         break;
269     default:
270         LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
271         errno = CNID_ERR_DB; 
272         goto cleanup;
273     }
274     if ((rc = cursor->c_close(cursor)) != 0) {
275         LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc));
276         errno = CNID_ERR_DB; 
277         return CNID_INVALID;
278     }
279     return hint;
280 cleanup:
281     if ((rc = cursor->c_close(cursor)) != 0) {
282         LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc));
283         return CNID_INVALID;
284     }
285     return CNID_INVALID;
286 }
287 #endif /* CNID_DB_CDB */
288
289 /* ------------------------ */
290 cnid_t cnid_add(void *CNID, const struct stat *st,
291                 const cnid_t did, const char *name, const int len,
292                 cnid_t hint)
293 {
294     CNID_private *db;
295     DBT key, data;
296     cnid_t id;
297     int rc;
298
299     if (!(db = CNID) || !st || !name) {
300         errno = CNID_ERR_PARAM;
301         return CNID_INVALID;
302     }
303
304     /* Do a lookup. */
305     id = cnid_lookup(db, st, did, name, len);
306     /* ... Return id if it is valid, or if Rootinfo is read-only. */
307     if (id || (db->flags & CNIDFLAG_DB_RO)) {
308 #ifdef DEBUG
309         LOG(log_info, logtype_default, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
310 #endif
311         return id;
312     }
313
314     /* Initialize our DBT data structures. */
315     memset(&key, 0, sizeof(key));
316     memset(&data, 0, sizeof(data));
317
318     /* Just tickle hint, and the key will change (gotta love pointers). */
319     key.data = &hint;
320     key.size = sizeof(hint);
321
322     if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
323         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
324         errno = CNID_ERR_PATH;
325         return CNID_INVALID;
326     }
327
328     data.size = CNID_HEADER_LEN + len + 1;
329
330     /* Start off with the hint.  It should be in network byte order.
331      * We need to make sure that somebody doesn't add in restricted
332      * cnid's to the database. */
333     if (ntohl(hint) >= CNID_START) {
334         /* If the key doesn't exist, add it in.  Don't fiddle with nextID. */
335         rc = add_cnid(db, &key, &data);
336         switch (rc) {
337         case DB_KEYEXIST: /* Need to use RootInfo after all. */
338             break;
339         default:
340             LOG(log_error, logtype_default, "cnid_add: Unable to add CNID %u: %s", ntohl(hint), db_strerror(rc));
341             errno = CNID_ERR_DB;
342             return CNID_INVALID;
343         case 0:
344 #ifdef DEBUG
345             LOG(log_info, logtype_default, "cnid_add: Used hint for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
346 #endif
347             return hint;
348         }
349     }
350     hint = get_cnid(db);
351     if (hint == 0) {
352         errno = CNID_ERR_DB;
353         return CNID_INVALID;
354     }
355     
356     /* Now we need to add the CNID data to the databases. */
357     rc = add_cnid(db, &key, &data);
358     if (rc) {
359         LOG(log_error, logtype_default, "cnid_add: Failed to add CNID for %s to database using hint %u: %s", name, ntohl(hint), db_strerror(rc));
360         errno = CNID_ERR_DB;
361         return CNID_INVALID;
362     }
363
364 #ifdef DEBUG
365     LOG(log_info, logtype_default, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
366 #endif
367
368     return hint;
369 }
370 #endif /* CNID_DB */
371