Ejemplo 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;
}
Ejemplo n.º 2
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;
}
Ejemplo 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;
  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;
}
Ejemplo n.º 4
0
static void *readchanges_thread(void *arg) {
  w_root_t *root = arg;
  struct winwatch_root_state *state = root->watch;
  DWORD size = WATCHMAN_BATCH_LIMIT * (sizeof(FILE_NOTIFY_INFORMATION) + 512);
  char *buf;
  DWORD err, filter;
  OVERLAPPED olap;
  BOOL initiate_read = true;
  HANDLE handles[2] = { state->olap, state->ping };
  DWORD bytes;

  w_set_thread_name("readchange %.*s",
      root->root_path->len, root->root_path->buf);

  // Block until winmatch_root_st is waiting for our initialization
  pthread_mutex_lock(&state->mtx);

  filter = FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|
    FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE|
    FILE_NOTIFY_CHANGE_LAST_WRITE;

  memset(&olap, 0, sizeof(olap));
  olap.hEvent = state->olap;

  buf = malloc(size);
  if (!buf) {
    w_log(W_LOG_ERR, "failed to allocate %u bytes for dirchanges buf\n", size);
    goto out;
  }

  if (!ReadDirectoryChangesW(state->dir_handle, buf, size,
        TRUE, filter, NULL, &olap, NULL)) {
    err = GetLastError();
    w_log(W_LOG_ERR,
        "ReadDirectoryChangesW: failed, cancel watch. %s\n",
        win32_strerror(err));
    w_root_lock(root);
    w_root_cancel(root);
    w_root_unlock(root);
    goto out;
  }
  // Signal that we are done with init.  We MUST do this AFTER our first
  // successful ReadDirectoryChangesW, otherwise there is a race condition
  // where we'll miss observing the cookie for a query that comes in
  // after we've crawled but before the watch is established.
  w_log(W_LOG_DBG, "ReadDirectoryChangesW signalling as init done");
  pthread_cond_signal(&state->cond);
  pthread_mutex_unlock(&state->mtx);
  initiate_read = false;

  // The state->mutex must not be held when we enter the loop
  while (!root->cancelled) {
    if (initiate_read) {
      if (!ReadDirectoryChangesW(state->dir_handle,
            buf, size, TRUE, filter, NULL, &olap, NULL)) {
        err = GetLastError();
        w_log(W_LOG_ERR,
            "ReadDirectoryChangesW: failed, cancel watch. %s\n",
            win32_strerror(err));
        w_root_lock(root);
        w_root_cancel(root);
        w_root_unlock(root);
        break;
      } else {
        initiate_read = false;
      }
    }

    w_log(W_LOG_DBG, "waiting for change notifications");
    DWORD status = WaitForMultipleObjects(2, handles, FALSE, INFINITE);

    if (status == WAIT_OBJECT_0) {
      bytes = 0;
      if (!GetOverlappedResult(state->dir_handle, &olap,
            &bytes, FALSE)) {
        err = GetLastError();
        w_log(W_LOG_ERR, "overlapped ReadDirectoryChangesW(%s): 0x%x %s\n",
            root->root_path->buf,
            err, win32_strerror(err));

        if (err == ERROR_INVALID_PARAMETER && size > NETWORK_BUF_SIZE) {
          // May be a network buffer related size issue; the docs say that
          // we can hit this when watching a UNC path. Let's downsize and
          // retry the read just one time
          w_log(W_LOG_ERR, "retrying watch for possible network location %s "
              "with smaller buffer\n", root->root_path->buf);
          size = NETWORK_BUF_SIZE;
          initiate_read = true;
          continue;
        }

        if (err == ERROR_NOTIFY_ENUM_DIR) {
          w_root_schedule_recrawl(root, "ERROR_NOTIFY_ENUM_DIR");
        } else {
          w_log(W_LOG_ERR, "Cancelling watch for %s\n",
              root->root_path->buf);
          w_root_lock(root);
          w_root_cancel(root);
          w_root_unlock(root);
          break;
        }
      } else {
        PFILE_NOTIFY_INFORMATION not = (PFILE_NOTIFY_INFORMATION)buf;
        struct winwatch_changed_item *head = NULL, *tail = NULL;
        while (true) {
          struct winwatch_changed_item *item;
          DWORD n_chars;
          w_string_t *name, *full;

          // FileNameLength is in BYTES, but FileName is WCHAR
          n_chars = not->FileNameLength / sizeof(not->FileName[0]);
          name = w_string_new_wchar(not->FileName, n_chars);

          full = w_string_path_cat(root->root_path, name);
          w_string_delref(name);

          if (w_is_ignored(root, full->buf, full->len)) {
            w_string_delref(full);
          } else {
            item = calloc(1, sizeof(*item));
            item->name = full;

            if (tail) {
              tail->next = item;
            } else {
              head = item;
            }
            tail = item;
          }

          // Advance to next item
          if (not->NextEntryOffset == 0) {
            break;
          }
          not = (PFILE_NOTIFY_INFORMATION)(not->NextEntryOffset + (char*)not);
        }

        if (tail) {
          pthread_mutex_lock(&state->mtx);
          if (state->tail) {
            state->tail->next = head;
          } else {
            state->head = head;
          }
          state->tail = tail;
          pthread_mutex_unlock(&state->mtx);
          pthread_cond_signal(&state->cond);
        }
        ResetEvent(state->olap);
        initiate_read = true;
      }
    } else if (status == WAIT_OBJECT_0 + 1) {
      w_log(W_LOG_ERR, "signalled\n");
      break;
    } else {
      w_log(W_LOG_ERR, "impossible wait status=%d\n",
          status);
      break;
    }
  }

  pthread_mutex_lock(&state->mtx);
out:
  // Signal to winwatch_root_start that we're done initializing in
  // the failure path.  We'll also do this after we've completed
  // the run loop in the success path; it's a spurious wakeup but
  // harmless and saves us from adding and setting a control flag
  // in each of the failure `goto` statements. winwatch_root_dtor
  // will `pthread_join` us before `state` is freed.
  pthread_cond_signal(&state->cond);
  pthread_mutex_unlock(&state->mtx);

  if (buf) {
    free(buf);
  }
  w_log(W_LOG_DBG, "done\n");
  w_root_delref(root);
  return NULL;
}
Ejemplo n.º 5
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");
    }
  }
}
Ejemplo n.º 6
0
static void fse_callback(ConstFSEventStreamRef streamRef,
   void *clientCallBackInfo,
   size_t numEvents,
   void *eventPaths,
   const FSEventStreamEventFlags eventFlags[],
   const FSEventStreamEventId eventIds[])
{
  size_t i;
  char **paths = eventPaths;
  w_root_t *root = clientCallBackInfo;
  char pathbuf[WATCHMAN_NAME_MAX];
  struct watchman_fsevent *head = NULL, *tail = NULL, *evt;
  struct fsevents_root_state *state = root->watch;

  unused_parameter(streamRef);
  unused_parameter(eventIds);
  unused_parameter(eventFlags);

  for (i = 0; i < numEvents; i++) {
    uint32_t len;

    len = strlen(paths[i]);
    if (len >= sizeof(pathbuf)-1) {
      w_log(W_LOG_DBG, "FSEvents: %s name is too big :-(\n", paths[i]);
      w_root_cancel(root);
      break;
    }

    strcpy(pathbuf, paths[i]);
    while (pathbuf[len-1] == '/') {
      pathbuf[len-1] = '\0';
      len--;
    }

    if (w_is_ignored(root, paths[i], len)) {
      continue;
    }

    evt = calloc(1, sizeof(*evt));
    if (!evt) {
      w_log(W_LOG_DBG, "FSEvents: OOM!");
      w_root_cancel(root);
      break;
    }

    evt->path = w_string_new(pathbuf);
    evt->flags = eventFlags[i];
    if (tail) {
      tail->next = evt;
    } else {
      head = evt;
    }
    tail = evt;

    w_log(W_LOG_DBG, "fse_thread: add %s %" PRIx32 "\n", pathbuf, evt->flags);
  }

  pthread_mutex_lock(&state->fse_mtx);
  if (state->fse_tail) {
    state->fse_tail->next = head;
  } else {
    state->fse_head = head;
  }
  state->fse_tail = tail;
  pthread_mutex_unlock(&state->fse_mtx);
  pthread_cond_signal(&state->fse_cond);
}
Ejemplo n.º 7
0
static bool fsevents_root_consume_notify(watchman_global_watcher_t watcher,
    w_root_t *root)
{
  struct watchman_fsevent *head, *evt;
  int n = 0;
  struct timeval now;
  bool recurse;
  struct fsevents_root_state *state = root->watch;
  unused_parameter(watcher);

  pthread_mutex_lock(&state->fse_mtx);
  head = state->fse_head;
  state->fse_head = NULL;
  state->fse_tail = NULL;
  pthread_mutex_unlock(&state->fse_mtx);

  gettimeofday(&now, 0);

  while (head) {
    evt = head;
    head = head->next;
    n++;

    if (evt->flags & kFSEventStreamEventFlagUserDropped) {
      w_root_schedule_recrawl(root, "kFSEventStreamEventFlagUserDropped");
break_out:
      w_string_delref(evt->path);
      free(evt);
      break;
    }

    if (evt->flags & kFSEventStreamEventFlagKernelDropped) {
      w_root_schedule_recrawl(root, "kFSEventStreamEventFlagKernelDropped");
      goto break_out;
    }

    if (evt->flags & kFSEventStreamEventFlagUnmount) {
      w_log(W_LOG_ERR, "kFSEventStreamEventFlagUnmount %.*s, cancel watch\n",
        evt->path->len, evt->path->buf);
      w_root_cancel(root);
      goto break_out;
    }

    if (evt->flags & kFSEventStreamEventFlagRootChanged) {
      w_log(W_LOG_ERR,
        "kFSEventStreamEventFlagRootChanged %.*s, cancel watch\n",
        evt->path->len, evt->path->buf);
      w_root_cancel(root);
      goto break_out;
    }

    recurse = (evt->flags & kFSEventStreamEventFlagMustScanSubDirs)
              ? true : false;

    w_root_add_pending(root, evt->path, recurse, now, true);

    w_string_delref(evt->path);
    free(evt);
  }

  return n > 0;
}