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