]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/db3/cnid_db3_add.c
adf30f392f473ebedbd8dfa7b83375ad3b2ded18
[netatalk.git] / libatalk / cnid / db3 / cnid_db3_add.c
1 /*
2  * $Id: cnid_db3_add.c,v 1.1.4.2.2.1 2005-01-30 20:56:22 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     int flag;
122     cnid_t hint,id;
123
124     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
125     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
126     rootinfo_key.data = ROOTINFO_KEY;
127     rootinfo_key.size = ROOTINFO_KEYLEN;
128
129 retry:
130     if ((rc = db3_txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
131         LOG(log_error, logtype_default, "cnid_add: Failed to begin transaction: %s", db_strerror(rc));
132         errno = CNID_ERR_DB;
133         return CNID_INVALID;
134     }
135     switch (rc = db->db_didname->get(db->db_didname, tid, &rootinfo_key,
136                                      &rootinfo_data, DB_RMW)) {
137     case DB_LOCK_DEADLOCK:
138         if ((rc = db3_txn_abort(tid)) != 0) {
139             LOG(log_error, logtype_default, "cnid_add: txn_abort: %s", db_strerror(rc));
140             errno = CNID_ERR_DB;
141             return CNID_INVALID;
142         }
143         goto retry;
144     case 0:
145         memcpy(&hint, rootinfo_data.data, sizeof(hint));
146         id = ntohl(hint);
147         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
148          * needs to be recycled before proceding. */
149         if (++id == CNID_INVALID) {
150             db3_txn_abort(tid);
151             LOG(log_error, logtype_default, "cnid_add: FATAL: Cannot add CNID.  CNID database has reached its limit.");
152             errno = CNID_ERR_MAX;
153             return CNID_INVALID;
154         }
155         hint = htonl(id);
156 /* #ifdef DEBUG */
157 #if 0
158         LOG(log_info, logtype_default, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
159 #endif
160         break;
161     case DB_NOTFOUND:
162         hint = htonl(CNID_START);
163 /* #ifdef DEBUG */
164 #if 0
165         LOG(log_info, logtype_default, "cnid_add: Using CNID_START for did %u, name %s", ntohl(did), name);
166 #endif
167         break;
168     default:
169         LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
170         goto cleanup_abort;
171     }
172
173     rootinfo_data.data = &hint;
174     rootinfo_data.size = sizeof(hint);
175
176     switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
177     case DB_LOCK_DEADLOCK:
178         if ((rc = db3_txn_abort(tid)) != 0) {
179             LOG(log_error, logtype_default, "cnid_add: txn_abort: %s", db_strerror(rc));
180             errno = CNID_ERR_DB;
181             return CNID_INVALID;
182         }
183         goto retry;
184     case 0:
185         /* The transaction finished, commit it. */
186         if ((rc = db3_txn_commit(tid, 0)) != 0) {
187             LOG(log_error, logtype_default, "cnid_add: Unable to commit transaction: %s", db_strerror(rc));
188             errno = CNID_ERR_DB;
189             return CNID_INVALID;
190         }
191         break;
192     default:
193         LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
194         goto cleanup_abort;
195     }
196     return hint;
197     
198 cleanup_abort:
199     db3_txn_abort(tid);
200     errno = CNID_ERR_DB;
201     return CNID_INVALID;
202 }
203
204 /* ------------------------ */
205 cnid_t cnid_db3_add(struct _cnid_db *cdb, const struct stat *st,
206                 const cnid_t did, char *name, const int len,
207                 cnid_t hint)
208 {
209     CNID_private *db;
210     DBT key, data;
211     cnid_t id;
212     int rc;
213
214     if (!cdb || !(db = cdb->_private) || !st || !name) {
215         errno = CNID_ERR_PARAM;
216         return CNID_INVALID;
217     }
218
219     /* Do a lookup. */
220     id = cnid_db3_lookup(cdb, st, did, name, len);
221     /* ... Return id if it is valid, or if Rootinfo is read-only. */
222     if (id || (db->flags & CNIDFLAG_DB_RO)) {
223 #ifdef DEBUG
224         LOG(log_info, logtype_default, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
225 #endif
226         return id;
227     }
228
229     /* Initialize our DBT data structures. */
230     memset(&key, 0, sizeof(key));
231     memset(&data, 0, sizeof(data));
232
233     /* Just tickle hint, and the key will change (gotta love pointers). */
234     key.data = &hint;
235     key.size = sizeof(hint);
236
237     if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
238         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
239         errno = CNID_ERR_PATH;
240         return CNID_INVALID;
241     }
242
243     data.size = CNID_HEADER_LEN + len + 1;
244
245     /* Start off with the hint.  It should be in network byte order.
246      * We need to make sure that somebody doesn't add in restricted
247      * cnid's to the database. */
248     if (ntohl(hint) >= CNID_START) {
249         /* If the key doesn't exist, add it in.  Don't fiddle with nextID. */
250         rc = add_cnid(db, &key, &data);
251         switch (rc) {
252         case DB_KEYEXIST: /* Need to use RootInfo after all. */
253             break;
254         default:
255             LOG(log_error, logtype_default, "cnid_add: Unable to add CNID %u: %s", ntohl(hint), db_strerror(rc));
256             errno = CNID_ERR_DB;
257             return CNID_INVALID;
258         case 0:
259 #ifdef DEBUG
260             LOG(log_info, logtype_default, "cnid_add: Used hint for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
261 #endif
262             return hint;
263         }
264     }
265     hint = get_cnid(db);
266     if (hint == 0) {
267         errno = CNID_ERR_DB;
268         return CNID_INVALID;
269     }
270     
271     /* Now we need to add the CNID data to the databases. */
272     rc = add_cnid(db, &key, &data);
273     if (rc) {
274         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));
275         errno = CNID_ERR_DB;
276         return CNID_INVALID;
277     }
278
279 #ifdef DEBUG
280     LOG(log_info, logtype_default, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
281 #endif
282
283     return hint;
284 }
285
286 #endif /* CNID_BACKEND_DB3 */