/** Checks that the basename component of the input path exactly * matches the canonical case of the path on disk. * It only makes sense to call this function on a case insensitive filesystem. * If the case does not match, throws an exception. */ static void checkCanonicalBaseName(const char *path) { #ifdef __APPLE__ struct attrlist attrlist; struct { uint32_t len; attrreference_t ref; char canonical_name[WATCHMAN_NAME_MAX]; } vomit; w_string_piece pathPiece(path); auto base = pathPiece.baseName(); memset(&attrlist, 0, sizeof(attrlist)); attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; attrlist.commonattr = ATTR_CMN_NAME; if (getattrlist(path, &attrlist, &vomit, sizeof(vomit), FSOPT_NOFOLLOW) == -1) { throw std::system_error(errno, std::generic_category(), to<std::string>("checkCanonicalBaseName(", path, "): getattrlist failed")); } w_string_piece name(((char *)&vomit.ref) + vomit.ref.attr_dataoffset); if (name != base) { throw std::system_error( ENOENT, std::generic_category(), to<std::string>("checkCanonicalBaseName(", path, "): (", name, ") doesn't match canonical base (", base, ")")); } #else // Older Linux and BSDish systems are in this category. // This is the awful portable fallback used in the absence of // a system specific way to detect this. w_string_piece pathPiece(path); auto parent = pathPiece.dirName().asWString(); auto dir = w_dir_open(parent.c_str()); auto base = pathPiece.baseName(); while (true) { auto ent = dir->readDir(); if (!ent) { // We didn't find an entry that exactly matched -> fail throw std::system_error( ENOENT, std::generic_category(), to<std::string>("checkCanonicalBaseName(", path, "): no match found in parent dir")); } // Note: we don't break out early if we get a case-insensitive match // because the dir may contain multiple representations of the same // name. For example, Bash-for-Windows has dirs that contain both // "pod" and "Pod" dirs in its perl installation. We want to make // sure that we've observed all of the entries in the dir before // giving up. if (w_string_piece(ent->d_name) == base) { // Exact match; all is good! return; } } #endif }
std::unique_ptr<watchman_dir_handle> FSEventsWatcher::startWatchDir( const std::shared_ptr<w_root_t>&, struct watchman_dir*, struct timeval, const char* path) { return w_dir_open(path); }
// internal initialization for root void watchman_root::init() { // This just opens and releases the dir. If an exception is thrown // it will bubble up. w_dir_open(root_path.c_str()); // We can't use shared_from_this() here as we are being called from // inside the constructor and we'd hit a bad_weak_ptr exception. inner.init(this); time(&inner.last_cmd_timestamp); }
static struct watchman_dir_handle *winwatch_root_start_watch_dir( watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct watchman_dir_handle *osdir; unused_parameter(watcher); osdir = w_dir_open(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, strerror(errno)); return NULL; } return osdir; }
static struct watchman_dir_handle *inot_root_start_watch_dir( watchman_global_watcher_t watcher, w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct inot_root_state *state = root->watch; struct watchman_dir_handle *osdir = NULL; int newwd, err; unused_parameter(watcher); // Carry out our very strict opendir first to ensure that we're not // traversing symlinks in the context of this root osdir = w_dir_open(path); if (!osdir) { handle_open_errno(root, dir, now, "opendir", errno, NULL); return NULL; } // The directory might be different since the last time we looked at it, so // call inotify_add_watch unconditionally. newwd = inotify_add_watch(state->infd, path, WATCHMAN_INOTIFY_MASK); if (newwd == -1) { err = errno; if (errno == ENOSPC || errno == ENOMEM) { // Limits exceeded, no recovery from our perspective set_poison_state(root, dir->path, now, "inotify-add-watch", errno, inot_strerror(errno)); } else { handle_open_errno(root, dir, now, "inotify_add_watch", errno, inot_strerror(errno)); } w_dir_close(osdir); errno = err; return NULL; } // record mapping pthread_mutex_lock(&state->lock); w_ht_replace(state->wd_to_name, newwd, w_ht_ptr_val(dir->path)); pthread_mutex_unlock(&state->lock); w_log(W_LOG_DBG, "adding %d -> %s mapping\n", newwd, path); return osdir; }
static struct watchman_dir_handle *kqueue_root_start_watch_dir( w_root_t *root, struct watchman_dir *dir, struct timeval now, const char *path) { struct kqueue_root_state *state = root->watch; struct watchman_dir_handle *osdir; struct stat st, osdirst; struct kevent k; int newwd; w_string_t *dir_name; osdir = w_dir_open(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); w_dir_close(osdir); return NULL; } if (fstat(newwd, &st) == -1 || fstat(w_dir_fd(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); w_dir_close(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); w_dir_close(osdir); return NULL; } memset(&k, 0, sizeof(k)); dir_name = w_dir_copy_full_path(dir); EV_SET(&k, newwd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE | NOTE_DELETE | NOTE_EXTEND | NOTE_RENAME, 0, SET_DIR_BIT(dir_name)); // 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_name), newwd); w_ht_replace(state->fd_to_name, newwd, w_ht_ptr_val(dir_name)); 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_name)); 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_name->buf, newwd); } w_string_delref(dir_name); return osdir; }