]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cdb/cnid_cdb_open.c
Adjust what gets distributed
[netatalk.git] / libatalk / cnid / cdb / cnid_cdb_open.c
1 /*
2  * $Id: cnid_cdb_open.c,v 1.4 2009-11-24 12:18:19 didg Exp $
3  *
4  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * CNID database support. 
8  *
9  * here's the deal:
10  *  1) afpd already caches did's. 
11  *  2) the database stores cnid's as both did/name and dev/ino pairs. 
12  *  3) RootInfo holds the value of the NextID.
13  *  4) the cnid database gets called in the following manner --
14  *     start a database:
15  *     cnid = cnid_open(root_dir);
16  *
17  *     allocate a new id: 
18  *     newid = cnid_add(cnid, dev, ino, parent did,
19  *     name, id); id is a hint for a specific id. pass 0 if you don't
20  *     care. if the id is already assigned, you won't get what you
21  *     requested.
22  *
23  *     given an id, get a did/name and dev/ino pair.
24  *     name = cnid_get(cnid, &id); given an id, return the corresponding
25  *     info.
26  *     return code = cnid_delete(cnid, id); delete an entry. 
27  *
28  * with AFP, CNIDs 0-2 have special meanings. here they are:
29  * 0 -- invalid cnid
30  * 1 -- parent of root directory (handled by afpd) 
31  * 2 -- root directory (handled by afpd)
32  *
33  * CNIDs 4-16 are reserved according to page 31 of the AFP 3.0 spec so, 
34  * CNID_START begins at 17.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif /* HAVE_CONFIG_H */
40
41 #ifdef CNID_BACKEND_CDB
42 #include "cnid_cdb_private.h"
43
44 #ifndef MIN
45 #define MIN(a, b)  ((a) < (b) ? (a) : (b))
46 #endif /* ! MIN */
47
48 #define DBHOME        ".AppleDB"
49 #define DBCNID        "cnid2.db"
50 #define DBDEVINO      "devino.db"
51 #define DBDIDNAME     "didname.db"      /* did/full name mapping */
52 #define DBLOCKFILE    "cnid.lock"
53
54 #define DBHOMELEN    8
55 #define DBLEN        10
56
57 /* we version the did/name database so that we can change the format
58  * if necessary. the key is in the form of a did/name pair. in this case,
59  * we use 0/0. */
60 #define DBVERSION_KEY    "\0\0\0\0\0"
61 #define DBVERSION_KEYLEN 5
62 #define DBVERSION1       0x00000001U
63 #define DBVERSION        DBVERSION1
64
65 #define DBOPTIONS    (DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL)
66
67 #define MAXITER     0xFFFF      /* maximum number of simultaneously open CNID
68                                  * databases. */
69
70 static char *old_dbfiles[] = {"cnid.db", NULL};
71
72 /* -----------------------
73  * bandaid for LanTest performance pb. for now not used, cf. ifdef 0 below
74 */
75 static int my_yield(void)
76 {
77     struct timeval t;
78     int ret;
79
80     t.tv_sec = 0;
81     t.tv_usec = 1000;
82     ret = select(0, NULL, NULL, NULL, &t);
83     return 0;
84 }
85
86 /* --------------- */
87 static int didname(DB *dbp _U_, const DBT *pkey _U_, const DBT *pdata, DBT *skey)
88 {
89 int len;
90  
91     memset(skey, 0, sizeof(DBT));
92     skey->data = (char *)pdata->data + CNID_DID_OFS;
93     len = strlen((char *)skey->data + CNID_DID_LEN);
94     skey->size = CNID_DID_LEN + len + 1;
95     return (0);
96 }
97  
98 /* --------------- */
99 static int devino(DB *dbp _U_, const DBT *pkey _U_, const DBT *pdata, DBT *skey)
100 {
101     memset(skey, 0, sizeof(DBT));
102     skey->data = (char *)pdata->data + CNID_DEVINO_OFS;
103     skey->size = CNID_DEVINO_LEN;
104     return (0);
105 }
106  
107 /* --------------- */
108 static int  my_associate (DB *p, DB *s,
109                    int (*callback)(DB *, const DBT *,const DBT *, DBT *),
110                    u_int32_t flags)
111 {
112 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
113     return p->associate(p, NULL, s, callback, flags);
114 #else
115 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 0)
116     return p->associate(p,       s, callback, flags);
117 #else
118     return 0; /* FIXME */
119 #endif
120 #endif
121 }
122
123 /* --------------- */
124 static int my_open(DB * p, const char *f, const char *d, DBTYPE t, u_int32_t flags, int mode)
125 {
126 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
127     return p->open(p, NULL, f, d, t, flags, mode);
128 #else
129     return p->open(p, f, d, t, flags, mode);
130 #endif
131 }
132
133 /* --------------- */
134 static struct _cnid_db *cnid_cdb_new(const char *volpath)
135 {
136     struct _cnid_db *cdb;
137     int major, minor, patch;
138     char *version_str;
139
140     version_str = db_version(&major, &minor, &patch);
141
142     /* check library match, ignore if only patch level changed */
143     if ( major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR)
144     {
145         LOG(log_error, logtype_cnid, "cnid_cdb_new: the Berkeley DB library version used does not match the version compiled with: (%u.%u)/(%u.%u)", DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor); 
146         return NULL;
147     }
148
149     if ((cdb = (struct _cnid_db *) calloc(1, sizeof(struct _cnid_db))) == NULL)
150         return NULL;
151
152     if ((cdb->volpath = strdup(volpath)) == NULL) {
153         free(cdb);
154         return NULL;
155     }
156
157     cdb->flags = CNID_FLAG_PERSISTENT;
158
159     cdb->cnid_add = cnid_cdb_add;
160     cdb->cnid_delete = cnid_cdb_delete;
161     cdb->cnid_get = cnid_cdb_get;
162     cdb->cnid_lookup = cnid_cdb_lookup;
163     cdb->cnid_nextid = NULL;    /*cnid_cdb_nextid;*/
164     cdb->cnid_resolve = cnid_cdb_resolve;
165     cdb->cnid_update = cnid_cdb_update;
166     cdb->cnid_close = cnid_cdb_close;
167     cdb->cnid_getstamp = cnid_cdb_getstamp;
168     cdb->cnid_rebuild_add = cnid_cdb_rebuild_add;
169
170     return cdb;
171 }
172
173 /* --------------- */
174 static int upgrade_required(char *dbdir)
175 {
176     char path[MAXPATHLEN + 1];
177     int len, i;
178     int found = 0;
179     struct stat st;
180     
181     strcpy(path, dbdir);
182
183     len = strlen(path);
184     if (path[len - 1] != '/') {
185         strcat(path, "/");
186         len++;
187     }
188     
189     for (i = 0; old_dbfiles[i] != NULL; i++) {
190         strcpy(path + len, old_dbfiles[i]);
191         if ( !(stat(path, &st) < 0) ) {
192             found++;
193             continue;
194         }
195         if (errno != ENOENT) {
196             LOG(log_error, logtype_default, "cnid_open: Checking %s gave %s", path, strerror(errno));
197             found++;
198         }
199     }
200     return found;
201 }
202
203 /* --------------- */
204 struct _cnid_db *cnid_cdb_open(const char *dir, mode_t mask, u_int32_t flags _U_)
205 {
206     struct stat st;
207     char path[MAXPATHLEN + 1];
208     CNID_private *db;
209     struct _cnid_db *cdb;
210     int open_flag, len;
211     static int first = 0;
212     int rc;
213
214     if (!dir || *dir == 0) {
215         return NULL;
216     }
217
218     /* this checks .AppleDB.
219        We need space for dir + '/' + DBHOMELEN + '/' + DBLEN */
220     if ((len = strlen(dir)) > (MAXPATHLEN - DBHOMELEN - DBLEN - 2)) {
221         LOG(log_error, logtype_default, "cnid_open: Pathname too large: %s", dir);
222         return NULL;
223     }
224
225     if ((cdb = cnid_cdb_new(dir)) == NULL) {
226         LOG(log_error, logtype_default, "cnid_open: Unable to allocate memory for database");
227         return NULL;
228     }
229
230     if ((db = (CNID_private *) calloc(1, sizeof(CNID_private))) == NULL) {
231         LOG(log_error, logtype_default, "cnid_open: Unable to allocate memory for database");
232         goto fail_cdb;
233     }
234
235     cdb->_private = (void *) db;
236     db->magic = CNID_DB_MAGIC;
237
238     strcpy(path, dir);
239     if (path[len - 1] != '/') {
240         strcat(path, "/");
241         len++;
242     }
243
244     strcpy(path + len, DBHOME);
245     if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777 & ~mask) < 0)) {
246         LOG(log_error, logtype_default, "cnid_open: DBHOME mkdir failed for %s", path);
247         goto fail_adouble;
248     }
249
250     if (upgrade_required(path)) {
251         LOG(log_error, logtype_default, "cnid_open: Found version 1 of the CNID database. Please upgrade to version 2");
252         goto fail_adouble;
253     }
254
255     /* Print out the version of BDB we're linked against. */
256     if (!first) {
257         first = 1;
258         LOG(log_info, logtype_default, "CNID DB initialized using %s", db_version(NULL, NULL, NULL));
259     }
260
261     open_flag = DB_CREATE;
262
263     /* We need to be able to open the database environment with full
264      * transaction, logging, and locking support if we ever hope to 
265      * be a true multi-acess file server. */
266     if ((rc = db_env_create(&db->dbenv, 0)) != 0) {
267         LOG(log_error, logtype_default, "cnid_open: db_env_create: %s", db_strerror(rc));
268         goto fail_lock;
269     }
270
271     /* Open the database environment. */
272     if ((rc = db->dbenv->open(db->dbenv, path, DBOPTIONS, 0666 & ~mask)) != 0) {
273         LOG(log_error, logtype_default, "cnid_open: dbenv->open (rw) of %s failed: %s", path, db_strerror(rc));
274         /* FIXME: This should probably go. Even if it worked, any use for a read-only DB? Didier? */
275         if (rc == DB_RUNRECOVERY) {
276             /* This is the mother of all errors.  We _must_ fail here. */
277             LOG(log_error, logtype_default,
278                 "cnid_open: CATASTROPHIC ERROR opening database environment %s.  Run db_recovery -c immediately", path);
279             goto fail_lock;
280         }
281
282         /* We can't get a full transactional environment, so multi-access
283          * is out of the question.  Let's assume a read-only environment,
284          * and try to at least get a shared memory pool. */
285         if ((rc = db->dbenv->open(db->dbenv, path, DB_INIT_MPOOL, 0666 & ~mask)) != 0) {
286             /* Nope, not a MPOOL, either.  Last-ditch effort: we'll try to
287              * open the environment with no flags. */
288             if ((rc = db->dbenv->open(db->dbenv, path, 0, 0666 & ~mask)) != 0) {
289                 LOG(log_error, logtype_default, "cnid_open: dbenv->open of %s failed: %s", path, db_strerror(rc));
290                 goto fail_lock;
291             }
292         }
293         db->flags |= CNIDFLAG_DB_RO;
294         open_flag = DB_RDONLY;
295         LOG(log_info, logtype_default, "cnid_open: Obtained read-only database environment %s", path);
296     }
297
298     /* ---------------------- */
299     /* Main CNID database.  Use a hash for this one. */
300
301     if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
302         LOG(log_error, logtype_default, "cnid_open: Failed to create cnid database: %s",
303             db_strerror(rc));
304         goto fail_appinit;
305     }
306
307     if ((rc = my_open(db->db_cnid, DBCNID, DBCNID, DB_BTREE, open_flag, 0666 & ~mask)) != 0) {
308         LOG(log_error, logtype_default, "cnid_open: Failed to open dev/ino database: %s",
309             db_strerror(rc));
310         goto fail_appinit;
311     }
312
313     /* ---------------------- */
314     /* did/name reverse mapping.  We use a BTree for this one. */
315
316     if ((rc = db_create(&db->db_didname, db->dbenv, 0)) != 0) {
317         LOG(log_error, logtype_default, "cnid_open: Failed to create did/name database: %s",
318             db_strerror(rc));
319         goto fail_appinit;
320     }
321
322     if ((rc = my_open(db->db_didname, DBCNID, DBDIDNAME, DB_BTREE, open_flag, 0666 & ~mask))) {
323         LOG(log_error, logtype_default, "cnid_open: Failed to open did/name database: %s",
324             db_strerror(rc));
325         goto fail_appinit;
326     }
327
328     /* ---------------------- */
329     /* dev/ino reverse mapping.  Use a hash for this one. */
330
331     if ((rc = db_create(&db->db_devino, db->dbenv, 0)) != 0) {
332         LOG(log_error, logtype_default, "cnid_open: Failed to create dev/ino database: %s",
333             db_strerror(rc));
334         goto fail_appinit;
335     }
336
337     if ((rc = my_open(db->db_devino, DBCNID, DBDEVINO, DB_BTREE, open_flag, 0666 & ~mask)) != 0) {
338         LOG(log_error, logtype_default, "cnid_open: Failed to open devino database: %s",
339             db_strerror(rc));
340         goto fail_appinit;
341     }
342
343     /* ---------------------- */
344     /* Associate the secondary with the primary. */ 
345     if ((rc = my_associate(db->db_cnid, db->db_didname, didname, 0)) != 0) {
346         LOG(log_error, logtype_default, "cnid_open: Failed to associate didname database: %s",
347             db_strerror(rc));
348         goto fail_appinit;
349     }
350  
351     if ((rc = my_associate(db->db_cnid, db->db_devino, devino, 0)) != 0) {
352         LOG(log_error, logtype_default, "cnid_open: Failed to associate devino database: %s",
353             db_strerror(rc));
354         goto fail_appinit;
355     }
356  
357 #if 0
358     DBT key, pkey, data;
359     /* ---------------------- */
360     /* Check for version.  This way we can update the database if we need
361      * to change the format in any way. */
362     memset(&key, 0, sizeof(key));
363     memset(&pkey, 0, sizeof(DBT));
364     memset(&data, 0, sizeof(data));
365     key.data = DBVERSION_KEY;
366     key.size = DBVERSION_KEYLEN;
367
368     if ((rc = db->db_didname->pget(db->db_didname, NULL, &key, &pkey, &data, 0)) != 0) {
369         int ret;
370         {
371             u_int32_t version = htonl(DBVERSION);
372
373             data.data = &version;
374             data.size = sizeof(version);
375         }
376         if ((ret = db->db_didname->put(db->db_cnid, NULL, &key, &data,
377                                        DB_NOOVERWRITE))) {
378             LOG(log_error, logtype_default, "cnid_open: Error putting new version: %s",
379                 db_strerror(ret));
380             db->db_didname->close(db->db_didname, 0);
381             goto fail_appinit;
382         }
383     }
384 #endif
385
386     /* TODO In the future we might check for version number here. */
387 #if 0
388     memcpy(&version, data.data, sizeof(version));
389     if (version != ntohl(DBVERSION)) {
390         /* Do stuff here. */
391     }
392 #endif /* 0 */
393
394     db_env_set_func_yield(my_yield);
395     return cdb;
396
397   fail_appinit:
398     if (db->db_didname)
399         db->db_didname->close(db->db_didname, 0);
400     if (db->db_devino)
401         db->db_devino->close(db->db_devino, 0);
402     if (db->db_cnid)
403         db->db_cnid->close(db->db_cnid, 0);
404     LOG(log_error, logtype_default, "cnid_open: Failed to setup CNID DB environment");
405     db->dbenv->close(db->dbenv, 0);
406
407   fail_lock:
408
409   fail_adouble:
410
411     free(db);
412
413   fail_cdb:
414     if (cdb->volpath != NULL)
415         free(cdb->volpath);
416     free(cdb);
417
418     return NULL;
419 }
420
421 struct _cnid_module cnid_cdb_module = {
422     "cdb",
423     {NULL, NULL},
424     cnid_cdb_open,
425     CNID_FLAG_SETUID | CNID_FLAG_BLOCK
426 };
427
428
429 #endif /* CNID_BACKEND_CDB */