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