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