Index: kern/vfs_extattr.c =================================================================== --- kern/vfs_extattr.c +++ kern/vfs_extattr.c @@ -150,16 +150,18 @@ /*- * Set a named extended attribute on a file or directory * - * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", - * kernelspace string pointer "attrname", userspace buffer - * pointer "data", buffer length "nbytes", thread "td". + * Arguments: vnode "vp", attribute namespace "attrnamespace", + * "ioflag" for vnode locking control, + * kernelspace string pointer "attrname", + * buffer pointer "data", buffer length "nbytes", + * buffer uio adress-space control - "segflg", thread "td". * Returns: 0 on success, an error number otherwise - * Locks: none * References: vp must be a valid reference for the duration of the call */ -static int -extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, - void *data, size_t nbytes, struct thread *td) +int +extattr_set_vp(struct vnode *vp, int ioflg, int attrnamespace, + const char *attrname, void *data, size_t nbytes, enum uio_seg segflg, + struct thread *td) { struct mount *mp; struct uio auio; @@ -167,26 +169,29 @@ ssize_t cnt; int error; - error = vn_start_write(vp, &mp, V_WAIT | PCATCH); - if (error) - return (error); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + if (nbytes > IOSIZE_MAX) + return (EINVAL); aiov.iov_base = data; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; - if (nbytes > IOSIZE_MAX) { - error = EINVAL; - goto done; - } auio.uio_resid = nbytes; auio.uio_rw = UIO_WRITE; - auio.uio_segflg = UIO_USERSPACE; + auio.uio_segflg = segflg; auio.uio_td = td; cnt = nbytes; + if ((ioflg & IO_NODELOCKED) == 0) { + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error) + return (error); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + } + + ASSERT_VOP_LOCKED(vp, "IO_NODELOCKED with no vp lock held"); + #ifdef MAC error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace, attrname); @@ -200,8 +205,11 @@ td->td_retval[0] = cnt; done: - VOP_UNLOCK(vp, 0); - vn_finished_write(mp); + if ((ioflg & IO_NODELOCKED) == 0) { + vn_finished_write(mp); + VOP_UNLOCK(vp, 0); + } + return (error); } @@ -233,8 +241,8 @@ if (error) return (error); - error = extattr_set_vp(fp->f_vnode, uap->attrnamespace, - attrname, uap->data, uap->nbytes, td); + error = extattr_set_vp(fp->f_vnode, 0, uap->attrnamespace, + attrname, uap->data, uap->nbytes, UIO_USERSPACE, td); fdrop(fp, td); return (error); @@ -268,8 +276,8 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, - uap->data, uap->nbytes, td); + error = extattr_set_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, + uap->data, uap->nbytes, UIO_USERSPACE, td); vrele(nd.ni_vp); return (error); @@ -303,8 +311,8 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, - uap->data, uap->nbytes, td); + error = extattr_set_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, + uap->data, uap->nbytes, UIO_USERSPACE, td); vrele(nd.ni_vp); return (error); @@ -313,16 +321,19 @@ /*- * Get a named extended attribute on a file or directory * - * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", - * kernelspace string pointer "attrname", userspace buffer - * pointer "data", buffer length "nbytes", thread "td". + * Arguments: vnode "vp", "ioflag" for vnode locking control, + * attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", + * buffer pointer "data", buffer length "nbytes", + * buffer uio adress-space control - "segflg", thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ -static int -extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, - void *data, size_t nbytes, struct thread *td) +int +extattr_get_vp(struct vnode *vp, int ioflg, int attrnamespace, + const char *attrname, void *data, size_t nbytes, enum uio_seg segflg, + struct thread *td) { struct uio auio, *auiop; struct iovec aiov; @@ -330,7 +341,8 @@ size_t size, *sizep; int error; - vn_lock(vp, LK_SHARED | LK_RETRY); + if (nbytes > IOSIZE_MAX) + return (EINVAL); /* * Slightly unusual semantics: if the user provides a NULL data @@ -346,19 +358,20 @@ auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; - if (nbytes > IOSIZE_MAX) { - error = EINVAL; - goto done; - } auio.uio_resid = nbytes; auio.uio_rw = UIO_READ; - auio.uio_segflg = UIO_USERSPACE; + auio.uio_segflg = segflg; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else sizep = &size; + if ((ioflg & IO_NODELOCKED) == 0) + vn_lock(vp, LK_SHARED | LK_RETRY); + + ASSERT_VOP_LOCKED(vp, "IO_NODELOCKED with no vp lock held"); + #ifdef MAC error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace, attrname); @@ -376,7 +389,9 @@ td->td_retval[0] = size; done: - VOP_UNLOCK(vp, 0); + if ((ioflg & IO_NODELOCKED) == 0) + VOP_UNLOCK(vp, 0); + return (error); } @@ -408,8 +423,8 @@ if (error) return (error); - error = extattr_get_vp(fp->f_vnode, uap->attrnamespace, - attrname, uap->data, uap->nbytes, td); + error = extattr_get_vp(fp->f_vnode, 0, uap->attrnamespace, + attrname, uap->data, uap->nbytes, UIO_USERSPACE, td); fdrop(fp, td); return (error); @@ -442,8 +457,8 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, - uap->data, uap->nbytes, td); + error = extattr_get_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, + uap->data, uap->nbytes, UIO_USERSPACE, td); vrele(nd.ni_vp); return (error); @@ -477,8 +492,8 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, - uap->data, uap->nbytes, td); + error = extattr_get_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, + uap->data, uap->nbytes, UIO_USERSPACE, td); vrele(nd.ni_vp); return (error); @@ -488,23 +503,27 @@ * extattr_delete_vp(): Delete a named extended attribute on a file or * directory * - * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", - * kernelspace string pointer "attrname", proc "p" + * Arguments: vnode "vp", "ioflag" for vnode locking control, + * attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", thread "td". * Returns: 0 on success, an error number otherwise - * Locks: none * References: vp must be a valid reference for the duration of the call */ -static int -extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, - struct thread *td) +int +extattr_delete_vp(struct vnode *vp, int ioflg, int attrnamespace, + const char *attrname, struct thread *td) { struct mount *mp; int error; - error = vn_start_write(vp, &mp, V_WAIT | PCATCH); - if (error) - return (error); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + if ((ioflg & IO_NODELOCKED) == 0) { + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); + if (error) + return (error); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + } + + ASSERT_VOP_LOCKED(vp, "IO_NODELOCKED with no vp lock held"); #ifdef MAC error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace, @@ -521,8 +540,11 @@ #ifdef MAC done: #endif - VOP_UNLOCK(vp, 0); - vn_finished_write(mp); + if ((ioflg & IO_NODELOCKED) == 0) { + vn_finished_write(mp); + VOP_UNLOCK(vp, 0); + } + return (error); } @@ -552,7 +574,7 @@ if (error) return (error); - error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace, + error = extattr_delete_vp(fp->f_vnode, 0, uap->attrnamespace, attrname, td); fdrop(fp, td); return (error); @@ -583,7 +605,7 @@ return(error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); + error = extattr_delete_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, td); vrele(nd.ni_vp); return(error); } @@ -613,7 +635,7 @@ return(error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); + error = extattr_delete_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, td); vrele(nd.ni_vp); return(error); } @@ -621,16 +643,17 @@ /*- * Retrieve a list of extended attributes on a file or directory. * - * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace", - * userspace buffer pointer "data", buffer length "nbytes", - * thread "td". + * Arguments: vnode "vp", "ioflag" for vnode locking control, + * attribute namespace 'attrnamespace", + * buffer pointer "data", buffer length "nbytes", + * buffer uio adress-space control - "segflg", thread "td". * Returns: 0 on success, an error number otherwise * Locks: none * References: vp must be a valid reference for the duration of the call */ -static int -extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, - size_t nbytes, struct thread *td) +int +extattr_list_vp(struct vnode *vp, int ioflg, int attrnamespace, void *data, + size_t nbytes, enum uio_seg segflg, struct thread *td) { struct uio auio, *auiop; size_t size, *sizep; @@ -638,7 +661,8 @@ ssize_t cnt; int error; - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + if (nbytes > IOSIZE_MAX) + return (EINVAL); auiop = NULL; sizep = NULL; @@ -649,19 +673,20 @@ auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; - if (nbytes > IOSIZE_MAX) { - error = EINVAL; - goto done; - } auio.uio_resid = nbytes; auio.uio_rw = UIO_READ; - auio.uio_segflg = UIO_USERSPACE; + auio.uio_segflg = segflg; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else sizep = &size; + if ((ioflg & IO_NODELOCKED) == 0) + vn_lock(vp, LK_SHARED | LK_RETRY); + + ASSERT_VOP_LOCKED(vp, "IO_NODELOCKED with no vp lock held"); + #ifdef MAC error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace); if (error) @@ -678,11 +703,12 @@ td->td_retval[0] = size; done: - VOP_UNLOCK(vp, 0); + if ((ioflg & IO_NODELOCKED) == 0) + VOP_UNLOCK(vp, 0); + return (error); } - int sys_extattr_list_fd(td, uap) struct thread *td; @@ -704,8 +730,8 @@ if (error) return (error); - error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data, - uap->nbytes, td); + error = extattr_list_vp(fp->f_vnode, 0, uap->attrnamespace, uap->data, + uap->nbytes, UIO_USERSPACE, td); fdrop(fp, td); return (error); @@ -731,8 +757,8 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, - uap->nbytes, td); + error = extattr_list_vp(nd.ni_vp, 0, uap->attrnamespace, uap->data, + uap->nbytes, UIO_USERSPACE, td); vrele(nd.ni_vp); return (error); @@ -759,8 +785,8 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, - uap->nbytes, td); + error = extattr_list_vp(nd.ni_vp, 0, uap->attrnamespace, uap->data, + uap->nbytes, UIO_USERSPACE, td); vrele(nd.ni_vp); return (error); Index: sys/extattr.h =================================================================== --- sys/extattr.h +++ sys/extattr.h @@ -63,12 +63,23 @@ #ifdef _KERNEL #include +#include struct thread; struct ucred; struct vnode; int extattr_check_cred(struct vnode *vp, int attrnamespace, struct ucred *cred, struct thread *td, accmode_t accmode); +int extattr_delete_vp(struct vnode *vp, int ioflg, int attrnamespace, + const char *attrname, struct thread *td); +int extattr_get_vp(struct vnode *vp, int ioflg, int attrnamespace, + const char *attrname, void *data, size_t nbytes, + enum uio_seg segflg, struct thread *td); +int extattr_list_vp(struct vnode *vp, int ioflg, int attrnamespace, + void *data, size_t nbytes, enum uio_seg segflg, struct thread *td); +int extattr_set_vp(struct vnode *vp, int ioflg, int attrnamespace, + const char *attrname, void *data, size_t nbytes, + enum uio_seg segflg, struct thread *td); #else #include