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