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 <attr/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
->fs_sm
== 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
->fs_sm
!= 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
->fs_sm
== 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
->fs_sm
== SM_PASSTHROUGH
) ||
153 (fs_ctx
->fs_sm
== SM_NONE
)) {
154 tsize
= readlink(rpath(fs_ctx
, path
, buffer
), buf
, bufsz
);
159 static int local_close(FsContext
*ctx
, int fd
)
164 static int local_closedir(FsContext
*ctx
, DIR *dir
)
166 return closedir(dir
);
169 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
, int flags
)
171 char buffer
[PATH_MAX
];
172 char *path
= fs_path
->data
;
174 return open(rpath(ctx
, path
, buffer
), flags
);
177 static DIR *local_opendir(FsContext
*ctx
, V9fsPath
*fs_path
)
179 char buffer
[PATH_MAX
];
180 char *path
= fs_path
->data
;
182 return opendir(rpath(ctx
, path
, buffer
));
185 static void local_rewinddir(FsContext
*ctx
, DIR *dir
)
187 return rewinddir(dir
);
190 static off_t
local_telldir(FsContext
*ctx
, DIR *dir
)
195 static int local_readdir_r(FsContext
*ctx
, DIR *dir
, struct dirent
*entry
,
196 struct dirent
**result
)
198 return readdir_r(dir
, entry
, result
);
201 static void local_seekdir(FsContext
*ctx
, DIR *dir
, off_t off
)
203 return seekdir(dir
, off
);
206 static ssize_t
local_preadv(FsContext
*ctx
, int fd
, const struct iovec
*iov
,
207 int iovcnt
, off_t offset
)
210 return preadv(fd
, iov
, iovcnt
, offset
);
212 int err
= lseek(fd
, offset
, SEEK_SET
);
216 return readv(fd
, iov
, iovcnt
);
221 static ssize_t
local_pwritev(FsContext
*ctx
, int fd
, const struct iovec
*iov
,
222 int iovcnt
, off_t offset
)
227 ret
= pwritev(fd
, iov
, iovcnt
, offset
);
229 int err
= lseek(fd
, offset
, SEEK_SET
);
233 ret
= writev(fd
, iov
, iovcnt
);
236 #ifdef CONFIG_SYNC_FILE_RANGE
237 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
239 * Initiate a writeback. This is not a data integrity sync.
240 * We want to ensure that we don't leave dirty pages in the cache
241 * after write when writeout=immediate is sepcified.
243 sync_file_range(fd
, offset
, ret
,
244 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
250 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
252 char buffer
[PATH_MAX
];
253 char *path
= fs_path
->data
;
255 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
256 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
257 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
258 (fs_ctx
->fs_sm
== SM_NONE
)) {
259 return chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
264 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
265 const char *name
, FsCred
*credp
)
271 char buffer
[PATH_MAX
];
273 v9fs_string_init(&fullname
);
274 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
275 path
= fullname
.data
;
277 /* Determine the security model */
278 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
279 err
= mknod(rpath(fs_ctx
, path
, buffer
),
280 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
284 local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
289 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
290 (fs_ctx
->fs_sm
== SM_NONE
)) {
291 err
= mknod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
,
296 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
305 remove(rpath(fs_ctx
, path
, buffer
));
308 v9fs_string_free(&fullname
);
312 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
313 const char *name
, FsCred
*credp
)
319 char buffer
[PATH_MAX
];
321 v9fs_string_init(&fullname
);
322 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
323 path
= fullname
.data
;
325 /* Determine the security model */
326 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
327 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
331 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
332 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
337 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
338 (fs_ctx
->fs_sm
== SM_NONE
)) {
339 err
= mkdir(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
343 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
352 remove(rpath(fs_ctx
, path
, buffer
));
355 v9fs_string_free(&fullname
);
359 static int local_fstat(FsContext
*fs_ctx
, int fd
, struct stat
*stbuf
)
362 err
= fstat(fd
, stbuf
);
366 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
367 /* Actual credentials are part of extended attrs */
373 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
374 stbuf
->st_uid
= tmp_uid
;
376 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
377 stbuf
->st_gid
= tmp_gid
;
379 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
380 stbuf
->st_mode
= tmp_mode
;
382 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
383 stbuf
->st_rdev
= tmp_dev
;
389 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
390 int flags
, FsCred
*credp
)
397 char buffer
[PATH_MAX
];
399 v9fs_string_init(&fullname
);
400 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
401 path
= fullname
.data
;
403 /* Determine the security model */
404 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
405 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
410 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
411 /* Set cleint credentials in xattr */
412 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
417 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
418 (fs_ctx
->fs_sm
== SM_NONE
)) {
419 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
424 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
435 remove(rpath(fs_ctx
, path
, buffer
));
438 v9fs_string_free(&fullname
);
443 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
444 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
450 char buffer
[PATH_MAX
];
452 v9fs_string_init(&fullname
);
453 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
454 newpath
= fullname
.data
;
456 /* Determine the security model */
457 if (fs_ctx
->fs_sm
== SM_MAPPED
) {
459 ssize_t oldpath_size
, write_size
;
460 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
466 /* Write the oldpath (target) to the file. */
467 oldpath_size
= strlen(oldpath
);
469 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
470 } while (write_size
== -1 && errno
== EINTR
);
472 if (write_size
!= oldpath_size
) {
479 /* Set cleint credentials in symlink's xattr */
480 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
481 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
486 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
487 (fs_ctx
->fs_sm
== SM_NONE
)) {
488 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
492 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
496 * If we fail to change ownership and if we are
497 * using security model none. Ignore the error
499 if (fs_ctx
->fs_sm
!= SM_NONE
) {
509 remove(rpath(fs_ctx
, newpath
, buffer
));
512 v9fs_string_free(&fullname
);
516 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
517 V9fsPath
*dirpath
, const char *name
)
521 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
523 v9fs_string_init(&newpath
);
524 v9fs_string_sprintf(&newpath
, "%s/%s", dirpath
->data
, name
);
526 ret
= link(rpath(ctx
, oldpath
->data
, buffer
),
527 rpath(ctx
, newpath
.data
, buffer1
));
528 v9fs_string_free(&newpath
);
532 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
534 char buffer
[PATH_MAX
];
535 char *path
= fs_path
->data
;
537 return truncate(rpath(ctx
, path
, buffer
), size
);
540 static int local_rename(FsContext
*ctx
, const char *oldpath
,
543 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
545 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
548 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
550 char buffer
[PATH_MAX
];
551 char *path
= fs_path
->data
;
553 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
554 (fs_ctx
->fs_sm
== SM_PASSTHROUGH
)) {
555 return lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
557 } else if (fs_ctx
->fs_sm
== SM_MAPPED
) {
558 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
559 } else if ((fs_ctx
->fs_sm
== SM_PASSTHROUGH
) ||
560 (fs_ctx
->fs_sm
== SM_NONE
)) {
561 return lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
567 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
568 const struct timespec
*buf
)
570 char buffer
[PATH_MAX
];
571 char *path
= fs_path
->data
;
573 return qemu_utimensat(AT_FDCWD
, rpath(s
, path
, buffer
), buf
,
574 AT_SYMLINK_NOFOLLOW
);
577 static int local_remove(FsContext
*ctx
, const char *path
)
579 char buffer
[PATH_MAX
];
580 return remove(rpath(ctx
, path
, buffer
));
583 static int local_fsync(FsContext
*ctx
, int fd
, int datasync
)
586 return qemu_fdatasync(fd
);
592 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
594 char buffer
[PATH_MAX
];
595 char *path
= fs_path
->data
;
597 return statfs(rpath(s
, path
, buffer
), stbuf
);
600 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
601 const char *name
, void *value
, size_t size
)
603 char *path
= fs_path
->data
;
605 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
608 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
609 void *value
, size_t size
)
611 char *path
= fs_path
->data
;
613 return v9fs_list_xattr(ctx
, path
, value
, size
);
616 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
617 void *value
, size_t size
, int flags
)
619 char *path
= fs_path
->data
;
621 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
624 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
627 char *path
= fs_path
->data
;
629 return v9fs_remove_xattr(ctx
, path
, name
);
632 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
633 const char *name
, V9fsPath
*target
)
636 v9fs_string_sprintf((V9fsString
*)target
, "%s/%s",
637 dir_path
->data
, name
);
639 v9fs_string_sprintf((V9fsString
*)target
, "%s", name
);
641 /* Bump the size for including terminating NULL */
646 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
647 const char *old_name
, V9fsPath
*newdir
,
648 const char *new_name
)
651 V9fsString old_full_name
, new_full_name
;
653 v9fs_string_init(&old_full_name
);
654 v9fs_string_init(&new_full_name
);
656 v9fs_string_sprintf(&old_full_name
, "%s/%s", olddir
->data
, old_name
);
657 v9fs_string_sprintf(&new_full_name
, "%s/%s", newdir
->data
, new_name
);
659 ret
= local_rename(ctx
, old_full_name
.data
, new_full_name
.data
);
660 v9fs_string_free(&old_full_name
);
661 v9fs_string_free(&new_full_name
);
665 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
666 const char *name
, int flags
)
670 char buffer
[PATH_MAX
];
671 v9fs_string_init(&fullname
);
673 v9fs_string_sprintf(&fullname
, "%s/%s", dir
->data
, name
);
674 ret
= remove(rpath(ctx
, fullname
.data
, buffer
));
675 v9fs_string_free(&fullname
);
680 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
681 mode_t st_mode
, uint64_t *st_gen
)
685 * Do not try to open special files like device nodes, fifos etc
686 * We can get fd for regular files and directories only
688 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
691 fd
= local_open(ctx
, path
, O_RDONLY
);
695 err
= ioctl(fd
, FS_IOC_GETVERSION
, st_gen
);
696 local_close(ctx
, fd
);
700 static int local_init(FsContext
*ctx
)
705 ctx
->flags
|= PATHNAME_FSCONTEXT
;
706 err
= statfs(ctx
->fs_root
, &stbuf
);
708 switch (stbuf
.f_type
) {
709 case EXT2_SUPER_MAGIC
:
710 case BTRFS_SUPER_MAGIC
:
711 case REISERFS_SUPER_MAGIC
:
712 case XFS_SUPER_MAGIC
:
713 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
720 FileOperations local_ops
= {
722 .lstat
= local_lstat
,
723 .readlink
= local_readlink
,
724 .close
= local_close
,
725 .closedir
= local_closedir
,
727 .opendir
= local_opendir
,
728 .rewinddir
= local_rewinddir
,
729 .telldir
= local_telldir
,
730 .readdir_r
= local_readdir_r
,
731 .seekdir
= local_seekdir
,
732 .preadv
= local_preadv
,
733 .pwritev
= local_pwritev
,
734 .chmod
= local_chmod
,
735 .mknod
= local_mknod
,
736 .mkdir
= local_mkdir
,
737 .fstat
= local_fstat
,
738 .open2
= local_open2
,
739 .symlink
= local_symlink
,
741 .truncate
= local_truncate
,
742 .rename
= local_rename
,
743 .chown
= local_chown
,
744 .utimensat
= local_utimensat
,
745 .remove
= local_remove
,
746 .fsync
= local_fsync
,
747 .statfs
= local_statfs
,
748 .lgetxattr
= local_lgetxattr
,
749 .llistxattr
= local_llistxattr
,
750 .lsetxattr
= local_lsetxattr
,
751 .lremovexattr
= local_lremovexattr
,
752 .name_to_path
= local_name_to_path
,
753 .renameat
= local_renameat
,
754 .unlinkat
= local_unlinkat
,