xref: /linux/fs/fuse/xattr.c (revision 80d443e8876602be2c130f79c4de81e12e2a700d)
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)
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 
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 
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 
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_allow_current_process(fc))
117 		return -EACCES;
118 
119 	if (fc->no_listxattr)
120 		return -EOPNOTSUPP;
121 
122 	memset(&inarg, 0, sizeof(inarg));
123 	inarg.size = size;
124 	args.in.h.opcode = FUSE_LISTXATTR;
125 	args.in.h.nodeid = get_node_id(inode);
126 	args.in.numargs = 1;
127 	args.in.args[0].size = sizeof(inarg);
128 	args.in.args[0].value = &inarg;
129 	/* This is really two different operations rolled into one */
130 	args.out.numargs = 1;
131 	if (size) {
132 		args.out.argvar = 1;
133 		args.out.args[0].size = size;
134 		args.out.args[0].value = list;
135 	} else {
136 		args.out.args[0].size = sizeof(outarg);
137 		args.out.args[0].value = &outarg;
138 	}
139 	ret = fuse_simple_request(fc, &args);
140 	if (!ret && !size)
141 		ret = min_t(ssize_t, outarg.size, XATTR_LIST_MAX);
142 	if (ret > 0 && size)
143 		ret = fuse_verify_xattr_list(list, ret);
144 	if (ret == -ENOSYS) {
145 		fc->no_listxattr = 1;
146 		ret = -EOPNOTSUPP;
147 	}
148 	return ret;
149 }
150 
151 int fuse_removexattr(struct inode *inode, const char *name)
152 {
153 	struct fuse_conn *fc = get_fuse_conn(inode);
154 	FUSE_ARGS(args);
155 	int err;
156 
157 	if (fc->no_removexattr)
158 		return -EOPNOTSUPP;
159 
160 	args.in.h.opcode = FUSE_REMOVEXATTR;
161 	args.in.h.nodeid = get_node_id(inode);
162 	args.in.numargs = 1;
163 	args.in.args[0].size = strlen(name) + 1;
164 	args.in.args[0].value = name;
165 	err = fuse_simple_request(fc, &args);
166 	if (err == -ENOSYS) {
167 		fc->no_removexattr = 1;
168 		err = -EOPNOTSUPP;
169 	}
170 	if (!err) {
171 		fuse_invalidate_attr(inode);
172 		fuse_update_ctime(inode);
173 	}
174 	return err;
175 }
176 
177 static int fuse_xattr_get(const struct xattr_handler *handler,
178 			 struct dentry *dentry, struct inode *inode,
179 			 const char *name, void *value, size_t size)
180 {
181 	return fuse_getxattr(inode, name, value, size);
182 }
183 
184 static int fuse_xattr_set(const struct xattr_handler *handler,
185 			  struct dentry *dentry, struct inode *inode,
186 			  const char *name, const void *value, size_t size,
187 			  int flags)
188 {
189 	if (!value)
190 		return fuse_removexattr(inode, name);
191 
192 	return fuse_setxattr(inode, name, value, size, flags);
193 }
194 
195 static const struct xattr_handler fuse_xattr_handler = {
196 	.prefix = "",
197 	.get    = fuse_xattr_get,
198 	.set    = fuse_xattr_set,
199 };
200 
201 const struct xattr_handler *fuse_xattr_handlers[] = {
202 	&fuse_xattr_handler,
203 	NULL
204 };
205 
206 const struct xattr_handler *fuse_acl_xattr_handlers[] = {
207 	&posix_acl_access_xattr_handler,
208 	&posix_acl_default_xattr_handler,
209 	&fuse_xattr_handler,
210 	NULL
211 };
212