From owner-freebsd-arch@FreeBSD.ORG Thu Dec 22 23:19:32 2011 Return-Path: Delivered-To: arch@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 4E9F0106566B; Thu, 22 Dec 2011 23:19:32 +0000 (UTC) (envelope-from rwatson@freebsd.org) Received: from cyrus.watson.org (cyrus.watson.org [65.122.17.42]) by mx1.freebsd.org (Postfix) with ESMTP id 138588FC0C; Thu, 22 Dec 2011 23:19:32 +0000 (UTC) Received: from [192.168.2.115] (host86-161-238-124.range86-161.btcentralplus.com [86.161.238.124]) by cyrus.watson.org (Postfix) with ESMTPSA id 9E3D346B0A; Thu, 22 Dec 2011 18:19:30 -0500 (EST) Mime-Version: 1.0 (Apple Message framework v1084) Content-Type: text/plain; charset=us-ascii From: "Robert N. M. Watson" In-Reply-To: <201112221105.31746.jhb@freebsd.org> Date: Thu, 22 Dec 2011 23:19:27 +0000 Content-Transfer-Encoding: quoted-printable Message-Id: References: <201112221105.31746.jhb@freebsd.org> To: John Baldwin X-Mailer: Apple Mail (2.1084) Cc: arch@freebsd.org Subject: Re: Post NOTE_ATTRIB EVFILT_VNODE events for extattr changes X-BeenThere: freebsd-arch@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Discussion related to FreeBSD architecture List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 22 Dec 2011 23:19:32 -0000 On 22 Dec 2011, at 16:05, John Baldwin wrote: > A co-worker noticed that there is currently no way to get an = EVFILT_VNODE=20 > notification if the extended attributes for a given file change. I = looked at=20 > OS X and found that it posts a NOTE_ATTRIB event (typically used for=20= > VOP_SETATTR()) for successful attempts to set or delete an extended = attribute. =20 > I have a patch to do the same along with a test application for both = OS X and=20 > FreeBSD. With this patch FreeBSD now has the same behavior as OS X. This seems reasonable to me, although I've not tested the patch you = attached myself. I'm not familiar with the pre/post-vop hook mechanism, but I assume that = this will fire when internal extended attribute I/O, for example = triggered by ACL changes, occurs? Robert >=20 > Index: kern/vnode_if.src > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- kern/vnode_if.src (revision 228777) > +++ kern/vnode_if.src (working copy) > @@ -569,6 +569,7 @@ >=20 >=20 > %% deleteextattr vp E E E > +%! deleteextattr post vop_deleteextattr_post >=20 > vop_deleteextattr { > IN struct vnode *vp; > @@ -580,6 +581,7 @@ >=20 >=20 > %% setextattr vp E E E > +%! setextattr post vop_setextattr_post >=20 > vop_setextattr { > IN struct vnode *vp; > Index: kern/vfs_subr.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- kern/vfs_subr.c (revision 228777) > +++ kern/vfs_subr.c (working copy) > @@ -4033,6 +4033,15 @@ > } >=20 > void > +vop_deleteextattr_post(void *ap, int rc) > +{ > + struct vop_setattr_args *a =3D ap; > + > + if (!rc) > + VFS_KNOTE_LOCKED(a->a_vp, NOTE_ATTRIB); > +} > + > +void > vop_link_post(void *ap, int rc) > { > struct vop_link_args *a =3D ap; > @@ -4114,6 +4123,15 @@ > } >=20 > void > +vop_setextattr_post(void *ap, int rc) > +{ > + struct vop_setattr_args *a =3D ap; > + > + if (!rc) > + VFS_KNOTE_LOCKED(a->a_vp, NOTE_ATTRIB); > +} > + > +void > vop_symlink_post(void *ap, int rc) > { > struct vop_symlink_args *a =3D ap; > Index: sys/vnode.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > --- sys/vnode.h (revision 228777) > +++ sys/vnode.h (working copy) > @@ -705,6 +705,7 @@ >=20 > /* These are called from within the actual VOPS. */ > void vop_create_post(void *a, int rc); > +void vop_deleteextattr_post(void *a, int rc); > void vop_link_post(void *a, int rc); > void vop_lock_pre(void *a); > void vop_lock_post(void *a, int rc); > @@ -717,6 +718,7 @@ > void vop_rename_pre(void *a); > void vop_rmdir_post(void *a, int rc); > void vop_setattr_post(void *a, int rc); > +void vop_setextattr_post(void *a, int rc); > void vop_strategy_pre(void *a); > void vop_symlink_post(void *a, int rc); > void vop_unlock_post(void *a, int rc); >=20 >=20 > /*- > * Test to see if attempts to set or remove extended attributes > * provoke a NOTE_ATTRIB vnode kevent. > */ >=20 > #ifdef __FreeBSD__ > #include > #include > #endif >=20 > #ifdef __APPLE__ > #include > #endif >=20 > #include > #include > #include > #include > #include > #include > #include > #include > #include > #include >=20 > #ifdef __FreeBSD__ > static ssize_t > getea(int fd, const char *name, void *value, size_t size) > { >=20 > return (extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, name, > value, size)); > } >=20 > static int > setea(int fd, const char *name, const void *value, size_t size) > { > ssize_t retval; >=20 > retval =3D extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, name, > value, size); > if (retval < 0) > return (-1); > return (0); > } >=20 > static int > delea(int fd, const char *name) > { >=20 > return (extattr_delete_fd(fd, EXTATTR_NAMESPACE_USER, name)); > } >=20 > static int > listea(int fd, void *buf, size_t size) > { >=20 > return (extattr_list_fd(fd, EXTATTR_NAMESPACE_USER, buf, size)); > } >=20 > static int > compare_ealist(const char *name, void *buf, size_t size) > { > char *p; > int i, len; >=20 > i =3D 0; > p =3D buf; > if (size =3D=3D 0) > return (1); > len =3D p[0]; > p++; > if (len !=3D size - 1) > return (1); > if (strlen(name) !=3D len) > return (1); > return (strncmp(p, name, len)); > } > #endif >=20 > #ifdef __APPLE__ > static ssize_t > getea(int fd, const char *name, void *value, size_t size) > { >=20 > return (fgetxattr(fd, name, value, size, 0, 0)); > } >=20 > static int > setea(int fd, const char *name, const void *value, size_t size) > { >=20 > return (fsetxattr(fd, name, value, size, 0, 0)); > } >=20 > static int > delea(int fd, const char *name) > { >=20 > return (fremovexattr(fd, name, 0)); > } >=20 > static int > listea(int fd, void *buf, size_t size) > { >=20 > return (flistxattr(fd, buf, size, 0)); > } >=20 > static int > compare_ealist(const char *name, void *buf, size_t size) > { >=20 > if (size =3D=3D 0) > return (1); > if (strlen(name) !=3D size - 1) > return (1); > return (strncmp(buf, name, size)); > } > #endif >=20 > #define EA_NAME "foo" >=20 > char template[] =3D "/tmp/kevent_extattr.XXXXXX"; > static int fd, kq; >=20 > static void > check_ea(const char *value, const char *desc) > { > ssize_t retval; > size_t size; > char *buf; >=20 > retval =3D getea(fd, EA_NAME, NULL, 0); > if (retval < 0) { > if (errno =3D=3D ENOATTR) { > if (value !=3D NULL) > printf("Missing ea: %s\n", desc); > return; > } > err(1, "getea"); > } > size =3D (size_t)retval; > buf =3D malloc(size); > retval =3D getea(fd, EA_NAME, buf, size); > if (retval < 0) > err(1, "getea"); > if ((size_t)retval !=3D size) > errx(1, "getea: short read"); > if (value =3D=3D NULL) > printf("Unexpected ea value %*s: %s\n", (int)size, > buf, desc); > else if (strncmp(value, buf, size) !=3D 0) > printf("Mismatched ea values %*s vs %s: %s\n", = (int)size, > buf, value, desc); > free(buf); > } >=20 > static void > check_ealist(const char *name, const char *desc) > { > ssize_t retval; > size_t size; > char *buf; >=20 > retval =3D listea(fd, NULL, 0); > if (retval < 0) > err(1, "listea"); > size =3D (size_t)retval; > if (size =3D=3D 0) { > if (name !=3D NULL) { > printf("Missing ea: %s\n", desc); > return; > } > return; > } > buf =3D malloc(size); > retval =3D listea(fd, buf, size); > if (retval < 0) > err(1, "listea"); > if ((size_t)retval !=3D size) > errx(1, "listea: short read"); > if (name =3D=3D NULL) > printf("Unexpected ea: %s\n", desc); > else if (compare_ealist(name, buf, size) !=3D 0) > printf("Mismatched ea list: %s\n", desc); > free(buf); > } >=20 > static void > check_event(bool expected, const char *desc) > { > struct timespec ts =3D { 0, 0 }; > struct kevent ev; > int retval; >=20 > retval =3D kevent(kq, NULL, 0, &ev, 1, &ts); > if (retval < 0) > err(1, "kevent"); > if (!expected) { > if (retval !=3D 0) > printf("Unexpected kevent: %s\n", desc); > } else { > if (retval =3D=3D 0) > printf("Missing kevent: %s\n", desc); > else if (ev.fflags !=3D NOTE_ATTRIB) > printf("Wrong flags (%x vs %x): %s\n", > ev.fflags, NOTE_ATTRIB, desc); > } > } >=20 > int > main(int ac, char **av) > { > struct kevent ev; >=20 > kq =3D kqueue(); > if (kq < 0) > err(1, "kqueue"); > fd =3D mkstemp(template); > if (fd < 0) > err(1, "mkstemp"); > if (unlink(template) < 0) > err(1, "unlink"); > EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_ATTRIB, 0, = 0); > if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) > err(1, "kevent(EV_ADD)"); >=20 > check_event(false, "initial check"); > check_ea(NULL, "initial check"); > check_ealist(NULL, "initial check"); >=20 > if (setea(fd, EA_NAME, "bar", sizeof("bar")) < 0) > err(1, "setea(%s =3D \"bar\")", EA_NAME); > check_event(true, "first set"); >=20 > /* > * The check_ea() reads the EA and should not have triggered > * an event. > */ > check_ea("bar", "first set"); > check_event(false, "getea"); >=20 > /* > * The ea list requests in this check should not trigger an > * event either. > */ > check_ealist(EA_NAME, "first set"); > check_event(false, "listea"); >=20 > if (setea(fd, EA_NAME, "baz", sizeof("baz")) < 0) > err(1, "setea(%s =3D \"baz\")", EA_NAME); > check_event(true, "second set"); > check_ea("baz", "second set"); > check_ealist(EA_NAME, "second set"); >=20 > if (delea(fd, EA_NAME) < 0) > err(1, "delea(%s)", EA_NAME); > check_event(true, "remove"); > check_ea(NULL, "remove"); > check_ealist(NULL, "remove"); >=20 > close(fd); > close(kq); > return (0); > } >=20 > --=20 > John Baldwin