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.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
);
63 static void local_mapped_file_attr(FsContext
*ctx
, const char *path
,
68 char attr_path
[PATH_MAX
];
70 local_mapped_attr_path(ctx
, path
, attr_path
);
71 fp
= fopen(attr_path
, "r");
75 memset(buf
, 0, ATTR_MAX
);
76 while (fgets(buf
, ATTR_MAX
, fp
)) {
77 if (!strncmp(buf
, "virtfs.uid", 10)) {
78 stbuf
->st_uid
= atoi(buf
+11);
79 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
80 stbuf
->st_gid
= atoi(buf
+11);
81 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
82 stbuf
->st_mode
= atoi(buf
+12);
83 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
84 stbuf
->st_rdev
= atoi(buf
+12);
86 memset(buf
, 0, ATTR_MAX
);
91 static int local_lstat(FsContext
*fs_ctx
, V9fsPath
*fs_path
, struct stat
*stbuf
)
94 char buffer
[PATH_MAX
];
95 char *path
= fs_path
->data
;
97 err
= lstat(rpath(fs_ctx
, path
, buffer
), stbuf
);
101 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
102 /* Actual credentials are part of extended attrs */
107 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.uid", &tmp_uid
,
108 sizeof(uid_t
)) > 0) {
109 stbuf
->st_uid
= tmp_uid
;
111 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.gid", &tmp_gid
,
112 sizeof(gid_t
)) > 0) {
113 stbuf
->st_gid
= tmp_gid
;
115 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.mode",
116 &tmp_mode
, sizeof(mode_t
)) > 0) {
117 stbuf
->st_mode
= tmp_mode
;
119 if (getxattr(rpath(fs_ctx
, path
, buffer
), "user.virtfs.rdev", &tmp_dev
,
120 sizeof(dev_t
)) > 0) {
121 stbuf
->st_rdev
= tmp_dev
;
123 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
124 local_mapped_file_attr(fs_ctx
, path
, stbuf
);
129 static int local_create_mapped_attr_dir(FsContext
*ctx
, const char *path
)
132 char attr_dir
[PATH_MAX
];
133 char *tmp_path
= g_strdup(path
);
135 snprintf(attr_dir
, PATH_MAX
, "%s/%s/%s",
136 ctx
->fs_root
, dirname(tmp_path
), VIRTFS_META_DIR
);
138 err
= mkdir(attr_dir
, 0700);
139 if (err
< 0 && errno
== EEXIST
) {
146 static int local_set_mapped_file_attr(FsContext
*ctx
,
147 const char *path
, FsCred
*credp
)
152 char attr_path
[PATH_MAX
];
153 int uid
= -1, gid
= -1, mode
= -1, rdev
= -1;
155 fp
= fopen(local_mapped_attr_path(ctx
, path
, attr_path
), "r");
157 goto create_map_file
;
159 memset(buf
, 0, ATTR_MAX
);
160 while (fgets(buf
, ATTR_MAX
, fp
)) {
161 if (!strncmp(buf
, "virtfs.uid", 10)) {
163 } else if (!strncmp(buf
, "virtfs.gid", 10)) {
165 } else if (!strncmp(buf
, "virtfs.mode", 11)) {
167 } else if (!strncmp(buf
, "virtfs.rdev", 11)) {
170 memset(buf
, 0, ATTR_MAX
);
173 goto update_map_file
;
176 ret
= local_create_mapped_attr_dir(ctx
, path
);
182 fp
= fopen(attr_path
, "w");
188 if (credp
->fc_uid
!= -1) {
191 if (credp
->fc_gid
!= -1) {
194 if (credp
->fc_mode
!= -1) {
195 mode
= credp
->fc_mode
;
197 if (credp
->fc_rdev
!= -1) {
198 rdev
= credp
->fc_rdev
;
203 fprintf(fp
, "virtfs.uid=%d\n", uid
);
206 fprintf(fp
, "virtfs.gid=%d\n", gid
);
209 fprintf(fp
, "virtfs.mode=%d\n", mode
);
212 fprintf(fp
, "virtfs.rdev=%d\n", rdev
);
220 static int local_set_xattr(const char *path
, FsCred
*credp
)
224 if (credp
->fc_uid
!= -1) {
225 err
= setxattr(path
, "user.virtfs.uid", &credp
->fc_uid
, sizeof(uid_t
),
231 if (credp
->fc_gid
!= -1) {
232 err
= setxattr(path
, "user.virtfs.gid", &credp
->fc_gid
, sizeof(gid_t
),
238 if (credp
->fc_mode
!= -1) {
239 err
= setxattr(path
, "user.virtfs.mode", &credp
->fc_mode
,
245 if (credp
->fc_rdev
!= -1) {
246 err
= setxattr(path
, "user.virtfs.rdev", &credp
->fc_rdev
,
255 static int local_post_create_passthrough(FsContext
*fs_ctx
, const char *path
,
258 char buffer
[PATH_MAX
];
260 if (lchown(rpath(fs_ctx
, path
, buffer
), credp
->fc_uid
,
261 credp
->fc_gid
) < 0) {
263 * If we fail to change ownership and if we are
264 * using security model none. Ignore the error
266 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
271 if (chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
& 07777) < 0) {
277 static ssize_t
local_readlink(FsContext
*fs_ctx
, V9fsPath
*fs_path
,
278 char *buf
, size_t bufsz
)
281 char buffer
[PATH_MAX
];
282 char *path
= fs_path
->data
;
284 if ((fs_ctx
->export_flags
& V9FS_SM_MAPPED
) ||
285 (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
287 fd
= open(rpath(fs_ctx
, path
, buffer
), O_RDONLY
);
292 tsize
= read(fd
, (void *)buf
, bufsz
);
293 } while (tsize
== -1 && errno
== EINTR
);
296 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
297 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
298 tsize
= readlink(rpath(fs_ctx
, path
, buffer
), buf
, bufsz
);
303 static int local_close(FsContext
*ctx
, V9fsFidOpenState
*fs
)
305 return close(fs
->fd
);
308 static int local_closedir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
310 return closedir(fs
->dir
);
313 static int local_open(FsContext
*ctx
, V9fsPath
*fs_path
,
314 int flags
, V9fsFidOpenState
*fs
)
316 char buffer
[PATH_MAX
];
317 char *path
= fs_path
->data
;
319 fs
->fd
= open(rpath(ctx
, path
, buffer
), flags
);
323 static int local_opendir(FsContext
*ctx
,
324 V9fsPath
*fs_path
, V9fsFidOpenState
*fs
)
326 char buffer
[PATH_MAX
];
327 char *path
= fs_path
->data
;
329 fs
->dir
= opendir(rpath(ctx
, path
, buffer
));
336 static void local_rewinddir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
338 return rewinddir(fs
->dir
);
341 static off_t
local_telldir(FsContext
*ctx
, V9fsFidOpenState
*fs
)
343 return telldir(fs
->dir
);
346 static int local_readdir_r(FsContext
*ctx
, V9fsFidOpenState
*fs
,
347 struct dirent
*entry
,
348 struct dirent
**result
)
353 ret
= readdir_r(fs
->dir
, entry
, result
);
354 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
355 if (!ret
&& *result
!= NULL
&&
356 !strcmp(entry
->d_name
, VIRTFS_META_DIR
)) {
357 /* skp the meta data directory */
364 static void local_seekdir(FsContext
*ctx
, V9fsFidOpenState
*fs
, off_t off
)
366 return seekdir(fs
->dir
, off
);
369 static ssize_t
local_preadv(FsContext
*ctx
, V9fsFidOpenState
*fs
,
370 const struct iovec
*iov
,
371 int iovcnt
, off_t offset
)
374 return preadv(fs
->fd
, iov
, iovcnt
, offset
);
376 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
380 return readv(fs
->fd
, iov
, iovcnt
);
385 static ssize_t
local_pwritev(FsContext
*ctx
, V9fsFidOpenState
*fs
,
386 const struct iovec
*iov
,
387 int iovcnt
, off_t offset
)
392 ret
= pwritev(fs
->fd
, iov
, iovcnt
, offset
);
394 int err
= lseek(fs
->fd
, offset
, SEEK_SET
);
398 ret
= writev(fs
->fd
, iov
, iovcnt
);
401 #ifdef CONFIG_SYNC_FILE_RANGE
402 if (ret
> 0 && ctx
->export_flags
& V9FS_IMMEDIATE_WRITEOUT
) {
404 * Initiate a writeback. This is not a data integrity sync.
405 * We want to ensure that we don't leave dirty pages in the cache
406 * after write when writeout=immediate is sepcified.
408 sync_file_range(fs
->fd
, offset
, ret
,
409 SYNC_FILE_RANGE_WAIT_BEFORE
| SYNC_FILE_RANGE_WRITE
);
415 static int local_chmod(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
417 char buffer
[PATH_MAX
];
418 char *path
= fs_path
->data
;
420 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
421 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
422 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
423 return local_set_mapped_file_attr(fs_ctx
, path
, credp
);
424 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
425 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
426 return chmod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
431 static int local_mknod(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
432 const char *name
, FsCred
*credp
)
438 char buffer
[PATH_MAX
];
440 v9fs_string_init(&fullname
);
441 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
442 path
= fullname
.data
;
444 /* Determine the security model */
445 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
446 err
= mknod(rpath(fs_ctx
, path
, buffer
),
447 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
451 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
456 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
458 err
= mknod(rpath(fs_ctx
, path
, buffer
),
459 SM_LOCAL_MODE_BITS
|S_IFREG
, 0);
463 err
= local_set_mapped_file_attr(fs_ctx
, path
, credp
);
468 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
469 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
470 err
= mknod(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
,
475 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
484 remove(rpath(fs_ctx
, path
, buffer
));
487 v9fs_string_free(&fullname
);
491 static int local_mkdir(FsContext
*fs_ctx
, V9fsPath
*dir_path
,
492 const char *name
, FsCred
*credp
)
498 char buffer
[PATH_MAX
];
500 v9fs_string_init(&fullname
);
501 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
502 path
= fullname
.data
;
504 /* Determine the security model */
505 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
506 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
510 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
511 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
516 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
517 err
= mkdir(rpath(fs_ctx
, path
, buffer
), SM_LOCAL_DIR_MODE_BITS
);
521 credp
->fc_mode
= credp
->fc_mode
|S_IFDIR
;
522 err
= local_set_mapped_file_attr(fs_ctx
, path
, credp
);
527 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
528 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
529 err
= mkdir(rpath(fs_ctx
, path
, buffer
), credp
->fc_mode
);
533 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
542 remove(rpath(fs_ctx
, path
, buffer
));
545 v9fs_string_free(&fullname
);
549 static int local_fstat(FsContext
*fs_ctx
, int fid_type
,
550 V9fsFidOpenState
*fs
, struct stat
*stbuf
)
554 if (fid_type
== P9_FID_DIR
) {
560 err
= fstat(fd
, stbuf
);
564 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
565 /* Actual credentials are part of extended attrs */
571 if (fgetxattr(fd
, "user.virtfs.uid",
572 &tmp_uid
, sizeof(uid_t
)) > 0) {
573 stbuf
->st_uid
= tmp_uid
;
575 if (fgetxattr(fd
, "user.virtfs.gid",
576 &tmp_gid
, sizeof(gid_t
)) > 0) {
577 stbuf
->st_gid
= tmp_gid
;
579 if (fgetxattr(fd
, "user.virtfs.mode",
580 &tmp_mode
, sizeof(mode_t
)) > 0) {
581 stbuf
->st_mode
= tmp_mode
;
583 if (fgetxattr(fd
, "user.virtfs.rdev",
584 &tmp_dev
, sizeof(dev_t
)) > 0) {
585 stbuf
->st_rdev
= tmp_dev
;
587 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
594 static int local_open2(FsContext
*fs_ctx
, V9fsPath
*dir_path
, const char *name
,
595 int flags
, FsCred
*credp
, V9fsFidOpenState
*fs
)
602 char buffer
[PATH_MAX
];
604 v9fs_string_init(&fullname
);
605 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
606 path
= fullname
.data
;
608 /* Determine the security model */
609 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
610 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
615 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
616 /* Set cleint credentials in xattr */
617 err
= local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
622 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
623 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, SM_LOCAL_MODE_BITS
);
628 credp
->fc_mode
= credp
->fc_mode
|S_IFREG
;
629 /* Set client credentials in .virtfs_metadata directory files */
630 err
= local_set_mapped_file_attr(fs_ctx
, path
, credp
);
635 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
636 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
637 fd
= open(rpath(fs_ctx
, path
, buffer
), flags
, credp
->fc_mode
);
642 err
= local_post_create_passthrough(fs_ctx
, path
, credp
);
654 remove(rpath(fs_ctx
, path
, buffer
));
657 v9fs_string_free(&fullname
);
662 static int local_symlink(FsContext
*fs_ctx
, const char *oldpath
,
663 V9fsPath
*dir_path
, const char *name
, FsCred
*credp
)
669 char buffer
[PATH_MAX
];
671 v9fs_string_init(&fullname
);
672 v9fs_string_sprintf(&fullname
, "%s/%s", dir_path
->data
, name
);
673 newpath
= fullname
.data
;
675 /* Determine the security model */
676 if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
678 ssize_t oldpath_size
, write_size
;
679 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
685 /* Write the oldpath (target) to the file. */
686 oldpath_size
= strlen(oldpath
);
688 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
689 } while (write_size
== -1 && errno
== EINTR
);
691 if (write_size
!= oldpath_size
) {
698 /* Set cleint credentials in symlink's xattr */
699 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
700 err
= local_set_xattr(rpath(fs_ctx
, newpath
, buffer
), credp
);
705 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
707 ssize_t oldpath_size
, write_size
;
708 fd
= open(rpath(fs_ctx
, newpath
, buffer
), O_CREAT
|O_EXCL
|O_RDWR
,
714 /* Write the oldpath (target) to the file. */
715 oldpath_size
= strlen(oldpath
);
717 write_size
= write(fd
, (void *)oldpath
, oldpath_size
);
718 } while (write_size
== -1 && errno
== EINTR
);
720 if (write_size
!= oldpath_size
) {
727 /* Set cleint credentials in symlink's xattr */
728 credp
->fc_mode
= credp
->fc_mode
|S_IFLNK
;
729 err
= local_set_mapped_file_attr(fs_ctx
, newpath
, credp
);
734 } else if ((fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
735 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
736 err
= symlink(oldpath
, rpath(fs_ctx
, newpath
, buffer
));
740 err
= lchown(rpath(fs_ctx
, newpath
, buffer
), credp
->fc_uid
,
744 * If we fail to change ownership and if we are
745 * using security model none. Ignore the error
747 if ((fs_ctx
->export_flags
& V9FS_SEC_MASK
) != V9FS_SM_NONE
) {
757 remove(rpath(fs_ctx
, newpath
, buffer
));
760 v9fs_string_free(&fullname
);
764 static int local_link(FsContext
*ctx
, V9fsPath
*oldpath
,
765 V9fsPath
*dirpath
, const char *name
)
769 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
771 v9fs_string_init(&newpath
);
772 v9fs_string_sprintf(&newpath
, "%s/%s", dirpath
->data
, name
);
774 ret
= link(rpath(ctx
, oldpath
->data
, buffer
),
775 rpath(ctx
, newpath
.data
, buffer1
));
777 /* now link the virtfs_metadata files */
778 if (!ret
&& (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
)) {
779 /* Link the .virtfs_metadata files. Create the metada directory */
780 ret
= local_create_mapped_attr_dir(ctx
, newpath
.data
);
784 ret
= link(local_mapped_attr_path(ctx
, oldpath
->data
, buffer
),
785 local_mapped_attr_path(ctx
, newpath
.data
, buffer1
));
786 if (ret
< 0 && errno
!= ENOENT
) {
791 v9fs_string_free(&newpath
);
795 static int local_truncate(FsContext
*ctx
, V9fsPath
*fs_path
, off_t size
)
797 char buffer
[PATH_MAX
];
798 char *path
= fs_path
->data
;
800 return truncate(rpath(ctx
, path
, buffer
), size
);
803 static int local_rename(FsContext
*ctx
, const char *oldpath
,
807 char buffer
[PATH_MAX
], buffer1
[PATH_MAX
];
809 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
810 err
= local_create_mapped_attr_dir(ctx
, newpath
);
814 /* rename the .virtfs_metadata files */
815 err
= rename(local_mapped_attr_path(ctx
, oldpath
, buffer
),
816 local_mapped_attr_path(ctx
, newpath
, buffer1
));
817 if (err
< 0 && errno
!= ENOENT
) {
821 return rename(rpath(ctx
, oldpath
, buffer
), rpath(ctx
, newpath
, buffer1
));
824 static int local_chown(FsContext
*fs_ctx
, V9fsPath
*fs_path
, FsCred
*credp
)
826 char buffer
[PATH_MAX
];
827 char *path
= fs_path
->data
;
829 if ((credp
->fc_uid
== -1 && credp
->fc_gid
== -1) ||
830 (fs_ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) ||
831 (fs_ctx
->export_flags
& V9FS_SM_NONE
)) {
832 return lchown(rpath(fs_ctx
, path
, buffer
),
833 credp
->fc_uid
, credp
->fc_gid
);
834 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED
) {
835 return local_set_xattr(rpath(fs_ctx
, path
, buffer
), credp
);
836 } else if (fs_ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
837 return local_set_mapped_file_attr(fs_ctx
, path
, credp
);
842 static int local_utimensat(FsContext
*s
, V9fsPath
*fs_path
,
843 const struct timespec
*buf
)
845 char buffer
[PATH_MAX
];
846 char *path
= fs_path
->data
;
848 return qemu_utimens(rpath(s
, path
, buffer
), buf
);
851 static int local_remove(FsContext
*ctx
, const char *path
)
855 char buffer
[PATH_MAX
];
857 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
858 err
= lstat(rpath(ctx
, path
, buffer
), &stbuf
);
863 * If directory remove .virtfs_metadata contained in the
866 if (S_ISDIR(stbuf
.st_mode
)) {
867 sprintf(buffer
, "%s/%s/%s", ctx
->fs_root
, path
, VIRTFS_META_DIR
);
868 err
= remove(buffer
);
869 if (err
< 0 && errno
!= ENOENT
) {
871 * We didn't had the .virtfs_metadata file. May be file created
872 * in non-mapped mode ?. Ignore ENOENT.
878 * Now remove the name from parent directory
879 * .virtfs_metadata directory
881 err
= remove(local_mapped_attr_path(ctx
, path
, buffer
));;
882 if (err
< 0 && errno
!= ENOENT
) {
884 * We didn't had the .virtfs_metadata file. May be file created
885 * in non-mapped mode ?. Ignore ENOENT.
890 return remove(rpath(ctx
, path
, buffer
));
895 static int local_fsync(FsContext
*ctx
, int fid_type
,
896 V9fsFidOpenState
*fs
, int datasync
)
900 if (fid_type
== P9_FID_DIR
) {
907 return qemu_fdatasync(fd
);
913 static int local_statfs(FsContext
*s
, V9fsPath
*fs_path
, struct statfs
*stbuf
)
915 char buffer
[PATH_MAX
];
916 char *path
= fs_path
->data
;
918 return statfs(rpath(s
, path
, buffer
), stbuf
);
921 static ssize_t
local_lgetxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
922 const char *name
, void *value
, size_t size
)
924 char *path
= fs_path
->data
;
926 return v9fs_get_xattr(ctx
, path
, name
, value
, size
);
929 static ssize_t
local_llistxattr(FsContext
*ctx
, V9fsPath
*fs_path
,
930 void *value
, size_t size
)
932 char *path
= fs_path
->data
;
934 return v9fs_list_xattr(ctx
, path
, value
, size
);
937 static int local_lsetxattr(FsContext
*ctx
, V9fsPath
*fs_path
, const char *name
,
938 void *value
, size_t size
, int flags
)
940 char *path
= fs_path
->data
;
942 return v9fs_set_xattr(ctx
, path
, name
, value
, size
, flags
);
945 static int local_lremovexattr(FsContext
*ctx
, V9fsPath
*fs_path
,
948 char *path
= fs_path
->data
;
950 return v9fs_remove_xattr(ctx
, path
, name
);
953 static int local_name_to_path(FsContext
*ctx
, V9fsPath
*dir_path
,
954 const char *name
, V9fsPath
*target
)
957 v9fs_string_sprintf((V9fsString
*)target
, "%s/%s",
958 dir_path
->data
, name
);
960 v9fs_string_sprintf((V9fsString
*)target
, "%s", name
);
962 /* Bump the size for including terminating NULL */
967 static int local_renameat(FsContext
*ctx
, V9fsPath
*olddir
,
968 const char *old_name
, V9fsPath
*newdir
,
969 const char *new_name
)
972 V9fsString old_full_name
, new_full_name
;
974 v9fs_string_init(&old_full_name
);
975 v9fs_string_init(&new_full_name
);
977 v9fs_string_sprintf(&old_full_name
, "%s/%s", olddir
->data
, old_name
);
978 v9fs_string_sprintf(&new_full_name
, "%s/%s", newdir
->data
, new_name
);
980 ret
= local_rename(ctx
, old_full_name
.data
, new_full_name
.data
);
981 v9fs_string_free(&old_full_name
);
982 v9fs_string_free(&new_full_name
);
986 static int local_unlinkat(FsContext
*ctx
, V9fsPath
*dir
,
987 const char *name
, int flags
)
991 char buffer
[PATH_MAX
];
993 v9fs_string_init(&fullname
);
995 v9fs_string_sprintf(&fullname
, "%s/%s", dir
->data
, name
);
996 if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
997 if (flags
== AT_REMOVEDIR
) {
999 * If directory remove .virtfs_metadata contained in the
1002 sprintf(buffer
, "%s/%s/%s", ctx
->fs_root
,
1003 fullname
.data
, VIRTFS_META_DIR
);
1004 ret
= remove(buffer
);
1005 if (ret
< 0 && errno
!= ENOENT
) {
1007 * We didn't had the .virtfs_metadata file. May be file created
1008 * in non-mapped mode ?. Ignore ENOENT.
1014 * Now remove the name from parent directory
1015 * .virtfs_metadata directory.
1017 ret
= remove(local_mapped_attr_path(ctx
, fullname
.data
, buffer
));
1018 if (ret
< 0 && errno
!= ENOENT
) {
1020 * We didn't had the .virtfs_metadata file. May be file created
1021 * in non-mapped mode ?. Ignore ENOENT.
1026 /* Remove the name finally */
1027 ret
= remove(rpath(ctx
, fullname
.data
, buffer
));
1028 v9fs_string_free(&fullname
);
1034 static int local_ioc_getversion(FsContext
*ctx
, V9fsPath
*path
,
1035 mode_t st_mode
, uint64_t *st_gen
)
1038 #ifdef FS_IOC_GETVERSION
1039 V9fsFidOpenState fid_open
;
1042 * Do not try to open special files like device nodes, fifos etc
1043 * We can get fd for regular files and directories only
1045 if (!S_ISREG(st_mode
) && !S_ISDIR(st_mode
)) {
1048 err
= local_open(ctx
, path
, O_RDONLY
, &fid_open
);
1052 err
= ioctl(fid_open
.fd
, FS_IOC_GETVERSION
, st_gen
);
1053 local_close(ctx
, &fid_open
);
1060 static int local_init(FsContext
*ctx
)
1063 struct statfs stbuf
;
1065 if (ctx
->export_flags
& V9FS_SM_PASSTHROUGH
) {
1066 ctx
->xops
= passthrough_xattr_ops
;
1067 } else if (ctx
->export_flags
& V9FS_SM_MAPPED
) {
1068 ctx
->xops
= mapped_xattr_ops
;
1069 } else if (ctx
->export_flags
& V9FS_SM_NONE
) {
1070 ctx
->xops
= none_xattr_ops
;
1071 } else if (ctx
->export_flags
& V9FS_SM_MAPPED_FILE
) {
1073 * xattr operation for mapped-file and passthrough
1076 ctx
->xops
= passthrough_xattr_ops
;
1078 ctx
->export_flags
|= V9FS_PATHNAME_FSCONTEXT
;
1079 #ifdef FS_IOC_GETVERSION
1081 * use ioc_getversion only if the iocl is definied
1083 err
= statfs(ctx
->fs_root
, &stbuf
);
1085 switch (stbuf
.f_type
) {
1086 case EXT2_SUPER_MAGIC
:
1087 case BTRFS_SUPER_MAGIC
:
1088 case REISERFS_SUPER_MAGIC
:
1089 case XFS_SUPER_MAGIC
:
1090 ctx
->exops
.get_st_gen
= local_ioc_getversion
;
1098 static int local_parse_opts(QemuOpts
*opts
, struct FsDriverEntry
*fse
)
1100 const char *sec_model
= qemu_opt_get(opts
, "security_model");
1101 const char *path
= qemu_opt_get(opts
, "path");
1104 fprintf(stderr
, "security model not specified, "
1105 "local fs needs security model\nvalid options are:"
1106 "\tsecurity_model=[passthrough|mapped|none]\n");
1110 if (!strcmp(sec_model
, "passthrough")) {
1111 fse
->export_flags
|= V9FS_SM_PASSTHROUGH
;
1112 } else if (!strcmp(sec_model
, "mapped") ||
1113 !strcmp(sec_model
, "mapped-xattr")) {
1114 fse
->export_flags
|= V9FS_SM_MAPPED
;
1115 } else if (!strcmp(sec_model
, "none")) {
1116 fse
->export_flags
|= V9FS_SM_NONE
;
1117 } else if (!strcmp(sec_model
, "mapped-file")) {
1118 fse
->export_flags
|= V9FS_SM_MAPPED_FILE
;
1120 fprintf(stderr
, "Invalid security model %s specified, valid options are"
1121 "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
1127 fprintf(stderr
, "fsdev: No path specified.\n");
1130 fse
->path
= g_strdup(path
);
1135 FileOperations local_ops
= {
1136 .parse_opts
= local_parse_opts
,
1138 .lstat
= local_lstat
,
1139 .readlink
= local_readlink
,
1140 .close
= local_close
,
1141 .closedir
= local_closedir
,
1143 .opendir
= local_opendir
,
1144 .rewinddir
= local_rewinddir
,
1145 .telldir
= local_telldir
,
1146 .readdir_r
= local_readdir_r
,
1147 .seekdir
= local_seekdir
,
1148 .preadv
= local_preadv
,
1149 .pwritev
= local_pwritev
,
1150 .chmod
= local_chmod
,
1151 .mknod
= local_mknod
,
1152 .mkdir
= local_mkdir
,
1153 .fstat
= local_fstat
,
1154 .open2
= local_open2
,
1155 .symlink
= local_symlink
,
1157 .truncate
= local_truncate
,
1158 .rename
= local_rename
,
1159 .chown
= local_chown
,
1160 .utimensat
= local_utimensat
,
1161 .remove
= local_remove
,
1162 .fsync
= local_fsync
,
1163 .statfs
= local_statfs
,
1164 .lgetxattr
= local_lgetxattr
,
1165 .llistxattr
= local_llistxattr
,
1166 .lsetxattr
= local_lsetxattr
,
1167 .lremovexattr
= local_lremovexattr
,
1168 .name_to_path
= local_name_to_path
,
1169 .renameat
= local_renameat
,
1170 .unlinkat
= local_unlinkat
,