Preserve video temp mode and test durable file commit boundaries

This commit is contained in:
evgeny committed 2026-10-24 14:39:46 +02:00
1 parent aa7a9b74f4
commit 9e0949434e
3 files changed
+50 -9

No files matched your search

+2 -2
View File
@@ -713,7 +713,7 @@ static int vc_run(struct vc_ctx* ctx) {
static void vc_thread_entry(void* raw) {
struct vc_ctx* ctx = raw;
video_ffmpeg_init();
if (file_io_paths(ctx->source, ctx->destination) || file_io_temp_open(&ctx->file, ctx->destination)) { ctx->error = -1; return; }
if (file_io_paths(ctx->source, ctx->destination) || file_io_temp_open(&ctx->file, ctx->destination, 0)) { ctx->error = -1; return; }
struct stat info;
if (!stat(ctx->source, &info)) ctx->source_size = info.st_size;
int error = vc_run(ctx);
@@ -844,7 +844,7 @@ int video_extract_thumbnail(const char* src, const char* dst, int max_w) {
}
if (error < 0) goto done;
phase = "write JPEG";
if (file_io_temp_open(&output, dst)) { error = AVERROR(EIO); goto done; }
if (file_io_temp_open(&output, dst, 0)) { error = AVERROR(EIO); goto done; }
if (fwrite(packet->data, 1, packet->size, output.file) != (size_t)packet->size) {
error = AVERROR(errno ? errno : EIO); goto done;
}
+1 -1
View File
@@ -884,7 +884,7 @@ if OS_LINUX
test_media_primitives_LDFLAGS = -Wl,--wrap=fopen -Wl,--wrap=fdopen -Wl,--wrap=fread -Wl,--wrap=__fread_chk -Wl,--wrap=ferror \
-Wl,--wrap=fwrite -Wl,--wrap=fflush -Wl,--wrap=fclose -Wl,--wrap=rename -Wl,--wrap=RAND_bytes \
-Wl,--wrap=opendir -Wl,--wrap=readdir -Wl,--wrap=closedir -Wl,--wrap=fseeko \
-Wl,--wrap=pthread_create -Wl,--wrap=u_calloc_impl -Wl,--wrap=uasync_post_reserved
-Wl,--wrap=pthread_create -Wl,--wrap=u_calloc_impl -Wl,--wrap=uasync_post_reserved -Wl,--wrap=fsync
endif
test_media_index_SOURCES = test_media_index.c
+47 -6
View File
@@ -19,7 +19,8 @@ static char root[256], source[512], destination[512];
static unsigned visits;
#ifdef __linux__
enum fault { NONE, READ, WRITE, FLUSH, SOURCE_CLOSE, OUTPUT_CLOSE, FDOPEN, PUBLISH, OPEN_DIR, READ_DIR, CLOSE_DIR, SEEK, THREAD };
enum fault { NONE, READ, WRITE, FLUSH, SOURCE_CLOSE, OUTPUT_CLOSE, FDOPEN, PUBLISH, OPEN_DIR, READ_DIR, CLOSE_DIR, SEEK, THREAD,
SYNC_FILE, SYNC_DIRECTORY };
static enum fault fault;
static int injected, read_failed, rng_after = -1, allocation_after = -1;
static FILE *input_file, *output_file;
@@ -35,6 +36,7 @@ extern size_t __real_fwrite(const void*, size_t, size_t, FILE*);
extern int __real_ferror(FILE*), __real_fflush(FILE*), __real_fclose(FILE*);
extern int __real_rename(const char*, const char*), __real_RAND_bytes(unsigned char*, int);
extern int __real_fseeko(FILE*, off_t, int);
extern int __real_fsync(int);
extern DIR *__real_opendir(const char*);
extern struct dirent *__real_readdir(DIR*);
extern int __real_closedir(DIR*);
@@ -84,6 +86,11 @@ int __wrap_fclose(FILE *file) {
return result;
}
int __wrap_rename(const char *from, const char *to) { return inject(PUBLISH) ? -1 : __real_rename(from, to); }
int __wrap_fsync(int fd) {
struct stat data; assert(!fstat(fd, &data));
if (inject(S_ISDIR(data.st_mode) ? SYNC_DIRECTORY : SYNC_FILE)) return -1;
return __real_fsync(fd);
}
int __wrap_RAND_bytes(unsigned char *out, int size) {
if (rng_after == 0) { injected = 1; return 0; }
if (rng_after > 0) rng_after--;
@@ -169,7 +176,7 @@ static void temporary_tests(void) {
struct file_io_temp outputs[3] = {0};
char paths[3][FILE_IO_PATH_MAX], stale[FILE_IO_PATH_MAX];
for (unsigned i = 0; i < 3; i++) {
assert(!file_io_temp_open(&outputs[i], destination));
assert(!file_io_temp_open(&outputs[i], destination, 0));
snprintf(paths[i], sizeof(paths[i]), "%s", outputs[i].temporary);
assert(file_io_remove_orphan(paths[i]) == 0);
assert(fputs("owned", outputs[i].file) >= 0 && !file_io_temp_finish(&outputs[i]));
@@ -187,10 +194,10 @@ static void temporary_tests(void) {
#ifdef __linux__
struct file_io_temp failed = {0};
fault = FDOPEN; injected = 0;
assert(file_io_temp_open(&failed, destination) == -1 && injected); fault = NONE;
assert(file_io_temp_open(&failed, destination, 0) == -1 && injected); fault = NONE;
assert(failed.temporary[0] && file_io_remove_orphan(failed.temporary) == 0);
file_io_temp_discard(&failed);
assert(!file_io_temp_open(&failed, destination) && !file_io_temp_finish(&failed));
assert(!file_io_temp_open(&failed, destination, 0) && !file_io_temp_finish(&failed));
fault = PUBLISH; injected = 0;
assert(file_io_temp_publish(&failed, source, destination) == -1 && injected); fault = NONE;
assert(file_io_remove_orphan(failed.temporary) == 0); file_io_temp_discard(&failed);
@@ -198,6 +205,40 @@ static void temporary_tests(void) {
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "PASS active temp ownership: open/finished, multiple generations, publish/discard and failures");
}
/* Ошибка до rename сохраняет прежний файл; отказ directory sync уже не отменяет замену. */
static void durable_tests(void) {
struct file_io_temp output = {0};
write_text(destination, "previous");
assert(!file_io_temp_open(&output, destination, FILE_IO_DURABLE));
assert(fputs("durable", output.file) >= 0 && !file_io_temp_finish(&output));
check_text(destination, "previous"); assert(file_io_remove_orphan(output.temporary) == 0);
assert(!file_io_temp_publish(&output, source, destination) && !output.temporary[0]);
file_io_temp_discard(&output); check_text(destination, "durable");
assert(!file_io_sync_file(destination) && !file_io_sync_directory("relative-name"));
#ifndef _WIN32
assert(!file_io_sync_directory("/file"));
#endif
#ifdef __linux__
assert(!file_io_temp_open(&output, destination, FILE_IO_DURABLE));
assert(fputs("failed", output.file) >= 0);
fault = SYNC_FILE; injected = 0;
assert(file_io_temp_finish(&output) == -1 && injected && !output.file && output.temporary[0]); fault = NONE;
file_io_temp_discard(&output); check_text(destination, "durable");
assert(!file_io_temp_open(&output, destination, FILE_IO_DURABLE));
assert(fputs("installed", output.file) >= 0 && !file_io_temp_finish(&output));
fault = SYNC_DIRECTORY; injected = 0;
assert(file_io_temp_publish(&output, source, destination) == -1 && injected && !output.temporary[0]); fault = NONE;
file_io_temp_discard(&output); check_text(destination, "installed");
assert(!file_io_temp_open(&output, destination, 0));
assert(fputs("ordinary", output.file) >= 0);
fault = SYNC_FILE; injected = 0;
assert(!file_io_temp_finish(&output) && !injected); fault = SYNC_DIRECTORY;
assert(!file_io_temp_publish(&output, source, destination) && !injected); fault = NONE;
file_io_temp_discard(&output); check_text(destination, "ordinary");
#endif
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "PASS durable temp: sync-before-close, rename commit point, directory failure and ordinary mode");
}
static void crypto_tests(void) {
uint8_t output[64], zero[64] = {0};
memset(output, 0xa5, sizeof(output));
@@ -291,7 +332,7 @@ static void registration_tests(struct UASYNC *ua) {
struct temp_job { struct file_io_temp output; pthread_barrier_t barrier; int publish; };
static void* temp_worker(void* arg) {
struct temp_job* job = arg;
assert(!file_io_temp_open(&job->output, destination));
assert(!file_io_temp_open(&job->output, destination, 0));
assert(fputs("worker", job->output.file) >= 0 && !file_io_temp_finish(&job->output));
pthread_barrier_wait(&job->barrier); pthread_barrier_wait(&job->barrier);
if (job->publish) assert(!file_io_temp_publish(&job->output, source, destination));
@@ -384,7 +425,7 @@ int main(void) {
#endif
assert(!utun_mkdir(root, 0700)); snprintf(source, sizeof(source), "%s/source", root);
snprintf(destination, sizeof(destination), "%s/destination", root);
copy_tests(); temporary_tests(); crypto_tests(); size_and_walk_tests();
copy_tests(); temporary_tests(); durable_tests(); crypto_tests(); size_and_walk_tests();
struct UASYNC *ua = uasync_create(); assert(ua);
registration_tests(ua);
#ifdef __linux__