Index: kern/vfs_extattr.c =================================================================== --- kern/vfs_extattr.c +++ kern/vfs_extattr.c @@ -150,16 +150,17 @@ /*- * 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", + * kernelspace buffer pointer "data", buffer length "nbytes", + * 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, struct thread *td) { struct mount *mp; struct uio auio; @@ -167,26 +168,27 @@ 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 = UIO_SYSSPACE; 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); + } + #ifdef MAC error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace, attrname); @@ -200,8 +202,11 @@ td->td_retval[0] = cnt; done: - VOP_UNLOCK(vp, 0); - vn_finished_write(mp); + if ((ioflg & IO_NODELOCKED) == 0) { + VOP_UNLOCK(vp, 0); + vn_finished_write(mp); + } + return (error); } @@ -218,6 +223,7 @@ { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; + char *data; cap_rights_t rights; int error; @@ -228,15 +234,22 @@ return (error); AUDIT_ARG_TEXT(attrname); + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = copyin(uap->data, data, uap->nbytes); + if (error) + goto done; + error = getvnode(td, uap->fd, cap_rights_init(&rights, CAP_EXTATTR_SET), &fp); if (error) - return (error); + goto done; - 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, + data, uap->nbytes, td); fdrop(fp, td); +done: + free(data, M_TEMP); return (error); } @@ -253,6 +266,7 @@ { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; + char *data; int error; AUDIT_ARG_VALUE(uap->attrnamespace); @@ -261,17 +275,24 @@ return (error); AUDIT_ARG_TEXT(attrname); + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = copyin(uap->data, data, uap->nbytes); + if (error) + goto done; + NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) - return (error); + goto done; 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, + data, uap->nbytes, td); vrele(nd.ni_vp); + +done: + free(data, M_TEMP); return (error); } @@ -288,6 +309,7 @@ { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; + char *data; int error; AUDIT_ARG_VALUE(uap->attrnamespace); @@ -296,33 +318,42 @@ return (error); AUDIT_ARG_TEXT(attrname); + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = copyin(uap->data, data, uap->nbytes); + if (error) + goto done; + NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); if (error) - return (error); + goto done; 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, + data, uap->nbytes, td); vrele(nd.ni_vp); + +done: + free(data, M_TEMP); return (error); } /*- * 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", + * kernelspace buffer pointer "data", buffer length "nbytes", + * 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, struct thread *td) { struct uio auio, *auiop; struct iovec aiov; @@ -330,7 +361,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 +378,18 @@ 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 = UIO_SYSSPACE; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else sizep = &size; + if ((ioflg & IO_NODELOCKED) == 0) + vn_lock(vp, LK_SHARED | LK_RETRY); + #ifdef MAC error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace, attrname); @@ -376,7 +407,9 @@ td->td_retval[0] = size; done: - VOP_UNLOCK(vp, 0); + if ((ioflg & IO_NODELOCKED) == 0) + VOP_UNLOCK(vp, 0); + return (error); } @@ -393,9 +426,12 @@ { struct file *fp; char attrname[EXTATTR_MAXNAMELEN]; + char *data; cap_rights_t rights; int error; + data = NULL; + AUDIT_ARG_FD(uap->fd); AUDIT_ARG_VALUE(uap->attrnamespace); error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); @@ -408,10 +444,17 @@ if (error) return (error); - error = extattr_get_vp(fp->f_vnode, uap->attrnamespace, - attrname, uap->data, uap->nbytes, td); + if (uap->data != NULL) + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); - fdrop(fp, td); + error = extattr_get_vp(fp->f_vnode, 0, uap->attrnamespace, + attrname, data, uap->nbytes, td); + fdrop(fp, td); + + if (error == 0 && uap->data != NULL) + error = copyout(data, uap->data, uap->nbytes); + + free(data, M_TEMP); return (error); } @@ -428,8 +471,11 @@ { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; + char *data; int error; + data = NULL; + AUDIT_ARG_VALUE(uap->attrnamespace); error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) @@ -442,10 +488,17 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, - uap->data, uap->nbytes, td); + if (uap->data != NULL) + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = extattr_get_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, + data, uap->nbytes, td); vrele(nd.ni_vp); + + if (error == 0 && uap->data != NULL) + error = copyout(data, uap->data, uap->nbytes); + + free(data, M_TEMP); return (error); } @@ -462,8 +515,11 @@ { struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; + char *data; int error; + data = NULL; + AUDIT_ARG_VALUE(uap->attrnamespace); error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); if (error) @@ -477,10 +533,17 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, - uap->data, uap->nbytes, td); + if (uap->data != NULL) + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = extattr_get_vp(nd.ni_vp, 0, uap->attrnamespace, attrname, + uap->data, uap->nbytes, td); vrele(nd.ni_vp); + + if (error == 0 && uap->data != NULL) + error = copyout(data, uap->data, uap->nbytes); + + free(data, M_TEMP); return (error); } @@ -488,23 +551,25 @@ * 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); + } #ifdef MAC error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace, @@ -521,8 +586,11 @@ #ifdef MAC done: #endif - VOP_UNLOCK(vp, 0); - vn_finished_write(mp); + if ((ioflg & IO_NODELOCKED) == 0) { + VOP_UNLOCK(vp, 0); + vn_finished_write(mp); + } + return (error); } @@ -552,7 +620,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 +651,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 +681,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,15 +689,16 @@ /*- * 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", + * Arguments: vnode "vp", "ioflag" for vnode locking control, + * attribute namespace 'attrnamespace", + * kernelspace buffer pointer "data", buffer length "nbytes", * 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, +int +extattr_list_vp(struct vnode *vp, int ioflg, int attrnamespace, void *data, size_t nbytes, struct thread *td) { struct uio auio, *auiop; @@ -638,7 +707,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 +719,18 @@ 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 = UIO_SYSSPACE; auio.uio_td = td; auiop = &auio; cnt = nbytes; } else sizep = &size; + if ((ioflg & IO_NODELOCKED) == 0) + vn_lock(vp, LK_SHARED | LK_RETRY); + #ifdef MAC error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace); if (error) @@ -678,11 +747,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; @@ -694,9 +764,12 @@ } */ *uap; { struct file *fp; + char *data; cap_rights_t rights; int error; + data = NULL; + AUDIT_ARG_FD(uap->fd); AUDIT_ARG_VALUE(uap->attrnamespace); error = getvnode(td, uap->fd, @@ -704,10 +777,17 @@ if (error) return (error); - error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data, - uap->nbytes, td); + if (uap->data != NULL) + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = extattr_list_vp(fp->f_vnode, 0, uap->attrnamespace, uap->data, + uap->nbytes, td); fdrop(fp, td); + + if (error == 0 && uap->data != NULL) + error = copyout(data, uap->data, uap->nbytes); + + free(data, M_TEMP); return (error); } @@ -722,8 +802,11 @@ } */ *uap; { struct nameidata nd; + char *data; int error; + data = NULL; + AUDIT_ARG_VALUE(uap->attrnamespace); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); @@ -731,10 +814,17 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, - uap->nbytes, td); + if (uap->data != NULL) + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = extattr_list_vp(nd.ni_vp, 0, uap->attrnamespace, uap->data, + uap->nbytes, td); vrele(nd.ni_vp); + + if (error == 0 && uap->data != NULL) + error = copyout(data, uap->data, uap->nbytes); + + free(data, M_TEMP); return (error); } @@ -749,8 +839,11 @@ } */ *uap; { struct nameidata nd; + char *data; int error; + data = NULL; + AUDIT_ARG_VALUE(uap->attrnamespace); NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); @@ -759,9 +852,16 @@ return (error); NDFREE(&nd, NDF_ONLY_PNBUF); - error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, - uap->nbytes, td); + if (uap->data != NULL) + data = malloc(uap->nbytes, M_TEMP, M_WAITOK); + error = extattr_list_vp(nd.ni_vp, 0, uap->attrnamespace, uap->data, + uap->nbytes, td); vrele(nd.ni_vp); + + if (error == 0 && uap->data != NULL) + error = copyout(data, uap->data, uap->nbytes); + + free(data, M_TEMP); return (error); } Index: sys/extattr.h =================================================================== --- sys/extattr.h +++ sys/extattr.h @@ -63,12 +63,21 @@ #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, struct thread *td); +int extattr_list_vp(struct vnode *vp, int ioflg, int attrnamespace, + 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, struct thread *td); #else #include