Changeset View
Standalone View
sys/compat/linux/linux_xattr.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
pfgUnsubmitted Done Inline Actionspfg: ```
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
``` | |||||
* | |||||
* Copyright (c) 2017 Fedor Uporov | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer | |||||
* in this position and unchanged. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
#include "opt_compat.h" | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/capsicum.h> | |||||
#include <sys/conf.h> | |||||
#include <sys/dirent.h> | |||||
#include <sys/extattr.h> | |||||
#include <sys/fcntl.h> | |||||
#include <sys/file.h> | |||||
#include <sys/filedesc.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/mount.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/namei.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/sx.h> | |||||
#include <sys/syscallsubr.h> | |||||
#include <sys/sysproto.h> | |||||
#include <sys/tty.h> | |||||
#include <sys/unistd.h> | |||||
#include <sys/vnode.h> | |||||
#include <security/audit/audit.h> | |||||
#ifdef COMPAT_LINUX32 | |||||
#include <machine/../linux32/linux.h> | |||||
#include <machine/../linux32/linux32_proto.h> | |||||
#else | |||||
#include <machine/../linux/linux.h> | |||||
#include <machine/../linux/linux_proto.h> | |||||
#endif | |||||
#include <compat/linux/linux_util.h> | |||||
#include <compat/linux/linux_xattr.h> | |||||
static int | |||||
linux_xattr_namespace_from_linux(const char *linux_name, int *attrnamespace, | |||||
const char **bsd_name) | |||||
{ | |||||
int prefix_len; | |||||
/* | |||||
* The only 'user.' linux namespace is supported for now. | |||||
*/ | |||||
if (bcmp(linux_name, EXTATTR_NAMESPACE_USER_STRING, | |||||
strlen(EXTATTR_NAMESPACE_USER_STRING)) == 0 && | |||||
linux_name[strlen(EXTATTR_NAMESPACE_USER_STRING)] == | |||||
linux_xattr_name_separator) { | |||||
*attrnamespace = EXTATTR_NAMESPACE_USER; | |||||
prefix_len = strlen(EXTATTR_NAMESPACE_USER_STRING) + 1; | |||||
*bsd_name = &linux_name[prefix_len]; | |||||
return (0); | |||||
} | |||||
return (EOPNOTSUPP); | |||||
} | |||||
static int | |||||
linux_setxattr_common(struct vnode *vp, const char *attrname, | |||||
cemUnsubmitted Not Done Inline Actionscould be linux_extattr_set_vp() to match the FreeBSD extattr_set_vp(). cem: could be linux_extattr_set_vp() to match the FreeBSD extattr_set_vp(). | |||||
char *value, size_t size, int flags, struct thread *td) | |||||
{ | |||||
struct mount *mp; | |||||
struct uio auio; | |||||
struct iovec aiov; | |||||
const char *bsd_attrname; | |||||
struct vattr vattr; | |||||
int attrnamespace, error; | |||||
/* | |||||
* Validate fs objects types according LTP requirements | |||||
* for 'user.' namespace xattrs. | |||||
Done Inline ActionsIs this requirement documented somewhere? I skimmed FreeBSD and Linux extattr manual pages and did not see this fs object type or error code documented. cem: Is this requirement documented somewhere? I skimmed FreeBSD and Linux extattr manual pages and… | |||||
*/ | |||||
if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) | |||||
return (EPERM); | |||||
/* Validate xattr value size. */ | |||||
if (size > XATTR_SIZE_MAX) | |||||
return (E2BIG); | |||||
/* Validate create/replace flags. */ | |||||
if (flags && (flags != XATTR_CREATE && flags != XATTR_REPLACE)) | |||||
return (EINVAL); | |||||
error = vn_start_write(vp, &mp, V_WAIT | PCATCH); | |||||
if (error) | |||||
return (error); | |||||
Done Inline ActionsI don't understand what vap is for at all. Why not just use vattr directly? cem: I don't understand what `vap` is for at all. Why not just use `vattr` directly? | |||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | |||||
/* | |||||
* Check stat immutable/append flags. | |||||
cemUnsubmitted Not Done Inline ActionsWhy do we check these? extattr_set_vp() doesn't. We should probably perform the same MAC checking extattr_set_vp() does. Hm, most of the content of this routine could be replaced by a function call, if we made extattr_set_vp non-static. cem: Why do we check these? extattr_set_vp() doesn't.
We should probably perform the same MAC… | |||||
fsuAuthorUnsubmitted Not Done Inline ActionsBecause it is required by LTP test cases. fsu: Because it is required by LTP test cases.
MAC checks are not required because this logic works… | |||||
rwatsonUnsubmitted Not Done Inline ActionsI'm confused by this statement. MAC checks are always required, regardless of whether we are modifying user extended attributes or otherwise. You should really be using the underlying vnode access methods from VFS: vn_extattr_set(), vn_extattr_set(), etc, rather than rolling your own. They exist to avoid code duplication leading to implementation errors, such as the one you have just described. rwatson: I'm confused by this statement. MAC checks are always required, regardless of whether we are… | |||||
*/ | |||||
vattr.va_birthtime.tv_sec = -1; | |||||
vattr.va_birthtime.tv_nsec = 0; | |||||
vattr.va_fsid = VNOVAL; | |||||
vattr.va_rdev = NODEV; | |||||
error = VOP_GETATTR(vp, &vattr, NOCRED); | |||||
if (error) | |||||
goto done; | |||||
if (vattr.va_flags & (IMMUTABLE | APPEND)) { | |||||
error = EPERM; | |||||
goto done; | |||||
} | |||||
error = linux_xattr_namespace_from_linux(attrname, &attrnamespace, | |||||
&bsd_attrname); | |||||
if (error) | |||||
goto done; | |||||
/* | |||||
* Check create/replace file flags. | |||||
*/ | |||||
if (flags) { | |||||
error = VOP_GETEXTATTR(vp, attrnamespace, bsd_attrname, NULL, NULL, | |||||
td->td_ucred, td); | |||||
if (error && error != ENOATTR) | |||||
goto done; | |||||
else if (error == ENOATTR && (flags & XATTR_REPLACE)) | |||||
goto done; | |||||
else if (error == 0 && !(flags & XATTR_REPLACE)) { | |||||
error = EEXIST; | |||||
goto done; | |||||
} | |||||
} | |||||
Done Inline ActionsCan drop else and indentation. Everything after if(...) goto foo; is else. cem: Can drop else and indentation. Everything after `if(...) goto foo;` is else. | |||||
aiov.iov_base = value; | |||||
Done Inline ActionsTRYUPGRADE is typically just called UPGRADE. UPGRADE can fail. This lock call needs error checking. Finally, why take SHARED initially and UPGRADE? It's not like there is a happy path where we can finish the operation SHARED only. Instead we simply do some error checking with SHARED before upgrading. Given that, I think it makes the most sense to just take EXCLUSIVE initially and skip this upgrade. cem: TRYUPGRADE is typically just called UPGRADE.
UPGRADE can fail. This lock call needs error… | |||||
aiov.iov_len = size; | |||||
auio.uio_iov = &aiov; | |||||
auio.uio_iovcnt = 1; | |||||
auio.uio_offset = 0; | |||||
if (size > IOSIZE_MAX) { | |||||
cemUnsubmitted Not Done Inline ActionsIt seems like this check could be moved up to the beginning of the function cem: It seems like this check could be moved up to the beginning of the function | |||||
error = ERANGE; | |||||
goto done; | |||||
} | |||||
auio.uio_resid = size; | |||||
auio.uio_rw = UIO_WRITE; | |||||
auio.uio_segflg = UIO_USERSPACE; | |||||
auio.uio_td = td; | |||||
error = VOP_SETEXTATTR(vp, attrnamespace, bsd_attrname, &auio, | |||||
td->td_ucred, td); | |||||
done: | |||||
VOP_UNLOCK(vp, 0); | |||||
vn_finished_write(mp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_setxattr(struct thread *td, struct linux_setxattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
cemUnsubmitted Not Done Inline ActionsShouldn't these use the Linux constant XATTR_NAME_MAX instead of the FreeBSD one? cem: Shouldn't these use the Linux constant XATTR_NAME_MAX instead of the FreeBSD one? | |||||
int error; | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
cemUnsubmitted Not Done Inline ActionsI'd use sizeof(attrname) in place of EXTATTR_MAXNAMELEN, but that's a weak style preference. cem: I'd use `sizeof(attrname)` in place of EXTATTR_MAXNAMELEN, but that's a weak style preference. | |||||
if (error && error == ENAMETOOLONG) | |||||
return (ERANGE); | |||||
else if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, | |||||
args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_setxattr_common(nd.ni_vp, attrname, args->value, | |||||
args->size, args->flags, td); | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_lsetxattr(struct thread *td, struct linux_lsetxattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
int error; | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error && error == ENAMETOOLONG) | |||||
return (ERANGE); | |||||
else if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, | |||||
args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_setxattr_common(nd.ni_vp, attrname, args->value, | |||||
args->size, args->flags, td); | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_fsetxattr(struct thread *td, struct linux_fsetxattr_args *args) | |||||
{ | |||||
struct file *fp; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
cap_rights_t rights; | |||||
int error; | |||||
AUDIT_ARG_FD(args->fd); | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error && error == ENAMETOOLONG) | |||||
return (ERANGE); | |||||
else if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
error = getvnode(td, args->fd, | |||||
cap_rights_init(&rights, CAP_EXTATTR_SET), &fp); | |||||
if (error) | |||||
return (error); | |||||
error = linux_setxattr_common(fp->f_vnode, attrname, args->value, | |||||
args->size, args->flags, td); | |||||
fdrop(fp, td); | |||||
return (error); | |||||
} | |||||
static int | |||||
linux_getxattr_common(struct vnode *vp, const char *attrname, | |||||
cemUnsubmitted Not Done Inline ActionsThis could probably be a thin shim around a non-static extattr_get_vp(). cem: This could probably be a thin shim around a non-static extattr_get_vp(). | |||||
char *value, size_t size, struct thread *td) | |||||
{ | |||||
struct uio auio; | |||||
struct iovec aiov; | |||||
const char *bsd_attrname; | |||||
size_t bsd_size = 0; | |||||
int attrnamespace, error; | |||||
error = linux_xattr_namespace_from_linux(attrname, &attrnamespace, | |||||
&bsd_attrname); | |||||
if (error) | |||||
return (error); | |||||
/* | |||||
* The only files and directories can have 'user.' xattrs. | |||||
*/ | |||||
if (attrnamespace == EXTATTR_NAMESPACE_USER && | |||||
vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) | |||||
return (EPERM); | |||||
vn_lock(vp, LK_SHARED | LK_RETRY); | |||||
rwatsonUnsubmitted Not Done Inline ActionsYou appear to have skipped the MAC check here as well. This check is very important and should not be omitted. More ideally, you would use the existing vn_extattr_get(9) call, which will do that check for you. rwatson: You appear to have skipped the MAC check here as well. This check is very important and should… | |||||
/* | |||||
* Request xattr value size and check that the input buffer size is | |||||
* not less then xattr value size. | |||||
*/ | |||||
error = VOP_GETEXTATTR(vp, attrnamespace, bsd_attrname, NULL, &bsd_size, | |||||
td->td_ucred, td); | |||||
if (error) | |||||
goto done; | |||||
if (size && bsd_size > size) { | |||||
error = ERANGE; | |||||
goto done; | |||||
} | |||||
else if(value == NULL) | |||||
cemUnsubmitted Not Done Inline Actionsstyle nits: should be combined with above line; missing space between if and ( cem: style nits: should be combined with above line; missing space between `if` and `(` | |||||
goto done; | |||||
aiov.iov_base = value; | |||||
aiov.iov_len = bsd_size; | |||||
auio.uio_iov = &aiov; | |||||
auio.uio_iovcnt = 1; | |||||
auio.uio_offset = 0; | |||||
if (bsd_size > IOSIZE_MAX) { | |||||
error = ERANGE; | |||||
goto done; | |||||
} | |||||
auio.uio_resid = bsd_size; | |||||
auio.uio_rw = UIO_READ; | |||||
auio.uio_segflg = UIO_USERSPACE; | |||||
auio.uio_td = td; | |||||
error = VOP_GETEXTATTR(vp, attrnamespace, bsd_attrname, &auio, NULL, | |||||
td->td_ucred, td); | |||||
done: | |||||
if (bsd_size) | |||||
td->td_retval[0] = bsd_size; | |||||
else | |||||
td->td_retval[0] = 0; | |||||
VOP_UNLOCK(vp, 0); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_getxattr(struct thread *td, struct linux_getxattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
int error; | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_getxattr_common(nd.ni_vp, attrname, args->value, | |||||
args->size, td); | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_lgetxattr(struct thread *td, struct linux_lgetxattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
int error; | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_getxattr_common(nd.ni_vp, attrname, args->value, | |||||
args->size, td); | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_fgetxattr(struct thread *td, struct linux_fgetxattr_args *args) | |||||
{ | |||||
struct file *fp; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
cap_rights_t rights; | |||||
int error; | |||||
AUDIT_ARG_FD(args->fd); | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
error = getvnode(td, args->fd, | |||||
cap_rights_init(&rights, CAP_EXTATTR_GET), &fp); | |||||
if (error) | |||||
return (error); | |||||
error = linux_getxattr_common(fp->f_vnode, attrname, args->value, | |||||
args->size, td); | |||||
fdrop(fp, td); | |||||
return (error); | |||||
} | |||||
/* | |||||
* This function is used to convert bsd attr names list to linux compatible. | |||||
* The conversion consist of the next steps: | |||||
* 1) Adding the terminating zero in the end of the string. | |||||
Done Inline ActionsTypo: convert pfg: Typo: convert | |||||
* 2) Removing byte with characters number. | |||||
* 3) Adding namespace prefix. | |||||
* | |||||
* <name0_len>name0<name1_len>name1 ... | |||||
* to | |||||
* user.name0\0user.name1\0 ... | |||||
* | |||||
* Also, this function could be used to retrieve the size | |||||
* of linux attr names buffer form bsd-compatible one. | |||||
* Let's pass the linux_buf == NULL in this case. | |||||
*/ | |||||
cemUnsubmitted Not Done Inline ActionsProbably worth linking to fs/fuse's fuse_xattrlist_convert(), which is kind of the inverse function. (Also, I didn't review this function yet.) cem: Probably worth linking to fs/fuse's fuse_xattrlist_convert(), which is kind of the inverse… | |||||
static int | |||||
linux_xattrs_names_convert_to_linux(int attrnamespace, | |||||
const char *bsd_buf, size_t bsd_buf_size, | |||||
char *linux_buf, size_t* linux_buf_size) | |||||
{ | |||||
const char *attrnamespace_prefix, *bsd_p; | |||||
char *linux_p; | |||||
cemUnsubmitted Not Done Inline ActionsAs used by linux_listxattr -> linux_listxattr_common, linux_buf and thus linux_p point into userspace. To write to them, you need to use copyout(). It would also be good to more explicitly name the passed pointers so it is clear they are dangerous userspace pointers. cem: As used by linux_listxattr -> linux_listxattr_common, linux_buf and thus linux_p point into… | |||||
fsuAuthorUnsubmitted Not Done Inline ActionsYep, will be fixed later. fsu: Yep, will be fixed later. | |||||
unsigned int size; | |||||
bsd_p = bsd_buf; | |||||
linux_p = linux_buf; | |||||
size = 0; | |||||
if (linux_buf_size == NULL) | |||||
return (EINVAL); | |||||
*linux_buf_size = 0; | |||||
if (attrnamespace == EXTATTR_NAMESPACE_USER) | |||||
attrnamespace_prefix = EXTATTR_NAMESPACE_USER_STRING; | |||||
else if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM) | |||||
attrnamespace_prefix = EXTATTR_NAMESPACE_SYSTEM_STRING; | |||||
else | |||||
return (EINVAL); | |||||
while (bsd_p < bsd_buf + bsd_buf_size) { | |||||
/* Add namespace prefix. */ | |||||
size = strlen(attrnamespace_prefix); | |||||
/* Add separator. */ | |||||
size += sizeof(linux_xattr_name_separator); | |||||
/* Fill the name. */ | |||||
size += *bsd_p; | |||||
/* Add terminating zero. */ | |||||
size += 1; | |||||
/* Fill name if linux_buf was passed. */ | |||||
if (linux_p != NULL) { | |||||
snprintf(linux_p, size, "%s%c%s", | |||||
attrnamespace_prefix, linux_xattr_name_separator, | |||||
bsd_p + 1); | |||||
linux_p[size] = '\0'; | |||||
linux_p += size; | |||||
} | |||||
bsd_p += *bsd_p; | |||||
bsd_p += 1; | |||||
*linux_buf_size += size; | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
linux_listxattr_common(struct vnode *vp, char *buf, size_t size, struct thread *td) | |||||
{ | |||||
struct uio auio; | |||||
struct iovec aiov; | |||||
char *bsd_buf = NULL; | |||||
size_t bsd_size, linux_size; | |||||
int error; | |||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | |||||
cemUnsubmitted Not Done Inline ActionsIt seems odd to need an exclusive lock for listing! The VOP only requires SHARED. cem: It seems odd to need an exclusive lock for listing! The VOP only requires SHARED. | |||||
cemUnsubmitted Not Done Inline ActionsWe should perform the same MAC check as extattr_list_vp(). cem: We should perform the same MAC check as extattr_list_vp(). | |||||
rwatsonUnsubmitted Not Done Inline ActionsYes, a MAC check is required here as well. Ideally vn_extattr_list(9) would be used to avoid code duplication. rwatson: Yes, a MAC check is required here as well. Ideally vn_extattr_list(9) would be used to avoid… | |||||
/* | |||||
* Unfortunately we should do two listxattr() calls per one system call, | |||||
* because it is impossible to get linux compatible xattrs list size | |||||
* without bsd list reading. | |||||
*/ | |||||
error = VOP_LISTEXTATTR(vp, LINUX_BSD_USER_NAMESPACE, NULL, &bsd_size, | |||||
cemUnsubmitted Not Done Inline ActionsThis implementation will only pass through user.foo extended attributes to Linux programs. Should Linux programs be able to list / inspect system. attributes too? cem: This implementation will only pass through `user.foo` extended attributes to Linux programs. | |||||
fsuAuthorUnsubmitted Not Done Inline ActionsThe patch is working only with user. namespace. fsu: The patch is working only with user. namespace.
The system. namespace mean that these are ACL's… | |||||
td->td_ucred, td); | |||||
if (error) | |||||
goto done; | |||||
bsd_buf = malloc(bsd_size, M_TEMP, M_WAITOK); | |||||
if (bsd_buf == NULL) { | |||||
cemUnsubmitted Not Done Inline ActionsThis cannot occur. The NULL check should be removed. cem: This cannot occur. The NULL check should be removed. | |||||
error = ENOMEM; | |||||
goto done; | |||||
} | |||||
aiov.iov_base = bsd_buf; | |||||
aiov.iov_len = bsd_size; | |||||
auio.uio_iov = &aiov; | |||||
auio.uio_iovcnt = 1; | |||||
auio.uio_offset = 0; | |||||
if (bsd_size > IOSIZE_MAX) { | |||||
error = ERANGE; | |||||
goto done; | |||||
} | |||||
auio.uio_resid = bsd_size; | |||||
auio.uio_rw = UIO_READ; | |||||
auio.uio_segflg = UIO_SYSSPACE; | |||||
auio.uio_td = td; | |||||
error = VOP_LISTEXTATTR(vp, LINUX_BSD_USER_NAMESPACE, &auio, NULL, | |||||
td->td_ucred, td); | |||||
if (error) | |||||
goto done; | |||||
error = linux_xattrs_names_convert_to_linux(LINUX_BSD_USER_NAMESPACE, | |||||
bsd_buf, bsd_size, buf, &linux_size); | |||||
if (error) { | |||||
td->td_retval[0] = 0; | |||||
cemUnsubmitted Not Done Inline ActionsI'm not sure this is this needed cem: I'm not sure this is this needed | |||||
} else { | |||||
/* Check, that we have space in the input buffer. */ | |||||
if (size && size < linux_size) { | |||||
td->td_retval[0] = 0; | |||||
cemUnsubmitted Not Done Inline ActionsNor this cem: Nor this | |||||
error = ERANGE; | |||||
} else | |||||
td->td_retval[0] = linux_size; | |||||
} | |||||
done: | |||||
free(bsd_buf, M_TEMP); | |||||
VOP_UNLOCK(vp, 0); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_listxattr(struct thread *td, struct linux_listxattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
int error; | |||||
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_listxattr_common(nd.ni_vp, args->list, args->size, td); | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_llistxattr(struct thread *td, struct linux_llistxattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
int error; | |||||
NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, args->path, | |||||
td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_listxattr_common(nd.ni_vp, args->list, args->size, td); | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_flistxattr(struct thread *td, struct linux_flistxattr_args *args) | |||||
{ | |||||
struct file *fp; | |||||
cap_rights_t rights; | |||||
int error; | |||||
AUDIT_ARG_FD(args->fd); | |||||
error = getvnode(td, args->fd, | |||||
cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp); | |||||
if (error) | |||||
return (error); | |||||
error = linux_listxattr_common(fp->f_vnode, args->list, args->size, td); | |||||
fdrop(fp, td); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_removexattr(struct thread *td, struct linux_removexattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
const char *bsd_attrname; | |||||
int attrnamespace, error; | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_xattr_namespace_from_linux(attrname, &attrnamespace, | |||||
&bsd_attrname); | |||||
if (error) | |||||
goto done; | |||||
error = extattr_delete_vp(nd.ni_vp, attrnamespace, bsd_attrname, td); | |||||
done: | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_lremovexattr(struct thread *td, struct linux_lremovexattr_args *args) | |||||
{ | |||||
struct nameidata nd; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
const char *bsd_attrname; | |||||
int attrnamespace, error; | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, args->path, td); | |||||
error = namei(&nd); | |||||
if (error) | |||||
return (error); | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
error = linux_xattr_namespace_from_linux(attrname, &attrnamespace, | |||||
&bsd_attrname); | |||||
if (error) | |||||
goto done; | |||||
error = extattr_delete_vp(nd.ni_vp, attrnamespace, bsd_attrname, td); | |||||
done: | |||||
vrele(nd.ni_vp); | |||||
return (error); | |||||
} | |||||
int | |||||
linux_fremovexattr(struct thread *td, struct linux_fremovexattr_args *args) | |||||
{ | |||||
struct file *fp; | |||||
char attrname[EXTATTR_MAXNAMELEN]; | |||||
cap_rights_t rights; | |||||
const char *bsd_attrname; | |||||
int attrnamespace, error; | |||||
AUDIT_ARG_FD(args->fd); | |||||
error = copyinstr(args->name, attrname, EXTATTR_MAXNAMELEN, NULL); | |||||
if (error) | |||||
return (error); | |||||
AUDIT_ARG_TEXT(attrname); | |||||
error = getvnode(td, args->fd, | |||||
cap_rights_init(&rights, CAP_EXTATTR_GET), &fp); | |||||
if (error) | |||||
return (error); | |||||
error = linux_xattr_namespace_from_linux(attrname, &attrnamespace, | |||||
&bsd_attrname); | |||||
if (error) | |||||
goto done; | |||||
error = extattr_delete_vp(fp->f_vnode, attrnamespace, bsd_attrname, td); | |||||
done: | |||||
fdrop(fp, td); | |||||
return (error); | |||||
} |