svn commit: r325161 - stable/11/sys/fs/fuse
Fedor Uporov
fsu at FreeBSD.org
Mon Oct 30 18:03:34 UTC 2017
Author: fsu
Date: Mon Oct 30 18:03:33 2017
New Revision: 325161
URL: https://svnweb.freebsd.org/changeset/base/325161
Log:
MFC r324620:
Add extended attributes support to fuse kernel module.
Author: ken
Reviewed by: cem, pfg (mentor)
Approved by: pfg (mentor)
Differential Revision: https://reviews.freebsd.org/D12485
Modified:
stable/11/sys/fs/fuse/fuse_ipc.c
stable/11/sys/fs/fuse/fuse_vnops.c
Modified: stable/11/sys/fs/fuse/fuse_ipc.c
==============================================================================
--- stable/11/sys/fs/fuse/fuse_ipc.c Mon Oct 30 15:57:48 2017 (r325160)
+++ stable/11/sys/fs/fuse/fuse_ipc.c Mon Oct 30 18:03:33 2017 (r325161)
@@ -636,23 +636,20 @@ fuse_body_audit(struct fuse_ticket *ftick, size_t blen
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");
+ /*
+ * These can have varying response lengths, and 0 length
+ * isn't necessarily invalid.
+ */
+ err = 0;
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:
Modified: stable/11/sys/fs/fuse/fuse_vnops.c
==============================================================================
--- stable/11/sys/fs/fuse/fuse_vnops.c Mon Oct 30 15:57:48 2017 (r325160)
+++ stable/11/sys/fs/fuse/fuse_vnops.c Mon Oct 30 18:03:33 2017 (r325161)
@@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/namei.h>
+#include <sys/extattr.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/filedesc.h>
@@ -111,10 +112,13 @@ __FBSDID("$FreeBSD$");
static vop_access_t fuse_vnop_access;
static vop_close_t fuse_vnop_close;
static vop_create_t fuse_vnop_create;
+static vop_deleteextattr_t fuse_vnop_deleteextattr;
static vop_fsync_t fuse_vnop_fsync;
static vop_getattr_t fuse_vnop_getattr;
+static vop_getextattr_t fuse_vnop_getextattr;
static vop_inactive_t fuse_vnop_inactive;
static vop_link_t fuse_vnop_link;
+static vop_listextattr_t fuse_vnop_listextattr;
static vop_lookup_t fuse_vnop_lookup;
static vop_mkdir_t fuse_vnop_mkdir;
static vop_mknod_t fuse_vnop_mknod;
@@ -127,6 +131,7 @@ static vop_remove_t fuse_vnop_remove;
static vop_rename_t fuse_vnop_rename;
static vop_rmdir_t fuse_vnop_rmdir;
static vop_setattr_t fuse_vnop_setattr;
+static vop_setextattr_t fuse_vnop_setextattr;
static vop_strategy_t fuse_vnop_strategy;
static vop_symlink_t fuse_vnop_symlink;
static vop_write_t fuse_vnop_write;
@@ -139,10 +144,13 @@ struct vop_vector fuse_vnops = {
.vop_access = fuse_vnop_access,
.vop_close = fuse_vnop_close,
.vop_create = fuse_vnop_create,
+ .vop_deleteextattr = fuse_vnop_deleteextattr,
.vop_fsync = fuse_vnop_fsync,
.vop_getattr = fuse_vnop_getattr,
+ .vop_getextattr = fuse_vnop_getextattr,
.vop_inactive = fuse_vnop_inactive,
.vop_link = fuse_vnop_link,
+ .vop_listextattr = fuse_vnop_listextattr,
.vop_lookup = fuse_vnop_lookup,
.vop_mkdir = fuse_vnop_mkdir,
.vop_mknod = fuse_vnop_mknod,
@@ -156,6 +164,7 @@ struct vop_vector fuse_vnops = {
.vop_rename = fuse_vnop_rename,
.vop_rmdir = fuse_vnop_rmdir,
.vop_setattr = fuse_vnop_setattr,
+ .vop_setextattr = fuse_vnop_setextattr,
.vop_strategy = fuse_vnop_strategy,
.vop_symlink = fuse_vnop_symlink,
.vop_write = fuse_vnop_write,
@@ -1955,6 +1964,383 @@ fuse_vnop_putpages(struct vop_putpages_args *ap)
}
}
return rtvals[0];
+}
+
+static const char extattr_namespace_separator = '.';
+
+/*
+ struct vop_getextattr_args {
+ struct vop_generic_args a_gen;
+ 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 = {0};
+ struct fuse_getxattr_in *get_xattr_in;
+ struct fuse_getxattr_out *get_xattr_out;
+ struct mount *mp = vnode_mount(vp);
+ char *prefix;
+ size_t len;
+ char *attr_str;
+ struct thread *td = ap->a_td;
+ struct ucred *cred = ap->a_cred;
+ int err = 0;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp))
+ return ENXIO;
+
+ /* Default to looking for user attributes. */
+ if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
+ prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
+ else
+ prefix = EXTATTR_NAMESPACE_USER_STRING;
+
+ len = strlen(prefix) + sizeof(extattr_namespace_separator) +
+ strlen(ap->a_name) + 1;
+
+ fdisp_init(&fdi, len + sizeof(*get_xattr_in));
+ fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, td, cred);
+
+ get_xattr_in = fdi.indata;
+ /*
+ * Check to see whether we're querying the available size or
+ * issuing the actual request. If we pass in 0, we get back struct
+ * 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)
+ get_xattr_in->size = 0;
+ else
+ get_xattr_in->size = uio->uio_resid;
+
+ attr_str = (char *)fdi.indata + sizeof(*get_xattr_in);
+ snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
+ ap->a_name);
+
+ err = fdisp_wait_answ(&fdi);
+
+ if (err != 0) {
+ if (err == ENOSYS)
+ fsess_set_notimpl(mp, FUSE_GETXATTR);
+ debug_printf("getxattr: got err=%d from daemon\n", err);
+ 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) {
+ *ap->a_size = get_xattr_out->size;
+ } else if (fdi.iosize > 0) {
+ err = uiomove(fdi.answ, fdi.iosize, uio);
+ } else {
+ err = EINVAL;
+ }
+
+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 = {0};
+ struct fuse_setxattr_in *set_xattr_in;
+ struct mount *mp = vnode_mount(vp);
+ char *prefix;
+ size_t len;
+ char *attr_str;
+ struct thread *td = ap->a_td;
+ struct ucred *cred = ap->a_cred;
+ int err = 0;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp))
+ return ENXIO;
+
+ /* Default to looking for user attributes. */
+ if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
+ prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
+ else
+ prefix = EXTATTR_NAMESPACE_USER_STRING;
+
+ len = strlen(prefix) + sizeof(extattr_namespace_separator) +
+ strlen(ap->a_name) + 1;
+
+ fdisp_init(&fdi, len + sizeof(*set_xattr_in) + uio->uio_resid);
+ fdisp_make_vp(&fdi, FUSE_SETXATTR, vp, td, cred);
+
+ set_xattr_in = fdi.indata;
+ set_xattr_in->size = uio->uio_resid;
+
+ attr_str = (char *)fdi.indata + sizeof(*set_xattr_in);
+ snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
+ ap->a_name);
+
+ err = uiomove((char *)fdi.indata + sizeof(*set_xattr_in) + len,
+ uio->uio_resid, uio);
+ if (err != 0) {
+ debug_printf("setxattr: got error %d from uiomove\n", err);
+ goto out;
+ }
+
+ err = fdisp_wait_answ(&fdi);
+
+ if (err != 0) {
+ if (err == ENOSYS)
+ fsess_set_notimpl(mp, FUSE_SETXATTR);
+ debug_printf("setxattr: got err=%d from daemon\n", err);
+ goto out;
+ }
+
+out:
+ fdisp_destroy(&fdi);
+ return (err);
+}
+
+/*
+ * The Linux / FUSE extended attribute list is simply a collection of
+ * NUL-terminated strings. The FreeBSD extended attribute list is a single
+ * byte length followed by a non-NUL terminated string. So, this allows
+ * conversion of the Linux / FUSE format to the FreeBSD format in place.
+ * Linux attribute names are reported with the namespace as a prefix (e.g.
+ * "user.attribute_name"), but in FreeBSD they are reported without the
+ * namespace prefix (e.g. "attribute_name"). So, we're going from:
+ *
+ * user.attr_name1\0user.attr_name2\0
+ *
+ * to:
+ *
+ * <num>attr_name1<num>attr_name2
+ *
+ * Where "<num>" is a single byte number of characters in the attribute name.
+ *
+ * Args:
+ * prefix - exattr namespace prefix string
+ * list, list_len - input list with namespace prefixes
+ * bsd_list, bsd_list_len - output list compatible with bsd vfs
+ */
+static int
+fuse_xattrlist_convert(char *prefix, const char *list, int list_len,
+ char *bsd_list, int *bsd_list_len)
+{
+ int len, pos, dist_to_next, prefix_len;
+
+ pos = 0;
+ *bsd_list_len = 0;
+ prefix_len = strlen(prefix);
+
+ while (pos < list_len && list[pos] != '\0') {
+ dist_to_next = strlen(&list[pos]) + 1;
+ if (bcmp(&list[pos], prefix, prefix_len) == 0 &&
+ list[pos + prefix_len] == extattr_namespace_separator) {
+ len = dist_to_next -
+ (prefix_len + sizeof(extattr_namespace_separator)) - 1;
+ if (len >= EXTATTR_MAXNAMELEN)
+ return (ENAMETOOLONG);
+
+ bsd_list[*bsd_list_len] = len;
+ memcpy(&bsd_list[*bsd_list_len + 1],
+ &list[pos + prefix_len +
+ sizeof(extattr_namespace_separator)], len);
+
+ *bsd_list_len += len + 1;
+ }
+
+ pos += dist_to_next;
+ }
+
+ return (0);
+}
+
+/*
+ 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 = {0};
+ struct fuse_getxattr_in *get_xattr_in;
+ struct fuse_getxattr_out *get_xattr_out;
+ struct mount *mp = vnode_mount(vp);
+ size_t len;
+ char *prefix;
+ char *attr_str;
+ char *bsd_list = NULL;
+ int bsd_list_len;
+ struct thread *td = ap->a_td;
+ struct ucred *cred = ap->a_cred;
+ int err = 0;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp))
+ return ENXIO;
+
+ /*
+ * Add space for a NUL and the period separator if enabled.
+ * Default to looking for user attributes.
+ */
+ if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
+ prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
+ else
+ prefix = EXTATTR_NAMESPACE_USER_STRING;
+
+ len = strlen(prefix) + sizeof(extattr_namespace_separator) + 1;
+
+ fdisp_init(&fdi, sizeof(*get_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);
+ snprintf(attr_str, len, "%s%c", prefix, extattr_namespace_separator);
+
+ err = fdisp_wait_answ(&fdi);
+ if (err != 0) {
+ if (err == ENOSYS)
+ fsess_set_notimpl(mp, FUSE_LISTXATTR);
+ debug_printf("listextattr: got err=%d from daemon\n", err);
+ goto out;
+ }
+
+ if ((fdi.answ == NULL) || (fdi.iosize == 0)) {
+ err = EINVAL;
+ 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;
+
+ 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);
+ 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 = {0};
+ struct mount *mp = vnode_mount(vp);
+ char *prefix;
+ size_t len;
+ char *attr_str;
+ struct thread *td = ap->a_td;
+ struct ucred *cred = ap->a_cred;
+ int err;
+
+ fuse_trace_printf_vnop();
+
+ if (fuse_isdeadfs(vp))
+ return ENXIO;
+
+ /* Default to looking for user attributes. */
+ if (ap->a_attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
+ prefix = EXTATTR_NAMESPACE_SYSTEM_STRING;
+ else
+ prefix = EXTATTR_NAMESPACE_USER_STRING;
+
+ len = strlen(prefix) + sizeof(extattr_namespace_separator) +
+ strlen(ap->a_name) + 1;
+
+ fdisp_init(&fdi, len);
+ fdisp_make_vp(&fdi, FUSE_REMOVEXATTR, vp, td, cred);
+
+ attr_str = fdi.indata;
+ snprintf(attr_str, len, "%s%c%s", prefix, extattr_namespace_separator,
+ ap->a_name);
+
+ err = fdisp_wait_answ(&fdi);
+ if (err != 0) {
+ if (err == ENOSYS)
+ fsess_set_notimpl(mp, FUSE_REMOVEXATTR);
+ debug_printf("removexattr: got err=%d from daemon\n", err);
+ }
+
+ fdisp_destroy(&fdi);
+ return (err);
}
/*
More information about the svn-src-all
mailing list