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 ) |
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: &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: &args); |
83 | if (!ret && !size) |
84 | ret = min_t(ssize_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(p: list, maxlen: 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(dentry: 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(fc: 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: &args); |
145 | if (!ret && !size) |
146 | ret = min_t(ssize_t, outarg.size, XATTR_LIST_MAX); |
147 | if (ret > 0 && size) |
148 | ret = fuse_verify_xattr_list(list, size: 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: &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, extra_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 | |