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.
13 #include "qemu/osdep.h"
18 #include "fsdev/qemu-fsdev.h" /* local_ops */
19 #include <arpa/inet.h>
22 #include <sys/socket.h>
24 #include "qemu/xattr.h"
25 #include "qapi/error.h"
26 #include "qemu/cutils.h"
27 #include "qemu/error-report.h"
28 #include "qemu/option.h"
31 #ifdef CONFIG_LINUX_MAGIC_H
32 #include <linux/magic.h>
34 #include <sys/ioctl.h>
36 #ifndef XFS_SUPER_MAGIC
37 #define XFS_SUPER_MAGIC 0x58465342
39 #ifndef EXT2_SUPER_MAGIC
40 #define EXT2_SUPER_MAGIC 0xEF53
42 #ifndef REISERFS_SUPER_MAGIC
43 #define REISERFS_SUPER_MAGIC 0x52654973
45 #ifndef BTRFS_SUPER_MAGIC
46 #define BTRFS_SUPER_MAGIC 0x9123683E
53 int local_open_nofollow(FsContext
*fs_ctx
, const char *path
, int flags
,
56 LocalData
*data
= fs_ctx
->private;
57 int fd
= data
->mountfd
;
59 while (*path
&& fd
!= -1) {
64 /* Only relative paths without consecutive slashes */
67 head
= g_strdup(path
);
68 c
= qemu_strchrnul(path
, '/');
70 /* Intermediate path element */
73 next_fd
= openat_dir(fd
, head
);
75 /* Rightmost path element */
76 next_fd
= openat_file(fd
, head
, flags
, mode
);
80 if (fd
!= data
->mountfd
) {
81 close_preserve_errno(fd
);
86 assert(fd
!= data
->mountfd
);
90 int local_opendir_nofollow(FsContext
*fs_ctx
, const char *path
)
92 return local_open_nofollow(fs_ctx
, path
, O_DIRECTORY
| O_RDONLY
, 0);
95 static void renameat_preserve_errno(int odirfd
, const char *opath
, int ndirfd
,
99 renameat(odirfd
, opath
, ndirfd
, npath
);
103 static void unlinkat_preserve_errno(int dirfd
, const char *path
, int flags
)
106 unlinkat(dirfd
, path
, flags
);
110 #define VIRTFS_META_DIR ".virtfs_metadata"
111 #define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root"
113 static FILE *local_fopenat(int dirfd
, const char *name
, const char *mode
)
119 * only supports two modes
121 if (mode
[0] == 'r') {
123 } else if (mode
[0] == 'w') {
124 flags
= O_WRONLY
| O_TRUNC
| O_CREAT
;
125 o_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
;
129 fd
= openat_file(dirfd
, name
, flags
, o_mode
);
133 fp
= fdopen(fd
, mode
);
141 static void local_mapped_file_attr(int dirfd
, const char *name
,
148 if (strcmp(name
, ".")) {
149 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
150 if (map_dirfd
== -1) {
154 fp
= local_fopenat(map_dirfd
, name
, "r");
155 close_preserve_errno(map_dirfd
);
157 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "r");
162 memset(buf
, 0, ATTR_MAX
);
163 while (fgets(buf
, ATTR_MAX
, fp
)) {
164 if (!strncmp(buf
, "virtfs.uid", 10)) {
165 stbuf
->st_uid
= atoi(buf
+11);
166 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
167 stbuf
->st_gid
= atoi(buf
+11);
168 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
169 stbuf
->st_mode
= atoi(buf
+12);
170 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
171 stbuf
->st_rdev
= atoi(buf
+12);
173 memset(buf
, 0, ATTR_MAX
);
178 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
181 char *dirpath
= g_path_get_dirname(fs_path
->data
);
182 char *name
= g_path_get_basename(fs_path
->data
);
185 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
190 err
= fstatat(dirfd
, name
, stbuf
, AT_SYMLINK_NOFOLLOW
);
194 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
195 /* Actual credentials are part of extended attrs */
201 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.uid", &tmp_uid
,
202 sizeof(uid_t
)) > 0) {
203 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
205 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.gid", &tmp_gid
,
206 sizeof(gid_t
)) > 0) {
207 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
209 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.mode", &tmp_mode
,
210 sizeof(mode_t
)) > 0) {
211 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
213 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.rdev", &tmp_dev
,
214 sizeof(dev_t
)) > 0) {
215 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
217 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
218 local_mapped_file_attr(dirfd
, name
, stbuf
);
222 close_preserve_errno(dirfd
);
229 static int local_set_mapped_file_attrat(int dirfd
, const char *name
,
235 int uid
= -1, gid
= -1, mode
= -1, rdev
= -1;
236 int map_dirfd
= -1, map_fd
;
237 bool is_root
= !strcmp(name
, ".");
240 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "r");
242 if (errno
== ENOENT
) {
243 goto update_map_file
;
249 ret
= mkdirat(dirfd
, VIRTFS_META_DIR
, 0700);
250 if (ret
< 0 && errno
!= EEXIST
) {
254 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
255 if (map_dirfd
== -1) {
259 fp
= local_fopenat(map_dirfd
, name
, "r");
261 if (errno
== ENOENT
) {
262 goto update_map_file
;
264 close_preserve_errno(map_dirfd
);
269 memset(buf
, 0, ATTR_MAX
);
270 while (fgets(buf
, ATTR_MAX
, fp
)) {
271 if (!strncmp(buf
, "virtfs.uid", 10)) {
272 uid
= atoi(buf
+ 11);
273 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
274 gid
= atoi(buf
+ 11);
275 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
276 mode
= atoi(buf
+ 12);
277 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
278 rdev
= atoi(buf
+ 12);
280 memset(buf
, 0, ATTR_MAX
);
286 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "w");
288 fp
= local_fopenat(map_dirfd
, name
, "w");
289 /* We can't go this far with map_dirfd not being a valid file descriptor
290 * but some versions of gcc aren't smart enough to see it.
292 if (map_dirfd
!= -1) {
293 close_preserve_errno(map_dirfd
);
301 assert(map_fd
!= -1);
302 ret
= fchmod(map_fd
, 0600);
305 if (credp
->fc_uid
!= -1) {
308 if (credp
->fc_gid
!= -1) {
311 if (credp
->fc_mode
!= (mode_t
)-1) {
312 mode
= credp
->fc_mode
;
314 if (credp
->fc_rdev
!= -1) {
315 rdev
= credp
->fc_rdev
;
319 fprintf(fp
, "virtfs.uid=%d\n", uid
);
322 fprintf(fp
, "virtfs.gid=%d\n", gid
);
325 fprintf(fp
, "virtfs.mode=%d\n", mode
);
328 fprintf(fp
, "virtfs.rdev=%d\n", rdev
);
335 static int fchmodat_nofollow(int dirfd
, const char *name
, mode_t mode
)
340 /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
341 * Unfortunately, the linux kernel doesn't implement it yet.
344 /* First, we clear non-racing symlinks out of the way. */
345 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
)) {
348 if (S_ISLNK(stbuf
.st_mode
)) {
353 fd
= openat_file(dirfd
, name
, O_RDONLY
| O_PATH_9P_UTIL
| O_NOFOLLOW
, 0);
354 #if O_PATH_9P_UTIL == 0
355 /* Fallback for systems that don't support O_PATH: we depend on the file
356 * being readable or writable.
359 /* In case the file is writable-only and isn't a directory. */
360 if (errno
== EACCES
) {
361 fd
= openat_file(dirfd
, name
, O_WRONLY
, 0);
363 if (fd
== -1 && errno
== EISDIR
) {
370 ret
= fchmod(fd
, mode
);
372 /* Access modes are ignored when O_PATH is supported. If name is a symbolic
373 * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
374 * referring to the symbolic link.
380 /* Now we handle racing symlinks. */
381 ret
= fstat(fd
, &stbuf
);
383 if (S_ISLNK(stbuf
.st_mode
)) {
387 char *proc_path
= g_strdup_printf("/proc/self/fd/%d", fd
);
388 ret
= chmod(proc_path
, mode
);
393 close_preserve_errno(fd
);
397 static int local_set_xattrat(int dirfd
, const char *path
, FsCred
*credp
)
401 if (credp
->fc_uid
!= -1) {
402 uint32_t tmp_uid
= cpu_to_le32(credp
->fc_uid
);
403 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.uid", &tmp_uid
,
409 if (credp
->fc_gid
!= -1) {
410 uint32_t tmp_gid
= cpu_to_le32(credp
->fc_gid
);
411 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.gid", &tmp_gid
,
417 if (credp
->fc_mode
!= (mode_t
)-1) {
418 uint32_t tmp_mode
= cpu_to_le32(credp
->fc_mode
);
419 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.mode", &tmp_mode
,
425 if (credp
->fc_rdev
!= -1) {
426 uint64_t tmp_rdev
= cpu_to_le64(credp
->fc_rdev
);
427 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.rdev", &tmp_rdev
,
436 static int local_set_cred_passthrough(FsContext
*fs_ctx
, int dirfd
,
437 const char *name
, FsCred
*credp
)
439 if (fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
440 AT_SYMLINK_NOFOLLOW
) < 0) {
442 * If we fail to change ownership and if we are
443 * using security model none. Ignore the error
445 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
450 return fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
& 07777);
453 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
454 char *buf
, size_t bufsz
)
458 if ((fs_ctx
->export_flags
& V9FS_SM_MAPPED
) ||
459 (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
462 fd
= local_open_nofollow(fs_ctx
, fs_path
->data
, O_RDONLY
, 0);
467 tsize
= read(fd
, (void *)buf
, bufsz
);
468 } while (tsize
== -1 && errno
== EINTR
);
469 close_preserve_errno(fd
);
470 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
471 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
472 char *dirpath
= g_path_get_dirname(fs_path
->data
);
473 char *name
= g_path_get_basename(fs_path
->data
);
476 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
481 tsize
= readlinkat(dirfd
, name
, buf
, bufsz
);
482 close_preserve_errno(dirfd
);
490 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
492 return close(fs
->fd
);
495 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
497 return closedir(fs
->dir
.stream
);
500 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
501 int flags
, V9fsFidOpenState
*fs
)
505 fd
= local_open_nofollow(ctx
, fs_path
->data
, flags
, 0);
513 static int local_opendir(FsContext
*ctx
,
514 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
519 dirfd
= local_opendir_nofollow(ctx
, fs_path
->data
);
524 stream
= fdopendir(dirfd
);
529 fs
->dir
.stream
= stream
;
533 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
535 rewinddir(fs
->dir
.stream
);
538 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
540 return telldir(fs
->dir
.stream
);
543 static bool local_is_mapped_file_metadata(FsContext
*fs_ctx
, const char *name
)
546 !strcmp(name
, VIRTFS_META_DIR
) || !strcmp(name
, VIRTFS_META_ROOT_FILE
);
549 static struct dirent
*local_readdir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
551 struct dirent
*entry
;
554 entry
= readdir(fs
->dir
.stream
);
559 if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
560 entry
->d_type
= DT_UNKNOWN
;
561 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
562 if (local_is_mapped_file_metadata(ctx
, entry
->d_name
)) {
563 /* skip the meta data */
566 entry
->d_type
= DT_UNKNOWN
;
572 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
574 seekdir(fs
->dir
.stream
, off
);
577 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
578 const struct iovec
*iov
,
579 int iovcnt
, off_t offset
)
582 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
584 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
588 return readv(fs
->fd
, iov
, iovcnt
);
593 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
594 const struct iovec
*iov
,
595 int iovcnt
, off_t offset
)
599 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
601 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
605 ret
= writev(fs
->fd
, iov
, iovcnt
);
608 #ifdef CONFIG_SYNC_FILE_RANGE
609 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
611 * Initiate a writeback. This is not a data integrity sync.
612 * We want to ensure that we don't leave dirty pages in the cache
613 * after write when writeout=immediate is sepcified.
615 sync_file_range(fs
->fd
, offset
, ret
,
616 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
622 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
624 char *dirpath
= g_path_get_dirname(fs_path
->data
);
625 char *name
= g_path_get_basename(fs_path
->data
);
629 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
634 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
635 ret
= local_set_xattrat(dirfd
, name
, credp
);
636 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
637 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
638 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
639 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
640 ret
= fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
);
642 close_preserve_errno(dirfd
);
650 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
651 const char *name
, FsCred
*credp
)
656 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
657 local_is_mapped_file_metadata(fs_ctx
, name
)) {
662 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
667 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
668 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
669 err
= mknodat(dirfd
, name
, fs_ctx
->fmode
| S_IFREG
, 0);
674 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
675 err
= local_set_xattrat(dirfd
, name
, credp
);
677 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
682 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
683 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
684 err
= mknodat(dirfd
, name
, credp
->fc_mode
, credp
->fc_rdev
);
688 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
696 unlinkat_preserve_errno(dirfd
, name
, 0);
698 close_preserve_errno(dirfd
);
702 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
703 const char *name
, FsCred
*credp
)
708 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
709 local_is_mapped_file_metadata(fs_ctx
, name
)) {
714 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
719 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
720 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
721 err
= mkdirat(dirfd
, name
, fs_ctx
->dmode
);
725 credp
->fc_mode
= credp
->fc_mode
| S_IFDIR
;
727 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
728 err
= local_set_xattrat(dirfd
, name
, credp
);
730 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
735 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
736 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
737 err
= mkdirat(dirfd
, name
, credp
->fc_mode
);
741 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
749 unlinkat_preserve_errno(dirfd
, name
, AT_REMOVEDIR
);
751 close_preserve_errno(dirfd
);
755 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
756 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
760 if (fid_type
== P9_FID_DIR
) {
761 fd
= dirfd(fs
->dir
.stream
);
766 err
= fstat(fd
, stbuf
);
770 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
771 /* Actual credentials are part of extended attrs */
777 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
778 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
780 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
781 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
783 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
784 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
786 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
787 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
789 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
796 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
797 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
803 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
804 local_is_mapped_file_metadata(fs_ctx
, name
)) {
810 * Mark all the open to not follow symlinks
814 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
819 /* Determine the security model */
820 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
821 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
822 fd
= openat_file(dirfd
, name
, flags
, fs_ctx
->fmode
);
826 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
827 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
828 /* Set cleint credentials in xattr */
829 err
= local_set_xattrat(dirfd
, name
, credp
);
831 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
836 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
837 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
838 fd
= openat_file(dirfd
, name
, flags
, credp
->fc_mode
);
842 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
852 unlinkat_preserve_errno(dirfd
, name
,
853 flags
& O_DIRECTORY
? AT_REMOVEDIR
: 0);
854 close_preserve_errno(fd
);
856 close_preserve_errno(dirfd
);
861 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
862 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
867 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
868 local_is_mapped_file_metadata(fs_ctx
, name
)) {
873 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
878 /* Determine the security model */
879 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
880 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
882 ssize_t oldpath_size
, write_size
;
884 fd
= openat_file(dirfd
, name
, O_CREAT
| O_EXCL
| O_RDWR
,
889 /* Write the oldpath (target) to the file. */
890 oldpath_size
= strlen(oldpath
);
892 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
893 } while (write_size
== -1 && errno
== EINTR
);
894 close_preserve_errno(fd
);
896 if (write_size
!= oldpath_size
) {
899 /* Set cleint credentials in symlink's xattr */
900 credp
->fc_mode
= credp
->fc_mode
| S_IFLNK
;
902 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
903 err
= local_set_xattrat(dirfd
, name
, credp
);
905 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
910 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
911 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
912 err
= symlinkat(oldpath
, dirfd
, name
);
916 err
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
917 AT_SYMLINK_NOFOLLOW
);
920 * If we fail to change ownership and if we are
921 * using security model none. Ignore the error
923 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
933 unlinkat_preserve_errno(dirfd
, name
, 0);
935 close_preserve_errno(dirfd
);
939 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
940 V9fsPath
*dirpath
, const char *name
)
942 char *odirpath
= g_path_get_dirname(oldpath
->data
);
943 char *oname
= g_path_get_basename(oldpath
->data
);
947 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
948 local_is_mapped_file_metadata(ctx
, name
)) {
953 odirfd
= local_opendir_nofollow(ctx
, odirpath
);
958 ndirfd
= local_opendir_nofollow(ctx
, dirpath
->data
);
960 close_preserve_errno(odirfd
);
964 ret
= linkat(odirfd
, oname
, ndirfd
, name
, 0);
969 /* now link the virtfs_metadata files */
970 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
971 int omap_dirfd
, nmap_dirfd
;
973 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
974 if (ret
< 0 && errno
!= EEXIST
) {
978 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
979 if (omap_dirfd
== -1) {
983 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
984 if (nmap_dirfd
== -1) {
985 close_preserve_errno(omap_dirfd
);
989 ret
= linkat(omap_dirfd
, oname
, nmap_dirfd
, name
, 0);
990 close_preserve_errno(nmap_dirfd
);
991 close_preserve_errno(omap_dirfd
);
992 if (ret
< 0 && errno
!= ENOENT
) {
1003 unlinkat_preserve_errno(ndirfd
, name
, 0);
1005 close_preserve_errno(ndirfd
);
1006 close_preserve_errno(odirfd
);
1013 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
1017 fd
= local_open_nofollow(ctx
, fs_path
->data
, O_WRONLY
, 0);
1021 ret
= ftruncate(fd
, size
);
1022 close_preserve_errno(fd
);
1026 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
1028 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1029 char *name
= g_path_get_basename(fs_path
->data
);
1033 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
1038 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
1039 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
1040 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
1041 ret
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
1042 AT_SYMLINK_NOFOLLOW
);
1043 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
1044 ret
= local_set_xattrat(dirfd
, name
, credp
);
1045 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1046 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
1049 close_preserve_errno(dirfd
);
1056 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
1057 const struct timespec
*buf
)
1059 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1060 char *name
= g_path_get_basename(fs_path
->data
);
1061 int dirfd
, ret
= -1;
1063 dirfd
= local_opendir_nofollow(s
, dirpath
);
1068 ret
= utimensat(dirfd
, name
, buf
, AT_SYMLINK_NOFOLLOW
);
1069 close_preserve_errno(dirfd
);
1076 static int local_unlinkat_common(FsContext
*ctx
, int dirfd
, const char *name
,
1081 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1084 /* We need to remove the metadata as well:
1085 * - the metadata directory if we're removing a directory
1086 * - the metadata file in the parent's metadata directory
1088 * If any of these are missing (ie, ENOENT) then we're probably
1089 * trying to remove something that wasn't created in mapped-file
1090 * mode. We just ignore the error.
1092 if (flags
== AT_REMOVEDIR
) {
1095 fd
= openat_dir(dirfd
, name
);
1099 ret
= unlinkat(fd
, VIRTFS_META_DIR
, AT_REMOVEDIR
);
1100 close_preserve_errno(fd
);
1101 if (ret
< 0 && errno
!= ENOENT
) {
1105 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
1106 if (map_dirfd
!= -1) {
1107 ret
= unlinkat(map_dirfd
, name
, 0);
1108 close_preserve_errno(map_dirfd
);
1109 if (ret
< 0 && errno
!= ENOENT
) {
1112 } else if (errno
!= ENOENT
) {
1117 ret
= unlinkat(dirfd
, name
, flags
);
1122 static int local_remove(FsContext
*ctx
, const char *path
)
1125 char *dirpath
= g_path_get_dirname(path
);
1126 char *name
= g_path_get_basename(path
);
1131 dirfd
= local_opendir_nofollow(ctx
, dirpath
);
1136 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
) < 0) {
1140 if (S_ISDIR(stbuf
.st_mode
)) {
1141 flags
|= AT_REMOVEDIR
;
1144 err
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1146 close_preserve_errno(dirfd
);
1153 static int local_fsync(FsContext
*ctx
, int fid_type
,
1154 V9fsFidOpenState
*fs
, int datasync
)
1158 if (fid_type
== P9_FID_DIR
) {
1159 fd
= dirfd(fs
->dir
.stream
);
1165 return qemu_fdatasync(fd
);
1171 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
1175 fd
= local_open_nofollow(s
, fs_path
->data
, O_RDONLY
, 0);
1179 ret
= fstatfs(fd
, stbuf
);
1180 close_preserve_errno(fd
);
1184 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1185 const char *name
, void *value
, size_t size
)
1187 char *path
= fs_path
->data
;
1189 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
1192 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1193 void *value
, size_t size
)
1195 char *path
= fs_path
->data
;
1197 return v9fs_list_xattr(ctx
, path
, value
, size
);
1200 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
1201 void *value
, size_t size
, int flags
)
1203 char *path
= fs_path
->data
;
1205 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
1208 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1211 char *path
= fs_path
->data
;
1213 return v9fs_remove_xattr(ctx
, path
, name
);
1216 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
1217 const char *name
, V9fsPath
*target
)
1219 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1220 local_is_mapped_file_metadata(ctx
, name
)) {
1226 if (!strcmp(name
, ".")) {
1227 /* "." relative to "foo/bar" is "foo/bar" */
1228 v9fs_path_copy(target
, dir_path
);
1229 } else if (!strcmp(name
, "..")) {
1230 if (!strcmp(dir_path
->data
, ".")) {
1231 /* ".." relative to the root is "." */
1232 v9fs_path_sprintf(target
, ".");
1234 char *tmp
= g_path_get_dirname(dir_path
->data
);
1235 /* Symbolic links are resolved by the client. We can assume
1236 * that ".." relative to "foo/bar" is equivalent to "foo"
1238 v9fs_path_sprintf(target
, "%s", tmp
);
1242 assert(!strchr(name
, '/'));
1243 v9fs_path_sprintf(target
, "%s/%s", dir_path
->data
, name
);
1245 } else if (!strcmp(name
, "/") || !strcmp(name
, ".") ||
1246 !strcmp(name
, "..")) {
1247 /* This is the root fid */
1248 v9fs_path_sprintf(target
, ".");
1250 assert(!strchr(name
, '/'));
1251 v9fs_path_sprintf(target
, "./%s", name
);
1256 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
1257 const char *old_name
, V9fsPath
*newdir
,
1258 const char *new_name
)
1263 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1264 (local_is_mapped_file_metadata(ctx
, old_name
) ||
1265 local_is_mapped_file_metadata(ctx
, new_name
))) {
1270 odirfd
= local_opendir_nofollow(ctx
, olddir
->data
);
1275 ndirfd
= local_opendir_nofollow(ctx
, newdir
->data
);
1277 close_preserve_errno(odirfd
);
1281 ret
= renameat(odirfd
, old_name
, ndirfd
, new_name
);
1286 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1287 int omap_dirfd
, nmap_dirfd
;
1289 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
1290 if (ret
< 0 && errno
!= EEXIST
) {
1291 goto err_undo_rename
;
1294 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
1295 if (omap_dirfd
== -1) {
1299 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
1300 if (nmap_dirfd
== -1) {
1301 close_preserve_errno(omap_dirfd
);
1305 /* rename the .virtfs_metadata files */
1306 ret
= renameat(omap_dirfd
, old_name
, nmap_dirfd
, new_name
);
1307 close_preserve_errno(nmap_dirfd
);
1308 close_preserve_errno(omap_dirfd
);
1309 if (ret
< 0 && errno
!= ENOENT
) {
1310 goto err_undo_rename
;
1320 renameat_preserve_errno(ndirfd
, new_name
, odirfd
, old_name
);
1322 close_preserve_errno(ndirfd
);
1323 close_preserve_errno(odirfd
);
1327 static void v9fs_path_init_dirname(V9fsPath
*path
, const char *str
)
1329 path
->data
= g_path_get_dirname(str
);
1330 path
->size
= strlen(path
->data
) + 1;
1333 static int local_rename(FsContext
*ctx
, const char *oldpath
,
1334 const char *newpath
)
1337 char *oname
= g_path_get_basename(oldpath
);
1338 char *nname
= g_path_get_basename(newpath
);
1339 V9fsPath olddir
, newdir
;
1341 v9fs_path_init_dirname(&olddir
, oldpath
);
1342 v9fs_path_init_dirname(&newdir
, newpath
);
1344 err
= local_renameat(ctx
, &olddir
, oname
, &newdir
, nname
);
1346 v9fs_path_free(&newdir
);
1347 v9fs_path_free(&olddir
);
1354 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
1355 const char *name
, int flags
)
1360 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1361 local_is_mapped_file_metadata(ctx
, name
)) {
1366 dirfd
= local_opendir_nofollow(ctx
, dir
->data
);
1371 ret
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1372 close_preserve_errno(dirfd
);
1376 #ifdef FS_IOC_GETVERSION
1377 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
1378 mode_t st_mode
, uint64_t *st_gen
)
1381 V9fsFidOpenState fid_open
;
1384 * Do not try to open special files like device nodes, fifos etc
1385 * We can get fd for regular files and directories only
1387 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
1391 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
1395 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
1396 local_close(ctx
, &fid_open
);
1401 static int local_ioc_getversion_init(FsContext
*ctx
, LocalData
*data
, Error
**errp
)
1403 #ifdef FS_IOC_GETVERSION
1404 struct statfs stbuf
;
1407 * use ioc_getversion only if the ioctl is definied
1409 if (fstatfs(data
->mountfd
, &stbuf
) < 0) {
1410 error_setg_errno(errp
, errno
,
1411 "failed to stat file system at '%s'", ctx
->fs_root
);
1414 switch (stbuf
.f_type
) {
1415 case EXT2_SUPER_MAGIC
:
1416 case BTRFS_SUPER_MAGIC
:
1417 case REISERFS_SUPER_MAGIC
:
1418 case XFS_SUPER_MAGIC
:
1419 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
1426 static int local_init(FsContext
*ctx
, Error
**errp
)
1428 LocalData
*data
= g_malloc(sizeof(*data
));
1430 data
->mountfd
= open(ctx
->fs_root
, O_DIRECTORY
| O_RDONLY
);
1431 if (data
->mountfd
== -1) {
1432 error_setg_errno(errp
, errno
, "failed to open '%s'", ctx
->fs_root
);
1436 if (local_ioc_getversion_init(ctx
, data
, errp
) < 0) {
1437 close(data
->mountfd
);
1441 if (ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) {
1442 ctx
->xops
= passthrough_xattr_ops
;
1443 } else if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
1444 ctx
->xops
= mapped_xattr_ops
;
1445 } else if (ctx
->export_flags
& V9FS_SM_NONE
) {
1446 ctx
->xops
= none_xattr_ops
;
1447 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1449 * xattr operation for mapped-file and passthrough
1452 ctx
->xops
= passthrough_xattr_ops
;
1454 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
1456 ctx
->private = data
;
1464 static void local_cleanup(FsContext
*ctx
)
1466 LocalData
*data
= ctx
->private;
1472 close(data
->mountfd
);
1476 static void error_append_security_model_hint(Error
**errp
)
1478 error_append_hint(errp
, "Valid options are: security_model="
1479 "[passthrough|mapped-xattr|mapped-file|none]\n");
1482 static int local_parse_opts(QemuOpts
*opts
, FsDriverEntry
*fse
, Error
**errp
)
1484 const char *sec_model
= qemu_opt_get(opts
, "security_model");
1485 const char *path
= qemu_opt_get(opts
, "path");
1486 const char *multidevs
= qemu_opt_get(opts
, "multidevs");
1487 Error
*local_err
= NULL
;
1490 error_setg(errp
, "security_model property not set");
1491 error_append_security_model_hint(errp
);
1495 if (!strcmp(sec_model
, "passthrough")) {
1496 fse
->export_flags
|= V9FS_SM_PASSTHROUGH
;
1497 } else if (!strcmp(sec_model
, "mapped") ||
1498 !strcmp(sec_model
, "mapped-xattr")) {
1499 fse
->export_flags
|= V9FS_SM_MAPPED
;
1500 } else if (!strcmp(sec_model
, "none")) {
1501 fse
->export_flags
|= V9FS_SM_NONE
;
1502 } else if (!strcmp(sec_model
, "mapped-file")) {
1503 fse
->export_flags
|= V9FS_SM_MAPPED_FILE
;
1505 error_setg(errp
, "invalid security_model property '%s'", sec_model
);
1506 error_append_security_model_hint(errp
);
1511 if (!strcmp(multidevs
, "remap")) {
1512 fse
->export_flags
&= ~V9FS_FORBID_MULTIDEVS
;
1513 fse
->export_flags
|= V9FS_REMAP_INODES
;
1514 } else if (!strcmp(multidevs
, "forbid")) {
1515 fse
->export_flags
&= ~V9FS_REMAP_INODES
;
1516 fse
->export_flags
|= V9FS_FORBID_MULTIDEVS
;
1517 } else if (!strcmp(multidevs
, "warn")) {
1518 fse
->export_flags
&= ~V9FS_FORBID_MULTIDEVS
;
1519 fse
->export_flags
&= ~V9FS_REMAP_INODES
;
1521 error_setg(&local_err
, "invalid multidevs property '%s'",
1523 error_append_hint(&local_err
, "Valid options are: multidevs="
1524 "[remap|forbid|warn]\n");
1525 error_propagate(errp
, local_err
);
1531 error_setg(errp
, "path property not set");
1535 if (fsdev_throttle_parse_opts(opts
, &fse
->fst
, &local_err
)) {
1536 error_propagate_prepend(errp
, local_err
,
1537 "invalid throttle configuration: ");
1541 if (fse
->export_flags
& V9FS_SM_MAPPED
||
1542 fse
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1544 qemu_opt_get_number(opts
, "fmode", SM_LOCAL_MODE_BITS
) & 0777;
1546 qemu_opt_get_number(opts
, "dmode", SM_LOCAL_DIR_MODE_BITS
) & 0777;
1548 if (qemu_opt_find(opts
, "fmode")) {
1549 error_setg(errp
, "fmode is only valid for mapped security modes");
1552 if (qemu_opt_find(opts
, "dmode")) {
1553 error_setg(errp
, "dmode is only valid for mapped security modes");
1558 fse
->path
= g_strdup(path
);
1563 FileOperations local_ops
= {
1564 .parse_opts
= local_parse_opts
,
1566 .cleanup
= local_cleanup
,
1567 .lstat
= local_lstat
,
1568 .readlink
= local_readlink
,
1569 .close
= local_close
,
1570 .closedir
= local_closedir
,
1572 .opendir
= local_opendir
,
1573 .rewinddir
= local_rewinddir
,
1574 .telldir
= local_telldir
,
1575 .readdir
= local_readdir
,
1576 .seekdir
= local_seekdir
,
1577 .preadv
= local_preadv
,
1578 .pwritev
= local_pwritev
,
1579 .chmod
= local_chmod
,
1580 .mknod
= local_mknod
,
1581 .mkdir
= local_mkdir
,
1582 .fstat
= local_fstat
,
1583 .open2
= local_open2
,
1584 .symlink
= local_symlink
,
1586 .truncate
= local_truncate
,
1587 .rename
= local_rename
,
1588 .chown
= local_chown
,
1589 .utimensat
= local_utimensat
,
1590 .remove
= local_remove
,
1591 .fsync
= local_fsync
,
1592 .statfs
= local_statfs
,
1593 .lgetxattr
= local_lgetxattr
,
1594 .llistxattr
= local_llistxattr
,
1595 .lsetxattr
= local_lsetxattr
,
1596 .lremovexattr
= local_lremovexattr
,
1597 .name_to_path
= local_name_to_path
,
1598 .renameat
= local_renameat
,
1599 .unlinkat
= local_unlinkat
,