diff --git a/src/chat/chat_msg.c b/src/chat/chat_msg.c index 5e86f5f1..51eac980 100644 --- a/src/chat/chat_msg.c +++ b/src/chat/chat_msg.c @@ -1341,10 +1341,8 @@ void chat_media_autodownload_backfill(struct UTUN_INSTANCE* inst) { uint64_t now_ms = (uint64_t)ntp_time_get_seconds(inst) * 1000ULL; uint64_t cutoff_ms = now_ms - (uint64_t)days * 86400ULL * 1000ULL; - char media_base[512]; - const char* last_slash = strrchr(cc->db_path, '/'); - if (last_slash) snprintf(media_base, sizeof(media_base), "%.*s", (int)(last_slash - cc->db_path), cc->db_path); - else snprintf(media_base, sizeof(media_base), "%s", cc->db_path); + char media_base[MEDIA_BASE_MAX]; + if (media_index_get_base(cc->db, media_base)) return; struct bf_list list = {0}; bf_collect(cc, media_base, NULL, &list); @@ -1411,10 +1409,8 @@ void chat_media_autodownload_backfill_channel(struct UTUN_INSTANCE* inst, const uint64_t now_ms = (uint64_t)ntp_time_get_seconds(inst) * 1000ULL; uint64_t cutoff_ms = now_ms - (uint64_t)days * 86400ULL * 1000ULL; - char media_base[512]; - const char* last_slash = strrchr(cc->db_path, '/'); - if (last_slash) snprintf(media_base, sizeof(media_base), "%.*s", (int)(last_slash - cc->db_path), cc->db_path); - else snprintf(media_base, sizeof(media_base), "%s", cc->db_path); + char media_base[MEDIA_BASE_MAX]; + if (media_index_get_base(cc->db, media_base)) return; struct bf_list list = {0}; bf_collect(cc, media_base, ch_id, &list); @@ -1454,12 +1450,6 @@ struct media_msg_file { typedef void (*media_msg_file_cb)(const struct media_msg_file* f, void* arg); -static void chat_media_base(struct chat_core_ctx* cc, char* out, size_t out_sz) { - const char* ls = strrchr(cc->db_path, '/'); - if (ls) snprintf(out, out_sz, "%.*s", (int)(ls - cc->db_path), cc->db_path); - else snprintf(out, out_sz, "%s", cc->db_path); -} - /* Итератор по всем медиасообщениям всех каналов (без фильтра по node_id/времени). */ static void chat_media_foreach_media_message(struct chat_core_ctx* cc, const char* media_base, media_msg_file_cb cb, void* arg) { if (!cc->db || !cb) return; @@ -1604,7 +1594,8 @@ static void cache_file_collect_cb(const struct media_msg_file* f, void* arg) { /* Фактическая сумма размеров файлов, соответствующих сообщениям. */ static uint64_t chat_media_cache_scan_sum(struct chat_core_ctx* cc) { - char media_base[512]; chat_media_base(cc, media_base, sizeof(media_base)); + char media_base[MEDIA_BASE_MAX]; + if (media_index_get_base(cc->db, media_base)) return; struct cache_file_list files = {0}; chat_media_foreach_media_message(cc, media_base, cache_file_collect_cb, &files); uint64_t sum = 0; @@ -1661,7 +1652,8 @@ static void chat_media_cache_cleanup_orphans(struct UTUN_INSTANCE* inst) { struct chat_core_ctx* cc = CC(inst); if (!cc || !cc->initialized || !cc->db || !inst) return; - char media_base[512]; chat_media_base(cc, media_base, sizeof(media_base)); + char media_base[MEDIA_BASE_MAX]; + if (media_index_get_base(cc->db, media_base)) return; char media_dir[600]; snprintf(media_dir, sizeof(media_dir), "%s/media", media_base); struct file_io_info directory; @@ -1671,6 +1663,21 @@ static void chat_media_cache_cleanup_orphans(struct UTUN_INSTANCE* inst) { struct cache_rel_list rels = {0}; chat_media_foreach_media_message(cc, media_base, cache_rel_collect_cb, &rels); + sqlite3_stmt* preparations = NULL; + int prep_rc = sqlite3_prepare_v2(cc->db, "SELECT location FROM media_preparations UNION SELECT location FROM media_files", + -1, &preparations, NULL); + if (prep_rc == SQLITE_OK) { + while ((prep_rc = sqlite3_step(preparations)) == SQLITE_ROW) { + const char* location = (const char*)sqlite3_column_text(preparations, 0); + if (location) cache_rel_add(&rels, location); + } + } + int prep_finish = preparations ? sqlite3_finalize(preparations) : SQLITE_OK; + if (prep_rc != SQLITE_DONE || prep_finish != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media orphan scan cannot protect indexed/preparing files rc=%d", prep_rc); + for (int i = 0; i < rels.count; i++) u_free(rels.items[i]); + u_free(rels.items); return; + } /* Ещё не опубликованные контейнеры и их временные файлы принадлежат crypto worker. */ if (cc->attachment_initialized) { sqlite3_stmt* active = NULL; @@ -1719,7 +1726,8 @@ void chat_media_cache_enforce(struct UTUN_INSTANCE* inst) { return; } - char media_base[512]; chat_media_base(cc, media_base, sizeof(media_base)); + char media_base[MEDIA_BASE_MAX]; + if (media_index_get_base(cc->db, media_base)) return; struct cache_file_list files = {0}; chat_media_foreach_media_message(cc, media_base, cache_file_collect_cb, &files); diff --git a/src/media_delivery/media_index.c b/src/media_delivery/media_index.c index a10d01f3..54833080 100644 --- a/src/media_delivery/media_index.c +++ b/src/media_delivery/media_index.c @@ -103,7 +103,8 @@ int media_index_get_base(sqlite3* db, char base[MEDIA_BASE_MAX]) { sqlite3_stmt* st = NULL; int rc = db && base ? sqlite3_prepare_v2(db, "SELECT value FROM ui_state WHERE key='media_base'", -1, &st, NULL) : SQLITE_MISUSE; if (rc == SQLITE_OK) rc = sqlite3_step(st); - int valid = rc == SQLITE_ROW && !mi_clean_path((const char*)sqlite3_column_text(st, 0), base, MEDIA_BASE_MAX); + const char* value = rc == SQLITE_ROW ? (const char*)sqlite3_column_text(st, 0) : NULL; + int valid = value && sqlite3_column_bytes(st, 0) == (int)strlen(value) && !mi_clean_path(value, base, MEDIA_BASE_MAX); if (st && sqlite3_finalize(st) != SQLITE_OK) valid = 0; if (valid) return 0; DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_index cannot read media_base rc=%d: %s", rc, db ? sqlite3_errmsg(db) : "no DB"); @@ -256,7 +257,8 @@ static int mi_remove(const char* path) { } int media_index_delete_channel(sqlite3* db, const char* chat_id, const char* media_base) { - if (!db || !chat_id || !*chat_id || !sqlite3_get_autocommit(db)) { + char checked_base[MEDIA_BASE_MAX]; + if (!db || !chat_id || !*chat_id || !sqlite3_get_autocommit(db) || mi_clean_path(media_base, checked_base, sizeof(checked_base))) { 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; };