递归添加子目录时inotify_add_watch报'无此文件或目录'错误如何解决?
解决Linux inotify监控深层子目录失败问题
问题现象
- 仅根路径(如/tmp)下直接创建的子目录(如/tmp/subdir1)可正常添加监控,watch_fd值正常递增
- 创建更深层级子目录(如/tmp/subdir1/subdir2)时,触发
inotify_add_watch: No such file or directory错误,watch_fd返回-1 - 程序运行期间无法自动识别新创建的深层子目录,需重启才能生效
问题根源
- 路径拼接错误:事件处理逻辑中始终用初始根路径拼接
event->name生成新目录路径。当深层子目录创建时,触发的是其父目录的IN_CREATE事件,此时event->name是子目录名(如subdir2),用根路径拼接会得到错误路径(/tmp/subdir2),而非实际的/tmp/subdir1/subdir2。 - 递归深度参数错误:添加新目录监控时固定传入
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; }
修复效果验证
- 编译:
gcc inotifyaddwatch_subdirs.c -o inotifyaddwatch_subdirs - 启动:
./inotifyaddwatch_subdirs /tmp 5 - 测试:
- 创建
/tmp/subdir1:程序自动添加监控 - 在
subdir1下创建subdir2:程序正确识别路径并添加监控,无ENOENT错误 - 所有符合深度要求的新目录都会被自动监控
- 创建
内容的提问来源于stack exchange,提问作者beyond_for_time
相关产品推荐
相关产品推荐

