2 * Virtio 9p Posix callback
4 * Copyright IBM, Corp. 2010
7 * Anthony Liguori <aliguori@us.ibm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
14 #include "hw/virtio/virtio.h"
15 #include "virtio-9p.h"
16 #include "virtio-9p-xattr.h"
17 #include <arpa/inet.h>
20 #include <sys/socket.h>
22 #include "qemu/xattr.h"
25 #ifdef CONFIG_LINUX_MAGIC_H
26 #include <linux/magic.h>
28 #include <sys/ioctl.h>
30 #ifndef XFS_SUPER_MAGIC
31 #define XFS_SUPER_MAGIC 0x58465342
33 #ifndef EXT2_SUPER_MAGIC
34 #define EXT2_SUPER_MAGIC 0xEF53
36 #ifndef REISERFS_SUPER_MAGIC
37 #define REISERFS_SUPER_MAGIC 0x52654973
39 #ifndef BTRFS_SUPER_MAGIC
40 #define BTRFS_SUPER_MAGIC 0x9123683E
43 #define VIRTFS_META_DIR ".virtfs_metadata"
45 static const char *local_mapped_attr_path(FsContext
*ctx
,
46 const char *path
, char *buffer
)
49 char *tmp_path
= g_strdup(path
);
50 char *base_name
= basename(tmp_path
);
52 /* NULL terminate the directory */
54 *(base_name
- 1) = '\0';
56 snprintf(buffer
, PATH_MAX
, "%s/%s/%s/%s",
57 ctx
->fs_root
, dir_name
, VIRTFS_META_DIR
, base_name
);
62 static FILE *local_fopen(const char *path
, const char *mode
)
66 int flags
= O_NOFOLLOW
;
68 * only supports two modes
72 } else if (mode
[0] == 'w') {
73 flags
|= O_WRONLY
| O_TRUNC
| O_CREAT
;
74 o_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
;
78 fd
= open(path
, flags
, o_mode
);
82 fp
= fdopen(fd
, mode
);
90 static void local_mapped_file_attr(FsContext
*ctx
, const char *path
,
95 char attr_path
[PATH_MAX
];
97 local_mapped_attr_path(ctx
, path
, attr_path
);
98 fp
= local_fopen(attr_path
, "r");
102 memset(buf
, 0, ATTR_MAX
);
103 while (fgets(buf
, ATTR_MAX
, fp
)) {
104 if (!strncmp(buf
, "virtfs.uid", 10)) {
105 stbuf
->st_uid
= atoi(buf
+11);
106 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
107 stbuf
->st_gid
= atoi(buf
+11);
108 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
109 stbuf
->st_mode
= atoi(buf
+12);
110 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
111 stbuf
->st_rdev
= atoi(buf
+12);
113 memset(buf
, 0, ATTR_MAX
);
118 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
121 char buffer
[PATH_MAX
];
122 char *path
= fs_path
->data
;
124 err
= lstat(rpath(fs_ctx
, path
, buffer
), stbuf
);
128 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
129 /* Actual credentials are part of extended attrs */
134 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.uid", &tmp_uid
,
135 sizeof(uid_t
)) > 0) {
136 stbuf
->st_uid
= tmp_uid
;
138 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.gid", &tmp_gid
,
139 sizeof(gid_t
)) > 0) {
140 stbuf
->st_gid
= tmp_gid
;
142 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.mode",
143 &tmp_mode
, sizeof(mode_t
)) > 0) {
144 stbuf
->st_mode
= tmp_mode
;
146 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.rdev", &tmp_dev
,
147 sizeof(dev_t
)) > 0) {
148 stbuf
->st_rdev
= tmp_dev
;
150 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
151 local_mapped_file_attr(fs_ctx
, path
, stbuf
);
156 static int local_create_mapped_attr_dir(FsContext
*ctx
, const char *path
)
159 char attr_dir
[PATH_MAX
];
160 char *tmp_path
= g_strdup(path
);
162 snprintf(attr_dir
, PATH_MAX
, "%s/%s/%s",
163 ctx
->fs_root
, dirname(tmp_path
), VIRTFS_META_DIR
);
165 err
= mkdir(attr_dir
, 0700);
166 if (err
< 0 && errno
== EEXIST
) {
173 static int local_set_mapped_file_attr(FsContext
*ctx
,
174 const char *path
, FsCred
*credp
)
179 char attr_path
[PATH_MAX
];
180 int uid
= -1, gid
= -1, mode
= -1, rdev
= -1;
182 fp
= local_fopen(local_mapped_attr_path(ctx
, path
, attr_path
), "r");
184 goto create_map_file
;
186 memset(buf
, 0, ATTR_MAX
);
187 while (fgets(buf
, ATTR_MAX
, fp
)) {
188 if (!strncmp(buf
, "virtfs.uid", 10)) {
190 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
192 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
194 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
197 memset(buf
, 0, ATTR_MAX
);
200 goto update_map_file
;
203 ret
= local_create_mapped_attr_dir(ctx
, path
);
209 fp
= local_fopen(attr_path
, "w");
215 if (credp
->fc_uid
!= -1) {
218 if (credp
->fc_gid
!= -1) {
221 if (credp
->fc_mode
!= -1) {
222 mode
= credp
->fc_mode
;
224 if (credp
->fc_rdev
!= -1) {
225 rdev
= credp
->fc_rdev
;
230 fprintf(fp
, "virtfs.uid=%d\n", uid
);
233 fprintf(fp
, "virtfs.gid=%d\n", gid
);
236 fprintf(fp
, "virtfs.mode=%d\n", mode
);
239 fprintf(fp
, "virtfs.rdev=%d\n", rdev
);
247 static int local_set_xattr(const char *path
, FsCred
*credp
)
251 if (credp
->fc_uid
!= -1) {
252 err
= setxattr(path
, "user.virtfs.uid", &credp
->fc_uid
, sizeof(uid_t
),
258 if (credp
->fc_gid
!= -1) {
259 err
= setxattr(path
, "user.virtfs.gid", &credp
->fc_gid
, sizeof(gid_t
),
265 if (credp
->fc_mode
!= -1) {
266 err
= setxattr(path
, "user.virtfs.mode", &credp
->fc_mode
,
272 if (credp
->fc_rdev
!= -1) {
273 err
= setxattr(path
, "user.virtfs.rdev", &credp
->fc_rdev
,
282 static int local_post_create_passthrough(FsContext
*fs_ctx
, const char *path
,
285 char buffer
[PATH_MAX
];
287 if (lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
288 credp
->fc_gid
) < 0) {
290 * If we fail to change ownership and if we are
291 * using security model none. Ignore the error
293 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
298 if (chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
& 07777) < 0) {
304 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
305 char *buf
, size_t bufsz
)
308 char buffer
[PATH_MAX
];
309 char *path
= fs_path
->data
;
311 if ((fs_ctx
->export_flags
& V9FS_SM_MAPPED
) ||
312 (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
314 fd
= open(rpath(fs_ctx
, path
, buffer
), O_RDONLY
| O_NOFOLLOW
);
319 tsize
= read(fd
, (void *)buf
, bufsz
);
320 } while (tsize
== -1 && errno
== EINTR
);
323 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
324 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
325 tsize
= readlink(rpath(fs_ctx
, path
, buffer
), buf
, bufsz
);
330 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
332 return close(fs
->fd
);
335 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
337 return closedir(fs
->dir
);
340 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
341 int flags
, V9fsFidOpenState
*fs
)
343 char buffer
[PATH_MAX
];
344 char *path
= fs_path
->data
;
346 fs
->fd
= open(rpath(ctx
, path
, buffer
), flags
| O_NOFOLLOW
);
350 static int local_opendir(FsContext
*ctx
,
351 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
353 char buffer
[PATH_MAX
];
354 char *path
= fs_path
->data
;
356 fs
->dir
= opendir(rpath(ctx
, path
, buffer
));
363 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
365 return rewinddir(fs
->dir
);
368 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
370 return telldir(fs
->dir
);
373 static int local_readdir_r(FsContext
*ctx
, V9fsFidOpenState
*fs
,
374 struct dirent
*entry
,
375 struct dirent
**result
)
380 ret
= readdir_r(fs
->dir
, entry
, result
);
381 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
382 if (!ret
&& *result
!= NULL
&&
383 !strcmp(entry
->d_name
, VIRTFS_META_DIR
)) {
384 /* skp the meta data directory */
391 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
393 return seekdir(fs
->dir
, off
);
396 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
397 const struct iovec
*iov
,
398 int iovcnt
, off_t offset
)
401 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
403 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
407 return readv(fs
->fd
, iov
, iovcnt
);
412 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
413 const struct iovec
*iov
,
414 int iovcnt
, off_t offset
)
419 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
421 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
425 ret
= writev(fs
->fd
, iov
, iovcnt
);
428 #ifdef CONFIG_SYNC_FILE_RANGE
429 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
431 * Initiate a writeback. This is not a data integrity sync.
432 * We want to ensure that we don't leave dirty pages in the cache
433 * after write when writeout=immediate is sepcified.
435 sync_file_range(fs
->fd
, offset
, ret
,
436 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
442 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
444 char buffer
[PATH_MAX
];
445 char *path
= fs_path
->data
;
447 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
448 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
449 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
450 return local_set_mapped_file_attr(fs_ctx
, path
, credp
);
451 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
452 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
453 return chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
458 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
459 const char *name
, FsCred
*credp
)
465 char buffer
[PATH_MAX
];
467 v9fs_string_init(&fullname
);
468 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
469 path
= fullname
.data
;
471 /* Determine the security model */
472 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
473 err
= mknod(rpath(fs_ctx
, path
, buffer
),
474 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
478 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
483 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
485 err
= mknod(rpath(fs_ctx
, path
, buffer
),
486 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
490 err
= local_set_mapped_file_attr(fs_ctx
, path
, credp
);
495 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
496 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
497 err
= mknod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
,
502 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
511 remove(rpath(fs_ctx
, path
, buffer
));
514 v9fs_string_free(&fullname
);
518 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
519 const char *name
, FsCred
*credp
)
525 char buffer
[PATH_MAX
];
527 v9fs_string_init(&fullname
);
528 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
529 path
= fullname
.data
;
531 /* Determine the security model */
532 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
533 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
537 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
538 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
543 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
544 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
548 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
549 err
= local_set_mapped_file_attr(fs_ctx
, path
, credp
);
554 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
555 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
556 err
= mkdir(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
560 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
569 remove(rpath(fs_ctx
, path
, buffer
));
572 v9fs_string_free(&fullname
);
576 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
577 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
581 if (fid_type
== P9_FID_DIR
) {
587 err
= fstat(fd
, stbuf
);
591 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
592 /* Actual credentials are part of extended attrs */
598 if (fgetxattr(fd
, "user.virtfs.uid",
599 &tmp_uid
, sizeof(uid_t
)) > 0) {
600 stbuf
->st_uid
= tmp_uid
;
602 if (fgetxattr(fd
, "user.virtfs.gid",
603 &tmp_gid
, sizeof(gid_t
)) > 0) {
604 stbuf
->st_gid
= tmp_gid
;
606 if (fgetxattr(fd
, "user.virtfs.mode",
607 &tmp_mode
, sizeof(mode_t
)) > 0) {
608 stbuf
->st_mode
= tmp_mode
;
610 if (fgetxattr(fd
, "user.virtfs.rdev",
611 &tmp_dev
, sizeof(dev_t
)) > 0) {
612 stbuf
->st_rdev
= tmp_dev
;
614 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
621 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
622 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
629 char buffer
[PATH_MAX
];
632 * Mark all the open to not follow symlinks
636 v9fs_string_init(&fullname
);
637 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
638 path
= fullname
.data
;
640 /* Determine the security model */
641 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
642 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
647 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
648 /* Set cleint credentials in xattr */
649 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
654 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
655 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
660 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
661 /* Set client credentials in .virtfs_metadata directory files */
662 err
= local_set_mapped_file_attr(fs_ctx
, path
, credp
);
667 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
668 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
669 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
674 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
686 remove(rpath(fs_ctx
, path
, buffer
));
689 v9fs_string_free(&fullname
);
694 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
695 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
701 char buffer
[PATH_MAX
];
703 v9fs_string_init(&fullname
);
704 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
705 newpath
= fullname
.data
;
707 /* Determine the security model */
708 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
710 ssize_t oldpath_size
, write_size
;
711 fd
= open(rpath(fs_ctx
, newpath
, buffer
),
712 O_CREAT
|O_EXCL
|O_RDWR
|O_NOFOLLOW
,
718 /* Write the oldpath (target) to the file. */
719 oldpath_size
= strlen(oldpath
);
721 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
722 } while (write_size
== -1 && errno
== EINTR
);
724 if (write_size
!= oldpath_size
) {
731 /* Set cleint credentials in symlink's xattr */
732 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
733 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
738 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
740 ssize_t oldpath_size
, write_size
;
741 fd
= open(rpath(fs_ctx
, newpath
, buffer
),
742 O_CREAT
|O_EXCL
|O_RDWR
|O_NOFOLLOW
,
748 /* Write the oldpath (target) to the file. */
749 oldpath_size
= strlen(oldpath
);
751 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
752 } while (write_size
== -1 && errno
== EINTR
);
754 if (write_size
!= oldpath_size
) {
761 /* Set cleint credentials in symlink's xattr */
762 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
763 err
= local_set_mapped_file_attr(fs_ctx
, newpath
, credp
);
768 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
769 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
770 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
774 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
778 * If we fail to change ownership and if we are
779 * using security model none. Ignore the error
781 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
791 remove(rpath(fs_ctx
, newpath
, buffer
));
794 v9fs_string_free(&fullname
);
798 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
799 V9fsPath
*dirpath
, const char *name
)
803 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
805 v9fs_string_init(&newpath
);
806 v9fs_string_sprintf(&newpath
, "%s/%s", dirpath
->data
, name
);
808 ret
= link(rpath(ctx
, oldpath
->data
, buffer
),
809 rpath(ctx
, newpath
.data
, buffer1
));
811 /* now link the virtfs_metadata files */
812 if (!ret
&& (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
813 /* Link the .virtfs_metadata files. Create the metada directory */
814 ret
= local_create_mapped_attr_dir(ctx
, newpath
.data
);
818 ret
= link(local_mapped_attr_path(ctx
, oldpath
->data
, buffer
),
819 local_mapped_attr_path(ctx
, newpath
.data
, buffer1
));
820 if (ret
< 0 && errno
!= ENOENT
) {
825 v9fs_string_free(&newpath
);
829 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
831 char buffer
[PATH_MAX
];
832 char *path
= fs_path
->data
;
834 return truncate(rpath(ctx
, path
, buffer
), size
);
837 static int local_rename(FsContext
*ctx
, const char *oldpath
,
841 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
843 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
844 err
= local_create_mapped_attr_dir(ctx
, newpath
);
848 /* rename the .virtfs_metadata files */
849 err
= rename(local_mapped_attr_path(ctx
, oldpath
, buffer
),
850 local_mapped_attr_path(ctx
, newpath
, buffer1
));
851 if (err
< 0 && errno
!= ENOENT
) {
855 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
858 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
860 char buffer
[PATH_MAX
];
861 char *path
= fs_path
->data
;
863 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
864 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
865 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
866 return lchown(rpath(fs_ctx
, path
, buffer
),
867 credp
->fc_uid
, credp
->fc_gid
);
868 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
869 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
870 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
871 return local_set_mapped_file_attr(fs_ctx
, path
, credp
);
876 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
877 const struct timespec
*buf
)
879 char buffer
[PATH_MAX
];
880 char *path
= fs_path
->data
;
882 return qemu_utimens(rpath(s
, path
, buffer
), buf
);
885 static int local_remove(FsContext
*ctx
, const char *path
)
889 char buffer
[PATH_MAX
];
891 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
892 err
= lstat(rpath(ctx
, path
, buffer
), &stbuf
);
897 * If directory remove .virtfs_metadata contained in the
900 if (S_ISDIR(stbuf
.st_mode
)) {
901 sprintf(buffer
, "%s/%s/%s", ctx
->fs_root
, path
, VIRTFS_META_DIR
);
902 err
= remove(buffer
);
903 if (err
< 0 && errno
!= ENOENT
) {
905 * We didn't had the .virtfs_metadata file. May be file created
906 * in non-mapped mode ?. Ignore ENOENT.
912 * Now remove the name from parent directory
913 * .virtfs_metadata directory
915 err
= remove(local_mapped_attr_path(ctx
, path
, buffer
));
916 if (err
< 0 && errno
!= ENOENT
) {
918 * We didn't had the .virtfs_metadata file. May be file created
919 * in non-mapped mode ?. Ignore ENOENT.
924 return remove(rpath(ctx
, path
, buffer
));
929 static int local_fsync(FsContext
*ctx
, int fid_type
,
930 V9fsFidOpenState
*fs
, int datasync
)
934 if (fid_type
== P9_FID_DIR
) {
941 return qemu_fdatasync(fd
);
947 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
949 char buffer
[PATH_MAX
];
950 char *path
= fs_path
->data
;
952 return statfs(rpath(s
, path
, buffer
), stbuf
);
955 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
956 const char *name
, void *value
, size_t size
)
958 char *path
= fs_path
->data
;
960 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
963 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
964 void *value
, size_t size
)
966 char *path
= fs_path
->data
;
968 return v9fs_list_xattr(ctx
, path
, value
, size
);
971 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
972 void *value
, size_t size
, int flags
)
974 char *path
= fs_path
->data
;
976 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
979 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
982 char *path
= fs_path
->data
;
984 return v9fs_remove_xattr(ctx
, path
, name
);
987 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
988 const char *name
, V9fsPath
*target
)
991 v9fs_string_sprintf((V9fsString
*)target
, "%s/%s",
992 dir_path
->data
, name
);
994 v9fs_string_sprintf((V9fsString
*)target
, "%s", name
);
996 /* Bump the size for including terminating NULL */
1001 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
1002 const char *old_name
, V9fsPath
*newdir
,
1003 const char *new_name
)
1006 V9fsString old_full_name
, new_full_name
;
1008 v9fs_string_init(&old_full_name
);
1009 v9fs_string_init(&new_full_name
);
1011 v9fs_string_sprintf(&old_full_name
, "%s/%s", olddir
->data
, old_name
);
1012 v9fs_string_sprintf(&new_full_name
, "%s/%s", newdir
->data
, new_name
);
1014 ret
= local_rename(ctx
, old_full_name
.data
, new_full_name
.data
);
1015 v9fs_string_free(&old_full_name
);
1016 v9fs_string_free(&new_full_name
);
1020 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
1021 const char *name
, int flags
)
1024 V9fsString fullname
;
1025 char buffer
[PATH_MAX
];
1027 v9fs_string_init(&fullname
);
1029 v9fs_string_sprintf(&fullname
, "%s/%s", dir
->data
, name
);
1030 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1031 if (flags
== AT_REMOVEDIR
) {
1033 * If directory remove .virtfs_metadata contained in the
1036 sprintf(buffer
, "%s/%s/%s", ctx
->fs_root
,
1037 fullname
.data
, VIRTFS_META_DIR
);
1038 ret
= remove(buffer
);
1039 if (ret
< 0 && errno
!= ENOENT
) {
1041 * We didn't had the .virtfs_metadata file. May be file created
1042 * in non-mapped mode ?. Ignore ENOENT.
1048 * Now remove the name from parent directory
1049 * .virtfs_metadata directory.
1051 ret
= remove(local_mapped_attr_path(ctx
, fullname
.data
, buffer
));
1052 if (ret
< 0 && errno
!= ENOENT
) {
1054 * We didn't had the .virtfs_metadata file. May be file created
1055 * in non-mapped mode ?. Ignore ENOENT.
1060 /* Remove the name finally */
1061 ret
= remove(rpath(ctx
, fullname
.data
, buffer
));
1062 v9fs_string_free(&fullname
);
1068 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
1069 mode_t st_mode
, uint64_t *st_gen
)
1071 #ifdef FS_IOC_GETVERSION
1073 V9fsFidOpenState fid_open
;
1076 * Do not try to open special files like device nodes, fifos etc
1077 * We can get fd for regular files and directories only
1079 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
1083 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
1087 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
1088 local_close(ctx
, &fid_open
);
1096 static int local_init(FsContext
*ctx
)
1099 struct statfs stbuf
;
1101 if (ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) {
1102 ctx
->xops
= passthrough_xattr_ops
;
1103 } else if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
1104 ctx
->xops
= mapped_xattr_ops
;
1105 } else if (ctx
->export_flags
& V9FS_SM_NONE
) {
1106 ctx
->xops
= none_xattr_ops
;
1107 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1109 * xattr operation for mapped-file and passthrough
1112 ctx
->xops
= passthrough_xattr_ops
;
1114 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
1115 #ifdef FS_IOC_GETVERSION
1117 * use ioc_getversion only if the iocl is definied
1119 err
= statfs(ctx
->fs_root
, &stbuf
);
1121 switch (stbuf
.f_type
) {
1122 case EXT2_SUPER_MAGIC
:
1123 case BTRFS_SUPER_MAGIC
:
1124 case REISERFS_SUPER_MAGIC
:
1125 case XFS_SUPER_MAGIC
:
1126 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
1134 static int local_parse_opts(QemuOpts
*opts
, struct FsDriverEntry
*fse
)
1136 const char *sec_model
= qemu_opt_get(opts
, "security_model");
1137 const char *path
= qemu_opt_get(opts
, "path");
1140 fprintf(stderr
, "security model not specified, "
1141 "local fs needs security model\nvalid options are:"
1142 "\tsecurity_model=[passthrough|mapped|none]\n");
1146 if (!strcmp(sec_model
, "passthrough")) {
1147 fse
->export_flags
|= V9FS_SM_PASSTHROUGH
;
1148 } else if (!strcmp(sec_model
, "mapped") ||
1149 !strcmp(sec_model
, "mapped-xattr")) {
1150 fse
->export_flags
|= V9FS_SM_MAPPED
;
1151 } else if (!strcmp(sec_model
, "none")) {
1152 fse
->export_flags
|= V9FS_SM_NONE
;
1153 } else if (!strcmp(sec_model
, "mapped-file")) {
1154 fse
->export_flags
|= V9FS_SM_MAPPED_FILE
;
1156 fprintf(stderr
, "Invalid security model %s specified, valid options are"
1157 "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
1163 fprintf(stderr
, "fsdev: No path specified.\n");
1166 fse
->path
= g_strdup(path
);
1171 FileOperations local_ops
= {
1172 .parse_opts
= local_parse_opts
,
1174 .lstat
= local_lstat
,
1175 .readlink
= local_readlink
,
1176 .close
= local_close
,
1177 .closedir
= local_closedir
,
1179 .opendir
= local_opendir
,
1180 .rewinddir
= local_rewinddir
,
1181 .telldir
= local_telldir
,
1182 .readdir_r
= local_readdir_r
,
1183 .seekdir
= local_seekdir
,
1184 .preadv
= local_preadv
,
1185 .pwritev
= local_pwritev
,
1186 .chmod
= local_chmod
,
1187 .mknod
= local_mknod
,
1188 .mkdir
= local_mkdir
,
1189 .fstat
= local_fstat
,
1190 .open2
= local_open2
,
1191 .symlink
= local_symlink
,
1193 .truncate
= local_truncate
,
1194 .rename
= local_rename
,
1195 .chown
= local_chown
,
1196 .utimensat
= local_utimensat
,
1197 .remove
= local_remove
,
1198 .fsync
= local_fsync
,
1199 .statfs
= local_statfs
,
1200 .lgetxattr
= local_lgetxattr
,
1201 .llistxattr
= local_llistxattr
,
1202 .lsetxattr
= local_lsetxattr
,
1203 .lremovexattr
= local_lremovexattr
,
1204 .name_to_path
= local_name_to_path
,
1205 .renameat
= local_renameat
,
1206 .unlinkat
= local_unlinkat
,