fanotify_init系统调用及示例

fanotify_init – 初始化fanotify监控实例

fanotify_init是一个Linux系统调用,用于创建和初始化fanotify文件系统监控实例。fanotify是Linux内核提供的高级文件系统事件监控机制,可以监控文件访问、修改等事件。

函数原型

#include <sys/fanotify.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>

int fanotify_init(unsigned int flags, unsigned int event_f_flags);

功能

创建一个新的fanotify监控实例,返回文件描述符用于后续的监控操作。

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

参数

  • unsigned int flags: fanotify实例标志
    • FAN_CLASS_NOTIF: 仅通知类事件
    • FAN_CLASS_CONTENT: 内容监控类事件
    • FAN_CLASS_PRE_CONTENT: 预内容监控类事件
    • FAN_CLOEXEC: 设置执行时关闭标志
    • FAN_NONBLOCK: 设置非阻塞模式
    • FAN_UNLIMITED_QUEUE: 无限制队列大小
    • FAN_UNLIMITED_MARKS: 无限制标记数量
  • unsigned int event_f_flags: 事件文件描述符标志
    • O_RDONLY: 只读打开事件文件
    • O_WRONLY: 只写打开事件文件
    • O_RDWR: 读写打开事件文件
    • O_LARGEFILE: 支持大文件
    • O_CLOEXEC: 执行时关闭

返回值

  • 成功时返回fanotify文件描述符(非负整数)
  • 失败时返回-1,并设置errno

特殊限制

  • 需要Linux 2.6.39以上内核支持
  • 需要CAP_SYS_ADMIN能力
  • 某些标志需要特定内核版本

相似函数

  • fanotify_mark(): 添加监控标记
  • read(): 读取fanotify事件
  • inotify_init(): 旧版文件监控机制

示例代码

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fanotify.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <poll.h>

// 系统调用包装
static int fanotify_init_wrapper(unsigned int flags, unsigned int event_f_flags) {
    return syscall(__NR_fanotify_init, flags, event_f_flags);
}

// 读取fanotify事件
int read_fanotify_events(int fanotify_fd) {
    char buffer[4096];
    ssize_t len;
    struct fanotify_event_metadata *metadata;
    
    len = read(fanotify_fd, buffer, sizeof(buffer));
    if (len == -1) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return 0; // 非阻塞模式下无事件
        }
        perror("读取fanotify事件失败");
        return -1;
    }
    
    metadata = (struct fanotify_event_metadata *)buffer;
    while (FAN_EVENT_OK(metadata, len)) {
        if (metadata->vers != FANOTIFY_METADATA_VERSION) {
            fprintf(stderr, "不支持的元数据版本\n");
            return -1;
        }
        
        printf("事件: fd=%d, mask=0x%x, pid=%d\n", 
               metadata->fd, metadata->mask, metadata->pid);
        
        // 获取文件路径
        if (metadata->fd != FAN_NOFD) {
            char path[PATH_MAX];
            char proc_path[64];
            snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", metadata->fd);
            
            ssize_t path_len = readlink(proc_path, path, sizeof(path) - 1);
            if (path_len != -1) {
                path[path_len] = '\0';
                printf("  文件路径: %s\n", path);
            }
            
            close(metadata->fd);
        }
        
        metadata = FAN_EVENT_NEXT(metadata, len);
    }
    
    return 0;
}

int main() {
    int fanotify_fd;
    int result;
    
    printf("=== Fanotify_init 函数示例 ===\n");
    printf("当前用户 UID: %d\n", getuid());
    printf("当前有效 UID: %d\n", geteuid());
    
    // 示例1: 基本使用
    printf("\n示例1: 基本使用\n");
    
    // 需要root权限或CAP_SYS_ADMIN能力
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_NOTIF, O_RDONLY);
    if (fanotify_fd == -1) {
        if (errno == EPERM) {
            printf("权限不足创建fanotify实例: %s\n", strerror(errno));
            printf("说明: 需要root权限或CAP_SYS_ADMIN能力\n");
        } else {
            printf("创建fanotify实例失败: %s\n", strerror(errno));
        }
        printf("在root权限下重新运行此程序以查看完整功能\n");
        return 0;
    }
    
    printf("成功创建fanotify实例,文件描述符: %d\n", fanotify_fd);
    
    // 检查文件描述符属性
    int flags = fcntl(fanotify_fd, F_GETFD);
    if (flags != -1) {
        printf("fanotify文件描述符验证成功\n");
    }
    
    // 关闭fanotify实例
    close(fanotify_fd);
    printf("关闭fanotify实例\n");
    
    // 示例2: 使用不同标志
    printf("\n示例2: 使用不同标志\n");
    
    // 使用FAN_CLOEXEC标志
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_NOTIF | FAN_CLOEXEC, O_RDONLY);
    if (fanotify_fd != -1) {
        printf("使用FAN_CLOEXEC标志创建成功: %d\n", fanotify_fd);
        
        // 验证CLOEXEC标志
        flags = fcntl(fanotify_fd, F_GETFD);
        if (flags != -1 && (flags & FD_CLOEXEC)) {
            printf("FAN_CLOEXEC标志已正确设置\n");
        }
        
        close(fanotify_fd);
    }
    
    // 使用FAN_NONBLOCK标志
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_NOTIF | FAN_NONBLOCK, O_RDONLY);
    if (fanotify_fd != -1) {
        printf("使用FAN_NONBLOCK标志创建成功: %d\n", fanotify_fd);
        
        // 测试非阻塞特性
        char buffer[1024];
        ssize_t result = read(fanotify_fd, buffer, sizeof(buffer));
        if (result == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                printf("非阻塞读取正确返回EAGAIN\n");
            }
        }
        
        close(fanotify_fd);
    }
    
    // 示例3: 不同类别的fanotify实例
    printf("\n示例3: 不同类别的fanotify实例\n");
    
    printf("fanotify类别说明:\n");
    
    // FAN_CLASS_NOTIF - 通知类
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_NOTIF, O_RDONLY);
    if (fanotify_fd != -1) {
        printf("FAN_CLASS_NOTIF (通知类) 创建成功\n");
        printf("  - 仅接收通知事件\n");
        printf("  - 不需要响应事件\n");
        printf("  - 权限要求较低\n");
        close(fanotify_fd);
    }
    
    // FAN_CLASS_CONTENT - 内容监控类
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_CONTENT, O_RDONLY);
    if (fanotify_fd != -1) {
        printf("FAN_CLASS_CONTENT (内容监控类) 创建成功\n");
        printf("  - 可以读取文件内容\n");
        printf("  - 需要更高权限\n");
        close(fanotify_fd);
    }
    
    // FAN_CLASS_PRE_CONTENT - 预内容监控类
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_PRE_CONTENT, O_RDONLY);
    if (fanotify_fd != -1) {
        printf("FAN_CLASS_PRE_CONTENT (预内容监控类) 创建成功\n");
        printf("  - 可以在操作前拦截\n");
        printf("  - 需要最高权限\n");
        close(fanotify_fd);
    }
    
    // 示例4: 错误处理演示
    printf("\n示例4: 错误处理演示\n");
    
    // 使用无效标志
    fanotify_fd = fanotify_init_wrapper(0x1000, O_RDONLY);
    if (fanotify_fd == -1) {
        if (errno == EINVAL) {
            printf("无效标志错误处理正确: %s\n", strerror(errno));
        }
    }
    
    // 使用无效的event_f_flags
    fanotify_fd = fanotify_init_wrapper(FAN_CLASS_NOTIF, 0x1000);
    if (fanotify_fd == -1) {
        if (errno == EINVAL) {
            printf("无效event_f_flags错误处理正确: %s\n", strerror(errno));
        }
    }
    
    // 示例5: 权限和能力说明
    printf("\n示例5: 权限和能力说明\n");
    
    printf("fanotify权限要求:\n");
    printf("FAN_CLASS_NOTIF:\n");
    printf("  - 需要对监控目录的读权限\n");
    printf("  - 一般用户可使用\n\n");
    
    printf("FAN_CLASS_CONTENT:\n");
    printf("  - 需要CAP_SYS_ADMIN能力\n");
    printf("  - 或root权限\n\n");
    
    printf("FAN_CLASS_PRE_CONTENT:\n");
    printf("  - 需要CAP_SYS_ADMIN能力\n");
    printf("  - 或root权限\n");
    printf("  - 最高权限级别\n\n");
    
    // 示例6: 实际使用场景
    printf("示例6: 实际使用场景\n");
    
    printf("fanotify典型应用场景:\n");
    printf("1. 文件完整性监控\n");
    printf("2. 入侵检测系统\n");
    printf("3. 文件访问审计\n");
    printf("4. 实时备份系统\n");
    printf("5. 安全策略执行\n");
    printf("6. 系统监控工具\n\n");
    
    // 示例监控代码框架
    printf("基本监控代码框架:\n");
    printf("int setup_file_monitoring(const char* path) {\n");
    printf("    int fanotify_fd = fanotify_init(FAN_CLASS_NOTIF, O_RDONLY);\n");
    printf("    if (fanotify_fd == -1) return -1;\n");
    printf("    \n");
    printf("    // 添加监控标记\n");
    printf("    if (fanotify_mark(fanotify_fd, FAN_MARK_ADD, \n");
    printf("                     FAN_OPEN | FAN_CLOSE_WRITE,\n");
    printf("                     AT_FDCWD, path) == -1) {\n");
    printf("        close(fanotify_fd);\n");
    printf("        return -1;\n");
    printf("    }\n");
    printf("    \n");
    printf("    return fanotify_fd;\n");
    printf("}\n\n");
    
    // 示例7: 事件处理循环
    printf("示例7: 事件处理循环\n");
    printf("fanotify事件处理循环示例:\n");
    printf("void monitor_files(int fanotify_fd) {\n");
    printf("    struct pollfd pfd = {.fd = fanotify_fd, .events = POLLIN};\n");
    printf("    \n");
    printf("    while (running) {\n");
    printf("        int ret = poll(&pfd, 1, 1000);  // 1秒超时\n");
    printf("        if (ret > 0 && (pfd.revents & POLLIN)) {\n");
    printf("            handle_fanotify_events(fanotify_fd);\n");
    printf("        }\n");
    printf("    }\n");
    printf("}\n\n");
    
    // 示例8: 与inotify的对比
    printf("示例8: 与inotify的对比\n");
    
    printf("fanotify vs inotify:\n");
    printf("fanotify优势:\n");
    printf("  - 可以监控整个文件系统\n");
    printf("  - 支持文件内容访问监控\n");
    printf("  - 可以获取文件描述符\n");
    printf("  - 支持权限检查\n");
    printf("  - 更高的性能\n\n");
    
    printf("inotify优势:\n");
    printf("  - 更广泛的内核支持\n");
    printf("  - 更简单的API\n");
    printf("  - 更低的权限要求\n");
    printf("  - 更好的可移植性\n\n");
    
    // 示例9: 性能和资源考虑
    printf("示例9: 性能和资源考虑\n");
    
    printf("fanotify性能特点:\n");
    printf("1. 内核级实现,性能优秀\n");
    printf("2. 批量事件处理\n");
    printf("3. 可配置的队列大小\n");
    printf("4. 低CPU开销\n\n");
    
    printf("资源使用考虑:\n");
    printf("1. 每个实例消耗内核资源\n");
    printf("2. 事件队列有大小限制\n");
    printf("3. 及时处理事件避免队列溢出\n");
    printf("4. 合理设置监控范围\n\n");
    
    // 示例10: 安全考虑
    printf("示例10: 安全考虑\n");
    
    printf("使用fanotify的安全注意事项:\n");
    printf("1. 需要适当权限\n");
    printf("2. 避免监控敏感目录\n");
    printf("3. 及时关闭不需要的实例\n");
    printf("4. 处理事件时注意安全\n");
    printf("5. 防止事件处理中的拒绝服务\n");
    printf("6. 限制监控的文件数量\n\n");
    
    printf("总结:\n");
    printf("fanotify_init是创建fanotify监控实例的基础函数\n");
    printf("提供了比inotify更强大的文件监控能力\n");
    printf("需要适当的权限和能力\n");
    printf("支持多种监控类别和标志\n");
    printf("是构建高级文件监控应用的重要工具\n");
    
    return 0;
}
此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

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