]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cdb/cnid_cdb_add.c
a1e69a76ac5446a37afde531b208c9cbdc1a46bf
[netatalk.git] / libatalk / cnid / cdb / cnid_cdb_add.c
1 /*
2  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
3  * All Rights Reserved. See COPYRIGHT.
4  *
5  * cnid_add (db, dev, ino, did, name, hint):
6  * add a name to the CNID database. we use both dev/ino and did/name
7  * to keep track of things.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif /* HAVE_CONFIG_H */
13
14 #ifdef CNID_BACKEND_CDB
15 #include <arpa/inet.h>
16 #include "cnid_cdb_private.h"
17
18 extern int cnid_cdb_update(struct _cnid_db *cdb, cnid_t id, const struct stat *st,
19                            cnid_t did, const char *name, size_t len);
20
21 #define tid    NULL
22
23 static void make_devino_data(unsigned char *buf, dev_t dev, ino_t ino)
24 {
25     buf[CNID_DEV_LEN - 1] = dev; dev >>= 8;
26     buf[CNID_DEV_LEN - 2] = dev; dev >>= 8;
27     buf[CNID_DEV_LEN - 3] = dev; dev >>= 8;
28     buf[CNID_DEV_LEN - 4] = dev; dev >>= 8;
29     buf[CNID_DEV_LEN - 5] = dev; dev >>= 8;
30     buf[CNID_DEV_LEN - 6] = dev; dev >>= 8;
31     buf[CNID_DEV_LEN - 7] = dev; dev >>= 8;
32     buf[CNID_DEV_LEN - 8] = dev;
33
34     buf[CNID_DEV_LEN + CNID_INO_LEN - 1] = ino; ino >>= 8;
35     buf[CNID_DEV_LEN + CNID_INO_LEN - 2] = ino; ino >>= 8;
36     buf[CNID_DEV_LEN + CNID_INO_LEN - 3] = ino; ino >>= 8;
37     buf[CNID_DEV_LEN + CNID_INO_LEN - 4] = ino; ino >>= 8;
38     buf[CNID_DEV_LEN + CNID_INO_LEN - 5] = ino; ino >>= 8;
39     buf[CNID_DEV_LEN + CNID_INO_LEN - 6] = ino; ino >>= 8;
40     buf[CNID_DEV_LEN + CNID_INO_LEN - 7] = ino; ino >>= 8;
41     buf[CNID_DEV_LEN + CNID_INO_LEN - 8] = ino;    
42 }
43
44 unsigned char *make_cnid_data(u_int32_t flags, const struct stat *st, const cnid_t did,
45                      const char *name, const size_t len)
46 {
47     static unsigned char start[CNID_HEADER_LEN + MAXPATHLEN + 1];
48     unsigned char *buf = start  +CNID_LEN;
49     u_int32_t i;
50
51     if (len > MAXPATHLEN)
52         return NULL;
53
54     make_devino_data(buf, !(flags & CNID_FLAG_NODEV)?st->st_dev:0, st->st_ino);
55     buf += CNID_DEVINO_LEN;
56
57     i = S_ISDIR(st->st_mode)?1:0;
58     i = htonl(i);
59     memcpy(buf, &i, sizeof(i));
60     buf += sizeof(i);
61     
62     /* did is already in network byte order */
63     memcpy(buf, &did, sizeof(did));
64     buf += sizeof(did);
65
66     memcpy(buf, name, len);
67     *(buf + len) = '\0';
68
69     return start;
70 }    
71
72 /* --------------- */
73 static int db_stamp(void *buffer, size_t size)
74 {
75 time_t t;
76     memset(buffer, 0, size);
77     /* return the current time. */
78     if (size < sizeof(time_t))
79         return -1;
80     t = time(NULL);
81     memcpy(buffer,&t, sizeof(time_t));
82     return 0;
83
84 }
85
86
87 /* ----------------------------- */
88 static cnid_t get_cnid(CNID_private *db)
89 {
90     DBT rootinfo_key, rootinfo_data;
91     DBC  *cursor;
92     int rc;
93     int flag, setstamp=0;
94     cnid_t hint,id;
95     char buf[ROOTINFO_DATALEN];
96     char stamp[CNID_DEV_LEN];
97      
98     if ((rc = db->db_cnid->cursor(db->db_cnid, NULL, &cursor, DB_WRITECURSOR) ) != 0) {
99         LOG(log_error, logtype_default, "get_cnid: Unable to get a cursor: %s", db_strerror(rc));
100         return CNID_INVALID;
101     }
102
103     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
104     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
105     rootinfo_key.data = ROOTINFO_KEY;
106     rootinfo_key.size = ROOTINFO_KEYLEN;
107
108     switch (rc = cursor->c_get(cursor, &rootinfo_key, &rootinfo_data, DB_SET)) {
109     case 0:
110         memcpy(&hint, (char *)rootinfo_data.data +CNID_TYPE_OFS, sizeof(hint));
111         id = ntohl(hint);
112         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
113          * needs to be recycled before proceding. */
114         if (++id == CNID_INVALID) {
115             LOG(log_error, logtype_default, "cnid_add: FATAL: CNID database has reached its limit.");
116             errno = CNID_ERR_MAX;
117             goto cleanup;
118         }
119         hint = htonl(id);
120         flag = DB_CURRENT;
121         break;
122     case DB_NOTFOUND:
123         hint = htonl(CNID_START);
124         flag = DB_KEYFIRST;
125         setstamp = 1;
126         break;
127     default:
128         LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
129         errno = CNID_ERR_DB; 
130         goto cleanup;
131     }
132     
133     memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
134     rootinfo_data.data = buf;
135     rootinfo_data.size = ROOTINFO_DATALEN;
136     memcpy((char *)rootinfo_data.data +CNID_TYPE_OFS, &hint, sizeof(hint));
137     if (setstamp) {
138         if (db_stamp(stamp, CNID_DEV_LEN) < 0) {
139             goto cleanup;
140         }
141         memcpy((char *)rootinfo_data.data +CNID_DEV_OFS, stamp, sizeof(stamp));
142     }
143
144     
145     switch (rc = cursor->c_put(cursor, &rootinfo_key, &rootinfo_data, flag)) {
146     case 0:
147         break;
148     default:
149         LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
150         errno = CNID_ERR_DB; 
151         goto cleanup;
152     }
153     if ((rc = cursor->c_close(cursor)) != 0) {
154         LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc));
155         errno = CNID_ERR_DB; 
156         return CNID_INVALID;
157     }
158     return hint;
159 cleanup:
160     if ((rc = cursor->c_close(cursor)) != 0) {
161         LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc));
162         return CNID_INVALID;
163     }
164     return CNID_INVALID;
165 }
166
167 /* ------------------------ */
168 cnid_t cnid_cdb_add(struct _cnid_db *cdb, const struct stat *st,
169                     cnid_t did, const char *name, size_t len, cnid_t hint)
170 {
171     CNID_private *db;
172     DBT key, data;
173     cnid_t id;
174     int rc;
175
176     if (!cdb || !(db = cdb->_private) || !st || !name) {
177         errno = CNID_ERR_PARAM;
178         return CNID_INVALID;
179     }
180
181     /* Do a lookup. */
182     id = cnid_cdb_lookup(cdb, st, did, name, len);
183     /* ... Return id if it is valid, or if Rootinfo is read-only. */
184     if (id || (db->flags & CNIDFLAG_DB_RO)) {
185 #ifdef DEBUG
186         LOG(log_debug9, logtype_default, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
187 #endif
188         return id;
189     }
190
191     /* Initialize our DBT data structures. */
192     memset(&key, 0, sizeof(key));
193     memset(&data, 0, sizeof(data));
194
195     if ((data.data = make_cnid_data(cdb->flags, st, did, name, len)) == NULL) {
196         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
197         errno = CNID_ERR_PATH;
198         return CNID_INVALID;
199     }
200     data.size = CNID_HEADER_LEN + len + 1;
201
202     if ((hint = get_cnid(db)) == 0) {
203          errno = CNID_ERR_DB;
204          return CNID_INVALID;
205     }
206     memcpy(data.data, &hint, sizeof(hint));
207     
208     key.data = &hint;
209     key.size = sizeof(hint);
210
211     /* Now we need to add the CNID data to the databases. */
212     if ((rc = db->db_cnid->put(db->db_cnid, tid, &key, &data, DB_NOOVERWRITE))) {
213         if (rc == EINVAL) {
214             /* if we have a duplicate
215              * on cnid it's a fatal error.
216              * on dev:inode
217              *   - leftover should have been delete before.
218              *   - a second process already updated the db
219              *   - it's a new file eg our file is already deleted and replaced
220              * on did:name leftover
221             */
222             if (cnid_cdb_update(cdb, hint, st, did, name, len)) {
223                 errno = CNID_ERR_DB;
224                 return CNID_INVALID;
225             }
226         }
227         else {
228             LOG(log_error, logtype_default
229                    , "cnid_add: Failed to add CNID for %s to database using hint %u: %s", 
230                    name, ntohl(hint), db_strerror(rc));  
231             errno = CNID_ERR_DB;
232             return CNID_INVALID;
233         }
234     }
235
236 #ifdef DEBUG
237     LOG(log_debug9, logtype_default, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
238 #endif
239
240     return hint;
241 }
242
243 /* cnid_cbd_getstamp */
244 /*-----------------------*/
245 int cnid_cdb_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len)
246 {
247     DBT key, data;
248     int rc;
249     CNID_private *db;
250
251     if (!cdb || !(db = cdb->_private) || !buffer || !len) {
252         errno = CNID_ERR_PARAM;
253         return -1;
254     }
255
256     memset(buffer, 0, len);
257     memset(&key, 0, sizeof(key));
258     memset(&data, 0, sizeof(data));
259
260     key.data = ROOTINFO_KEY;
261     key.size = ROOTINFO_KEYLEN;
262
263     if (0 != (rc = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0 )) ) {
264         if (rc != DB_NOTFOUND) {
265             LOG(log_error, logtype_default, "cnid_lookup: Unable to get database stamp: %s",
266                db_strerror(rc));
267             errno = CNID_ERR_DB;
268             return -1;
269         }
270         /* we waste a single ID here... */
271         get_cnid(db);
272         memset(&key, 0, sizeof(key));
273         memset(&data, 0, sizeof(data));
274         key.data = ROOTINFO_KEY;
275         key.size = ROOTINFO_KEYLEN;
276         if (0 != (rc = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0 )) ) {
277             LOG(log_error, logtype_default, "cnid_getstamp: failed to get rootinfo: %s", 
278                db_strerror(rc));
279             errno = CNID_ERR_DB;
280             return -1;
281         }
282     }
283
284     memcpy(buffer, (char*)data.data + CNID_DEV_OFS, len);
285 #ifdef DEBUG
286     LOG(log_debug9, logtype_cnid, "cnid_getstamp: Returning stamp");
287 #endif
288    return 0;
289 }
290
291
292 #endif /* CNID_BACKEND_CDB */