fix(member-files): reject local FIFO without blocking disk worker

This commit is contained in:
evgeny committed 2026-10-24 18:47:06 +02:00
1 parent 1c1b2b2026
commit 41f0aa2439
2 files changed
+42 -10

No files matched your search

+10 -3
View File
@@ -58,7 +58,10 @@ static int disk_win_error(const char* operation) {
static int disk_handle_info(HANDLE h, struct mf_disk_info* out) {
BY_HANDLE_FILE_INFORMATION i;
if (!GetFileInformationByHandle(h, &i)) return disk_win_error("GetFileInformationByHandle");
if ((i.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || i.nNumberOfLinks > 1) return MF_INVALID_PATH;
if ((i.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || i.nNumberOfLinks > 1) {
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: unsupported file attributes=%lu links=%lu", i.dwFileAttributes, i.nNumberOfLinks);
return MF_INVALID_PATH;
}
memset(out, 0, sizeof(*out));
out->type = i.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? MF_DIRECTORY : MF_FILE;
out->size = out->type == MF_FILE ? ((uint64_t)i.nFileSizeHigh << 32) | i.nFileSizeLow : 0;
@@ -254,7 +257,11 @@ static int disk_parent_open(struct mf_disk_root* root, const char* path, struct
}
static int disk_posix_info(const struct stat* st, struct mf_disk_info* i) {
if ((!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) || (S_ISREG(st->st_mode) && st->st_nlink != 1)) return MF_INVALID_PATH;
if ((!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) || (S_ISREG(st->st_mode) && st->st_nlink != 1)) {
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: unsupported file mode=%lo links=%llu",
(unsigned long)st->st_mode, (unsigned long long)st->st_nlink);
return MF_INVALID_PATH;
}
memset(i, 0, sizeof(*i)); i->type = S_ISDIR(st->st_mode) ? MF_DIRECTORY : MF_FILE;
i->size = i->type == MF_FILE ? (uint64_t)st->st_size : 0; i->mtime = (uint64_t)st->st_mtime;
i->identity[0] = st->st_dev; i->identity[1] = st->st_ino;
@@ -352,7 +359,7 @@ int mf_disk_resize(int fd, uint64_t size) { return ftruncate(fd, (off_t)size) ?
int mf_disk_sync(int fd) { return fsync(fd) ? disk_error("sync", NULL) : MF_OK; }
int mf_disk_close(int fd) { return close(fd) ? disk_error("close", NULL) : MF_OK; }
int mf_disk_local_open(const char* path, int create, int* fd) {
*fd = open(path, O_CLOEXEC | O_NOFOLLOW | (create ? O_RDWR | O_CREAT | O_EXCL : O_RDONLY), 0600);
*fd = open(path, O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK | (create ? O_RDWR | O_CREAT | O_EXCL : O_RDONLY), 0600);
return *fd < 0 ? disk_error("local open", path) : MF_OK;
}
int mf_disk_local_publish(const char* from, const char* to) { return rename(from, to) ? disk_error("local publish", to) : MF_OK; }
+32 -7
View File
@@ -1,39 +1,64 @@
/* Примитивы диска для storage и клиентского worker. На POSIX пути разрешаются
/* Синхронные примитивы диска для storage и клиентского worker; не вызывать из uasync callbacks.
* Возврат — MF_OK либо положительный mf_status, не errno/-1. Аргументы/выходы обязательны, кроме отмеченных NULL.
* Серверные path/from/to — уже проверенные относительные пути; сам слой не запрещает '.'/'..'.
* На POSIX пути разрешаются
* относительно открытого каталога через *at/O_NOFOLLOW. Windows удерживает каталоги
* без FILE_SHARE_DELETE и отвергает reparse points; имена преобразуются UTF-8→UTF-16.
* fd принадлежит вызывающему; read/write работают по 64-битному смещению. */
* fd принадлежит вызывающему; read/write работают по 64-битному смещению.
* Локальные пути приложения не ограничены корнем storage; их родители/назначение остаются ответственностью caller. */
#ifndef MEMBER_FILES_DISK_H
#define MEMBER_FILES_DISK_H
#include "protocol.h"
#include "../../lib/platform_compat.h"
struct mf_disk_info { uint64_t size, mtime, identity[6]; uint8_t type; };
struct mf_disk_info {
uint64_t size; // Байты regular-файла; каталог — 0.
uint64_t mtime; // Unix seconds; для version используются более точные identity.
uint64_t identity[6]; // Непрозрачный снимок inode/file ID и времён; платформенно зависим, не wire.
uint8_t type; // MF_FILE/MF_DIRECTORY; другие типы и regular hardlink отклоняются.
};
struct mf_disk_root {
char path[2048];
char path[2048]; // Заимствованный при open путь копируется; предел2047 байт + NUL.
#ifdef _WIN32
HANDLE handle;
HANDLE handle; // Собственный handle корня, закрывается после всех операций.
#else
int fd;
int fd; // Собственный directory fd; операции разрешают имена относительно него.
#endif
};
/* Создать корень, если отсутствует (не создаёт родителей), и закрепить его. Close нужен только после успеха. */
int mf_disk_root_open(struct mf_disk_root* root, const char* path);
void mf_disk_root_close(struct mf_disk_root* root);
/* Открыть regular-файл без усечения. create!=0 — эксклюзивное создание, writable!=0 — read/write.
* fd использовать/закрывать только после MF_OK; отказ не гарантирует значение *fd. */
int mf_disk_open(struct mf_disk_root* root, const char* path, int create, int writable, int* fd);
/* Снимок без чтения содержимого: stat — по имени, info — уже открытого fd. Выход использовать только при успехе. */
int mf_disk_stat(struct mf_disk_root* root, const char* path, struct mf_disk_info* info);
int mf_disk_info(int fd, struct mf_disk_info* info);
/* created, если не NULL, показывает факт создания даже при последующей ошибке sync. */
/* Создать один каталог. created, если не NULL, показывает факт создания даже при последующей ошибке sync. */
int mf_disk_mkdir(struct mf_disk_root* root, const char* path, int* created);
/* Удалить файл либо пустой каталог. Ошибка sync на POSIX возможна уже после удаления. */
int mf_disk_remove(struct mf_disk_root* root, const char* path, int directory);
/* Заменяющий rename: запрет замены при обычном RENAME проверяет storage. На POSIX sync обоих родителей;
* ошибка sync не означает откат уже выполненного rename. */
int mf_disk_rename(struct mf_disk_root* root, const char* from, const char* to);
/* name/info заимствованы на время callback; 0 — продолжить, >0 — успешная ранняя остановка, <0 — -mf_status. */
typedef int (*mf_disk_entry_fn)(void* arg, const char* name, const struct mf_disk_info* info);
/* Перечислить записи в порядке файловой системы, без '.'/'..'. Пустой path означает корень. */
int mf_disk_list(struct mf_disk_root* root, const char* path, mf_disk_entry_fn cb, void* arg);
/* Точный диапазон: 0 байт допустимо, короткий read — ошибка. Caller гарантирует offset+size <= INT64_MAX. */
int mf_disk_read(int fd, uint64_t offset, void* data, size_t size);
int mf_disk_write(int fd, uint64_t offset, const void* data, size_t size);
/* Изменить длину (size<=INT64_MAX), синхронизировать содержимое либо закрыть fd; каждый вызов независим. */
int mf_disk_resize(int fd, uint64_t size);
int mf_disk_sync(int fd);
int mf_disk_close(int fd);
/* Локальный source (create=0) либо эксклюзивный read/write temp (create!=0), без усечения.
* Тип проверяет caller через info; POSIX O_NONBLOCK позволяет отвергнуть FIFO до блокирующего чтения. */
int mf_disk_local_open(const char* path, int create, int* fd);
/* Заменить local назначение: POSIX rename без sync каталога; Windows MOVEFILE_WRITE_THROUGH. */
int mf_disk_local_publish(const char* from, const char* to);
/* Удалить собственный local temp; отсутствие самого файла допустимо. Владение проверяет caller. */
int mf_disk_local_remove(const char* path);
/* SHA256 ровно size байт от нуля, включая пустой файл; ошибка делает hash непригодным. fd остаётся у caller. */
int mf_disk_hash(int fd, uint64_t size, uint8_t hash[32]);
#endif