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