Apply storage policy before cancellation and physical retirement

This commit is contained in:
evgeny committed 2026-11-03 13:00:16 +01:00
1 parent d5f3f74ba5
commit c5c6d96384
1 file changed
+30 -27
+30 -27
View File
@@ -1002,24 +1002,35 @@ static void dm_custody_remove(struct dm_media_state* media, const uint8_t id[16]
media_async_submit(media->inst->media_async, media->inst->ua, dm_file_work, job, dm_file_done, job);
}
/* Подпись B позволяет любому авторизованному S завершить удаление точного артефакта. */
/* Все факты уже проверены и сохранены. Терминальное состояние предшествует отмене I/O. */
static int dm_custody_complete(struct UTUN_INSTANCE* inst, const uint8_t id[16], int policy) {
int done = dm_media_retention_done(inst, id, policy);
if (done <= 0) return done;
sqlite3_stmt* st = dm_media_prepare(inst, policy ?
"UPDATE dm_custody SET ready=-1,retry_at=0,receipt=(SELECT taken FROM dm_file_targets"
" WHERE id=? AND side=1 AND taken IS NOT NULL LIMIT 1) WHERE id=? AND ready>=0" :
"UPDATE dm_custody SET ready=-1,retry_at=0,receipt=(SELECT received FROM dm_file_targets"
" WHERE id=? AND side=1 AND received IS NOT NULL LIMIT 1) WHERE id=? AND ready>=0");
if (!st) return -1;
sqlite3_bind_blob(st, 1, id, 16, SQLITE_STATIC); sqlite3_bind_blob(st, 2, id, 16, SQLITE_STATIC);
if (dm_media_step(inst, st)) return -1;
if (sqlite3_changes(inst->topo_sqlite_db)) DEBUG_INFO(DEBUG_CATEGORY_DM, "DM custody: retention complete delete_on_open=%d", policy);
return 1;
}
/* DELETE переносит факт; удаление определяет сохранённая политика M. */
static void dm_custody_delete(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t source, const uint8_t* r) {
if (dm_media_verify_receipt(inst, r, NULL, 0)) return;
uint64_t author, recipient;
memcpy(&author, r + 24, 8); memcpy(&recipient, r + 32, 8);
/* Для существующей копии точная подпись B разрешает удаление даже после отзыва роли M/S. */
sqlite3_stmt* st = dm_media_prepare(inst, "SELECT body FROM dm_custody WHERE id=?");
sqlite3_stmt* st = dm_media_prepare(inst, "SELECT body,delete_on_open FROM dm_custody WHERE id=?");
if (!st) return;
sqlite3_bind_blob(st, 1, r + 40, 16, SQLITE_STATIC);
int rc = sqlite3_step(st);
if (rc == SQLITE_ROW) {
int valid = !dm_media_verify_receipt(inst, r, sqlite3_column_blob(st, 0), (size_t)sqlite3_column_bytes(st, 0));
int policy = sqlite3_column_int(st, 1);
int valid = !dm_media_receipt_save(inst, r, sqlite3_column_blob(st, 0), (size_t)sqlite3_column_bytes(st, 0));
sqlite3_finalize(st);
if (!valid) return;
st = dm_media_prepare(inst, "UPDATE dm_custody SET ready=-1,receipt=?,retry_at=0 WHERE id=?");
if (!st) return;
sqlite3_bind_blob(st, 1, r, DM_MEDIA_RECEIPT_SIZE, SQLITE_STATIC); sqlite3_bind_blob(st, 2, r + 40, 16, SQLITE_STATIC);
if (dm_media_step(inst, st)) return;
if (!valid || dm_custody_complete(inst, r + 40, policy) != 1) return;
file_transfer_cancel_object(inst->file_transfer, r + 40);
dm_custody_remove(inst->dm_media, r + 40);
return;
@@ -1030,20 +1041,11 @@ static void dm_custody_delete(struct UTUN_INSTANCE* inst, uint64_t group, uint64
if (!st) return;
sqlite3_bind_blob(st, 1, r + 40, 16, SQLITE_STATIC);
rc = sqlite3_step(st);
int exact = rc == SQLITE_DONE || (rc == SQLITE_ROW && sqlite3_column_bytes(st, 0) == 32 &&
!memcmp(sqlite3_column_blob(st, 0), r + 56, 32));
int exact = rc == SQLITE_ROW && sqlite3_column_bytes(st, 0) == 32 && !memcmp(sqlite3_column_blob(st, 0), r + 56, 32);
sqlite3_finalize(st);
if (!exact) { DEBUG_WARN(DEBUG_CATEGORY_DM, "dm_media: delete conflicts with retired artifact"); return; }
if (rc == SQLITE_DONE && (!dm_media_scope(inst, group, source, "supernode", author, recipient) ||
!dm_media_scope(inst, group, inst->node_id, "storage", author, recipient))) {
DEBUG_WARN(DEBUG_CATEGORY_DM, "dm_media: unknown DELETE without custody grant source=%llu", (unsigned long long)source); return;
}
st = dm_media_prepare(inst, "INSERT INTO dm_custody_done(id,body_hash,receipt) VALUES(?,?,?)"
" ON CONFLICT(id) DO UPDATE SET receipt=excluded.receipt");
if (!st) return;
sqlite3_bind_blob(st, 1, r + 40, 16, SQLITE_STATIC); sqlite3_bind_blob(st, 2, r + 56, 32, SQLITE_STATIC);
sqlite3_bind_blob(st, 3, r, DM_MEDIA_RECEIPT_SIZE, SQLITE_STATIC);
if (!dm_media_step(inst, st)) dm_media_send(inst, group, source, DM_MEDIA_DELETE_ACK, r + 40, 16);
if (exact) dm_media_send(inst, group, source, DM_MEDIA_DELETE_ACK, r + 40, 16);
else if (rc == SQLITE_DONE) DEBUG_DEBUG(DEBUG_CATEGORY_DM, "DM custody: fact before STORE; S retains it for retry");
else DEBUG_WARN(DEBUG_CATEGORY_DM, "dm_media: delete conflicts with retired artifact rc=%d", rc);
}
/* OFFER не меняет идентичность беседы. Чужой holder допустим только от S. */
@@ -1092,7 +1094,7 @@ static void dm_media_control(struct UTUN_INSTANCE* inst, uint64_t group, uint64_
if (author == inst->node_id) {
dm_media_accept_receipt(inst, p);
} else dm_mailbox_media_recv(dm_mailbox_get_media(inst), group, source, cmd, p, len);
} else if ((cmd == DM_MEDIA_STORED && len == 24) || (cmd == DM_MEDIA_DELETE_ACK && len == 16) ||
} else if (((cmd == DM_MEDIA_STORED || cmd == DM_MEDIA_RESERVED) && len == 25) || (cmd == DM_MEDIA_DELETE_ACK && len == 16) ||
(cmd == DM_MEDIA_EXPIRED && len == 16) || (cmd == DM_MEDIA_REJECT && len == 17)) {
dm_mailbox_media_recv(dm_mailbox_get_media(inst), group, source, cmd, p, len);
if (cmd == DM_MEDIA_EXPIRED) {
@@ -1184,7 +1186,7 @@ static void dm_files_tick(struct dm_media_state* media, uint64_t now) {
static void dm_custody_tick(struct dm_media_state* media, uint64_t now) {
struct UTUN_INSTANCE* inst = media->inst;
for (int i = 0; i < 8; i++) {
sqlite3_stmt* st = dm_media_prepare(inst, "SELECT id,body,author,supernode,group_id,expires,ready FROM dm_custody"
sqlite3_stmt* st = dm_media_prepare(inst, "SELECT id,body,author,supernode,group_id,expires,ready,delete_on_open FROM dm_custody"
" WHERE retry_at<=? ORDER BY retry_at LIMIT 1");
if (!st) return;
sqlite3_bind_int64(st, 1, (sqlite3_int64)now);
@@ -1200,7 +1202,9 @@ static void dm_custody_tick(struct dm_media_state* media, uint64_t now) {
uint64_t author = (uint64_t)sqlite3_column_int64(st, 2), super = (uint64_t)sqlite3_column_int64(st, 3);
uint64_t group = (uint64_t)sqlite3_column_int64(st, 4), expires = (uint64_t)sqlite3_column_int64(st, 5);
int ready = sqlite3_column_int(st, 6);
int policy = sqlite3_column_int(st, 7);
sqlite3_finalize(st);
if (ready >= 0 && dm_custody_complete(inst, id, policy) == 1) ready = -1;
st = dm_media_prepare(inst, "UPDATE dm_custody SET retry_at=?,ready=CASE WHEN expires<=? THEN -1 ELSE ready END WHERE id=?");
if (!st) return;
sqlite3_bind_int64(st, 1, (sqlite3_int64)(now + 5)); sqlite3_bind_int64(st, 2, (sqlite3_int64)now);
@@ -1214,8 +1218,7 @@ static void dm_custody_tick(struct dm_media_state* media, uint64_t now) {
uint64_t gid = group;
if (desc && dm_route_group(inst, author, group, &gid)) dm_file_pull(media, 1, gid, author, desc, author);
} else {
uint8_t stored[24]; memcpy(stored, id, 16); memcpy(stored + 16, &expires, 8);
dm_media_send(inst, group, super, DM_MEDIA_STORED, stored, sizeof(stored));
dm_custody_status(inst, group, super, id);
}
}
}