+ /*
+ * Tell a (generally networked) filesystem that we're no longer watching
+ * If the FS wants to track contexts, it should still be using the one from
+ * the VNODE_MONITOR_BEGIN.
+ */
+ VNOP_MONITOR(vp, 0, VNODE_MONITOR_END, (void*)kn, ctx);
+ vnode_put(vp);
+}
+
+
+/*
+ * Used for EVFILT_READ
+ *
+ * Takes only VFIFO or VREG. vnode is locked. We handle the "poll" case
+ * differently than the regular case for VREG files. If not in poll(),
+ * then we need to know current fileproc offset for VREG.
+ */
+static intptr_t
+vnode_readable_data_count(vnode_t vp, off_t current_offset, int ispoll)
+{
+ if (vnode_isfifo(vp)) {
+#if FIFO
+ int cnt;
+ int err = fifo_charcount(vp, &cnt);
+ if (err == 0) {
+ return (intptr_t)cnt;
+ } else
+#endif
+ {
+ return (intptr_t)0;
+ }
+ } else if (vnode_isreg(vp)) {
+ if (ispoll) {
+ return (intptr_t)1;
+ }
+
+ off_t amount;
+ amount = vp->v_un.vu_ubcinfo->ui_size - current_offset;
+ if (amount > (off_t)INTPTR_MAX) {
+ return INTPTR_MAX;
+ } else if (amount < (off_t)INTPTR_MIN) {
+ return INTPTR_MIN;
+ } else {
+ return (intptr_t)amount;
+ }
+ } else {
+ panic("Should never have an EVFILT_READ except for reg or fifo.");
+ return 0;
+ }
+}
+
+/*
+ * Used for EVFILT_WRITE.
+ *
+ * For regular vnodes, we can always write (1). For named pipes,
+ * see how much space there is in the buffer. Nothing else is covered.
+ */
+static intptr_t
+vnode_writable_space_count(vnode_t vp)
+{
+ if (vnode_isfifo(vp)) {
+#if FIFO
+ long spc;
+ int err = fifo_freespace(vp, &spc);
+ if (err == 0) {
+ return (intptr_t)spc;
+ } else
+#endif
+ {
+ return (intptr_t)0;
+ }
+ } else if (vnode_isreg(vp)) {
+ return (intptr_t)1;
+ } else {
+ panic("Should never have an EVFILT_READ except for reg or fifo.");
+ return 0;
+ }
+}
+
+/*
+ * Determine whether this knote should be active
+ *
+ * This is kind of subtle.
+ * --First, notice if the vnode has been revoked: in so, override hint
+ * --EVFILT_READ knotes are checked no matter what the hint is
+ * --Other knotes activate based on hint.
+ * --If hint is revoke, set special flags and activate
+ */
+static int
+filt_vnode(struct knote *kn, long hint)
+{
+ vnode_t vp = (struct vnode *)kn->kn_hook;
+ int activate = 0;
+ long orig_hint = hint;
+
+ if (0 == hint) {
+ vnode_lock(vp);
+
+ if (vnode_getiocount(vp, kn->kn_hookid, VNODE_NODEAD | VNODE_WITHID) != 0) {
+ /* Is recycled */
+ hint = NOTE_REVOKE;
+ }
+ } else {
+ lck_mtx_assert(&vp->v_lock, LCK_MTX_ASSERT_OWNED);
+ }
+
+ /* Special handling for vnodes that are in recycle or already gone */
+ if (NOTE_REVOKE == hint) {
+ kn->kn_flags |= (EV_EOF | EV_ONESHOT);
+ activate = 1;
+
+ if ((kn->kn_filter == EVFILT_VNODE) && (kn->kn_sfflags & NOTE_REVOKE)) {
+ kn->kn_fflags |= NOTE_REVOKE;
+ }
+ } else {
+ switch(kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_data = vnode_readable_data_count(vp, kn->kn_fp->f_fglob->fg_offset, (kn->kn_flags & EV_POLL));
+
+ if (kn->kn_data != 0) {
+ activate = 1;
+ }
+ break;
+ case EVFILT_WRITE:
+ kn->kn_data = vnode_writable_space_count(vp);
+
+ if (kn->kn_data != 0) {
+ activate = 1;
+ }
+ break;
+ case EVFILT_VNODE:
+ /* Check events this note matches against the hint */
+ if (kn->kn_sfflags & hint) {
+ kn->kn_fflags |= hint; /* Set which event occurred */
+ }
+ if (kn->kn_fflags != 0) {
+ activate = 1;
+ }
+ break;
+ default:
+ panic("Invalid knote filter on a vnode!\n");
+ }
+ }
+
+ if (orig_hint == 0) {
+ /*
+ * Definitely need to unlock, may need to put
+ */
+ if (hint == 0) {
+ vnode_put_locked(vp);
+ }
+ vnode_unlock(vp);
+ }
+
+ return (activate);