module dfanotify; public import fanotify; import core.sys.posix.unistd : read, 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; import std.conv : to; import core.stdc.errno : errno; import core.stdc.string : strerror; import core.stdc.stdint; struct FanotifyEvent { fanotify_event_metadata meta; string name; @property uint64_t mask() const { return meta.mask; } @property int eventFd() const { return meta.fd; } @property int pid() const { return meta.pid; } bool isOpen() const { return (mask & FAN_OPEN) != 0; } bool isModify() const { return (mask & FAN_MODIFY) != 0; } bool isCloseWrite() const { return (mask & FAN_CLOSE_WRITE) != 0; } bool isCloseNoWrite() const { return (mask & FAN_CLOSE_NOWRITE) != 0; } bool isAccess() const { return (mask & FAN_ACCESS) != 0; } bool isCreate() const { return (mask & FAN_CREATE) != 0; } bool isDelete() const { return (mask & FAN_DELETE) != 0; } } class Fanotify { 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)); } ~this() { if (fd >= 0) { 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); } } FanotifyEvent[] readEvents(size_t bufferSize = 4096) { ubyte[] buffer = new ubyte[bufferSize]; ssize_t len = read(fd, buffer.ptr, buffer.length); if (len <= 0) { return []; } FanotifyEvent[] events; size_t offset = 0; while (offset + FAN_EVENT_METADATA_LEN <= len) { 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); size_t infoOffset = offset + fanotify_event_metadata.sizeof; while (infoOffset < offset + meta.event_len) { 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) { size_t fidOffset = infoOffset + fanotify_event_info_header.sizeof + __kernel_fsid_t.sizeof; 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; } } infoOffset += hdr.len; } events ~= ev; offset += meta.event_len; } return events; } @property int handle() const { return fd; } }