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 /* Access modes are ignored when O_PATH is supported. We try O_RDONLY and
353 * O_WRONLY for old-systems that don't support O_PATH.
355 fd
= openat_file(dirfd
, name
, O_RDONLY
| O_PATH_9P_UTIL
, 0);
356 #if O_PATH_9P_UTIL == 0
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
);
375 /* Now we handle racing symlinks. */
376 ret
= fstat(fd
, &stbuf
);
378 if (S_ISLNK(stbuf
.st_mode
)) {
382 char *proc_path
= g_strdup_printf("/proc/self/fd/%d", fd
);
383 ret
= chmod(proc_path
, mode
);
388 close_preserve_errno(fd
);
392 static int local_set_xattrat(int dirfd
, const char *path
, FsCred
*credp
)
396 if (credp
->fc_uid
!= -1) {
397 uint32_t tmp_uid
= cpu_to_le32(credp
->fc_uid
);
398 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.uid", &tmp_uid
,
404 if (credp
->fc_gid
!= -1) {
405 uint32_t tmp_gid
= cpu_to_le32(credp
->fc_gid
);
406 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.gid", &tmp_gid
,
412 if (credp
->fc_mode
!= -1) {
413 uint32_t tmp_mode
= cpu_to_le32(credp
->fc_mode
);
414 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.mode", &tmp_mode
,
420 if (credp
->fc_rdev
!= -1) {
421 uint64_t tmp_rdev
= cpu_to_le64(credp
->fc_rdev
);
422 err
= fsetxattrat_nofollow(dirfd
, path
, "user.virtfs.rdev", &tmp_rdev
,
431 static int local_set_cred_passthrough(FsContext
*fs_ctx
, int dirfd
,
432 const char *name
, FsCred
*credp
)
434 if (fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
435 AT_SYMLINK_NOFOLLOW
) < 0) {
437 * If we fail to change ownership and if we are
438 * using security model none. Ignore the error
440 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
445 return fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
& 07777);
448 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
449 char *buf
, size_t bufsz
)
453 if ((fs_ctx
->export_flags
& V9FS_SM_MAPPED
) ||
454 (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
457 fd
= local_open_nofollow(fs_ctx
, fs_path
->data
, O_RDONLY
, 0);
462 tsize
= read(fd
, (void *)buf
, bufsz
);
463 } while (tsize
== -1 && errno
== EINTR
);
464 close_preserve_errno(fd
);
465 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
466 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
467 char *dirpath
= g_path_get_dirname(fs_path
->data
);
468 char *name
= g_path_get_basename(fs_path
->data
);
471 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
476 tsize
= readlinkat(dirfd
, name
, buf
, bufsz
);
477 close_preserve_errno(dirfd
);
485 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
487 return close(fs
->fd
);
490 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
492 return closedir(fs
->dir
.stream
);
495 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
496 int flags
, V9fsFidOpenState
*fs
)
500 fd
= local_open_nofollow(ctx
, fs_path
->data
, flags
, 0);
508 static int local_opendir(FsContext
*ctx
,
509 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
514 dirfd
= local_opendir_nofollow(ctx
, fs_path
->data
);
519 stream
= fdopendir(dirfd
);
524 fs
->dir
.stream
= stream
;
528 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
530 rewinddir(fs
->dir
.stream
);
533 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
535 return telldir(fs
->dir
.stream
);
538 static bool local_is_mapped_file_metadata(FsContext
*fs_ctx
, const char *name
)
541 !strcmp(name
, VIRTFS_META_DIR
) || !strcmp(name
, VIRTFS_META_ROOT_FILE
);
544 static struct dirent
*local_readdir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
546 struct dirent
*entry
;
549 entry
= readdir(fs
->dir
.stream
);
554 if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
555 entry
->d_type
= DT_UNKNOWN
;
556 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
557 if (local_is_mapped_file_metadata(ctx
, entry
->d_name
)) {
558 /* skip the meta data */
561 entry
->d_type
= DT_UNKNOWN
;
567 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
569 seekdir(fs
->dir
.stream
, off
);
572 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
573 const struct iovec
*iov
,
574 int iovcnt
, off_t offset
)
577 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
579 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
583 return readv(fs
->fd
, iov
, iovcnt
);
588 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
589 const struct iovec
*iov
,
590 int iovcnt
, off_t offset
)
594 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
596 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
600 ret
= writev(fs
->fd
, iov
, iovcnt
);
603 #ifdef CONFIG_SYNC_FILE_RANGE
604 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
606 * Initiate a writeback. This is not a data integrity sync.
607 * We want to ensure that we don't leave dirty pages in the cache
608 * after write when writeout=immediate is sepcified.
610 sync_file_range(fs
->fd
, offset
, ret
,
611 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
617 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
619 char *dirpath
= g_path_get_dirname(fs_path
->data
);
620 char *name
= g_path_get_basename(fs_path
->data
);
624 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
629 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
630 ret
= local_set_xattrat(dirfd
, name
, credp
);
631 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
632 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
633 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
634 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
635 ret
= fchmodat_nofollow(dirfd
, name
, credp
->fc_mode
);
637 close_preserve_errno(dirfd
);
645 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
646 const char *name
, FsCred
*credp
)
651 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
652 local_is_mapped_file_metadata(fs_ctx
, name
)) {
657 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
662 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
663 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
664 err
= mknodat(dirfd
, name
, fs_ctx
->fmode
| S_IFREG
, 0);
669 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
670 err
= local_set_xattrat(dirfd
, name
, credp
);
672 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
677 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
678 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
679 err
= mknodat(dirfd
, name
, credp
->fc_mode
, credp
->fc_rdev
);
683 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
691 unlinkat_preserve_errno(dirfd
, name
, 0);
693 close_preserve_errno(dirfd
);
697 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
698 const char *name
, FsCred
*credp
)
703 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
704 local_is_mapped_file_metadata(fs_ctx
, name
)) {
709 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
714 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
715 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
716 err
= mkdirat(dirfd
, name
, fs_ctx
->dmode
);
720 credp
->fc_mode
= credp
->fc_mode
| S_IFDIR
;
722 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
723 err
= local_set_xattrat(dirfd
, name
, credp
);
725 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
730 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
731 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
732 err
= mkdirat(dirfd
, name
, credp
->fc_mode
);
736 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
744 unlinkat_preserve_errno(dirfd
, name
, AT_REMOVEDIR
);
746 close_preserve_errno(dirfd
);
750 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
751 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
755 if (fid_type
== P9_FID_DIR
) {
756 fd
= dirfd(fs
->dir
.stream
);
761 err
= fstat(fd
, stbuf
);
765 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
766 /* Actual credentials are part of extended attrs */
772 if (fgetxattr(fd
, "user.virtfs.uid", &tmp_uid
, sizeof(uid_t
)) > 0) {
773 stbuf
->st_uid
= le32_to_cpu(tmp_uid
);
775 if (fgetxattr(fd
, "user.virtfs.gid", &tmp_gid
, sizeof(gid_t
)) > 0) {
776 stbuf
->st_gid
= le32_to_cpu(tmp_gid
);
778 if (fgetxattr(fd
, "user.virtfs.mode", &tmp_mode
, sizeof(mode_t
)) > 0) {
779 stbuf
->st_mode
= le32_to_cpu(tmp_mode
);
781 if (fgetxattr(fd
, "user.virtfs.rdev", &tmp_dev
, sizeof(dev_t
)) > 0) {
782 stbuf
->st_rdev
= le64_to_cpu(tmp_dev
);
784 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
791 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
792 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
798 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
799 local_is_mapped_file_metadata(fs_ctx
, name
)) {
805 * Mark all the open to not follow symlinks
809 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
814 /* Determine the security model */
815 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
816 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
817 fd
= openat_file(dirfd
, name
, flags
, fs_ctx
->fmode
);
821 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
822 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
823 /* Set cleint credentials in xattr */
824 err
= local_set_xattrat(dirfd
, name
, credp
);
826 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
831 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
832 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
833 fd
= openat_file(dirfd
, name
, flags
, credp
->fc_mode
);
837 err
= local_set_cred_passthrough(fs_ctx
, dirfd
, name
, credp
);
847 unlinkat_preserve_errno(dirfd
, name
,
848 flags
& O_DIRECTORY
? AT_REMOVEDIR
: 0);
849 close_preserve_errno(fd
);
851 close_preserve_errno(dirfd
);
856 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
857 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
862 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
863 local_is_mapped_file_metadata(fs_ctx
, name
)) {
868 dirfd
= local_opendir_nofollow(fs_ctx
, dir_path
->data
);
873 /* Determine the security model */
874 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
||
875 fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
877 ssize_t oldpath_size
, write_size
;
879 fd
= openat_file(dirfd
, name
, O_CREAT
| O_EXCL
| O_RDWR
,
884 /* Write the oldpath (target) to the file. */
885 oldpath_size
= strlen(oldpath
);
887 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
888 } while (write_size
== -1 && errno
== EINTR
);
889 close_preserve_errno(fd
);
891 if (write_size
!= oldpath_size
) {
894 /* Set cleint credentials in symlink's xattr */
895 credp
->fc_mode
= credp
->fc_mode
| S_IFLNK
;
897 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
898 err
= local_set_xattrat(dirfd
, name
, credp
);
900 err
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
905 } else if (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
||
906 fs_ctx
->export_flags
& V9FS_SM_NONE
) {
907 err
= symlinkat(oldpath
, dirfd
, name
);
911 err
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
912 AT_SYMLINK_NOFOLLOW
);
915 * If we fail to change ownership and if we are
916 * using security model none. Ignore the error
918 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
928 unlinkat_preserve_errno(dirfd
, name
, 0);
930 close_preserve_errno(dirfd
);
934 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
935 V9fsPath
*dirpath
, const char *name
)
937 char *odirpath
= g_path_get_dirname(oldpath
->data
);
938 char *oname
= g_path_get_basename(oldpath
->data
);
942 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
943 local_is_mapped_file_metadata(ctx
, name
)) {
948 odirfd
= local_opendir_nofollow(ctx
, odirpath
);
953 ndirfd
= local_opendir_nofollow(ctx
, dirpath
->data
);
955 close_preserve_errno(odirfd
);
959 ret
= linkat(odirfd
, oname
, ndirfd
, name
, 0);
964 /* now link the virtfs_metadata files */
965 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
966 int omap_dirfd
, nmap_dirfd
;
968 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
969 if (ret
< 0 && errno
!= EEXIST
) {
973 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
974 if (omap_dirfd
== -1) {
978 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
979 if (nmap_dirfd
== -1) {
980 close_preserve_errno(omap_dirfd
);
984 ret
= linkat(omap_dirfd
, oname
, nmap_dirfd
, name
, 0);
985 close_preserve_errno(nmap_dirfd
);
986 close_preserve_errno(omap_dirfd
);
987 if (ret
< 0 && errno
!= ENOENT
) {
998 unlinkat_preserve_errno(ndirfd
, name
, 0);
1000 close_preserve_errno(ndirfd
);
1001 close_preserve_errno(odirfd
);
1008 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
1012 fd
= local_open_nofollow(ctx
, fs_path
->data
, O_WRONLY
, 0);
1016 ret
= ftruncate(fd
, size
);
1017 close_preserve_errno(fd
);
1021 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
1023 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1024 char *name
= g_path_get_basename(fs_path
->data
);
1028 dirfd
= local_opendir_nofollow(fs_ctx
, dirpath
);
1033 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
1034 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
1035 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
1036 ret
= fchownat(dirfd
, name
, credp
->fc_uid
, credp
->fc_gid
,
1037 AT_SYMLINK_NOFOLLOW
);
1038 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
1039 ret
= local_set_xattrat(dirfd
, name
, credp
);
1040 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1041 ret
= local_set_mapped_file_attrat(dirfd
, name
, credp
);
1044 close_preserve_errno(dirfd
);
1051 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
1052 const struct timespec
*buf
)
1054 char *dirpath
= g_path_get_dirname(fs_path
->data
);
1055 char *name
= g_path_get_basename(fs_path
->data
);
1056 int dirfd
, ret
= -1;
1058 dirfd
= local_opendir_nofollow(s
, dirpath
);
1063 ret
= utimensat(dirfd
, name
, buf
, AT_SYMLINK_NOFOLLOW
);
1064 close_preserve_errno(dirfd
);
1071 static int local_unlinkat_common(FsContext
*ctx
, int dirfd
, const char *name
,
1076 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1079 /* We need to remove the metadata as well:
1080 * - the metadata directory if we're removing a directory
1081 * - the metadata file in the parent's metadata directory
1083 * If any of these are missing (ie, ENOENT) then we're probably
1084 * trying to remove something that wasn't created in mapped-file
1085 * mode. We just ignore the error.
1087 if (flags
== AT_REMOVEDIR
) {
1090 fd
= openat_dir(dirfd
, name
);
1094 ret
= unlinkat(fd
, VIRTFS_META_DIR
, AT_REMOVEDIR
);
1095 close_preserve_errno(fd
);
1096 if (ret
< 0 && errno
!= ENOENT
) {
1100 map_dirfd
= openat_dir(dirfd
, VIRTFS_META_DIR
);
1101 if (map_dirfd
!= -1) {
1102 ret
= unlinkat(map_dirfd
, name
, 0);
1103 close_preserve_errno(map_dirfd
);
1104 if (ret
< 0 && errno
!= ENOENT
) {
1107 } else if (errno
!= ENOENT
) {
1112 ret
= unlinkat(dirfd
, name
, flags
);
1117 static int local_remove(FsContext
*ctx
, const char *path
)
1120 char *dirpath
= g_path_get_dirname(path
);
1121 char *name
= g_path_get_basename(path
);
1126 dirfd
= local_opendir_nofollow(ctx
, dirpath
);
1131 if (fstatat(dirfd
, name
, &stbuf
, AT_SYMLINK_NOFOLLOW
) < 0) {
1135 if (S_ISDIR(stbuf
.st_mode
)) {
1136 flags
|= AT_REMOVEDIR
;
1139 err
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1141 close_preserve_errno(dirfd
);
1148 static int local_fsync(FsContext
*ctx
, int fid_type
,
1149 V9fsFidOpenState
*fs
, int datasync
)
1153 if (fid_type
== P9_FID_DIR
) {
1154 fd
= dirfd(fs
->dir
.stream
);
1160 return qemu_fdatasync(fd
);
1166 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
1170 fd
= local_open_nofollow(s
, fs_path
->data
, O_RDONLY
, 0);
1174 ret
= fstatfs(fd
, stbuf
);
1175 close_preserve_errno(fd
);
1179 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1180 const char *name
, void *value
, size_t size
)
1182 char *path
= fs_path
->data
;
1184 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
1187 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1188 void *value
, size_t size
)
1190 char *path
= fs_path
->data
;
1192 return v9fs_list_xattr(ctx
, path
, value
, size
);
1195 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
1196 void *value
, size_t size
, int flags
)
1198 char *path
= fs_path
->data
;
1200 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
1203 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
1206 char *path
= fs_path
->data
;
1208 return v9fs_remove_xattr(ctx
, path
, name
);
1211 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
1212 const char *name
, V9fsPath
*target
)
1214 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1215 local_is_mapped_file_metadata(ctx
, name
)) {
1221 if (!strcmp(name
, ".")) {
1222 /* "." relative to "foo/bar" is "foo/bar" */
1223 v9fs_path_copy(target
, dir_path
);
1224 } else if (!strcmp(name
, "..")) {
1225 if (!strcmp(dir_path
->data
, ".")) {
1226 /* ".." relative to the root is "." */
1227 v9fs_path_sprintf(target
, ".");
1229 char *tmp
= g_path_get_dirname(dir_path
->data
);
1230 /* Symbolic links are resolved by the client. We can assume
1231 * that ".." relative to "foo/bar" is equivalent to "foo"
1233 v9fs_path_sprintf(target
, "%s", tmp
);
1237 assert(!strchr(name
, '/'));
1238 v9fs_path_sprintf(target
, "%s/%s", dir_path
->data
, name
);
1240 } else if (!strcmp(name
, "/") || !strcmp(name
, ".") ||
1241 !strcmp(name
, "..")) {
1242 /* This is the root fid */
1243 v9fs_path_sprintf(target
, ".");
1245 assert(!strchr(name
, '/'));
1246 v9fs_path_sprintf(target
, "./%s", name
);
1251 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
1252 const char *old_name
, V9fsPath
*newdir
,
1253 const char *new_name
)
1258 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1259 (local_is_mapped_file_metadata(ctx
, old_name
) ||
1260 local_is_mapped_file_metadata(ctx
, new_name
))) {
1265 odirfd
= local_opendir_nofollow(ctx
, olddir
->data
);
1270 ndirfd
= local_opendir_nofollow(ctx
, newdir
->data
);
1272 close_preserve_errno(odirfd
);
1276 ret
= renameat(odirfd
, old_name
, ndirfd
, new_name
);
1281 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1282 int omap_dirfd
, nmap_dirfd
;
1284 ret
= mkdirat(ndirfd
, VIRTFS_META_DIR
, 0700);
1285 if (ret
< 0 && errno
!= EEXIST
) {
1286 goto err_undo_rename
;
1289 omap_dirfd
= openat_dir(odirfd
, VIRTFS_META_DIR
);
1290 if (omap_dirfd
== -1) {
1294 nmap_dirfd
= openat_dir(ndirfd
, VIRTFS_META_DIR
);
1295 if (nmap_dirfd
== -1) {
1296 close_preserve_errno(omap_dirfd
);
1300 /* rename the .virtfs_metadata files */
1301 ret
= renameat(omap_dirfd
, old_name
, nmap_dirfd
, new_name
);
1302 close_preserve_errno(nmap_dirfd
);
1303 close_preserve_errno(omap_dirfd
);
1304 if (ret
< 0 && errno
!= ENOENT
) {
1305 goto err_undo_rename
;
1315 renameat_preserve_errno(ndirfd
, new_name
, odirfd
, old_name
);
1317 close_preserve_errno(ndirfd
);
1318 close_preserve_errno(odirfd
);
1322 static void v9fs_path_init_dirname(V9fsPath
*path
, const char *str
)
1324 path
->data
= g_path_get_dirname(str
);
1325 path
->size
= strlen(path
->data
) + 1;
1328 static int local_rename(FsContext
*ctx
, const char *oldpath
,
1329 const char *newpath
)
1332 char *oname
= g_path_get_basename(oldpath
);
1333 char *nname
= g_path_get_basename(newpath
);
1334 V9fsPath olddir
, newdir
;
1336 v9fs_path_init_dirname(&olddir
, oldpath
);
1337 v9fs_path_init_dirname(&newdir
, newpath
);
1339 err
= local_renameat(ctx
, &olddir
, oname
, &newdir
, nname
);
1341 v9fs_path_free(&newdir
);
1342 v9fs_path_free(&olddir
);
1349 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
1350 const char *name
, int flags
)
1355 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
&&
1356 local_is_mapped_file_metadata(ctx
, name
)) {
1361 dirfd
= local_opendir_nofollow(ctx
, dir
->data
);
1366 ret
= local_unlinkat_common(ctx
, dirfd
, name
, flags
);
1367 close_preserve_errno(dirfd
);
1371 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
1372 mode_t st_mode
, uint64_t *st_gen
)
1374 #ifdef FS_IOC_GETVERSION
1376 V9fsFidOpenState fid_open
;
1379 * Do not try to open special files like device nodes, fifos etc
1380 * We can get fd for regular files and directories only
1382 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
1386 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
1390 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
1391 local_close(ctx
, &fid_open
);
1399 static int local_init(FsContext
*ctx
)
1401 struct statfs stbuf
;
1402 LocalData
*data
= g_malloc(sizeof(*data
));
1404 data
->mountfd
= open(ctx
->fs_root
, O_DIRECTORY
| O_RDONLY
);
1405 if (data
->mountfd
== -1) {
1409 #ifdef FS_IOC_GETVERSION
1411 * use ioc_getversion only if the ioctl is definied
1413 if (fstatfs(data
->mountfd
, &stbuf
) < 0) {
1414 close_preserve_errno(data
->mountfd
);
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
;
1427 if (ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) {
1428 ctx
->xops
= passthrough_xattr_ops
;
1429 } else if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
1430 ctx
->xops
= mapped_xattr_ops
;
1431 } else if (ctx
->export_flags
& V9FS_SM_NONE
) {
1432 ctx
->xops
= none_xattr_ops
;
1433 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1435 * xattr operation for mapped-file and passthrough
1438 ctx
->xops
= passthrough_xattr_ops
;
1440 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
1442 ctx
->private = data
;
1450 static void local_cleanup(FsContext
*ctx
)
1452 LocalData
*data
= ctx
->private;
1454 close(data
->mountfd
);
1458 static int local_parse_opts(QemuOpts
*opts
, struct FsDriverEntry
*fse
)
1460 const char *sec_model
= qemu_opt_get(opts
, "security_model");
1461 const char *path
= qemu_opt_get(opts
, "path");
1465 error_report("Security model not specified, local fs needs security model");
1466 error_printf("valid options are:"
1467 "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1471 if (!strcmp(sec_model
, "passthrough")) {
1472 fse
->export_flags
|= V9FS_SM_PASSTHROUGH
;
1473 } else if (!strcmp(sec_model
, "mapped") ||
1474 !strcmp(sec_model
, "mapped-xattr")) {
1475 fse
->export_flags
|= V9FS_SM_MAPPED
;
1476 } else if (!strcmp(sec_model
, "none")) {
1477 fse
->export_flags
|= V9FS_SM_NONE
;
1478 } else if (!strcmp(sec_model
, "mapped-file")) {
1479 fse
->export_flags
|= V9FS_SM_MAPPED_FILE
;
1481 error_report("Invalid security model %s specified", sec_model
);
1482 error_printf("valid options are:"
1483 "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1488 error_report("fsdev: No path specified");
1492 fsdev_throttle_parse_opts(opts
, &fse
->fst
, &err
);
1494 error_reportf_err(err
, "Throttle configuration is not valid: ");
1498 if (fse
->export_flags
& V9FS_SM_MAPPED
||
1499 fse
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1501 qemu_opt_get_number(opts
, "fmode", SM_LOCAL_MODE_BITS
) & 0777;
1503 qemu_opt_get_number(opts
, "dmode", SM_LOCAL_DIR_MODE_BITS
) & 0777;
1505 if (qemu_opt_find(opts
, "fmode")) {
1506 error_report("fmode is only valid for mapped 9p modes");
1509 if (qemu_opt_find(opts
, "dmode")) {
1510 error_report("dmode is only valid for mapped 9p modes");
1515 fse
->path
= g_strdup(path
);
1520 FileOperations local_ops
= {
1521 .parse_opts
= local_parse_opts
,
1523 .cleanup
= local_cleanup
,
1524 .lstat
= local_lstat
,
1525 .readlink
= local_readlink
,
1526 .close
= local_close
,
1527 .closedir
= local_closedir
,
1529 .opendir
= local_opendir
,
1530 .rewinddir
= local_rewinddir
,
1531 .telldir
= local_telldir
,
1532 .readdir
= local_readdir
,
1533 .seekdir
= local_seekdir
,
1534 .preadv
= local_preadv
,
1535 .pwritev
= local_pwritev
,
1536 .chmod
= local_chmod
,
1537 .mknod
= local_mknod
,
1538 .mkdir
= local_mkdir
,
1539 .fstat
= local_fstat
,
1540 .open2
= local_open2
,
1541 .symlink
= local_symlink
,
1543 .truncate
= local_truncate
,
1544 .rename
= local_rename
,
1545 .chown
= local_chown
,
1546 .utimensat
= local_utimensat
,
1547 .remove
= local_remove
,
1548 .fsync
= local_fsync
,
1549 .statfs
= local_statfs
,
1550 .lgetxattr
= local_lgetxattr
,
1551 .llistxattr
= local_llistxattr
,
1552 .lsetxattr
= local_lsetxattr
,
1553 .lremovexattr
= local_lremovexattr
,
1554 .name_to_path
= local_name_to_path
,
1555 .renameat
= local_renameat
,
1556 .unlinkat
= local_unlinkat
,