fanotify_mark系统调用及示例

fanotify_mark - 文件系统通知标记

函数介绍

fanotify_mark是一个Linux系统调用,用于在fanotify实例上添加、修改或删除文件系统对象的监视标记。fanotify是Linux的文件系统通知机制,可以监控文件访问和修改事件。

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

fanotify_init系统调用及示例-CSDN博客

https://www.calcguide.tech/2025/08/23/fanotify-mark系统调用及示例/

函数原型

1
2
3
4
5
6
7
#include <sys/fanotify.h>
#include <fcntl.h>
#include <unistd.h>

int fanotify_mark(int fanotify_fd, unsigned int flags,
uint64_t mask, int dirfd, const char *pathname);

功能

在fanotify文件描述符上设置监视标记,用于监控指定文件系统对象的事件。

参数

  • int fanotify_fd: fanotify实例的文件描述符(通过fanotify_init获得)

unsigned int flags: 操作标志

  • FAN_MARK_ADD: 添加标记

  • FAN_MARK_REMOVE: 删除标记

  • FAN_MARK_FLUSH: 刷新所有标记

  • FAN_MARK_DONT_FOLLOW: 不跟随符号链接

  • FAN_MARK_ONLYDIR: 只监视目录

  • FAN_MARK_MOUNT: 监视整个挂载点

  • FAN_MARK_FILESYSTEM: 监视整个文件系统

uint64_t mask: 事件掩码,指定要监视的事件类型

  • FAN_ACCESS: 文件被访问

  • FAN_OPEN: 文件被打开

  • FAN_CLOSE_WRITE: 可写文件被关闭

  • FAN_CLOSE_NOWRITE: 只读文件被关闭

  • FAN_MODIFY: 文件被修改

  • FAN_OPEN_PERM: 文件打开权限检查

  • FAN_ACCESS_PERM: 文件访问权限检查

int dirfd: 目录文件描述符(用于相对路径)

  • AT_FDCWD: 使用当前工作目录

const char *pathname: 要监视的文件或目录路径

返回值

  • 成功时返回0

  • 失败时返回-1,并设置errno

特殊限制

  • 需要Linux 2.6.39以上内核支持

  • 需要CAP_SYS_ADMIN能力或特定权限

  • 某些操作需要root权限

相似函数

  • fanotify_init(): 初始化fanotify实例

  • inotify_add_watch(): inotify监视添加

  • read(): 读取fanotify事件

示例代码

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fanotify.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <poll.h>
#include <signal.h>

// 全局变量用于信号处理
volatile sig_atomic_t keep_running = 1;

// 信号处理函数
void signal_handler(int sig) {
printf("\n接收到信号 %d,准备退出...\n", sig);
keep_running = 0;
}

// 事件类型转换为字符串
const char* event_type_to_string(uint64_t mask) {
if (mask & FAN_ACCESS) return "ACCESS";
if (mask & FAN_OPEN) return "OPEN";
if (mask & FAN_CLOSE_WRITE) return "CLOSE_WRITE";
if (mask & FAN_CLOSE_NOWRITE) return "CLOSE_NOWRITE";
if (mask & FAN_MODIFY) return "MODIFY";
if (mask & FAN_OPEN_PERM) return "OPEN_PERM";
if (mask & FAN_ACCESS_PERM) return "ACCESS_PERM";
return "UNKNOWN";
}

int main() {
int fan_fd, fd, result;
char buffer&#91;4096];

printf("=== Fanotify_mark 函数示例 ===\n");

// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

// 示例1: 基本使用
printf("\n示例1: 基本使用\n");

// 创建测试文件
fd = open("test_fanotify.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, "test content for fanotify", 25);
close(fd);
printf("创建测试文件: test_fanotify.txt\n");
}

// 初始化fanotify实例
fan_fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY);
if (fan_fd == -1) {
if (errno == EPERM) {
printf("权限不足初始化fanotify: %s\n", strerror(errno));
printf("说明: 需要CAP_SYS_ADMIN能力或root权限\n");
unlink("test_fanotify.txt");
exit(EXIT_FAILURE);
} else {
perror("fanotify_init失败");
unlink("test_fanotify.txt");
exit(EXIT_FAILURE);
}
}
printf("成功初始化fanotify实例,文件描述符: %d\n", fan_fd);

// 示例2: 添加监视标记
printf("\n示例2: 添加监视标记\n");

// 监视当前目录下的测试文件
result = fanotify_mark(fan_fd, FAN_MARK_ADD,
FAN_OPEN | FAN_CLOSE | FAN_ACCESS | FAN_MODIFY,
AT_FDCWD, "test_fanotify.txt");
if (result == -1) {
perror("添加监视标记失败");
} else {
printf("成功添加监视标记到 test_fanotify.txt\n");
printf("监视事件: OPEN | CLOSE | ACCESS | MODIFY\n");
}

// 示例3: 监视目录
printf("\n示例3: 监视目录\n");

// 创建测试目录
if (mkdir("fanotify_test_dir", 0755) == -1 && errno != EEXIST) {
perror("创建测试目录失败");
} else {
printf("创建测试目录: fanotify_test_dir\n");

// 监视整个目录
result = fanotify_mark(fan_fd, FAN_MARK_ADD,
FAN_OPEN | FAN_CLOSE | FAN_ACCESS,
AT_FDCWD, "fanotify_test_dir");
if (result == 0) {
printf("成功添加目录监视标记\n");
}

// 在目录中创建文件进行测试
fd = open("fanotify_test_dir/test_file.txt", O_CREAT | O_WRONLY, 0644);
if (fd != -1) {
write(fd, "test file in directory", 22);
close(fd);
printf("在目录中创建测试文件\n");
}
}

// 示例4: 不同的标记操作
printf("\n示例4: 不同的标记操作\n");

// FAN_MARK_REMOVE - 删除标记
result = fanotify_mark(fan_fd, FAN_MARK_REMOVE,
FAN_OPEN | FAN_CLOSE,
AT_FDCWD, "test_fanotify.txt");
if (result == 0) {
printf("成功删除部分监视标记\n");
} else {
printf("删除监视标记失败: %s\n", strerror(errno));
}

// FAN_MARK_FLUSH - 刷新所有标记
result = fanotify_mark(fan_fd, FAN_MARK_FLUSH, 0, 0, NULL);
if (result == 0) {
printf("成功刷新所有监视标记\n");
}

// 重新添加标记进行后续测试
fanotify_mark(fan_fd, FAN_MARK_ADD,
FAN_OPEN | FAN_CLOSE | FAN_ACCESS,
AT_FDCWD, "test_fanotify.txt");

// 示例5: 错误处理演示
printf("\n示例5: 错误处理演示\n");

// 使用无效的fanotify文件描述符
result = fanotify_mark(999, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, "test.txt");
if (result == -1) {
if (errno == EBADF) {
printf("无效fanotify文件描述符错误处理正确: %s\n", strerror(errno));
}
}

// 使用无效的标志
result = fanotify_mark(fan_fd, 0x1000, FAN_OPEN, AT_FDCWD, "test.txt");
if (result == -1) {
if (errno == EINVAL) {
printf("无效标志错误处理正确: %s\n", strerror(errno));
}
}

// 监视不存在的文件
result = fanotify_mark(fan_fd, FAN_MARK_ADD, FAN_OPEN, AT_FDCWD, "nonexistent.txt");
if (result == -1) {
if (errno == ENOENT) {
printf("监视不存在文件错误处理正确: %s\n", strerror(errno));
}
}

// 示例6: 权限相关操作
printf("\n示例6: 权限相关操作\n");

printf("fanotify权限要求:\n");
printf("1. 需要CAP_SYS_ADMIN能力\n");
printf("2. 或者root权限\n");
printf("3. 某些操作可能需要额外权限\n");
printf("4. 文件访问权限仍然适用\n\n");

// 示例7: 实际应用场景演示
printf("示例7: 实际应用场景演示\n");

printf("文件完整性监控系统:\n");
printf("int setup_file_monitoring(const char* path) {\n");
printf(" int fan_fd = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY);\n");
printf(" if (fan_fd == -1) return -1;\n");
printf(" \n");
printf(" // 监视文件修改\n");
printf(" fanotify_mark(fan_fd, FAN_MARK_ADD, \n");
printf(" FAN_CLOSE_WRITE | FAN_MODIFY,\n");
printf(" AT_FDCWD, path);\n");
printf(" \n");
printf(" return fan_fd;\n");
printf("}\n\n");

printf("实时备份系统:\n");
printf("int setup_backup_monitoring(const char* directory) {\n");
printf(" int fan_fd = fanotify_init(FAN_CLASS_CONTENT, O_RDONLY);\n");
printf(" if (fan_fd == -1) return -1;\n");
printf(" \n");
printf(" // 监视目录及其子目录的所有修改\n");
printf(" fanotify_mark(fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT,\n");
printf(" FAN_CLOSE_WRITE | FAN_MOVED_TO | FAN_CREATE,\n");
printf(" AT_FDCWD, directory);\n");
printf(" \n");
printf(" return fan_fd;\n");
printf("}\n\n");

// 示例8: 事件处理演示
printf("示例8: 事件处理演示\n");

printf("典型的fanotify事件处理循环:\n");
printf("while (keep_running) {\n");
printf(" ssize_t len = read(fan_fd, buffer, sizeof(buffer));\n");
printf(" if (len == -1 && errno != EAGAIN) {\n");
printf(" perror(\"读取fanotify事件失败\");\n");
printf(" break;\n");
printf(" }\n");
printf(" \n");
printf(" struct fanotify_event_metadata *metadata;\n");
printf(" for (metadata = (void*)buffer;\n");
printf(" FAN_EVENT_OK(metadata, len);\n");
printf(" metadata = FAN_EVENT_NEXT(metadata, len)) {\n");
printf(" \n");
printf(" // 处理事件\n");
printf(" printf(\"事件: %%s, 文件描述符: %%d\\n\",\n");
printf(" event_type_to_string(metadata->mask),\n");
printf(" metadata->fd);\n");
printf(" \n");
printf(" // 关闭事件提供的文件描述符\n");
printf(" close(metadata->fd);\n");
printf(" }\n");
printf("}\n\n");

// 示例9: 不同监视类型说明
printf("示例9: 不同监视类型说明\n");

printf("FAN_MARK_ADD:\n");
printf(" - 添加新的监视标记\n");
printf(" - 可以与现有标记共存\n\n");

printf("FAN_MARK_REMOVE:\n");
printf(" - 删除指定的监视标记\n");
printf(" - 只删除指定的事件类型\n\n");

printf("FAN_MARK_FLUSH:\n");
printf(" - 删除所有监视标记\n");
printf(" - 清空整个监视设置\n\n");

printf("FAN_MARK_DONT_FOLLOW:\n");
printf(" - 不跟随符号链接\n");
printf(" - 直接监视符号链接本身\n\n");

printf("FAN_MARK_ONLYDIR:\n");
printf(" - 只监视目录\n");
printf(" - 如果路径不是目录则失败\n\n");

printf("FAN_MARK_MOUNT:\n");
printf(" - 监视整个挂载点\n");
printf(" - 包括挂载点下的所有文件\n\n");

printf("FAN_MARK_FILESYSTEM:\n");
printf(" - 监视整个文件系统\n");
printf(" - 包括所有挂载的实例\n\n");

// 示例10: 事件类型说明
printf("示例10: 事件类型说明\n");

printf("基本事件类型:\n");
printf("FAN_ACCESS: 文件被访问(读取)\n");
printf("FAN_OPEN: 文件被打开\n");
printf("FAN_CLOSE_WRITE: 可写文件被关闭\n");
printf("FAN_CLOSE_NOWRITE: 只读文件被关闭\n");
printf("FAN_MODIFY: 文件内容被修改\n\n");

printf("权限检查事件类型:\n");
printf("FAN_OPEN_PERM: 文件打开权限检查(需要响应)\n");
printf("FAN_ACCESS_PERM: 文件访问权限检查(需要响应)\n\n");

printf("创建和删除事件:\n");
printf("FAN_CREATE: 文件或目录被创建\n");
printf("FAN_DELETE: 文件或目录被删除\n");
printf("FAN_MOVED_FROM: 文件被移出\n");
printf("FAN_MOVED_TO: 文件被移入\n\n");

// 示例11: 性能和资源考虑
printf("示例11: 性能和资源考虑\n");

printf("fanotify性能特点:\n");
printf("1. 内核级别的通知机制\n");
printf("2. 比轮询方式更高效\n");
printf("3. 支持大量文件监视\n");
printf("4. 低延迟事件通知\n\n");

printf("资源使用考虑:\n");
printf("1. 每个监视标记消耗内核资源\n");
printf("2. 事件队列有大小限制\n");
printf("3. 需要及时处理事件\n");
printf("4. 避免监视过多文件\n\n");

printf("优化建议:\n");
printf("1. 合理选择监视的事件类型\n");
printf("2. 及时处理和响应事件\n");
printf("3. 避免不必要的监视标记\n");
printf("4. 使用适当的fanotify类\n");
printf("5. 监控系统资源使用\n\n");

// 示例12: 安全考虑
printf("示例12: 安全考虑\n");

printf("fanotify安全特性:\n");
printf("1. 需要特殊权限才能使用\n");
printf("2. 不能监视没有访问权限的文件\n");
printf("3. 提供权限检查事件类型\n");
printf("4. 事件包含文件描述符便于验证\n\n");

printf("安全使用建议:\n");
printf("1. 验证事件中的文件描述符\n");
printf("2. 谨慎使用权限检查事件\n");
printf("3. 限制监视的文件范围\n");
printf("4. 及时响应权限检查事件\n");
printf("5. 避免泄露敏感文件信息\n\n");

// 清理资源
printf("清理测试资源...\n");
close(fan_fd);
unlink("test_fanotify.txt");
unlink("fanotify_test_dir/test_file.txt");
rmdir("fanotify_test_dir");

printf("\n总结:\n");
printf("fanotify_mark是fanotify文件系统监控的核心函数\n");
printf("支持灵活的监视标记管理和多种监视类型\n");
printf("需要适当的权限才能使用\n");
printf("适用于文件完整性监控、实时备份等场景\n");
printf("是Linux系统管理和安全监控的重要工具\n");

return 0;
}
data-ad-format="auto" data-full-width-responsive="true">