]> git.proxmox.com Git - mirror_qemu.git/blame - hw/9pfs/9p-local.c
9pfs: local: keep a file descriptor on the shared folder
[mirror_qemu.git] / hw / 9pfs / 9p-local.c
CommitLineData
9f107513 1/*
f00d4f59 2 * 9p Posix callback
9f107513
AL
3 *
4 * Copyright IBM, Corp. 2010
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.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 */
873c3213 13
fbc04127 14#include "qemu/osdep.h"
ebe74f8b 15#include "9p.h"
267ae092 16#include "9p-xattr.h"
0e35a378 17#include "9p-util.h"
69b15212 18#include "fsdev/qemu-fsdev.h" /* local_ops */
c494dd6f 19#include <arpa/inet.h>
131dcb25
AL
20#include <pwd.h>
21#include <grp.h>
c494dd6f
AL
22#include <sys/socket.h>
23#include <sys/un.h>
1de7afc9 24#include "qemu/xattr.h"
f348b6d1 25#include "qemu/cutils.h"
63325b18 26#include "qemu/error-report.h"
2c30dd74 27#include <libgen.h>
e06a765e
HPB
28#include <linux/fs.h>
29#ifdef CONFIG_LINUX_MAGIC_H
30#include <linux/magic.h>
31#endif
32#include <sys/ioctl.h>
33
34#ifndef XFS_SUPER_MAGIC
35#define XFS_SUPER_MAGIC 0x58465342
36#endif
37#ifndef EXT2_SUPER_MAGIC
38#define EXT2_SUPER_MAGIC 0xEF53
39#endif
40#ifndef REISERFS_SUPER_MAGIC
41#define REISERFS_SUPER_MAGIC 0x52654973
42#endif
43#ifndef BTRFS_SUPER_MAGIC
44#define BTRFS_SUPER_MAGIC 0x9123683E
45#endif
131dcb25 46
0e35a378
GK
47typedef struct {
48 int mountfd;
49} LocalData;
50
2c30dd74
AK
51#define VIRTFS_META_DIR ".virtfs_metadata"
52
4fa4ce71 53static char *local_mapped_attr_path(FsContext *ctx, const char *path)
2c30dd74 54{
1b6f85e2
MT
55 int dirlen;
56 const char *name = strrchr(path, '/');
57 if (name) {
58 dirlen = name - path;
59 ++name;
60 } else {
61 name = path;
62 dirlen = 0;
63 }
64 return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
65 dirlen, path, VIRTFS_META_DIR, name);
2c30dd74
AK
66}
67
0ceb092e
AK
68static FILE *local_fopen(const char *path, const char *mode)
69{
70 int fd, o_mode = 0;
71 FILE *fp;
72 int flags = O_NOFOLLOW;
73 /*
74 * only supports two modes
75 */
76 if (mode[0] == 'r') {
77 flags |= O_RDONLY;
78 } else if (mode[0] == 'w') {
79 flags |= O_WRONLY | O_TRUNC | O_CREAT;
80 o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
81 } else {
82 return NULL;
83 }
84 fd = open(path, flags, o_mode);
85 if (fd == -1) {
86 return NULL;
87 }
88 fp = fdopen(fd, mode);
89 if (!fp) {
90 close(fd);
91 }
92 return fp;
93}
94
2c30dd74
AK
95#define ATTR_MAX 100
96static void local_mapped_file_attr(FsContext *ctx, const char *path,
97 struct stat *stbuf)
98{
99 FILE *fp;
100 char buf[ATTR_MAX];
4fa4ce71 101 char *attr_path;
2c30dd74 102
4fa4ce71 103 attr_path = local_mapped_attr_path(ctx, path);
0ceb092e 104 fp = local_fopen(attr_path, "r");
4fa4ce71 105 g_free(attr_path);
2c30dd74
AK
106 if (!fp) {
107 return;
108 }
109 memset(buf, 0, ATTR_MAX);
110 while (fgets(buf, ATTR_MAX, fp)) {
111 if (!strncmp(buf, "virtfs.uid", 10)) {
112 stbuf->st_uid = atoi(buf+11);
113 } else if (!strncmp(buf, "virtfs.gid", 10)) {
114 stbuf->st_gid = atoi(buf+11);
115 } else if (!strncmp(buf, "virtfs.mode", 11)) {
116 stbuf->st_mode = atoi(buf+12);
117 } else if (!strncmp(buf, "virtfs.rdev", 11)) {
118 stbuf->st_rdev = atoi(buf+12);
119 }
120 memset(buf, 0, ATTR_MAX);
121 }
122 fclose(fp);
123}
124
2289be19 125static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
131dcb25 126{
1237ad76 127 int err;
4fa4ce71 128 char *buffer;
2289be19
AK
129 char *path = fs_path->data;
130
4fa4ce71
CG
131 buffer = rpath(fs_ctx, path);
132 err = lstat(buffer, stbuf);
1237ad76 133 if (err) {
4fa4ce71 134 goto err_out;
1237ad76 135 }
b97400ca 136 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1237ad76
VJ
137 /* Actual credentials are part of extended attrs */
138 uid_t tmp_uid;
139 gid_t tmp_gid;
140 mode_t tmp_mode;
141 dev_t tmp_dev;
4fa4ce71 142 if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
f8ad4a89 143 stbuf->st_uid = le32_to_cpu(tmp_uid);
1237ad76 144 }
4fa4ce71 145 if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
f8ad4a89 146 stbuf->st_gid = le32_to_cpu(tmp_gid);
1237ad76 147 }
4fa4ce71 148 if (getxattr(buffer, "user.virtfs.mode",
faa44e3d 149 &tmp_mode, sizeof(mode_t)) > 0) {
f8ad4a89 150 stbuf->st_mode = le32_to_cpu(tmp_mode);
1237ad76 151 }
4fa4ce71 152 if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
f8ad4a89 153 stbuf->st_rdev = le64_to_cpu(tmp_dev);
1237ad76 154 }
2c30dd74
AK
155 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
156 local_mapped_file_attr(fs_ctx, path, stbuf);
157 }
4fa4ce71
CG
158
159err_out:
160 g_free(buffer);
2c30dd74
AK
161 return err;
162}
163
164static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
165{
166 int err;
4fa4ce71 167 char *attr_dir;
d3f8e138 168 char *tmp_path = g_strdup(path);
2c30dd74 169
4fa4ce71 170 attr_dir = g_strdup_printf("%s/%s/%s",
2c30dd74
AK
171 ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
172
173 err = mkdir(attr_dir, 0700);
174 if (err < 0 && errno == EEXIST) {
175 err = 0;
1237ad76 176 }
4fa4ce71 177 g_free(attr_dir);
d3f8e138 178 g_free(tmp_path);
1237ad76 179 return err;
131dcb25
AL
180}
181
2c30dd74
AK
182static int local_set_mapped_file_attr(FsContext *ctx,
183 const char *path, FsCred *credp)
184{
185 FILE *fp;
186 int ret = 0;
187 char buf[ATTR_MAX];
4fa4ce71 188 char *attr_path;
2c30dd74
AK
189 int uid = -1, gid = -1, mode = -1, rdev = -1;
190
4fa4ce71
CG
191 attr_path = local_mapped_attr_path(ctx, path);
192 fp = local_fopen(attr_path, "r");
2c30dd74
AK
193 if (!fp) {
194 goto create_map_file;
195 }
196 memset(buf, 0, ATTR_MAX);
197 while (fgets(buf, ATTR_MAX, fp)) {
198 if (!strncmp(buf, "virtfs.uid", 10)) {
199 uid = atoi(buf+11);
200 } else if (!strncmp(buf, "virtfs.gid", 10)) {
201 gid = atoi(buf+11);
202 } else if (!strncmp(buf, "virtfs.mode", 11)) {
203 mode = atoi(buf+12);
204 } else if (!strncmp(buf, "virtfs.rdev", 11)) {
205 rdev = atoi(buf+12);
206 }
207 memset(buf, 0, ATTR_MAX);
208 }
209 fclose(fp);
210 goto update_map_file;
211
212create_map_file:
213 ret = local_create_mapped_attr_dir(ctx, path);
214 if (ret < 0) {
215 goto err_out;
216 }
217
218update_map_file:
0ceb092e 219 fp = local_fopen(attr_path, "w");
2c30dd74
AK
220 if (!fp) {
221 ret = -1;
222 goto err_out;
223 }
224
225 if (credp->fc_uid != -1) {
226 uid = credp->fc_uid;
227 }
228 if (credp->fc_gid != -1) {
229 gid = credp->fc_gid;
230 }
231 if (credp->fc_mode != -1) {
232 mode = credp->fc_mode;
233 }
234 if (credp->fc_rdev != -1) {
235 rdev = credp->fc_rdev;
236 }
237
238
239 if (uid != -1) {
240 fprintf(fp, "virtfs.uid=%d\n", uid);
241 }
242 if (gid != -1) {
243 fprintf(fp, "virtfs.gid=%d\n", gid);
244 }
245 if (mode != -1) {
246 fprintf(fp, "virtfs.mode=%d\n", mode);
247 }
248 if (rdev != -1) {
249 fprintf(fp, "virtfs.rdev=%d\n", rdev);
250 }
251 fclose(fp);
252
253err_out:
4fa4ce71 254 g_free(attr_path);
2c30dd74
AK
255 return ret;
256}
257
758e8e38 258static int local_set_xattr(const char *path, FsCred *credp)
131dcb25 259{
758e8e38 260 int err;
2289be19 261
758e8e38 262 if (credp->fc_uid != -1) {
f8ad4a89
AK
263 uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
264 err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
758e8e38
VJ
265 if (err) {
266 return err;
267 }
131dcb25 268 }
758e8e38 269 if (credp->fc_gid != -1) {
f8ad4a89
AK
270 uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
271 err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
758e8e38
VJ
272 if (err) {
273 return err;
274 }
131dcb25 275 }
758e8e38 276 if (credp->fc_mode != -1) {
f8ad4a89
AK
277 uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
278 err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
758e8e38
VJ
279 if (err) {
280 return err;
281 }
131dcb25 282 }
758e8e38 283 if (credp->fc_rdev != -1) {
f8ad4a89
AK
284 uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
285 err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
758e8e38
VJ
286 if (err) {
287 return err;
288 }
131dcb25 289 }
131dcb25
AL
290 return 0;
291}
292
4750a96f 293static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
2289be19 294 FsCred *credp)
4750a96f 295{
4fa4ce71 296 char *buffer;
2289be19 297
4fa4ce71
CG
298 buffer = rpath(fs_ctx, path);
299 if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
12848bfc
AK
300 /*
301 * If we fail to change ownership and if we are
302 * using security model none. Ignore the error
303 */
b97400ca 304 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
4fa4ce71 305 goto err;
12848bfc 306 }
4750a96f 307 }
2d40564a 308
4fa4ce71
CG
309 if (chmod(buffer, credp->fc_mode & 07777) < 0) {
310 goto err;
2d40564a 311 }
4fa4ce71
CG
312
313 g_free(buffer);
4750a96f 314 return 0;
4fa4ce71
CG
315err:
316 g_free(buffer);
317 return -1;
4750a96f
VJ
318}
319
2289be19
AK
320static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
321 char *buf, size_t bufsz)
131dcb25 322{
879c2813 323 ssize_t tsize = -1;
4fa4ce71 324 char *buffer;
2289be19
AK
325 char *path = fs_path->data;
326
2c30dd74
AK
327 if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
328 (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
879c2813 329 int fd;
4fa4ce71
CG
330 buffer = rpath(fs_ctx, path);
331 fd = open(buffer, O_RDONLY | O_NOFOLLOW);
332 g_free(buffer);
879c2813
VJ
333 if (fd == -1) {
334 return -1;
335 }
336 do {
337 tsize = read(fd, (void *)buf, bufsz);
338 } while (tsize == -1 && errno == EINTR);
339 close(fd);
b97400ca
AK
340 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
341 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
342 buffer = rpath(fs_ctx, path);
343 tsize = readlink(buffer, buf, bufsz);
344 g_free(buffer);
879c2813
VJ
345 }
346 return tsize;
131dcb25
AL
347}
348
cc720ddb 349static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
131dcb25 350{
cc720ddb 351 return close(fs->fd);
131dcb25
AL
352}
353
cc720ddb 354static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
131dcb25 355{
f314ea4e 356 return closedir(fs->dir.stream);
131dcb25 357}
9f107513 358
cc720ddb
AK
359static int local_open(FsContext *ctx, V9fsPath *fs_path,
360 int flags, V9fsFidOpenState *fs)
a6568fe2 361{
4fa4ce71 362 char *buffer;
2289be19 363 char *path = fs_path->data;
21328e1e 364 int fd;
2289be19 365
4fa4ce71 366 buffer = rpath(ctx, path);
21328e1e 367 fd = open(buffer, flags | O_NOFOLLOW);
4fa4ce71 368 g_free(buffer);
21328e1e
GK
369 if (fd == -1) {
370 return -1;
371 }
372 fs->fd = fd;
cc720ddb 373 return fs->fd;
a6568fe2
AL
374}
375
cc720ddb
AK
376static int local_opendir(FsContext *ctx,
377 V9fsPath *fs_path, V9fsFidOpenState *fs)
a6568fe2 378{
4fa4ce71 379 char *buffer;
2289be19 380 char *path = fs_path->data;
21328e1e 381 DIR *stream;
2289be19 382
4fa4ce71 383 buffer = rpath(ctx, path);
21328e1e 384 stream = opendir(buffer);
4fa4ce71 385 g_free(buffer);
21328e1e 386 if (!stream) {
cc720ddb
AK
387 return -1;
388 }
21328e1e 389 fs->dir.stream = stream;
cc720ddb 390 return 0;
a6568fe2
AL
391}
392
cc720ddb 393static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
a9231555 394{
f314ea4e 395 rewinddir(fs->dir.stream);
a9231555
AL
396}
397
cc720ddb 398static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
a9231555 399{
f314ea4e 400 return telldir(fs->dir.stream);
a9231555
AL
401}
402
635324e8 403static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
a9231555 404{
635324e8 405 struct dirent *entry;
2c30dd74
AK
406
407again:
635324e8
GK
408 entry = readdir(fs->dir.stream);
409 if (!entry) {
410 return NULL;
411 }
412
840a1bf2
BB
413 if (ctx->export_flags & V9FS_SM_MAPPED) {
414 entry->d_type = DT_UNKNOWN;
415 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
635324e8 416 if (!strcmp(entry->d_name, VIRTFS_META_DIR)) {
2c30dd74
AK
417 /* skp the meta data directory */
418 goto again;
419 }
840a1bf2 420 entry->d_type = DT_UNKNOWN;
2c30dd74 421 }
635324e8
GK
422
423 return entry;
a9231555
AL
424}
425
cc720ddb 426static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
a9231555 427{
f314ea4e 428 seekdir(fs->dir.stream, off);
a9231555
AL
429}
430
cc720ddb
AK
431static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
432 const struct iovec *iov,
56d15a53 433 int iovcnt, off_t offset)
a9231555 434{
56d15a53 435#ifdef CONFIG_PREADV
cc720ddb 436 return preadv(fs->fd, iov, iovcnt, offset);
56d15a53 437#else
cc720ddb 438 int err = lseek(fs->fd, offset, SEEK_SET);
56d15a53
SG
439 if (err == -1) {
440 return err;
441 } else {
cc720ddb 442 return readv(fs->fd, iov, iovcnt);
56d15a53
SG
443 }
444#endif
a9231555
AL
445}
446
cc720ddb
AK
447static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
448 const struct iovec *iov,
2289be19 449 int iovcnt, off_t offset)
8449360c 450{
6fe76acc 451 ssize_t ret;
56d15a53 452#ifdef CONFIG_PREADV
cc720ddb 453 ret = pwritev(fs->fd, iov, iovcnt, offset);
56d15a53 454#else
cc720ddb 455 int err = lseek(fs->fd, offset, SEEK_SET);
56d15a53
SG
456 if (err == -1) {
457 return err;
458 } else {
cc720ddb 459 ret = writev(fs->fd, iov, iovcnt);
56d15a53
SG
460 }
461#endif
d3ab98e6
AK
462#ifdef CONFIG_SYNC_FILE_RANGE
463 if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
464 /*
465 * Initiate a writeback. This is not a data integrity sync.
466 * We want to ensure that we don't leave dirty pages in the cache
467 * after write when writeout=immediate is sepcified.
468 */
cc720ddb 469 sync_file_range(fs->fd, offset, ret,
d3ab98e6
AK
470 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
471 }
472#endif
473 return ret;
8449360c
AL
474}
475
2289be19 476static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
c494dd6f 477{
4fa4ce71
CG
478 char *buffer;
479 int ret = -1;
2289be19
AK
480 char *path = fs_path->data;
481
b97400ca 482 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
4fa4ce71
CG
483 buffer = rpath(fs_ctx, path);
484 ret = local_set_xattr(buffer, credp);
485 g_free(buffer);
2c30dd74
AK
486 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
487 return local_set_mapped_file_attr(fs_ctx, path, credp);
b97400ca
AK
488 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
489 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
490 buffer = rpath(fs_ctx, path);
491 ret = chmod(buffer, credp->fc_mode);
492 g_free(buffer);
e95ead32 493 }
4fa4ce71 494 return ret;
c494dd6f
AL
495}
496
2289be19
AK
497static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
498 const char *name, FsCred *credp)
c494dd6f 499{
2289be19 500 char *path;
1c293312
VJ
501 int err = -1;
502 int serrno = 0;
2289be19 503 V9fsString fullname;
4ed7b2c3 504 char *buffer = NULL;
1c293312 505
2289be19
AK
506 v9fs_string_init(&fullname);
507 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
508 path = fullname.data;
509
1c293312 510 /* Determine the security model */
b97400ca 511 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
4fa4ce71
CG
512 buffer = rpath(fs_ctx, path);
513 err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
1c293312 514 if (err == -1) {
2289be19 515 goto out;
1c293312 516 }
4fa4ce71 517 err = local_set_xattr(buffer, credp);
1c293312
VJ
518 if (err == -1) {
519 serrno = errno;
520 goto err_end;
521 }
2c30dd74
AK
522 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
523
4fa4ce71
CG
524 buffer = rpath(fs_ctx, path);
525 err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
2c30dd74
AK
526 if (err == -1) {
527 goto out;
528 }
529 err = local_set_mapped_file_attr(fs_ctx, path, credp);
530 if (err == -1) {
531 serrno = errno;
532 goto err_end;
533 }
b97400ca
AK
534 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
535 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
536 buffer = rpath(fs_ctx, path);
537 err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
1c293312 538 if (err == -1) {
2289be19 539 goto out;
1c293312
VJ
540 }
541 err = local_post_create_passthrough(fs_ctx, path, credp);
542 if (err == -1) {
543 serrno = errno;
544 goto err_end;
545 }
546 }
2289be19 547 goto out;
1c293312
VJ
548
549err_end:
4fa4ce71 550 remove(buffer);
1c293312 551 errno = serrno;
2289be19 552out:
4ed7b2c3 553 g_free(buffer);
2289be19 554 v9fs_string_free(&fullname);
1c293312 555 return err;
c494dd6f
AL
556}
557
2289be19
AK
558static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
559 const char *name, FsCred *credp)
c494dd6f 560{
2289be19 561 char *path;
00ec5c37
VJ
562 int err = -1;
563 int serrno = 0;
2289be19 564 V9fsString fullname;
4ed7b2c3 565 char *buffer = NULL;
00ec5c37 566
2289be19
AK
567 v9fs_string_init(&fullname);
568 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
569 path = fullname.data;
570
00ec5c37 571 /* Determine the security model */
b97400ca 572 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
4fa4ce71
CG
573 buffer = rpath(fs_ctx, path);
574 err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
00ec5c37 575 if (err == -1) {
2289be19 576 goto out;
00ec5c37
VJ
577 }
578 credp->fc_mode = credp->fc_mode|S_IFDIR;
4fa4ce71 579 err = local_set_xattr(buffer, credp);
00ec5c37
VJ
580 if (err == -1) {
581 serrno = errno;
582 goto err_end;
583 }
2c30dd74 584 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
4fa4ce71
CG
585 buffer = rpath(fs_ctx, path);
586 err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
2c30dd74
AK
587 if (err == -1) {
588 goto out;
589 }
590 credp->fc_mode = credp->fc_mode|S_IFDIR;
591 err = local_set_mapped_file_attr(fs_ctx, path, credp);
592 if (err == -1) {
593 serrno = errno;
594 goto err_end;
595 }
b97400ca
AK
596 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
597 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
598 buffer = rpath(fs_ctx, path);
599 err = mkdir(buffer, credp->fc_mode);
00ec5c37 600 if (err == -1) {
2289be19 601 goto out;
00ec5c37
VJ
602 }
603 err = local_post_create_passthrough(fs_ctx, path, credp);
604 if (err == -1) {
605 serrno = errno;
606 goto err_end;
607 }
608 }
2289be19 609 goto out;
00ec5c37
VJ
610
611err_end:
4fa4ce71 612 remove(buffer);
00ec5c37 613 errno = serrno;
2289be19 614out:
4ed7b2c3 615 g_free(buffer);
2289be19 616 v9fs_string_free(&fullname);
00ec5c37 617 return err;
c494dd6f
AL
618}
619
8b888272 620static int local_fstat(FsContext *fs_ctx, int fid_type,
cc720ddb 621 V9fsFidOpenState *fs, struct stat *stbuf)
c494dd6f 622{
8b888272
AK
623 int err, fd;
624
625 if (fid_type == P9_FID_DIR) {
f314ea4e 626 fd = dirfd(fs->dir.stream);
8b888272
AK
627 } else {
628 fd = fs->fd;
629 }
630
631 err = fstat(fd, stbuf);
1237ad76
VJ
632 if (err) {
633 return err;
634 }
b97400ca 635 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1237ad76
VJ
636 /* Actual credentials are part of extended attrs */
637 uid_t tmp_uid;
638 gid_t tmp_gid;
639 mode_t tmp_mode;
640 dev_t tmp_dev;
641
f8ad4a89
AK
642 if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
643 stbuf->st_uid = le32_to_cpu(tmp_uid);
1237ad76 644 }
f8ad4a89
AK
645 if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
646 stbuf->st_gid = le32_to_cpu(tmp_gid);
1237ad76 647 }
f8ad4a89
AK
648 if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
649 stbuf->st_mode = le32_to_cpu(tmp_mode);
1237ad76 650 }
f8ad4a89
AK
651 if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
652 stbuf->st_rdev = le64_to_cpu(tmp_dev);
1237ad76 653 }
2c30dd74
AK
654 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
655 errno = EOPNOTSUPP;
656 return -1;
1237ad76
VJ
657 }
658 return err;
c494dd6f
AL
659}
660
2289be19 661static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
cc720ddb 662 int flags, FsCred *credp, V9fsFidOpenState *fs)
c494dd6f 663{
2289be19 664 char *path;
4750a96f
VJ
665 int fd = -1;
666 int err = -1;
667 int serrno = 0;
2289be19 668 V9fsString fullname;
4ed7b2c3 669 char *buffer = NULL;
4750a96f 670
0ceb092e
AK
671 /*
672 * Mark all the open to not follow symlinks
673 */
674 flags |= O_NOFOLLOW;
675
2289be19
AK
676 v9fs_string_init(&fullname);
677 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
678 path = fullname.data;
679
4750a96f 680 /* Determine the security model */
b97400ca 681 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
4fa4ce71
CG
682 buffer = rpath(fs_ctx, path);
683 fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
4750a96f 684 if (fd == -1) {
2289be19
AK
685 err = fd;
686 goto out;
4750a96f
VJ
687 }
688 credp->fc_mode = credp->fc_mode|S_IFREG;
689 /* Set cleint credentials in xattr */
4fa4ce71 690 err = local_set_xattr(buffer, credp);
4750a96f
VJ
691 if (err == -1) {
692 serrno = errno;
693 goto err_end;
694 }
2c30dd74 695 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
4fa4ce71
CG
696 buffer = rpath(fs_ctx, path);
697 fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
2c30dd74
AK
698 if (fd == -1) {
699 err = fd;
700 goto out;
701 }
702 credp->fc_mode = credp->fc_mode|S_IFREG;
703 /* Set client credentials in .virtfs_metadata directory files */
704 err = local_set_mapped_file_attr(fs_ctx, path, credp);
705 if (err == -1) {
706 serrno = errno;
707 goto err_end;
708 }
b97400ca
AK
709 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
710 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
711 buffer = rpath(fs_ctx, path);
712 fd = open(buffer, flags, credp->fc_mode);
4750a96f 713 if (fd == -1) {
2289be19
AK
714 err = fd;
715 goto out;
4750a96f
VJ
716 }
717 err = local_post_create_passthrough(fs_ctx, path, credp);
718 if (err == -1) {
719 serrno = errno;
720 goto err_end;
721 }
722 }
2289be19 723 err = fd;
cc720ddb 724 fs->fd = fd;
2289be19 725 goto out;
4750a96f
VJ
726
727err_end:
728 close(fd);
4fa4ce71 729 remove(buffer);
4750a96f 730 errno = serrno;
2289be19 731out:
4ed7b2c3 732 g_free(buffer);
2289be19 733 v9fs_string_free(&fullname);
4750a96f 734 return err;
c494dd6f
AL
735}
736
758e8e38 737
879c2813 738static int local_symlink(FsContext *fs_ctx, const char *oldpath,
2289be19 739 V9fsPath *dir_path, const char *name, FsCred *credp)
c494dd6f 740{
879c2813
VJ
741 int err = -1;
742 int serrno = 0;
2289be19
AK
743 char *newpath;
744 V9fsString fullname;
4ed7b2c3 745 char *buffer = NULL;
879c2813 746
2289be19
AK
747 v9fs_string_init(&fullname);
748 v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
749 newpath = fullname.data;
750
879c2813 751 /* Determine the security model */
b97400ca 752 if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
879c2813
VJ
753 int fd;
754 ssize_t oldpath_size, write_size;
4fa4ce71
CG
755 buffer = rpath(fs_ctx, newpath);
756 fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
879c2813 757 if (fd == -1) {
2289be19
AK
758 err = fd;
759 goto out;
879c2813
VJ
760 }
761 /* Write the oldpath (target) to the file. */
f35bde2f 762 oldpath_size = strlen(oldpath);
879c2813
VJ
763 do {
764 write_size = write(fd, (void *)oldpath, oldpath_size);
765 } while (write_size == -1 && errno == EINTR);
766
767 if (write_size != oldpath_size) {
768 serrno = errno;
769 close(fd);
770 err = -1;
771 goto err_end;
772 }
773 close(fd);
774 /* Set cleint credentials in symlink's xattr */
775 credp->fc_mode = credp->fc_mode|S_IFLNK;
4fa4ce71 776 err = local_set_xattr(buffer, credp);
879c2813
VJ
777 if (err == -1) {
778 serrno = errno;
779 goto err_end;
780 }
2c30dd74
AK
781 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
782 int fd;
783 ssize_t oldpath_size, write_size;
4fa4ce71
CG
784 buffer = rpath(fs_ctx, newpath);
785 fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
2c30dd74
AK
786 if (fd == -1) {
787 err = fd;
788 goto out;
789 }
790 /* Write the oldpath (target) to the file. */
791 oldpath_size = strlen(oldpath);
792 do {
793 write_size = write(fd, (void *)oldpath, oldpath_size);
794 } while (write_size == -1 && errno == EINTR);
795
796 if (write_size != oldpath_size) {
797 serrno = errno;
798 close(fd);
799 err = -1;
800 goto err_end;
801 }
802 close(fd);
803 /* Set cleint credentials in symlink's xattr */
804 credp->fc_mode = credp->fc_mode|S_IFLNK;
805 err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
806 if (err == -1) {
807 serrno = errno;
808 goto err_end;
809 }
b97400ca
AK
810 } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
811 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
812 buffer = rpath(fs_ctx, newpath);
813 err = symlink(oldpath, buffer);
879c2813 814 if (err) {
2289be19 815 goto out;
879c2813 816 }
4fa4ce71 817 err = lchown(buffer, credp->fc_uid, credp->fc_gid);
879c2813 818 if (err == -1) {
12848bfc
AK
819 /*
820 * If we fail to change ownership and if we are
821 * using security model none. Ignore the error
822 */
b97400ca 823 if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
12848bfc
AK
824 serrno = errno;
825 goto err_end;
826 } else
827 err = 0;
879c2813
VJ
828 }
829 }
2289be19 830 goto out;
879c2813
VJ
831
832err_end:
4fa4ce71 833 remove(buffer);
879c2813 834 errno = serrno;
2289be19 835out:
4ed7b2c3 836 g_free(buffer);
2289be19 837 v9fs_string_free(&fullname);
879c2813 838 return err;
c494dd6f
AL
839}
840
2289be19
AK
841static int local_link(FsContext *ctx, V9fsPath *oldpath,
842 V9fsPath *dirpath, const char *name)
c494dd6f 843{
2289be19
AK
844 int ret;
845 V9fsString newpath;
4fa4ce71 846 char *buffer, *buffer1;
c494dd6f 847
2289be19
AK
848 v9fs_string_init(&newpath);
849 v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
850
4fa4ce71
CG
851 buffer = rpath(ctx, oldpath->data);
852 buffer1 = rpath(ctx, newpath.data);
853 ret = link(buffer, buffer1);
854 g_free(buffer);
855 g_free(buffer1);
2c30dd74
AK
856
857 /* now link the virtfs_metadata files */
858 if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
859 /* Link the .virtfs_metadata files. Create the metada directory */
860 ret = local_create_mapped_attr_dir(ctx, newpath.data);
861 if (ret < 0) {
862 goto err_out;
863 }
4fa4ce71
CG
864 buffer = local_mapped_attr_path(ctx, oldpath->data);
865 buffer1 = local_mapped_attr_path(ctx, newpath.data);
866 ret = link(buffer, buffer1);
867 g_free(buffer);
868 g_free(buffer1);
2c30dd74
AK
869 if (ret < 0 && errno != ENOENT) {
870 goto err_out;
871 }
872 }
873err_out:
2289be19
AK
874 v9fs_string_free(&newpath);
875 return ret;
c494dd6f
AL
876}
877
2289be19 878static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
8cf89e00 879{
4fa4ce71
CG
880 char *buffer;
881 int ret;
2289be19
AK
882 char *path = fs_path->data;
883
4fa4ce71
CG
884 buffer = rpath(ctx, path);
885 ret = truncate(buffer, size);
886 g_free(buffer);
887 return ret;
8cf89e00
AL
888}
889
890static int local_rename(FsContext *ctx, const char *oldpath,
891 const char *newpath)
892{
2c30dd74 893 int err;
4fa4ce71 894 char *buffer, *buffer1;
8cf89e00 895
2c30dd74
AK
896 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
897 err = local_create_mapped_attr_dir(ctx, newpath);
898 if (err < 0) {
899 return err;
900 }
901 /* rename the .virtfs_metadata files */
4fa4ce71
CG
902 buffer = local_mapped_attr_path(ctx, oldpath);
903 buffer1 = local_mapped_attr_path(ctx, newpath);
904 err = rename(buffer, buffer1);
905 g_free(buffer);
906 g_free(buffer1);
2c30dd74
AK
907 if (err < 0 && errno != ENOENT) {
908 return err;
909 }
910 }
4fa4ce71
CG
911
912 buffer = rpath(ctx, oldpath);
913 buffer1 = rpath(ctx, newpath);
914 err = rename(buffer, buffer1);
915 g_free(buffer);
916 g_free(buffer1);
917 return err;
8cf89e00
AL
918}
919
2289be19 920static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
8cf89e00 921{
4fa4ce71
CG
922 char *buffer;
923 int ret = -1;
2289be19
AK
924 char *path = fs_path->data;
925
c79ce737 926 if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
17b1971f
AK
927 (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
928 (fs_ctx->export_flags & V9FS_SM_NONE)) {
4fa4ce71
CG
929 buffer = rpath(fs_ctx, path);
930 ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
931 g_free(buffer);
b97400ca 932 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
4fa4ce71
CG
933 buffer = rpath(fs_ctx, path);
934 ret = local_set_xattr(buffer, credp);
935 g_free(buffer);
2c30dd74
AK
936 } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
937 return local_set_mapped_file_attr(fs_ctx, path, credp);
f7613bee 938 }
4fa4ce71 939 return ret;
8cf89e00
AL
940}
941
2289be19 942static int local_utimensat(FsContext *s, V9fsPath *fs_path,
38671423 943 const struct timespec *buf)
8cf89e00 944{
4fa4ce71
CG
945 char *buffer;
946 int ret;
2289be19
AK
947 char *path = fs_path->data;
948
4fa4ce71
CG
949 buffer = rpath(s, path);
950 ret = qemu_utimens(buffer, buf);
951 g_free(buffer);
952 return ret;
8cf89e00
AL
953}
954
5bae1900
AL
955static int local_remove(FsContext *ctx, const char *path)
956{
2c30dd74
AK
957 int err;
958 struct stat stbuf;
4fa4ce71 959 char *buffer;
2c30dd74
AK
960
961 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
4fa4ce71
CG
962 buffer = rpath(ctx, path);
963 err = lstat(buffer, &stbuf);
964 g_free(buffer);
2c30dd74
AK
965 if (err) {
966 goto err_out;
967 }
968 /*
969 * If directory remove .virtfs_metadata contained in the
970 * directory
971 */
972 if (S_ISDIR(stbuf.st_mode)) {
4fa4ce71
CG
973 buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
974 path, VIRTFS_META_DIR);
2c30dd74 975 err = remove(buffer);
4fa4ce71 976 g_free(buffer);
2c30dd74
AK
977 if (err < 0 && errno != ENOENT) {
978 /*
979 * We didn't had the .virtfs_metadata file. May be file created
980 * in non-mapped mode ?. Ignore ENOENT.
981 */
982 goto err_out;
983 }
984 }
985 /*
986 * Now remove the name from parent directory
987 * .virtfs_metadata directory
988 */
4fa4ce71
CG
989 buffer = local_mapped_attr_path(ctx, path);
990 err = remove(buffer);
991 g_free(buffer);
2c30dd74
AK
992 if (err < 0 && errno != ENOENT) {
993 /*
994 * We didn't had the .virtfs_metadata file. May be file created
995 * in non-mapped mode ?. Ignore ENOENT.
996 */
997 goto err_out;
998 }
999 }
4fa4ce71
CG
1000
1001 buffer = rpath(ctx, path);
1002 err = remove(buffer);
1003 g_free(buffer);
2c30dd74
AK
1004err_out:
1005 return err;
5bae1900
AL
1006}
1007
8b888272
AK
1008static int local_fsync(FsContext *ctx, int fid_type,
1009 V9fsFidOpenState *fs, int datasync)
8cf89e00 1010{
8b888272
AK
1011 int fd;
1012
1013 if (fid_type == P9_FID_DIR) {
f314ea4e 1014 fd = dirfd(fs->dir.stream);
8b888272
AK
1015 } else {
1016 fd = fs->fd;
1017 }
1018
49594973 1019 if (datasync) {
8b888272 1020 return qemu_fdatasync(fd);
49594973 1021 } else {
8b888272 1022 return fsync(fd);
49594973 1023 }
8cf89e00
AL
1024}
1025
2289be19 1026static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
be940c87 1027{
4fa4ce71
CG
1028 char *buffer;
1029 int ret;
2289be19
AK
1030 char *path = fs_path->data;
1031
4fa4ce71
CG
1032 buffer = rpath(s, path);
1033 ret = statfs(buffer, stbuf);
1034 g_free(buffer);
1035 return ret;
be940c87
MK
1036}
1037
2289be19 1038static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
fa32ef88
AK
1039 const char *name, void *value, size_t size)
1040{
2289be19
AK
1041 char *path = fs_path->data;
1042
fc22118d 1043 return v9fs_get_xattr(ctx, path, name, value, size);
fa32ef88
AK
1044}
1045
2289be19 1046static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
fa32ef88
AK
1047 void *value, size_t size)
1048{
2289be19
AK
1049 char *path = fs_path->data;
1050
fc22118d 1051 return v9fs_list_xattr(ctx, path, value, size);
fa32ef88
AK
1052}
1053
2289be19 1054static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
10b468bd
AK
1055 void *value, size_t size, int flags)
1056{
2289be19
AK
1057 char *path = fs_path->data;
1058
fc22118d 1059 return v9fs_set_xattr(ctx, path, name, value, size, flags);
10b468bd
AK
1060}
1061
2289be19
AK
1062static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1063 const char *name)
9ed3ef26 1064{
2289be19
AK
1065 char *path = fs_path->data;
1066
fc22118d 1067 return v9fs_remove_xattr(ctx, path, name);
9ed3ef26
AK
1068}
1069
2289be19
AK
1070static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1071 const char *name, V9fsPath *target)
1072{
1073 if (dir_path) {
e3e83f2e 1074 v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
2289be19 1075 } else {
e3e83f2e 1076 v9fs_path_sprintf(target, "%s", name);
2289be19 1077 }
2289be19
AK
1078 return 0;
1079}
1080
1081static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1082 const char *old_name, V9fsPath *newdir,
1083 const char *new_name)
1084{
1085 int ret;
1086 V9fsString old_full_name, new_full_name;
1087
1088 v9fs_string_init(&old_full_name);
1089 v9fs_string_init(&new_full_name);
1090
1091 v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1092 v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1093
1094 ret = local_rename(ctx, old_full_name.data, new_full_name.data);
1095 v9fs_string_free(&old_full_name);
1096 v9fs_string_free(&new_full_name);
1097 return ret;
1098}
1099
1100static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1101 const char *name, int flags)
1102{
1103 int ret;
1104 V9fsString fullname;
4fa4ce71 1105 char *buffer;
2c30dd74 1106
2289be19
AK
1107 v9fs_string_init(&fullname);
1108
1109 v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
2c30dd74
AK
1110 if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1111 if (flags == AT_REMOVEDIR) {
1112 /*
1113 * If directory remove .virtfs_metadata contained in the
1114 * directory
1115 */
4fa4ce71
CG
1116 buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
1117 fullname.data, VIRTFS_META_DIR);
2c30dd74 1118 ret = remove(buffer);
4fa4ce71 1119 g_free(buffer);
2c30dd74
AK
1120 if (ret < 0 && errno != ENOENT) {
1121 /*
1122 * We didn't had the .virtfs_metadata file. May be file created
1123 * in non-mapped mode ?. Ignore ENOENT.
1124 */
1125 goto err_out;
1126 }
1127 }
1128 /*
1129 * Now remove the name from parent directory
1130 * .virtfs_metadata directory.
1131 */
4fa4ce71
CG
1132 buffer = local_mapped_attr_path(ctx, fullname.data);
1133 ret = remove(buffer);
1134 g_free(buffer);
2c30dd74
AK
1135 if (ret < 0 && errno != ENOENT) {
1136 /*
1137 * We didn't had the .virtfs_metadata file. May be file created
1138 * in non-mapped mode ?. Ignore ENOENT.
1139 */
1140 goto err_out;
1141 }
1142 }
1143 /* Remove the name finally */
4fa4ce71
CG
1144 buffer = rpath(ctx, fullname.data);
1145 ret = remove(buffer);
1146 g_free(buffer);
2289be19 1147
2c30dd74 1148err_out:
75b7931e 1149 v9fs_string_free(&fullname);
2289be19
AK
1150 return ret;
1151}
9ed3ef26 1152
e06a765e
HPB
1153static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1154 mode_t st_mode, uint64_t *st_gen)
1155{
ae0f940e 1156#ifdef FS_IOC_GETVERSION
0e5fc994 1157 int err;
cc720ddb
AK
1158 V9fsFidOpenState fid_open;
1159
e06a765e
HPB
1160 /*
1161 * Do not try to open special files like device nodes, fifos etc
1162 * We can get fd for regular files and directories only
1163 */
1164 if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1a9978a5
KS
1165 errno = ENOTTY;
1166 return -1;
e06a765e 1167 }
cc720ddb
AK
1168 err = local_open(ctx, path, O_RDONLY, &fid_open);
1169 if (err < 0) {
1170 return err;
e06a765e 1171 }
cc720ddb
AK
1172 err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1173 local_close(ctx, &fid_open);
0e5fc994 1174 return err;
ae0f940e 1175#else
0e5fc994
KS
1176 errno = ENOTTY;
1177 return -1;
ae0f940e 1178#endif
e06a765e
HPB
1179}
1180
0174fe73
AK
1181static int local_init(FsContext *ctx)
1182{
e06a765e 1183 struct statfs stbuf;
0e35a378
GK
1184 LocalData *data = g_malloc(sizeof(*data));
1185
1186 data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
1187 if (data->mountfd == -1) {
1188 goto err;
1189 }
e06a765e 1190
00c90bd1
GK
1191#ifdef FS_IOC_GETVERSION
1192 /*
1193 * use ioc_getversion only if the ioctl is definied
1194 */
0e35a378
GK
1195 if (fstatfs(data->mountfd, &stbuf) < 0) {
1196 close_preserve_errno(data->mountfd);
1197 goto err;
00c90bd1
GK
1198 }
1199 switch (stbuf.f_type) {
1200 case EXT2_SUPER_MAGIC:
1201 case BTRFS_SUPER_MAGIC:
1202 case REISERFS_SUPER_MAGIC:
1203 case XFS_SUPER_MAGIC:
1204 ctx->exops.get_st_gen = local_ioc_getversion;
1205 break;
1206 }
1207#endif
1208
2c30dd74
AK
1209 if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1210 ctx->xops = passthrough_xattr_ops;
1211 } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1212 ctx->xops = mapped_xattr_ops;
1213 } else if (ctx->export_flags & V9FS_SM_NONE) {
1214 ctx->xops = none_xattr_ops;
1215 } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1216 /*
1217 * xattr operation for mapped-file and passthrough
1218 * remain same.
1219 */
1220 ctx->xops = passthrough_xattr_ops;
1221 }
c98f1d4a 1222 ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
00c90bd1 1223
0e35a378 1224 ctx->private = data;
00c90bd1 1225 return 0;
0e35a378
GK
1226
1227err:
1228 g_free(data);
1229 return -1;
1230}
1231
1232static void local_cleanup(FsContext *ctx)
1233{
1234 LocalData *data = ctx->private;
1235
1236 close(data->mountfd);
1237 g_free(data);
0174fe73
AK
1238}
1239
99519f0a
AK
1240static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1241{
1242 const char *sec_model = qemu_opt_get(opts, "security_model");
1243 const char *path = qemu_opt_get(opts, "path");
1244
1245 if (!sec_model) {
63325b18
GK
1246 error_report("Security model not specified, local fs needs security model");
1247 error_printf("valid options are:"
1248 "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
99519f0a
AK
1249 return -1;
1250 }
1251
1252 if (!strcmp(sec_model, "passthrough")) {
1253 fse->export_flags |= V9FS_SM_PASSTHROUGH;
2c30dd74
AK
1254 } else if (!strcmp(sec_model, "mapped") ||
1255 !strcmp(sec_model, "mapped-xattr")) {
99519f0a
AK
1256 fse->export_flags |= V9FS_SM_MAPPED;
1257 } else if (!strcmp(sec_model, "none")) {
1258 fse->export_flags |= V9FS_SM_NONE;
2c30dd74
AK
1259 } else if (!strcmp(sec_model, "mapped-file")) {
1260 fse->export_flags |= V9FS_SM_MAPPED_FILE;
99519f0a 1261 } else {
63325b18
GK
1262 error_report("Invalid security model %s specified", sec_model);
1263 error_printf("valid options are:"
1264 "\t[passthrough|mapped-xattr|mapped-file|none]\n");
99519f0a
AK
1265 return -1;
1266 }
1267
1268 if (!path) {
63325b18 1269 error_report("fsdev: No path specified");
99519f0a
AK
1270 return -1;
1271 }
1272 fse->path = g_strdup(path);
1273
1274 return 0;
1275}
1276
9f107513 1277FileOperations local_ops = {
99519f0a 1278 .parse_opts = local_parse_opts,
0174fe73 1279 .init = local_init,
0e35a378 1280 .cleanup = local_cleanup,
131dcb25 1281 .lstat = local_lstat,
131dcb25
AL
1282 .readlink = local_readlink,
1283 .close = local_close,
1284 .closedir = local_closedir,
a6568fe2
AL
1285 .open = local_open,
1286 .opendir = local_opendir,
a9231555
AL
1287 .rewinddir = local_rewinddir,
1288 .telldir = local_telldir,
635324e8 1289 .readdir = local_readdir,
a9231555 1290 .seekdir = local_seekdir,
56d15a53
SG
1291 .preadv = local_preadv,
1292 .pwritev = local_pwritev,
c494dd6f
AL
1293 .chmod = local_chmod,
1294 .mknod = local_mknod,
c494dd6f
AL
1295 .mkdir = local_mkdir,
1296 .fstat = local_fstat,
1297 .open2 = local_open2,
1298 .symlink = local_symlink,
1299 .link = local_link,
8cf89e00
AL
1300 .truncate = local_truncate,
1301 .rename = local_rename,
1302 .chown = local_chown,
74bc02b2 1303 .utimensat = local_utimensat,
5bae1900 1304 .remove = local_remove,
8cf89e00 1305 .fsync = local_fsync,
be940c87 1306 .statfs = local_statfs,
fa32ef88
AK
1307 .lgetxattr = local_lgetxattr,
1308 .llistxattr = local_llistxattr,
10b468bd 1309 .lsetxattr = local_lsetxattr,
9ed3ef26 1310 .lremovexattr = local_lremovexattr,
2289be19
AK
1311 .name_to_path = local_name_to_path,
1312 .renameat = local_renameat,
1313 .unlinkat = local_unlinkat,
9f107513 1314};