Промежуток 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 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);
// Обработка 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)
@ -52,5 +69,9 @@ void main()
if (e.isDelete)
writeln(" - Удаление файла/директории");
}
// Опционально: вызов постобработки
e.postProcess();
}
}
}

View file

@ -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;
}
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
{
private int fd = -1;
private int fd_ = -1;
// Конструктор: инициализация с флагами
this(uint initFlags, uint eventFFlags = O_RDONLY | O_LARGEFILE)
{
fd = fanotify_init(initFlags, eventFFlags);
enforce(fd >= 0, "Ошибка инициализации fanotify: " ~ to!string(fd));
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_;
}
}