diff --git a/bin/linux/fsnotifier b/bin/linux/fsnotifier index 715b78f2d45f..6075b42a38a8 100755 Binary files a/bin/linux/fsnotifier and b/bin/linux/fsnotifier differ diff --git a/bin/linux/fsnotifier64 b/bin/linux/fsnotifier64 index 13dfd34a9549..ed0854873662 100755 Binary files a/bin/linux/fsnotifier64 and b/bin/linux/fsnotifier64 differ diff --git a/native/fsNotifier/linux/fsnotifier.h b/native/fsNotifier/linux/fsnotifier.h index f3234e2fde9d..b8dd17b38831 100644 --- a/native/fsNotifier/linux/fsnotifier.h +++ b/native/fsNotifier/linux/fsnotifier.h @@ -61,7 +61,7 @@ void set_inotify_callback(void (* callback)(char*, int)); int get_inotify_fd(); int get_watch_count(); bool watch_limit_reached(); -int watch(const char* root, array* ignores); +int watch(const char* root); void unwatch(int id); bool process_inotify_input(); void close_inotify(); diff --git a/native/fsNotifier/linux/inotify.c b/native/fsNotifier/linux/inotify.c index 04d749e48d83..d71737fee507 100644 --- a/native/fsNotifier/linux/inotify.c +++ b/native/fsNotifier/linux/inotify.c @@ -117,8 +117,10 @@ inline bool watch_limit_reached() { } +#define EVENT_MASK IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF + static int add_watch(const char* path, watch_node* parent) { - int wd = inotify_add_watch(inotify_fd, path, IN_MODIFY | IN_ATTRIB | IN_CREATE | IN_DELETE | IN_MOVE | IN_DELETE_SELF); + int wd = inotify_add_watch(inotify_fd, path, EVENT_MASK); if (wd < 0) { if (errno == ENOSPC) { limit_reached = true; @@ -132,7 +134,11 @@ static int add_watch(const char* path, watch_node* parent) { watch_node* node = table_get(watches, wd); if (node != NULL) { - if (node->wd != wd || strcmp(node->name, path) != 0) { + if (node->wd != wd) { + userlog(LOG_ERR, "table error: corruption at %d:%s / %d:%s)", wd, path, node->wd, node->name); + return ERR_ABORT; + } + else if (strcmp(node->name, path) != 0) { char buf1[PATH_MAX], buf2[PATH_MAX]; const char* normalized1 = realpath(node->name, buf1); const char* normalized2 = realpath(path, buf2); @@ -141,7 +147,7 @@ static int add_watch(const char* path, watch_node* parent) { return ERR_ABORT; } else { - userlog(LOG_WARNING, "intersection at %d: (new %s, existing %s, real %s)", wd, path, node->name, normalized1); + userlog(LOG_INFO, "intersection at %d: (new %s, existing %s, real %s)", wd, path, node->name, normalized1); return ERR_IGNORE; } } @@ -210,54 +216,24 @@ static void rm_watch(int wd, bool update_parent) { } -static bool is_directory(struct dirent* entry, const char* path) { - if (entry->d_type == DT_DIR) { - return true; - } - else if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) { - struct stat st; - return (stat(path, &st) == 0 && S_ISDIR(st.st_mode)); - } - return false; -} - -static bool is_ignored(const char* path, array* ignores) { - if (ignores != NULL) { - int pl = strlen(path); - for (int i=0; i= il && strncmp(path, ignore, il) == 0) { - userlog(LOG_DEBUG, "path %s is under unwatchable %s - ignoring", path, ignore); - return true; - } - } - } - return false; -} - -static int walk_tree(const char* path, watch_node* parent, array* ignores, bool recursive) { - if (is_ignored(path, ignores)) { - return ERR_IGNORE; - } - +static int walk_tree(const char* path, watch_node* parent, bool recursive) { DIR* dir; if (recursive) { - dir = opendir(path); - if (dir == NULL) { - if (errno == EACCES) { + if ((dir = opendir(path)) == NULL) { + if (errno == EACCES || errno == ENOENT) { + userlog(LOG_DEBUG, "opendir(%s): %d", path, errno); return ERR_IGNORE; } - else if (errno == ENOTDIR) { // "future" root - return add_watch(path, parent); + else { + userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno)); + return ERR_CONTINUE; } - userlog(LOG_ERR, "opendir(%s): %s", path, strerror(errno)); - return ERR_CONTINUE; } } int id = add_watch(path, parent); - if (!recursive) { + + if (dir == NULL) { return id; } else if (id < 0) { @@ -265,7 +241,6 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores, bool return id; } - struct dirent* entry; char subdir[PATH_MAX]; strcpy(subdir, path); if (subdir[strlen(subdir) - 1] != '/') { @@ -273,17 +248,16 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores, bool } char* p = subdir + strlen(subdir); + struct dirent* entry; while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + if (entry->d_type != DT_DIR || + strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } strcpy(p, entry->d_name); - if (!is_directory(entry, subdir)) { - continue; - } - int subdir_id = walk_tree(subdir, table_get(watches, id), ignores, recursive); + int subdir_id = walk_tree(subdir, table_get(watches, id), recursive); if (subdir_id < 0 && subdir_id != ERR_IGNORE) { rm_watch(id, true); id = subdir_id; @@ -296,16 +270,34 @@ static int walk_tree(const char* path, watch_node* parent, array* ignores, bool } -int watch(const char* root, array* ignores) { +int watch(const char* root) { bool recursive = true; if (root[0] == '|') { root++; recursive = false; } - char buf[PATH_MAX]; - const char* normalized = realpath(root, buf); - return walk_tree((normalized != NULL ? normalized : root), NULL, ignores, recursive); + struct stat st; + if (stat(root, &st) != 0) { + if (errno == EACCES) { + return ERR_IGNORE; + } + else if (errno == ENOENT) { + return ERR_CONTINUE; + } + userlog(LOG_ERR, "stat(%s): %s", root, strerror(errno)); + return ERR_ABORT; + } + + if (S_ISREG(st.st_mode)) { + recursive = false; + } + else if (!S_ISDIR(st.st_mode)) { + userlog(LOG_WARNING, "unexpected node type: %s, %d", root, st.st_mode); + return ERR_IGNORE; + } + + return walk_tree(root, NULL, recursive); } @@ -320,8 +312,9 @@ static bool process_inotify_event(struct inotify_event* event) { return true; } + bool is_dir = (event->mask & IN_ISDIR) == IN_ISDIR; userlog(LOG_DEBUG, "inotify: wd=%d mask=%d dir=%d name=%s", - event->wd, event->mask & (~IN_ISDIR), (event->mask & IN_ISDIR) != 0, node->name); + event->wd, event->mask & (~IN_ISDIR), is_dir, node->name); char path[PATH_MAX]; strcpy(path, node->name); @@ -332,14 +325,14 @@ static bool process_inotify_event(struct inotify_event* event) { strcat(path, event->name); } - if ((event->mask & IN_CREATE || event->mask & IN_MOVED_TO) && event->mask & IN_ISDIR) { - int result = walk_tree(path, node, NULL, true); + if (is_dir && ((event->mask & IN_CREATE) == IN_CREATE || (event->mask & IN_MOVED_TO) == IN_MOVED_TO)) { + int result = walk_tree(path, node, true); if (result < 0 && result != ERR_IGNORE) { return false; } } - if ((event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) && event->mask & IN_ISDIR) { + if (is_dir && ((event->mask & IN_DELETE) == IN_DELETE || (event->mask & IN_MOVED_FROM) == IN_MOVED_FROM)) { for (int i=0; ikids); i++) { watch_node* kid = array_get(node->kids, i); if (kid != NULL && strcmp(kid->name, path) == 0) { diff --git a/native/fsNotifier/linux/main.c b/native/fsNotifier/linux/main.c index ce9d6c275ea7..180140b344f7 100644 --- a/native/fsNotifier/linux/main.c +++ b/native/fsNotifier/linux/main.c @@ -33,7 +33,7 @@ #define LOG_ENV_ERROR "error" #define LOG_ENV_OFF "off" -#define VERSION "1.1" +#define VERSION "1.2" #define VERSION_MSG "fsnotifier " VERSION "\n" #define USAGE_MSG \ @@ -215,6 +215,7 @@ static bool read_input() { userlog(LOG_DEBUG, "input: %s", (line ? line : "")); if (line == NULL || strcmp(line, "EXIT") == 0) { + userlog(LOG_INFO, "exiting: %s", line); return false; } @@ -241,6 +242,12 @@ static bool read_input() { return update_roots(new_roots); } + if (strcmp(line, "VERSION") == 0) { + output(VERSION "\n"); + return true; + } + + userlog(LOG_INFO, "unrecognised command: %s", line); return true; } @@ -302,15 +309,22 @@ static void unregister_roots() { static bool register_roots(array* new_roots, array* unwatchable, array* mounts) { for (int i=0; i= 0) { + if (id >= 0) { watch_root* root = malloc(sizeof(watch_root)); CHECK_NULL(root, false); root->id = id; root->name = new_root; CHECK_NULL(array_push(roots, root), false); } + else if (id == ERR_ABORT) { + return false; + } + else if (id == ERR_IGNORE) { + free(new_root); + } else { if (show_warning && watch_limit_reached()) { int limit = get_watch_count(); @@ -339,7 +356,10 @@ static bool register_roots(array* new_roots, array* unwatchable, array* mounts) output("MESSAGE\n" INOTIFY_LIMIT_MSG, limit); show_warning = false; // warn only once } - CHECK_NULL(array_push(unwatchable, new_root), false); + char* copy = strdup(unflattened); + CHECK_NULL(copy, false); + CHECK_NULL(array_push(unwatchable, copy), false); + free(new_root); } }