]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cnid_open.c
apply parts of the netbsd build patch by Patrick Welche <prlw1@newn.cam.ac.uk>, mostl...
[netatalk.git] / libatalk / cnid / cnid_open.c
1 /*
2  * $Id: cnid_open.c,v 1.54 2003-07-10 13:50:39 rlewczuk 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_DB
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif /* HAVE_UNISTD_H */
48 #ifdef HAVE_FCNTL_H
49 #include <fcntl.h>
50 #endif /* HAVE_FCNTL_H */
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 #include <atalk/logger.h>
54 #ifdef HAVE_SYS_TIME_H
55 #include <sys/time.h>
56 #endif /* HAVE_SYS_TIME_H */
57
58 #include <db.h>
59
60 #include <atalk/adouble.h>
61 #include <atalk/cnid.h>
62 #include <atalk/util.h>
63
64 #include "cnid_private.h"
65
66 #ifndef MIN
67 #define MIN(a, b)  ((a) < (b) ? (a) : (b))
68 #endif /* ! MIN */
69
70 #ifndef MAX
71 #define MAX(a, b)  ((a) > (b) ? (a) : (b))
72 #endif /* ! MIN */
73
74
75 #define DBHOME        ".AppleDB"
76 #define DBCNID        "cnid.db"
77 #define DBDEVINO      "devino.db"
78 #define DBDIDNAME     "didname.db"   /* did/full name mapping */
79 #define DBMANGLE      "mangle.db"    /* filename mangling */
80 #define DBLOCKFILE    "cnid.lock"
81 #define DBRECOVERFILE "cnid.dbrecover"
82 #define DBCLOSEFILE   "cnid.close"
83
84 #define DBHOMELEN    8
85 #define DBLEN        10
86
87 /* we version the did/name database so that we can change the format
88  * if necessary. the key is in the form of a did/name pair. in this case,
89  * we use 0/0. */
90 #define DBVERSION_KEY    "\0\0\0\0\0"
91 #define DBVERSION_KEYLEN 5
92 #define DBVERSION1       0x00000001U
93 #define DBVERSION        DBVERSION1
94
95 #ifdef CNID_DB_CDB
96 #define DBOPTIONS    (DB_CREATE | DB_INIT_CDB | DB_INIT_MPOOL)
97 #else /* !CNID_DB_CDB */
98 #if DB_VERSION_MAJOR >= 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR > 1)
99 #define DBOPTIONS    (DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \
100 DB_INIT_LOG | DB_INIT_TXN)
101 #else /* DB_VERSION_MINOR < 1 */
102 /*#define DBOPTIONS    (DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \
103 DB_INIT_LOG | DB_INIT_TXN | DB_TXN_NOSYNC)*/
104 #define DBOPTIONS    (DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | \
105 DB_INIT_LOG | DB_INIT_TXN)
106 #endif /* DB_VERSION_MINOR */
107 #endif /* CNID_DB_CDB */
108
109 #ifndef CNID_DB_CDB
110 /* Let's try and use the youngest lock detector if present.
111  * If we can't do that, then let BDB use its default deadlock detector. */
112 #if defined DB_LOCK_YOUNGEST
113 #define DEAD_LOCK_DETECT DB_LOCK_YOUNGEST
114 #else /* DB_LOCK_YOUNGEST */
115 #define DEAD_LOCK_DETECT DB_LOCK_DEFAULT
116 #endif /* DB_LOCK_YOUNGEST */
117 #endif /* CNID_DB_CDB */
118
119 static cnid_t cnid_rebuild (CNID_private *, mode_t);
120 static int    cnid_recover (char *, mode_t);
121
122 #define MAXITER     0xFFFF /* maximum number of simultaneously open CNID
123 * databases. */
124
125 /* -----------------------
126  * bandaid for LanTest performance pb.
127 */
128 static int my_yield(void) 
129 {
130     struct timeval t;
131     int ret;
132
133     t.tv_sec = 0;
134     t.tv_usec = 1000;
135     ret = select(0, NULL, NULL, NULL, &t);
136     return 0;
137 }
138
139 /* --------------- */
140 static int  my_open(DB *p, const char *f, const char *d, DBTYPE t, u_int32_t flags, int mode)
141 {
142 #if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)
143     return p->open(p, NULL, f, d, t, flags, mode);
144 #else
145     return p->open(p,       f, d, t, flags, mode);
146 #endif
147 }
148
149 void *cnid_open(const char *dir, mode_t mask) {
150     struct stat st;
151     struct flock lock;
152     char path[MAXPATHLEN + 1];
153     CNID_private *db;
154     DBT key, data;
155     DB_TXN *tid;
156     int open_flag, len;
157     int rc;
158     int can_check = 0, require_rebuild = 0;
159     u_int32_t ncnid, ndidname, ndevino;
160     DB_HASH_STAT *sp;
161     static int first = 0;
162
163     ncnid = ndidname = ndevino = 0;
164
165
166     if (!dir) {
167         return NULL;
168     }
169
170     /* this checks .AppleDB */
171     if ((len = strlen(dir)) > (MAXPATHLEN - DBLEN - 1)) {
172         LOG(log_error, logtype_default, "cnid_open: Pathname too large: %s", dir);
173         return NULL;
174     }
175
176     if ((db = (CNID_private *)calloc(1, sizeof(CNID_private))) == NULL) {
177         LOG(log_error, logtype_default, "cnid_open: Unable to allocate memory for database");
178         return NULL;
179     }
180
181     db->magic = CNID_DB_MAGIC;
182
183     strcpy(path, dir);
184     if (path[len - 1] != '/') {
185         strcat(path, "/");
186         len++;
187     }
188
189     strcpy(path + len, DBHOME);
190     if ((stat(path, &st) < 0) && (ad_mkdir(path, 0777 & ~mask) < 0)) {
191         LOG(log_error, logtype_default, "cnid_open: DBHOME mkdir failed for %s", path);
192         goto fail_adouble;
193     }
194
195     lock.l_type = F_WRLCK;
196     lock.l_whence = SEEK_SET;
197     /* Make sure cnid.lock goes in .AppleDB. */
198     strcat(path, "/");
199     len++;
200
201     strcat(path, DBLOCKFILE);
202     strcpy(db->lock_file, path);
203     if ( stat (path, &st) == 0)
204         can_check = 1;
205
206     if ((db->lockfd = open(path, O_RDWR | O_CREAT, 0666 & ~mask)) > -1) {
207         lock.l_start = 0;
208         lock.l_len = 1;
209         if (fcntl(db->lockfd, F_SETLK, &lock) < 0) {
210                 /* Now try to get a shared lock */
211                 lock.l_type = F_RDLCK;
212
213                 while (fcntl(db->lockfd, F_SETLKW, &lock) < 0) {
214                         if ( errno != EINTR )
215                                 goto fail_db;
216                 }
217                 can_check = 0;
218         }
219         else {
220                 /* We got an exclusive lock, so we're the first/only process accessing the database */
221                 if ( can_check ) {
222                         /* cnid.lock existed but we got an exclusive lock */
223                         /* This means the previous afpd probably crashed  */
224                         /* or got terminated so we run recovery           */
225
226                         path[len + DBHOMELEN] = '\0';
227                         if ( cnid_recover( path, mask) != 0)
228                                 goto fail_lock;
229                 }
230                 else {
231                         /* We're the only process accesing the db, so lets do some constistency checks */
232                         can_check = 1; 
233                 }
234         }
235     }
236     else
237     {
238         LOG(log_error, logtype_default, "cnid_open: Cannot open cnid.lock file in %s lock (lock failed)", path);
239         goto fail_db;
240     }
241
242     path[len + DBHOMELEN] = '\0';
243     open_flag = DB_CREATE;
244
245     /* We need to be able to open the database environment with full
246      * transaction, logging, and locking support if we ever hope to 
247      * be a true multi-acess file server. */
248     if ((rc = db_env_create(&db->dbenv, 0)) != 0) {
249         LOG(log_error, logtype_default, "cnid_open: db_env_create: %s", db_strerror(rc));
250         goto fail_lock;
251     }
252
253     if ((rc = db->dbenv->open(db->dbenv, path, DB_JOINENV, 0666 & ~mask)) !=0 &&
254         (rc = db->dbenv->open(db->dbenv, path, DBOPTIONS, 0666 & ~mask)) != 0) {
255         if (rc == DB_RUNRECOVERY) {
256             /* This is the mother of all errors.  We _must_ fail here. */
257             LOG(log_error, logtype_default, "cnid_open: CATASTROPHIC ERROR opening database environment %s.  Run db_recovery -c immediately", path);
258             goto fail_lock;
259         }
260
261         /* We can't get a full transactional environment, so multi-access
262          * is out of the question.  Let's assume a read-only environment,
263          * and try to at least get a shared memory pool. */
264         if ((rc = db->dbenv->open(db->dbenv, path, DB_INIT_MPOOL, 0666 & ~mask)) != 0) {
265             /* Nope, not a MPOOL, either.  Last-ditch effort: we'll try to
266              * open the environment with no flags. */
267             if ((rc = db->dbenv->open(db->dbenv, path, 0, 0666 & ~mask)) != 0) {
268                 LOG(log_error, logtype_default, "cnid_open: dbenv->open of %s failed: %s",
269                     path, db_strerror(rc));
270                 goto fail_lock;
271             }
272         }
273         db->flags |= CNIDFLAG_DB_RO;
274         open_flag = DB_RDONLY;
275         LOG(log_info, logtype_default, "cnid_open: Obtained read-only database environment %s", path);
276     }
277
278     /* did/name reverse mapping.  We use a BTree for this one. */
279     if ((rc = db_create(&db->db_didname, db->dbenv, 0)) != 0) {
280         LOG(log_error, logtype_default, "cnid_open: Failed to create did/name database: %s",
281             db_strerror(rc));
282         goto fail_appinit;
283     }
284
285     if ((rc = my_open(db->db_didname, DBDIDNAME, NULL, DB_HASH, open_flag, 0666 & ~mask))) {
286         LOG(log_error, logtype_default, "cnid_open: Failed to open did/name database: %s",
287             db_strerror(rc));
288         /* We leak some memory here, but otherwise sometime we run into a SIGSEGV in close */   
289         db->db_didname = NULL; 
290         if (can_check) {
291                 require_rebuild = 1;
292                 goto rebuild;
293         }
294         else
295                 goto fail_appinit;
296     }
297
298     /* Check for version.  This way we can update the database if we need
299      * to change the format in any way. */
300     memset(&key, 0, sizeof(key));
301     memset(&data, 0, sizeof(data));
302     key.data = DBVERSION_KEY;
303     key.size = DBVERSION_KEYLEN;
304
305     if ((rc = db->db_didname->get(db->db_didname, NULL, &key, &data, 0)) != 0) {
306         int ret;
307         {
308             u_int32_t version = htonl(DBVERSION);
309
310             data.data = &version;
311             data.size = sizeof(version);
312         }
313         if ((ret = db->db_didname->put(db->db_didname, NULL, &key, &data,
314                                        DB_NOOVERWRITE))) {
315             LOG(log_error, logtype_default, "cnid_open: Error putting new version: %s",
316                 db_strerror(ret));
317             goto fail_appinit;
318         }
319     }
320
321     /* TODO In the future we might check for version number here. */
322 #if 0
323     memcpy(&version, data.data, sizeof(version));
324     if (version != ntohl(DBVERSION)) {
325         /* Do stuff here. */
326     }
327 #endif /* 0 */
328
329     /* dev/ino reverse mapping.  Use a hash for this one. */
330     if ((rc = db_create(&db->db_devino, db->dbenv, 0)) != 0) {
331         LOG(log_error, logtype_default, "cnid_open: Failed to create dev/ino database: %s",
332             db_strerror(rc));
333         goto fail_appinit;
334     }
335
336     if ((rc = my_open(db->db_devino, DBDEVINO, NULL, DB_HASH, open_flag, 0666 & ~mask))) {
337         LOG(log_error, logtype_default, "cnid_open: Failed to open devino database: %s",
338             db_strerror(rc));
339         db->db_devino = NULL; 
340         if (can_check) {
341                 require_rebuild = 1;
342                 goto rebuild;
343         }
344         else
345                 goto fail_appinit;
346     }
347
348 rebuild:
349
350     /* Main CNID database.  Use a hash for this one. */
351     if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
352         LOG(log_error, logtype_default, "cnid_open: Failed to create cnid database: %s",
353             db_strerror(rc));
354         goto fail_appinit;
355     }
356
357     if ((rc = my_open(db->db_cnid, DBCNID, NULL, DB_HASH, open_flag, 0666 & ~mask))) {
358         LOG(log_error, logtype_default, "cnid_open: Failed to open cnid database: %s",
359             db_strerror(rc));
360         if ( require_rebuild )
361                 LOG(log_error, logtype_default, "cnid_open: CNID Databases are corrupted beyond repair, your probably should delete all files in %s", path);
362         goto fail_appinit;
363     }
364
365     /* Check database consistency */
366     if ( can_check ) {
367         if ((db->db_didname) && ((rc = db->db_didname->stat(db->db_didname, &sp, 0)) != 0)) {
368                 LOG(log_error, logtype_default, "cnid_open: Failed to stat did/macname database: %s",
369                     db_strerror(rc));
370                 require_rebuild = 1;
371         }
372         if (sp) {
373                 ndidname = (sp->hash_nkeys > 2) ? sp->hash_nkeys-2 : 0;
374                 free (sp);
375         }
376
377         if ((db->db_devino) && ((rc = db->db_devino->stat(db->db_devino, &sp, 0)) != 0)) {
378                 LOG(log_error, logtype_default, "cnid_open: Failed to stat dev/ino database: %s",
379                     db_strerror(rc));
380                 require_rebuild = 1;
381         }
382         if (sp) {
383                 ndevino = sp->hash_nkeys;
384                 free (sp);
385         }
386
387         if ((rc = db->db_cnid->stat(db->db_cnid, &sp, 0)) != 0) {
388                 LOG(log_error, logtype_default, "cnid_open: Failed to stat cnid database: %s",
389                     db_strerror(rc));
390                 LOG(log_error, logtype_default, "cnid_open: CNID Databases are corrupted beyond repair, your probably should delete all files in %s", path);
391                 goto fail_appinit;
392         }
393         ncnid = sp->hash_nkeys;
394
395         if ( require_rebuild || ndidname != ncnid || ndevino != ncnid ) {
396                 LOG(log_error, logtype_default, "cnid_open: databases corrupt, number of entries mismatch (didname: %u, devino: %u, cnid: %u)", ndidname, ndevino, ncnid);
397                 if ( CNID_INVALID == cnid_rebuild(db, mask) ) 
398                         goto fail_appinit;
399         }
400         /* now downgrade to a shared lock */
401
402         lock.l_type = F_RDLCK;
403         if (fcntl(db->lockfd, F_SETLK, &lock) < 0) {
404                 LOG (log_error, logtype_default, "cnid_open: Cannot set shared lock");
405         }
406         can_check = 0;
407     }
408
409 #ifdef FILE_MANGLING
410     /* filename mangling database.  Use a hash for this one. */
411     if ((rc = db_create(&db->db_mangle, db->dbenv, 0)) != 0) {
412         LOG(log_error, logtype_default, "cnid_open: Failed to create mangle database: %s", db_strerror(rc));
413         goto fail_appinit;
414     }
415
416     if ((rc = my_open(db->db_mangle, DBMANGLE, NULL, DB_HASH, open_flag, 0666 & ~mask))) {
417         LOG(log_error, logtype_default, "cnid_open: Failed to open mangle database: %s", db_strerror(rc));
418         goto fail_appinit;
419     }
420 #endif /* FILE_MANGLING */
421
422     /* Print out the version of BDB we're linked against. only once */
423     if (!first) {
424         first = 1;
425         LOG(log_info, logtype_default, "CNID DB initialized using %s", db_version(NULL, NULL, NULL));
426     }
427
428     db_env_set_func_yield(my_yield);
429     return db;
430
431 fail_appinit:
432     if (db->db_didname) db->db_didname->close(db->db_didname, 0);
433     if (db->db_devino)  db->db_devino->close(db->db_devino, 0);
434     if (db->db_cnid)    db->db_cnid->close(db->db_cnid, 0);
435     LOG(log_error, logtype_default, "cnid_open: Failed to setup CNID DB environment");
436     db->dbenv->close(db->dbenv, 0);
437
438 fail_lock:
439     if (db->lockfd > -1 && !can_check ) {
440         close(db->lockfd);
441         (void)remove(db->lock_file);
442     }
443
444 fail_adouble:
445
446 fail_db:
447     free(db);
448     return NULL;
449 }
450
451
452 /*-------------------------*/
453
454 static int cnid_recover(char *path, mode_t mask)
455 {
456         DB_ENV * dbenv;
457         int open_flag = 0, rc = 0;
458
459         if ( !path || strlen(path) == 0)
460                 return -1;
461
462         LOG (log_info, logtype_default, "cnid_open: running recover on %s", path);
463
464         /* Remove any existing environment */
465
466         if ((rc = db_env_create(&dbenv, 0)) != 0) {
467                 LOG(log_error, logtype_default, "cnid_recover: db_env_create: %s", db_strerror(rc));
468                 return rc;
469         }
470         if ((rc = dbenv->remove(dbenv, path, DB_FORCE)) != 0) {
471                 LOG(log_error, logtype_default, "cnid_recover db_env_remove: %s", db_strerror(rc));
472                 return rc;
473         }
474
475         /* Create and open recover environment */
476
477         if ((rc = db_env_create(&dbenv, 0)) != 0) {
478                 LOG(log_error, logtype_default, "cnid_recover: db_env_create: %s", db_strerror(rc));
479                 return rc;
480         }
481
482         open_flag = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE;             
483         if ((rc = dbenv->open(dbenv, path, open_flag, 0666 & ~mask)) != 0) {
484                 LOG (log_error, logtype_default, "cnid_recover: RECOVERY failed with %s", db_strerror(rc));
485                 return rc;
486         }
487
488         if ((rc = dbenv->close(dbenv, 0)) != 0) {
489                 LOG(log_error, logtype_default, "cnid_recover: dbenv->close: %s", db_strerror(rc));
490                 return rc;
491         }
492
493         /* Remove recover environment */
494                         
495         if ((rc = db_env_create(&dbenv, 0)) != 0) {
496                 LOG(log_error, logtype_default, "cnid_recover: db_env_create: %s", db_strerror(rc));
497                 return rc;
498         }
499
500         if ((rc = dbenv->remove(dbenv, path, DB_FORCE)) != 0) {
501                 LOG(log_error, logtype_default, "cnid_recover: db_env_remove: %s", db_strerror(rc));
502                 return rc;
503         }
504
505         return 0;
506 }
507
508
509 static cnid_t cnid_rebuild (CNID_private *db, mode_t mask)
510 {
511
512 #ifdef CNID_DB_CDB
513         DB *tmpdb;
514         DBC *cursor;
515         DBT key, data, altkey, altdata, dupkey, dupdata;
516         int rc;
517         cnid_t id, dupid, maxcnid = CNID_START;
518         u_int32_t countp;
519         u_int32_t version;
520
521         static char buffer[MAXPATHLEN + CNID_HEADER_LEN + 1];
522         LOG(log_info, logtype_default, "starting rebuild of databases");
523
524         if ((rc = db->db_cnid->cursor(db->db_cnid, NULL, &cursor, DB_WRITECURSOR) ) != 0) {
525                 LOG(log_error, logtype_default, "cnid_rebuild: Unable to get a cursor: %s", db_strerror(rc));
526                 return CNID_INVALID;
527         }
528
529         memset(&key, 0, sizeof(key));
530         memset(&data, 0, sizeof(data));
531         memset(&altkey, 0, sizeof(key));
532         memset(&altdata, 0, sizeof(data));
533         memset(&dupdata, 0, sizeof(dupdata));
534         memset(&dupkey, 0, sizeof(dupkey));
535
536         /* close didname and devino, then recreate them */
537         if (db->db_didname) db->db_didname->close(db->db_didname, 0);
538         if (db->db_devino) db->db_devino->close(db->db_devino, 0);
539
540         if ((rc = db_create(&db->db_didname, db->dbenv, 0)) != 0) {
541                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to recreate did/name database: %s",
542                         db_strerror(rc));
543                 goto abort;
544         }
545
546         if ((rc = my_open(db->db_didname, DBDIDNAME, NULL, DB_HASH, DB_CREATE|DB_TRUNCATE, 0666 & ~mask))) {
547                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to open did/name database: %s",
548                         db_strerror(rc));
549                 goto abort;
550         }
551
552         if ((rc = db_create(&db->db_devino, db->dbenv, 0)) != 0) {
553                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to recreate dev/ino database: %s",
554                         db_strerror(rc));
555                 goto abort;
556         }
557
558         if ((rc = my_open(db->db_devino, DBDEVINO, NULL, DB_HASH, DB_CREATE|DB_TRUNCATE, 0666 & ~mask))) {
559                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to open dev/ino database: %s",
560                         db_strerror(rc));
561                 goto abort;
562         }
563
564         db->db_devino->sync( db->db_devino, 0);
565         db->db_didname->sync( db->db_didname, 0);
566
567
568         /* now create the temporary cnid database */
569         if ((rc = db_create(&tmpdb, db->dbenv, 0)) != 0) {
570                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to create tmp database: %s",
571                         db_strerror(rc));
572                 goto abort;
573         }
574
575         if ((rc = my_open(tmpdb, DBRECOVERFILE, NULL, DB_HASH, DB_CREATE|DB_TRUNCATE, 0666 & ~mask))) {
576                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to open recover database: %s",
577                         db_strerror(rc));
578                 goto abort;
579         }
580
581         /* add rootinfo and version to didname */
582         id = 0;
583         key.data = ROOTINFO_KEY;
584         key.size = ROOTINFO_KEYLEN;
585         data.data = &id;
586         data.size = sizeof(id);
587
588         if ((rc = db->db_didname->put(db->db_didname, NULL, &key, &data, 0))) {
589                 LOG(log_error, logtype_default, "cnid_rebuild: Error adding ROOTINFO: %s", db_strerror(rc));
590                 goto abort;
591         }
592
593         memset(&key, 0, sizeof(key));
594         memset(&data, 0, sizeof(data));
595         key.data = DBVERSION_KEY;
596         key.size = DBVERSION_KEYLEN;
597         version = htonl(DBVERSION);
598         data.data = &version;
599         data.size = sizeof(version);
600
601         if ((rc = db->db_didname->put(db->db_didname, NULL, &key, &data, 0))) {
602                 LOG(log_error, logtype_default, "cnid_rebuild: Error putting version: %s",
603                         db_strerror(rc));
604                 goto abort;
605         }
606
607         /* now recover the databases from cnid.db*/
608
609         memset(&key, 0, sizeof(key));
610         memset(&data, 0, sizeof(data));
611         data.data = buffer;
612         data.ulen = MAXPATHLEN + CNID_HEADER_LEN +1;
613         data.flags = DB_DBT_USERMEM;
614         countp = 0;
615
616         while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
617                 memcpy ( &id, key.data, sizeof(id));
618                 id = ntohl (id);
619                 if ( id < CNID_START ) {
620                         LOG(log_error, logtype_default, "cnid_rebuild: Dropping invalid entry with id: %u", id);
621                         continue;
622                 }
623                         
624                 maxcnid = MAX( id, maxcnid);
625
626                 /* check for duplicate cnid */
627
628                 if ((rc = tmpdb->get(tmpdb, NULL, &key, &dupdata, 0))) {
629                         if ( rc != DB_NOTFOUND ) {
630                                 LOG(log_error, logtype_default, "cnid_rebuild: Error getting entry from cnid: %s",
631                                         db_strerror(rc));
632                                 goto abort;
633                         }
634                 }
635                 else /* duplicate cnid */
636                 {
637                         LOG(log_error, logtype_default, "cnid_rebuild: Dropping duplicate file entry: %u", id);
638                         continue;
639                 }
640
641                 
642                 /* dev/ino database */
643                 altkey.data = data.data;
644                 altkey.size = CNID_DEVINO_LEN;
645                 altdata.data = key.data;
646                 altdata.size = key.size;
647                 if ((rc = db->db_devino->put(db->db_devino, NULL, &altkey, &altdata, DB_NOOVERWRITE))) {
648                         if ( rc != DB_KEYEXIST) {
649                                 LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry %u to dev/ino: %s",
650                                 id, db_strerror(rc));
651                                 goto abort;
652                         }
653
654                         /* handle duplicate (file with 2 ids) */
655
656                         dupkey.data = data.data;
657                         dupkey.size = CNID_DEVINO_LEN;
658                         if (( rc = db->db_devino->get(db->db_devino, NULL, &dupkey, &dupdata, 0))) {
659                                 LOG(log_error, logtype_default, "cnid_rebuild: Error getting entry from did/name: %s",
660                                         db_strerror(rc));
661                                 goto abort;
662                         }
663                         memcpy ( &dupid, dupdata.data, sizeof(id));
664                         dupid = ntohl (dupid);
665                         LOG(log_error, logtype_default, "cnid_rebuild: Dropping duplicate file entry: %u", MIN(id,dupid));
666                         
667                         /* Use the entry with a higher cnid */
668                         if ( id < dupid)
669                                 continue;       
670                         else
671                                 if ((rc = db->db_devino->put(db->db_devino, NULL, &altkey, &altdata, 0))) {
672                                         LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry %u to dev/ino: %s",
673                                         id, db_strerror(rc));
674                                         goto abort;
675                                 }
676                 }
677
678                 /* did/name database */
679                 altkey.data = (char *) data.data + CNID_DEVINO_LEN;
680                 altkey.size = data.size - CNID_DEVINO_LEN;
681                 if ((rc = db->db_didname->put(db->db_didname, NULL, &altkey, &altdata, 0))) {
682                         LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry to did/name: %s",
683                                 db_strerror(rc));
684                         goto abort;
685                 }
686
687                 /* recover database */
688
689                 if ((rc = tmpdb->put(tmpdb, NULL, &key, &data, 0))) {
690                         LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry to recoverdb: %s",
691                                 db_strerror(rc));
692                         goto abort;
693                 }
694                 countp++;
695         }
696
697         /* set ROOTINFO to maxcnid */
698         maxcnid = htonl(maxcnid);
699         key.data = ROOTINFO_KEY;
700         key.size = ROOTINFO_KEYLEN;
701         data.data = &maxcnid;
702         data.size = sizeof(maxcnid);
703
704         if ((rc = db->db_didname->put(db->db_didname, NULL, &key, &data, 0))) {
705                 LOG (log_error, logtype_default, "cnid_rebuild: Failed to update ROOTINFO %s", db_strerror(rc));
706                 goto abort;
707         }
708
709         /* delete cnid.db and rename cniddb.recover to cnid.db */
710
711         cursor->c_close(cursor);
712         db->db_cnid->close(db->db_cnid, 0);
713
714         if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
715                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to create database: %s", db_strerror(rc));
716                 goto fail;
717         }
718         if ((rc = db->db_cnid->remove(db->db_cnid, DBCNID, NULL, 0)) != 0) {
719                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to remove database: %s", db_strerror(rc));
720                 db->db_cnid = NULL;
721                 goto fail;
722         }
723
724         if ((rc = tmpdb->close(tmpdb, 0)) != 0) {
725                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to close database: %s", db_strerror(rc));
726                 goto fail;
727         }
728         if ((rc = db_create(&tmpdb, db->dbenv, 0)) != 0) {
729                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to create database: %s", db_strerror(rc));
730                 goto fail;
731         }
732         if ((rc = tmpdb->rename(tmpdb, DBRECOVERFILE, NULL, DBCNID, 0)) != 0) {
733                 LOG(log_error, logtype_default, "cnid_rebuild: Rename database failed: %s", db_strerror(rc));
734                 goto fail;
735         }
736
737         if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
738                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to recreate cnid database: %s", db_strerror(rc));
739                 goto fail;
740         }
741
742         if ((rc = my_open(db->db_cnid, DBCNID, NULL, DB_HASH, 0, 0666 & ~mask))) {
743                 LOG(log_error, logtype_default, "cnid_rebuild: Failed to open cnid database: %s", db_strerror(rc));
744                 goto fail;
745         }
746
747         db->db_devino->sync( db->db_devino, 0);
748         db->db_didname->sync( db->db_didname, 0);
749         db->db_cnid->sync( db->db_cnid, 0);
750         
751         LOG (log_info, logtype_default, "cnid_rebuild: Recovered %u entries, database rebuild complete", countp);
752         return 1;
753
754 abort:
755         cursor->c_close(cursor);
756 fail:
757         LOG (log_error, logtype_default, "cnid_rebuild: Database rebuild failed");
758         return CNID_INVALID;
759 #else
760         return CNID_INVALID;
761 #endif
762
763 }
764
765 #endif /* CNID_DB */