+/*-------------------------*/
+
+static int cnid_recover(char *path, mode_t mask)
+{
+ DB_ENV * dbenv;
+ int open_flag = 0, rc = 0;
+
+ if ( !path || strlen(path) == 0)
+ return -1;
+
+ LOG (log_info, logtype_default, "cnid_open: running recover on %s", path);
+
+ /* Remove any existing environment */
+
+ if ((rc = db_env_create(&dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_recover: db_env_create: %s", db_strerror(rc));
+ return rc;
+ }
+ if ((rc = dbenv->remove(dbenv, path, DB_FORCE)) != 0) {
+ LOG(log_error, logtype_default, "cnid_recover db_env_remove: %s", db_strerror(rc));
+ return rc;
+ }
+
+ /* Create and open recover environment */
+
+ if ((rc = db_env_create(&dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_recover: db_env_create: %s", db_strerror(rc));
+ return rc;
+ }
+
+ open_flag = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE;
+ if ((rc = dbenv->open(dbenv, path, open_flag, 0666 & ~mask)) != 0) {
+ LOG (log_error, logtype_default, "cnid_recover: RECOVERY failed with %s", db_strerror(rc));
+ return rc;
+ }
+
+ if ((rc = dbenv->close(dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_recover: dbenv->close: %s", db_strerror(rc));
+ return rc;
+ }
+
+ /* Remove recover environment */
+
+ if ((rc = db_env_create(&dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_recover: db_env_create: %s", db_strerror(rc));
+ return rc;
+ }
+
+ if ((rc = dbenv->remove(dbenv, path, DB_FORCE)) != 0) {
+ LOG(log_error, logtype_default, "cnid_recover: db_env_remove: %s", db_strerror(rc));
+ return rc;
+ }
+
+ return 0;
+}
+
+
+static cnid_t cnid_rebuild (CNID_private *db, mode_t mask)
+{
+
+#ifdef CNID_DB_CDB
+ DB *tmpdb;
+ DBC *cursor;
+ DBT key, data, altkey, altdata, dupkey, dupdata;
+ int rc;
+ cnid_t id, dupid, maxcnid = CNID_START;
+ u_int32_t countp;
+ u_int32_t version;
+
+ static char buffer[MAXPATHLEN + CNID_HEADER_LEN + 1];
+ LOG(log_info, logtype_default, "starting rebuild of databases");
+
+ if ((rc = db->db_cnid->cursor(db->db_cnid, NULL, &cursor, DB_WRITECURSOR) ) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Unable to get a cursor: %s", db_strerror(rc));
+ return CNID_INVALID;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ memset(&altkey, 0, sizeof(key));
+ memset(&altdata, 0, sizeof(data));
+ memset(&dupdata, 0, sizeof(dupdata));
+ memset(&dupkey, 0, sizeof(dupkey));
+
+ /* close didname and devino, then recreate them */
+ if (db->db_didname) db->db_didname->close(db->db_didname, 0);
+ if (db->db_devino) db->db_devino->close(db->db_devino, 0);
+
+ if ((rc = db_create(&db->db_didname, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to recreate did/name database: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ if ((rc = my_open(db->db_didname, DBDIDNAME, NULL, DB_HASH, DB_CREATE|DB_TRUNCATE, 0666 & ~mask))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to open did/name database: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ if ((rc = db_create(&db->db_devino, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to recreate dev/ino database: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ if ((rc = my_open(db->db_devino, DBDEVINO, NULL, DB_HASH, DB_CREATE|DB_TRUNCATE, 0666 & ~mask))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to open dev/ino database: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ db->db_devino->sync( db->db_devino, 0);
+ db->db_didname->sync( db->db_didname, 0);
+
+
+ /* now create the temporary cnid database */
+ if ((rc = db_create(&tmpdb, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to create tmp database: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ if ((rc = my_open(tmpdb, DBRECOVERFILE, NULL, DB_HASH, DB_CREATE|DB_TRUNCATE, 0666 & ~mask))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to open recover database: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ /* add rootinfo and version to didname */
+ id = 0;
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+ data.data = &id;
+ data.size = sizeof(id);
+
+ if ((rc = db->db_didname->put(db->db_didname, NULL, &key, &data, 0))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error adding ROOTINFO: %s", db_strerror(rc));
+ goto abort;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = DBVERSION_KEY;
+ key.size = DBVERSION_KEYLEN;
+ version = htonl(DBVERSION);
+ data.data = &version;
+ data.size = sizeof(version);
+
+ if ((rc = db->db_didname->put(db->db_didname, NULL, &key, &data, 0))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error putting version: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ /* now recover the databases from cnid.db*/
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ data.data = buffer;
+ data.ulen = MAXPATHLEN + CNID_HEADER_LEN +1;
+ data.flags = DB_DBT_USERMEM;
+ countp = 0;
+
+ while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
+ memcpy ( &id, key.data, sizeof(id));
+ id = ntohl (id);
+ if ( id < CNID_START ) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Dropping invalid entry with id: %u", id);
+ continue;
+ }
+
+ maxcnid = MAX( id, maxcnid);
+
+ /* check for duplicate cnid */
+
+ if ((rc = tmpdb->get(tmpdb, NULL, &key, &dupdata, 0))) {
+ if ( rc != DB_NOTFOUND ) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error getting entry from cnid: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+ }
+ else /* duplicate cnid */
+ {
+ LOG(log_error, logtype_default, "cnid_rebuild: Dropping duplicate file entry: %u", id);
+ continue;
+ }
+
+
+ /* dev/ino database */
+ altkey.data = data.data;
+ altkey.size = CNID_DEVINO_LEN;
+ altdata.data = key.data;
+ altdata.size = key.size;
+ if ((rc = db->db_devino->put(db->db_devino, NULL, &altkey, &altdata, DB_NOOVERWRITE))) {
+ if ( rc != DB_KEYEXIST) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry %u to dev/ino: %s",
+ id, db_strerror(rc));
+ goto abort;
+ }
+
+ /* handle duplicate (file with 2 ids) */
+
+ dupkey.data = data.data;
+ dupkey.size = CNID_DEVINO_LEN;
+ if (( rc = db->db_devino->get(db->db_devino, NULL, &dupkey, &dupdata, 0))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error getting entry from did/name: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+ memcpy ( &dupid, dupdata.data, sizeof(id));
+ dupid = ntohl (dupid);
+ LOG(log_error, logtype_default, "cnid_rebuild: Dropping duplicate file entry: %u", MIN(id,dupid));
+
+ /* Use the entry with a higher cnid */
+ if ( id < dupid)
+ continue;
+ else
+ if ((rc = db->db_devino->put(db->db_devino, NULL, &altkey, &altdata, 0))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry %u to dev/ino: %s",
+ id, db_strerror(rc));
+ goto abort;
+ }
+ }
+
+ /* did/name database */
+ altkey.data = (char *) data.data + CNID_DEVINO_LEN;
+ altkey.size = data.size - CNID_DEVINO_LEN;
+ if ((rc = db->db_didname->put(db->db_didname, NULL, &altkey, &altdata, 0))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry to did/name: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+
+ /* recover database */
+
+ if ((rc = tmpdb->put(tmpdb, NULL, &key, &data, 0))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Error adding entry to recoverdb: %s",
+ db_strerror(rc));
+ goto abort;
+ }
+ countp++;
+ }
+
+ /* set ROOTINFO to maxcnid */
+ maxcnid = htonl(maxcnid);
+ key.data = ROOTINFO_KEY;
+ key.size = ROOTINFO_KEYLEN;
+ data.data = &maxcnid;
+ data.size = sizeof(maxcnid);
+
+ if ((rc = db->db_didname->put(db->db_didname, NULL, &key, &data, 0))) {
+ LOG (log_error, logtype_default, "cnid_rebuild: Failed to update ROOTINFO %s", db_strerror(rc));
+ goto abort;
+ }
+
+ /* delete cnid.db and rename cniddb.recover to cnid.db */
+
+ cursor->c_close(cursor);
+ db->db_cnid->close(db->db_cnid, 0);
+
+ if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to create database: %s", db_strerror(rc));
+ goto fail;
+ }
+ if ((rc = db->db_cnid->remove(db->db_cnid, DBCNID, NULL, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to remove database: %s", db_strerror(rc));
+ db->db_cnid = NULL;
+ goto fail;
+ }
+
+ if ((rc = tmpdb->close(tmpdb, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to close database: %s", db_strerror(rc));
+ goto fail;
+ }
+ if ((rc = db_create(&tmpdb, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to create database: %s", db_strerror(rc));
+ goto fail;
+ }
+ if ((rc = tmpdb->rename(tmpdb, DBRECOVERFILE, NULL, DBCNID, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Rename database failed: %s", db_strerror(rc));
+ goto fail;
+ }
+
+ if ((rc = db_create(&db->db_cnid, db->dbenv, 0)) != 0) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to recreate cnid database: %s", db_strerror(rc));
+ goto fail;
+ }
+
+ if ((rc = my_open(db->db_cnid, DBCNID, NULL, DB_HASH, 0, 0666 & ~mask))) {
+ LOG(log_error, logtype_default, "cnid_rebuild: Failed to open cnid database: %s", db_strerror(rc));
+ goto fail;
+ }
+
+ db->db_devino->sync( db->db_devino, 0);
+ db->db_didname->sync( db->db_didname, 0);
+ db->db_cnid->sync( db->db_cnid, 0);
+
+ LOG (log_info, logtype_default, "cnid_rebuild: Recovered %u entries, database rebuild complete", countp);
+ return 1;
+
+abort:
+ cursor->c_close(cursor);
+fail:
+ LOG (log_error, logtype_default, "cnid_rebuild: Database rebuild failed");
+ return CNID_INVALID;
+#else
+ return CNID_INVALID;
+#endif
+
+}