Index: fuse_ipc.c =================================================================== --- fuse_ipc.c +++ fuse_ipc.c @@ -549,6 +549,7 @@ { int err = 0; enum fuse_opcode opcode; + struct fuse_getxattr_in *fgin; debug_printf("ftick=%p, blen = %zu\n", ftick, blen); @@ -636,23 +637,23 @@ break; case FUSE_SETXATTR: - panic("FUSE_SETXATTR implementor has forgotten to define a" - " response body format check"); + err = (blen == 0) ? 0 : EINVAL; break; case FUSE_GETXATTR: - panic("FUSE_GETXATTR implementor has forgotten to define a" - " response body format check"); - break; - case FUSE_LISTXATTR: - panic("FUSE_LISTXATTR implementor has forgotten to define a" - " response body format check"); + fgin = (struct fuse_getxattr_in *) + ((char *)ftick->tk_ms_fiov.base + + sizeof(struct fuse_in_header)); + if (fgin->size == 0) + err = (blen == sizeof(struct fuse_getxattr_out)) ? 0 : + EINVAL; + else + err = (blen <= fgin->size) ? 0 : EINVAL; break; case FUSE_REMOVEXATTR: - panic("FUSE_REMOVEXATTR implementor has forgotten to define a" - " response body format check"); + err = (blen == 0) ? 0 : EINVAL; break; case FUSE_FLUSH: Index: fuse_vnops.c =================================================================== --- fuse_vnops.c +++ fuse_vnops.c @@ -82,6 +82,7 @@ #include #include #include +#include #include #include @@ -133,6 +134,10 @@ static vop_getpages_t fuse_vnop_getpages; static vop_putpages_t fuse_vnop_putpages; static vop_print_t fuse_vnop_print; +static vop_getextattr_t fuse_vnop_getextattr; +static vop_setextattr_t fuse_vnop_setextattr; +static vop_listextattr_t fuse_vnop_listextattr; +static vop_deleteextattr_t fuse_vnop_deleteextattr; struct vop_vector fuse_vnops = { .vop_default = &default_vnodeops, @@ -162,6 +167,10 @@ .vop_getpages = fuse_vnop_getpages, .vop_putpages = fuse_vnop_putpages, .vop_print = fuse_vnop_print, + .vop_getextattr = fuse_vnop_getextattr, + .vop_setextattr = fuse_vnop_setextattr, + .vop_listextattr = fuse_vnop_listextattr, + .vop_deleteextattr = fuse_vnop_deleteextattr, }; static u_long fuse_lookup_cache_hits = 0; @@ -1980,3 +1989,214 @@ return 0; } + +/* + struct vop_getextattr_args { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + size_t *a_size; + struct ucred *a_cred; + struct thread *a_td; + }; +*/ +static int +fuse_vnop_getextattr(struct vop_getextattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct fuse_dispatcher fdi; + struct fuse_getxattr_in *fgin; + struct fuse_getxattr_out *fgout; + int err = 0; + size_t len; + + FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp)); + + /* Fuse only supports one attribute name space, so make it "user". */ + if (ap->a_attrnamespace != EXTATTR_NAMESPACE_USER) + return (ENOATTR); + if (fuse_isdeadfs(vp) != 0) + return (ENXIO); + len = strlen(ap->a_name); + fdisp_init(&fdi, sizeof(struct fuse_getxattr_in) + len + 1); + fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, curthread, ap->a_cred); + fgin = (struct fuse_getxattr_in *)fdi.indata; + if (uio != NULL) + fgin->size = uio->uio_resid; + else if (ap->a_size != NULL) + fgin->size = 0; + else { + err = EPERM; + goto out; + } + fgin++; + memcpy(fgin, ap->a_name, len + 1); + + err = fdisp_wait_answ(&fdi); + if (err != 0) + goto out; + + if (uio != NULL) { + if (ap->a_size != NULL) + *ap->a_size = fdi.iosize; + if (fdi.iosize > 0) + err = uiomove(fdi.answ, fdi.iosize, uio); + } else if (ap->a_size != NULL) { + /* When size is set to 0, it returns the size. */ + fgout = (struct fuse_getxattr_out *)fdi.answ; + *ap->a_size = fgout->size; + } +out: + fdisp_destroy(&fdi); + return (err); +} + +/* + struct vop_setextattr_args { + struct vop_generic_args a_gen; + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + struct ucred *a_cred; + struct thread *a_td; + }; +*/ +static int +fuse_vnop_setextattr(struct vop_setextattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct fuse_dispatcher fdi; + struct fuse_setxattr_in *fsin; + int err = 0; + size_t len; + char *cp; + + FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp)); + + /* Fuse only supports one attribute name space, so make it "user". */ + if (ap->a_attrnamespace != EXTATTR_NAMESPACE_USER) + return (ENOATTR); + if (fuse_isdeadfs(vp) != 0) + return (ENXIO); + len = strlen(ap->a_name); + fdisp_init(&fdi, sizeof(struct fuse_setxattr_in) + len + 1 + + uio->uio_resid); + fdisp_make_vp(&fdi, FUSE_SETXATTR, vp, curthread, ap->a_cred); + fsin = (struct fuse_setxattr_in *)fdi.indata; + fsin->size = uio->uio_resid; + fsin->flags = 0; + fsin++; + cp = (char *)fsin; + memcpy(cp, ap->a_name, len + 1); + cp += len + 1; + err = uiomove(cp, uio->uio_resid, uio); + if (err != 0) + goto out; + + err = fdisp_wait_answ(&fdi); +out: + fdisp_destroy(&fdi); + return (err); +} + +/* + struct vop_listextattr_args { + struct vop_generic_args a_gen; + struct vnode *a_vp; + int a_attrnamespace; + struct uio *a_uio; + size_t *a_size; + struct ucred *a_cred; + struct thread *a_td; + }; +*/ +static int +fuse_vnop_listextattr(struct vop_listextattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + struct fuse_dispatcher fdi; + struct fuse_getxattr_in *fgin; + struct fuse_getxattr_out *fgout; + int err = 0; + + FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp)); + + /* Fuse only supports one attribute name space, so make it "user". */ + if (ap->a_attrnamespace != EXTATTR_NAMESPACE_USER) + return (ENOATTR); + if (fuse_isdeadfs(vp) != 0) + return (ENXIO); + fdisp_init(&fdi, sizeof(struct fuse_getxattr_in)); + fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, curthread, ap->a_cred); + fgin = (struct fuse_getxattr_in *)fdi.indata; + if (uio != NULL) + fgin->size = uio->uio_resid; + else if (ap->a_size != NULL) + fgin->size = 0; + else { + err = EPERM; + goto out; + } + + err = fdisp_wait_answ(&fdi); + if (err != 0) + goto out; + + if (uio != NULL) { + if (ap->a_size != NULL) + *ap->a_size = fdi.iosize; + if (fdi.iosize > 0) + err = uiomove(fdi.answ, fdi.iosize, uio); + } else if (ap->a_size != NULL) { + /* When size is set to 0, it returns the size. */ + fgout = (struct fuse_getxattr_out *)fdi.answ; + *ap->a_size = fgout->size; + } +out: + fdisp_destroy(&fdi); + return (err); +} + +/* + struct vop_deleteextattr_args { + struct vop_generic_args a_gen; + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct ucred *a_cred; + struct thread *a_td; + }; +*/ +static int +fuse_vnop_deleteextattr(struct vop_deleteextattr_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct fuse_dispatcher fdi; + int err = 0; + size_t len; + char *cp; + + FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp)); + + /* Fuse only supports one attribute name space, so make it "user". */ + if (ap->a_attrnamespace != EXTATTR_NAMESPACE_USER) + return (ENOATTR); + if (fuse_isdeadfs(vp) != 0) + return (ENXIO); + len = strlen(ap->a_name); + fdisp_init(&fdi, len + 1); + fdisp_make_vp(&fdi, FUSE_REMOVEXATTR, vp, curthread, ap->a_cred); + cp = (char *)fdi.indata; + memcpy(cp, ap->a_name, len + 1); + + err = fdisp_wait_answ(&fdi); + + fdisp_destroy(&fdi); + return (err); +} +