Bound recursive walker stack and report exact copy failure phases

This commit is contained in:
evgeny committed 2026-10-22 10:26:56 +02:00
1 parent bf3834b95d
commit 607de2a074
2 files changed
+35 -22

No files matched your search

+33 -21
View File
@@ -158,24 +158,29 @@ int ma_copy_file(const char* source, const char* destination) {
FILE* input = file_io_open_read(source);
if (!input) return -1;
struct file_io_temp output = {0};
const char* phase = "validate paths";
int status = file_io_paths(source, destination);
uint64_t total = 0;
if (status == 0) phase = "create temporary";
if (status == 0 && file_io_temp_open(&output, destination)) status = -1;
if (status == 0) {
uint8_t buffer[65536]; size_t count;
phase = "read/write";
while ((count = fread(buffer, 1, sizeof(buffer), input)) > 0) {
if (fwrite(buffer, 1, count, output.file) != count) { status = -1; break; }
total += count;
}
if (ferror(input)) status = -1;
}
if (fclose(input)) status = -1;
if (fclose(input)) { phase = "close source"; status = -1; }
if (status == 0) phase = "flush/close temporary";
if (status == 0 && file_io_temp_finish(&output)) status = -1;
if (status == 0) phase = "publish";
if (status == 0 && file_io_temp_publish(&output, source, destination)) status = -1;
file_io_temp_discard(&output);
if (status < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "copy failed source=%s destination=%s bytes=%llu errno=%d",
source, destination ? destination : "(null)", (unsigned long long)total, errno);
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "copy failed phase=%s source=%s destination=%s bytes=%llu",
phase, source, destination ? destination : "(null)", (unsigned long long)total);
return -1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "copy complete source=%s destination=%s bytes=%llu same_file=%d",
@@ -210,18 +215,21 @@ static int ma_walk_recursive(const char* directory, struct ma_walk_ctx* ctx, uns
/* Одна запись: ссылки/устройства пропускаются, ошибки делают обход неполным. */
static int ma_walk_entry(const char* directory, const char* name, struct ma_walk_ctx* ctx, unsigned depth) {
if (!strcmp(name, ".") || !strcmp(name, "..")) return 0;
char full[FILE_IO_PATH_MAX];
int length = snprintf(full, sizeof(full), "%s/%s", directory, name);
if (length < 0 || (size_t)length >= sizeof(full)) {
size_t length = strlen(directory) + strlen(name) + 2;
if (length > FILE_IO_PATH_MAX) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory entry path too long directory=%s name=%s", directory, name); return -1;
}
char* full = u_malloc((uint32_t)length);
if (!full) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory entry allocation failed bytes=%zu", length); return -1; }
snprintf(full, length, "%s/%s", directory, name);
struct file_io_info info;
if (file_io_inspect(full, 0, &info)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory entry inspection failed path=%s", full); return -1;
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory entry inspection failed path=%s", full); u_free(full); return -1;
}
if (info.type == FILE_IO_DIRECTORY) return ma_walk_recursive(full, ctx, depth + 1);
int status = 0;
if (info.type == FILE_IO_DIRECTORY) status = ma_walk_recursive(full, ctx, depth + 1);
if (info.type == FILE_IO_REGULAR) ctx->cb(ctx->arg, full, info.size);
return 0;
u_free(full); return status;
}
/* Ограниченный по глубине обход без перехода по конечным ссылкам. */
@@ -239,27 +247,30 @@ static int ma_walk_recursive(const char* directory, struct ma_walk_ctx* ctx, uns
}
int status = 0;
#ifdef _WIN32
char pattern[FILE_IO_PATH_MAX]; wchar_t wide[FILE_IO_PATH_MAX];
int length = snprintf(pattern, sizeof(pattern), "%s/*", directory);
if (length < 0 || (size_t)length >= sizeof(pattern)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory search path too long path=%s", directory); return -1;
struct ma_search { wchar_t pattern[FILE_IO_PATH_MAX]; char name[FILE_IO_PATH_MAX]; WIN32_FIND_DATAW data; };
struct ma_search* search = u_calloc(1, sizeof(*search));
if (!search) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory search allocation failed"); return -1; }
if (file_io_wide(directory, search->pattern)) { u_free(search); return -1; }
size_t length = wcslen(search->pattern);
if (length + 3 > FILE_IO_PATH_MAX) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory search path too long path=%s", directory); u_free(search); return -1;
}
if (file_io_wide(pattern, wide)) return -1;
WIN32_FIND_DATAW data;
HANDLE find = FindFirstFileW(wide, &data);
memcpy(search->pattern + length, L"/*", 3 * sizeof(wchar_t));
HANDLE find = FindFirstFileW(search->pattern, &search->data);
if (find == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
u_free(search);
if (error == ERROR_FILE_NOT_FOUND) return 0; // Открытый выше каталог пуст.
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory open failed path=%s winerr=%lu", directory, error); return -1;
}
do {
char name[FILE_IO_PATH_MAX];
if (!WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, data.cFileName, -1, name, sizeof(name), NULL, NULL)) {
if (!WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, search->data.cFileName, -1,
search->name, sizeof(search->name), NULL, NULL)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory entry UTF-8 conversion failed path=%s winerr=%lu", directory, GetLastError());
status = -1; break;
}
if (ma_walk_entry(directory, name, ctx, depth)) { status = -1; break; }
if (!FindNextFileW(find, &data)) {
if (ma_walk_entry(directory, search->name, ctx, depth)) { status = -1; break; }
if (!FindNextFileW(find, &search->data)) {
DWORD error = GetLastError();
if (error != ERROR_NO_MORE_FILES) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory read failed path=%s winerr=%lu", directory, error); status = -1;
@@ -270,6 +281,7 @@ static int ma_walk_recursive(const char* directory, struct ma_walk_ctx* ctx, uns
if (!FindClose(find)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory close failed path=%s winerr=%lu", directory, GetLastError()); status = -1;
}
u_free(search);
#else
DIR* dir = opendir(directory);
if (!dir) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory open failed path=%s errno=%d", directory, errno); return -1; }
@@ -288,7 +300,7 @@ static int ma_walk_recursive(const char* directory, struct ma_walk_ctx* ctx, uns
}
int ma_dir_walk(const char* directory, ma_dir_walk_cb callback, void* arg) {
if (!directory || !*directory || !callback) {
if (!directory || !*directory || strlen(directory) >= FILE_IO_PATH_MAX || !callback) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "directory walk invalid path/callback"); return -1;
}
struct ma_walk_ctx ctx = {.cb = callback, .arg = arg};
+2 -1
View File
@@ -44,7 +44,8 @@ int ma_sha256_file(const char* path, uint8_t hash_out[32]);
* data=NULL допустим только при len=0. 0/-1. */
int ma_sign_block(const uint8_t* ed25519_privkey,
const uint8_t* data, size_t len, uint8_t sig_out[64]);
/* Копировать regular src в отсутствующий/regular dst через соседний temp; 0/-1. Пути UTF-8, <4096 байт.
/* Копировать regular src в отсутствующий/regular dst через соседний temp; 0/-1. Пути UTF-8, <4096 байт,
* причём dst вместе с суффиксом .tmp.<32 hex> и NUL должен помещаться в 4096 байт.
* Один существующий файл (включая ./ и hardlink) — успешный no-op. Конечные ссылки назначения отклоняются.
* Ошибка сохраняет src и прежний dst; src/каталоги не менять во время вызова. POSIX результат имеет права 0600.
* Публикация происходит внутри вызова; последующая отмена media_async не откатывает копирование. */