Linux repositories inspector

fanotify(7) - Russkiy

Linux
2019-08-02

man-pages-ru

Russian man pages from the Linux Documentation Project

manpages

Manual pages about using a GNU/Linux system

man-pages

Linux kernel and C library user-space interface documentation

ИМЯ

fanotify - отслеживание событий в файловой системе

ОПИСАНИЕ

Программный интерфейс fanotify уведомляет о событиях в файловой системе и перехватывает их. Например, его можно использовать для сканирования файлов на вирусы и управления иерархическим хранилищем. В настоящее время, поддерживается только ограниченный набор событий. В частности, не поддерживаются события создания, удаления и перемещения (о программном интерфейсе для этих событий смотрите в inotify(7)).
Дополнительные возможности по сравнению с программным интерфейсом inotify(7): способность отслеживать все объекты в смонтированной файловой системе, давать права на доступ и читать или изменять файлы перед тем как доступ получат другие приложения.
В программный интерфейс входят следующие системные вызовы: fanotify_init(2), fanotify_mark(2), read(2), write(2) и close(2).

Вызовы fanotify_init(), fanotify_mark() и группы уведомлений

Системный вызов fanotify_init(2) создаёт и инициализирует группу уведомления fanotify и возвращает указывающий на неё файловый дескриптор.
Группа уведомления fanotify — это внутренний объект ядра, в котором хранится список файлов, каталогов, файловых систем и точек монтирования, для которых должны создаваться события.
У каждой записи в группе уведомления fanotify есть две битовые маски: меток и игнорирования. В маске меток указывается для каких действий на файлами должны создаваться события. В маске игнорирования указывается для каких действий не должны создаваться события. Имея маски таких типов можно пометить файловую систему, точку монтирования или каталог для получения событий, и в тоже время игнорировать события для определённых объектов в этой точке монтирования или каталоге.
Системный вызов fanotify_mark(2) добавляет файл, каталог, файловую систему или точку монтирования в группу уведомления и задаёт какие события должны отслеживаться (или игнорироваться), или удаляет или изменяет нужную запись.
Возможное применение маски игнорирования — кэш файлов. Интересующие события для файлового кэша — изменение файла и закрытие. Для этого добавляем кэшируемый каталог или точку монтирования для приёма этих событий. После получения первого события об изменении файла, соответствующая запись кэша помечается как недействительная. Дальнейшие события об изменении файла нас не интересуют, пока файл не будет закрыт. Для этого событие об изменении можно добавить в маску игнорирования. При получении события о закрытии, событие об изменении можно удалить из маски игнорирования и запись файлового кэша можно обновить.
Записи в группе уведомления fanotify ссылаются на файл и каталог по номеру иноды (inode), а на точку монтирования — через ID монтирования. При переименовании или перемещении файла или каталога внутри той же точки монтирования соответствующая запись остаётся. Если файл или каталог удаляется или перемещается в другую точку монтирования, или если файловая система или точка монтирования размонтируется, то соответствующая запись удаляется.

Очередь событий

Для возникающих событий с объектами файловой системы, которые отслеживаются группой уведомления, система fanotify генерирует события и помещает их в очередь. После этого события можно прочитать (с помощью read(2) и подобных) из файлового дескриптора fanotify, возвращённого fanotify_init(2).
Генерируется два типа событий: события уведомления и события доступа. Уведомляющие события просто информируют и не требуют действия от принявшего приложения, за исключением того, что файловый дескриптор, передаваемый в событии общего типа, должен быть закрыт. Закрытие файловых дескрипторов для каждого события применяется только приложениями, которые инициализировали fanotify без использования FAN_REPORT_FID (смотрите ниже). События доступа запрашивают получившее приложение о разрешении доступа к файлу. Для этих событий получатель должен написать ответ, давать ли доступ или нет.
Событие удаляется из очереди событий группы fanotify после прочтения. События доступа, которые были прочитаны, остаются во внутреннем списке группы fanotify до тех пор, пока решение о доступе не будет записано в файловый дескриптор fanotify, или файловый дескриптор fanotify не будет закрыт.

Чтение событий fanotify

Вызов read(2) с файловым дескриптором, полученным от fanotify_init(2), блокирует выполнение (если не указан флаг FAN_NONBLOCK в вызове fanotify_init(2)) до тех пор, пока не произойдёт файловое событие или вызов не будет прерван сигналом (смотрите signal(7)).
При указании флага FAN_REPORT_FID в fanotify_init(2) структуры данных возвращаются слушателю событий в каждом событии. После успешного выполнения read(2) буфер чтения содержит одну или более следующих структур:
struct fanotify_event_metadata {
__u32 event_len;
__u8 vers;
__u8 reserved;
__u16 metadata_len;
__aligned_u64 mask;
__s32 fd;
__s32 pid; };
Если в fanotify_init(2) среди флагов указан и FAN_REPORT_FID, то вы также должны ожидать структуру после общей структуры fanotify_event_metadata в буфере чтения:
struct fanotify_event_info_fid {
struct fanotify_event_info_header hdr;
__kernel_fsid_t fsid;
unsigned char file_handle[0]; };
Для увеличения производительности рекомендуется использовать буфер большого размера (например, 4096 байт) для того, чтобы получить несколько событий за один вызов read(2).
Возвращаемое read(2) значение — количество байт помещённых в буфер, или -1 в случае ошибки (но смотрите ДЕФЕКТЫ).
Поля структуры fanotify_event_metadata:
event_len
Длина данных текущего события и смещение на следующее событие в буфере. Без FAN_REPORT_FID значение event_len всегда равно FAN_EVENT_METADATA_LEN. С FAN_REPORT_FID значение event_len также включает идентификатор файла переменной длины.
vers Номер версии структуры. Он должен сравниваться с FANOTIFY_METADATA_VERSION для проверки того, что структуры, возвращаемые во время выполнения, соответствуют структурам, определённым во время компиляция. В случае несоответствия приложение должно прекратить попытки использовать файловый дескриптор fanotify.
reserved
Не используется.
metadata_len
Длина структуры. Это поле было добавлено для облегчения реализации необязательных заголовков разных типов событий. В текущей реализации такие необязательные заголовки отсутствуют.
mask Битовая маска, описывающая событие (смотрите далее).
fd Открытый файловый дескриптор объекта доступа или FAN_NOFD, если возникло переполнение очереди. Если файловый дескриптор fanotify инициализирован с использованием FAN_REPORT_FID,то приложения должны ожидать, что это значение равно FAN_NOFD у каждого полученного события. Файловый дескриптор можно использовать для доступа к содержимому отслеживаемого файла или каталога. Читающее приложение ответственно за закрытие этого файлового дескриптора.
Когда вызывается fanotify_init(2) вызывающий может указать (в аргументе event_f_flags) различные флаги состояния файла, которые будут установлены на открытом файловом дескрипторе, соответствующем этому файловому дескриптору. Также, на отрываемом файловом дескрипторе устанавливается (внутри ядра) флаг состояния файла FMODE_NONOTIFY. Этот флаг подавляет генерацию событий fanotify. Таким образом, когда получатель события fanotify обратится к отслеживаемому файлу или каталогу через этот файловый дескриптор, дополнительных событий создано не будет.
pid Если в fanotify_init(2) установлен флаг FAN_REPORT_TID, то это TID нити, из-за которой возникло событие. В противном случае это PID процесса, из-за которой возникло событие.
Программа, слушающая события fanotify, может сравнить этот PID с PID, возвращаемым getpid(2), для проверки, что событие не возникло из-за самого слушающего, а из-за доступа к файлу другого процесса.
В битовой маске mask указывают события, произошедшие с одиночным объектом файловой системы. В маске может быть установлено несколько бит, если было более одного события с отслеживаемым объектом файловой системы. В частности, возникшие друг за другом события с одним объектом файловой системы и произошедшие из-за одного процесса могут быть объединены в одно событие, за исключением того, что два события доступа никогда не объединяются в одном элементе очереди.
Биты маски mask:
FAN_ACCESS
Доступ (на чтение) к файлу или каталогу (но смотрите ДЕФЕКТЫ).
FAN_OPEN
Файл или каталог открыт.
FAN_OPEN_EXEC
Файл открыт для выполнения. Смотрите ЗАМЕЧАНИЯ в fanotify_mark(2).
FAN_ATTRIB
Метаданные файла или каталога изменены.
FAN_CREATE
Создан дочерний файл или каталог в отслеживаемом родителе.
FAN_DELETE
Удалён дочерний файл или каталог в отслеживаемом родителе.
FAN_DELETE_SELF
Отслеживаемый файл или каталог был удалён.
FAN_MOVED_FROM
Дочерний файл или каталог был перемещён из отслеживаемого родительского каталога.
FAN_MOVED_TO
Дочерний файл или каталог был помещён в отслеживаемый родительский каталог.
FAN_MOVE_SELF
Отслеживаемый файл или каталог был перемещён.
FAN_MODIFY
Файл изменён.
FAN_CLOSE_WRITE
Файл, открытый на запись (O_WRONLY или O_RDWR), закрыт.
FAN_CLOSE_NOWRITE
Файл или каталог, открытый только для чтения (O_RDONLY), закрыт.
FAN_Q_OVERFLOW
Очередь событий превысила ограничение в 16384 записи. Это ограничение можно изменить, указав флаг FAN_UNLIMITED_QUEUE при вызове fanotify_init(2).
FAN_ACCESS_PERM
Приложение хочет прочитать файл или каталог, например, с помощью read(2) или readdir(2). Читатель события должен написать ответ (описано далее) о разрешении доступа к объекту файловой системы.
FAN_OPEN_PERM
Приложение хочет открыть файл или каталог. Читатель события должен написать ответ о разрешении открытия объекта файловой системы.
FAN_OPEN_EXEC_PERM
Приложение хочет открыть файл для выполнения. Читатель должен написать ответ о разрешении открытия объекта файловой системы для выполнения. Смотрите ЗАМЕЧАНИЯ в fanotify_mark(2).
Для проверки любого события закрытия может использоваться следующая битовая маска:
FAN_CLOSE
Файл закрыт. Это синоним:
FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE
Для проверки любого события перемещения может использоваться следующая битовая маска:
FAN_MOVE
Файл или каталог был перемещён. Это синоним для:
FAN_MOVED_FROM | FAN_MOVED_TO
Поля структуры fanotify_event_info_fid:
hdr Структура типа fanotify_event_info_header. Это общий заголовок, содержащий информацию для описания дополнительной информации, присоединяемой к событию. Например, когда файловый дескриптор fanotify создан с использованием FAN_REPORT_FID, поле info_type этого заголовка равно FAN_EVENT_INFO_TYPE_FID. Слушатели события могут использовать это поле для проверки того, что полученная дополнительная информация о событии имеет правильный тип. Также fanotify_event_info_header содержит поле len. В текущей реализации значение len всегда равно (event_len - FAN_EVENT_METADATA_LEN).
fsid Уникальный идентификатор файловой системы, содержащей объект, связанный с событием. Это структура имеет тип __kernel_fsid_t и содержит те же значения что и f_fsid при вызове statfs(2).
file_handle
Структура переменной длины, имеет тип file_handle. Это закрытый описатель, который соответствует заданному объекту файловой системы, возвращается name_to_handle_at(2). Её можно использовать как уникально определяемую файл в файловой системе и передавать аргументом в open_by_handle_at(2). Заметим, что для событий элементов каталога, таких как FAN_CREATE, FAN_DELETE, и FAN_MOVE, значение file_handle описывает изменяемый каталог, а не созданный/удалённый/перемещённый дочерний объект. События FAN_ATTRIB, FAN_DELETE_SELF и FAN_MOVE_SELF будут содержать информацию file_handle о дочернем объекте, если дочерний объект отслеживается.
Следующие макросы позволяют обходить буфер с метаданными событий fanotify, возвращаемый read(2) из файлового дескриптора fanotify:
FAN_EVENT_OK(meta, len)
Этот макрос сверяет оставшуюся длину len буфера meta с длиной структуры метаданных и полем event_len из первой структуры метаданных в буфере.
FAN_EVENT_NEXT(meta, len)
Этот макрос использует длину из поля event_len структуры метаданных, на которую указывает meta, для вычисления адреса следующей структуры метаданных, которая находится после meta. В поле len указано количество байт метаданных, оставшихся в буфере. Макрос возвращает указатель на следующую структуру метаданных после meta и уменьшает len на количество байт в структуре метаданных, которая была пропущена (т. е., вычитает meta->event_len из len).
Дополнительно есть:
FAN_EVENT_METADATA_LEN
Этот макрос возвращает размер (в байтах) структуры fanotify_event_metadata. Это минимальный размер (и, в настоящее время, единственный) метаданных любого события.

Отслеживание событий через файловый дескриптор fanotify

Когда возникает событие fanotify файловый дескриптор fanotify помечается как доступный для чтения при его передаче в epoll(7), poll(2) или select(2).

Работа с событиями доступа

Для событий доступа приложение должно записать (write(2)) в файловый дескриптор fanotify следующую структуру:
struct fanotify_response {
__s32 fd;
__u32 response; };
Поля этой структуры имеют следующее назначение:
fd Файловый дескриптор из структуры fanotify_event_metadata.
response
В этом поле указывает о разрешении доступа или запрещении. Данное значение должно быть равно FAN_ALLOW, чтобы разрешить операцию с файлом, или FAN_DENY для запрета.
Если доступ запрещается, то запрашивающее приложение получит ошибку EPERM.

Закрытие файлового дескриптора fanotify

Когда все файловые дескрипторы, указывающие на группу уведомления fanotify, закрыты, группа fanotify освобождается и её ресурсы становятся доступны ядру для повторного использования. После close(2) все оставшиеся непросмотренные события доступа будут разрешены.

/proc/[pid]/fdinfo

Файл /proc/[pid]/fdinfo/[fd] содержит информацию о метках fanotify для файлового дескриптора fd процесса pid Подробности смотрите в proc(5).

ОШИБКИ

Кроме обычных ошибок read(2) при чтении из файлового дескриптора fanotify могут возникать следующие ошибки:
EINVAL Буфер слишком мал для хранения события.
EMFILE Достигнуто максимальное попроцессное количество открытых файлов. Смотрите описание RLIMIT_NOFILE в getrlimit(2).
ENFILE Достигнут предел на общее количество открытых файлов в системе. Смотрите /proc/sys/fs/file-max в proc(5).
ETXTBSY
Эта ошибка возвращается read(2), если при вызове fanotify_init(2) в аргументе event_f_flags был указан O_RDWR или O_WRONLY и произошло событие с отслеживаемым файлом, который в данный момент выполняется.
Кроме обычных ошибок write(2) при записи в файловый дескриптор fanotify могут возникать следующие ошибки:
EINVAL Свойство для проверки прав доступа fanotify не включено в настройках ядра или некорректное значение response в структуре ответа.
ENOENT Некорректный файловый дескриптор fd в структуре ответа. Это может происходить, когда ответ на право доступа уже был записан.

ВЕРСИИ

Программный интерфейс fanotify представлен в версии 2.6.36 ядра Linux и включён в версии 2.6.37. Поддержка fdinfo была добавлена в версии 3.8.

СООТВЕТСТВИЕ СТАНДАРТАМ

Программный интерфейс fanotify есть только в Linux.

ЗАМЕЧАНИЯ

Программный интерфейс fanotify доступен только, если ядро собрано с включённым параметром настройки CONFIG_FANOTIFY. Также, работа с доступом в fanotify доступна только, если включён параметр настройки CONFIG_FANOTIFY_ACCESS_PERMISSIONS.

Ограничения и подводные камни

Fanotify сообщает только о событиях, которые возникли при использовании пользовательскими программами программного интерфейса файловой системы. Поэтому события об обращении к файлам в сетевых файловых системах не отлавливаются.
Программный интерфейс fanotify не сообщает о доступе и изменениях, которые могут произойти из-за mmap(2), msync(2) и munmap(2).
События для каталогов создаются только, если сам каталог открывается, читается и закрывается. Добавление, удаление и изменение потомков отслеживаемого каталога не приводит к возникновению событий.
Fanotify не следит за каталогами рекурсивно: чтобы следить за подкаталогами каталога, нужно их явно пометить (и, заметим, что программный интерфейс fanotify не позволяет отслеживать создание подкаталога, что затрудняет рекурсивное слежение). Отслеживание точек монтирования позволяет следить за всем деревом каталогов. Отслеживание файловых систем позволяет следить за изменениями, сделанными в любом смонтированном экземпляре файловой системы.
Очередь событий может переполниться. В этом случае события теряются.

ДЕФЕКТЫ

До Linux 3.19, fallocate(2) не генерировал событий fanotify. Начиная с Linux 3.19, вызовы fallocate(2) генерируют событие FAN_MODIFY.
В Linux 3.17 существуют следующие дефекты:
* В Linux объект файловой системы может быть доступен через несколько путей, например, часть файловой системы может быть перемонтирована mount(8) с использованием параметра --bind. Ожидающий слушатель получит уведомления об объекте файловой системы только из запрошенной точки монтирования. О событиях из других точек уведомлений не поступит.
* При генерации события не делается проверка, что пользовательскому ID получающего процесса разрешено читать или писать в файл перед передачей файлового дескриптора на этот файл. Это представляет некоторый риск безопасности, когда у программ, выполняющихся непривилегированными пользователями, есть мандат CAP_SYS_ADMIN.
* Если вызов read(2) получает несколько событий из очереди fanotify и возникает ошибка, будет возвращена полная длина событий, которые были успешно скопированы в буфер пользовательского пространства до ошибки. Возвращаемое значение не будет равно -1, и в errno не записывается код ошибки. То есть читающее приложение не может обнаружить ошибку.

ПРИМЕР

Далее показано два примера программы, в которых продемонстрированоиспользование программного интерфейса fanotify.

Программа-пример: fanotify_example.c

В первой программе показано как использовать fanotify с информацией об событийном объекте, передаваемом в виде файлового дескриптора. Программа помечает точку монтирования, переданную в аргументе командной строки, и ждёт событий с типом FAN_OPEN_PERM и FAN_CLOSE_WRITE. При возникновении событий доступа выдаёт ответ FAN_ALLOW.
В сеансе оболочки далее показан пример запуска программы. В сеансе выполняется редактирование файла /home/user/temp/notes. Перед открытием файла возникает событие FAN_OPEN_PERM. После закрытия файла возникает событие FAN_CLOSE_WRITE. Выполнение программы заканчивается после нажатия пользователем клавиши ENTER.
# ./fanotify_example /home Нажмите enter для завершения работы. Ожидание событий. FAN_OPEN_PERM: Файл /home/user/temp/notes FAN_CLOSE_WRITE: Файл /home/user/temp/notes
Ожидание событий прекращено.

Исходный код программы: fanotify_example.c

#define _GNU_SOURCE /* для получения определения O_LARGEFILE */ #include <errno.h> #include <fcntl.h> #include <limits.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <sys/fanotify.h> #include <unistd.h>
/* читаем все доступные события fanotify из файлового дескриптора «fd» */
static void handle_events(int fd) {
const struct fanotify_event_metadata *metadata;
struct fanotify_event_metadata buf[200];
ssize_t len;
char path[PATH_MAX];
ssize_t path_len;
char procfd_path[PATH_MAX];
struct fanotify_response response;
/* проходим по всем событиям, которые можем прочитать
из файлового дескриптора fanotify */
for (;;) {
/* читаем несколько событий */
len = read(fd, (void *) &buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* проверяем, достигнут ли конец доступных данных */
if (len <= 0)
break;
/* выбираем первое событие в буфере */
metadata = buf;
/* проходим по всем событиям в буфере */
while (FAN_EVENT_OK(metadata, len)) {
/* проверяем, что структуры, использовавшиеся при сборке,
идентичны структурам при выполнении */
if (metadata->vers != FANOTIFY_METADATA_VERSION) {
fprintf(stderr,
"Версия метаданных fanotify не совпадает.\n");
exit(EXIT_FAILURE);
}
/* metadata->fd содержит или FAN_NOFD, указывающее
на переполнение очереди, или файловый дескриптор
(неотрицательное целое). Здесь мы просто игнорируем
переполнение очереди. */
if (metadata->fd >= 0) {
/* обрабатываем событие на право открытия */
if (metadata->mask & FAN_OPEN_PERM) {
printf("FAN_OPEN_PERM: ");
/* разрешаем открыть файл */
response.fd = metadata->fd;
response.response = FAN_ALLOW;
write(fd, &response,
sizeof(struct fanotify_response));
}
/* обрабатываем событие закрытия записываемого файла */
if (metadata->mask & FAN_CLOSE_WRITE)
printf("FAN_CLOSE_WRITE: ");
/* получаем и выводим имя файла, к которому
отслеживается доступ */
snprintf(procfd_path, sizeof(procfd_path),
"/proc/self/fd/%d", metadata->fd);
path_len = readlink(procfd_path, path,
sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = \(aq\0\(aq;
printf("Файл %s\n", path);
/* закрываем файловый дескриптор из события */
close(metadata->fd);
}
/* переходим на следующее событие */
metadata = FAN_EVENT_NEXT(metadata, len);
}
} }
int main(int argc, char *argv[]) {
char buf;
int fd, poll_num;
nfds_t nfds;
struct pollfd fds[2];
/* проверяем заданную точку монтирования */
if (argc != 2) {
fprintf(stderr, "Использование: %s ТОЧКА_МОНТИРОВАНИЯ\n",
argv[0]);
exit(EXIT_FAILURE);
}
printf("Нажмите enter для завершения работы.\n");
/* Создаём файловый дескриптор для доступа к fanotify API */
fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
O_RDONLY | O_LARGEFILE);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* Помечаем точку монтирования для:
- событий доступа перед открытием файлов
- событий уведомления после закрытия файлового дескриптора
для файла открытого для записи */
if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD,
argv[1]) == -1) {
perror("fanotify_mark");
exit(EXIT_FAILURE);
}
/* подготовка к опросу */
nfds = 2;
/* ввод с консоли */
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
/* ввод из fanotify */
fds[1].fd = fd;
fds[1].events = POLLIN;
/* цикл ожидания входящих событий */
printf("Ожидание событий.\n");
while (1) {
poll_num = poll(fds, nfds, -1);
if (poll_num == -1) {
if (errno == EINTR) /* прервано сигналом */
continue; /* перезапуск poll() */
perror("poll"); /* неожиданная ошибка */
exit(EXIT_FAILURE);
}
if (poll_num > 0) {
if (fds[0].revents & POLLIN) {
/* доступен ввод с консоли: опустошаем stdin и выходим */
while (read(STDIN_FILENO, &buf, 1) > 0 && buf != \(aq\n\(aq)
continue;
break;
}
if (fds[1].revents & POLLIN) {
/* доступны события fanotify */
handle_events(fd);
}
}
}
printf("Ожидание событий прекращено.\n");
exit(EXIT_SUCCESS); }

Программа-пример: fanotify_fid.c

Во второй программе показано как использовать fanotify с включённым FAN_REPORT_FID. Программа помечает объект файловой системы, переданный в аргументе командной строки, и ждёт возникновения события с типом FAN_CREATE. В маске событий задаётся какой тип объекта файловой системы ждать — файл или каталог. После того как все события из буфера будут прочитаны и правильно обработаны программа просто завершает работу.
В следующих сеансах показано два разных запуска программы с разными выполняемыми действиями над наблюдаемым объектом.
В первом сеансе ставится отметка на /home/user. После этого создаётся обычный файл /home/user/testfile.txt. В результате возникает событие FAN_CREATE, в котором передаётсяобъект отслеживаемого родительского каталога файла. Программа завершает выполнение после обработки всех захваченных и помещённых в буфер событий.
# ./fanotify_fid /home/user Ожидание событий. FAN_CREATE (создан файл): Каталог /home/user изменён. Все события успешно обработаны. Завершение программы.
$ touch /home/user/testing # в другом терминале
Во втором сеансе ставится отметка на /home/user. После этого создаётся каталог /home/user/testdir. В результате такого специального действия в программу передаются события FAN_CREATE и FAN_ONDIR.
# ./fanotify_fid /home/user Ожидание событий. FAN_CREATE | FAN_ONDIR (создан подкаталог):
Каталог /home/user изменён. Все события успешно обработаны. Завершение программы.
$ mkdir -p /home/user/testing # в другом терминале

Исходный код программы: fanotify_fid.c

#define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/fanotify.h> #include <unistd.h>
#define BUF_SIZE 256
int main(int argc, char **argv) {
int fd, ret, event_fd;
ssize_t len, path_len;
char path[PATH_MAX];
char procfd_path[PATH_MAX];
char events_buf[BUF_SIZE];
struct file_handle *file_handle;
struct fanotify_event_metadata *metadata;
struct fanotify_event_info_fid *fid;
if (argc != 2) {
fprintf(stderr, "Некорректное количество аргументов в командной строке.\n");
exit(EXIT_FAILURE);
}
/* создаём файловый дескриптор fanotify с флагом FAN_REPORT_FID для
того, чтобы программа могла принимать события fid. */
fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, 0);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* ставим метку на объект файловой системы, заданный в argv[1]. */
ret = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_ONLYDIR,
FAN_CREATE | FAN_ONDIR,
AT_FDCWD, argv[1]);
if (ret == -1) {
perror("fanotify_mark");
exit(EXIT_FAILURE);
}
printf("Ожидание событий.\n");
/* читаем события из очереди событий в буфер */
len = read(fd, (void *) &events_buf, sizeof(events_buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* обрабатываем все события в буфере */
for (metadata = (struct fanotify_event_metadata *) events_buf;
FAN_EVENT_OK(metadata, len);
metadata = FAN_EVENT_NEXT(metadata, len)) {
fid = (struct fanotify_event_info_fid *) (metadata + 1);
file_handle = (struct file_handle *) fid->handle;
/* проверим, что информация о событии правильного типа */
if (fid->hdr.info_type != FAN_EVENT_INFO_TYPE_FID) {
fprintf(stderr, "Получена информация о событии неожидаемого типа.\n");
exit(EXIT_FAILURE);
}
if (metadata->mask == FAN_CREATE)
printf("FAN_CREATE (создан файл):");
if (metadata->mask == FAN_CREATE | FAN_ONDIR)
printf("FAN_CREATE | FAN_ONDIR (создан подкаталог):");
/* metadata->fd присваивается FAN_NOFD, если включён
FAN_REPORT_FID. Чтобы получить файловый дескриптор для
файлового объекта, соответствующего событию, можно
использовать struct file_handle, которая находится
внутри fanotify_event_info_fid и системный вызов
open_by_handle_at(2). Проверка на ESTALE нужна, чтобы
учесть ситуацию, когда файловый описатель для объекта
был удалён до этого системного вызова. */
event_fd = open_by_handle_at(AT_FDCWD, file_handle, O_RDONLY);
if (ret == -1) {
if (errno == ESTALE) {
printf("Обработчик файл более недействителен. "
"Файл был удалён\n");
continue;
} else {
perror("open_by_handle_at");
exit(EXIT_FAILURE);
}
}
snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
event_fd);
/* получаем и выводим путь изменённой dentry */
path_len = readlink(procfd_path, path, sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = \(aq\0\(aq;
printf("\tКаталог \(aq%s\(aq изменён.\n", path);
/* закрываем связанный файловый дескриптор этого события */
close(event_fd);
}
printf("Все события успешно обработаны. Завершение программы.\n");
exit(EXIT_SUCCESS); }

СМОТРИТЕ ТАКЖЕ

⇧ Top