Propagate RNG errors and preserve 64-bit media sizes through callers
This commit is contained in:
1 parent
f399ded5ba
commit
0e418c7fa9
3 files changed
+89
-60
No files matched your search
+21
-9
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 освобождает сам модуль.
|
||||
|
||||
Reference in new issue
Block a user