int db_schema_verify(DB_txn *const txn) { char const magic[] = "DBDB schema layer v1"; size_t const len = sizeof(magic)-1; DB_val key[1]; DB_VAL_STORAGE(key, DB_VARINT_MAX*2); db_bind_uint64(key, DBSchema); db_bind_uint64(key, 0); DB_val val[1]; DB_cursor *cur; int rc = db_txn_cursor(txn, &cur); if(rc < 0) return rc; rc = db_cursor_first(cur, NULL, NULL, +1); if(rc < 0 && DB_NOTFOUND != rc) return rc; // If the database is completely empty // we can assume it's ours to play with if(DB_NOTFOUND == rc) { *val = (DB_val){ len, (char *)magic }; rc = db_put(txn, key, val, 0); if(rc < 0) return rc; return 0; } rc = db_get(txn, key, val); if(DB_NOTFOUND == rc) return DB_VERSION_MISMATCH; if(rc < 0) return rc; if(len != val->size) return DB_VERSION_MISMATCH; if(0 != memcmp(val->data, magic, len)) return DB_VERSION_MISMATCH; return 0; }
int SLNSessionGetFileInfo(SLNSessionRef const session, strarg_t const URI, SLNFileInfo *const info) { DB_env *db = NULL; int rc = SLNSessionDBOpen(session, SLN_RDONLY, &db); if(rc < 0) return rc; DB_txn *txn = NULL; rc = db_txn_begin(db, NULL, DB_RDONLY, &txn); if(rc < 0) { SLNSessionDBClose(session, &db); return rc; } DB_cursor *cursor; rc = db_txn_cursor(txn, &cursor); assert(!rc); DB_range fileIDs[1]; SLNURIAndFileIDRange1(fileIDs, txn, URI); DB_val URIAndFileID_key[1]; rc = db_cursor_firstr(cursor, fileIDs, URIAndFileID_key, NULL, +1); DB_val file_val[1]; if(rc >= 0) { strarg_t URI2; uint64_t fileID; SLNURIAndFileIDKeyUnpack(URIAndFileID_key, txn, &URI2, &fileID); assert(0 == strcmp(URI, URI2)); if(info) { DB_val fileID_key[1]; SLNFileByIDKeyPack(fileID_key, txn, fileID); rc = db_get(txn, fileID_key, file_val); } } if(rc < 0) { db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return rc; } if(info) { // Clear padding for later assert_zeroed. memset(info, 0, sizeof(*info)); strarg_t const internalHash = db_read_string(file_val, txn); strarg_t const type = db_read_string(file_val, txn); uint64_t const size = db_read_uint64(file_val); info->hash = strdup(internalHash); info->path = SLNRepoCopyInternalPath(SLNSessionGetRepo(session), internalHash); info->type = strdup(type); info->size = size; if(!info->hash || !info->path || !info->type) { SLNFileInfoCleanup(info); db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return DB_ENOMEM; } } db_txn_abort(txn); txn = NULL; SLNSessionDBClose(session, &db); return 0; }
uint64_t db_next_id(dbid_t const table, DB_txn *const txn) { DB_cursor *cur = NULL; if(db_txn_cursor(txn, &cur) < 0) return 0; DB_range range[1]; DB_RANGE_STORAGE(range, DB_VARINT_MAX); db_bind_uint64(range->min, table+0); db_bind_uint64(range->max, table+1); DB_val prev[1]; int rc = db_cursor_firstr(cur, range, prev, NULL, -1); if(DB_NOTFOUND == rc) return 1; if(rc < 0) return 0; uint64_t const t = db_read_uint64(prev); assert(table == t); return db_read_uint64(prev)+1; }
int SLNFilterSeekToPosition(SLNFilterRef const filter, SLNFilterPosition const *const pos, DB_txn *const txn) { if(!pos->URI) { SLNFilterSeek(filter, pos->dir, pos->sortID, pos->fileID); if(valid(pos->fileID)) SLNFilterStep(filter, pos->dir); return 0; } DB_cursor *cursor = NULL; int rc = db_txn_cursor(txn, &cursor); if(rc < 0) return rc; DB_range range[1]; DB_val key[1]; SLNURIAndFileIDRange1(range, txn, pos->URI); rc = db_cursor_firstr(cursor, range, key, NULL, +1); if(rc < 0) return rc; // Test that this URI gives us a unique, unambiguous position. // This is guaranteed for the internal hash and effectively // guaranteed for other cryptographic hashes, but may not be // true for other hash algorithms. rc = db_cursor_nextr(cursor, range, NULL, NULL, +1); if(rc >= 0) return DB_KEYEXIST; if(DB_NOTFOUND != rc) return rc; strarg_t u; uint64_t fileID; SLNURIAndFileIDKeyUnpack(key, txn, &u, &fileID); assert(0 == strcmp(pos->URI, u)); SLNAgeRange const ages = SLNFilterFullAge(filter, fileID); if(!valid(ages.min) || ages.min > ages.max) return DB_NOTFOUND; uint64_t const sortID = ages.min; SLNFilterSeek(filter, pos->dir, sortID, fileID); SLNFilterStep(filter, pos->dir); // Start just before/after the URI. // TODO: Stepping is almost assuredly wrong if the URI doesn't match // the filter. We should check if our seek was a direct hit, and // only step if it was. return 0; }
static uint64_t add_metafile(DB_txn *const txn, uint64_t const fileID, strarg_t const targetURI) { uint64_t const metaFileID = fileID; uint64_t const latestMetaFileID = db_next_id(SLNMetaFileByID, txn); if(metaFileID < latestMetaFileID) return 0; // If it's not a new file, then it's not a new meta-file. // Note that ordinary files can't be "promoted" to meta-files later // because that would break the ordering. DB_val null = { 0, NULL }; DB_cursor *cursor = NULL; int rc = db_txn_cursor(txn, &cursor); assert(rc >= 0); DB_val metaFileID_key[1]; SLNMetaFileByIDKeyPack(metaFileID_key, txn, metaFileID); DB_val metaFile_val[1]; SLNMetaFileByIDValPack(metaFile_val, txn, fileID, targetURI); rc = db_put(txn, metaFileID_key, metaFile_val, DB_NOOVERWRITE_FAST); assert(rc >= 0); DB_range alts[1]; SLNTargetURIAndMetaFileIDRange1(alts, txn, targetURI); rc = db_cursor_firstr(cursor, alts, NULL, NULL, +1); assert(rc >= 0 || DB_NOTFOUND == rc); if(DB_NOTFOUND == rc) { DB_val unique[1]; SLNFirstUniqueMetaFileIDKeyPack(unique, txn, metaFileID); rc = db_put(txn, unique, &null, DB_NOOVERWRITE_FAST); assert(rc >= 0); } DB_val targetURI_key[1]; SLNTargetURIAndMetaFileIDKeyPack(targetURI_key, txn, targetURI, metaFileID); rc = db_put(txn, targetURI_key, &null, DB_NOOVERWRITE_FAST); assert(rc >= 0); return metaFileID; }
int SLNSessionGetSubmittedFile(SLNSessionRef const session, DB_txn *const txn, strarg_t const URI) { uint64_t const sessionID = SLNSessionGetID(session); DB_cursor *cursor = NULL; int rc = db_txn_cursor(txn, &cursor); if(rc < 0) return rc; DB_range range[1]; DB_val filekey[1]; SLNURIAndFileIDRange1(range, txn, URI); rc = db_cursor_firstr(cursor, range, filekey, NULL, +1); if(rc < 0) return rc; for(; rc >= 0; rc = db_cursor_nextr(cursor, range, filekey, NULL, +1)) { strarg_t u; uint64_t fileID; SLNURIAndFileIDKeyUnpack(filekey, txn, &u, &fileID); DB_val sessionkey[1]; SLNFileIDAndSessionIDKeyPack(sessionkey, txn, fileID, sessionID); rc = db_cursor_seek(cursor, sessionkey, NULL, 0); if(DB_NOTFOUND == rc) continue; return rc; } return SLN_NOSESSION; }
int SLNSessionSetSubmittedFile(SLNSessionRef const session, DB_txn *const txn, strarg_t const URI) { uint64_t const sessionID = SLNSessionGetID(session); DB_cursor *cursor = NULL; int rc = db_txn_cursor(txn, &cursor); if(rc < 0) return rc; DB_range range[1]; DB_val filekey[1]; SLNURIAndFileIDRange1(range, txn, URI); rc = db_cursor_firstr(cursor, range, filekey, NULL, +1); if(rc < 0) return rc; strarg_t u; uint64_t fileID; SLNURIAndFileIDKeyUnpack(filekey, txn, &u, &fileID); DB_val sessionkey[1], null[1]; SLNFileIDAndSessionIDKeyPack(sessionkey, txn, fileID, sessionID); db_nullval(null); rc = db_put(txn, sessionkey, null, 0); if(rc < 0) return rc; return rc; }
int db_get(DB_txn *const txn, DB_val *const key, DB_val *const data) { DB_cursor *cursor; int rc = db_txn_cursor(txn, &cursor); if(rc < 0) return rc; return db_cursor_seek(cursor, key, data, 0); }
int db_put(DB_txn *const txn, DB_val *const key, DB_val *const data, unsigned const flags) { DB_cursor *cursor; int rc = db_txn_cursor(txn, &cursor); if(rc < 0) return rc; return db_cursor_put(cursor, key, data, flags); }