]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cnid_add.c
Add some more debugging code.
[netatalk.git] / libatalk / cnid / cnid_add.c
1 /*
2  * $Id: cnid_add.c,v 1.14 2001-10-22 03:40:18 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
30 #include <db.h>
31 #include <netatalk/endian.h>
32
33 #include <atalk/adouble.h>
34 #include <atalk/cnid.h>
35 #include <atalk/util.h>
36
37 #include "cnid_private.h"
38
39 /* add an entry to the CNID databases. we do this as a transaction
40  * to prevent messiness. */
41 static int add_cnid(CNID_private *db, DB_TXN *ptid, DBT *key, DBT *data) {
42         DBT altkey, altdata;
43         DB_TXN *tid;
44         /* We create rc here because using errno is bad.  Why?  Well, if you 
45          * use errno once, then call another function which resets it, you're
46          * screwed. */
47         int rc;
48
49         memset(&altkey, 0, sizeof(altkey));
50         memset(&altdata, 0, sizeof(altdata));
51
52 retry:
53         if ((rc = txn_begin(db->dbenv, ptid, &tid, 0)) != 0) {
54                 return rc;
55         }
56
57
58         /* main database */
59         if ((rc = db->db_cnid->put(db->db_cnid, tid, key, data, DB_NOOVERWRITE))) {
60                 int ret;
61                 if ((ret = txn_abort(tid)) != 0) {
62                         return ret;
63                 }
64                 if (rc == DB_LOCK_DEADLOCK) {
65                         goto retry;
66                 }
67
68                 return rc;
69         }
70
71
72         /* dev/ino database */
73         altkey.data = data->data;
74         altkey.size = CNID_DEVINO_LEN;
75         altdata.data = key->data;
76         altdata.size = key->size;
77         if ((rc = db->db_devino->put(db->db_devino, tid, &altkey, &altdata, 0))) {
78                 int ret;
79                 if ((ret = txn_abort(tid)) != 0) {
80                         return ret;
81                 }
82                 if (rc == DB_LOCK_DEADLOCK) {
83                         goto retry;
84                 }
85
86                 return rc;
87         }
88
89
90         /* did/name database */
91         altkey.data = (char *) data->data + CNID_DEVINO_LEN;
92         altkey.size = data->size - CNID_DEVINO_LEN;
93         if ((rc = db->db_didname->put(db->db_didname, tid, &altkey, &altdata, 0))) {
94                 int ret;
95                 if ((ret = txn_abort(tid)) != 0) {
96                         return ret;
97                 }
98                 if (rc == DB_LOCK_DEADLOCK) {
99                         goto retry;
100                 }
101
102                 return rc;
103         }
104
105         if ((rc = txn_commit(tid, 0)) != 0) {
106                 syslog(LOG_ERR, "add_cnid: Failed to commit transaction: %s",
107                        db_strerror(rc));
108                 return rc;
109         }
110         return 0;
111 }
112
113 cnid_t cnid_add(void *CNID, const struct stat *st,
114        const cnid_t did, const char *name, const int len,
115            cnid_t hint)
116 {
117         CNID_private *db;
118         DBT key, data, rootinfo_key, rootinfo_data;
119         DB_TXN *tid;
120         cnid_t id, save;
121         int rc;
122
123         if (!(db = CNID) || !st || !name) {
124                 return 0;
125         }
126
127         /* Do a lookup. */
128         id = cnid_lookup(db, st, did, name, len);
129         /* ... Return id if it is valid, or if Rootinfo is read-only. */
130         if (id || (db->flags & CNIDFLAG_DB_RO)) {
131 #ifdef DEBUG
132                 syslog(LOG_INFO, "cnid_add: Looked up did %u, name %s as %u",
133                        ntohl(did), name, ntohl(id));
134 #endif
135                 return id;
136         }
137
138         /* Initialize our DBT data structures. */
139         memset(&key, 0, sizeof(key));
140         memset(&data, 0, sizeof(data));
141
142         /* Just tickle hint, and the key will change (gotta love pointers). */
143         key.data = &hint;
144         key.size = sizeof(hint);
145
146         if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
147                 syslog(LOG_ERR, "cnid_add: Path name is too long");
148                 goto cleanup_err;
149         }
150
151         data.size = CNID_HEADER_LEN + len + 1;
152
153         /* Start off with the hint.  It should be in network byte order.
154          * We need to make sure that somebody doesn't add in restricted
155          * cnid's to the database. */
156         if (ntohl(hint) >= CNID_START) {
157                 /* If the key doesn't exist, add it in.  Don't fiddle with nextID. */
158                 rc = add_cnid(db, NULL, &key, &data);
159                 switch (rc) {
160                         case DB_KEYEXIST: /* Need to use RootInfo after all. */
161                                 break;
162                         default:
163                                 syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
164                                        ntohl(hint), db_strerror(rc));
165                                 goto cleanup_err;
166                         case 0:
167 #ifdef DEBUG
168                                 syslog(LOG_INFO, "cnid_add: Used hint for did %u, name %s as %u",
169                                        ntohl(did), name, ntohl(hint));
170 #endif
171                                 return hint;
172                 }
173         }
174
175         memset(&rootinfo_key, 0, sizeof(rootinfo_key));
176         memset(&rootinfo_data, 0, sizeof(rootinfo_data));
177         rootinfo_key.data = ROOTINFO_KEY;
178         rootinfo_key.size = ROOTINFO_KEYLEN;
179 retry:
180         if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
181                 syslog(LOG_ERR, "cnid_add: Failed to begin transaction: %s",
182                        db_strerror(rc));
183                 goto cleanup_err;
184         }
185
186         /* Get the key. */
187         switch (rc = db->db_didname->get(db->db_didname, tid, &rootinfo_key,
188                 &rootinfo_data, DB_RMW)) {
189                 case DB_LOCK_DEADLOCK:
190                         if ((rc = txn_abort(tid)) != 0) {
191                                 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
192                                 goto cleanup_err;
193                         }
194                         goto retry;
195                 case 0:
196                         memcpy(&hint, rootinfo_data.data, sizeof(hint));
197 #ifdef DEBUG
198                         syslog(LOG_INFO, "cnid_add: Found rootinfo for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
199 #endif
200                         break;
201                 case DB_NOTFOUND:
202                         hint = htonl(CNID_START);
203 #ifdef DEBUG
204                         syslog(LOG_INFO, "cnid_add: Using CNID_START for did %u, name %s",
205                                ntohl(did), name);
206 #endif
207                         break;
208                 default:
209                         syslog(LOG_ERR, "cnid_add: Unable to lookup rootinfo: %s",
210                                db_strerror(rc));
211                         goto cleanup_abort;
212         }
213
214         /* Search for a new id.  We keep the first id around to check for
215          * wrap-around.  NOTE: I do it this way so that we can go back and
216          * fill in holes. */
217         save = id = ntohl(hint);
218         while ((rc = add_cnid(db, tid, &key, &data)) != 0) {
219                 /* Don't use any special CNIDs. */
220                 if (++id < CNID_START) {
221                         id = CNID_START;
222                 }
223
224                 if ((rc != DB_KEYEXIST) || (save == id)) {
225                         syslog(LOG_ERR, "cnid_add: Unable to add CNID %u: %s",
226                                ntohl(hint), db_strerror(rc));
227                         goto cleanup_abort;
228                 }
229                 hint = htonl(id);
230         }
231
232         rootinfo_data.data = &hint;
233         rootinfo_data.size = sizeof(hint);
234
235         switch (rc = db->db_didname->put(db->db_didname, tid, &rootinfo_key, &rootinfo_data, 0)) {
236                 case DB_LOCK_DEADLOCK:
237                         if ((rc = txn_abort(tid)) != 0) {
238                                 syslog(LOG_ERR, "cnid_add: txn_abort: %s", db_strerror(rc));
239                                 goto cleanup_err;
240                         }
241                         goto retry;
242                 case 0:
243                         break;
244                 default:
245                         syslog(LOG_ERR, "cnid_add: Unable to update rootinfo: %s",
246                                db_strerror(rc));
247                         goto cleanup_abort;
248         }
249
250 cleanup_commit:
251         /* The transaction finished, commit it. */
252         if ((rc = txn_commit(tid, 0)) != 0) {
253                 syslog(LOG_ERR, "cnid_add: Unable to commit transaction: %s",
254                        db_strerror(rc));
255                 goto cleanup_err;
256         }
257
258 #ifdef DEBUG
259         syslog(LOG_INFO, "cnid_add: Returned CNID for did %u, name %s as %u",
260                ntohl(did), name, ntohl(hint));
261 #endif
262         return hint;
263
264 cleanup_abort:
265         txn_abort(tid);
266
267 cleanup_err:
268         return 0;
269 }
270 #endif /* CNID_DB */
271