static DIR *kqueue_root_start_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct kqueue_root_state *state = root->watch; DIR *osdir; struct stat st, osdirst; struct kevent k; int newwd; unused_parameter(watcher); osdir = opendir_nofollow(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, NULL); return NULL; } newwd = open(path, O_NOFOLLOW|O_EVTONLY|O_CLOEXEC); if (newwd == -1) { // directory got deleted between opendir and open handle_open_errno(root, dir, now, "open", errno, NULL); closedir(osdir); return NULL; } if (fstat(newwd, &st) == -1 || fstat(dirfd(osdir), &osdirst) == -1) { // whaaa? w_log(W_LOG_ERR, "fstat on opened dir %s failed: %s\n", path, strerror(errno)); w_root_schedule_recrawl(root, "fstat failed"); close(newwd); closedir(osdir); return NULL; } if (st.st_dev != osdirst.st_dev || st.st_ino != osdirst.st_ino) { // directory got replaced between opendir and open -- at this point its // parent's being watched, so we let filesystem events take care of it handle_open_errno(root, dir, now, "open", ENOTDIR, NULL); close(newwd); closedir(osdir); return NULL; } dir->wd = newwd; memset(&k, 0, sizeof(k)); EV_SET(&k, dir->wd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE|NOTE_DELETE|NOTE_EXTEND|NOTE_RENAME, 0, SET_DIR_BIT(dir)); if (kevent(state->kq_fd, &k, 1, NULL, 0, 0)) { w_log(W_LOG_DBG, "kevent EV_ADD dir %s failed: %s", path, strerror(errno)); close(dir->wd); dir->wd = -1; } return osdir; }
static DIR *portfs_root_start_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct portfs_root_state *state = root->watch; DIR *osdir; struct stat st; unused_parameter(watcher); osdir = opendir_nofollow(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, NULL); return NULL; } if (fstat(dirfd(osdir), &st) == -1) { // whaaa? w_log(W_LOG_ERR, "fstat on opened dir %s failed: %s\n", path, strerror(errno)); w_root_schedule_recrawl(root, "fstat failed"); closedir(osdir); return NULL; } dir->port_file.fo_mtime = st.st_atim; dir->port_file.fo_mtime = st.st_mtim; dir->port_file.fo_ctime = st.st_ctim; dir->port_file.fo_name = (char*)dir->path->buf; errno = 0; if (port_associate(state->port_fd, PORT_SOURCE_FILE, (uintptr_t)&dir->port_file, WATCHMAN_PORT_EVENTS, SET_DIR_BIT(dir))) { w_log(W_LOG_ERR, "port_associate %s %s\n", dir->port_file.fo_name, strerror(errno)); } return osdir; }
static void kqueue_root_stop_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir) { struct kqueue_root_state *state = root->watch; struct kevent k; unused_parameter(watcher); if (dir->wd == -1) { return; } memset(&k, 0, sizeof(k)); EV_SET(&k, dir->wd, EVFILT_VNODE, EV_DELETE, 0, 0, SET_DIR_BIT(dir)); if (kevent(state->kq_fd, &k, 1, NULL, 0, 0)) { w_log(W_LOG_ERR, "rm_watch: %d %.*s %s\n", dir->wd, dir->path->len, dir->path->buf, strerror(errno)); w_root_schedule_recrawl(root, "rm_watch failed"); } close(dir->wd); dir->wd = -1; }
static DIR *kqueue_root_start_watch_dir(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct kqueue_root_state *state = root->watch; DIR *osdir; struct stat st, osdirst; struct kevent k; int newwd; unused_parameter(watcher); osdir = opendir_nofollow(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, NULL); return NULL; } newwd = open(path, O_NOFOLLOW|O_EVTONLY|O_CLOEXEC); if (newwd == -1) { // directory got deleted between opendir and open handle_open_errno(root, dir, now, "open", errno, NULL); closedir(osdir); return NULL; } if (fstat(newwd, &st) == -1 || fstat(dirfd(osdir), &osdirst) == -1) { // whaaa? w_log(W_LOG_ERR, "fstat on opened dir %s failed: %s\n", path, strerror(errno)); w_root_schedule_recrawl(root, "fstat failed"); close(newwd); closedir(osdir); return NULL; } if (st.st_dev != osdirst.st_dev || st.st_ino != osdirst.st_ino) { // directory got replaced between opendir and open -- at this point its // parent's being watched, so we let filesystem events take care of it handle_open_errno(root, dir, now, "open", ENOTDIR, NULL); close(newwd); closedir(osdir); return NULL; } memset(&k, 0, sizeof(k)); EV_SET(&k, newwd, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_WRITE|NOTE_DELETE|NOTE_EXTEND|NOTE_RENAME, 0, SET_DIR_BIT(dir->path)); // Our mapping needs to be visible before we add it to the queue, // otherwise we can get a wakeup and not know what it is pthread_mutex_lock(&state->lock); w_ht_replace(state->name_to_fd, w_ht_ptr_val(dir->path), newwd); w_ht_replace(state->fd_to_name, newwd, w_ht_ptr_val(dir->path)); pthread_mutex_unlock(&state->lock); if (kevent(state->kq_fd, &k, 1, NULL, 0, 0)) { w_log(W_LOG_DBG, "kevent EV_ADD dir %s failed: %s", path, strerror(errno)); close(newwd); pthread_mutex_lock(&state->lock); w_ht_del(state->name_to_fd, w_ht_ptr_val(dir->path)); w_ht_del(state->fd_to_name, newwd); pthread_mutex_unlock(&state->lock); } else { w_log(W_LOG_DBG, "kevent dir %s -> %d\n", dir->path->buf, newwd); } return osdir; }