Preserve retryable media cleanup and validate served paths

This commit is contained in:
evgeny committed 2026-10-22 14:41:45 +02:00
1 parent 889dfc411a
commit 113f0259ea
2 files changed
+75 -47

No files matched your search

+8 -14
View File
@@ -1,6 +1,8 @@
#include "media_delivery.h"
#include "media_delivery_proto.h"
#include "media_download.h"
#include "media_index.h"
#include "../../lib/file_io.h"
#include "../utun_instance.h"
#include "../routing_layer/etcp_router.h"
#include "../routing_layer/topo_group.h"
@@ -994,21 +996,13 @@ static void md_handle_block_req(struct media_delivery_ctx* md, uint64_t from_nod
int64_t block_length = block_end - block_start;
/* find media_base */
char media_base[512] = "/tmp/utun_media";
{
sqlite3_stmt* ms = NULL;
sqlite3_prepare_v2(db, "SELECT value FROM ui_state WHERE key='media_base'", -1, &ms, NULL);
if (ms && sqlite3_step(ms) == SQLITE_ROW) {
const char* mb = (const char*)sqlite3_column_text(ms, 0);
if (mb) snprintf(media_base, sizeof(media_base), "%s", mb);
}
if (ms) sqlite3_finalize(ms);
char media_base[MEDIA_BASE_MAX], path[MEDIA_PATH_MAX];
if (!loc || sqlite3_column_bytes(st, 0) != (int)strlen(loc) || media_index_get_base(db, media_base)
|| media_index_path(media_base, loc, path)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "%s: invalid stored media path", MD_ID);
sqlite3_finalize(st); md->active_streams--; return;
}
(void)media_base;
char path[2048];
snprintf(path, sizeof(path), "%s/%s", media_base, loc);
FILE* f = fopen(path, "rb");
FILE* f = file_io_open_read(path);
sqlite3_finalize(st);
if (!f) {
+67 -33
View File
@@ -216,43 +216,77 @@ int media_calc_block_size(uint64_t file_size) {
return (int)target;
}
/* Удаление regular/link без перехода через конечную ссылку; каталоги не удаляются. */
static int mi_remove(const char* path) {
struct file_io_info info;
int found = file_io_inspect(path, 0, &info);
if (found == 1) return 0;
if (found || (info.type != FILE_IO_REGULAR && info.type != FILE_IO_LINK)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index cannot remove non-file path=%s", path); return -1;
}
#ifdef _WIN32
wchar_t wide[FILE_IO_PATH_MAX];
if (file_io_wide(path, wide)) return -1;
int rc = _wremove(wide);
#else
int rc = remove(path);
#endif
if (!rc || errno == ENOENT) return 0;
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index remove failed path=%s errno=%d: %s", path, errno, strerror(errno));
return -1;
}
int media_index_delete_channel(sqlite3* db, const char* chat_id, const char* media_base) {
if (!db || !chat_id || !chat_id[0] || !media_base || !media_base[0]) return -1;
char base[512];
size_t base_len = strlen(media_base);
while (base_len > 0 && media_base[base_len - 1] == '/') base_len--;
snprintf(base, sizeof(base), "%.*s", (int)base_len, media_base);
int files = 0, removed = 0;
if (!db || !chat_id || !*chat_id || !sqlite3_get_autocommit(db)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index delete invalid args/external transaction"); return -1;
}
struct mi_delete_file { char location[MEDIA_PATH_MAX]; char path[MEDIA_PATH_MAX]; int shared; };
struct mi_delete_file* files = NULL; uint32_t count = 0;
sqlite3_stmt* st = NULL;
if (sqlite3_prepare_v2(db,
"SELECT DISTINCT location FROM media_files WHERE chat_id=? AND location!=''",
-1, &st, NULL) == SQLITE_OK) {
sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
while (sqlite3_step(st) == SQLITE_ROW) {
const char* loc = (const char*)sqlite3_column_text(st, 0);
if (!loc || !loc[0]) continue;
files++;
char full[1024];
snprintf(full, sizeof(full), "%s/%s", base, loc);
if (remove(full) == 0) removed++;
else DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "media_index: delete_channel remove failed: %s", full);
}
sqlite3_finalize(st);
const char* query = "SELECT DISTINCT location,EXISTS(SELECT 1 FROM media_files other"
" WHERE other.location=mine.location AND other.chat_id!=mine.chat_id)"
" FROM media_files mine WHERE chat_id=?";
int rc = sqlite3_prepare_v2(db, query, -1, &st, NULL);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
if (rc != SQLITE_OK) goto failed;
/* Полный снимок и проверка всех путей до первого необратимого удаления. */
while ((rc = sqlite3_step(st)) == SQLITE_ROW) {
const char* location = (const char*)sqlite3_column_text(st, 0);
char path[MEDIA_PATH_MAX];
if (!location || sqlite3_column_bytes(st, 0) != (int)strlen(location)
|| media_index_path(media_base, location, path) || count >= UINT32_MAX / sizeof(*files) - 1) goto failed;
struct mi_delete_file* grown = u_realloc(files, (count + 1) * sizeof(*files));
if (!grown) goto failed;
files = grown;
snprintf(files[count].location, sizeof(files[count].location), "%s", location);
memcpy(files[count].path, path, strlen(path) + 1);
files[count].shared = sqlite3_column_int(st, 1); count++;
}
int rows = 0;
sqlite3_stmt* del = NULL;
if (sqlite3_prepare_v2(db, "DELETE FROM media_files WHERE chat_id=?", -1, &del, NULL) == SQLITE_OK) {
sqlite3_bind_text(del, 1, chat_id, -1, SQLITE_STATIC);
if (sqlite3_step(del) == SQLITE_DONE) rows = sqlite3_changes(db);
sqlite3_finalize(del);
if (rc != SQLITE_DONE) goto failed;
rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto failed;
rc = sqlite3_prepare_v2(db, "DELETE FROM media_files WHERE chat_id=? AND location=?", -1, &st, NULL);
if (rc != SQLITE_OK) goto failed;
int errors = 0, rows = 0;
for (uint32_t i = 0; i < count; i++) {
if (!files[i].shared && mi_remove(files[i].path)) { errors++; continue; }
rc = sqlite3_bind_text(st, 1, chat_id, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_bind_text(st, 2, files[i].location, -1, SQLITE_STATIC);
if (rc == SQLITE_OK) rc = sqlite3_step(st);
if (rc != SQLITE_DONE) goto failed;
rows += sqlite3_changes(db);
rc = sqlite3_reset(st);
if (rc != SQLITE_OK) goto failed;
}
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "media_index: delete_channel chat=%s files=%d removed=%d rows=%d",
chat_id, files, removed, rows);
return 0;
rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto failed;
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "media_index delete chat=%s paths=%u rows=%d failures=%d", chat_id, count, rows, errors);
u_free(files); return errors ? -1 : 0;
failed:
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index incomplete delete chat=%s rc=%d paths=%u: %s",
chat_id, rc, count, sqlite3_errmsg(db));
if (st) sqlite3_finalize(st);
u_free(files); return -1;
}
int media_index_generate_uuid(uint8_t uuid_out[16]) {