Preserve retryable media cleanup and validate served paths
This commit is contained in:
1 parent
889dfc411a
commit
113f0259ea
2 files changed
+75
-47
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
Reference in new issue
Block a user