2 * Virtio 9p Posix callback
4 * Copyright IBM, Corp. 2010
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include "hw/virtio.h"
15 #include "virtio-9p.h"
16 #include "virtio-9p-xattr.h"
17 #include <arpa/inet.h>
20 #include <sys/socket.h>
22 #include "qemu-xattr.h"
24 #ifdef CONFIG_LINUX_MAGIC_H
25 #include <linux/magic.h>
27 #include <sys/ioctl.h>
29 #ifndef XFS_SUPER_MAGIC
30 #define XFS_SUPER_MAGIC 0x58465342
32 #ifndef EXT2_SUPER_MAGIC
33 #define EXT2_SUPER_MAGIC 0xEF53
35 #ifndef REISERFS_SUPER_MAGIC
36 #define REISERFS_SUPER_MAGIC 0x52654973
38 #ifndef BTRFS_SUPER_MAGIC
39 #define BTRFS_SUPER_MAGIC 0x9123683E
42 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
45 char buffer
[PATH_MAX
];
46 char *path
= fs_path
->data
;
48 err
= lstat(rpath(fs_ctx
, path
, buffer
), stbuf
);
52 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
53 /* Actual credentials are part of extended attrs */
58 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.uid", &tmp_uid
,
60 stbuf
->st_uid
= tmp_uid
;
62 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.gid", &tmp_gid
,
64 stbuf
->st_gid
= tmp_gid
;
66 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.mode",
67 &tmp_mode
, sizeof(mode_t
)) > 0) {
68 stbuf
->st_mode
= tmp_mode
;
70 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.rdev", &tmp_dev
,
72 stbuf
->st_rdev
= tmp_dev
;
78 static int local_set_xattr(const char *path
, FsCred
*credp
)
82 if (credp
->fc_uid
!= -1) {
83 err
= setxattr(path
, "user.virtfs.uid", &credp
->fc_uid
, sizeof(uid_t
),
89 if (credp
->fc_gid
!= -1) {
90 err
= setxattr(path
, "user.virtfs.gid", &credp
->fc_gid
, sizeof(gid_t
),
96 if (credp
->fc_mode
!= -1) {
97 err
= setxattr(path
, "user.virtfs.mode", &credp
->fc_mode
,
103 if (credp
->fc_rdev
!= -1) {
104 err
= setxattr(path
, "user.virtfs.rdev", &credp
->fc_rdev
,
113 static int local_post_create_passthrough(FsContext
*fs_ctx
, const char *path
,
116 char buffer
[PATH_MAX
];
118 if (chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
& 07777) < 0) {
121 if (lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
122 credp
->fc_gid
) < 0) {
124 * If we fail to change ownership and if we are
125 * using security model none. Ignore the error
127 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
134 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
135 char *buf
, size_t bufsz
)
138 char buffer
[PATH_MAX
];
139 char *path
= fs_path
->data
;
141 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
143 fd
= open(rpath(fs_ctx
, path
, buffer
), O_RDONLY
);
148 tsize
= read(fd
, (void *)buf
, bufsz
);
149 } while (tsize
== -1 && errno
== EINTR
);
152 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
153 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
154 tsize
= readlink(rpath(fs_ctx
, path
, buffer
), buf
, bufsz
);
159 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
161 return close(fs
->fd
);
164 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
166 return closedir(fs
->dir
);
169 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
170 int flags
, V9fsFidOpenState
*fs
)
172 char buffer
[PATH_MAX
];
173 char *path
= fs_path
->data
;
175 fs
->fd
= open(rpath(ctx
, path
, buffer
), flags
);
179 static int local_opendir(FsContext
*ctx
,
180 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
182 char buffer
[PATH_MAX
];
183 char *path
= fs_path
->data
;
185 fs
->dir
= opendir(rpath(ctx
, path
, buffer
));
192 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
194 return rewinddir(fs
->dir
);
197 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
199 return telldir(fs
->dir
);
202 static int local_readdir_r(FsContext
*ctx
, V9fsFidOpenState
*fs
,
203 struct dirent
*entry
,
204 struct dirent
**result
)
206 return readdir_r(fs
->dir
, entry
, result
);
209 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
211 return seekdir(fs
->dir
, off
);
214 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
215 const struct iovec
*iov
,
216 int iovcnt
, off_t offset
)
219 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
221 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
225 return readv(fs
->fd
, iov
, iovcnt
);
230 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
231 const struct iovec
*iov
,
232 int iovcnt
, off_t offset
)
237 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
239 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
243 ret
= writev(fs
->fd
, iov
, iovcnt
);
246 #ifdef CONFIG_SYNC_FILE_RANGE
247 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
249 * Initiate a writeback. This is not a data integrity sync.
250 * We want to ensure that we don't leave dirty pages in the cache
251 * after write when writeout=immediate is sepcified.
253 sync_file_range(fs
->fd
, offset
, ret
,
254 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
260 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
262 char buffer
[PATH_MAX
];
263 char *path
= fs_path
->data
;
265 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
266 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
267 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
268 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
269 return chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
274 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
275 const char *name
, FsCred
*credp
)
281 char buffer
[PATH_MAX
];
283 v9fs_string_init(&fullname
);
284 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
285 path
= fullname
.data
;
287 /* Determine the security model */
288 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
289 err
= mknod(rpath(fs_ctx
, path
, buffer
),
290 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
294 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
299 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
300 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
301 err
= mknod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
,
306 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
315 remove(rpath(fs_ctx
, path
, buffer
));
318 v9fs_string_free(&fullname
);
322 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
323 const char *name
, FsCred
*credp
)
329 char buffer
[PATH_MAX
];
331 v9fs_string_init(&fullname
);
332 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
333 path
= fullname
.data
;
335 /* Determine the security model */
336 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
337 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
341 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
342 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
347 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
348 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
349 err
= mkdir(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
353 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
362 remove(rpath(fs_ctx
, path
, buffer
));
365 v9fs_string_free(&fullname
);
369 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
370 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
374 if (fid_type
== P9_FID_DIR
) {
380 err
= fstat(fd
, stbuf
);
384 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
385 /* Actual credentials are part of extended attrs */
391 if (fgetxattr(fd
, "user.virtfs.uid",
392 &tmp_uid
, sizeof(uid_t
)) > 0) {
393 stbuf
->st_uid
= tmp_uid
;
395 if (fgetxattr(fd
, "user.virtfs.gid",
396 &tmp_gid
, sizeof(gid_t
)) > 0) {
397 stbuf
->st_gid
= tmp_gid
;
399 if (fgetxattr(fd
, "user.virtfs.mode",
400 &tmp_mode
, sizeof(mode_t
)) > 0) {
401 stbuf
->st_mode
= tmp_mode
;
403 if (fgetxattr(fd
, "user.virtfs.rdev",
404 &tmp_dev
, sizeof(dev_t
)) > 0) {
405 stbuf
->st_rdev
= tmp_dev
;
411 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
412 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
419 char buffer
[PATH_MAX
];
421 v9fs_string_init(&fullname
);
422 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
423 path
= fullname
.data
;
425 /* Determine the security model */
426 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
427 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
432 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
433 /* Set cleint credentials in xattr */
434 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
439 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
440 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
441 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
446 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
458 remove(rpath(fs_ctx
, path
, buffer
));
461 v9fs_string_free(&fullname
);
466 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
467 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
473 char buffer
[PATH_MAX
];
475 v9fs_string_init(&fullname
);
476 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
477 newpath
= fullname
.data
;
479 /* Determine the security model */
480 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
482 ssize_t oldpath_size
, write_size
;
483 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
489 /* Write the oldpath (target) to the file. */
490 oldpath_size
= strlen(oldpath
);
492 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
493 } while (write_size
== -1 && errno
== EINTR
);
495 if (write_size
!= oldpath_size
) {
502 /* Set cleint credentials in symlink's xattr */
503 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
504 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
509 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
510 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
511 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
515 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
519 * If we fail to change ownership and if we are
520 * using security model none. Ignore the error
522 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
532 remove(rpath(fs_ctx
, newpath
, buffer
));
535 v9fs_string_free(&fullname
);
539 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
540 V9fsPath
*dirpath
, const char *name
)
544 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
546 v9fs_string_init(&newpath
);
547 v9fs_string_sprintf(&newpath
, "%s/%s", dirpath
->data
, name
);
549 ret
= link(rpath(ctx
, oldpath
->data
, buffer
),
550 rpath(ctx
, newpath
.data
, buffer1
));
551 v9fs_string_free(&newpath
);
555 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
557 char buffer
[PATH_MAX
];
558 char *path
= fs_path
->data
;
560 return truncate(rpath(ctx
, path
, buffer
), size
);
563 static int local_rename(FsContext
*ctx
, const char *oldpath
,
566 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
568 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
571 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
573 char buffer
[PATH_MAX
];
574 char *path
= fs_path
->data
;
576 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
577 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
578 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
579 return lchown(rpath(fs_ctx
, path
, buffer
),
580 credp
->fc_uid
, credp
->fc_gid
);
581 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
582 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
587 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
588 const struct timespec
*buf
)
590 char buffer
[PATH_MAX
];
591 char *path
= fs_path
->data
;
593 return qemu_utimens(rpath(s
, path
, buffer
), buf
);
596 static int local_remove(FsContext
*ctx
, const char *path
)
598 char buffer
[PATH_MAX
];
599 return remove(rpath(ctx
, path
, buffer
));
602 static int local_fsync(FsContext
*ctx
, int fid_type
,
603 V9fsFidOpenState
*fs
, int datasync
)
607 if (fid_type
== P9_FID_DIR
) {
614 return qemu_fdatasync(fd
);
620 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
622 char buffer
[PATH_MAX
];
623 char *path
= fs_path
->data
;
625 return statfs(rpath(s
, path
, buffer
), stbuf
);
628 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
629 const char *name
, void *value
, size_t size
)
631 char *path
= fs_path
->data
;
633 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
636 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
637 void *value
, size_t size
)
639 char *path
= fs_path
->data
;
641 return v9fs_list_xattr(ctx
, path
, value
, size
);
644 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
645 void *value
, size_t size
, int flags
)
647 char *path
= fs_path
->data
;
649 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
652 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
655 char *path
= fs_path
->data
;
657 return v9fs_remove_xattr(ctx
, path
, name
);
660 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
661 const char *name
, V9fsPath
*target
)
664 v9fs_string_sprintf((V9fsString
*)target
, "%s/%s",
665 dir_path
->data
, name
);
667 v9fs_string_sprintf((V9fsString
*)target
, "%s", name
);
669 /* Bump the size for including terminating NULL */
674 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
675 const char *old_name
, V9fsPath
*newdir
,
676 const char *new_name
)
679 V9fsString old_full_name
, new_full_name
;
681 v9fs_string_init(&old_full_name
);
682 v9fs_string_init(&new_full_name
);
684 v9fs_string_sprintf(&old_full_name
, "%s/%s", olddir
->data
, old_name
);
685 v9fs_string_sprintf(&new_full_name
, "%s/%s", newdir
->data
, new_name
);
687 ret
= local_rename(ctx
, old_full_name
.data
, new_full_name
.data
);
688 v9fs_string_free(&old_full_name
);
689 v9fs_string_free(&new_full_name
);
693 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
694 const char *name
, int flags
)
698 char buffer
[PATH_MAX
];
699 v9fs_string_init(&fullname
);
701 v9fs_string_sprintf(&fullname
, "%s/%s", dir
->data
, name
);
702 ret
= remove(rpath(ctx
, fullname
.data
, buffer
));
703 v9fs_string_free(&fullname
);
708 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
709 mode_t st_mode
, uint64_t *st_gen
)
712 #ifdef FS_IOC_GETVERSION
713 V9fsFidOpenState fid_open
;
716 * Do not try to open special files like device nodes, fifos etc
717 * We can get fd for regular files and directories only
719 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
722 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
726 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
727 local_close(ctx
, &fid_open
);
734 static int local_init(FsContext
*ctx
)
739 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
740 #ifdef FS_IOC_GETVERSION
742 * use ioc_getversion only if the iocl is definied
744 err
= statfs(ctx
->fs_root
, &stbuf
);
746 switch (stbuf
.f_type
) {
747 case EXT2_SUPER_MAGIC
:
748 case BTRFS_SUPER_MAGIC
:
749 case REISERFS_SUPER_MAGIC
:
750 case XFS_SUPER_MAGIC
:
751 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
759 FileOperations local_ops
= {
761 .lstat
= local_lstat
,
762 .readlink
= local_readlink
,
763 .close
= local_close
,
764 .closedir
= local_closedir
,
766 .opendir
= local_opendir
,
767 .rewinddir
= local_rewinddir
,
768 .telldir
= local_telldir
,
769 .readdir_r
= local_readdir_r
,
770 .seekdir
= local_seekdir
,
771 .preadv
= local_preadv
,
772 .pwritev
= local_pwritev
,
773 .chmod
= local_chmod
,
774 .mknod
= local_mknod
,
775 .mkdir
= local_mkdir
,
776 .fstat
= local_fstat
,
777 .open2
= local_open2
,
778 .symlink
= local_symlink
,
780 .truncate
= local_truncate
,
781 .rename
= local_rename
,
782 .chown
= local_chown
,
783 .utimensat
= local_utimensat
,
784 .remove
= local_remove
,
785 .fsync
= local_fsync
,
786 .statfs
= local_statfs
,
787 .lgetxattr
= local_lgetxattr
,
788 .llistxattr
= local_llistxattr
,
789 .lsetxattr
= local_lsetxattr
,
790 .lremovexattr
= local_lremovexattr
,
791 .name_to_path
= local_name_to_path
,
792 .renameat
= local_renameat
,
793 .unlinkat
= local_unlinkat
,