xref: /openbmc/linux/fs/fuse/xattr.c (revision 48cc39c3)
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_invalidate_attr(inode);
47 		fuse_update_ctime(inode);
48 	}
49 	return err;
50 }
51 
52 ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
53 		      size_t size)
54 {
55 	struct fuse_mount *fm = get_fuse_mount(inode);
56 	FUSE_ARGS(args);
57 	struct fuse_getxattr_in inarg;
58 	struct fuse_getxattr_out outarg;
59 	ssize_t ret;
60 
61 	if (fm->fc->no_getxattr)
62 		return -EOPNOTSUPP;
63 
64 	memset(&inarg, 0, sizeof(inarg));
65 	inarg.size = size;
66 	args.opcode = FUSE_GETXATTR;
67 	args.nodeid = get_node_id(inode);
68 	args.in_numargs = 2;
69 	args.in_args[0].size = sizeof(inarg);
70 	args.in_args[0].value = &inarg;
71 	args.in_args[1].size = strlen(name) + 1;
72 	args.in_args[1].value = name;
73 	/* This is really two different operations rolled into one */
74 	args.out_numargs = 1;
75 	if (size) {
76 		args.out_argvar = true;
77 		args.out_args[0].size = size;
78 		args.out_args[0].value = value;
79 	} else {
80 		args.out_args[0].size = sizeof(outarg);
81 		args.out_args[0].value = &outarg;
82 	}
83 	ret = fuse_simple_request(fm, &args);
84 	if (!ret && !size)
85 		ret = min_t(ssize_t, outarg.size, XATTR_SIZE_MAX);
86 	if (ret == -ENOSYS) {
87 		fm->fc->no_getxattr = 1;
88 		ret = -EOPNOTSUPP;
89 	}
90 	return ret;
91 }
92 
93 static int fuse_verify_xattr_list(char *list, size_t size)
94 {
95 	size_t origsize = size;
96 
97 	while (size) {
98 		size_t thislen = strnlen(list, size);
99 
100 		if (!thislen || thislen == size)
101 			return -EIO;
102 
103 		size -= thislen + 1;
104 		list += thislen + 1;
105 	}
106 
107 	return origsize;
108 }
109 
110 ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
111 {
112 	struct inode *inode = d_inode(entry);
113 	struct fuse_mount *fm = get_fuse_mount(inode);
114 	FUSE_ARGS(args);
115 	struct fuse_getxattr_in inarg;
116 	struct fuse_getxattr_out outarg;
117 	ssize_t ret;
118 
119 	if (fuse_is_bad(inode))
120 		return -EIO;
121 
122 	if (!fuse_allow_current_process(fm->fc))
123 		return -EACCES;
124 
125 	if (fm->fc->no_listxattr)
126 		return -EOPNOTSUPP;
127 
128 	memset(&inarg, 0, sizeof(inarg));
129 	inarg.size = size;
130 	args.opcode = FUSE_LISTXATTR;
131 	args.nodeid = get_node_id(inode);
132 	args.in_numargs = 1;
133 	args.in_args[0].size = sizeof(inarg);
134 	args.in_args[0].value = &inarg;
135 	/* This is really two different operations rolled into one */
136 	args.out_numargs = 1;
137 	if (size) {
138 		args.out_argvar = true;
139 		args.out_args[0].size = size;
140 		args.out_args[0].value = list;
141 	} else {
142 		args.out_args[0].size = sizeof(outarg);
143 		args.out_args[0].value = &outarg;
144 	}
145 	ret = fuse_simple_request(fm, &args);
146 	if (!ret && !size)
147 		ret = min_t(ssize_t, outarg.size, XATTR_LIST_MAX);
148 	if (ret > 0 && size)
149 		ret = fuse_verify_xattr_list(list, ret);
150 	if (ret == -ENOSYS) {
151 		fm->fc->no_listxattr = 1;
152 		ret = -EOPNOTSUPP;
153 	}
154 	return ret;
155 }
156 
157 int fuse_removexattr(struct inode *inode, const char *name)
158 {
159 	struct fuse_mount *fm = get_fuse_mount(inode);
160 	FUSE_ARGS(args);
161 	int err;
162 
163 	if (fm->fc->no_removexattr)
164 		return -EOPNOTSUPP;
165 
166 	args.opcode = FUSE_REMOVEXATTR;
167 	args.nodeid = get_node_id(inode);
168 	args.in_numargs = 1;
169 	args.in_args[0].size = strlen(name) + 1;
170 	args.in_args[0].value = name;
171 	err = fuse_simple_request(fm, &args);
172 	if (err == -ENOSYS) {
173 		fm->fc->no_removexattr = 1;
174 		err = -EOPNOTSUPP;
175 	}
176 	if (!err) {
177 		fuse_invalidate_attr(inode);
178 		fuse_update_ctime(inode);
179 	}
180 	return err;
181 }
182 
183 static int fuse_xattr_get(const struct xattr_handler *handler,
184 			 struct dentry *dentry, struct inode *inode,
185 			 const char *name, void *value, size_t size)
186 {
187 	if (fuse_is_bad(inode))
188 		return -EIO;
189 
190 	return fuse_getxattr(inode, name, value, size);
191 }
192 
193 static int fuse_xattr_set(const struct xattr_handler *handler,
194 			  struct user_namespace *mnt_userns,
195 			  struct dentry *dentry, struct inode *inode,
196 			  const char *name, const void *value, size_t size,
197 			  int flags)
198 {
199 	if (fuse_is_bad(inode))
200 		return -EIO;
201 
202 	if (!value)
203 		return fuse_removexattr(inode, name);
204 
205 	return fuse_setxattr(inode, name, value, size, flags, 0);
206 }
207 
208 static bool no_xattr_list(struct dentry *dentry)
209 {
210 	return false;
211 }
212 
213 static int no_xattr_get(const struct xattr_handler *handler,
214 			struct dentry *dentry, struct inode *inode,
215 			const char *name, void *value, size_t size)
216 {
217 	return -EOPNOTSUPP;
218 }
219 
220 static int no_xattr_set(const struct xattr_handler *handler,
221 			struct user_namespace *mnt_userns,
222 			struct dentry *dentry, struct inode *nodee,
223 			const char *name, const void *value,
224 			size_t size, int flags)
225 {
226 	return -EOPNOTSUPP;
227 }
228 
229 static const struct xattr_handler fuse_xattr_handler = {
230 	.prefix = "",
231 	.get    = fuse_xattr_get,
232 	.set    = fuse_xattr_set,
233 };
234 
235 const struct xattr_handler *fuse_xattr_handlers[] = {
236 	&fuse_xattr_handler,
237 	NULL
238 };
239 
240 const struct xattr_handler *fuse_acl_xattr_handlers[] = {
241 	&posix_acl_access_xattr_handler,
242 	&posix_acl_default_xattr_handler,
243 	&fuse_xattr_handler,
244 	NULL
245 };
246 
247 static const struct xattr_handler fuse_no_acl_access_xattr_handler = {
248 	.name  = XATTR_NAME_POSIX_ACL_ACCESS,
249 	.flags = ACL_TYPE_ACCESS,
250 	.list  = no_xattr_list,
251 	.get   = no_xattr_get,
252 	.set   = no_xattr_set,
253 };
254 
255 static const struct xattr_handler fuse_no_acl_default_xattr_handler = {
256 	.name  = XATTR_NAME_POSIX_ACL_DEFAULT,
257 	.flags = ACL_TYPE_ACCESS,
258 	.list  = no_xattr_list,
259 	.get   = no_xattr_get,
260 	.set   = no_xattr_set,
261 };
262 
263 const struct xattr_handler *fuse_no_acl_xattr_handlers[] = {
264 	&fuse_no_acl_access_xattr_handler,
265 	&fuse_no_acl_default_xattr_handler,
266 	&fuse_xattr_handler,
267 	NULL
268 };
269