Report native Android database cleanup failures accurately

This commit is contained in:
evgeny committed 2026-10-27 17:18:13 +01:00
1 parent b8e7b18937
commit 3b42547740
3 files changed
+84 -13

No files matched your search

+47 -1
View File
@@ -2,6 +2,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "../lib/debug_config.h"
#include "../lib/mem.h"
@@ -78,6 +79,47 @@ static int check_bridge_config(void) {
return 0;
}
/* Настоящий unlink: все три файла, повторное удаление, ошибка одного пути и восстановление после неё. */
static int check_database_cleanup(const char* directory) {
REQUIRE(utun_bridge_clear_database(NULL) == -1);
REQUIRE(utun_bridge_clear_database("") == -1);
REQUIRE(!utun_bridge_clear_database(directory));
const char* names[] = {"chats.db", "chats.db-wal", "chats.db-shm"};
char path[1024];
for (unsigned i = 0; i < 3; ++i) {
snprintf(path, sizeof(path), "%s/%s", directory, names[i]);
FILE* file = fopen(path, "wb"); REQUIRE(file);
int written = fputs("fixture", file);
int closed = fclose(file);
REQUIRE(written >= 0 && !closed);
}
REQUIRE(!utun_bridge_clear_database(directory));
for (unsigned i = 0; i < 3; ++i) {
snprintf(path, sizeof(path), "%s/%s", directory, names[i]);
REQUIRE(test_unlink(path) == -1 && errno == ENOENT);
}
REQUIRE(!utun_bridge_clear_database(directory));
snprintf(path, sizeof(path), "%s/chats.db-wal", directory);
REQUIRE(!utun_mkdir(path, 0700));
for (unsigned i = 0; i < 3; i += 2) {
snprintf(path, sizeof(path), "%s/%s", directory, names[i]);
FILE* file = fopen(path, "wb"); REQUIRE(file);
int written = fputs("fixture", file);
int closed = fclose(file);
REQUIRE(written >= 0 && !closed);
}
REQUIRE(utun_bridge_clear_database(directory) == -1);
for (unsigned i = 0; i < 3; i += 2) {
snprintf(path, sizeof(path), "%s/%s", directory, names[i]);
REQUIRE(test_unlink(path) == -1 && errno == ENOENT);
}
snprintf(path, sizeof(path), "%s/chats.db-wal", directory);
REQUIRE(!test_rmdir(path));
REQUIRE(!utun_bridge_clear_database(directory));
printf("[PASS] database cleanup: exact files, missing files, real unlink failure and retry\n");
return 0;
}
/* Один и тот же файл декодируется покадрово и батчами: PCM и количество отсчётов должны совпасть. */
static int check_voice_batches(const char* path, int rate, int channels) {
int frame = rate / 50 * channels, count = frame * 27;
@@ -129,8 +171,12 @@ int main(void) {
/* Проверки bridge требуют исправного владения таблицей провайдера. */
if (!failed) failed += check_bridge_config();
utun_config_set_provider(NULL);
char directory[512] = "/tmp/utun_android_bridge_XXXXXX", path[1024];
char directory[512], path[1024];
const char* temporary = getenv("TMPDIR");
int length = snprintf(directory, sizeof(directory), "%s/utun_android_bridge_XXXXXX", temporary ? temporary : "/tmp");
REQUIRE(length > 0 && length < (int)sizeof(directory));
REQUIRE(!test_mkdtemp(directory));
failed += check_database_cleanup(directory);
snprintf(path, sizeof(path), "%s/voice.opus", directory);
const int rates[] = {8000, 12000, 16000, 24000, 48000};
for (unsigned i = 0; i < sizeof(rates) / sizeof(rates[0]); i++)
@@ -51,7 +51,8 @@
#include <strings.h>
#include <stdarg.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include "../../../lib/platform_compat.h"
#include <sys/stat.h>
#ifdef __ANDROID__
@@ -176,6 +177,35 @@ void utun_bridge_destroy(void) {
bridge_log(BLEV_INFO, "bridge destroy");
}
/* Удалять только после остановки ядра: отсутствие файла нормально, остальные ошибки не скрывать. */
int utun_bridge_clear_database(const char* db_path) {
if (!db_path || !db_path[0]) { bridge_log(BLEV_ERROR, "clearDatabase: no db path"); return -1; }
const char* names[] = {"chats.db", "chats.db-wal", "chats.db-shm"};
size_t length = strlen(db_path);
if (length > SIZE_MAX - sizeof("/chats.db-wal")) {
bridge_log(BLEV_ERROR, "clearDatabase: path length overflow bytes=%zu", length); return -1;
}
size_t capacity = length + sizeof("/chats.db-wal");
char* path = u_malloc(capacity);
if (!path) { bridge_log(BLEV_ERROR, "clearDatabase: path allocation failed bytes=%zu", capacity); return -1; }
unsigned removed = 0, missing = 0, failed = 0;
for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
snprintf(path, capacity, "%s/%s", db_path, names[i]);
if (!unlink(path)) removed++;
else {
int error = errno;
if (error == ENOENT) missing++;
else {
failed++;
bridge_log(BLEV_ERROR, "clearDatabase: unlink failed path=%s errno=%d: %s", path, error, strerror(error));
}
}
}
u_free(path);
bridge_log(failed ? BLEV_WARN : BLEV_INFO, "clearDatabase: path=%s removed=%u missing=%u failed=%u", db_path, removed, missing, failed);
return failed ? -1 : 0;
}
int utun_bridge_start(const char* config_text) {
bridge_log(BLEV_INFO, "bridge start: launching utun instance");
int rc = instance_lite_start(config_text);
@@ -2462,17 +2492,7 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeRegenerateKeys(
JNIEXPORT jboolean JNICALL Java_com_utun_chat_data_NativeLib_nativeClearDatabase(
JNIEnv* env, jobject thiz) {
(void)env; (void)thiz;
if (!g_db_path[0]) {
bridge_log(BLEV_ERROR, "clearDatabase: no db path");
return JNI_FALSE;
}
char db_file[512], wal_file[512], shm_file[512];
snprintf(db_file, sizeof(db_file), "%s/chats.db", g_db_path);
snprintf(wal_file, sizeof(wal_file), "%s/chats.db-wal", g_db_path);
snprintf(shm_file, sizeof(shm_file), "%s/chats.db-shm", g_db_path);
unlink(db_file); unlink(wal_file); unlink(shm_file);
bridge_log(BLEV_INFO, "clearDatabase: deleted DB files at %s", g_db_path);
return JNI_TRUE;
return utun_bridge_clear_database(g_db_path) == 0 ? JNI_TRUE : JNI_FALSE;
}
/* ── Voice recording JNI ── */
@@ -54,6 +54,11 @@ bool utun_bridge_init(const char* db_path, int control_port,
* Готовность сервисов — CHAT_EVT_SERVICE_STARTED, а не возврат start. */
int utun_bridge_start(const char* config_text);
/* После полного stop, без параллельного start: удалить chats.db и его -wal/-shm из db_path.
* db_path — непустой каталог. 0 — все файлы удалены/уже отсутствуют; -1 — неверный путь, память либо ошибка IO.
* При частичном отказе проверяет остальные файлы; каждую ошибку и итоговые счётчики пишет в лог. */
int utun_bridge_clear_database(const char* db_path);
/* Освободить recorder, вызвать instance_lite_stop и снять C log/config callbacks.
* Config-provider сбрасывается к defaults; повторный init начинает новую регистрацию.
* Перед вызовом остановить внешние media workers/DSP; ограничения ожидания — instance_lite.h.