xref: /qemu/hw/9pfs/9p-xattr.c (revision 6402cbbb)
1 /*
2  * 9p  xattr callback
3  *
4  * Copyright IBM, Corp. 2010
5  *
6  * Authors:
7  * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "9p.h"
16 #include "fsdev/file-op-9p.h"
17 #include "9p-xattr.h"
18 #include "9p-util.h"
19 #include "9p-local.h"
20 
21 
22 static XattrOperations *get_xattr_operations(XattrOperations **h,
23                                              const char *name)
24 {
25     XattrOperations *xops;
26     for (xops = *(h)++; xops != NULL; xops = *(h)++) {
27         if (!strncmp(name, xops->name, strlen(xops->name))) {
28             return xops;
29         }
30     }
31     return NULL;
32 }
33 
34 ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
35                        const char *name, void *value, size_t size)
36 {
37     XattrOperations *xops = get_xattr_operations(ctx->xops, name);
38     if (xops) {
39         return xops->getxattr(ctx, path, name, value, size);
40     }
41     errno = EOPNOTSUPP;
42     return -1;
43 }
44 
45 ssize_t pt_listxattr(FsContext *ctx, const char *path,
46                      char *name, void *value, size_t size)
47 {
48     int name_size = strlen(name) + 1;
49     if (!value) {
50         return name_size;
51     }
52 
53     if (size < name_size) {
54         errno = ERANGE;
55         return -1;
56     }
57 
58     /* no need for strncpy: name_size is strlen(name)+1 */
59     memcpy(value, name, name_size);
60     return name_size;
61 }
62 
63 static ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
64                                      char *list, size_t size)
65 {
66     char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
67     int ret;
68 
69     ret = llistxattr(proc_path, list, size);
70     g_free(proc_path);
71     return ret;
72 }
73 
74 /*
75  * Get the list and pass to each layer to find out whether
76  * to send the data or not
77  */
78 ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
79                         void *value, size_t vsize)
80 {
81     ssize_t size = 0;
82     void *ovalue = value;
83     XattrOperations *xops;
84     char *orig_value, *orig_value_start;
85     ssize_t xattr_len, parsed_len = 0, attr_len;
86     char *dirpath, *name;
87     int dirfd;
88 
89     /* Get the actual len */
90     dirpath = g_path_get_dirname(path);
91     dirfd = local_opendir_nofollow(ctx, dirpath);
92     g_free(dirpath);
93     if (dirfd == -1) {
94         return -1;
95     }
96 
97     name = g_path_get_basename(path);
98     xattr_len = flistxattrat_nofollow(dirfd, name, value, 0);
99     if (xattr_len <= 0) {
100         g_free(name);
101         close_preserve_errno(dirfd);
102         return xattr_len;
103     }
104 
105     /* Now fetch the xattr and find the actual size */
106     orig_value = g_malloc(xattr_len);
107     xattr_len = flistxattrat_nofollow(dirfd, name, orig_value, xattr_len);
108     g_free(name);
109     close_preserve_errno(dirfd);
110     if (xattr_len < 0) {
111         g_free(orig_value);
112         return -1;
113     }
114 
115     /* store the orig pointer */
116     orig_value_start = orig_value;
117     while (xattr_len > parsed_len) {
118         xops = get_xattr_operations(ctx->xops, orig_value);
119         if (!xops) {
120             goto next_entry;
121         }
122 
123         if (!value) {
124             size += xops->listxattr(ctx, path, orig_value, value, vsize);
125         } else {
126             size = xops->listxattr(ctx, path, orig_value, value, vsize);
127             if (size < 0) {
128                 goto err_out;
129             }
130             value += size;
131             vsize -= size;
132         }
133 next_entry:
134         /* Got the next entry */
135         attr_len = strlen(orig_value) + 1;
136         parsed_len += attr_len;
137         orig_value += attr_len;
138     }
139     if (value) {
140         size = value - ovalue;
141     }
142 
143 err_out:
144     g_free(orig_value_start);
145     return size;
146 }
147 
148 int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
149                    void *value, size_t size, int flags)
150 {
151     XattrOperations *xops = get_xattr_operations(ctx->xops, name);
152     if (xops) {
153         return xops->setxattr(ctx, path, name, value, size, flags);
154     }
155     errno = EOPNOTSUPP;
156     return -1;
157 
158 }
159 
160 int v9fs_remove_xattr(FsContext *ctx,
161                       const char *path, const char *name)
162 {
163     XattrOperations *xops = get_xattr_operations(ctx->xops, name);
164     if (xops) {
165         return xops->removexattr(ctx, path, name);
166     }
167     errno = EOPNOTSUPP;
168     return -1;
169 
170 }
171 
172 ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
173                                 const char *name, void *value, size_t size)
174 {
175     char *dirpath = g_path_get_dirname(path);
176     char *filename = g_path_get_basename(path);
177     int dirfd;
178     ssize_t ret = -1;
179 
180     dirfd = local_opendir_nofollow(ctx, dirpath);
181     if (dirfd == -1) {
182         goto out;
183     }
184 
185     ret = fgetxattrat_nofollow(dirfd, filename, name, value, size);
186     close_preserve_errno(dirfd);
187 out:
188     g_free(dirpath);
189     g_free(filename);
190     return ret;
191 }
192 
193 ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
194                     void *value, size_t size)
195 {
196     return local_getxattr_nofollow(ctx, path, name, value, size);
197 }
198 
199 int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
200                          void *value, size_t size, int flags)
201 {
202     char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
203     int ret;
204 
205     ret = lsetxattr(proc_path, name, value, size, flags);
206     g_free(proc_path);
207     return ret;
208 }
209 
210 ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
211                                 const char *name, void *value, size_t size,
212                                 int flags)
213 {
214     char *dirpath = g_path_get_dirname(path);
215     char *filename = g_path_get_basename(path);
216     int dirfd;
217     ssize_t ret = -1;
218 
219     dirfd = local_opendir_nofollow(ctx, dirpath);
220     if (dirfd == -1) {
221         goto out;
222     }
223 
224     ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags);
225     close_preserve_errno(dirfd);
226 out:
227     g_free(dirpath);
228     g_free(filename);
229     return ret;
230 }
231 
232 int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
233                 size_t size, int flags)
234 {
235     return local_setxattr_nofollow(ctx, path, name, value, size, flags);
236 }
237 
238 static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
239                                        const char *name)
240 {
241     char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
242     int ret;
243 
244     ret = lremovexattr(proc_path, name);
245     g_free(proc_path);
246     return ret;
247 }
248 
249 ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
250                                    const char *name)
251 {
252     char *dirpath = g_path_get_dirname(path);
253     char *filename = g_path_get_basename(path);
254     int dirfd;
255     ssize_t ret = -1;
256 
257     dirfd = local_opendir_nofollow(ctx, dirpath);
258     if (dirfd == -1) {
259         goto out;
260     }
261 
262     ret = fremovexattrat_nofollow(dirfd, filename, name);
263     close_preserve_errno(dirfd);
264 out:
265     g_free(dirpath);
266     g_free(filename);
267     return ret;
268 }
269 
270 int pt_removexattr(FsContext *ctx, const char *path, const char *name)
271 {
272     return local_removexattr_nofollow(ctx, path, name);
273 }
274 
275 ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
276                         void *value, size_t size)
277 {
278     errno = ENOTSUP;
279     return -1;
280 }
281 
282 int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
283                     void *value, size_t size, int flags)
284 {
285     errno = ENOTSUP;
286     return -1;
287 }
288 
289 ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
290                          void *value, size_t size)
291 {
292     return 0;
293 }
294 
295 int notsup_removexattr(FsContext *ctx, const char *path, const char *name)
296 {
297     errno = ENOTSUP;
298     return -1;
299 }
300 
301 XattrOperations *mapped_xattr_ops[] = {
302     &mapped_user_xattr,
303     &mapped_pacl_xattr,
304     &mapped_dacl_xattr,
305     NULL,
306 };
307 
308 XattrOperations *passthrough_xattr_ops[] = {
309     &passthrough_user_xattr,
310     &passthrough_acl_xattr,
311     NULL,
312 };
313 
314 /* for .user none model should be same as passthrough */
315 XattrOperations *none_xattr_ops[] = {
316     &passthrough_user_xattr,
317     &none_acl_xattr,
318     NULL,
319 };
320