static bool portfs_root_consume_notify(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_pending_collection *coll) { struct portfs_root_state *state = root->watch; uint_t i, n; struct timeval now; unused_parameter(watcher); errno = 0; n = 1; if (port_getn(state->port_fd, state->portevents, sizeof(state->portevents) / sizeof(state->portevents[0]), &n, NULL)) { if (errno == EINTR) { return false; } w_log(W_LOG_FATAL, "port_getn: %s\n", strerror(errno)); } w_log(W_LOG_DBG, "port_getn: n=%u\n", n); if (n == 0) { return false; } for (i = 0; i < n; i++) { if (IS_DIR_BIT_SET(state->portevents[i].portev_user)) { struct watchman_dir *dir = DECODE_DIR(state->portevents[i].portev_user); uint32_t pe = state->portevents[i].portev_events; w_log(W_LOG_DBG, "port: dir %.*s [0x%x]\n", dir->path->len, dir->path->buf, pe); if ((pe & (FILE_RENAME_FROM|UNMOUNTED|MOUNTEDOVER|FILE_DELETE)) && w_string_equal(dir->path, root->root_path)) { w_log(W_LOG_ERR, "root dir %s has been (re)moved (code 0x%x), canceling watch\n", root->root_path->buf, pe); w_root_cancel(root); return false; } w_pending_coll_add(coll, dir->path, false, now, true); } else { struct watchman_file *file = state->portevents[i].portev_user; w_string_t *path; path = w_string_path_cat(file->parent->path, file->name); w_pending_coll_add(coll, path, true, now, true); w_log(W_LOG_DBG, "port: file %.*s\n", path->len, path->buf); w_string_delref(path); } } return true; }
void handle_open_errno( const std::shared_ptr<w_root_t>& root, struct watchman_dir* dir, struct timeval now, const char* syscall, const std::error_code& err) { auto dir_name = dir->getFullPath(); bool log_warning = true; bool transient = false; if (err == watchman::error_code::no_such_file_or_directory || err == watchman::error_code::not_a_directory || err == watchman::error_code::too_many_symbolic_link_levels) { log_warning = false; transient = false; } else if (err == watchman::error_code::permission_denied) { log_warning = true; transient = false; } else if (err == watchman::error_code::system_limits_exceeded) { set_poison_state(dir_name, now, syscall, err); return; } else { log_warning = true; transient = true; } if (w_string_equal(dir_name, root->root_path)) { if (!transient) { watchman::log( watchman::ERR, syscall, "(", dir_name, ") -> ", err.message(), ". Root was deleted; cancelling watch\n"); root->cancel(); return; } } auto warn = w_string::build( syscall, "(", dir_name, ") -> ", err.message(), ". Marking this portion of the tree deleted"); watchman::log( err == watchman::error_code::no_such_file_or_directory ? watchman::DBG : watchman::ERR, warn, "\n"); if (log_warning) { root->recrawlInfo.wlock()->warning = warn; } }
static bool kqueue_root_consume_notify(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_pending_collection *coll) { struct kqueue_root_state *state = root->watch; int n; int i; struct timespec ts = { 0, 0 }; struct timeval now; unused_parameter(watcher); errno = 0; w_log(W_LOG_DBG, "kqueue(%s)\n", root->root_path->buf); n = kevent(state->kq_fd, NULL, 0, state->keventbuf, sizeof(state->keventbuf) / sizeof(state->keventbuf[0]), &ts); w_log(W_LOG_DBG, "consume_kqueue: %s n=%d err=%s\n", root->root_path->buf, n, strerror(errno)); if (root->cancelled) { return 0; } gettimeofday(&now, NULL); for (i = 0; n > 0 && i < n; i++) { uint32_t fflags = state->keventbuf[i].fflags; if (IS_DIR_BIT_SET(state->keventbuf[i].udata)) { struct watchman_dir *dir = DECODE_DIR(state->keventbuf[i].udata); w_log(W_LOG_DBG, " KQ dir %s [0x%x]\n", dir->path->buf, fflags); if ((fflags & (NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE)) && w_string_equal(dir->path, root->root_path)) { w_log(W_LOG_ERR, "root dir %s has been (re)moved [code 0x%x], canceling watch\n", root->root_path->buf, fflags); w_root_cancel(root); return 0; } w_pending_coll_add(coll, dir->path, false, now, false); } else { // NetBSD defines udata as intptr type, so the cast is necessary struct watchman_file *file = (void *)state->keventbuf[i].udata; w_string_t *path; path = w_string_path_cat(file->parent->path, file->name); w_pending_coll_add(coll, path, true, now, true); w_log(W_LOG_DBG, " KQ file %.*s [0x%x]\n", path->len, path->buf, fflags); w_string_delref(path); } } return n > 0; }
bool w_query_file_matches_relative_root( struct w_query_ctx *ctx, struct watchman_file *f) { w_string_t *parent_path; bool result; if (ctx->query->relative_root == NULL) { return true; } parent_path = compute_parent_path(ctx, f); // "in relative root" here does not mean exactly the relative root, so compare // against the relative root's parent. result = w_string_equal(parent_path, ctx->query->relative_root) || w_string_startswith(parent_path, ctx->query->relative_root_slash); return result; }
static bool eval_name(struct w_query_ctx *ctx, struct watchman_file *file, void *data) { struct name_data *name = data; w_string_t *str; if (name->wholename) { str = w_query_ctx_get_wholename(ctx); } else { str = file->name; } if (name->map) { bool matched; w_ht_val_t val; if (name->caseless) { str = w_string_dup_lower(str); if (!str) { return false; } } matched = w_ht_lookup(name->map, w_ht_ptr_val(str), &val, false); if (name->caseless) { w_string_delref(str); } return matched; } if (name->caseless) { return w_string_equal_caseless(str, name->name); } return w_string_equal(str, name->name); }
static bool kqueue_root_consume_notify(watchman_global_watcher_t watcher, w_root_t *root, struct watchman_pending_collection *coll) { struct kqueue_root_state *state = root->watch; int n; int i; struct timespec ts = { 0, 0 }; struct timeval now; unused_parameter(watcher); errno = 0; n = kevent(state->kq_fd, NULL, 0, state->keventbuf, sizeof(state->keventbuf) / sizeof(state->keventbuf[0]), &ts); w_log(W_LOG_DBG, "consume_kqueue: %s n=%d err=%s\n", root->root_path->buf, n, strerror(errno)); if (root->cancelled) { return 0; } gettimeofday(&now, NULL); for (i = 0; n > 0 && i < n; i++) { uint32_t fflags = state->keventbuf[i].fflags; bool is_dir = IS_DIR_BIT_SET(state->keventbuf[i].udata); w_string_t *path; char flags_label[128]; int fd = state->keventbuf[i].ident; w_expand_flags(kflags, fflags, flags_label, sizeof(flags_label)); pthread_mutex_lock(&state->lock); path = w_ht_val_ptr(w_ht_get(state->fd_to_name, fd)); if (!path) { // Was likely a buffered notification for something that we decided // to stop watching w_log(W_LOG_DBG, " KQ notif for fd=%d; flags=0x%x %s no ref for it in fd_to_name\n", fd, fflags, flags_label); pthread_mutex_unlock(&state->lock); continue; } w_string_addref(path); w_log(W_LOG_DBG, " KQ fd=%d path %s [0x%x %s]\n", fd, path->buf, fflags, flags_label); if ((fflags & (NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE))) { struct kevent k; if (w_string_equal(path, root->root_path)) { w_log(W_LOG_ERR, "root dir %s has been (re)moved [code 0x%x], canceling watch\n", root->root_path->buf, fflags); w_root_cancel(root); pthread_mutex_unlock(&state->lock); return 0; } // Remove our watch bits memset(&k, 0, sizeof(k)); EV_SET(&k, fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); kevent(state->kq_fd, &k, 1, NULL, 0, 0); w_ht_del(state->name_to_fd, w_ht_ptr_val(path)); w_ht_del(state->fd_to_name, fd); } pthread_mutex_unlock(&state->lock); w_pending_coll_add(coll, path, !is_dir, now, !is_dir); w_string_delref(path); } return n > 0; }
static bool path_generator( w_query *query, w_root_t *root, struct w_query_ctx *ctx) { struct watchman_file *f; uint32_t i; for (i = 0; i < query->npaths; i++) { struct watchman_dir *dir; w_string_t *dir_name, *file_name, *full_name; // Compose path with root full_name = w_string_path_cat(root->root_path, query->paths[i].name); // special case of root dir itself if (w_string_equal(root->root_path, full_name)) { // dirname on the root is outside the root, which is useless dir = w_root_resolve_dir(root, full_name, false); goto is_dir; } // Ideally, we'd just resolve it directly as a dir and be done. // It's not quite so simple though, because we may resolve a dir // that had been deleted and replaced by a file. // We prefer to resolve the parent and walk down. dir_name = w_string_dirname(full_name); if (!dir_name) { w_string_delref(full_name); continue; } dir = w_root_resolve_dir(root, dir_name, false); w_string_delref(dir_name); if (!dir) { // Doesn't exist, and never has w_string_delref(full_name); continue; } if (dir->files) { file_name = w_string_basename(query->paths[i].name); f = w_ht_val_ptr(w_ht_get(dir->files, w_ht_ptr_val(file_name))); w_string_delref(file_name); // If it's a file (but not an existent dir) if (f && (!f->exists || !S_ISDIR(f->st.st_mode))) { w_string_delref(full_name); if (!w_query_process_file(query, ctx, f)) { return false; } continue; } } // Is it a dir? dir = w_ht_val_ptr(w_ht_get(dir->dirs, w_ht_ptr_val(full_name))); w_string_delref(full_name); is_dir: // We got a dir; process recursively to specified depth if (dir && !dir_generator(query, root, ctx, dir, query->paths[i].depth)) { return false; } } return true; }
bool w_string::operator==(const w_string& other) const { return w_string_equal(str_, other.str_); }
static void process_inotify_event( w_root_t *root, struct watchman_pending_collection *coll, struct inotify_event *ine, struct timeval now) { struct inot_root_state *state = root->watch; char flags_label[128]; w_expand_flags(inflags, ine->mask, flags_label, sizeof(flags_label)); w_log(W_LOG_DBG, "notify: wd=%d mask=0x%x %s %s\n", ine->wd, ine->mask, flags_label, ine->len > 0 ? ine->name : ""); if (ine->wd == -1 && (ine->mask & IN_Q_OVERFLOW)) { /* we missed something, will need to re-crawl */ w_root_schedule_recrawl(root, "IN_Q_OVERFLOW"); } else if (ine->wd != -1) { w_string_t *dir_name = NULL; w_string_t *name = NULL; char buf[WATCHMAN_NAME_MAX]; int pending_flags = W_PENDING_VIA_NOTIFY; pthread_mutex_lock(&state->lock); dir_name = w_ht_val_ptr(w_ht_get(state->wd_to_name, ine->wd)); if (dir_name) { w_string_addref(dir_name); } pthread_mutex_unlock(&state->lock); if (dir_name) { if (ine->len > 0) { snprintf(buf, sizeof(buf), "%.*s/%s", dir_name->len, dir_name->buf, ine->name); name = w_string_new(buf); } else { name = dir_name; w_string_addref(name); } } if (ine->len > 0 && (ine->mask & (IN_MOVED_FROM|IN_ISDIR)) == (IN_MOVED_FROM|IN_ISDIR)) { struct pending_move mv; // record this as a pending move, so that we can automatically // watch the target when we get the other side of it. mv.created = now.tv_sec; mv.name = name; pthread_mutex_lock(&state->lock); if (!w_ht_replace(state->move_map, ine->cookie, w_ht_ptr_val(&mv))) { w_log(W_LOG_FATAL, "failed to store %" PRIx32 " -> %s in move map\n", ine->cookie, name->buf); } pthread_mutex_unlock(&state->lock); w_log(W_LOG_DBG, "recording move_from %" PRIx32 " %s\n", ine->cookie, name->buf); } if (ine->len > 0 && (ine->mask & (IN_MOVED_TO|IN_ISDIR)) == (IN_MOVED_FROM|IN_ISDIR)) { struct pending_move *old; pthread_mutex_lock(&state->lock); old = w_ht_val_ptr(w_ht_get(state->move_map, ine->cookie)); if (old) { int wd = inotify_add_watch(state->infd, name->buf, WATCHMAN_INOTIFY_MASK); if (wd == -1) { if (errno == ENOSPC || errno == ENOMEM) { // Limits exceeded, no recovery from our perspective set_poison_state(root, name, now, "inotify-add-watch", errno, inot_strerror(errno)); } else { w_log(W_LOG_DBG, "add_watch: %s %s\n", name->buf, inot_strerror(errno)); } } else { w_log(W_LOG_DBG, "moved %s -> %s\n", old->name->buf, name->buf); w_ht_replace(state->wd_to_name, wd, w_ht_ptr_val(name)); } } else { w_log(W_LOG_DBG, "move: cookie=%" PRIx32 " not found in move map %s\n", ine->cookie, name->buf); } pthread_mutex_unlock(&state->lock); } if (dir_name) { if ((ine->mask & (IN_UNMOUNT|IN_IGNORED|IN_DELETE_SELF|IN_MOVE_SELF))) { w_string_t *pname; if (w_string_equal(root->root_path, name)) { w_log(W_LOG_ERR, "root dir %s has been (re)moved, canceling watch\n", root->root_path->buf); w_string_delref(name); w_string_delref(dir_name); w_root_cancel(root); return; } // We need to examine the parent and crawl down pname = w_string_dirname(name); w_log(W_LOG_DBG, "mask=%x, focus on parent: %.*s\n", ine->mask, pname->len, pname->buf); w_string_delref(name); name = pname; pending_flags |= W_PENDING_RECURSIVE; } if (ine->mask & (IN_CREATE|IN_DELETE)) { pending_flags |= W_PENDING_RECURSIVE; } w_log(W_LOG_DBG, "add_pending for inotify mask=%x %.*s\n", ine->mask, name->len, name->buf); w_pending_coll_add(coll, name, now, pending_flags); w_string_delref(name); // The kernel removed the wd -> name mapping, so let's update // our state here also if ((ine->mask & IN_IGNORED) != 0) { w_log(W_LOG_DBG, "mask=%x: remove watch %d %.*s\n", ine->mask, ine->wd, dir_name->len, dir_name->buf); pthread_mutex_lock(&state->lock); w_ht_del(state->wd_to_name, ine->wd); pthread_mutex_unlock(&state->lock); } w_string_delref(dir_name); } else if ((ine->mask & (IN_MOVE_SELF|IN_IGNORED)) == 0) { // If we can't resolve the dir, and this isn't notification // that it has gone away, then we want to recrawl to fix // up our state. w_log(W_LOG_ERR, "wanted dir %d for mask %x but not found %.*s\n", ine->wd, ine->mask, ine->len, ine->name); w_root_schedule_recrawl(root, "dir missing from internal state"); } } }