Skip site navigation (1)Skip section navigation (2)
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>