Date: Thu, 22 Dec 2011 23:19:27 +0000 From: "Robert N. M. Watson" <rwatson@freebsd.org> To: John Baldwin <jhb@freebsd.org> Cc: arch@freebsd.org Subject: Re: Post NOTE_ATTRIB EVFILT_VNODE events for extattr changes Message-ID: <E6AD9F35-90F2-466F-89A2-587E0C1309FF@freebsd.org> In-Reply-To: <201112221105.31746.jhb@freebsd.org> References: <201112221105.31746.jhb@freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
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 <sys/types.h> > #include <sys/extattr.h> > #endif >=20 > #ifdef __APPLE__ > #include <sys/xattr.h> > #endif >=20 > #include <sys/event.h> > #include <sys/time.h> > #include <err.h> > #include <errno.h> > #include <stdbool.h> > #include <stdint.h> > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > #include <unistd.h> >=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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E6AD9F35-90F2-466F-89A2-587E0C1309FF>