1 /* 2 * FUSE: Filesystem in Userspace 3 * Copyright (C) 2001-2016 Miklos Szeredi <miklos@szeredi.hu> 4 * 5 * This program can be distributed under the terms of the GNU GPL. 6 * See the file COPYING. 7 */ 8 9 #include "fuse_i.h" 10 11 #include <linux/xattr.h> 12 #include <linux/posix_acl_xattr.h> 13 14 int fuse_setxattr(struct inode *inode, const char *name, const void *value, 15 size_t size, int flags, unsigned int extra_flags) 16 { 17 struct fuse_mount *fm = get_fuse_mount(inode); 18 FUSE_ARGS(args); 19 struct fuse_setxattr_in inarg; 20 int err; 21 22 if (fm->fc->no_setxattr) 23 return -EOPNOTSUPP; 24 25 memset(&inarg, 0, sizeof(inarg)); 26 inarg.size = size; 27 inarg.flags = flags; 28 inarg.setxattr_flags = extra_flags; 29 30 args.opcode = FUSE_SETXATTR; 31 args.nodeid = get_node_id(inode); 32 args.in_numargs = 3; 33 args.in_args[0].size = fm->fc->setxattr_ext ? 34 sizeof(inarg) : FUSE_COMPAT_SETXATTR_IN_SIZE; 35 args.in_args[0].value = &inarg; 36 args.in_args[1].size = strlen(name) + 1; 37 args.in_args[1].value = name; 38 args.in_args[2].size = size; 39 args.in_args[2].value = value; 40 err = fuse_simple_request(fm, &args); 41 if (err == -ENOSYS) { 42 fm->fc->no_setxattr = 1; 43 err = -EOPNOTSUPP; 44 } 45 if (!err) 46 fuse_update_ctime(inode); 47 48 return err; 49 } 50 51 ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value, 52 size_t size) 53 { 54 struct fuse_mount *fm = get_fuse_mount(inode); 55 FUSE_ARGS(args); 56 struct fuse_getxattr_in inarg; 57 struct fuse_getxattr_out outarg; 58 ssize_t ret; 59 60 if (fm->fc->no_getxattr) 61 return -EOPNOTSUPP; 62 63 memset(&inarg, 0, sizeof(inarg)); 64 inarg.size = size; 65 args.opcode = FUSE_GETXATTR; 66 args.nodeid = get_node_id(inode); 67 args.in_numargs = 2; 68 args.in_args[0].size = sizeof(inarg); 69 args.in_args[0].value = &inarg; 70 args.in_args[1].size = strlen(name) + 1; 71 args.in_args[1].value = name; 72 /* This is really two different operations rolled into one */ 73 args.out_numargs = 1; 74 if (size) { 75 args.out_argvar = true; 76 args.out_args[0].size = size; 77 args.out_args[0].value = value; 78 } else { 79 args.out_args[0].size = sizeof(outarg); 80 args.out_args[0].value = &outarg; 81 } 82 ret = fuse_simple_request(fm, &args); 83 if (!ret && !size) 84 ret = min_t(size_t, outarg.size, XATTR_SIZE_MAX); 85 if (ret == -ENOSYS) { 86 fm->fc->no_getxattr = 1; 87 ret = -EOPNOTSUPP; 88 } 89 return ret; 90 } 91 92 static int fuse_verify_xattr_list(char *list, size_t size) 93 { 94 size_t origsize = size; 95 96 while (size) { 97 size_t thislen = strnlen(list, size); 98 99 if (!thislen || thislen == size) 100 return -EIO; 101 102 size -= thislen + 1; 103 list += thislen + 1; 104 } 105 106 return origsize; 107 } 108 109 ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) 110 { 111 struct inode *inode = d_inode(entry); 112 struct fuse_mount *fm = get_fuse_mount(inode); 113 FUSE_ARGS(args); 114 struct fuse_getxattr_in inarg; 115 struct fuse_getxattr_out outarg; 116 ssize_t ret; 117 118 if (fuse_is_bad(inode)) 119 return -EIO; 120 121 if (!fuse_allow_current_process(fm->fc)) 122 return -EACCES; 123 124 if (fm->fc->no_listxattr) 125 return -EOPNOTSUPP; 126 127 memset(&inarg, 0, sizeof(inarg)); 128 inarg.size = size; 129 args.opcode = FUSE_LISTXATTR; 130 args.nodeid = get_node_id(inode); 131 args.in_numargs = 1; 132 args.in_args[0].size = sizeof(inarg); 133 args.in_args[0].value = &inarg; 134 /* This is really two different operations rolled into one */ 135 args.out_numargs = 1; 136 if (size) { 137 args.out_argvar = true; 138 args.out_args[0].size = size; 139 args.out_args[0].value = list; 140 } else { 141 args.out_args[0].size = sizeof(outarg); 142 args.out_args[0].value = &outarg; 143 } 144 ret = fuse_simple_request(fm, &args); 145 if (!ret && !size) 146 ret = min_t(size_t, outarg.size, XATTR_LIST_MAX); 147 if (ret > 0 && size) 148 ret = fuse_verify_xattr_list(list, ret); 149 if (ret == -ENOSYS) { 150 fm->fc->no_listxattr = 1; 151 ret = -EOPNOTSUPP; 152 } 153 return ret; 154 } 155 156 int fuse_removexattr(struct inode *inode, const char *name) 157 { 158 struct fuse_mount *fm = get_fuse_mount(inode); 159 FUSE_ARGS(args); 160 int err; 161 162 if (fm->fc->no_removexattr) 163 return -EOPNOTSUPP; 164 165 args.opcode = FUSE_REMOVEXATTR; 166 args.nodeid = get_node_id(inode); 167 args.in_numargs = 1; 168 args.in_args[0].size = strlen(name) + 1; 169 args.in_args[0].value = name; 170 err = fuse_simple_request(fm, &args); 171 if (err == -ENOSYS) { 172 fm->fc->no_removexattr = 1; 173 err = -EOPNOTSUPP; 174 } 175 if (!err) 176 fuse_update_ctime(inode); 177 178 return err; 179 } 180 181 static int fuse_xattr_get(const struct xattr_handler *handler, 182 struct dentry *dentry, struct inode *inode, 183 const char *name, void *value, size_t size) 184 { 185 if (fuse_is_bad(inode)) 186 return -EIO; 187 188 return fuse_getxattr(inode, name, value, size); 189 } 190 191 static int fuse_xattr_set(const struct xattr_handler *handler, 192 struct mnt_idmap *idmap, 193 struct dentry *dentry, struct inode *inode, 194 const char *name, const void *value, size_t size, 195 int flags) 196 { 197 if (fuse_is_bad(inode)) 198 return -EIO; 199 200 if (!value) 201 return fuse_removexattr(inode, name); 202 203 return fuse_setxattr(inode, name, value, size, flags, 0); 204 } 205 206 static const struct xattr_handler fuse_xattr_handler = { 207 .prefix = "", 208 .get = fuse_xattr_get, 209 .set = fuse_xattr_set, 210 }; 211 212 const struct xattr_handler * const fuse_xattr_handlers[] = { 213 &fuse_xattr_handler, 214 NULL 215 }; 216