xref: /qemu/hw/9pfs/9p-xattr.c (revision 3e36aba7)
1267ae092SWei Liu /*
2267ae092SWei Liu  * 9p  xattr callback
3267ae092SWei Liu  *
4267ae092SWei Liu  * Copyright IBM, Corp. 2010
5267ae092SWei Liu  *
6267ae092SWei Liu  * Authors:
7267ae092SWei Liu  * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
8267ae092SWei Liu  *
9267ae092SWei Liu  * This work is licensed under the terms of the GNU GPL, version 2.  See
10267ae092SWei Liu  * the COPYING file in the top-level directory.
11267ae092SWei Liu  *
12267ae092SWei Liu  */
13267ae092SWei Liu 
14fbc04127SPeter Maydell #include "qemu/osdep.h"
15ebe74f8bSWei Liu #include "9p.h"
16267ae092SWei Liu #include "fsdev/file-op-9p.h"
17267ae092SWei Liu #include "9p-xattr.h"
1856ad3e54SGreg Kurz #include "9p-util.h"
1956ad3e54SGreg Kurz #include "9p-local.h"
20267ae092SWei Liu 
21267ae092SWei Liu 
22267ae092SWei Liu static XattrOperations *get_xattr_operations(XattrOperations **h,
23267ae092SWei Liu                                              const char *name)
24267ae092SWei Liu {
25267ae092SWei Liu     XattrOperations *xops;
26267ae092SWei Liu     for (xops = *(h)++; xops != NULL; xops = *(h)++) {
27267ae092SWei Liu         if (!strncmp(name, xops->name, strlen(xops->name))) {
28267ae092SWei Liu             return xops;
29267ae092SWei Liu         }
30267ae092SWei Liu     }
31267ae092SWei Liu     return NULL;
32267ae092SWei Liu }
33267ae092SWei Liu 
34267ae092SWei Liu ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
35267ae092SWei Liu                        const char *name, void *value, size_t size)
36267ae092SWei Liu {
37267ae092SWei Liu     XattrOperations *xops = get_xattr_operations(ctx->xops, name);
38267ae092SWei Liu     if (xops) {
39267ae092SWei Liu         return xops->getxattr(ctx, path, name, value, size);
40267ae092SWei Liu     }
41267ae092SWei Liu     errno = EOPNOTSUPP;
42267ae092SWei Liu     return -1;
43267ae092SWei Liu }
44267ae092SWei Liu 
45267ae092SWei Liu ssize_t pt_listxattr(FsContext *ctx, const char *path,
46267ae092SWei Liu                      char *name, void *value, size_t size)
47267ae092SWei Liu {
48267ae092SWei Liu     int name_size = strlen(name) + 1;
49267ae092SWei Liu     if (!value) {
50267ae092SWei Liu         return name_size;
51267ae092SWei Liu     }
52267ae092SWei Liu 
53267ae092SWei Liu     if (size < name_size) {
54267ae092SWei Liu         errno = ERANGE;
55267ae092SWei Liu         return -1;
56267ae092SWei Liu     }
57267ae092SWei Liu 
58267ae092SWei Liu     /* no need for strncpy: name_size is strlen(name)+1 */
59267ae092SWei Liu     memcpy(value, name, name_size);
60267ae092SWei Liu     return name_size;
61267ae092SWei Liu }
62267ae092SWei Liu 
635507904eSGreg Kurz static ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
645507904eSGreg Kurz                                      char *list, size_t size)
655507904eSGreg Kurz {
665507904eSGreg Kurz     char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
675507904eSGreg Kurz     int ret;
685507904eSGreg Kurz 
695507904eSGreg Kurz     ret = llistxattr(proc_path, list, size);
705507904eSGreg Kurz     g_free(proc_path);
715507904eSGreg Kurz     return ret;
725507904eSGreg Kurz }
73267ae092SWei Liu 
74267ae092SWei Liu /*
75267ae092SWei Liu  * Get the list and pass to each layer to find out whether
76267ae092SWei Liu  * to send the data or not
77267ae092SWei Liu  */
78267ae092SWei Liu ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
79267ae092SWei Liu                         void *value, size_t vsize)
80267ae092SWei Liu {
81267ae092SWei Liu     ssize_t size = 0;
82267ae092SWei Liu     void *ovalue = value;
83267ae092SWei Liu     XattrOperations *xops;
84267ae092SWei Liu     char *orig_value, *orig_value_start;
85267ae092SWei Liu     ssize_t xattr_len, parsed_len = 0, attr_len;
865507904eSGreg Kurz     char *dirpath, *name;
875507904eSGreg Kurz     int dirfd;
88267ae092SWei Liu 
89267ae092SWei Liu     /* Get the actual len */
905507904eSGreg Kurz     dirpath = g_path_get_dirname(path);
915507904eSGreg Kurz     dirfd = local_opendir_nofollow(ctx, dirpath);
925507904eSGreg Kurz     g_free(dirpath);
935507904eSGreg Kurz     if (dirfd == -1) {
945507904eSGreg Kurz         return -1;
955507904eSGreg Kurz     }
965507904eSGreg Kurz 
975507904eSGreg Kurz     name = g_path_get_basename(path);
985507904eSGreg Kurz     xattr_len = flistxattrat_nofollow(dirfd, name, value, 0);
99267ae092SWei Liu     if (xattr_len <= 0) {
1005507904eSGreg Kurz         g_free(name);
1015507904eSGreg Kurz         close_preserve_errno(dirfd);
102267ae092SWei Liu         return xattr_len;
103267ae092SWei Liu     }
104267ae092SWei Liu 
105267ae092SWei Liu     /* Now fetch the xattr and find the actual size */
106267ae092SWei Liu     orig_value = g_malloc(xattr_len);
1075507904eSGreg Kurz     xattr_len = flistxattrat_nofollow(dirfd, name, orig_value, xattr_len);
1085507904eSGreg Kurz     g_free(name);
1095507904eSGreg Kurz     close_preserve_errno(dirfd);
1105507904eSGreg Kurz     if (xattr_len < 0) {
1115507904eSGreg Kurz         return -1;
1125507904eSGreg Kurz     }
113267ae092SWei Liu 
114267ae092SWei Liu     /* store the orig pointer */
115267ae092SWei Liu     orig_value_start = orig_value;
116267ae092SWei Liu     while (xattr_len > parsed_len) {
117267ae092SWei Liu         xops = get_xattr_operations(ctx->xops, orig_value);
118267ae092SWei Liu         if (!xops) {
119267ae092SWei Liu             goto next_entry;
120267ae092SWei Liu         }
121267ae092SWei Liu 
122267ae092SWei Liu         if (!value) {
123267ae092SWei Liu             size += xops->listxattr(ctx, path, orig_value, value, vsize);
124267ae092SWei Liu         } else {
125267ae092SWei Liu             size = xops->listxattr(ctx, path, orig_value, value, vsize);
126267ae092SWei Liu             if (size < 0) {
127267ae092SWei Liu                 goto err_out;
128267ae092SWei Liu             }
129267ae092SWei Liu             value += size;
130267ae092SWei Liu             vsize -= size;
131267ae092SWei Liu         }
132267ae092SWei Liu next_entry:
133267ae092SWei Liu         /* Got the next entry */
134267ae092SWei Liu         attr_len = strlen(orig_value) + 1;
135267ae092SWei Liu         parsed_len += attr_len;
136267ae092SWei Liu         orig_value += attr_len;
137267ae092SWei Liu     }
138267ae092SWei Liu     if (value) {
139267ae092SWei Liu         size = value - ovalue;
140267ae092SWei Liu     }
141267ae092SWei Liu 
142267ae092SWei Liu err_out:
143267ae092SWei Liu     g_free(orig_value_start);
144267ae092SWei Liu     return size;
145267ae092SWei Liu }
146267ae092SWei Liu 
147267ae092SWei Liu int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
148267ae092SWei Liu                    void *value, size_t size, int flags)
149267ae092SWei Liu {
150267ae092SWei Liu     XattrOperations *xops = get_xattr_operations(ctx->xops, name);
151267ae092SWei Liu     if (xops) {
152267ae092SWei Liu         return xops->setxattr(ctx, path, name, value, size, flags);
153267ae092SWei Liu     }
154267ae092SWei Liu     errno = EOPNOTSUPP;
155267ae092SWei Liu     return -1;
156267ae092SWei Liu 
157267ae092SWei Liu }
158267ae092SWei Liu 
159267ae092SWei Liu int v9fs_remove_xattr(FsContext *ctx,
160267ae092SWei Liu                       const char *path, const char *name)
161267ae092SWei Liu {
162267ae092SWei Liu     XattrOperations *xops = get_xattr_operations(ctx->xops, name);
163267ae092SWei Liu     if (xops) {
164267ae092SWei Liu         return xops->removexattr(ctx, path, name);
165267ae092SWei Liu     }
166267ae092SWei Liu     errno = EOPNOTSUPP;
167267ae092SWei Liu     return -1;
168267ae092SWei Liu 
169267ae092SWei Liu }
170267ae092SWei Liu 
17156ad3e54SGreg Kurz ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
17256ad3e54SGreg Kurz                                 const char *name, void *value, size_t size)
17356ad3e54SGreg Kurz {
17456ad3e54SGreg Kurz     char *dirpath = g_path_get_dirname(path);
17556ad3e54SGreg Kurz     char *filename = g_path_get_basename(path);
17656ad3e54SGreg Kurz     int dirfd;
17756ad3e54SGreg Kurz     ssize_t ret = -1;
17856ad3e54SGreg Kurz 
17956ad3e54SGreg Kurz     dirfd = local_opendir_nofollow(ctx, dirpath);
18056ad3e54SGreg Kurz     if (dirfd == -1) {
18156ad3e54SGreg Kurz         goto out;
18256ad3e54SGreg Kurz     }
18356ad3e54SGreg Kurz 
18456ad3e54SGreg Kurz     ret = fgetxattrat_nofollow(dirfd, filename, name, value, size);
18556ad3e54SGreg Kurz     close_preserve_errno(dirfd);
18656ad3e54SGreg Kurz out:
18756ad3e54SGreg Kurz     g_free(dirpath);
18856ad3e54SGreg Kurz     g_free(filename);
18956ad3e54SGreg Kurz     return ret;
19056ad3e54SGreg Kurz }
19156ad3e54SGreg Kurz 
19256fc494bSGreg Kurz ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
19356fc494bSGreg Kurz                     void *value, size_t size)
19456fc494bSGreg Kurz {
19556ad3e54SGreg Kurz     return local_getxattr_nofollow(ctx, path, name, value, size);
19656fc494bSGreg Kurz }
19756fc494bSGreg Kurz 
198*3e36aba7SGreg Kurz int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
199*3e36aba7SGreg Kurz                          void *value, size_t size, int flags)
200*3e36aba7SGreg Kurz {
201*3e36aba7SGreg Kurz     char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
202*3e36aba7SGreg Kurz     int ret;
203*3e36aba7SGreg Kurz 
204*3e36aba7SGreg Kurz     ret = lsetxattr(proc_path, name, value, size, flags);
205*3e36aba7SGreg Kurz     g_free(proc_path);
206*3e36aba7SGreg Kurz     return ret;
207*3e36aba7SGreg Kurz }
208*3e36aba7SGreg Kurz 
209*3e36aba7SGreg Kurz ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
210*3e36aba7SGreg Kurz                                 const char *name, void *value, size_t size,
211*3e36aba7SGreg Kurz                                 int flags)
212*3e36aba7SGreg Kurz {
213*3e36aba7SGreg Kurz     char *dirpath = g_path_get_dirname(path);
214*3e36aba7SGreg Kurz     char *filename = g_path_get_basename(path);
215*3e36aba7SGreg Kurz     int dirfd;
216*3e36aba7SGreg Kurz     ssize_t ret = -1;
217*3e36aba7SGreg Kurz 
218*3e36aba7SGreg Kurz     dirfd = local_opendir_nofollow(ctx, dirpath);
219*3e36aba7SGreg Kurz     if (dirfd == -1) {
220*3e36aba7SGreg Kurz         goto out;
221*3e36aba7SGreg Kurz     }
222*3e36aba7SGreg Kurz 
223*3e36aba7SGreg Kurz     ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags);
224*3e36aba7SGreg Kurz     close_preserve_errno(dirfd);
225*3e36aba7SGreg Kurz out:
226*3e36aba7SGreg Kurz     g_free(dirpath);
227*3e36aba7SGreg Kurz     g_free(filename);
228*3e36aba7SGreg Kurz     return ret;
229*3e36aba7SGreg Kurz }
230*3e36aba7SGreg Kurz 
23156fc494bSGreg Kurz int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
23256fc494bSGreg Kurz                 size_t size, int flags)
23356fc494bSGreg Kurz {
234*3e36aba7SGreg Kurz     return local_setxattr_nofollow(ctx, path, name, value, size, flags);
23556fc494bSGreg Kurz }
23656fc494bSGreg Kurz 
23756fc494bSGreg Kurz int pt_removexattr(FsContext *ctx, const char *path, const char *name)
23856fc494bSGreg Kurz {
23956fc494bSGreg Kurz     char *buffer;
24056fc494bSGreg Kurz     int ret;
24156fc494bSGreg Kurz 
24256fc494bSGreg Kurz     buffer = rpath(ctx, path);
24356fc494bSGreg Kurz     ret = lremovexattr(path, name);
24456fc494bSGreg Kurz     g_free(buffer);
24556fc494bSGreg Kurz     return ret;
24656fc494bSGreg Kurz }
24756fc494bSGreg Kurz 
24856fc494bSGreg Kurz ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
24956fc494bSGreg Kurz                         void *value, size_t size)
25056fc494bSGreg Kurz {
25156fc494bSGreg Kurz     errno = ENOTSUP;
25256fc494bSGreg Kurz     return -1;
25356fc494bSGreg Kurz }
25456fc494bSGreg Kurz 
25556fc494bSGreg Kurz int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
25656fc494bSGreg Kurz                     void *value, size_t size, int flags)
25756fc494bSGreg Kurz {
25856fc494bSGreg Kurz     errno = ENOTSUP;
25956fc494bSGreg Kurz     return -1;
26056fc494bSGreg Kurz }
26156fc494bSGreg Kurz 
26256fc494bSGreg Kurz ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
26356fc494bSGreg Kurz                          void *value, size_t size)
26456fc494bSGreg Kurz {
26556fc494bSGreg Kurz     return 0;
26656fc494bSGreg Kurz }
26756fc494bSGreg Kurz 
26856fc494bSGreg Kurz int notsup_removexattr(FsContext *ctx, const char *path, const char *name)
26956fc494bSGreg Kurz {
27056fc494bSGreg Kurz     errno = ENOTSUP;
27156fc494bSGreg Kurz     return -1;
27256fc494bSGreg Kurz }
27356fc494bSGreg Kurz 
274267ae092SWei Liu XattrOperations *mapped_xattr_ops[] = {
275267ae092SWei Liu     &mapped_user_xattr,
276267ae092SWei Liu     &mapped_pacl_xattr,
277267ae092SWei Liu     &mapped_dacl_xattr,
278267ae092SWei Liu     NULL,
279267ae092SWei Liu };
280267ae092SWei Liu 
281267ae092SWei Liu XattrOperations *passthrough_xattr_ops[] = {
282267ae092SWei Liu     &passthrough_user_xattr,
283267ae092SWei Liu     &passthrough_acl_xattr,
284267ae092SWei Liu     NULL,
285267ae092SWei Liu };
286267ae092SWei Liu 
287267ae092SWei Liu /* for .user none model should be same as passthrough */
288267ae092SWei Liu XattrOperations *none_xattr_ops[] = {
289267ae092SWei Liu     &passthrough_user_xattr,
290267ae092SWei Liu     &none_acl_xattr,
291267ae092SWei Liu     NULL,
292267ae092SWei Liu };
293