FUSE extattrs: fix issue when neither uio nor size were not passed to VOP_*.

The requested size was returned incorrectly in case uio == NULL from listextattr because the
nameprefix/name conversion was not applied.
Also, make a_size/uio returning logic more unified with other filesystems.

Reviewed by:    cem, pfg
MFC after:      2 weeks

Differential Revision:	https://reviews.freebsd.org/D13528
This commit is contained in:
Fedor Uporov 2018-08-21 18:39:47 +00:00
parent 4c1e1d2bcc
commit 493b4a8ccd
2 changed files with 62 additions and 49 deletions

View File

@ -282,7 +282,12 @@ struct fuse_fsync_in {
__u32 padding;
};
struct fuse_setxattr_in {
struct fuse_listxattr_in {
__u32 size;
__u32 flags;
};
struct fuse_listxattr_out {
__u32 size;
__u32 flags;
};
@ -297,6 +302,11 @@ struct fuse_getxattr_out {
__u32 padding;
};
struct fuse_setxattr_in {
__u32 size;
__u32 flags;
};
struct fuse_lk_in {
__u64 fh;
__u64 owner;

View File

@ -2047,7 +2047,7 @@ fuse_vnop_getextattr(struct vop_getextattr_args *ap)
* fuse_getxattr_out. If we pass in a non-zero size, we get back
* that much data, without the struct fuse_getxattr_out header.
*/
if (ap->a_size != NULL)
if (uio == NULL)
get_xattr_in->size = 0;
else
get_xattr_in->size = uio->uio_resid;
@ -2065,25 +2065,13 @@ fuse_vnop_getextattr(struct vop_getextattr_args *ap)
goto out;
}
/*
* If we get to this point (i.e. no error), we should have a valid
* answer of some sort. i.e. non-zero iosize and a valid pointer.
*/
if ((fdi.answ == NULL) || (fdi.iosize == 0)) {
debug_printf("getxattr: err = 0, but answ = %p, iosize = %zu\n",
fdi.answ, fdi.iosize);
err = EINVAL;
goto out;
}
get_xattr_out = fdi.answ;
if (ap->a_size != NULL) {
if (ap->a_size != NULL)
*ap->a_size = get_xattr_out->size;
} else if (fdi.iosize > 0) {
if (uio != NULL)
err = uiomove(fdi.answ, fdi.iosize, uio);
} else {
err = EINVAL;
}
out:
fdisp_destroy(&fdi);
@ -2233,14 +2221,16 @@ fuse_vnop_listextattr(struct vop_listextattr_args *ap)
struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
struct fuse_dispatcher fdi = {0};
struct fuse_getxattr_in *get_xattr_in;
struct fuse_getxattr_out *get_xattr_out;
struct fuse_listxattr_in *list_xattr_in;
struct fuse_listxattr_out *list_xattr_out;
struct mount *mp = vnode_mount(vp);
size_t len;
char *prefix;
char *attr_str;
char *bsd_list = NULL;
char *linux_list;
int bsd_list_len;
int linux_list_len;
struct thread *td = ap->a_td;
struct ucred *cred = ap->a_cred;
int err = 0;
@ -2261,17 +2251,15 @@ fuse_vnop_listextattr(struct vop_listextattr_args *ap)
len = strlen(prefix) + sizeof(extattr_namespace_separator) + 1;
fdisp_init(&fdi, sizeof(*get_xattr_in) + len);
fdisp_init(&fdi, sizeof(*list_xattr_in) + len);
fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
get_xattr_in = fdi.indata;
if (ap->a_size != NULL)
get_xattr_in->size = 0;
else
get_xattr_in->size = uio->uio_resid + sizeof(*get_xattr_out);
attr_str = (char *)fdi.indata + sizeof(*get_xattr_in);
/*
* Retrieve Linux / FUSE compatible list size.
*/
list_xattr_in = fdi.indata;
list_xattr_in->size = 0;
attr_str = (char *)fdi.indata + sizeof(*list_xattr_in);
snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
err = fdisp_wait_answ(&fdi);
@ -2282,32 +2270,47 @@ fuse_vnop_listextattr(struct vop_listextattr_args *ap)
goto out;
}
if ((fdi.answ == NULL) || (fdi.iosize == 0)) {
err = EINVAL;
list_xattr_out = fdi.answ;
linux_list_len = list_xattr_out->size;
if (linux_list_len == 0) {
if (ap->a_size != NULL)
*ap->a_size = linux_list_len;
goto out;
}
get_xattr_out = fdi.answ;
if (ap->a_size != NULL) {
*ap->a_size = get_xattr_out->size;
} else if (fdi.iosize > 0) {
/*
* The Linux / FUSE attribute list format isn't the same
* as FreeBSD's format. So we need to transform it into
* FreeBSD's format before giving it to the user.
*/
bsd_list = malloc(fdi.iosize, M_TEMP, M_WAITOK);
err = fuse_xattrlist_convert(prefix, fdi.answ, fdi.iosize,
bsd_list, &bsd_list_len);
if (err != 0)
goto out;
/*
* Retrieve Linux / FUSE compatible list values.
*/
fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, td, cred);
list_xattr_in = fdi.indata;
list_xattr_in->size = linux_list_len + sizeof(*list_xattr_out);
attr_str = (char *)fdi.indata + sizeof(*list_xattr_in);
snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
err = fdisp_wait_answ(&fdi);
if (err != 0)
goto out;
linux_list = fdi.answ;
linux_list_len = fdi.iosize;
/*
* Retrieve the BSD compatible list values.
* The Linux / FUSE attribute list format isn't the same
* as FreeBSD's format. So we need to transform it into
* FreeBSD's format before giving it to the user.
*/
bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK);
err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len,
bsd_list, &bsd_list_len);
if (err != 0)
goto out;
if (ap->a_size != NULL)
*ap->a_size = bsd_list_len;
if (uio != NULL)
err = uiomove(bsd_list, bsd_list_len, uio);
} else {
debug_printf("listextattr: returned iosize %zu for %s attribute list is "
"too small\n", fdi.iosize, prefix);
err = EINVAL;
}
out:
free(bsd_list, M_TEMP);