fanotify_mark系统调用及示例

fanotify_mark – 文件系统通知标记

函数介绍

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

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

函数原型

#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事件

示例代码

#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[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;
}
此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注