+ // Attempt to determine the underlying size of the object. This may
+ // require a database lookup if the length is not encoded into the object
+ // reference already.
+ int64_t object_size = 0;
+ if (ref.range_is_exact()) {
+ object_size = ref.get_range_length();
+ } else {
+ stmt = Prepare("select size from block_index "
+ "where segmentid = ? and object = ?");
+ sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
+ obj = ref.get_sequence();
+ sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
+ rc = sqlite3_step(stmt);
+ if (rc == SQLITE_ROW) {
+ object_size = sqlite3_column_int64(stmt, 0);
+ } else {
+ fprintf(stderr, "Warning: No block found in block_index for %s\n",
+ ref.to_string().c_str());
+ }
+ sqlite3_finalize(stmt);
+ }
+
+ // Possibly mark additional bytes as being referenced. The number of bytes
+ // referenced can only be increased (up to the object size). The bytes
+ // referenced will be set to the object size only if the entire object is
+ // referenced at once: a series of partial ranges that add up to the total
+ // size will have a reference size capped at just less than the full object
+ // size (we can't tell if some bytes were referenced multiple times, and
+ // thus we conservatively assume some bytes might still be unreferenced).
+ int64_t new_refs;
+ if (ref.has_range()) {
+ new_refs = ref.get_range_length();
+ } else {
+ new_refs = object_size;
+ }
+ int64_t new_size = old_size + new_refs;
+ if (old_size < object_size && new_refs < object_size)
+ new_size = min(new_size, object_size - 1);
+ new_size = min(object_size, new_size);
+ new_size = max(new_size, (int64_t)0);
+
+ if (new_size != old_size) {
+ stmt = Prepare("insert or replace "
+ "into snapshot_refs(segmentid, object, size) "
+ "values (?, ?, ?)");
+ sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
+ obj = ref.get_sequence();
+ sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
+ sqlite3_bind_int64(stmt, 3, new_size);
+
+ rc = sqlite3_step(stmt);
+ if (rc != SQLITE_DONE) {
+ fprintf(stderr, "Could not execute INSERT statement!\n");
+ ReportError(rc);
+ }
+
+ sqlite3_finalize(stmt);
+ }
+}
+
+void LocalDb::SetSegmentMetadata(const std::string &segment,
+ const std::string &path,
+ const std::string &checksum,
+ const std::string &type,
+ int data_size, int disk_size)
+{
+ int rc;
+ sqlite3_stmt *stmt;
+
+ stmt = Prepare("update segments set path = ?, checksum = ?, "
+ "type = ?, data_size = ?, disk_size = ?, "
+ "mtime = coalesce(mtime, julianday('now')) "
+ "where segmentid = ?");
+ sqlite3_bind_text(stmt, 1, path.c_str(), path.size(),
+ SQLITE_TRANSIENT);
+ sqlite3_bind_text(stmt, 2, checksum.c_str(), checksum.size(),
+ SQLITE_TRANSIENT);
+ sqlite3_bind_text(stmt, 3, type.c_str(), type.size(),
+ SQLITE_TRANSIENT);
+ sqlite3_bind_int64(stmt, 4, data_size);
+ sqlite3_bind_int64(stmt, 5, disk_size);
+ sqlite3_bind_int64(stmt, 6, SegmentToId(segment));
+
+ rc = sqlite3_step(stmt);
+ if (rc != SQLITE_DONE) {
+ fprintf(stderr, "Could not update segment checksum in database!\n");
+ ReportError(rc);
+ }
+
+ sqlite3_finalize(stmt);
+}
+
+bool LocalDb::GetSegmentMetadata(const string &segment,
+ string *seg_path,
+ string *seg_checksum)
+{
+ int rc;
+ sqlite3_stmt *stmt;
+ ObjectReference ref;
+ int found = false;
+
+ stmt = Prepare("select path, checksum from segments where segment = ?");
+ sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
+ SQLITE_TRANSIENT);
+
+ rc = sqlite3_step(stmt);
+ if (rc == SQLITE_DONE) {
+ } else if (rc == SQLITE_ROW) {
+ found = true;
+ const char *val;
+
+ val = (const char *)sqlite3_column_text(stmt, 0);
+ if (val == NULL)
+ found = false;
+ else
+ *seg_path = val;
+
+ val = (const char *)sqlite3_column_text(stmt, 1);
+ if (val == NULL)
+ found = false;
+ else
+ *seg_checksum = val;
+ } else {
+ fprintf(stderr, "Could not execute SELECT statement!\n");
+ ReportError(rc);
+ }
+
+ sqlite3_finalize(stmt);
+
+ return found;
+}
+
+/* Look up and return the packed representation of the subblock chunk
+ * signatures. Returns true if signatures were found for the specified object,
+ * and if so sets *buf to point at a buffer of memory (allocated with malloc;
+ * the caller should free it), and *len to the length of the buffer. */
+bool LocalDb::LoadChunkSignatures(ObjectReference ref,
+ void **buf, size_t *len,
+ string *algorithm)
+{
+ int rc;
+ sqlite3_stmt *stmt;
+ int found = false;
+
+ stmt = Prepare("select signatures, algorithm from subblock_signatures "
+ "where blockid = (select blockid from block_index "
+ " where segmentid = ? and object = ?)");
+ sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
+ string obj = ref.get_sequence();
+ sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
+
+ rc = sqlite3_step(stmt);
+ if (rc == SQLITE_DONE) {
+ } else if (rc == SQLITE_ROW) {
+ const void *data = sqlite3_column_blob(stmt, 0);
+ *len = sqlite3_column_bytes(stmt, 0);
+
+ if (*len > 0) {
+ *buf = malloc(*len);
+ if (*buf != NULL) {
+ memcpy(*buf, data, *len);
+ *algorithm = (const char *)sqlite3_column_text(stmt, 1);
+ found = true;
+ }
+ }
+ } else {
+ fprintf(stderr, "Could not execute SELECT statement!\n");
+ ReportError(rc);
+ }
+
+ sqlite3_finalize(stmt);
+
+ return found;
+}
+
+/* Store the subblock chunk signatures for a specified object. The object
+ * itself must have already been indexed in the database. */
+void LocalDb::StoreChunkSignatures(ObjectReference ref,
+ const void *buf, size_t len,
+ const string& algorithm)
+{
+ int rc;
+ sqlite3_stmt *stmt;
+
+ stmt = Prepare("select blockid from block_index "
+ "where segmentid = ? and object = ?");
+ sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));