Форум программистов, компьютерный форум, киберфорум
Loafer
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Знакомство с fanotify

Запись от Loafer размещена 16.08.2021 в 13:51

В ядре Linux есть подсистема, которая называется fanotify. Данная подсистема позволяет ставить под мониторинг файловые операции в операционной системе (ОС) над определенными объектами файловой системы: файлы, директории и точки монтирования. Основным объектом в данной подсистеме является внутренний объект ядра, который называется fanotify notification group или просто fanotify group (FG). FG представляет собой список объектов файловой системы, которые находятся под мониторингом. Каждый объект в этом списке называется entry. Entry для файла или директории представляет собой inode, а для точки монтирования - это mount ID. Для каждого entry существуют две битовые маски: mark и ignore. Битовая маска mark устанавливает список событий, которые находятся под мониторингом. Например, событие чтения из файла или событие открытия файла или директории. Битовая маска ignore нужна для того, чтобы исключить из мониторинга какие-то объекты файловой системы. Например, у нас под мониторингом находится большая иерархия файлов и нам необходимо какой-то файл добавить в исключение из мониторинга.
Надо отметить тот факт, что подсистема fanotify очень часто используется для создания антивирусного ПО. Работа с данной подсистемой осуществляется через event queue - очередь событий. То есть, если мы реализуем ПО, которое занимается мониторингом событий на объектах файловой системы, то эти самые события считываются из очереди событий. Примеры событий мы уже приводили ранее: например, чтение или открытие файла. Событий существует два типа: notification (информативные события) и permission (события, требующие разрешения доступа). С точки зрения программного кода, события представляют собой структуру языка C fanotify_event_metadata.
Информативные события не требуют никакой обработки со стороны разработчика, пишущего ПО для мониторинга. Эти события носят чисто характер нотификаций о некоторой операции, которая произошла в файловой системе. Разработчик может использовать эту информацию, например, для вывода в лог истории операций. Единственное, о чем нельзя забывать, что надо закрыть файловый дескриптор в структуре fanotify_event_metadata. Это файловый дескриптор того объекта файловой системы, событие по которому пришло.
События, требующие разрешения доступа, обрабатываются по другому. Как только разработчик считает их из очереди событий, данные события помещаются в хранилище, которое называется internal list (внутренний список). В данном внутреннем списке события лежат до тех пор, пока:
  1. разработчик не ответит fanotify, разрешает он или запрещает операцию над объектом файловой системы;
  2. не будет закрыт файловый дескриптор самого FG.

Ответ о разрешении или запрете операции над объектом файловой системы представляет собой структуру языка C fanotify_response. Данная структура должна быть записана в файловый дескриптор FG.
Здесь я приведу небольшой и тривиальный пример работы с подсистемой fanotify:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <fcntl.h>
#include <sys/fanotify.h>
#include <unistd.h>
 
#include <cstdlib>
#include <cstring>
#include <iostream>
 
using namespace std;
 
void ExitProgram() {
  cerr << strerror(errno) << endl;
  exit(EXIT_FAILURE);
}
 
int main() {
  /*
  Набор флагов, отвечающий за установку notification class'а слушающего приложения,
  а также установка повередения файлового дескриптора.
  */
  unsigned int flags =
    FAN_CLASS_CONTENT | // notification class, который нужен, если создается антивирус
    FAN_UNLIMITED_QUEUE; // флаг поведения, убирающий ограничеие на размер очереди событий
 
  /*
  Набор флагов, отвечающий за файловый статус файлового дескриптора.
  Это те же флаги, которые передаются при открытии обычного файла
  системным вызовом open.
  */
  unsigned int event_f_flags = O_RDONLY | O_LARGEFILE;
 
  /*
  Создаем внуренний объект ядра ОС, который называется fanotify notification group.
  Функция fanotify_init возвращает файловый дескриптор для доступа к очереди событий,
  которая принадлежит только что созданной fanotify notification group.
  */
  int fng = fanotify_init(flags, event_f_flags);
  if (fng == -1) {
    ExitProgram();
  }
  cout << "fanotify_init\n";
 
  /*
  Набор флагов, которые описывают, должны ли быть взведены флаги mark и/или
  ignore для определенного набора событий для объекта файловой системы в fanotify
  notification group. Объект файловой системы передается двумя последними параметрами
  в функции fanotify_mark.
  */
  flags = FAN_MARK_ADD | FAN_MARK_MOUNT;
 
  /*
  Набор флагов, которые описывают те события, которые должны быть промаркированы
  или проигнорированы в данном fanotify notification group. Маркирование или
  игнорирование событий задается с помощью проедыдещего набора флагов flags.
  */
  uint64_t mask = FAN_ACCESS_PERM;
 
  if (fanotify_mark(fng, flags, mask, AT_FDCWD, "/root/temp/loopfs") == -1) {
    ExitProgram();
  }
  cout << "fanotify_mark\n";
 
  // Установим исключение на один из объектов в папке /root/temp/loopfs
  if (fanotify_mark(fng, FAN_MARK_ADD | FAN_MARK_IGNORED_MASK, FAN_ACCESS_PERM, AT_FDCWD, "/root/temp/loopfs/folder/subfolder/test1.txt") == -1) {
    ExitProgram();
  }
  cout << "fanotify_mark\n";
  
  struct fanotify_event_metadata buffer [200];
  const struct fanotify_event_metadata* metadata = nullptr;
 
  // Читаем событие из очереди событий, которая принадлежит fanotify notification group
  size_t length = read(fng, buffer, sizeof(buffer));
  if (length == -1) {
    ExitProgram();
  }
  cout << "length = " << length << endl;
 
  metadata = buffer;
  
  // Начинаем обрабатывать прочитанные события
  while (FAN_EVENT_OK(metadata, length)) {
 
    // Проверяем версию структуры fanotify_event_metadata
    if (metadata->vers != FANOTIFY_METADATA_VERSION) {
      cerr << "Mismatch of fanotify metadata version\n";
      return EXIT_FAILURE;
    }
 
    // Проверяем, что файловый дестриптом объекта файловой системы корректен
    if (metadata->fd > 0) {
 
      // Проверямем, что полученное событие это событие FAN_ACCESS_PERM
      if (metadata->mask & FAN_ACCESS_PERM) {
        cout << "FAN_ACCESS_PERM: fd = " << metadata->fd << endl;
 
        /*
        Подготавливаем ответ по событию - резрешить или запретить
        (В данном случае мы запрещаем действие над объектом файловой системы)
        */
        struct fanotify_response response;
        response.fd = metadata->fd;
        response.response = FAN_DENY;
 
        // Отправляем ответ
        if (write(fng, &response, sizeof(response)) == -1) {
          ExitProgram();
        }
 
        // Закрываем файловый дескриптор объекта операционной системы
        if (close(metadata->fd) == -1) {
          ExitProgram();
        }
      }
    }
    else {
      cerr << "Invalid metadata->fd\n";
    }
 
    // Переходим к следующему событию
    metadata = FAN_EVENT_NEXT(metadata, length);
  }
 
  // Закрываем файловый дескриптор fanotify notification group
  if (close(fng) == -1) {
    ExitProgram();
  }
  cout << "SUCCESS\n";
 
  return EXIT_SUCCESS;
}
Стоит отменить тот факт, что данная подсистема имеет ряд ограничений. Например:
  1. Нельзя поставить под мониторинг директорию вместе со всеми файлами и поддиректориями, которые находятся в данной директории. Чтобы такое осуществить, директория должна представлять собой точку монтирования, что неудобно в некоторых случаях.
  2. Также нельзя исключить из сканирования директорию. Исключение из сканирования доступно только для файлов. Чтобы исключить из сканирования директорию необходимо превратить ее в точку монтирования, например, с помощью mount bind, что опять неудобно.
Размещено в CentOS
Показов 4257 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru