+#endif /* DB_VERSION_MINOR > 1 */
+
+ /* Open the database environment. */
+ if ((rc = db->dbenv->open(db->dbenv, path, DBOPTIONS | DBEXTRAS, 0666)) != 0) {
+ if (rc == DB_RUNRECOVERY) {
+ /* This is the mother of all errors. We _must_ fail here. */
+ LOG(log_error, logtype_default, "cnid_open: CATASTROPHIC ERROR opening database environment %s. Run db_recovery -c immediately", path);
+ goto fail_lock;
+ }
+
+ /* We can't get a full transactional environment, so multi-access
+ * is out of the question. Let's assume a read-only environment,
+ * and try to at least get a shared memory pool. */
+ if ((rc = db->dbenv->open(db->dbenv, path, DB_INIT_MPOOL, 0666)) != 0) {
+ /* Nope, not a MPOOL, either. Last-ditch effort: we'll try to
+ * open the environment with no flags. */
+ if ((rc = db->dbenv->open(db->dbenv, path, 0, 0666)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: dbenv->open of %s failed: %s",
+ path, db_strerror(rc));
+ goto fail_lock;
+ }
+ }
+ db->flags |= CNIDFLAG_DB_RO;
+ open_flag = DB_RDONLY;
+ LOG(log_info, logtype_default, "cnid_open: Obtained read-only database environment %s", path);
+ }
+
+ /* If we have the recovery lock, close the file, remove it, so other
+ * clients can proceed opening the DB environment. */
+ if (rfd > -1) {
+ (void)remove(recover_file);
+ switch(errno) {
+ case 0:
+ case ENOENT:
+ break;
+ default:
+ LOG(log_error, logtype_default, "cnid_open: Unable to remove %s: %s",
+ recover_file, strerror(errno));
+ }
+ close(rfd);
+ rfd = -1;
+ }
+
+ /* did/name reverse mapping. We use a BTree for this one. */
+ if ((rc = db_create(&db->db_didname, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to create did/name database: %s",
+ db_strerror(rc));
+ goto fail_appinit;
+ }
+
+ /*db->db_didname->set_bt_compare(db->db_didname, &compare_unix);*/
+ if ((rc = db->db_didname->open(db->db_didname, DBDIDNAME, NULL,
+ DB_HASH, open_flag, 0666))) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to open did/name database: %s",
+ db_strerror(rc));
+ goto fail_appinit;
+ }
+
+ /* Check for version. This way we can update the database if we need
+ * to change the format in any way. */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = DBVERSION_KEY;
+ key.size = DBVERSION_KEYLEN;
+
+dbversion_retry:
+ if ((rc = txn_begin(db->dbenv, NULL, &tid, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: txn_begin: failed to check db version: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+
+ while ((rc = db->db_didname->get(db->db_didname, tid, &key, &data, DB_RMW))) {
+ int ret;
+ switch (rc) {
+ case DB_LOCK_DEADLOCK:
+ if ((ret = txn_abort(tid)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: txn_abort: %s", db_strerror(ret));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+ goto dbversion_retry;
+ case DB_NOTFOUND:
+ {
+ u_int32_t version = htonl(DBVERSION);
+
+ data.data = &version;
+ data.size = sizeof(version);
+ }
+
+ if ((ret = db->db_didname->put(db->db_didname, tid, &key, &data,
+ DB_NOOVERWRITE))) {
+ if (ret == DB_LOCK_DEADLOCK) {
+ if ((ret = txn_abort(tid)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: txn_abort: %s",
+ db_strerror(ret));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+ goto dbversion_retry;
+ }
+ else if (ret == DB_RUNRECOVERY) {
+ /* At this point, we don't care if the transaction aborts
+ * successfully or not. */
+ txn_abort(tid);
+ LOG(log_error, logtype_default, "cnid_open: Error putting new version: %s",
+ db_strerror(ret));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+ }
+ break; /* while loop */
+ default:
+ txn_abort(tid);
+ LOG(log_error, logtype_default, "cnid_open: Failed to check db version: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+ }
+
+ if ((rc = txn_commit(tid, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to commit db version: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+
+ /* TODO In the future we might check for version number here. */
+#if 0
+ memcpy(&version, data.data, sizeof(version));
+ if (version != ntohl(DBVERSION)) {
+ /* Do stuff here. */
+ }
+#endif /* 0 */
+
+#ifdef EXTENDED_DB
+ /* did/macname (31 character) mapping. Use a BTree for this one. */
+ if ((rc = db_create(&db->db_macname, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to create did/macname database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+
+ db->db_macname->set_bt_compare(db->db_macname, &compare_mac);
+ if ((rc = db->db_macname->open(db->db_macname, DBMACNAME, NULL, DB_BTREE, open_flag, 0666)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to open did/macname database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ goto fail_appinit;
+ }
+
+ /* did/shortname (DOS 8.3) mapping. Use a BTree for this one. */
+ if ((rc = db_create(&db->db_shortname, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to create did/shortname database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ goto fail_appinit;
+ }
+
+ db->db_shortname->set_bt_compare(db->db_shortname, &compare_mac);
+ if ((rc = db->db_shortname->open(db->db_shortname, DBSHORTNAME, NULL, DB_BTREE, open_flag, 0666)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to open did/shortname database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ goto fail_appinit;
+ }
+
+ /* did/longname (Unicode) mapping. Use a BTree for this one. */
+ if ((rc = db_create(&db->db_longname, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to create did/longname database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ goto fail_appinit;
+ }
+
+ db->db_longname->set_bt_compare(db->db_longname, &compare_unicode);
+ if ((rc = db->db_longname->open(db->db_longname, DBLONGNAME, NULL, DB_BTREE, open_flag, 0666)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to open did/longname database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ goto fail_appinit;
+ }
+#endif /* EXTENDED_DB */
+
+ /* dev/ino reverse mapping. Use a hash for this one. */
+ if ((rc = db_create(&db->db_devino, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to create dev/ino database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+#ifdef EXTENDED_DB
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ db->db_longname->close(db->db_longname, 0);
+#endif /* EXTENDED_DB */
+ goto fail_appinit;
+ }
+
+ if ((rc = db->db_devino->open(db->db_devino, DBDEVINO, NULL, DB_HASH, open_flag, 0666)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to open devino database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+#ifdef EXTENDED_DB
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ db->db_longname->close(db->db_longname, 0);
+#endif /* EXTENDED_DB */
+ goto fail_appinit;
+ }
+
+ /* Main CNID database. Use a hash for this one. */
+ if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to create cnid database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+#ifdef EXTENDED_DB
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ db->db_longname->close(db->db_longname, 0);
+#endif /* EXTENDED_DB */
+ db->db_devino->close(db->db_devino, 0);
+ goto fail_appinit;
+ }
+
+
+ if ((rc = db->db_cnid->open(db->db_cnid, DBCNID, NULL, DB_HASH, open_flag, 0666)) != 0) {
+ LOG(log_error, logtype_default, "cnid_open: Failed to open dev/ino database: %s",
+ db_strerror(rc));
+ db->db_didname->close(db->db_didname, 0);
+#ifdef EXTENDED_DB
+ db->db_macname->close(db->db_macname, 0);
+ db->db_shortname->close(db->db_shortname, 0);
+ db->db_longname->close(db->db_longname, 0);
+#endif /* EXTENDED_DB */
+ goto fail_appinit;
+ }
+
+ return db;