// media_async.c — thread-per-task async engine + crypto/file primitives #include "media_async.h" #include "../../lib/u_async.h" #include "../../lib/mem.h" #include "../../lib/debug_config.h" #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #endif struct media_async { int initialized; }; struct ma_thread_ctx { struct UASYNC* ua; ma_work_fn work; void* data; ma_done_fn done; void* arg; }; static void ma_done_trampoline(void* raw) { struct ma_thread_ctx* ctx = (struct ma_thread_ctx*)raw; ctx->done(ctx->arg, 0); u_free(ctx); } static void* ma_thread_entry(void* raw) { struct ma_thread_ctx* ctx = (struct ma_thread_ctx*)raw; ctx->work(ctx->data); uasync_post(ctx->ua, ma_done_trampoline, ctx); return NULL; } struct media_async* media_async_create(void) { struct media_async* ma = u_malloc(sizeof(*ma)); if (!ma) return NULL; ma->initialized = 1; DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "media_async: created"); return ma; } void media_async_destroy(struct media_async* ma) { if (!ma) return; ma->initialized = 0; u_free(ma); DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "media_async: destroyed"); } void media_async_submit(struct media_async* ma, struct UASYNC* ua, ma_work_fn work, void* data, ma_done_fn done, void* arg) { (void)ma; if (!ua || !work || !done) return; struct ma_thread_ctx* ctx = u_malloc(sizeof(*ctx)); if (!ctx) { done(arg, -1); return; } ctx->ua = ua; ctx->work = work; ctx->data = data; ctx->done = done; ctx->arg = arg; pthread_t tid; int rc = pthread_create(&tid, NULL, ma_thread_entry, ctx); if (rc != 0) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media_async: pthread_create failed rc=%d", rc); u_free(ctx); done(arg, -1); return; } pthread_detach(tid); } /* ─── примитивы (вызываются из worker thread) ─── */ int ma_sha256_file(const char* path, uint8_t hash_out[32]) { FILE* f = fopen(path, "rb"); if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_sha256_file: cannot open %s", path); return -1; } SHA256_CTX ctx; SHA256_Init(&ctx); uint8_t buf[65536]; size_t rd; while ((rd = fread(buf, 1, sizeof(buf), f)) > 0) SHA256_Update(&ctx, buf, rd); fclose(f); SHA256_Final(hash_out, &ctx); return 0; } int ma_sign_block(const uint8_t* ed25519_privkey, const uint8_t* data, size_t len, uint8_t sig_out[64]) { EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, ed25519_privkey, 32); if (!pkey) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_sign_block: EVP_PKEY_new_raw_private_key failed"); return -1; } EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); if (!mdctx) { EVP_PKEY_free(pkey); return -1; } if (EVP_DigestSignInit(mdctx, NULL, NULL, NULL, pkey) != 1) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_sign_block: EVP_DigestSignInit failed"); EVP_MD_CTX_free(mdctx); EVP_PKEY_free(pkey); return -1; } size_t siglen = 64; int rc = EVP_DigestSign(mdctx, sig_out, &siglen, data, len); EVP_MD_CTX_free(mdctx); EVP_PKEY_free(pkey); if (rc != 1 || siglen != 64) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_sign_block: EVP_DigestSign failed rc=%d len=%zu", rc, siglen); return -1; } return 0; } int ma_copy_file(const char* src, const char* dst) { if (!src || !dst) return -1; if (strcmp(src, dst) == 0) return 0; FILE* s = fopen(src, "rb"); if (!s) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_copy_file: cannot open src %s", src); return -1; } FILE* d = fopen(dst, "wb"); if (!d) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_copy_file: cannot create dst %s", dst); fclose(s); return -1; } uint8_t buf[65536]; size_t rd; while ((rd = fread(buf, 1, sizeof(buf), s)) > 0) { if (fwrite(buf, 1, rd, d) != rd) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "ma_copy_file: write error at %s", dst); fclose(s); fclose(d); return -1; } } fclose(s); fclose(d); return 0; } int ma_file_size(const char* path) { struct stat st; if (stat(path, &st) != 0) return -1; return (int)(st.st_size > 0 ? st.st_size : 0); } void ma_uuid(uint8_t uuid_out[16]) { RAND_bytes(uuid_out, 16); } /* ─── Рекурсивный обход каталога (кроссплатформенный) ─── */ struct ma_walk_ctx { ma_dir_walk_cb cb; void* arg; }; #ifndef _WIN32 static void ma_walk_recursive(const char* dir, struct ma_walk_ctx* wc) { DIR* d = opendir(dir); if (!d) return; struct dirent* e; while ((e = readdir(d)) != NULL) { const char* n = e->d_name; if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) continue; char full[4096]; snprintf(full, sizeof(full), "%s/%s", dir, n); struct stat st; if (lstat(full, &st) != 0) continue; if (S_ISDIR(st.st_mode)) ma_walk_recursive(full, wc); else if (S_ISREG(st.st_mode)) wc->cb(wc->arg, full, (int64_t)st.st_size); } closedir(d); } #else static void ma_walk_recursive(const char* dir, struct ma_walk_ctx* wc) { char pattern[4096]; snprintf(pattern, sizeof(pattern), "%s/*", dir); WIN32_FIND_DATAA fd; HANDLE h = FindFirstFileA(pattern, &fd); if (h == INVALID_HANDLE_VALUE) return; do { const char* n = fd.cFileName; if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) continue; char full[4096]; snprintf(full, sizeof(full), "%s/%s", dir, n); if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ma_walk_recursive(full, wc); else { LARGE_INTEGER sz; sz.HighPart = fd.nFileSizeHigh; sz.LowPart = fd.nFileSizeLow; wc->cb(wc->arg, full, (int64_t)sz.QuadPart); } } while (FindNextFileA(h, &fd)); FindClose(h); } #endif int ma_dir_walk(const char* dir, ma_dir_walk_cb cb, void* arg) { if (!dir || !dir[0] || !cb) return -1; struct ma_walk_ctx wc; wc.cb = cb; wc.arg = arg; ma_walk_recursive(dir, &wc); return 0; }