Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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;
    }
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
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;
}
Exemplo n.º 5
0
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);
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
0
bool w_string::operator==(const w_string& other) const {
  return w_string_equal(str_, other.str_);
}
Exemplo n.º 9
0
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");
    }
  }
}