You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归添加子目录时inotify_add_watch报'无此文件或目录'错误如何解决?

解决Linux inotify监控深层子目录失败问题

问题现象

  • 仅根路径(如/tmp)下直接创建的子目录(如/tmp/subdir1)可正常添加监控,watch_fd值正常递增
  • 创建更深层级子目录(如/tmp/subdir1/subdir2)时,触发inotify_add_watch: No such file or directory错误,watch_fd返回-1
  • 程序运行期间无法自动识别新创建的深层子目录,需重启才能生效

问题根源

  1. 路径拼接错误:事件处理逻辑中始终用初始根路径拼接event->name生成新目录路径。当深层子目录创建时,触发的是其父目录的IN_CREATE事件,此时event->name是子目录名(如subdir2),用根路径拼接会得到错误路径(/tmp/subdir2),而非实际的/tmp/subdir1/subdir2。
  2. 递归深度参数错误:添加新目录监控时固定传入current_depth=1,导致深层目录的深度计算错误,无法正确匹配指定的监控深度。

修复方案

关键修改点

  • 为每个watch_fd关联对应的目录路径,事件触发时通过watch_fd找到父目录真实路径,再拼接子目录名生成正确路径。
  • 递归添加监控时,基于父目录的当前深度+1传递current_depth参数。
  • 新增映射表保存watch_fd与目录路径、深度的关联,同时在目录被删除时清理映射(优化项)。

修复后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>

#define EVENT_SIZE (sizeof(struct inotify_event))
#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
#define MAX_WATCHES 1024

// 保存watch_fd与目录路径、深度的映射
typedef struct {
    int fd;
    char path[PATH_MAX];
    int depth;
} WatchMap;

WatchMap watch_map[MAX_WATCHES];
int watch_count = 0;

// 添加watch到映射表
void add_watch_map(int fd, const char *path, int depth) {
    if (watch_count >= MAX_WATCHES) {
        fprintf(stderr, "Watch map full\n");
        return;
    }
    watch_map[watch_count].fd = fd;
    strncpy(watch_map[watch_count].path, path, PATH_MAX);
    watch_map[watch_count].depth = depth;
    watch_count++;
}

// 根据watch_fd查找对应的目录信息
WatchMap* find_watch_map(int fd) {
    for (int i = 0; i < watch_count; i++) {
        if (watch_map[i].fd == fd) {
            return &watch_map[i];
        }
    }
    return NULL;
}

// 从映射表移除watch
void remove_watch_map(int fd) {
    for (int i = 0; i < watch_count; i++) {
        if (watch_map[i].fd == fd) {
            for (int j = i; j < watch_count - 1; j++) {
                watch_map[j] = watch_map[j+1];
            }
            watch_count--;
            break;
        }
    }
}

// 递归添加inotify监控
void add_watch_recursive(int inotify_fd, const char *base_path, int max_depth, int current_depth) {
    if (current_depth > max_depth) {
        return;
    }

    int watch_fd = inotify_add_watch(inotify_fd, base_path, IN_CREATE | IN_DELETE | IN_DELETE_SELF);
    if (watch_fd < 0) {
        perror("inotify_add_watch");
        printf("Failed to watch %s\n", base_path);
        return;
    }
    printf("Added watch for %s (depth: %d, watch_fd: %d)\n", base_path, current_depth, watch_fd);
    add_watch_map(watch_fd, base_path, current_depth);

    // 初始启动时递归监控已存在的子目录
    DIR *dir = opendir(base_path);
    if (!dir) {
        perror("opendir");
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char path[PATH_MAX];
        snprintf(path, PATH_MAX, "%s/%s", base_path, entry->d_name);

        struct stat statbuf;
        if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
            add_watch_recursive(inotify_fd, path, max_depth, current_depth + 1);
        }
    }

    closedir(dir);
}

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <path> <max_depth>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *root_path = argv[1];
    int max_depth = atoi(argv[2]);

    if (max_depth < 0) {
        fprintf(stderr, "Error: Depth must be a non-negative integer\n");
        return EXIT_FAILURE;
    }

    // 检查根目录有效性
    struct stat statbuf;
    if (stat(root_path, &statbuf) != 0) {
        perror("stat");
        return EXIT_FAILURE;
    }
    if (!S_ISDIR(statbuf.st_mode)) {
        fprintf(stderr, "Error: %s is not a directory\n", root_path);
        return EXIT_FAILURE;
    }

    int inotify_fd = inotify_init();
    if (inotify_fd < 0) {
        perror("inotify_init");
        return EXIT_FAILURE;
    }

    // 初始化根目录监控
    add_watch_recursive(inotify_fd, root_path, max_depth, 0);

    printf("Watching %s and subdirectories up to depth %d\n", root_path, max_depth);

    char buffer[EVENT_BUF_LEN];
    while (1) {
        int length = read(inotify_fd, buffer, EVENT_BUF_LEN);
        if (length < 0) {
            perror("read");
            break;
        }

        int i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *)&buffer[i];
            if (event->len) {
                WatchMap *watch = find_watch_map(event->wd);
                if (!watch) {
                    printf("Unknown watch_fd: %d\n", event->wd);
                    i += EVENT_SIZE + event->len;
                    continue;
                }

                char event_path[PATH_MAX];
                snprintf(event_path, PATH_MAX, "%s/%s", watch->path, event->name);

                if (event->mask & IN_CREATE) {
                    if (event->mask & IN_ISDIR) {
                        printf("Directory created: %s\n", event_path);
                        // 计算新目录的深度:父目录深度+1
                        int new_depth = watch->depth + 1;
                        if (new_depth <= max_depth) {
                            add_watch_recursive(inotify_fd, event_path, max_depth, new_depth);
                        }
                    } else {
                        printf("File created: %s\n", event_path);
                    }
                } else if (event->mask & IN_DELETE) {
                    if (event->mask & IN_ISDIR) {
                        printf("Directory deleted: %s\n", event_path);
                    } else {
                        printf("File deleted: %s\n", event_path);
                    }
                } else if (event->mask & IN_DELETE_SELF) {
                    // 监控的目录被删除,清理映射
                    printf("Watched directory deleted: %s\n", watch->path);
                    remove_watch_map(event->wd);
                }
            }
            i += EVENT_SIZE + event->len;
        }
    }

    close(inotify_fd);
    return EXIT_SUCCESS;
}

修复效果验证

  1. 编译:gcc inotifyaddwatch_subdirs.c -o inotifyaddwatch_subdirs
  2. 启动:./inotifyaddwatch_subdirs /tmp 5
  3. 测试:
    • 创建/tmp/subdir1:程序自动添加监控
    • 在subdir1下创建subdir2:程序正确识别路径并添加监控,无ENOENT错误
    • 所有符合深度要求的新目录都会被自动监控

内容的提问来源于stack exchange,提问作者beyond_for_time

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 02:24:51