From cb3f40deeea788a7a3a6b430f7a35cdab07714e6 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Fri, 29 Aug 2025 00:10:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83?= =?UTF-8?q?=D1=82=D0=BE=D0=BA=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app.d | 65 ++++++++++++------ source/dfanotify.d | 163 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 171 insertions(+), 57 deletions(-) diff --git a/source/app.d b/source/app.d index 6a13c16..4c08dd0 100644 --- a/source/app.d +++ b/source/app.d @@ -1,56 +1,77 @@ import dfanotify; import std.stdio; -import std.file : readLink; import std.format : format; import core.sys.posix.fcntl : AT_FDCWD; -import core.sys.posix.unistd : close; import core.stdc.stdlib : exit; void main() { Fanotify fan; - try { + try + { fan = new Fanotify( - FAN_CLASS_NOTIF | FAN_CLOEXEC | FAN_REPORT_FID | FAN_REPORT_DIR_FID | FAN_REPORT_NAME); - } catch (Exception e) { + FAN_CLASS_NOTIF | FAN_CLOEXEC | FAN_REPORT_FID | FAN_REPORT_DIR_FID | FAN_REPORT_NAME + ); + } + catch (Exception e) + { writeln(e.msg); exit(1); } - auto eventMask = FAN_OPEN | FAN_MODIFY | FAN_CLOSE | FAN_CREATE | FAN_DELETE | FAN_EVENT_ON_CHILD; + auto eventMask = FAN_OPEN | FAN_MODIFY | FAN_CLOSE | FAN_CREATE | FAN_DELETE | FAN_EVENT_ON_CHILD | FAN_ACCESS; - try { + try + { fan.mark(FAN_MARK_ADD | FAN_MARK_ONLYDIR, eventMask, AT_FDCWD, "/tmp/scripts"); - } catch (Exception e) { + } + catch (Exception e) + { writeln(e.msg); exit(1); } - writeln("Мониторинг запущен для /tmp/scripts..."); + writeln( + "Мониторинг с разрешениями запущен для /tmp/scripts..."); while (true) { auto events = fan.readEvents(); - foreach (ref e; events) + foreach (e; events) { string path = e.name.length ? e.name : "unknown"; writefln("Событие: mask=0x%x, pid=%d, name/path=%s", e.mask, e.pid, path); - if (e.isOpen) - writeln(" - Открытие файла"); - if (e.isModify) - writeln(" - Модификация файла"); - if (e.isCloseWrite) - writeln(" - Закрытие после записи"); - if (e.isCloseNoWrite) - writeln(" - Закрытие без записи"); - if (e.isCreate) - writeln(" - Создание файла/директории"); - if (e.isDelete) - writeln(" - Удаление файла/директории"); + // Обработка permission-события + if (e.isOpenPerm) + { + writeln(" - Запрос на открытие файла. Отклоняем (предполагаем попытку записи)."); + e.respond(FAN_DENY); // Отклонить (для FAN_ALLOW используйте FAN_ALLOW) + } + else + { + // Обычные уведомления (не permission) + if (e.isOpen) + writeln(" - Открытие файла"); + if (e.isAccess) + writeln(" - Доступ к файлу"); + if (e.isModify) + writeln(" - Модификация файла"); + if (e.isCloseWrite) + writeln(" - Закрытие после записи"); + if (e.isCloseNoWrite) + writeln(" - Закрытие без записи"); + if (e.isCreate) + writeln(" - Создание файла/директории"); + if (e.isDelete) + writeln(" - Удаление файла/директории"); + } + + // Опционально: вызов постобработки + e.postProcess(); } } } diff --git a/source/dfanotify.d b/source/dfanotify.d index d678bc1..8144d08 100644 --- a/source/dfanotify.d +++ b/source/dfanotify.d @@ -2,7 +2,7 @@ module dfanotify; public import fanotify; -import core.sys.posix.unistd : read, close, ssize_t; +import core.sys.posix.unistd : read, write, close, ssize_t; import core.sys.posix.fcntl : O_RDONLY, O_RDWR, O_LARGEFILE, AT_FDCWD; import std.exception : enforce; import std.string : toStringz, fromStringz; @@ -11,26 +11,53 @@ import core.stdc.errno : errno; import core.stdc.string : strerror; import core.stdc.stdint; -struct FanotifyEvent +// Класс для представления события fanotify (ООП-стиль, с методами для проверки и обработки) +class FanotifyEvent { - fanotify_event_metadata meta; - string name; + private fanotify_event_metadata meta_; + private string name_; + private int fanFd_; // Ссылка на fanotify fd для отправки response (копируется при создании) + // Конструктор (value semantics, копирует данные) + this(fanotify_event_metadata meta, string name, int fanFd) + { + meta_ = meta; + name_ = name; + fanFd_ = fanFd; + } + + // Деструктор: автоматически закрывает fd события, если он валиден (RAII) + ~this() + { + if (meta_.fd >= 0 && meta_.fd != FAN_NOFD) + { + close(meta_.fd); + meta_.fd = -1; // Избегаем повторного закрытия + } + } + + // Геттеры (value types) @property uint64_t mask() const { - return meta.mask; + return meta_.mask; } @property int eventFd() const { - return meta.fd; + return meta_.fd; } @property int pid() const { - return meta.pid; + return meta_.pid; } + @property string name() const + { + return name_; + } + + // Методы проверки событий (без ref) bool isOpen() const { return (mask & FAN_OPEN) != 0; @@ -65,44 +92,97 @@ struct FanotifyEvent { return (mask & FAN_DELETE) != 0; } -} -class Fanotify -{ - private int fd = -1; - - this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE) + bool isOpenPerm() const { - fd = fanotify_init(initFlags, eventFFlags); - enforce(fd >= 0, "Ошибка инициализации fanotify: " ~ to!string(fd)); + return (mask & FAN_OPEN_PERM) != 0; } + bool isAccessPerm() const + { + return (mask & FAN_ACCESS_PERM) != 0; + } + + bool isOpenExecPerm() const + { + return (mask & FAN_OPEN_EXEC_PERM) != 0; + } + + bool isOverflow() const + { + return (mask & FAN_Q_OVERFLOW) != 0; + } + + bool isFsError() const + { + return (mask & FAN_FS_ERROR) != 0; + } + + // Метод для постобработки события (виртуальный, можно override для кастомной логики) + void postProcess() + { + // По умолчанию ничего, но можно добавить логику, например, логирование + } + + // Метод для отправки response (для permission-событий), закрывает fd автоматически после + void respond(uint response) + { + if (eventFd < 0 || eventFd == FAN_NOFD) + { + return; // Нет fd для response + } + + fanotify_response resp; + resp.fd = eventFd; + resp.response = response; + + ssize_t res = write(fanFd_, &resp, fanotify_response.sizeof); + enforce(res == fanotify_response.sizeof, "Ошибка записи response: " ~ strerror(errno) + .fromStringz.to!string); + + // Закрываем fd сразу после response (не ждем деструктора, но деструктор на всякий случай) + close(meta_.fd); + meta_.fd = -1; + } +} + +// Основной ООП-класс для управления fanotify +class Fanotify +{ + private int fd_ = -1; + + // Конструктор: инициализация с флагами + this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE) + { + fd_ = fanotify_init(initFlags, eventFFlags); + enforce(fd_ >= 0, "Ошибка инициализации fanotify: " ~ strerror(errno) + .fromStringz.to!string); + } + + // Деструктор: автоматически закрывает fanotify fd ~this() { - if (fd >= 0) + if (fd_ >= 0) { - close(fd); - fd = -1; + close(fd_); + fd_ = -1; } } + // Метод для добавления/удаления/модификации меток (управление событиями) void mark(uint markFlags, uint64_t eventMask, int dirFd = AT_FDCWD, string path = null) { const(char)* cPath = path ? path.toStringz() : null; - int res = fanotify_mark(fd, markFlags, eventMask, dirFd, cPath); - if (res == -1) - { - string errMsg = "Ошибка маркировки fanotify: " ~ to!string( - res) ~ " (errno: " ~ to!string(errno) ~ ", " ~ strerror(errno) - .fromStringz.to!string ~ ")"; - throw new Exception(errMsg); - } + int res = fanotify_mark(fd_, markFlags, eventMask, dirFd, cPath); + enforce(res == 0, "Ошибка маркировки fanotify: " ~ strerror(errno) + .fromStringz.to!string); } + // Метод для чтения событий (возвращает массив объектов событий) FanotifyEvent[] readEvents(size_t bufferSize = 4096) { ubyte[] buffer = new ubyte[bufferSize]; - ssize_t len = read(fd, buffer.ptr, buffer.length); + ssize_t len = read(fd_, buffer.ptr, buffer.length); if (len <= 0) { return []; @@ -112,43 +192,56 @@ class Fanotify size_t offset = 0; while (offset + FAN_EVENT_METADATA_LEN <= len) { - auto meta = cast(fanotify_event_metadata*)(buffer.ptr + offset); + auto meta = *(cast(fanotify_event_metadata*)(buffer.ptr + offset)); if (meta.event_len < FAN_EVENT_METADATA_LEN || offset + meta.event_len > len) { break; } - FanotifyEvent ev = FanotifyEvent(*meta); - + string name; size_t infoOffset = offset + fanotify_event_metadata.sizeof; while (infoOffset < offset + meta.event_len) { - auto hdr = cast(fanotify_event_info_header*)(buffer.ptr + infoOffset); + auto hdr = *(cast(fanotify_event_info_header*)(buffer.ptr + infoOffset)); if (hdr.len == 0 || infoOffset + hdr.len > offset + meta.event_len) { break; } - if (hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) + if (hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME || + hdr.info_type == FAN_EVENT_INFO_TYPE_OLD_DFID_NAME || + hdr.info_type == FAN_EVENT_INFO_TYPE_NEW_DFID_NAME) { size_t fidOffset = infoOffset + fanotify_event_info_header.sizeof + __kernel_fsid_t.sizeof; - auto handle = cast(file_handle*)(buffer.ptr + fidOffset); + auto handle = *(cast(file_handle*)(buffer.ptr + fidOffset)); size_t handleEnd = fidOffset + file_handle.sizeof + handle.handle_bytes; if (handleEnd < offset + meta.event_len) { - ev.name = (cast(char*)(buffer.ptr + handleEnd)).fromStringz.to!string; + name = (cast(char*)(buffer.ptr + handleEnd)).fromStringz.to!string; } } infoOffset += hdr.len; } + + auto ev = new FanotifyEvent(meta, name, fd_); events ~= ev; offset += meta.event_len; } return events; } + // Метод для постобработки всех событий (вызывает postProcess на каждом) + void postProcessEvents(FanotifyEvent[] events) + { + foreach (ev; events) + { + ev.postProcess(); + } + } + + // Геттер для fd (если нужно внешне, но лучше использовать методы) @property int handle() const { - return fd; + return fd_; } }