From feb327afc7f37ffbbc48d69ac18effa7fc9d9da1 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 18 Oct 2026 10:39:03 +0200 Subject: [PATCH] Preserve durable publication and restore evicted decrypted files --- src/chat/chat_attachment.c | 28 ++++++++++++++++++++++------ src/chat/chat_core_priv.h | 3 +++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/chat/chat_attachment.c b/src/chat/chat_attachment.c index 6824eb5a..57ecca14 100644 --- a/src/chat/chat_attachment.c +++ b/src/chat/chat_attachment.c @@ -123,12 +123,10 @@ static void ak_file_work(void* arg) { if (ak_parent(job->plain) || ak_parent(job->cipher)) return; snprintf(job->temporary, sizeof(job->temporary), "%s.part", job->encrypt ? job->cipher : job->plain); if (job->encrypt) { - if (ma_copy_file(job->source, job->plain) || encrypted_file_sync(job->plain) || + if (ma_copy_file(job->source, job->plain) || encrypted_file_sync(job->plain) || encrypted_file_sync_directory(job->plain) || encrypted_file_write(job->plain, job->temporary, job->key, job->author, job->id, &job->info, job->type)) return; } else if (encrypted_file_read(job->cipher, job->temporary, job->key, job->author, job->id, job->hash, &job->info, job->type)) return; - if (rename(job->temporary, job->encrypt ? job->cipher : job->plain)) { - DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: publish failed: %s", strerror(errno)); return; - } + if (encrypted_file_publish(job->temporary, job->encrypt ? job->cipher : job->plain)) return; job->error = 0; } @@ -182,7 +180,10 @@ static void ak_file_done(void* arg, int error) { if (remove(job->plain) && errno != ENOENT) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: deleted channel plaintext cleanup failed"); if (job->encrypt && remove(job->cipher) && errno != ENOENT) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment keys: deleted channel ciphertext cleanup failed"); } - if (valid) ak_changed(inst, job->channel); + if (valid) { + if (success && !job->encrypt) chat_media_cache_enforce(inst); + ak_changed(inst, job->channel); + } OPENSSL_cleanse(job->key, 32); u_free(job); } @@ -440,6 +441,19 @@ void chat_attachment_decision_trampoline(void* arg) { /* Пользовательский интерес хранится отдельно от фоновой storage-копии. */ void chat_attachment_want(struct UTUN_INSTANCE* inst, const char* channel, const uint8_t message[32]) { + sqlite3_stmt* check = ak_prepare(inst, "SELECT plain_path FROM attachment_keys WHERE channel=? AND message_id=? AND ready=1"); + if (!check) return; + sqlite3_bind_text(check, 1, channel, -1, SQLITE_STATIC); sqlite3_bind_blob(check, 2, message, 32, SQLITE_STATIC); + int result = sqlite3_step(check), missing = result == SQLITE_ROW && ma_file_size((const char*)sqlite3_column_text(check, 0)) < 0; + sqlite3_finalize(check); + if (result != SQLITE_ROW && result != SQLITE_DONE) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: cache lookup failed rc=%d", result); return; } + if (missing) { + sqlite3_stmt* reset = ak_prepare(inst, "UPDATE attachment_keys SET ready=0 WHERE channel=? AND message_id=? AND decrypting=0"); + if (!reset) return; + sqlite3_bind_text(reset, 1, channel, -1, SQLITE_STATIC); sqlite3_bind_blob(reset, 2, message, 32, SQLITE_STATIC); + if (ak_step(inst, reset)) return; + DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "attachment keys: decrypted cache missing, restoring from retained key channel=%s", channel); + } sqlite3_stmt* st = ak_prepare(inst, "UPDATE attachment_keys SET wanted=1,error=0 WHERE channel=? AND message_id=?"); if (!st) return; sqlite3_bind_text(st, 1, channel, -1, SQLITE_STATIC); sqlite3_bind_blob(st, 2, message, 32, SQLITE_STATIC); ak_step(inst, st); @@ -545,6 +559,7 @@ int chat_attachment_init(struct UTUN_INSTANCE* inst) { "command INTEGER NOT NULL,retry_at INTEGER NOT NULL DEFAULT 0,PRIMARY KEY(channel,message_id,peer));" "UPDATE attachment_keys SET decrypting=0; UPDATE attachment_key_outbox SET retry_at=0;")) return -1; CC(inst)->attachment_closing = 0; + CC(inst)->attachment_initialized = 1; if (etcp_router_bind(inst, ETCP_RT_ID_ATTACHMENT_KEYS, ak_receive)) return -1; CC(inst)->attachment_timer = uasync_set_timeout(inst->ua, 10000, inst, ak_tick, "attachment keys"); if (!CC(inst)->attachment_timer) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: initial timer allocation failed"); return -1; } @@ -553,7 +568,7 @@ int chat_attachment_init(struct UTUN_INSTANCE* inst) { /* Сначала снять сеть/таймер, потом дождаться workers с живым chat_core. */ void chat_attachment_quiesce(struct UTUN_INSTANCE* inst) { - if (!CC(inst)) return; + if (!CC(inst) || !CC(inst)->attachment_initialized || CC(inst)->attachment_closing) return; CC(inst)->attachment_closing = 1; if (CC(inst)->attachment_timer) uasync_cancel_timeout(inst->ua, CC(inst)->attachment_timer); CC(inst)->attachment_timer = NULL; @@ -567,6 +582,7 @@ void chat_attachment_destroy(struct UTUN_INSTANCE* inst) { /* Удаление ключей отменяет completion активных workers; они удалят свои результаты. */ void chat_attachment_delete_channel(struct UTUN_INSTANCE* inst, const char* channel) { + if (!CC(inst) || !CC(inst)->attachment_initialized) return; sqlite3_stmt* st = ak_prepare(inst, "SELECT plain_path FROM attachment_keys WHERE channel=?"); if (st) { sqlite3_bind_text(st, 1, channel, -1, SQLITE_STATIC); int rc; diff --git a/src/chat/chat_core_priv.h b/src/chat/chat_core_priv.h index d6994436..819aa918 100644 --- a/src/chat/chat_core_priv.h +++ b/src/chat/chat_core_priv.h @@ -100,6 +100,7 @@ struct chat_core_ctx { int media_channel_backfill_pending; void* attachment_timer; int attachment_closing; + int attachment_initialized; }; /* Per-instance chat context. inst->chat_core обязателен после chat_core_init. */ @@ -122,6 +123,8 @@ void on_msg_inserted(struct DB_SYNC_INSTANCE* si, const uint8_t message_id[32], const char* data, size_t len, uint64_t author, int initial_sync, void* arg); /* Разобрать подписанный manifest канального медиа; result освобождается media_index_result_free. */ int chat_msg_parse_media_body(const char* body, struct media_index_result* result); +/* Учесть также расшифрованные производные файлы и применить общую квоту кеша. */ +void chat_media_cache_enforce(struct UTUN_INSTANCE* inst); /* ── применить настройку group_autoconnect ко всем CHAT-группам (chat_channel.c) ── */