]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - fs/fuse/xattr.c
fuse: Use generic xattr ops
[mirror_ubuntu-hirsute-kernel.git] / fs / fuse / xattr.c
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
13 static int fuse_setxattr(struct inode *inode, const char *name,
14 const void *value, size_t size, int flags)
15 {
16 struct fuse_conn *fc = get_fuse_conn(inode);
17 FUSE_ARGS(args);
18 struct fuse_setxattr_in inarg;
19 int err;
20
21 if (fc->no_setxattr)
22 return -EOPNOTSUPP;
23
24 memset(&inarg, 0, sizeof(inarg));
25 inarg.size = size;
26 inarg.flags = flags;
27 args.in.h.opcode = FUSE_SETXATTR;
28 args.in.h.nodeid = get_node_id(inode);
29 args.in.numargs = 3;
30 args.in.args[0].size = sizeof(inarg);
31 args.in.args[0].value = &inarg;
32 args.in.args[1].size = strlen(name) + 1;
33 args.in.args[1].value = name;
34 args.in.args[2].size = size;
35 args.in.args[2].value = value;
36 err = fuse_simple_request(fc, &args);
37 if (err == -ENOSYS) {
38 fc->no_setxattr = 1;
39 err = -EOPNOTSUPP;
40 }
41 if (!err) {
42 fuse_invalidate_attr(inode);
43 fuse_update_ctime(inode);
44 }
45 return err;
46 }
47
48 static ssize_t fuse_getxattr(struct inode *inode, const char *name,
49 void *value, size_t size)
50 {
51 struct fuse_conn *fc = get_fuse_conn(inode);
52 FUSE_ARGS(args);
53 struct fuse_getxattr_in inarg;
54 struct fuse_getxattr_out outarg;
55 ssize_t ret;
56
57 if (fc->no_getxattr)
58 return -EOPNOTSUPP;
59
60 memset(&inarg, 0, sizeof(inarg));
61 inarg.size = size;
62 args.in.h.opcode = FUSE_GETXATTR;
63 args.in.h.nodeid = get_node_id(inode);
64 args.in.numargs = 2;
65 args.in.args[0].size = sizeof(inarg);
66 args.in.args[0].value = &inarg;
67 args.in.args[1].size = strlen(name) + 1;
68 args.in.args[1].value = name;
69 /* This is really two different operations rolled into one */
70 args.out.numargs = 1;
71 if (size) {
72 args.out.argvar = 1;
73 args.out.args[0].size = size;
74 args.out.args[0].value = value;
75 } else {
76 args.out.args[0].size = sizeof(outarg);
77 args.out.args[0].value = &outarg;
78 }
79 ret = fuse_simple_request(fc, &args);
80 if (!ret && !size)
81 ret = outarg.size;
82 if (ret == -ENOSYS) {
83 fc->no_getxattr = 1;
84 ret = -EOPNOTSUPP;
85 }
86 return ret;
87 }
88
89 static int fuse_verify_xattr_list(char *list, size_t size)
90 {
91 size_t origsize = size;
92
93 while (size) {
94 size_t thislen = strnlen(list, size);
95
96 if (!thislen || thislen == size)
97 return -EIO;
98
99 size -= thislen + 1;
100 list += thislen + 1;
101 }
102
103 return origsize;
104 }
105
106 ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
107 {
108 struct inode *inode = d_inode(entry);
109 struct fuse_conn *fc = get_fuse_conn(inode);
110 FUSE_ARGS(args);
111 struct fuse_getxattr_in inarg;
112 struct fuse_getxattr_out outarg;
113 ssize_t ret;
114
115 if (!fuse_allow_current_process(fc))
116 return -EACCES;
117
118 if (fc->no_listxattr)
119 return -EOPNOTSUPP;
120
121 memset(&inarg, 0, sizeof(inarg));
122 inarg.size = size;
123 args.in.h.opcode = FUSE_LISTXATTR;
124 args.in.h.nodeid = get_node_id(inode);
125 args.in.numargs = 1;
126 args.in.args[0].size = sizeof(inarg);
127 args.in.args[0].value = &inarg;
128 /* This is really two different operations rolled into one */
129 args.out.numargs = 1;
130 if (size) {
131 args.out.argvar = 1;
132 args.out.args[0].size = size;
133 args.out.args[0].value = list;
134 } else {
135 args.out.args[0].size = sizeof(outarg);
136 args.out.args[0].value = &outarg;
137 }
138 ret = fuse_simple_request(fc, &args);
139 if (!ret && !size)
140 ret = outarg.size;
141 if (ret > 0 && size)
142 ret = fuse_verify_xattr_list(list, ret);
143 if (ret == -ENOSYS) {
144 fc->no_listxattr = 1;
145 ret = -EOPNOTSUPP;
146 }
147 return ret;
148 }
149
150 static int fuse_removexattr(struct inode *inode, const char *name)
151 {
152 struct fuse_conn *fc = get_fuse_conn(inode);
153 FUSE_ARGS(args);
154 int err;
155
156 if (fc->no_removexattr)
157 return -EOPNOTSUPP;
158
159 args.in.h.opcode = FUSE_REMOVEXATTR;
160 args.in.h.nodeid = get_node_id(inode);
161 args.in.numargs = 1;
162 args.in.args[0].size = strlen(name) + 1;
163 args.in.args[0].value = name;
164 err = fuse_simple_request(fc, &args);
165 if (err == -ENOSYS) {
166 fc->no_removexattr = 1;
167 err = -EOPNOTSUPP;
168 }
169 if (!err) {
170 fuse_invalidate_attr(inode);
171 fuse_update_ctime(inode);
172 }
173 return err;
174 }
175
176 static int fuse_xattr_get(const struct xattr_handler *handler,
177 struct dentry *dentry, struct inode *inode,
178 const char *name, void *value, size_t size)
179 {
180 return fuse_getxattr(inode, name, value, size);
181 }
182
183 static int fuse_xattr_set(const struct xattr_handler *handler,
184 struct dentry *dentry, struct inode *inode,
185 const char *name, const void *value, size_t size,
186 int flags)
187 {
188 if (!value)
189 return fuse_removexattr(inode, name);
190
191 return fuse_setxattr(inode, name, value, size, flags);
192 }
193
194 static const struct xattr_handler fuse_xattr_handler = {
195 .prefix = "",
196 .get = fuse_xattr_get,
197 .set = fuse_xattr_set,
198 };
199
200 const struct xattr_handler *fuse_xattr_handlers[] = {
201 &fuse_xattr_handler,
202 NULL
203 };