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 "qemu/osdep.h"
19 #include "fsdev/qemu-fsdev.h" /* local_ops */
20 #include <arpa/inet.h>
23 #include <sys/socket.h>
25 #include "qemu/xattr.h"
26 #include "qemu/cutils.h"
27 #include "qemu/error-report.h"
30 #ifdef CONFIG_LINUX_MAGIC_H
31 #include <linux/magic.h>
33 #include <sys/ioctl.h>
35 #ifndef XFS_SUPER_MAGIC
36 #define XFS_SUPER_MAGIC 0x58465342
38 #ifndef EXT2_SUPER_MAGIC
39 #define EXT2_SUPER_MAGIC 0xEF53
41 #ifndef REISERFS_SUPER_MAGIC
42 #define REISERFS_SUPER_MAGIC 0x52654973
44 #ifndef BTRFS_SUPER_MAGIC
45 #define BTRFS_SUPER_MAGIC 0x9123683E
52 int local_open_nofollow(FsContext
*fs_ctx
, const char *path
, int flags
,
55 LocalData
*data
= fs_ctx
->private;
56 int fd
= data
->mountfd
;
58 while (*path
&& fd
!= -1) {
63 /* Only relative paths without consecutive slashes */
66 head
= g_strdup(path
);
67 c
= strchrnul(path
, '/');
69 /* Intermediate path element */
72 next_fd
= openat_dir(fd
, head
);
74 /* Rightmost path element */
75 next_fd
= openat_file(fd
, head
, flags
, mode
);
79 if (fd
!= data
->mountfd
) {
80 close_preserve_errno(fd
);
85 assert(fd
!= data
->mountfd
);
89 int local_opendir_nofollow(FsContext
*fs_ctx
, const char *path
)
91 return local_open_nofollow(fs_ctx
, path
, O_DIRECTORY
| O_RDONLY
, 0);
94 static void renameat_preserve_errno(int odirfd
, const char *opath
, int ndirfd
,
98 renameat(odirfd
, opath
, ndirfd
, npath
);
102 static void unlinkat_preserve_errno(int dirfd
, const char *path
, int flags
)
105 unlinkat(dirfd
, path
, flags
);
109 #define VIRTFS_META_DIR ".virtfs_metadata"
110 #define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root"
112 static FILE *local_fopenat(int dirfd
, const char *name
, const char *mode
)
118 * only supports two modes
120 if (mode
[0] == 'r') {
122 } else if (mode
[0] == 'w') {
123 flags
= O_WRONLY
| O_TRUNC
| O_CREAT
;
124 o_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
;
128 fd
= openat_file(dirfd
, name
, flags
, o_mode
);
132 fp
= fdopen(fd
, mode
);
140 static void local_mapped_file_attr(int dirfd
, const char *name
,
147 if (strcmp(name
, ".")) {
148 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
149 if (map_dirfd
== -1) {
153 fp
= local_fopenat(map_dirfd
, name
, "r");
154 close_preserve_errno(map_dirfd
);
156 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "r");
161 memset(buf
, 0, ATTR_MAX
);
162 while (fgets(buf
, ATTR_MAX
, fp
)) {
163 if (!strncmp(buf
, "virtfs.uid", 10)) {
164 stbuf
->st_uid
= atoi(buf
+11);
165 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
166 stbuf
->st_gid
= atoi(buf
+11);
167 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
168 stbuf
->st_mode
= atoi(buf
+12);
169 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
170 stbuf
->st_rdev
= atoi(buf
+12);
172 memset(buf
, 0, ATTR_MAX
);
177 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
180 char *dirpath
= g_path_get_dirname(fs_path
->data
);
181 char *name
= g_path_get_basename(fs_path
->data
);
184 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
189 err
= fstatat(dirfd
, name
, stbuf
, AT_SYMLINK_NOFOLLOW
);
193 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
194 /* Actual credentials are part of extended attrs */
200 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.uid", &tmp_uid
,
201 sizeof(uid_t
)) > 0) {
202 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
204 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.gid", &tmp_gid
,
205 sizeof(gid_t
)) > 0) {
206 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
208 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.mode", &tmp_mode
,
209 sizeof(mode_t
)) > 0) {
210 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
212 if (fgetxattrat_nofollow(dirfd
, name
, "user.virtfs.rdev", &tmp_dev
,
213 sizeof(dev_t
)) > 0) {
214 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
216 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
217 local_mapped_file_attr(dirfd
, name
, stbuf
);
221 close_preserve_errno(dirfd
);
228 static int local_set_mapped_file_attrat(int dirfd
, const char *name
,
234 int uid
= -1, gid
= -1, mode
= -1, rdev
= -1;
235 int map_dirfd
= -1, map_fd
;
236 bool is_root
= !strcmp(name
, ".");
239 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "r");
241 if (errno
== ENOENT
) {
242 goto update_map_file
;
248 ret
= mkdirat(dirfd
, VIRTFS_META_DIR
, 0700);
249 if (ret
< 0 && errno
!= EEXIST
) {
253 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
254 if (map_dirfd
== -1) {
258 fp
= local_fopenat(map_dirfd
, name
, "r");
260 if (errno
== ENOENT
) {
261 goto update_map_file
;
263 close_preserve_errno(map_dirfd
);
268 memset(buf
, 0, ATTR_MAX
);
269 while (fgets(buf
, ATTR_MAX
, fp
)) {
270 if (!strncmp(buf
, "virtfs.uid", 10)) {
271 uid
= atoi(buf
+ 11);
272 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
273 gid
= atoi(buf
+ 11);
274 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
275 mode
= atoi(buf
+ 12);
276 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
277 rdev
= atoi(buf
+ 12);
279 memset(buf
, 0, ATTR_MAX
);
285 fp
= local_fopenat(dirfd
, VIRTFS_META_ROOT_FILE
, "w");
287 fp
= local_fopenat(map_dirfd
, name
, "w");
288 /* We can't go this far with map_dirfd not being a valid file descriptor
289 * but some versions of gcc aren't smart enough to see it.
291 if (map_dirfd
!= -1) {
292 close_preserve_errno(map_dirfd
);
300 assert(map_fd
!= -1);
301 ret
= fchmod(map_fd
, 0600);
304 if (credp
->fc_uid
!= -1) {
307 if (credp
->fc_gid
!= -1) {
310 if (credp
->fc_mode
!= -1) {
311 mode
= credp
->fc_mode
;
313 if (credp
->fc_rdev
!= -1) {
314 rdev
= credp
->fc_rdev
;
318 fprintf(fp
, "virtfs.uid=%d\n", uid
);
321 fprintf(fp
, "virtfs.gid=%d\n", gid
);
324 fprintf(fp
, "virtfs.mode=%d\n", mode
);
327 fprintf(fp
, "virtfs.rdev=%d\n", rdev
);
334 static int fchmodat_nofollow(int dirfd
, const char *name
, mode_t mode
)
339 /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
340 * Unfortunately, the linux kernel doesn't implement it yet.
343 /* First, we clear non-racing symlinks out of the way. */
344 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
)) {
347 if (S_ISLNK(stbuf
.st_mode
)) {
352 fd
= openat_file(dirfd
, name
, O_RDONLY
| O_PATH_9P_UTIL
| O_NOFOLLOW
, 0);
353 #if O_PATH_9P_UTIL == 0
354 /* Fallback for systems that don't support O_PATH: we depend on the file
355 * being readable or writable.
358 /* In case the file is writable-only and isn't a directory. */
359 if (errno
== EACCES
) {
360 fd
= openat_file(dirfd
, name
, O_WRONLY
, 0);
362 if (fd
== -1 && errno
== EISDIR
) {
369 ret
= fchmod(fd
, mode
);
371 /* Access modes are ignored when O_PATH is supported. If name is a symbolic
372 * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
373 * referring to the symbolic link.
379 /* Now we handle racing symlinks. */
380 ret
= fstat(fd
, &stbuf
);
382 if (S_ISLNK(stbuf
.st_mode
)) {
386 char *proc_path
= g_strdup_printf("/proc/self/fd/%d", fd
);
387 ret
= chmod(proc_path
, mode
);
392 close_preserve_errno(fd
);
396 static int local_set_xattrat(int dirfd
, const char *path
, FsCred
*credp
)
400 if (credp
->fc_uid
!= -1) {
401 uint32_t tmp_uid
= cpu_to_le32(credp
->fc_uid
);
402 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.uid", &tmp_uid
,
408 if (credp
->fc_gid
!= -1) {
409 uint32_t tmp_gid
= cpu_to_le32(credp
->fc_gid
);
410 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.gid", &tmp_gid
,
416 if (credp
->fc_mode
!= -1) {
417 uint32_t tmp_mode
= cpu_to_le32(credp
->fc_mode
);
418 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.mode", &tmp_mode
,
424 if (credp
->fc_rdev
!= -1) {
425 uint64_t tmp_rdev
= cpu_to_le64(credp
->fc_rdev
);
426 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.rdev", &tmp_rdev
,
435 static int local_set_cred_passthrough(FsContext
*fs_ctx
, int dirfd
,
436 const char *name
, FsCred
*credp
)
438 if (fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
439 AT_SYMLINK_NOFOLLOW
) < 0) {
441 * If we fail to change ownership and if we are
442 * using security model none. Ignore the error
444 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
449 return fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
& 07777);
452 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
453 char *buf
, size_t bufsz
)
457 if ((fs_ctx
->export_flags
& V9FS_SM_MAPPED
) ||
458 (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
461 fd
= local_open_nofollow(fs_ctx
, fs_path
->data
, O_RDONLY
, 0);
466 tsize
= read(fd
, (void *)buf
, bufsz
);
467 } while (tsize
== -1 && errno
== EINTR
);
468 close_preserve_errno(fd
);
469 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
470 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
471 char *dirpath
= g_path_get_dirname(fs_path
->data
);
472 char *name
= g_path_get_basename(fs_path
->data
);
475 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
480 tsize
= readlinkat(dirfd
, name
, buf
, bufsz
);
481 close_preserve_errno(dirfd
);
489 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
491 return close(fs
->fd
);
494 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
496 return closedir(fs
->dir
.stream
);
499 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
500 int flags
, V9fsFidOpenState
*fs
)
504 fd
= local_open_nofollow(ctx
, fs_path
->data
, flags
, 0);
512 static int local_opendir(FsContext
*ctx
,
513 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
518 dirfd
= local_opendir_nofollow(ctx
, fs_path
->data
);
523 stream
= fdopendir(dirfd
);
528 fs
->dir
.stream
= stream
;
532 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
534 rewinddir(fs
->dir
.stream
);
537 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
539 return telldir(fs
->dir
.stream
);
542 static bool local_is_mapped_file_metadata(FsContext
*fs_ctx
, const char *name
)
545 !strcmp(name
, VIRTFS_META_DIR
) || !strcmp(name
, VIRTFS_META_ROOT_FILE
);
548 static struct dirent
*local_readdir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
550 struct dirent
*entry
;
553 entry
= readdir(fs
->dir
.stream
);
558 if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
559 entry
->d_type
= DT_UNKNOWN
;
560 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
561 if (local_is_mapped_file_metadata(ctx
, entry
->d_name
)) {
562 /* skip the meta data */
565 entry
->d_type
= DT_UNKNOWN
;
571 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
573 seekdir(fs
->dir
.stream
, off
);
576 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
577 const struct iovec
*iov
,
578 int iovcnt
, off_t offset
)
581 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
583 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
587 return readv(fs
->fd
, iov
, iovcnt
);
592 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
593 const struct iovec
*iov
,
594 int iovcnt
, off_t offset
)
598 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
600 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
604 ret
= writev(fs
->fd
, iov
, iovcnt
);
607 #ifdef CONFIG_SYNC_FILE_RANGE
608 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
610 * Initiate a writeback. This is not a data integrity sync.
611 * We want to ensure that we don't leave dirty pages in the cache
612 * after write when writeout=immediate is sepcified.
614 sync_file_range(fs
->fd
, offset
, ret
,
615 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
621 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
623 char *dirpath
= g_path_get_dirname(fs_path
->data
);
624 char *name
= g_path_get_basename(fs_path
->data
);
628 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
633 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
634 ret
= local_set_xattrat(dirfd
, name
, credp
);
635 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
636 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
637 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
638 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
639 ret
= fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
);
641 close_preserve_errno(dirfd
);
649 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
650 const char *name
, FsCred
*credp
)
655 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
656 local_is_mapped_file_metadata(fs_ctx
, name
)) {
661 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
666 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
667 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
668 err
= mknodat(dirfd
, name
, fs_ctx
->fmode
| S_IFREG
, 0);
673 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
674 err
= local_set_xattrat(dirfd
, name
, credp
);
676 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
681 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
682 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
683 err
= mknodat(dirfd
, name
, credp
->fc_mode
, credp
->fc_rdev
);
687 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
695 unlinkat_preserve_errno(dirfd
, name
, 0);
697 close_preserve_errno(dirfd
);
701 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
702 const char *name
, FsCred
*credp
)
707 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
708 local_is_mapped_file_metadata(fs_ctx
, name
)) {
713 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
718 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
719 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
720 err
= mkdirat(dirfd
, name
, fs_ctx
->dmode
);
724 credp
->fc_mode
= credp
->fc_mode
| S_IFDIR
;
726 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
727 err
= local_set_xattrat(dirfd
, name
, credp
);
729 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
734 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
735 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
736 err
= mkdirat(dirfd
, name
, credp
->fc_mode
);
740 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
748 unlinkat_preserve_errno(dirfd
, name
, AT_REMOVEDIR
);
750 close_preserve_errno(dirfd
);
754 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
755 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
759 if (fid_type
== P9_FID_DIR
) {
760 fd
= dirfd(fs
->dir
.stream
);
765 err
= fstat(fd
, stbuf
);
769 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
770 /* Actual credentials are part of extended attrs */
776 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
777 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
779 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
780 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
782 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
783 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
785 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
786 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
788 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
795 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
796 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
802 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
803 local_is_mapped_file_metadata(fs_ctx
, name
)) {
809 * Mark all the open to not follow symlinks
813 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
818 /* Determine the security model */
819 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
820 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
821 fd
= openat_file(dirfd
, name
, flags
, fs_ctx
->fmode
);
825 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
826 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
827 /* Set cleint credentials in xattr */
828 err
= local_set_xattrat(dirfd
, name
, credp
);
830 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
835 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
836 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
837 fd
= openat_file(dirfd
, name
, flags
, credp
->fc_mode
);
841 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
851 unlinkat_preserve_errno(dirfd
, name
,
852 flags
& O_DIRECTORY
? AT_REMOVEDIR
: 0);
853 close_preserve_errno(fd
);
855 close_preserve_errno(dirfd
);
860 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
861 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
866 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
867 local_is_mapped_file_metadata(fs_ctx
, name
)) {
872 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
877 /* Determine the security model */
878 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
879 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
881 ssize_t oldpath_size
, write_size
;
883 fd
= openat_file(dirfd
, name
, O_CREAT
| O_EXCL
| O_RDWR
,
888 /* Write the oldpath (target) to the file. */
889 oldpath_size
= strlen(oldpath
);
891 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
892 } while (write_size
== -1 && errno
== EINTR
);
893 close_preserve_errno(fd
);
895 if (write_size
!= oldpath_size
) {
898 /* Set cleint credentials in symlink's xattr */
899 credp
->fc_mode
= credp
->fc_mode
| S_IFLNK
;
901 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
902 err
= local_set_xattrat(dirfd
, name
, credp
);
904 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
909 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
910 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
911 err
= symlinkat(oldpath
, dirfd
, name
);
915 err
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
916 AT_SYMLINK_NOFOLLOW
);
919 * If we fail to change ownership and if we are
920 * using security model none. Ignore the error
922 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
932 unlinkat_preserve_errno(dirfd
, name
, 0);
934 close_preserve_errno(dirfd
);
938 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
939 V9fsPath
*dirpath
, const char *name
)
941 char *odirpath
= g_path_get_dirname(oldpath
->data
);
942 char *oname
= g_path_get_basename(oldpath
->data
);
946 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
947 local_is_mapped_file_metadata(ctx
, name
)) {
952 odirfd
= local_opendir_nofollow(ctx
, odirpath
);
957 ndirfd
= local_opendir_nofollow(ctx
, dirpath
->data
);
959 close_preserve_errno(odirfd
);
963 ret
= linkat(odirfd
, oname
, ndirfd
, name
, 0);
968 /* now link the virtfs_metadata files */
969 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
970 int omap_dirfd
, nmap_dirfd
;
972 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
973 if (ret
< 0 && errno
!= EEXIST
) {
977 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
978 if (omap_dirfd
== -1) {
982 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
983 if (nmap_dirfd
== -1) {
984 close_preserve_errno(omap_dirfd
);
988 ret
= linkat(omap_dirfd
, oname
, nmap_dirfd
, name
, 0);
989 close_preserve_errno(nmap_dirfd
);
990 close_preserve_errno(omap_dirfd
);
991 if (ret
< 0 && errno
!= ENOENT
) {
1002 unlinkat_preserve_errno(ndirfd
, name
, 0);
1004 close_preserve_errno(ndirfd
);
1005 close_preserve_errno(odirfd
);
1012 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
1016 fd
= local_open_nofollow(ctx
, fs_path
->data
, O_WRONLY
, 0);
1020 ret
= ftruncate(fd
, size
);
1021 close_preserve_errno(fd
);
1025 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
1027 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1028 char *name
= g_path_get_basename(fs_path
->data
);
1032 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
1037 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
1038 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
1039 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
1040 ret
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
1041 AT_SYMLINK_NOFOLLOW
);
1042 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
1043 ret
= local_set_xattrat(dirfd
, name
, credp
);
1044 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1045 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
1048 close_preserve_errno(dirfd
);
1055 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
1056 const struct timespec
*buf
)
1058 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1059 char *name
= g_path_get_basename(fs_path
->data
);
1060 int dirfd
, ret
= -1;
1062 dirfd
= local_opendir_nofollow(s
, dirpath
);
1067 ret
= utimensat(dirfd
, name
, buf
, AT_SYMLINK_NOFOLLOW
);
1068 close_preserve_errno(dirfd
);
1075 static int local_unlinkat_common(FsContext
*ctx
, int dirfd
, const char *name
,
1080 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1083 /* We need to remove the metadata as well:
1084 * - the metadata directory if we're removing a directory
1085 * - the metadata file in the parent's metadata directory
1087 * If any of these are missing (ie, ENOENT) then we're probably
1088 * trying to remove something that wasn't created in mapped-file
1089 * mode. We just ignore the error.
1091 if (flags
== AT_REMOVEDIR
) {
1094 fd
= openat_dir(dirfd
, name
);
1098 ret
= unlinkat(fd
, VIRTFS_META_DIR
, AT_REMOVEDIR
);
1099 close_preserve_errno(fd
);
1100 if (ret
< 0 && errno
!= ENOENT
) {
1104 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
1105 if (map_dirfd
!= -1) {
1106 ret
= unlinkat(map_dirfd
, name
, 0);
1107 close_preserve_errno(map_dirfd
);
1108 if (ret
< 0 && errno
!= ENOENT
) {
1111 } else if (errno
!= ENOENT
) {
1116 ret
= unlinkat(dirfd
, name
, flags
);
1121 static int local_remove(FsContext
*ctx
, const char *path
)
1124 char *dirpath
= g_path_get_dirname(path
);
1125 char *name
= g_path_get_basename(path
);
1130 dirfd
= local_opendir_nofollow(ctx
, dirpath
);
1135 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
) < 0) {
1139 if (S_ISDIR(stbuf
.st_mode
)) {
1140 flags
|= AT_REMOVEDIR
;
1143 err
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1145 close_preserve_errno(dirfd
);
1152 static int local_fsync(FsContext
*ctx
, int fid_type
,
1153 V9fsFidOpenState
*fs
, int datasync
)
1157 if (fid_type
== P9_FID_DIR
) {
1158 fd
= dirfd(fs
->dir
.stream
);
1164 return qemu_fdatasync(fd
);
1170 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
1174 fd
= local_open_nofollow(s
, fs_path
->data
, O_RDONLY
, 0);
1178 ret
= fstatfs(fd
, stbuf
);
1179 close_preserve_errno(fd
);
1183 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1184 const char *name
, void *value
, size_t size
)
1186 char *path
= fs_path
->data
;
1188 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
1191 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1192 void *value
, size_t size
)
1194 char *path
= fs_path
->data
;
1196 return v9fs_list_xattr(ctx
, path
, value
, size
);
1199 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
1200 void *value
, size_t size
, int flags
)
1202 char *path
= fs_path
->data
;
1204 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
1207 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1210 char *path
= fs_path
->data
;
1212 return v9fs_remove_xattr(ctx
, path
, name
);
1215 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
1216 const char *name
, V9fsPath
*target
)
1218 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1219 local_is_mapped_file_metadata(ctx
, name
)) {
1225 if (!strcmp(name
, ".")) {
1226 /* "." relative to "foo/bar" is "foo/bar" */
1227 v9fs_path_copy(target
, dir_path
);
1228 } else if (!strcmp(name
, "..")) {
1229 if (!strcmp(dir_path
->data
, ".")) {
1230 /* ".." relative to the root is "." */
1231 v9fs_path_sprintf(target
, ".");
1233 char *tmp
= g_path_get_dirname(dir_path
->data
);
1234 /* Symbolic links are resolved by the client. We can assume
1235 * that ".." relative to "foo/bar" is equivalent to "foo"
1237 v9fs_path_sprintf(target
, "%s", tmp
);
1241 assert(!strchr(name
, '/'));
1242 v9fs_path_sprintf(target
, "%s/%s", dir_path
->data
, name
);
1244 } else if (!strcmp(name
, "/") || !strcmp(name
, ".") ||
1245 !strcmp(name
, "..")) {
1246 /* This is the root fid */
1247 v9fs_path_sprintf(target
, ".");
1249 assert(!strchr(name
, '/'));
1250 v9fs_path_sprintf(target
, "./%s", name
);
1255 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
1256 const char *old_name
, V9fsPath
*newdir
,
1257 const char *new_name
)
1262 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1263 (local_is_mapped_file_metadata(ctx
, old_name
) ||
1264 local_is_mapped_file_metadata(ctx
, new_name
))) {
1269 odirfd
= local_opendir_nofollow(ctx
, olddir
->data
);
1274 ndirfd
= local_opendir_nofollow(ctx
, newdir
->data
);
1276 close_preserve_errno(odirfd
);
1280 ret
= renameat(odirfd
, old_name
, ndirfd
, new_name
);
1285 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1286 int omap_dirfd
, nmap_dirfd
;
1288 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
1289 if (ret
< 0 && errno
!= EEXIST
) {
1290 goto err_undo_rename
;
1293 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
1294 if (omap_dirfd
== -1) {
1298 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
1299 if (nmap_dirfd
== -1) {
1300 close_preserve_errno(omap_dirfd
);
1304 /* rename the .virtfs_metadata files */
1305 ret
= renameat(omap_dirfd
, old_name
, nmap_dirfd
, new_name
);
1306 close_preserve_errno(nmap_dirfd
);
1307 close_preserve_errno(omap_dirfd
);
1308 if (ret
< 0 && errno
!= ENOENT
) {
1309 goto err_undo_rename
;
1319 renameat_preserve_errno(ndirfd
, new_name
, odirfd
, old_name
);
1321 close_preserve_errno(ndirfd
);
1322 close_preserve_errno(odirfd
);
1326 static void v9fs_path_init_dirname(V9fsPath
*path
, const char *str
)
1328 path
->data
= g_path_get_dirname(str
);
1329 path
->size
= strlen(path
->data
) + 1;
1332 static int local_rename(FsContext
*ctx
, const char *oldpath
,
1333 const char *newpath
)
1336 char *oname
= g_path_get_basename(oldpath
);
1337 char *nname
= g_path_get_basename(newpath
);
1338 V9fsPath olddir
, newdir
;
1340 v9fs_path_init_dirname(&olddir
, oldpath
);
1341 v9fs_path_init_dirname(&newdir
, newpath
);
1343 err
= local_renameat(ctx
, &olddir
, oname
, &newdir
, nname
);
1345 v9fs_path_free(&newdir
);
1346 v9fs_path_free(&olddir
);
1353 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
1354 const char *name
, int flags
)
1359 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1360 local_is_mapped_file_metadata(ctx
, name
)) {
1365 dirfd
= local_opendir_nofollow(ctx
, dir
->data
);
1370 ret
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1371 close_preserve_errno(dirfd
);
1375 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
1376 mode_t st_mode
, uint64_t *st_gen
)
1378 #ifdef FS_IOC_GETVERSION
1380 V9fsFidOpenState fid_open
;
1383 * Do not try to open special files like device nodes, fifos etc
1384 * We can get fd for regular files and directories only
1386 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
1390 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
1394 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
1395 local_close(ctx
, &fid_open
);
1403 static int local_init(FsContext
*ctx
)
1405 struct statfs stbuf
;
1406 LocalData
*data
= g_malloc(sizeof(*data
));
1408 data
->mountfd
= open(ctx
->fs_root
, O_DIRECTORY
| O_RDONLY
);
1409 if (data
->mountfd
== -1) {
1413 #ifdef FS_IOC_GETVERSION
1415 * use ioc_getversion only if the ioctl is definied
1417 if (fstatfs(data
->mountfd
, &stbuf
) < 0) {
1418 close_preserve_errno(data
->mountfd
);
1421 switch (stbuf
.f_type
) {
1422 case EXT2_SUPER_MAGIC
:
1423 case BTRFS_SUPER_MAGIC
:
1424 case REISERFS_SUPER_MAGIC
:
1425 case XFS_SUPER_MAGIC
:
1426 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
1431 if (ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) {
1432 ctx
->xops
= passthrough_xattr_ops
;
1433 } else if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
1434 ctx
->xops
= mapped_xattr_ops
;
1435 } else if (ctx
->export_flags
& V9FS_SM_NONE
) {
1436 ctx
->xops
= none_xattr_ops
;
1437 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1439 * xattr operation for mapped-file and passthrough
1442 ctx
->xops
= passthrough_xattr_ops
;
1444 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
1446 ctx
->private = data
;
1454 static void local_cleanup(FsContext
*ctx
)
1456 LocalData
*data
= ctx
->private;
1458 close(data
->mountfd
);
1462 static int local_parse_opts(QemuOpts
*opts
, struct FsDriverEntry
*fse
)
1464 const char *sec_model
= qemu_opt_get(opts
, "security_model");
1465 const char *path
= qemu_opt_get(opts
, "path");
1469 error_report("Security model not specified, local fs needs security model");
1470 error_printf("valid options are:"
1471 "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1475 if (!strcmp(sec_model
, "passthrough")) {
1476 fse
->export_flags
|= V9FS_SM_PASSTHROUGH
;
1477 } else if (!strcmp(sec_model
, "mapped") ||
1478 !strcmp(sec_model
, "mapped-xattr")) {
1479 fse
->export_flags
|= V9FS_SM_MAPPED
;
1480 } else if (!strcmp(sec_model
, "none")) {
1481 fse
->export_flags
|= V9FS_SM_NONE
;
1482 } else if (!strcmp(sec_model
, "mapped-file")) {
1483 fse
->export_flags
|= V9FS_SM_MAPPED_FILE
;
1485 error_report("Invalid security model %s specified", sec_model
);
1486 error_printf("valid options are:"
1487 "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1492 error_report("fsdev: No path specified");
1496 fsdev_throttle_parse_opts(opts
, &fse
->fst
, &err
);
1498 error_reportf_err(err
, "Throttle configuration is not valid: ");
1502 if (fse
->export_flags
& V9FS_SM_MAPPED
||
1503 fse
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1505 qemu_opt_get_number(opts
, "fmode", SM_LOCAL_MODE_BITS
) & 0777;
1507 qemu_opt_get_number(opts
, "dmode", SM_LOCAL_DIR_MODE_BITS
) & 0777;
1509 if (qemu_opt_find(opts
, "fmode")) {
1510 error_report("fmode is only valid for mapped 9p modes");
1513 if (qemu_opt_find(opts
, "dmode")) {
1514 error_report("dmode is only valid for mapped 9p modes");
1519 fse
->path
= g_strdup(path
);
1524 FileOperations local_ops
= {
1525 .parse_opts
= local_parse_opts
,
1527 .cleanup
= local_cleanup
,
1528 .lstat
= local_lstat
,
1529 .readlink
= local_readlink
,
1530 .close
= local_close
,
1531 .closedir
= local_closedir
,
1533 .opendir
= local_opendir
,
1534 .rewinddir
= local_rewinddir
,
1535 .telldir
= local_telldir
,
1536 .readdir
= local_readdir
,
1537 .seekdir
= local_seekdir
,
1538 .preadv
= local_preadv
,
1539 .pwritev
= local_pwritev
,
1540 .chmod
= local_chmod
,
1541 .mknod
= local_mknod
,
1542 .mkdir
= local_mkdir
,
1543 .fstat
= local_fstat
,
1544 .open2
= local_open2
,
1545 .symlink
= local_symlink
,
1547 .truncate
= local_truncate
,
1548 .rename
= local_rename
,
1549 .chown
= local_chown
,
1550 .utimensat
= local_utimensat
,
1551 .remove
= local_remove
,
1552 .fsync
= local_fsync
,
1553 .statfs
= local_statfs
,
1554 .lgetxattr
= local_lgetxattr
,
1555 .llistxattr
= local_llistxattr
,
1556 .lsetxattr
= local_lsetxattr
,
1557 .lremovexattr
= local_lremovexattr
,
1558 .name_to_path
= local_name_to_path
,
1559 .renameat
= local_renameat
,
1560 .unlinkat
= local_unlinkat
,