]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/db3/cnid_db3_add.c
f699a74e66882448cb7873464b197db26df3f4a8
[netatalk.git] / libatalk / cnid / db3 / cnid_db3_add.c
1 /*
2  * $Id: cnid_db3_add.c,v 1.3 2005-05-03 14:55:13 didg 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_BACKEND_DB3
17
18 #include <stdio.h>
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <string.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif /* HAVE_UNISTD_H */
25 #ifdef HAVE_FCNTL_H
26 #include <fcntl.h>
27 #endif /* HAVE_FCNTL_H */
28 #include <errno.h>
29 #include <atalk/logger.h>
30 #ifdef HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif /* HAVE_SYS_TIME_H */
33
34 #ifdef HAVE_DB4_DB_H
35 #include <db4/db.h>
36 #else
37 #include <db.h>
38 #endif
39 #include <netatalk/endian.h>
40
41 #include <atalk/adouble.h>
42 #include "cnid_db3.h"
43 #include <atalk/util.h>
44
45 #include "cnid_db3_private.h"
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     DB_TXN *tid;
52     int rc, ret;
53
54     memset(&altkey, 0, sizeof(altkey));
55     memset(&altdata, 0, sizeof(altdata));
56
57 retry:
58     if ((rc = db3_txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
59         return rc;
60     }
61
62     /* main database */
63     if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
64         if (rc == DB_LOCK_DEADLOCK) {
65             if ((ret = db3_txn_abort(tid)) != 0) {
66                 return ret;
67             }
68             goto retry;
69         }
70         goto abort;
71     }
72
73     /* dev/ino database */
74     altkey.data = data->data;
75     altkey.size = CNID_DEVINO_LEN;
76     altdata.data = key->data;
77     altdata.size = key->size;
78     if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) {
79         if (rc == DB_LOCK_DEADLOCK) {
80             if ((ret = db3_txn_abort(tid)) != 0) {
81                 return ret;
82             }
83             goto retry;
84         }
85         goto abort;
86     }
87
88     /* did/name database */
89     altkey.data = (char *) data->data + CNID_DEVINO_LEN;
90     altkey.size = data->size - CNID_DEVINO_LEN;
91     if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) {
92         if (rc == DB_LOCK_DEADLOCK) {
93             if ((ret = db3_txn_abort(tid)) != 0) {
94                 return ret;
95             }
96             goto retry;
97         }
98         goto abort;
99     }
100
101     if ((rc = db3_txn_commit(tid, 0)) != 0) {
102         LOG(log_error, logtype_default, "add_cnid: Failed to commit transaction: %s", db_strerror(rc));
103         return rc;
104     }
105
106     return 0;
107
108 abort:
109     if ((ret = db3_txn_abort(tid)) != 0) {
110         return ret;
111     }
112     return rc;
113 }
114
115 /* ---------------------- */
116 static cnid_t get_cnid(CNID_private *db)
117 {
118     DBT rootinfo_key, rootinfo_data;
119     DB_TXN *tid;
120     int rc;
121     cnid_t hint,id;
122
123     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
124     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
125     rootinfo_key.data = ROOTINFO_KEY;
126     rootinfo_key.size = ROOTINFO_KEYLEN;
127
128 retry:
129     if ((rc = db3_txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
130         LOG(log_error, logtype_default, "cnid_add: Failed to begin transaction: %s", db_strerror(rc));
131         errno = CNID_ERR_DB;
132         return CNID_INVALID;
133     }
134     switch (rc = db->db_didname->get(db->db_didname, tid, &rootinfo_key,
135                                      &rootinfo_data, DB_RMW)) {
136     case DB_LOCK_DEADLOCK:
137         if ((rc = db3_txn_abort(tid)) != 0) {
138             LOG(log_error, logtype_default, "cnid_add: txn_abort: %s", db_strerror(rc));
139             errno = CNID_ERR_DB;
140             return CNID_INVALID;
141         }
142         goto retry;
143     case 0:
144         memcpy(&hint, rootinfo_data.data, sizeof(hint));
145         id = ntohl(hint);
146         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
147          * needs to be recycled before proceding. */
148         if (++id == CNID_INVALID) {
149             db3_txn_abort(tid);
150             LOG(log_error, logtype_default, "cnid_add: FATAL: Cannot add CNID.  CNID database has reached its limit.");
151             errno = CNID_ERR_MAX;
152             return CNID_INVALID;
153         }
154         hint = htonl(id);
155 /* #ifdef DEBUG */
156 #if 0
157         LOG(log_info, logtype_default, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
158 #endif
159         break;
160     case DB_NOTFOUND:
161         hint = htonl(CNID_START);
162 /* #ifdef DEBUG */
163 #if 0
164         LOG(log_info, logtype_default, "cnid_add: Using CNID_START for did %u, name %s", ntohl(did), name);
165 #endif
166         break;
167     default:
168         LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
169         goto cleanup_abort;
170     }
171
172     rootinfo_data.data = &hint;
173     rootinfo_data.size = sizeof(hint);
174
175     switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
176     case DB_LOCK_DEADLOCK:
177         if ((rc = db3_txn_abort(tid)) != 0) {
178             LOG(log_error, logtype_default, "cnid_add: txn_abort: %s", db_strerror(rc));
179             errno = CNID_ERR_DB;
180             return CNID_INVALID;
181         }
182         goto retry;
183     case 0:
184         /* The transaction finished, commit it. */
185         if ((rc = db3_txn_commit(tid, 0)) != 0) {
186             LOG(log_error, logtype_default, "cnid_add: Unable to commit transaction: %s", db_strerror(rc));
187             errno = CNID_ERR_DB;
188             return CNID_INVALID;
189         }
190         break;
191     default:
192         LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
193         goto cleanup_abort;
194     }
195     return hint;
196     
197 cleanup_abort:
198     db3_txn_abort(tid);
199     errno = CNID_ERR_DB;
200     return CNID_INVALID;
201 }
202
203 /* ------------------------ */
204 cnid_t cnid_db3_add(struct _cnid_db *cdb, const struct stat *st,
205                 const cnid_t did, char *name, const size_t len,
206                 cnid_t hint)
207 {
208     CNID_private *db;
209     DBT key, data;
210     cnid_t id;
211     int rc;
212
213     if (!cdb || !(db = cdb->_private) || !st || !name) {
214         errno = CNID_ERR_PARAM;
215         return CNID_INVALID;
216     }
217
218     /* Do a lookup. */
219     id = cnid_db3_lookup(cdb, st, did, name, len);
220     /* ... Return id if it is valid, or if Rootinfo is read-only. */
221     if (id || (db->flags & CNIDFLAG_DB_RO)) {
222 #ifdef DEBUG
223         LOG(log_info, logtype_default, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
224 #endif
225         return id;
226     }
227
228     /* Initialize our DBT data structures. */
229     memset(&key, 0, sizeof(key));
230     memset(&data, 0, sizeof(data));
231
232     /* Just tickle hint, and the key will change (gotta love pointers). */
233     key.data = &hint;
234     key.size = sizeof(hint);
235
236     if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
237         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
238         errno = CNID_ERR_PATH;
239         return CNID_INVALID;
240     }
241
242     data.size = CNID_HEADER_LEN + len + 1;
243
244     /* Start off with the hint.  It should be in network byte order.
245      * We need to make sure that somebody doesn't add in restricted
246      * cnid's to the database. */
247     if (ntohl(hint) >= CNID_START) {
248         /* If the key doesn't exist, add it in.  Don't fiddle with nextID. */
249         rc = add_cnid(db, &key, &data);
250         switch (rc) {
251         case DB_KEYEXIST: /* Need to use RootInfo after all. */
252             break;
253         default:
254             LOG(log_error, logtype_default, "cnid_add: Unable to add CNID %u: %s", ntohl(hint), db_strerror(rc));
255             errno = CNID_ERR_DB;
256             return CNID_INVALID;
257         case 0:
258 #ifdef DEBUG
259             LOG(log_info, logtype_default, "cnid_add: Used hint for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
260 #endif
261             return hint;
262         }
263     }
264     hint = get_cnid(db);
265     if (hint == 0) {
266         errno = CNID_ERR_DB;
267         return CNID_INVALID;
268     }
269     
270     /* Now we need to add the CNID data to the databases. */
271     rc = add_cnid(db, &key, &data);
272     if (rc) {
273         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));
274         errno = CNID_ERR_DB;
275         return CNID_INVALID;
276     }
277
278 #ifdef DEBUG
279     LOG(log_info, logtype_default, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
280 #endif
281
282     return hint;
283 }
284
285 #endif /* CNID_BACKEND_DB3 */