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