fuse: Use generic xattr ops
[cascardo/linux.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 };