Index: fs/fuse/fuse_vnops.c =================================================================== --- fs/fuse/fuse_vnops.c +++ fs/fuse/fuse_vnops.c @@ -2023,7 +2023,7 @@ * 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; @@ -2053,13 +2053,11 @@ } 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); @@ -2216,7 +2214,9 @@ 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; @@ -2240,13 +2240,11 @@ fdisp_init(&fdi, sizeof(*get_xattr_in) + len); fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, td, cred); + /* + * Retrieve Linux / FUSE compatible list size. + */ 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); - - + get_xattr_in->size = 0; attr_str = (char *)fdi.indata + sizeof(*get_xattr_in); snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator); @@ -2258,32 +2256,46 @@ goto out; } - if ((fdi.answ == NULL) || (fdi.iosize == 0)) { - err = EINVAL; + get_xattr_out = fdi.answ; + linux_list_len = get_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); + get_xattr_in->size = linux_list_len + sizeof(*get_xattr_out); + err = fdisp_wait_answ(&fdi); + if (err != 0) { + if (err == ENOSYS) + fsess_set_notimpl(mp, FUSE_LISTXATTR); + 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);