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 * Not so fast! You might want to read the 9p developer docs first:
15 * https://wiki.qemu.org/Documentation/9p
18 #include "qemu/osdep.h"
23 #include "fsdev/qemu-fsdev.h" /* local_ops */
24 #include <arpa/inet.h>
27 #include <sys/socket.h>
29 #include "qemu/xattr.h"
30 #include "qapi/error.h"
31 #include "qemu/cutils.h"
32 #include "qemu/error-report.h"
33 #include "qemu/option.h"
36 #ifdef CONFIG_LINUX_MAGIC_H
37 #include <linux/magic.h>
39 #include <sys/ioctl.h>
41 #ifndef XFS_SUPER_MAGIC
42 #define XFS_SUPER_MAGIC 0x58465342
44 #ifndef EXT2_SUPER_MAGIC
45 #define EXT2_SUPER_MAGIC 0xEF53
47 #ifndef REISERFS_SUPER_MAGIC
48 #define REISERFS_SUPER_MAGIC 0x52654973
50 #ifndef BTRFS_SUPER_MAGIC
51 #define BTRFS_SUPER_MAGIC 0x9123683E
58 int local_open_nofollow(FsContext
*fs_ctx
, const char *path
, int flags
,
61 LocalData
*data
= fs_ctx
->private;
62 int fd
= data
->mountfd
;
64 while (*path
&& fd
!= -1) {
69 /* Only relative paths without consecutive slashes */
72 head
= g_strdup(path
);
73 c
= qemu_strchrnul(path
, '/');
75 /* Intermediate path element */
78 next_fd
= openat_dir(fd
, head
);
80 /* Rightmost path element */
81 next_fd
= openat_file(fd
, head
, flags
, mode
);
85 if (fd
!= data
->mountfd
) {
86 close_preserve_errno(fd
);
91 assert(fd
!= data
->mountfd
);
95 int local_opendir_nofollow(FsContext
*fs_ctx
, const char *path
)
97 return local_open_nofollow(fs_ctx
, path
, O_DIRECTORY
| O_RDONLY
, 0);
100 static void renameat_preserve_errno(int odirfd
, const char *opath
, int ndirfd
,
104 renameat(odirfd
, opath
, ndirfd
, npath
);
108 static void unlinkat_preserve_errno(int dirfd
, const char *path
, int flags
)
111 unlinkat(dirfd
, path
, flags
);
115 #define VIRTFS_META_DIR ".virtfs_metadata"
116 #define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root"
118 static FILE *local_fopenat(int dirfd
, const char *name
, const char *mode
)
124 * only supports two modes
126 if (mode
[0] == 'r') {
128 } else if (mode
[0] == 'w') {
129 flags
= O_WRONLY
| O_TRUNC
| O_CREAT
;
130 o_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
;
134 fd
= openat_file(dirfd
, name
, flags
, o_mode
);
138 fp
= fdopen(fd
, mode
);
146 static void local_mapped_file_attr(int dirfd
, const char *name
,
153 if (strcmp(name
, ".")) {
154 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
155 if (map_dirfd
== -1) {
159 fp
= local_fopenat(map_dirfd
, name
, "r");
160 close_preserve_errno(map_dirfd
);
162 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "r");
167 memset(buf
, 0, ATTR_MAX
);
168 while (fgets(buf
, ATTR_MAX
, fp
)) {
169 if (!strncmp(buf
, "virtfs.uid", 10)) {
170 stbuf
->st_uid
= atoi(buf
+ 11);
171 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
172 stbuf
->st_gid
= atoi(buf
+ 11);
173 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
174 stbuf
->st_mode
= atoi(buf
+ 12);
175 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
176 stbuf
->st_rdev
= atoi(buf
+ 12);
178 memset(buf
, 0, ATTR_MAX
);
183 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
186 char *dirpath
= g_path_get_dirname(fs_path
->data
);
187 char *name
= g_path_get_basename(fs_path
->data
);
190 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
195 err
= fstatat(dirfd
, name
, stbuf
, AT_SYMLINK_NOFOLLOW
);
199 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
200 /* Actual credentials are part of extended attrs */
206 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.uid", &tmp_uid
,
207 sizeof(uid_t
)) > 0) {
208 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
210 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.gid", &tmp_gid
,
211 sizeof(gid_t
)) > 0) {
212 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
214 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.mode", &tmp_mode
,
215 sizeof(mode_t
)) > 0) {
216 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
218 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.rdev", &tmp_dev
,
219 sizeof(dev_t
)) > 0) {
220 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
222 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
223 local_mapped_file_attr(dirfd
, name
, stbuf
);
227 close_preserve_errno(dirfd
);
234 static int local_set_mapped_file_attrat(int dirfd
, const char *name
,
240 int uid
= -1, gid
= -1, mode
= -1, rdev
= -1;
241 int map_dirfd
= -1, map_fd
;
242 bool is_root
= !strcmp(name
, ".");
245 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "r");
247 if (errno
== ENOENT
) {
248 goto update_map_file
;
254 ret
= mkdirat(dirfd
, VIRTFS_META_DIR
, 0700);
255 if (ret
< 0 && errno
!= EEXIST
) {
259 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
260 if (map_dirfd
== -1) {
264 fp
= local_fopenat(map_dirfd
, name
, "r");
266 if (errno
== ENOENT
) {
267 goto update_map_file
;
269 close_preserve_errno(map_dirfd
);
274 memset(buf
, 0, ATTR_MAX
);
275 while (fgets(buf
, ATTR_MAX
, fp
)) {
276 if (!strncmp(buf
, "virtfs.uid", 10)) {
277 uid
= atoi(buf
+ 11);
278 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
279 gid
= atoi(buf
+ 11);
280 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
281 mode
= atoi(buf
+ 12);
282 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
283 rdev
= atoi(buf
+ 12);
285 memset(buf
, 0, ATTR_MAX
);
291 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "w");
293 fp
= local_fopenat(map_dirfd
, name
, "w");
294 /* We can't go this far with map_dirfd not being a valid file descriptor
295 * but some versions of gcc aren't smart enough to see it.
297 if (map_dirfd
!= -1) {
298 close_preserve_errno(map_dirfd
);
306 assert(map_fd
!= -1);
307 ret
= fchmod(map_fd
, 0600);
310 if (credp
->fc_uid
!= -1) {
313 if (credp
->fc_gid
!= -1) {
316 if (credp
->fc_mode
!= (mode_t
)-1) {
317 mode
= credp
->fc_mode
;
319 if (credp
->fc_rdev
!= -1) {
320 rdev
= credp
->fc_rdev
;
324 fprintf(fp
, "virtfs.uid=%d\n", uid
);
327 fprintf(fp
, "virtfs.gid=%d\n", gid
);
330 fprintf(fp
, "virtfs.mode=%d\n", mode
);
333 fprintf(fp
, "virtfs.rdev=%d\n", rdev
);
340 static int fchmodat_nofollow(int dirfd
, const char *name
, mode_t mode
)
345 /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
346 * Unfortunately, the linux kernel doesn't implement it yet.
349 /* First, we clear non-racing symlinks out of the way. */
350 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
)) {
353 if (S_ISLNK(stbuf
.st_mode
)) {
358 fd
= openat_file(dirfd
, name
, O_RDONLY
| O_PATH_9P_UTIL
| O_NOFOLLOW
, 0);
359 #if O_PATH_9P_UTIL == 0
360 /* Fallback for systems that don't support O_PATH: we depend on the file
361 * being readable or writable.
364 /* In case the file is writable-only and isn't a directory. */
365 if (errno
== EACCES
) {
366 fd
= openat_file(dirfd
, name
, O_WRONLY
, 0);
368 if (fd
== -1 && errno
== EISDIR
) {
375 ret
= fchmod(fd
, mode
);
377 /* Access modes are ignored when O_PATH is supported. If name is a symbolic
378 * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
379 * referring to the symbolic link.
385 /* Now we handle racing symlinks. */
386 ret
= fstat(fd
, &stbuf
);
388 if (S_ISLNK(stbuf
.st_mode
)) {
392 char *proc_path
= g_strdup_printf("/proc/self/fd/%d", fd
);
393 ret
= chmod(proc_path
, mode
);
398 close_preserve_errno(fd
);
402 static int local_set_xattrat(int dirfd
, const char *path
, FsCred
*credp
)
406 if (credp
->fc_uid
!= -1) {
407 uint32_t tmp_uid
= cpu_to_le32(credp
->fc_uid
);
408 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.uid", &tmp_uid
,
414 if (credp
->fc_gid
!= -1) {
415 uint32_t tmp_gid
= cpu_to_le32(credp
->fc_gid
);
416 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.gid", &tmp_gid
,
422 if (credp
->fc_mode
!= (mode_t
)-1) {
423 uint32_t tmp_mode
= cpu_to_le32(credp
->fc_mode
);
424 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.mode", &tmp_mode
,
430 if (credp
->fc_rdev
!= -1) {
431 uint64_t tmp_rdev
= cpu_to_le64(credp
->fc_rdev
);
432 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.rdev", &tmp_rdev
,
441 static int local_set_cred_passthrough(FsContext
*fs_ctx
, int dirfd
,
442 const char *name
, FsCred
*credp
)
444 if (fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
445 AT_SYMLINK_NOFOLLOW
) < 0) {
447 * If we fail to change ownership and if we are
448 * using security model none. Ignore the error
450 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
455 return fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
& 07777);
458 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
459 char *buf
, size_t bufsz
)
463 if ((fs_ctx
->export_flags
& V9FS_SM_MAPPED
) ||
464 (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
467 fd
= local_open_nofollow(fs_ctx
, fs_path
->data
, O_RDONLY
, 0);
472 tsize
= read(fd
, (void *)buf
, bufsz
);
473 } while (tsize
== -1 && errno
== EINTR
);
474 close_preserve_errno(fd
);
475 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
476 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
477 char *dirpath
= g_path_get_dirname(fs_path
->data
);
478 char *name
= g_path_get_basename(fs_path
->data
);
481 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
486 tsize
= readlinkat(dirfd
, name
, buf
, bufsz
);
487 close_preserve_errno(dirfd
);
495 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
497 return close(fs
->fd
);
500 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
502 return closedir(fs
->dir
.stream
);
505 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
506 int flags
, V9fsFidOpenState
*fs
)
510 fd
= local_open_nofollow(ctx
, fs_path
->data
, flags
, 0);
518 static int local_opendir(FsContext
*ctx
,
519 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
524 dirfd
= local_opendir_nofollow(ctx
, fs_path
->data
);
529 stream
= fdopendir(dirfd
);
534 fs
->dir
.stream
= stream
;
538 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
540 rewinddir(fs
->dir
.stream
);
543 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
545 return telldir(fs
->dir
.stream
);
548 static bool local_is_mapped_file_metadata(FsContext
*fs_ctx
, const char *name
)
551 !strcmp(name
, VIRTFS_META_DIR
) || !strcmp(name
, VIRTFS_META_ROOT_FILE
);
554 static struct dirent
*local_readdir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
556 struct dirent
*entry
;
559 entry
= readdir(fs
->dir
.stream
);
564 if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
565 entry
->d_type
= DT_UNKNOWN
;
566 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
567 if (local_is_mapped_file_metadata(ctx
, entry
->d_name
)) {
568 /* skip the meta data */
571 entry
->d_type
= DT_UNKNOWN
;
577 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
579 seekdir(fs
->dir
.stream
, off
);
582 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
583 const struct iovec
*iov
,
584 int iovcnt
, off_t offset
)
587 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
589 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
593 return readv(fs
->fd
, iov
, iovcnt
);
598 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
599 const struct iovec
*iov
,
600 int iovcnt
, off_t offset
)
604 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
606 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
610 ret
= writev(fs
->fd
, iov
, iovcnt
);
613 #ifdef CONFIG_SYNC_FILE_RANGE
614 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
616 * Initiate a writeback. This is not a data integrity sync.
617 * We want to ensure that we don't leave dirty pages in the cache
618 * after write when writeout=immediate is sepcified.
620 sync_file_range(fs
->fd
, offset
, ret
,
621 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
627 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
629 char *dirpath
= g_path_get_dirname(fs_path
->data
);
630 char *name
= g_path_get_basename(fs_path
->data
);
634 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
639 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
640 ret
= local_set_xattrat(dirfd
, name
, credp
);
641 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
642 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
643 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
644 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
645 ret
= fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
);
647 close_preserve_errno(dirfd
);
655 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
656 const char *name
, FsCred
*credp
)
661 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
662 local_is_mapped_file_metadata(fs_ctx
, name
)) {
667 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
672 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
673 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
674 err
= mknodat(dirfd
, name
, fs_ctx
->fmode
| S_IFREG
, 0);
679 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
680 err
= local_set_xattrat(dirfd
, name
, credp
);
682 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
687 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
688 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
689 err
= mknodat(dirfd
, name
, credp
->fc_mode
, credp
->fc_rdev
);
693 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
701 unlinkat_preserve_errno(dirfd
, name
, 0);
703 close_preserve_errno(dirfd
);
707 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
708 const char *name
, FsCred
*credp
)
713 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
714 local_is_mapped_file_metadata(fs_ctx
, name
)) {
719 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
724 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
725 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
726 err
= mkdirat(dirfd
, name
, fs_ctx
->dmode
);
730 credp
->fc_mode
= credp
->fc_mode
| S_IFDIR
;
732 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
733 err
= local_set_xattrat(dirfd
, name
, credp
);
735 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
740 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
741 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
742 err
= mkdirat(dirfd
, name
, credp
->fc_mode
);
746 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
754 unlinkat_preserve_errno(dirfd
, name
, AT_REMOVEDIR
);
756 close_preserve_errno(dirfd
);
760 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
761 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
765 if (fid_type
== P9_FID_DIR
) {
766 fd
= dirfd(fs
->dir
.stream
);
771 err
= fstat(fd
, stbuf
);
775 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
776 /* Actual credentials are part of extended attrs */
782 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
783 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
785 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
786 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
788 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
789 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
791 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
792 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
794 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
801 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
802 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
808 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
809 local_is_mapped_file_metadata(fs_ctx
, name
)) {
815 * Mark all the open to not follow symlinks
819 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
824 /* Determine the security model */
825 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
826 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
827 fd
= openat_file(dirfd
, name
, flags
, fs_ctx
->fmode
);
831 credp
->fc_mode
= credp
->fc_mode
| S_IFREG
;
832 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
833 /* Set cleint credentials in xattr */
834 err
= local_set_xattrat(dirfd
, name
, credp
);
836 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
841 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
842 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
843 fd
= openat_file(dirfd
, name
, flags
, credp
->fc_mode
);
847 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
857 unlinkat_preserve_errno(dirfd
, name
,
858 flags
& O_DIRECTORY
? AT_REMOVEDIR
: 0);
859 close_preserve_errno(fd
);
861 close_preserve_errno(dirfd
);
866 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
867 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
872 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
873 local_is_mapped_file_metadata(fs_ctx
, name
)) {
878 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
883 /* Determine the security model */
884 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
885 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
887 ssize_t oldpath_size
, write_size
;
889 fd
= openat_file(dirfd
, name
, O_CREAT
| O_EXCL
| O_RDWR
,
894 /* Write the oldpath (target) to the file. */
895 oldpath_size
= strlen(oldpath
);
897 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
898 } while (write_size
== -1 && errno
== EINTR
);
899 close_preserve_errno(fd
);
901 if (write_size
!= oldpath_size
) {
904 /* Set cleint credentials in symlink's xattr */
905 credp
->fc_mode
= credp
->fc_mode
| S_IFLNK
;
907 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
908 err
= local_set_xattrat(dirfd
, name
, credp
);
910 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
915 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
916 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
917 err
= symlinkat(oldpath
, dirfd
, name
);
921 err
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
922 AT_SYMLINK_NOFOLLOW
);
925 * If we fail to change ownership and if we are
926 * using security model none. Ignore the error
928 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
938 unlinkat_preserve_errno(dirfd
, name
, 0);
940 close_preserve_errno(dirfd
);
944 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
945 V9fsPath
*dirpath
, const char *name
)
947 char *odirpath
= g_path_get_dirname(oldpath
->data
);
948 char *oname
= g_path_get_basename(oldpath
->data
);
952 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
953 local_is_mapped_file_metadata(ctx
, name
)) {
958 odirfd
= local_opendir_nofollow(ctx
, odirpath
);
963 ndirfd
= local_opendir_nofollow(ctx
, dirpath
->data
);
965 close_preserve_errno(odirfd
);
969 ret
= linkat(odirfd
, oname
, ndirfd
, name
, 0);
974 /* now link the virtfs_metadata files */
975 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
976 int omap_dirfd
, nmap_dirfd
;
978 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
979 if (ret
< 0 && errno
!= EEXIST
) {
983 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
984 if (omap_dirfd
== -1) {
988 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
989 if (nmap_dirfd
== -1) {
990 close_preserve_errno(omap_dirfd
);
994 ret
= linkat(omap_dirfd
, oname
, nmap_dirfd
, name
, 0);
995 close_preserve_errno(nmap_dirfd
);
996 close_preserve_errno(omap_dirfd
);
997 if (ret
< 0 && errno
!= ENOENT
) {
1008 unlinkat_preserve_errno(ndirfd
, name
, 0);
1010 close_preserve_errno(ndirfd
);
1011 close_preserve_errno(odirfd
);
1018 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
1022 fd
= local_open_nofollow(ctx
, fs_path
->data
, O_WRONLY
, 0);
1026 ret
= ftruncate(fd
, size
);
1027 close_preserve_errno(fd
);
1031 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
1033 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1034 char *name
= g_path_get_basename(fs_path
->data
);
1038 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
1043 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
1044 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
1045 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
1046 ret
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
1047 AT_SYMLINK_NOFOLLOW
);
1048 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
1049 ret
= local_set_xattrat(dirfd
, name
, credp
);
1050 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1051 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
1054 close_preserve_errno(dirfd
);
1061 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
1062 const struct timespec
*buf
)
1064 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1065 char *name
= g_path_get_basename(fs_path
->data
);
1066 int dirfd
, ret
= -1;
1068 dirfd
= local_opendir_nofollow(s
, dirpath
);
1073 ret
= utimensat(dirfd
, name
, buf
, AT_SYMLINK_NOFOLLOW
);
1074 close_preserve_errno(dirfd
);
1081 static int local_unlinkat_common(FsContext
*ctx
, int dirfd
, const char *name
,
1086 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1089 /* We need to remove the metadata as well:
1090 * - the metadata directory if we're removing a directory
1091 * - the metadata file in the parent's metadata directory
1093 * If any of these are missing (ie, ENOENT) then we're probably
1094 * trying to remove something that wasn't created in mapped-file
1095 * mode. We just ignore the error.
1097 if (flags
== AT_REMOVEDIR
) {
1100 fd
= openat_dir(dirfd
, name
);
1104 ret
= unlinkat(fd
, VIRTFS_META_DIR
, AT_REMOVEDIR
);
1105 close_preserve_errno(fd
);
1106 if (ret
< 0 && errno
!= ENOENT
) {
1110 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
1111 if (map_dirfd
!= -1) {
1112 ret
= unlinkat(map_dirfd
, name
, 0);
1113 close_preserve_errno(map_dirfd
);
1114 if (ret
< 0 && errno
!= ENOENT
) {
1117 } else if (errno
!= ENOENT
) {
1122 return unlinkat(dirfd
, name
, flags
);
1125 static int local_remove(FsContext
*ctx
, const char *path
)
1128 char *dirpath
= g_path_get_dirname(path
);
1129 char *name
= g_path_get_basename(path
);
1134 dirfd
= local_opendir_nofollow(ctx
, dirpath
);
1139 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
) < 0) {
1143 if (S_ISDIR(stbuf
.st_mode
)) {
1144 flags
|= AT_REMOVEDIR
;
1147 err
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1149 close_preserve_errno(dirfd
);
1156 static int local_fsync(FsContext
*ctx
, int fid_type
,
1157 V9fsFidOpenState
*fs
, int datasync
)
1161 if (fid_type
== P9_FID_DIR
) {
1162 fd
= dirfd(fs
->dir
.stream
);
1168 return qemu_fdatasync(fd
);
1174 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
1178 fd
= local_open_nofollow(s
, fs_path
->data
, O_RDONLY
, 0);
1182 ret
= fstatfs(fd
, stbuf
);
1183 close_preserve_errno(fd
);
1187 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1188 const char *name
, void *value
, size_t size
)
1190 char *path
= fs_path
->data
;
1192 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
1195 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1196 void *value
, size_t size
)
1198 char *path
= fs_path
->data
;
1200 return v9fs_list_xattr(ctx
, path
, value
, size
);
1203 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
1204 void *value
, size_t size
, int flags
)
1206 char *path
= fs_path
->data
;
1208 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
1211 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1214 char *path
= fs_path
->data
;
1216 return v9fs_remove_xattr(ctx
, path
, name
);
1219 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
1220 const char *name
, V9fsPath
*target
)
1222 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1223 local_is_mapped_file_metadata(ctx
, name
)) {
1229 if (!strcmp(name
, ".")) {
1230 /* "." relative to "foo/bar" is "foo/bar" */
1231 v9fs_path_copy(target
, dir_path
);
1232 } else if (!strcmp(name
, "..")) {
1233 if (!strcmp(dir_path
->data
, ".")) {
1234 /* ".." relative to the root is "." */
1235 v9fs_path_sprintf(target
, ".");
1237 char *tmp
= g_path_get_dirname(dir_path
->data
);
1238 /* Symbolic links are resolved by the client. We can assume
1239 * that ".." relative to "foo/bar" is equivalent to "foo"
1241 v9fs_path_sprintf(target
, "%s", tmp
);
1245 assert(!strchr(name
, '/'));
1246 v9fs_path_sprintf(target
, "%s/%s", dir_path
->data
, name
);
1248 } else if (!strcmp(name
, "/") || !strcmp(name
, ".") ||
1249 !strcmp(name
, "..")) {
1250 /* This is the root fid */
1251 v9fs_path_sprintf(target
, ".");
1253 assert(!strchr(name
, '/'));
1254 v9fs_path_sprintf(target
, "./%s", name
);
1259 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
1260 const char *old_name
, V9fsPath
*newdir
,
1261 const char *new_name
)
1266 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1267 (local_is_mapped_file_metadata(ctx
, old_name
) ||
1268 local_is_mapped_file_metadata(ctx
, new_name
))) {
1273 odirfd
= local_opendir_nofollow(ctx
, olddir
->data
);
1278 ndirfd
= local_opendir_nofollow(ctx
, newdir
->data
);
1280 close_preserve_errno(odirfd
);
1284 ret
= renameat(odirfd
, old_name
, ndirfd
, new_name
);
1289 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1290 int omap_dirfd
, nmap_dirfd
;
1292 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
1293 if (ret
< 0 && errno
!= EEXIST
) {
1294 goto err_undo_rename
;
1297 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
1298 if (omap_dirfd
== -1) {
1302 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
1303 if (nmap_dirfd
== -1) {
1304 close_preserve_errno(omap_dirfd
);
1308 /* rename the .virtfs_metadata files */
1309 ret
= renameat(omap_dirfd
, old_name
, nmap_dirfd
, new_name
);
1310 close_preserve_errno(nmap_dirfd
);
1311 close_preserve_errno(omap_dirfd
);
1312 if (ret
< 0 && errno
!= ENOENT
) {
1313 goto err_undo_rename
;
1323 renameat_preserve_errno(ndirfd
, new_name
, odirfd
, old_name
);
1325 close_preserve_errno(ndirfd
);
1326 close_preserve_errno(odirfd
);
1330 static void v9fs_path_init_dirname(V9fsPath
*path
, const char *str
)
1332 path
->data
= g_path_get_dirname(str
);
1333 path
->size
= strlen(path
->data
) + 1;
1336 static int local_rename(FsContext
*ctx
, const char *oldpath
,
1337 const char *newpath
)
1340 char *oname
= g_path_get_basename(oldpath
);
1341 char *nname
= g_path_get_basename(newpath
);
1342 V9fsPath olddir
, newdir
;
1344 v9fs_path_init_dirname(&olddir
, oldpath
);
1345 v9fs_path_init_dirname(&newdir
, newpath
);
1347 err
= local_renameat(ctx
, &olddir
, oname
, &newdir
, nname
);
1349 v9fs_path_free(&newdir
);
1350 v9fs_path_free(&olddir
);
1357 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
1358 const char *name
, int flags
)
1363 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1364 local_is_mapped_file_metadata(ctx
, name
)) {
1369 dirfd
= local_opendir_nofollow(ctx
, dir
->data
);
1374 ret
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1375 close_preserve_errno(dirfd
);
1379 #ifdef FS_IOC_GETVERSION
1380 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
1381 mode_t st_mode
, uint64_t *st_gen
)
1384 V9fsFidOpenState fid_open
;
1387 * Do not try to open special files like device nodes, fifos etc
1388 * We can get fd for regular files and directories only
1390 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
1394 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
1398 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
1399 local_close(ctx
, &fid_open
);
1404 static int local_ioc_getversion_init(FsContext
*ctx
, LocalData
*data
, Error
**errp
)
1406 #ifdef FS_IOC_GETVERSION
1407 struct statfs stbuf
;
1410 * use ioc_getversion only if the ioctl is definied
1412 if (fstatfs(data
->mountfd
, &stbuf
) < 0) {
1413 error_setg_errno(errp
, errno
,
1414 "failed to stat file system at '%s'", ctx
->fs_root
);
1417 switch (stbuf
.f_type
) {
1418 case EXT2_SUPER_MAGIC
:
1419 case BTRFS_SUPER_MAGIC
:
1420 case REISERFS_SUPER_MAGIC
:
1421 case XFS_SUPER_MAGIC
:
1422 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
1429 static int local_init(FsContext
*ctx
, Error
**errp
)
1431 LocalData
*data
= g_malloc(sizeof(*data
));
1433 data
->mountfd
= open(ctx
->fs_root
, O_DIRECTORY
| O_RDONLY
);
1434 if (data
->mountfd
== -1) {
1435 error_setg_errno(errp
, errno
, "failed to open '%s'", ctx
->fs_root
);
1439 if (local_ioc_getversion_init(ctx
, data
, errp
) < 0) {
1440 close(data
->mountfd
);
1444 if (ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) {
1445 ctx
->xops
= passthrough_xattr_ops
;
1446 } else if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
1447 ctx
->xops
= mapped_xattr_ops
;
1448 } else if (ctx
->export_flags
& V9FS_SM_NONE
) {
1449 ctx
->xops
= none_xattr_ops
;
1450 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1452 * xattr operation for mapped-file and passthrough
1455 ctx
->xops
= passthrough_xattr_ops
;
1457 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
1459 ctx
->private = data
;
1467 static void local_cleanup(FsContext
*ctx
)
1469 LocalData
*data
= ctx
->private;
1475 close(data
->mountfd
);
1479 static void error_append_security_model_hint(Error
*const *errp
)
1481 error_append_hint(errp
, "Valid options are: security_model="
1482 "[passthrough|mapped-xattr|mapped-file|none]\n");
1485 static int local_parse_opts(QemuOpts
*opts
, FsDriverEntry
*fse
, Error
**errp
)
1488 const char *sec_model
= qemu_opt_get(opts
, "security_model");
1489 const char *path
= qemu_opt_get(opts
, "path");
1490 const char *multidevs
= qemu_opt_get(opts
, "multidevs");
1493 error_setg(errp
, "security_model property not set");
1494 error_append_security_model_hint(errp
);
1498 if (!strcmp(sec_model
, "passthrough")) {
1499 fse
->export_flags
|= V9FS_SM_PASSTHROUGH
;
1500 } else if (!strcmp(sec_model
, "mapped") ||
1501 !strcmp(sec_model
, "mapped-xattr")) {
1502 fse
->export_flags
|= V9FS_SM_MAPPED
;
1503 } else if (!strcmp(sec_model
, "none")) {
1504 fse
->export_flags
|= V9FS_SM_NONE
;
1505 } else if (!strcmp(sec_model
, "mapped-file")) {
1506 fse
->export_flags
|= V9FS_SM_MAPPED_FILE
;
1508 error_setg(errp
, "invalid security_model property '%s'", sec_model
);
1509 error_append_security_model_hint(errp
);
1514 if (!strcmp(multidevs
, "remap")) {
1515 fse
->export_flags
&= ~V9FS_FORBID_MULTIDEVS
;
1516 fse
->export_flags
|= V9FS_REMAP_INODES
;
1517 } else if (!strcmp(multidevs
, "forbid")) {
1518 fse
->export_flags
&= ~V9FS_REMAP_INODES
;
1519 fse
->export_flags
|= V9FS_FORBID_MULTIDEVS
;
1520 } else if (!strcmp(multidevs
, "warn")) {
1521 fse
->export_flags
&= ~V9FS_FORBID_MULTIDEVS
;
1522 fse
->export_flags
&= ~V9FS_REMAP_INODES
;
1524 error_setg(errp
, "invalid multidevs property '%s'",
1526 error_append_hint(errp
, "Valid options are: multidevs="
1527 "[remap|forbid|warn]\n");
1533 error_setg(errp
, "path property not set");
1537 if (fsdev_throttle_parse_opts(opts
, &fse
->fst
, errp
)) {
1538 error_prepend(errp
, "invalid throttle configuration: ");
1542 if (fse
->export_flags
& V9FS_SM_MAPPED
||
1543 fse
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1545 qemu_opt_get_number(opts
, "fmode", SM_LOCAL_MODE_BITS
) & 0777;
1547 qemu_opt_get_number(opts
, "dmode", SM_LOCAL_DIR_MODE_BITS
) & 0777;
1549 if (qemu_opt_find(opts
, "fmode")) {
1550 error_setg(errp
, "fmode is only valid for mapped security modes");
1553 if (qemu_opt_find(opts
, "dmode")) {
1554 error_setg(errp
, "dmode is only valid for mapped security modes");
1559 fse
->path
= g_strdup(path
);
1564 FileOperations local_ops
= {
1565 .parse_opts
= local_parse_opts
,
1567 .cleanup
= local_cleanup
,
1568 .lstat
= local_lstat
,
1569 .readlink
= local_readlink
,
1570 .close
= local_close
,
1571 .closedir
= local_closedir
,
1573 .opendir
= local_opendir
,
1574 .rewinddir
= local_rewinddir
,
1575 .telldir
= local_telldir
,
1576 .readdir
= local_readdir
,
1577 .seekdir
= local_seekdir
,
1578 .preadv
= local_preadv
,
1579 .pwritev
= local_pwritev
,
1580 .chmod
= local_chmod
,
1581 .mknod
= local_mknod
,
1582 .mkdir
= local_mkdir
,
1583 .fstat
= local_fstat
,
1584 .open2
= local_open2
,
1585 .symlink
= local_symlink
,
1587 .truncate
= local_truncate
,
1588 .rename
= local_rename
,
1589 .chown
= local_chown
,
1590 .utimensat
= local_utimensat
,
1591 .remove
= local_remove
,
1592 .fsync
= local_fsync
,
1593 .statfs
= local_statfs
,
1594 .lgetxattr
= local_lgetxattr
,
1595 .llistxattr
= local_llistxattr
,
1596 .lsetxattr
= local_lsetxattr
,
1597 .lremovexattr
= local_lremovexattr
,
1598 .name_to_path
= local_name_to_path
,
1599 .renameat
= local_renameat
,
1600 .unlinkat
= local_unlinkat
,