Промежуток 1

This commit is contained in:
Alexander Zhirov 2025-08-29 00:10:11 +03:00
parent ed0d2780c3
commit cb3f40deee
Signed by: alexander
GPG key ID: C8D8BE544A27C511
2 changed files with 171 additions and 57 deletions

View file

@ -1,46 +1,63 @@
import dfanotify; import dfanotify;
import std.stdio; import std.stdio;
import std.file : readLink;
import std.format : format; import std.format : format;
import core.sys.posix.fcntl : AT_FDCWD; import core.sys.posix.fcntl : AT_FDCWD;
import core.sys.posix.unistd : close;
import core.stdc.stdlib : exit; import core.stdc.stdlib : exit;
void main() void main()
{ {
Fanotify fan; Fanotify fan;
try { try
{
fan = new Fanotify( fan = new Fanotify(
FAN_CLASS_NOTIF | FAN_CLOEXEC | FAN_REPORT_FID | FAN_REPORT_DIR_FID | FAN_REPORT_NAME); FAN_CLASS_NOTIF | FAN_CLOEXEC | FAN_REPORT_FID | FAN_REPORT_DIR_FID | FAN_REPORT_NAME
} catch (Exception e) { );
}
catch (Exception e)
{
writeln(e.msg); writeln(e.msg);
exit(1); 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"); fan.mark(FAN_MARK_ADD | FAN_MARK_ONLYDIR, eventMask, AT_FDCWD, "/tmp/scripts");
} catch (Exception e) { }
catch (Exception e)
{
writeln(e.msg); writeln(e.msg);
exit(1); exit(1);
} }
writeln("Мониторинг запущен для /tmp/scripts..."); writeln(
"Мониторинг с разрешениями запущен для /tmp/scripts...");
while (true) while (true)
{ {
auto events = fan.readEvents(); auto events = fan.readEvents();
foreach (ref e; events) foreach (e; events)
{ {
string path = e.name.length ? e.name : "unknown"; string path = e.name.length ? e.name : "unknown";
writefln("Событие: mask=0x%x, pid=%d, name/path=%s", e.mask, e.pid, path); writefln("Событие: mask=0x%x, pid=%d, name/path=%s", e.mask, e.pid, path);
// Обработка permission-события
if (e.isOpenPerm)
{
writeln(" - Запрос на открытие файла. Отклоняем (предполагаем попытку записи).");
e.respond(FAN_DENY); // Отклонить (для FAN_ALLOW используйте FAN_ALLOW)
}
else
{
// Обычные уведомления (не permission)
if (e.isOpen) if (e.isOpen)
writeln(" - Открытие файла"); writeln(" - Открытие файла");
if (e.isAccess)
writeln(" - Доступ к файлу");
if (e.isModify) if (e.isModify)
writeln(" - Модификация файла"); writeln(" - Модификация файла");
if (e.isCloseWrite) if (e.isCloseWrite)
@ -52,5 +69,9 @@ void main()
if (e.isDelete) if (e.isDelete)
writeln(" - Удаление файла/директории"); writeln(" - Удаление файла/директории");
} }
// Опционально: вызов постобработки
e.postProcess();
}
} }
} }

View file

@ -2,7 +2,7 @@ module dfanotify;
public import fanotify; 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 core.sys.posix.fcntl : O_RDONLY, O_RDWR, O_LARGEFILE, AT_FDCWD;
import std.exception : enforce; import std.exception : enforce;
import std.string : toStringz, fromStringz; import std.string : toStringz, fromStringz;
@ -11,26 +11,53 @@ import core.stdc.errno : errno;
import core.stdc.string : strerror; import core.stdc.string : strerror;
import core.stdc.stdint; import core.stdc.stdint;
struct FanotifyEvent // Класс для представления события fanotify (ООП-стиль, с методами для проверки и обработки)
class FanotifyEvent
{ {
fanotify_event_metadata meta; private fanotify_event_metadata meta_;
string name; 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 @property uint64_t mask() const
{ {
return meta.mask; return meta_.mask;
} }
@property int eventFd() const @property int eventFd() const
{ {
return meta.fd; return meta_.fd;
} }
@property int pid() const @property int pid() const
{ {
return meta.pid; return meta_.pid;
} }
@property string name() const
{
return name_;
}
// Методы проверки событий (без ref)
bool isOpen() const bool isOpen() const
{ {
return (mask & FAN_OPEN) != 0; return (mask & FAN_OPEN) != 0;
@ -65,44 +92,97 @@ struct FanotifyEvent
{ {
return (mask & FAN_DELETE) != 0; return (mask & FAN_DELETE) != 0;
} }
bool isOpenPerm() const
{
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 class Fanotify
{ {
private int fd = -1; private int fd_ = -1;
// Конструктор: инициализация с флагами
this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE) this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE)
{ {
fd = fanotify_init(initFlags, eventFFlags); fd_ = fanotify_init(initFlags, eventFFlags);
enforce(fd >= 0, "Ошибка инициализации fanotify: " ~ to!string(fd)); enforce(fd_ >= 0, "Ошибка инициализации fanotify: " ~ strerror(errno)
.fromStringz.to!string);
} }
// Деструктор: автоматически закрывает fanotify fd
~this() ~this()
{ {
if (fd >= 0) if (fd_ >= 0)
{ {
close(fd); close(fd_);
fd = -1; fd_ = -1;
} }
} }
// Метод для добавления/удаления/модификации меток (управление событиями)
void mark(uint markFlags, uint64_t eventMask, int dirFd = AT_FDCWD, string path = null) void mark(uint markFlags, uint64_t eventMask, int dirFd = AT_FDCWD, string path = null)
{ {
const(char)* cPath = path ? path.toStringz() : null; const(char)* cPath = path ? path.toStringz() : null;
int res = fanotify_mark(fd, markFlags, eventMask, dirFd, cPath); int res = fanotify_mark(fd_, markFlags, eventMask, dirFd, cPath);
if (res == -1) enforce(res == 0, "Ошибка маркировки fanotify: " ~ strerror(errno)
{ .fromStringz.to!string);
string errMsg = "Ошибка маркировки fanotify: " ~ to!string(
res) ~ " (errno: " ~ to!string(errno) ~ ", " ~ strerror(errno)
.fromStringz.to!string ~ ")";
throw new Exception(errMsg);
}
} }
// Метод для чтения событий (возвращает массив объектов событий)
FanotifyEvent[] readEvents(size_t bufferSize = 4096) FanotifyEvent[] readEvents(size_t bufferSize = 4096)
{ {
ubyte[] buffer = new ubyte[bufferSize]; 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) if (len <= 0)
{ {
return []; return [];
@ -112,43 +192,56 @@ class Fanotify
size_t offset = 0; size_t offset = 0;
while (offset + FAN_EVENT_METADATA_LEN <= len) 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) if (meta.event_len < FAN_EVENT_METADATA_LEN || offset + meta.event_len > len)
{ {
break; break;
} }
FanotifyEvent ev = FanotifyEvent(*meta); string name;
size_t infoOffset = offset + fanotify_event_metadata.sizeof; size_t infoOffset = offset + fanotify_event_metadata.sizeof;
while (infoOffset < offset + meta.event_len) 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) if (hdr.len == 0 || infoOffset + hdr.len > offset + meta.event_len)
{ {
break; 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; 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; size_t handleEnd = fidOffset + file_handle.sizeof + handle.handle_bytes;
if (handleEnd < offset + meta.event_len) 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; infoOffset += hdr.len;
} }
auto ev = new FanotifyEvent(meta, name, fd_);
events ~= ev; events ~= ev;
offset += meta.event_len; offset += meta.event_len;
} }
return events; return events;
} }
// Метод для постобработки всех событий (вызывает postProcess на каждом)
void postProcessEvents(FanotifyEvent[] events)
{
foreach (ev; events)
{
ev.postProcess();
}
}
// Геттер для fd (если нужно внешне, но лучше использовать методы)
@property int handle() const @property int handle() const
{ {
return fd; return fd_;
} }
} }