Bound recursive walker stack and report exact copy failure phases
This commit is contained in:
1 parent
bf3834b95d
commit
607de2a074
2 files changed
+35
-22
No files matched your search
@@ -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};
|
||||
|
||||
@@ -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 не откатывает копирование. */
|
||||
|
||||
Reference in new issue
Block a user