This commit is contained in:
Alexander Zhirov 2025-08-21 20:09:16 +03:00
commit 97edc0d795
Signed by: alexander
GPG key ID: C8D8BE544A27C511
8 changed files with 314 additions and 0 deletions

16
.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
.dub
docs.json
__dummy.html
docs/
/fanotify
fanotify.so
fanotify.dylib
fanotify.dll
fanotify.a
fanotify.lib
fanotify-test-*
*.exe
*.pdb
*.o
*.obj
*.lst

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"editor.insertSpaces": false,
"editor.tabSize": 4,
"editor.detectIndentation": false
}

23
LICENSE Normal file
View file

@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

1
README.md Normal file
View file

@ -0,0 +1 @@
# fanotify

10
dub.json Normal file
View file

@ -0,0 +1,10 @@
{
"authors": [
"Alexander Zhirov"
],
"copyright": "Copyright © 2025, Alexander Zhirov",
"description": "The fanotify API provides notification and interception of filesystem events.",
"license": "BSL-1.0",
"name": "fanotify",
"targetType": "library"
}

5
dub.selections.json Normal file
View file

@ -0,0 +1,5 @@
{
"fileVersion": 1,
"versions": {
}
}

4
dub.settings.json Normal file
View file

@ -0,0 +1,4 @@
{
"defaultArchitecture": "x86_64",
"defaultCompiler": "ldc2"
}

250
source/fanotify.d Normal file
View file

@ -0,0 +1,250 @@
// Низкоуровневая обёртка над C API fanotify для D.
// Все значения взяты из uapi <linux/fanotify.h> (актуальная версия на основе предоставленного заголовка).
module fanotify;
import core.stdc.stdint; // uint64_t и т.п.
// ------------------------------------------------------------
// ФЛАГИ ДЛЯ fanotify_init(flags, event_f_flags)
// ------------------------------------------------------------
// Поведение файлового дескриптора группы
enum FAN_CLOEXEC = 0x0000_0001; // закрывать fd при exec()
enum FAN_NONBLOCK = 0x0000_0002; // неблокирующие read()
// Класс группы (определяет тип выдаваемых событий)
enum FAN_CLASS_NOTIF = 0x0000_0000; // только уведомления (не PERM)
enum FAN_CLASS_CONTENT = 0x0000_0004; // разрешения на доступ к СОДЕРЖИМОМУ (CONTENT)
enum FAN_CLASS_PRE_CONTENT = 0x0000_0008; // разрешения ДО доступа к содержимому (PRE_CONTENT)
// Deprecated - do not use this in programs and do not add new flags here!
deprecated enum FAN_ALL_CLASS_BITS = (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT);
// Лимиты очереди/меток
enum FAN_UNLIMITED_QUEUE = 0x0000_0010; // неограниченная очередь событий
enum FAN_UNLIMITED_MARKS = 0x0000_0020; // неограниченное число меток
enum FAN_ENABLE_AUDIT = 0x0000_0040; // включить аудит
// Формат отчётности в метаданных события
enum FAN_REPORT_PIDFD = 0x0000_0080; // добавлять PIDFD (если поддерживается ядром)
enum FAN_REPORT_TID = 0x0000_0100; // сообщать TID вместо PID
enum FAN_REPORT_FID = 0x0000_0200; // сообщать file-id (handle) вместо fd
enum FAN_REPORT_DIR_FID = 0x0000_0400; // сообщать dir file-id
enum FAN_REPORT_NAME = 0x0000_0800; // сообщать имя (ранее FAN_REPORT_DFID_NAME)
enum FAN_REPORT_TARGET_FID = 0x0000_1000; // сообщать target file-id
enum FAN_REPORT_FD_ERROR = 0x0000_2000; // event->fd может сообщать ошибки
enum FAN_REPORT_MNT = 0x0000_4000; // сообщать события монтирования
// Удобные сводные макросы
enum FAN_REPORT_DFID_NAME = (FAN_REPORT_DIR_FID | FAN_REPORT_NAME);
enum FAN_REPORT_DFID_NAME_TARGET = (FAN_REPORT_DFID_NAME | FAN_REPORT_FID | FAN_REPORT_TARGET_FID);
// Deprecated - do not use this in programs and do not add new flags here!
deprecated enum FAN_ALL_INIT_FLAGS =
(FAN_CLOEXEC | FAN_NONBLOCK | FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |
FAN_UNLIMITED_MARKS);
// Параметр event_f_flags для fanotify_init() — это флаги open(2):
// O_RDONLY / O_WRONLY / O_RDWR | O_LARGEFILE и т.п. (из <fcntl.h>).
// ------------------------------------------------------------
// ФЛАГИ ДЛЯ fanotify_mark(fanfd, flags, mask, dirfd, path)
// ------------------------------------------------------------
enum FAN_MARK_ADD = 0x0000_0001; // добавить/обновить метку
enum FAN_MARK_REMOVE = 0x0000_0002; // удалить метку
enum FAN_MARK_DONT_FOLLOW = 0x0000_0004; // не переходить по симлинкам (на path)
enum FAN_MARK_ONLYDIR = 0x0000_0008; // path должен быть каталогом
enum FAN_MARK_MOUNT = 0x0000_0010; // навесить на точку монтирования
enum FAN_MARK_IGNORED_MASK = 0x0000_0020; // задать игнорируемые события
enum FAN_MARK_IGNORED_SURV_MODIFY = 0x0000_0040; // игнор сохраняется при модификации
enum FAN_MARK_FLUSH = 0x0000_0080; // снять все метки у группы
enum FAN_MARK_FILESYSTEM = 0x0000_0100; // навесить на всю ФС
enum FAN_MARK_EVICTABLE = 0x0000_0200; // метка может быть evicted
enum FAN_MARK_IGNORE = 0x0000_0400; // игнорировать (взаимоисключающе с IGNORED_MASK, новое)
// These are NOT bitwise flags. Both bits can be used together.
enum FAN_MARK_INODE = 0x0000_0000; // на inode
enum FAN_MARK_MNTNS = 0x0000_0110; // на mount namespace
// Удобный макрос
enum FAN_MARK_IGNORE_SURV = (FAN_MARK_IGNORE | FAN_MARK_IGNORED_SURV_MODIFY);
// Deprecated - do not use this in programs and do not add new flags here!
deprecated enum FAN_ALL_MARK_FLAGS =
(FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_DONT_FOLLOW | FAN_MARK_ONLYDIR |
FAN_MARK_MOUNT | FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY |
FAN_MARK_FLUSH);
// ------------------------------------------------------------
// БИТЫ МАСКИ СОБЫТИЙ (mask) ДЛЯ fanotify_mark()
// ------------------------------------------------------------
// «Обычные» события
enum FAN_ACCESS = 0x0000_0001; // чтение (read)
enum FAN_MODIFY = 0x0000_0002; // запись/изменение
enum FAN_ATTRIB = 0x0000_0004; // метаданные (chmod/chown/utime)
enum FAN_CLOSE_WRITE = 0x0000_0008; // закрыт после записи
enum FAN_CLOSE_NOWRITE = 0x0000_0010; // закрыт без записи
enum FAN_OPEN = 0x0000_0020; // открыт
enum FAN_MOVED_FROM = 0x0000_0040; // перемещён «из»
enum FAN_MOVED_TO = 0x0000_0080; // перемещён «в»
enum FAN_CREATE = 0x0000_0100; // создан
enum FAN_DELETE = 0x0000_0200; // удалён
enum FAN_DELETE_SELF = 0x0000_0400; // удалён сам наблюдаемый объект
enum FAN_MOVE_SELF = 0x0000_0800; // перемещён сам наблюдаемый объект
enum FAN_OPEN_EXEC = 0x0000_1000; // открыт для исполнения
enum FAN_Q_OVERFLOW = 0x0000_4000; // переполнение очереди
enum FAN_FS_ERROR = 0x0000_8000; // ошибки ФС
// deprecated #define FAN_DIR_MODIFY 0x00080000 /* Deprecated (reserved) */
enum FAN_PRE_ACCESS = 0x0010_0000; // pre-content access hook
enum FAN_MNT_ATTACH = 0x0100_0000; // mount attached
enum FAN_MNT_DETACH = 0x0200_0000; // mount detached
enum FAN_RENAME = 0x1000_0000; // file renamed
// События «разрешения» (требуют CLASS_CONTENT/PRE_CONTENT)
enum FAN_OPEN_PERM = 0x0001_0000; // запрос на open() (исправлено имя/значение)
enum FAN_ACCESS_PERM = 0x0002_0000; // запрос на доступ к содержимому (исправлено имя/значение)
enum FAN_OPEN_EXEC_PERM = 0x0004_0000; // запрос на open(O_EXEC)
// Модификаторы области действия маски
enum FAN_EVENT_ON_CHILD = 0x0800_0000; // события на дочерние объекты каталога
enum FAN_ONDIR = 0x4000_0000; // событие относится к каталогу
// Составные удобные маски
enum FAN_CLOSE = (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE);
enum FAN_MOVE = (FAN_MOVED_FROM | FAN_MOVED_TO);
// Deprecated - do not use this in programs and do not add new flags here!
deprecated enum FAN_ALL_EVENTS =
(FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN);
// Deprecated - do not use this in programs and do not add new flags here!
deprecated enum FAN_ALL_PERM_EVENTS =
(FAN_OPEN_PERM | FAN_ACCESS_PERM);
// Deprecated - do not use this in programs and do not add new flags here!
deprecated enum FAN_ALL_OUTGOING_EVENTS =
(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_Q_OVERFLOW);
// ------------------------------------------------------------
// МЕТАДАННЫЕ СОБЫТИЯ
// ------------------------------------------------------------
struct fanotify_event_metadata {
uint event_len; // общая длина записи
ubyte vers; // версия (FANOTIFY_METADATA_VERSION)
ubyte reserved; // зарезервировано
ushort metadata_len; // длина этой структуры
uint64_t mask; // __aligned_u64: биты события/модификаторов
int fd; // файловый дескриптор (или FAN_NOFD)
int pid; // PID или TID (если FAN_REPORT_TID)
}
enum FANOTIFY_METADATA_VERSION = 3;
enum FAN_NOFD = -1;
enum FAN_NOPIDFD = FAN_NOFD;
enum FAN_EPIDFD = -2;
// ------------------------------------------------------------
// ДОП. ИНФОРМАЦИЯ (FID / PIDFD / ERROR / etc.) В ХВОСТЕ СОБЫТИЯ
// ------------------------------------------------------------
struct fanotify_event_info_header {
ubyte info_type; // тип записей ниже
ubyte pad; // выравнивание
ushort len; // длина этой записи
}
// Типы info-записей (идут после metadata)
enum FAN_EVENT_INFO_TYPE_FID = 1; // только file handle
enum FAN_EVENT_INFO_TYPE_DFID_NAME = 2; // dir handle + имя объекта
enum FAN_EVENT_INFO_TYPE_DFID = 3; // dir handle
enum FAN_EVENT_INFO_TYPE_PIDFD = 4; // PIDFD (если FAN_REPORT_PIDFD)
enum FAN_EVENT_INFO_TYPE_ERROR = 5; // error info
enum FAN_EVENT_INFO_TYPE_RANGE = 6; // range info
enum FAN_EVENT_INFO_TYPE_MNT = 7; // mount info
// Special info types for FAN_RENAME
enum FAN_EVENT_INFO_TYPE_OLD_DFID_NAME = 10;
enum FAN_EVENT_INFO_TYPE_NEW_DFID_NAME = 12;
// (Reserved: 11 и 13 для OLD_DFID и NEW_DFID)
struct __kernel_fsid_t { int[2] val; }
// Общий заголовок для FID-записей
struct fanotify_event_info_fid {
fanotify_event_info_header hdr;
__kernel_fsid_t fsid; // файловая система
// далее переменной длины: unsigned char handle[]; (opaque struct file_handle)
}
// Совместимо с open_by_handle_at()
struct file_handle {
uint handle_bytes; // длина f_handle
int handle_type; // тип handle
ubyte[0] f_handle; // гибкий хвост (реальные байты handle идут сразу за структурой)
}
// Структура для PIDFD
struct fanotify_event_info_pidfd {
fanotify_event_info_header hdr;
int pidfd; // __s32
}
// Структура для ERROR
struct fanotify_event_info_error {
fanotify_event_info_header hdr;
int error; // __s32
uint error_count; // __u32
}
// Структура для RANGE
struct fanotify_event_info_range {
fanotify_event_info_header hdr;
uint pad; // __u32
uint64_t offset; // __u64
uint64_t count; // __u64
}
// Структура для MNT
struct fanotify_event_info_mnt {
fanotify_event_info_header hdr;
uint64_t mnt_id; // __u64
}
// ------------------------------------------------------------
// СТРУКТУРЫ ДЛЯ RESPONSE (для PERM-событий)
// ------------------------------------------------------------
struct fanotify_response {
int fd; // __s32
uint response; // __u32
}
// Дополнительная информация в response
enum FAN_RESPONSE_INFO_NONE = 0;
enum FAN_RESPONSE_INFO_AUDIT_RULE = 1;
struct fanotify_response_info_header {
ubyte type;
ubyte pad;
ushort len;
}
struct fanotify_response_info_audit_rule {
fanotify_response_info_header hdr;
uint rule_number; // __u32
uint subj_trust; // __u32
uint obj_trust; // __u32
}
// Legit responses
enum FAN_ALLOW = 0x01;
enum FAN_DENY = 0x02;
enum FAN_AUDIT = 0x10; // Bitmask to create audit record
enum FAN_INFO = 0x20; // Bitmask to indicate additional information
// Для DENY с errno
enum FAN_ERRNO_BITS = 8;
enum FAN_ERRNO_SHIFT = (32 - FAN_ERRNO_BITS);
enum FAN_ERRNO_MASK = ((1 << FAN_ERRNO_BITS) - 1);
// Макрос: FAN_DENY_ERRNO(err) = (FAN_DENY | ((((__u32)(err)) & FAN_ERRNO_MASK) << FAN_ERRNO_SHIFT))
// ------------------------------------------------------------
// ПРОТОТИПЫ ФУНКЦИЙ C API
// ------------------------------------------------------------
extern(C) int fanotify_init(uint flags, uint event_f_flags);
extern(C) int fanotify_mark(
int fanotify_fd,
uint flags,
uint64_t mask,
int dirfd, // AT_FDCWD или fd каталога
const(char)* pathname // путь или null при INODE/FILESYSTEM/MOUNT
);
// Для работы с FID: открыть объект по handle (требует CAP_DAC_READ_SEARCH)
extern(C) int open_by_handle_at(int mount_fd, const(file_handle)* handle, int flags);
// ------------------------------------------------------------
// HELPER FUNCTIONS (макросы для работы с буфером metadata)
// ------------------------------------------------------------
enum FAN_EVENT_METADATA_LEN = fanotify_event_metadata.sizeof;
// Макросы в D-стиле (как функции или шаблоны, но для простоты как enum/комментарии)
// FAN_EVENT_NEXT(meta, len): len -= meta.event_len; return cast(fanotify_event_metadata*) (cast(char*) meta + meta.event_len);
// FAN_EVENT_OK(meta, len): (len >= FAN_EVENT_METADATA_LEN) && (meta.event_len >= FAN_EVENT_METADATA_LEN) && (meta.event_len <= len)