Propagate RNG errors and preserve 64-bit media sizes through callers

This commit is contained in:
evgeny committed 2026-10-22 10:20:02 +02:00
1 parent f399ded5ba
commit 0e418c7fa9
3 files changed
+89 -60

No files matched your search

+21 -9
View File
@@ -23,6 +23,7 @@
#include "../../lib/mem.h"
#include "../../lib/u_async.h"
#include "../../lib/platform_compat.h"
#include "../../lib/file_io.h"
#include <openssl/sha.h>
#include <stdlib.h>
@@ -358,7 +359,11 @@ void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_
/* лимит одного медиафайла применяется и к локальным отправкам */
uint64_t unit = inst->config->global.chatserver_storage_unit_size;
if (unit > 0) {
int64_t sz = (int64_t)ma_file_size(req->media_src);
int64_t sz = ma_file_size(req->media_src);
if (sz < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media submit source size unavailable path=%s", req->media_src);
chat_event_submitted(inst, req->request_id, 0); return;
}
if (sz > 0 && (uint64_t)sz > unit) {
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "%s: media too large for local send (%lld > %llu bytes), rejected ch=%s",
CC_ID, (long long)sz, (unsigned long long)unit, req->channel_id);
@@ -1153,8 +1158,8 @@ void chat_media_backfill(struct UTUN_INSTANCE* inst) {
char path[1536];
snprintf(path, sizeof(path), "%s/media/%s/%s", media_base, ch_id, final_name);
int fsz = ma_file_size(path);
if (fsz <= 0 || (int64_t)fsz != result.file_size) {
int64_t fsz = ma_file_size(path);
if (fsz <= 0 || fsz != result.file_size) {
u_free(body); media_index_result_free(&result);
continue;
}
@@ -1290,7 +1295,7 @@ static void bf_collect(struct chat_core_ctx* cc, const char* media_base, const c
char full_path[1536];
snprintf(full_path, sizeof(full_path), "%s/media/%s/%s", media_base, ch_id, final_name);
int64_t actual = (int64_t)ma_file_size(full_path);
int64_t actual = ma_file_size(full_path);
if (actual < 0) actual = 0;
if (l->count == l->cap) {
@@ -1588,8 +1593,8 @@ static void cache_rel_collect_cb(const struct media_msg_file* f, void* arg) {
}
static void cache_file_collect_cb(const struct media_msg_file* f, void* arg) {
int fsz = ma_file_size(f->full_path);
if (fsz > 0) cache_file_add((struct cache_file_list*)arg, f->full_path, f->ts_ms, (int64_t)fsz);
int64_t fsz = ma_file_size(f->full_path);
if (fsz > 0) cache_file_add((struct cache_file_list*)arg, f->full_path, f->ts_ms, fsz);
}
/* Фактическая сумма размеров файлов, соответствующих сообщениям. */
@@ -1654,6 +1659,11 @@ static void chat_media_cache_cleanup_orphans(struct UTUN_INSTANCE* inst) {
char media_base[512]; chat_media_base(cc, media_base, sizeof(media_base));
char media_dir[600]; snprintf(media_dir, sizeof(media_dir), "%s/media", media_base);
struct file_io_info directory;
int directory_status = file_io_inspect(media_dir, 0, &directory);
if (directory_status == 1) return; // До первого вложения каталога может не быть.
if (directory_status < 0) return;
struct cache_rel_list rels = {0};
chat_media_foreach_media_message(cc, media_base, cache_rel_collect_cb, &rels);
/* Ещё не опубликованные контейнеры и их временные файлы принадлежат crypto worker. */
@@ -1680,10 +1690,12 @@ static void chat_media_cache_cleanup_orphans(struct UTUN_INSTANCE* inst) {
ow.media_base = media_base; ow.mb_len = strlen(media_base);
ow.rels = rels.items; ow.rel_count = rels.count;
ow.removed = 0; ow.removed_bytes = 0;
ma_dir_walk(media_dir, orphan_walk_cb, &ow);
int walk_result = ma_dir_walk(media_dir, orphan_walk_cb, &ow);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: cache orphan cleanup done removed=%d bytes=%llu",
CC_ID, ow.removed, (unsigned long long)ow.removed_bytes);
if (walk_result) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "%s: cache orphan cleanup incomplete removed=%d bytes=%llu",
CC_ID, ow.removed, (unsigned long long)ow.removed_bytes);
else DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: cache orphan cleanup done removed=%d bytes=%llu",
CC_ID, ow.removed, (unsigned long long)ow.removed_bytes);
for (int i = 0; i < rels.count; i++) u_free(rels.items[i]);
u_free(rels.items);
+67 -50
View File
@@ -4,13 +4,16 @@
#include "../../lib/u_async.h"
#include "../../lib/mem.h"
#include "../../lib/debug_config.h"
#include "../../lib/file_io.h"
#include "../../lib/platform_compat.h"
#include <openssl/evp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#define MEDIA_BLOCK_MIN (10 * 1024 * 1024)
#define MEDIA_BLOCK_MAX (25 * 1024 * 1024)
@@ -105,7 +108,7 @@ int media_index_init(sqlite3* db) {
int media_calc_block_size(uint64_t file_size) {
if (file_size == 0) return 0;
uint64_t target = (file_size + MEDIA_BLOCK_TARGET - 1) / MEDIA_BLOCK_TARGET;
uint64_t target = (file_size - 1) / MEDIA_BLOCK_TARGET + 1;
if (target < MEDIA_BLOCK_MIN) target = MEDIA_BLOCK_MIN;
if (target > MEDIA_BLOCK_MAX) target = MEDIA_BLOCK_MAX;
return (int)target;
@@ -151,8 +154,7 @@ int media_index_delete_channel(sqlite3* db, const char* chat_id, const char* med
}
int media_index_generate_uuid(uint8_t uuid_out[16]) {
ma_uuid(uuid_out);
return 0;
return ma_uuid(uuid_out);
}
void media_index_result_free(struct media_index_result* result) {
@@ -243,54 +245,63 @@ struct mi_reg_ctx {
int err;
};
/* Только полная подготовка допускает commit; любая ошибка оставляет result для обычной очистки done. */
static void mi_reg_work(void* data) {
struct mi_reg_ctx* ctx = (struct mi_reg_ctx*)data;
if (ctx->copy_file && ma_copy_file(ctx->src_path, ctx->dest_path) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index: copy failed %s -> %s", ctx->src_path, ctx->dest_path);
ctx->err = -10; return;
struct mi_reg_ctx* ctx = data;
FILE* file = NULL; uint8_t* buffer = NULL;
const char* phase = "copy";
ctx->err = -10;
if (ctx->copy_file && ma_copy_file(ctx->src_path, ctx->dest_path)) goto done;
phase = "size"; ctx->err = -20;
int64_t size = ma_file_size(ctx->dest_path);
if (size <= 0) goto done;
int64_t block = media_calc_block_size((uint64_t)size);
uint64_t count = (uint64_t)(size / block + (size % block != 0));
phase = "block count"; ctx->err = -21;
if (count > INT_MAX || count > UINT32_MAX / 64) goto done;
ctx->result.file_size = size; ctx->result.block_size = block; ctx->result.num_blocks = (int)count;
phase = "hash"; ctx->err = -30;
if (ma_sha256_file(ctx->dest_path, ctx->result.content_hash)) goto done;
phase = "media UUID"; ctx->err = -31;
if (ma_uuid(ctx->result.media_id)) goto done;
phase = "block arrays"; ctx->err = -40;
ctx->result.block_ids = u_malloc((uint32_t)count * 16);
ctx->result.block_sigs = u_malloc((uint32_t)count * 64);
if (!ctx->result.block_ids || !ctx->result.block_sigs) goto done;
phase = "open"; ctx->err = -50;
file = file_io_open_read(ctx->dest_path);
if (!file) goto done;
for (int n = 0; n < ctx->result.num_blocks; n++) {
int64_t offset = (int64_t)n * block;
size_t length = (size_t)(n == ctx->result.num_blocks - 1 ? size - offset : block);
phase = "block allocation"; ctx->err = -60;
buffer = u_malloc((uint32_t)length);
if (!buffer) goto done;
phase = "seek"; ctx->err = -70;
#ifdef _WIN32
if (_fseeki64(file, offset, SEEK_SET)) goto done;
#else
if ((int64_t)(off_t)offset != offset || fseeko(file, (off_t)offset, SEEK_SET)) goto done;
#endif
phase = "read";
if (fread(buffer, 1, length, file) != length) goto done;
phase = "block UUID"; ctx->err = -81;
if (ma_uuid(ctx->result.block_ids + (size_t)n * 16)) goto done;
phase = "block signature"; ctx->err = -80;
if (ma_sign_block(ctx->ed25519_privkey, buffer, length, ctx->result.block_sigs + (size_t)n * 64)) goto done;
u_free(buffer); buffer = NULL;
}
int64_t fsize = (int64_t)ma_file_size(ctx->dest_path);
if (fsize <= 0) { ctx->err = -20; return; }
ctx->result.file_size = fsize;
int64_t bs = (int64_t)media_calc_block_size((uint64_t)fsize);
int nb = (int)((fsize + bs - 1) / bs);
ctx->result.block_size = bs;
ctx->result.num_blocks = nb;
if (ma_sha256_file(ctx->dest_path, ctx->result.content_hash) != 0) {
ctx->err = -30; return;
}
ma_uuid(ctx->result.media_id);
ctx->result.block_ids = u_malloc((size_t)nb * 16);
ctx->result.block_sigs = u_malloc((size_t)nb * 64);
if (!ctx->result.block_ids || !ctx->result.block_sigs) { ctx->err = -40; return; }
FILE* f = fopen(ctx->dest_path, "rb");
if (!f) { ctx->err = -50; return; }
for (int n = 0; n < nb; n++) {
int64_t off = n * bs;
int64_t sz = (n == nb - 1) ? fsize - off : bs;
uint8_t* buf = u_malloc((size_t)sz);
if (!buf) { fclose(f); ctx->err = -60; return; }
fseeko(f, (off_t)off, SEEK_SET);
size_t rd = fread(buf, 1, (size_t)sz, f);
if (rd != (size_t)sz) { u_free(buf); fclose(f); ctx->err = -70; return; }
ma_uuid(ctx->result.block_ids + n * 16);
if (ma_sign_block(ctx->ed25519_privkey, buf, (size_t)sz,
ctx->result.block_sigs + n * 64) != 0) {
u_free(buf); fclose(f); ctx->err = -80; return;
}
u_free(buf);
}
fclose(f);
phase = "close"; ctx->err = -71;
int close_error = fclose(file); file = NULL;
if (close_error) goto done;
ctx->err = 0;
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "media_index prepared path=%s bytes=%lld blocks=%d",
ctx->dest_path, (long long)size, ctx->result.num_blocks);
done:
if (ctx->err) DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index preparation failed phase=%s path=%s error=%d errno=%d",
phase, ctx->dest_path, ctx->err, errno);
u_free(buffer);
if (file && fclose(file)) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "media_index cleanup close failed path=%s errno=%d", ctx->dest_path, errno);
}
static void mi_reg_done(void* arg, int err) {
@@ -325,8 +336,14 @@ void media_index_register_async(
return;
}
if (!*src_path || !*dest_path || strlen(src_path) >= sizeof(((struct mi_reg_ctx*)0)->src_path)
|| strlen(dest_path) >= sizeof(((struct mi_reg_ctx*)0)->dest_path)
|| strlen(chat_id) >= sizeof(((struct mi_reg_ctx*)0)->chat_id)
|| strlen(media_base) >= sizeof(((struct mi_reg_ctx*)0)->media_base)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index register oversized/empty path"); cb(cb_arg, -1, NULL); return;
}
struct mi_reg_ctx* ctx = u_calloc(1, sizeof(*ctx));
if (!ctx) { cb(cb_arg, -1, NULL); return; }
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index context allocation failed"); cb(cb_arg, -1, NULL); return; }
ctx->ua = ua;
ctx->db = db;
+1 -1
View File
@@ -67,7 +67,7 @@ void media_index_register_async(
// ── Вспомогательные операции ──
// Заполнить 16 байт идентификатора; возвращает 0.
// Заполнить 16 байт идентификатора; 0/-1. При ошибке RNG выход обнулён, использовать его нельзя.
int media_index_generate_uuid(uint8_t uuid_out[16]);
// Освободить только массивы собственного result и обнулить их указатели; callback-result освобождает сам модуль.