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
,
370 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
373 err
= fstat(fs
->fd
, stbuf
);
377 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
378 /* Actual credentials are part of extended attrs */
384 if (fgetxattr(fs
->fd
, "user.virtfs.uid",
385 &tmp_uid
, sizeof(uid_t
)) > 0) {
386 stbuf
->st_uid
= tmp_uid
;
388 if (fgetxattr(fs
->fd
, "user.virtfs.gid",
389 &tmp_gid
, sizeof(gid_t
)) > 0) {
390 stbuf
->st_gid
= tmp_gid
;
392 if (fgetxattr(fs
->fd
, "user.virtfs.mode",
393 &tmp_mode
, sizeof(mode_t
)) > 0) {
394 stbuf
->st_mode
= tmp_mode
;
396 if (fgetxattr(fs
->fd
, "user.virtfs.rdev",
397 &tmp_dev
, sizeof(dev_t
)) > 0) {
398 stbuf
->st_rdev
= tmp_dev
;
404 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
405 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
412 char buffer
[PATH_MAX
];
414 v9fs_string_init(&fullname
);
415 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
416 path
= fullname
.data
;
418 /* Determine the security model */
419 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
420 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
425 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
426 /* Set cleint credentials in xattr */
427 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
432 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
433 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
434 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
439 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
451 remove(rpath(fs_ctx
, path
, buffer
));
454 v9fs_string_free(&fullname
);
459 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
460 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
466 char buffer
[PATH_MAX
];
468 v9fs_string_init(&fullname
);
469 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
470 newpath
= fullname
.data
;
472 /* Determine the security model */
473 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
475 ssize_t oldpath_size
, write_size
;
476 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
482 /* Write the oldpath (target) to the file. */
483 oldpath_size
= strlen(oldpath
);
485 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
486 } while (write_size
== -1 && errno
== EINTR
);
488 if (write_size
!= oldpath_size
) {
495 /* Set cleint credentials in symlink's xattr */
496 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
497 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
502 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
503 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
504 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
508 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
512 * If we fail to change ownership and if we are
513 * using security model none. Ignore the error
515 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
525 remove(rpath(fs_ctx
, newpath
, buffer
));
528 v9fs_string_free(&fullname
);
532 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
533 V9fsPath
*dirpath
, const char *name
)
537 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
539 v9fs_string_init(&newpath
);
540 v9fs_string_sprintf(&newpath
, "%s/%s", dirpath
->data
, name
);
542 ret
= link(rpath(ctx
, oldpath
->data
, buffer
),
543 rpath(ctx
, newpath
.data
, buffer1
));
544 v9fs_string_free(&newpath
);
548 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
550 char buffer
[PATH_MAX
];
551 char *path
= fs_path
->data
;
553 return truncate(rpath(ctx
, path
, buffer
), size
);
556 static int local_rename(FsContext
*ctx
, const char *oldpath
,
559 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
561 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
564 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
566 char buffer
[PATH_MAX
];
567 char *path
= fs_path
->data
;
569 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
570 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
571 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
572 return lchown(rpath(fs_ctx
, path
, buffer
),
573 credp
->fc_uid
, credp
->fc_gid
);
574 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
575 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
580 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
581 const struct timespec
*buf
)
583 char buffer
[PATH_MAX
];
584 char *path
= fs_path
->data
;
586 return qemu_utimensat(AT_FDCWD
, rpath(s
, path
, buffer
), buf
,
587 AT_SYMLINK_NOFOLLOW
);
590 static int local_remove(FsContext
*ctx
, const char *path
)
592 char buffer
[PATH_MAX
];
593 return remove(rpath(ctx
, path
, buffer
));
596 static int local_fsync(FsContext
*ctx
, V9fsFidOpenState
*fs
, int datasync
)
599 return qemu_fdatasync(fs
->fd
);
601 return fsync(fs
->fd
);
605 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
607 char buffer
[PATH_MAX
];
608 char *path
= fs_path
->data
;
610 return statfs(rpath(s
, path
, buffer
), stbuf
);
613 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
614 const char *name
, void *value
, size_t size
)
616 char *path
= fs_path
->data
;
618 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
621 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
622 void *value
, size_t size
)
624 char *path
= fs_path
->data
;
626 return v9fs_list_xattr(ctx
, path
, value
, size
);
629 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
630 void *value
, size_t size
, int flags
)
632 char *path
= fs_path
->data
;
634 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
637 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
640 char *path
= fs_path
->data
;
642 return v9fs_remove_xattr(ctx
, path
, name
);
645 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
646 const char *name
, V9fsPath
*target
)
649 v9fs_string_sprintf((V9fsString
*)target
, "%s/%s",
650 dir_path
->data
, name
);
652 v9fs_string_sprintf((V9fsString
*)target
, "%s", name
);
654 /* Bump the size for including terminating NULL */
659 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
660 const char *old_name
, V9fsPath
*newdir
,
661 const char *new_name
)
664 V9fsString old_full_name
, new_full_name
;
666 v9fs_string_init(&old_full_name
);
667 v9fs_string_init(&new_full_name
);
669 v9fs_string_sprintf(&old_full_name
, "%s/%s", olddir
->data
, old_name
);
670 v9fs_string_sprintf(&new_full_name
, "%s/%s", newdir
->data
, new_name
);
672 ret
= local_rename(ctx
, old_full_name
.data
, new_full_name
.data
);
673 v9fs_string_free(&old_full_name
);
674 v9fs_string_free(&new_full_name
);
678 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
679 const char *name
, int flags
)
683 char buffer
[PATH_MAX
];
684 v9fs_string_init(&fullname
);
686 v9fs_string_sprintf(&fullname
, "%s/%s", dir
->data
, name
);
687 ret
= remove(rpath(ctx
, fullname
.data
, buffer
));
688 v9fs_string_free(&fullname
);
693 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
694 mode_t st_mode
, uint64_t *st_gen
)
697 V9fsFidOpenState fid_open
;
700 * Do not try to open special files like device nodes, fifos etc
701 * We can get fd for regular files and directories only
703 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
706 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
710 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
711 local_close(ctx
, &fid_open
);
715 static int local_init(FsContext
*ctx
)
720 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
721 err
= statfs(ctx
->fs_root
, &stbuf
);
723 switch (stbuf
.f_type
) {
724 case EXT2_SUPER_MAGIC
:
725 case BTRFS_SUPER_MAGIC
:
726 case REISERFS_SUPER_MAGIC
:
727 case XFS_SUPER_MAGIC
:
728 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
735 FileOperations local_ops
= {
737 .lstat
= local_lstat
,
738 .readlink
= local_readlink
,
739 .close
= local_close
,
740 .closedir
= local_closedir
,
742 .opendir
= local_opendir
,
743 .rewinddir
= local_rewinddir
,
744 .telldir
= local_telldir
,
745 .readdir_r
= local_readdir_r
,
746 .seekdir
= local_seekdir
,
747 .preadv
= local_preadv
,
748 .pwritev
= local_pwritev
,
749 .chmod
= local_chmod
,
750 .mknod
= local_mknod
,
751 .mkdir
= local_mkdir
,
752 .fstat
= local_fstat
,
753 .open2
= local_open2
,
754 .symlink
= local_symlink
,
756 .truncate
= local_truncate
,
757 .rename
= local_rename
,
758 .chown
= local_chown
,
759 .utimensat
= local_utimensat
,
760 .remove
= local_remove
,
761 .fsync
= local_fsync
,
762 .statfs
= local_statfs
,
763 .lgetxattr
= local_lgetxattr
,
764 .llistxattr
= local_llistxattr
,
765 .lsetxattr
= local_lsetxattr
,
766 .lremovexattr
= local_lremovexattr
,
767 .name_to_path
= local_name_to_path
,
768 .renameat
= local_renameat
,
769 .unlinkat
= local_unlinkat
,