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
14*6f569084SChristian Schoenebeck /*
15*6f569084SChristian Schoenebeck * Not so fast! You might want to read the 9p developer docs first:
16*6f569084SChristian Schoenebeck * https://wiki.qemu.org/Documentation/9p
17*6f569084SChristian Schoenebeck */
18*6f569084SChristian Schoenebeck
19fbc04127SPeter Maydell #include "qemu/osdep.h"
20ebe74f8bSWei Liu #include "9p.h"
21267ae092SWei Liu #include "fsdev/file-op-9p.h"
22267ae092SWei Liu #include "9p-xattr.h"
2356ad3e54SGreg Kurz #include "9p-util.h"
2456ad3e54SGreg Kurz #include "9p-local.h"
25267ae092SWei Liu
26267ae092SWei Liu
get_xattr_operations(XattrOperations ** h,const char * name)27267ae092SWei Liu static XattrOperations *get_xattr_operations(XattrOperations **h,
28267ae092SWei Liu const char *name)
29267ae092SWei Liu {
30267ae092SWei Liu XattrOperations *xops;
31267ae092SWei Liu for (xops = *(h)++; xops != NULL; xops = *(h)++) {
32267ae092SWei Liu if (!strncmp(name, xops->name, strlen(xops->name))) {
33267ae092SWei Liu return xops;
34267ae092SWei Liu }
35267ae092SWei Liu }
36267ae092SWei Liu return NULL;
37267ae092SWei Liu }
38267ae092SWei Liu
v9fs_get_xattr(FsContext * ctx,const char * path,const char * name,void * value,size_t size)39267ae092SWei Liu ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
40267ae092SWei Liu const char *name, void *value, size_t size)
41267ae092SWei Liu {
42267ae092SWei Liu XattrOperations *xops = get_xattr_operations(ctx->xops, name);
43267ae092SWei Liu if (xops) {
44267ae092SWei Liu return xops->getxattr(ctx, path, name, value, size);
45267ae092SWei Liu }
46267ae092SWei Liu errno = EOPNOTSUPP;
47267ae092SWei Liu return -1;
48267ae092SWei Liu }
49267ae092SWei Liu
pt_listxattr(FsContext * ctx,const char * path,char * name,void * value,size_t size)50267ae092SWei Liu ssize_t pt_listxattr(FsContext *ctx, const char *path,
51267ae092SWei Liu char *name, void *value, size_t size)
52267ae092SWei Liu {
53267ae092SWei Liu int name_size = strlen(name) + 1;
54267ae092SWei Liu if (!value) {
55267ae092SWei Liu return name_size;
56267ae092SWei Liu }
57267ae092SWei Liu
58267ae092SWei Liu if (size < name_size) {
59267ae092SWei Liu errno = ERANGE;
60267ae092SWei Liu return -1;
61267ae092SWei Liu }
62267ae092SWei Liu
63267ae092SWei Liu /* no need for strncpy: name_size is strlen(name)+1 */
64267ae092SWei Liu memcpy(value, name, name_size);
65267ae092SWei Liu return name_size;
66267ae092SWei Liu }
67267ae092SWei Liu
68267ae092SWei Liu /*
69267ae092SWei Liu * Get the list and pass to each layer to find out whether
70267ae092SWei Liu * to send the data or not
71267ae092SWei Liu */
v9fs_list_xattr(FsContext * ctx,const char * path,void * value,size_t vsize)72267ae092SWei Liu ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
73267ae092SWei Liu void *value, size_t vsize)
74267ae092SWei Liu {
75267ae092SWei Liu ssize_t size = 0;
76267ae092SWei Liu void *ovalue = value;
77267ae092SWei Liu XattrOperations *xops;
78267ae092SWei Liu char *orig_value, *orig_value_start;
79267ae092SWei Liu ssize_t xattr_len, parsed_len = 0, attr_len;
805507904eSGreg Kurz char *dirpath, *name;
815507904eSGreg Kurz int dirfd;
82267ae092SWei Liu
83267ae092SWei Liu /* Get the actual len */
845507904eSGreg Kurz dirpath = g_path_get_dirname(path);
855507904eSGreg Kurz dirfd = local_opendir_nofollow(ctx, dirpath);
865507904eSGreg Kurz g_free(dirpath);
875507904eSGreg Kurz if (dirfd == -1) {
885507904eSGreg Kurz return -1;
895507904eSGreg Kurz }
905507904eSGreg Kurz
915507904eSGreg Kurz name = g_path_get_basename(path);
925507904eSGreg Kurz xattr_len = flistxattrat_nofollow(dirfd, name, value, 0);
93267ae092SWei Liu if (xattr_len <= 0) {
945507904eSGreg Kurz g_free(name);
955507904eSGreg Kurz close_preserve_errno(dirfd);
96267ae092SWei Liu return xattr_len;
97267ae092SWei Liu }
98267ae092SWei Liu
99267ae092SWei Liu /* Now fetch the xattr and find the actual size */
100267ae092SWei Liu orig_value = g_malloc(xattr_len);
1015507904eSGreg Kurz xattr_len = flistxattrat_nofollow(dirfd, name, orig_value, xattr_len);
1025507904eSGreg Kurz g_free(name);
1035507904eSGreg Kurz close_preserve_errno(dirfd);
1045507904eSGreg Kurz if (xattr_len < 0) {
1054ffcdef4SLi Qiang g_free(orig_value);
1065507904eSGreg Kurz return -1;
1075507904eSGreg Kurz }
108267ae092SWei Liu
109267ae092SWei Liu /* store the orig pointer */
110267ae092SWei Liu orig_value_start = orig_value;
111267ae092SWei Liu while (xattr_len > parsed_len) {
112267ae092SWei Liu xops = get_xattr_operations(ctx->xops, orig_value);
113267ae092SWei Liu if (!xops) {
114267ae092SWei Liu goto next_entry;
115267ae092SWei Liu }
116267ae092SWei Liu
117267ae092SWei Liu if (!value) {
118267ae092SWei Liu size += xops->listxattr(ctx, path, orig_value, value, vsize);
119267ae092SWei Liu } else {
120267ae092SWei Liu size = xops->listxattr(ctx, path, orig_value, value, vsize);
121267ae092SWei Liu if (size < 0) {
122267ae092SWei Liu goto err_out;
123267ae092SWei Liu }
124267ae092SWei Liu value += size;
125267ae092SWei Liu vsize -= size;
126267ae092SWei Liu }
127267ae092SWei Liu next_entry:
128267ae092SWei Liu /* Got the next entry */
129267ae092SWei Liu attr_len = strlen(orig_value) + 1;
130267ae092SWei Liu parsed_len += attr_len;
131267ae092SWei Liu orig_value += attr_len;
132267ae092SWei Liu }
133267ae092SWei Liu if (value) {
134267ae092SWei Liu size = value - ovalue;
135267ae092SWei Liu }
136267ae092SWei Liu
137267ae092SWei Liu err_out:
138267ae092SWei Liu g_free(orig_value_start);
139267ae092SWei Liu return size;
140267ae092SWei Liu }
141267ae092SWei Liu
v9fs_set_xattr(FsContext * ctx,const char * path,const char * name,void * value,size_t size,int flags)142267ae092SWei Liu int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
143267ae092SWei Liu void *value, size_t size, int flags)
144267ae092SWei Liu {
145267ae092SWei Liu XattrOperations *xops = get_xattr_operations(ctx->xops, name);
146267ae092SWei Liu if (xops) {
147267ae092SWei Liu return xops->setxattr(ctx, path, name, value, size, flags);
148267ae092SWei Liu }
149267ae092SWei Liu errno = EOPNOTSUPP;
150267ae092SWei Liu return -1;
151267ae092SWei Liu
152267ae092SWei Liu }
153267ae092SWei Liu
v9fs_remove_xattr(FsContext * ctx,const char * path,const char * name)154267ae092SWei Liu int v9fs_remove_xattr(FsContext *ctx,
155267ae092SWei Liu const char *path, const char *name)
156267ae092SWei Liu {
157267ae092SWei Liu XattrOperations *xops = get_xattr_operations(ctx->xops, name);
158267ae092SWei Liu if (xops) {
159267ae092SWei Liu return xops->removexattr(ctx, path, name);
160267ae092SWei Liu }
161267ae092SWei Liu errno = EOPNOTSUPP;
162267ae092SWei Liu return -1;
163267ae092SWei Liu
164267ae092SWei Liu }
165267ae092SWei Liu
local_getxattr_nofollow(FsContext * ctx,const char * path,const char * name,void * value,size_t size)16656ad3e54SGreg Kurz ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
16756ad3e54SGreg Kurz const char *name, void *value, size_t size)
16856ad3e54SGreg Kurz {
16956ad3e54SGreg Kurz char *dirpath = g_path_get_dirname(path);
17056ad3e54SGreg Kurz char *filename = g_path_get_basename(path);
17156ad3e54SGreg Kurz int dirfd;
17256ad3e54SGreg Kurz ssize_t ret = -1;
17356ad3e54SGreg Kurz
17456ad3e54SGreg Kurz dirfd = local_opendir_nofollow(ctx, dirpath);
17556ad3e54SGreg Kurz if (dirfd == -1) {
17656ad3e54SGreg Kurz goto out;
17756ad3e54SGreg Kurz }
17856ad3e54SGreg Kurz
17956ad3e54SGreg Kurz ret = fgetxattrat_nofollow(dirfd, filename, name, value, size);
18056ad3e54SGreg Kurz close_preserve_errno(dirfd);
18156ad3e54SGreg Kurz out:
18256ad3e54SGreg Kurz g_free(dirpath);
18356ad3e54SGreg Kurz g_free(filename);
18456ad3e54SGreg Kurz return ret;
18556ad3e54SGreg Kurz }
18656ad3e54SGreg Kurz
pt_getxattr(FsContext * ctx,const char * path,const char * name,void * value,size_t size)18756fc494bSGreg Kurz ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
18856fc494bSGreg Kurz void *value, size_t size)
18956fc494bSGreg Kurz {
19056ad3e54SGreg Kurz return local_getxattr_nofollow(ctx, path, name, value, size);
19156fc494bSGreg Kurz }
19256fc494bSGreg Kurz
local_setxattr_nofollow(FsContext * ctx,const char * path,const char * name,void * value,size_t size,int flags)1933e36aba7SGreg Kurz ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
1943e36aba7SGreg Kurz const char *name, void *value, size_t size,
1953e36aba7SGreg Kurz int flags)
1963e36aba7SGreg Kurz {
1973e36aba7SGreg Kurz char *dirpath = g_path_get_dirname(path);
1983e36aba7SGreg Kurz char *filename = g_path_get_basename(path);
1993e36aba7SGreg Kurz int dirfd;
2003e36aba7SGreg Kurz ssize_t ret = -1;
2013e36aba7SGreg Kurz
2023e36aba7SGreg Kurz dirfd = local_opendir_nofollow(ctx, dirpath);
2033e36aba7SGreg Kurz if (dirfd == -1) {
2043e36aba7SGreg Kurz goto out;
2053e36aba7SGreg Kurz }
2063e36aba7SGreg Kurz
2073e36aba7SGreg Kurz ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags);
2083e36aba7SGreg Kurz close_preserve_errno(dirfd);
2093e36aba7SGreg Kurz out:
2103e36aba7SGreg Kurz g_free(dirpath);
2113e36aba7SGreg Kurz g_free(filename);
2123e36aba7SGreg Kurz return ret;
2133e36aba7SGreg Kurz }
2143e36aba7SGreg Kurz
pt_setxattr(FsContext * ctx,const char * path,const char * name,void * value,size_t size,int flags)21556fc494bSGreg Kurz int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
21656fc494bSGreg Kurz size_t size, int flags)
21756fc494bSGreg Kurz {
2183e36aba7SGreg Kurz return local_setxattr_nofollow(ctx, path, name, value, size, flags);
21956fc494bSGreg Kurz }
22056fc494bSGreg Kurz
local_removexattr_nofollow(FsContext * ctx,const char * path,const char * name)22172f0d0bfSGreg Kurz ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
22272f0d0bfSGreg Kurz const char *name)
22372f0d0bfSGreg Kurz {
22472f0d0bfSGreg Kurz char *dirpath = g_path_get_dirname(path);
22572f0d0bfSGreg Kurz char *filename = g_path_get_basename(path);
22672f0d0bfSGreg Kurz int dirfd;
22772f0d0bfSGreg Kurz ssize_t ret = -1;
22872f0d0bfSGreg Kurz
22972f0d0bfSGreg Kurz dirfd = local_opendir_nofollow(ctx, dirpath);
23072f0d0bfSGreg Kurz if (dirfd == -1) {
23172f0d0bfSGreg Kurz goto out;
23272f0d0bfSGreg Kurz }
23372f0d0bfSGreg Kurz
23472f0d0bfSGreg Kurz ret = fremovexattrat_nofollow(dirfd, filename, name);
23572f0d0bfSGreg Kurz close_preserve_errno(dirfd);
23672f0d0bfSGreg Kurz out:
23772f0d0bfSGreg Kurz g_free(dirpath);
23872f0d0bfSGreg Kurz g_free(filename);
23972f0d0bfSGreg Kurz return ret;
24072f0d0bfSGreg Kurz }
24172f0d0bfSGreg Kurz
pt_removexattr(FsContext * ctx,const char * path,const char * name)24272f0d0bfSGreg Kurz int pt_removexattr(FsContext *ctx, const char *path, const char *name)
24372f0d0bfSGreg Kurz {
24472f0d0bfSGreg Kurz return local_removexattr_nofollow(ctx, path, name);
24572f0d0bfSGreg Kurz }
24672f0d0bfSGreg Kurz
notsup_getxattr(FsContext * ctx,const char * path,const char * name,void * value,size_t size)24756fc494bSGreg Kurz ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
24856fc494bSGreg Kurz void *value, size_t size)
24956fc494bSGreg Kurz {
25056fc494bSGreg Kurz errno = ENOTSUP;
25156fc494bSGreg Kurz return -1;
25256fc494bSGreg Kurz }
25356fc494bSGreg Kurz
notsup_setxattr(FsContext * ctx,const char * path,const char * name,void * value,size_t size,int flags)25456fc494bSGreg Kurz int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
25556fc494bSGreg Kurz void *value, size_t size, int flags)
25656fc494bSGreg Kurz {
25756fc494bSGreg Kurz errno = ENOTSUP;
25856fc494bSGreg Kurz return -1;
25956fc494bSGreg Kurz }
26056fc494bSGreg Kurz
notsup_listxattr(FsContext * ctx,const char * path,char * name,void * value,size_t size)26156fc494bSGreg Kurz ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
26256fc494bSGreg Kurz void *value, size_t size)
26356fc494bSGreg Kurz {
26456fc494bSGreg Kurz return 0;
26556fc494bSGreg Kurz }
26656fc494bSGreg Kurz
notsup_removexattr(FsContext * ctx,const char * path,const char * name)26756fc494bSGreg Kurz int notsup_removexattr(FsContext *ctx, const char *path, const char *name)
26856fc494bSGreg Kurz {
26956fc494bSGreg Kurz errno = ENOTSUP;
27056fc494bSGreg Kurz return -1;
27156fc494bSGreg Kurz }
27256fc494bSGreg Kurz
273267ae092SWei Liu XattrOperations *mapped_xattr_ops[] = {
274267ae092SWei Liu &mapped_user_xattr,
275267ae092SWei Liu &mapped_pacl_xattr,
276267ae092SWei Liu &mapped_dacl_xattr,
277267ae092SWei Liu NULL,
278267ae092SWei Liu };
279267ae092SWei Liu
280267ae092SWei Liu XattrOperations *passthrough_xattr_ops[] = {
281267ae092SWei Liu &passthrough_user_xattr,
282267ae092SWei Liu &passthrough_acl_xattr,
283267ae092SWei Liu NULL,
284267ae092SWei Liu };
285267ae092SWei Liu
286267ae092SWei Liu /* for .user none model should be same as passthrough */
287267ae092SWei Liu XattrOperations *none_xattr_ops[] = {
288267ae092SWei Liu &passthrough_user_xattr,
289267ae092SWei Liu &none_acl_xattr,
290267ae092SWei Liu NULL,
291267ae092SWei Liu };
292