* [PATCH 0/9] 9pfs: Add 9pfs support for Windows host @ 2022-04-25 14:26 Bin Meng 2022-04-25 14:26 ` [PATCH 1/9] hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS Bin Meng ` (9 more replies) 0 siblings, 10 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:26 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Guohuai Shi At present there is no Windows support for 9p file system. This series adds initial Windows support for 9p file system. 'local' file system backend driver is supported on Windows, including open, read, write, close, rename, remove, etc. All security models are supported. The mapped (mapped-xattr) security model is implemented using NTFS Alternate Data Stream (ADS) so the 9p export path shall be on an NTFS partition. 'synth' backend driver is updated only to build sucessuflly in a Windows build. Funtionalities are not tested as test cases do not run on Windows at present. Example command line to test: "-fsdev local,path=c:\msys64,security_model=mapped,id=p9 -device virtio-9p-pci,fsdev=p9,mount_tag=p9fs" Bin Meng (2): hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS qemu/xatth.h: Update for Windows build Guohuai Shi (7): hw/9pfs: Extract common stuff to 9p-local.h fsdev: Add missing definitions for Windows in file-op-9p.h hw/9pfs: Add a 'local' file system backend driver for Windows hw/9pfs: Update 9p-synth.c for Windows build fsdev: Enable 'local' file system driver backend for Windows meson.build: Turn on virtfs for Windows host hw/9p: win32: Translate Windows error number to Linux value meson.build | 10 +- fsdev/file-op-9p.h | 33 + hw/9pfs/9p-linux-errno.h | 151 +++++ hw/9pfs/9p-local.h | 20 + hw/9pfs/9p-util.h | 41 ++ hw/9pfs/9p.h | 27 + include/qemu/xattr.h | 4 +- fsdev/qemu-fsdev.c | 2 + hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ hw/9pfs/9p-local.c | 14 - hw/9pfs/9p-synth.c | 5 +- hw/9pfs/9p-util-win32.c | 341 +++++++++++ hw/9pfs/9p-xattr.c | 113 ++++ hw/9pfs/9p.c | 98 ++- hw/9pfs/codir.c | 15 + fsdev/meson.build | 1 + hw/9pfs/meson.build | 12 +- 17 files changed, 2103 insertions(+), 26 deletions(-) create mode 100644 hw/9pfs/9p-linux-errno.h create mode 100644 hw/9pfs/9p-local-win32.c create mode 100644 hw/9pfs/9p-util-win32.c -- 2.25.1 ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 1/9] hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng @ 2022-04-25 14:26 ` Bin Meng 2022-04-25 14:26 ` [PATCH 2/9] qemu/xatth.h: Update for Windows build Bin Meng ` (8 subsequent siblings) 9 siblings, 0 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:26 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Bin Meng <bin.meng@windriver.com> For better readability, Windows support of 9pfs local backend driver will be provided in a separate 9p-local-win32.c file, hence let's only compile 9p-local.c for Linux and macOS. As we don't plan to support 9p proxy on Windows, apply the same to 9p-proxy.c. Signed-off-by: Bin Meng <bin.meng@windriver.com> --- hw/9pfs/meson.build | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index 12443b6ad5..b4a8ff0913 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -1,8 +1,6 @@ fs_ss = ss.source_set() fs_ss.add(files( - '9p-local.c', '9p-posix-acl.c', - '9p-proxy.c', '9p-synth.c', '9p-xattr-user.c', '9p-xattr.c', @@ -13,8 +11,12 @@ fs_ss.add(files( 'coth.c', 'coxattr.c', )) -fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-util-linux.c')) -fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-util-darwin.c')) +fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-local.c', + '9p-proxy.c', + '9p-util-linux.c')) +fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-local.c', + '9p-proxy.c', + '9p-util-darwin.c')) fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c')) softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 2/9] qemu/xatth.h: Update for Windows build 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng 2022-04-25 14:26 ` [PATCH 1/9] hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS Bin Meng @ 2022-04-25 14:26 ` Bin Meng 2022-04-25 14:26 ` [PATCH 3/9] hw/9pfs: Extract common stuff to 9p-local.h Bin Meng ` (7 subsequent siblings) 9 siblings, 0 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:26 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Bin Meng <bin.meng@windriver.com> Windows does not have <sys/xattr.h>. Signed-off-by: Bin Meng <bin.meng@windriver.com> --- include/qemu/xattr.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/qemu/xattr.h b/include/qemu/xattr.h index f1d0f7be74..b08a934acc 100644 --- a/include/qemu/xattr.h +++ b/include/qemu/xattr.h @@ -25,7 +25,9 @@ # if !defined(ENOATTR) # define ENOATTR ENODATA # endif -# include <sys/xattr.h> +# ifndef CONFIG_WIN32 +# include <sys/xattr.h> +# endif #endif #endif -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 3/9] hw/9pfs: Extract common stuff to 9p-local.h 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng 2022-04-25 14:26 ` [PATCH 1/9] hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS Bin Meng 2022-04-25 14:26 ` [PATCH 2/9] qemu/xatth.h: Update for Windows build Bin Meng @ 2022-04-25 14:26 ` Bin Meng 2022-04-25 14:27 ` [PATCH 4/9] fsdev: Add missing definitions for Windows in file-op-9p.h Bin Meng ` (6 subsequent siblings) 9 siblings, 0 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:26 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> Some macros (e.g.: VIRTFS_META_DIR) are used by both Linux and macOS, and will be used by the upcoming Windows support too, extract them from 9p-local.c to 9p-local.h. Note local_is_mapped_file_metadata()is changed to an inline function. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- hw/9pfs/9p-local.h | 16 ++++++++++++++++ hw/9pfs/9p-local.c | 14 -------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h index 32c72749d9..55ea4b7883 100644 --- a/hw/9pfs/9p-local.h +++ b/hw/9pfs/9p-local.h @@ -13,6 +13,22 @@ #ifndef QEMU_9P_LOCAL_H #define QEMU_9P_LOCAL_H +#define VIRTFS_META_DIR ".virtfs_metadata" +#define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root" + +#define ATTR_MAX 100 + +typedef struct { + int mountfd; +} LocalData; + +static inline bool local_is_mapped_file_metadata(FsContext *fs_ctx, + const char *name) +{ + return + !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE); +} + int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, mode_t mode); int local_opendir_nofollow(FsContext *fs_ctx, const char *path); diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index d42ce6d8b8..73beef84a9 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -53,10 +53,6 @@ #define BTRFS_SUPER_MAGIC 0x9123683E #endif -typedef struct { - int mountfd; -} LocalData; - int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, mode_t mode) { @@ -114,9 +110,6 @@ static void unlinkat_preserve_errno(int dirfd, const char *path, int flags) errno = serrno; } -#define VIRTFS_META_DIR ".virtfs_metadata" -#define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root" - static FILE *local_fopenat(int dirfd, const char *name, const char *mode) { int fd, o_mode = 0; @@ -144,7 +137,6 @@ static FILE *local_fopenat(int dirfd, const char *name, const char *mode) return fp; } -#define ATTR_MAX 100 static void local_mapped_file_attr(int dirfd, const char *name, struct stat *stbuf) { @@ -547,12 +539,6 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) return telldir(fs->dir.stream); } -static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name) -{ - return - !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE); -} - static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) { struct dirent *entry; -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 4/9] fsdev: Add missing definitions for Windows in file-op-9p.h 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (2 preceding siblings ...) 2022-04-25 14:26 ` [PATCH 3/9] hw/9pfs: Extract common stuff to 9p-local.h Bin Meng @ 2022-04-25 14:27 ` Bin Meng 2022-05-04 17:35 ` Christian Schoenebeck 2022-04-25 14:27 ` [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows Bin Meng ` (5 subsequent siblings) 9 siblings, 1 reply; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:27 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> Add uid_t, gid_t and struct statfs definitions, which are currently missing from Windows headers, but are required when we add 9p file system support for Windows in later patches. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- fsdev/file-op-9p.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 4997677460..7d9a736b66 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -27,6 +27,39 @@ # include <sys/mount.h> #endif +#ifdef CONFIG_WIN32 + +/* POSIX structure not defined in Windows */ + +typedef uint32_t uid_t; +typedef uint32_t gid_t; + +/* from http://man7.org/linux/man-pages/man2/statfs.2.html */ +typedef uint32_t __fsword_t; +typedef uint32_t fsblkcnt_t; +typedef uint32_t fsfilcnt_t; + +/* from linux/include/uapi/asm-generic/posix_types.h */ +typedef struct { + long __val[2]; +} fsid_t; + +struct statfs { + __fsword_t f_type; + __fsword_t f_bsize; + fsblkcnt_t f_blocks; + fsblkcnt_t f_bfree; + fsblkcnt_t f_bavail; + fsfilcnt_t f_files; + fsfilcnt_t f_ffree; + fsid_t f_fsid; + __fsword_t f_namelen; + __fsword_t f_frsize; + __fsword_t f_flags; +}; + +#endif /* CONFIG_WIN32 */ + #define SM_LOCAL_MODE_BITS 0600 #define SM_LOCAL_DIR_MODE_BITS 0700 -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH 4/9] fsdev: Add missing definitions for Windows in file-op-9p.h 2022-04-25 14:27 ` [PATCH 4/9] fsdev: Add missing definitions for Windows in file-op-9p.h Bin Meng @ 2022-05-04 17:35 ` Christian Schoenebeck 0 siblings, 0 replies; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-04 17:35 UTC (permalink / raw) To: qemu-devel; +Cc: Greg Kurz, Bin Meng, Guohuai Shi, Bin Meng On Montag, 25. April 2022 16:27:00 CEST Bin Meng wrote: > From: Guohuai Shi <guohuai.shi@windriver.com> > > Add uid_t, gid_t and struct statfs definitions, which are currently > missing from Windows headers, but are required when we add 9p file > system support for Windows in later patches. > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > Signed-off-by: Bin Meng <bin.meng@windriver.com> > --- > > fsdev/file-op-9p.h | 33 +++++++++++++++++++++++++++++++++ > 1 file changed, 33 insertions(+) > > diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h > index 4997677460..7d9a736b66 100644 > --- a/fsdev/file-op-9p.h > +++ b/fsdev/file-op-9p.h > @@ -27,6 +27,39 @@ > # include <sys/mount.h> > #endif > > +#ifdef CONFIG_WIN32 > + > +/* POSIX structure not defined in Windows */ > + > +typedef uint32_t uid_t; > +typedef uint32_t gid_t; > + > +/* from http://man7.org/linux/man-pages/man2/statfs.2.html */ > +typedef uint32_t __fsword_t; > +typedef uint32_t fsblkcnt_t; > +typedef uint32_t fsfilcnt_t; > + > +/* from linux/include/uapi/asm-generic/posix_types.h */ > +typedef struct { > + long __val[2]; > +} fsid_t; > + > +struct statfs { > + __fsword_t f_type; > + __fsword_t f_bsize; > + fsblkcnt_t f_blocks; > + fsblkcnt_t f_bfree; > + fsblkcnt_t f_bavail; > + fsfilcnt_t f_files; > + fsfilcnt_t f_ffree; > + fsid_t f_fsid; > + __fsword_t f_namelen; > + __fsword_t f_frsize; > + __fsword_t f_flags; > +}; > + > +#endif /* CONFIG_WIN32 */ > + > #define SM_LOCAL_MODE_BITS 0600 > #define SM_LOCAL_DIR_MODE_BITS 0700 I don't think this header file is the right place to add these missing POSIX types. I would add them to 9p-util-windows.h or something like that. Best regards, Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (3 preceding siblings ...) 2022-04-25 14:27 ` [PATCH 4/9] fsdev: Add missing definitions for Windows in file-op-9p.h Bin Meng @ 2022-04-25 14:27 ` Bin Meng 2022-05-04 18:01 ` Christian Schoenebeck 2022-05-09 14:29 ` Greg Kurz 2022-04-25 14:27 ` [PATCH 6/9] hw/9pfs: Update 9p-synth.c for Windows build Bin Meng ` (4 subsequent siblings) 9 siblings, 2 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:27 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> Add a 9p local file system backend driver to support Windows, including open, read, write, close, rename, remove, etc. All security models are supported. The mapped (mapped-xattr) security model is implemented using NTFS Alternate Data Stream (ADS) so the 9p export path shall be on an NTFS partition. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- hw/9pfs/9p-linux-errno.h | 151 +++++ hw/9pfs/9p-local.h | 4 + hw/9pfs/9p-util.h | 41 ++ hw/9pfs/9p.h | 23 + hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ hw/9pfs/9p-util-win32.c | 303 ++++++++++ hw/9pfs/9p-xattr.c | 113 ++++ hw/9pfs/9p.c | 91 ++- hw/9pfs/codir.c | 15 + 9 files changed, 1982 insertions(+), 1 deletion(-) create mode 100644 hw/9pfs/9p-linux-errno.h create mode 100644 hw/9pfs/9p-local-win32.c create mode 100644 hw/9pfs/9p-util-win32.c diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h new file mode 100644 index 0000000000..b0d6ac45ac --- /dev/null +++ b/hw/9pfs/9p-linux-errno.h @@ -0,0 +1,151 @@ +/* + * 9p Linux errno translation definition + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <errno.h> + +#ifndef QEMU_9P_LINUX_ERRNO_H +#define QEMU_9P_LINUX_ERRNO_H + +/* + * This file contains the Linux errno definitions to translate errnos set by + * the 9P server (running on Windows) to a corresponding errno value. + * + * This list should be periodically reviewed and updated; particularly for + * errnos that might be set as a result of a file system operation. + */ + +#define L_EPERM 1 /* Operation not permitted */ +#define L_ENOENT 2 /* No such file or directory */ +#define L_ESRCH 3 /* No such process */ +#define L_EINTR 4 /* Interrupted system call */ +#define L_EIO 5 /* I/O error */ +#define L_ENXIO 6 /* No such device or address */ +#define L_E2BIG 7 /* Argument list too long */ +#define L_ENOEXEC 8 /* Exec format error */ +#define L_EBADF 9 /* Bad file number */ +#define L_ECHILD 10 /* No child processes */ +#define L_EAGAIN 11 /* Try again */ +#define L_ENOMEM 12 /* Out of memory */ +#define L_EACCES 13 /* Permission denied */ +#define L_EFAULT 14 /* Bad address */ +#define L_ENOTBLK 15 /* Block device required */ +#define L_EBUSY 16 /* Device or resource busy */ +#define L_EEXIST 17 /* File exists */ +#define L_EXDEV 18 /* Cross-device link */ +#define L_ENODEV 19 /* No such device */ +#define L_ENOTDIR 20 /* Not a directory */ +#define L_EISDIR 21 /* Is a directory */ +#define L_EINVAL 22 /* Invalid argument */ +#define L_ENFILE 23 /* File table overflow */ +#define L_EMFILE 24 /* Too many open files */ +#define L_ENOTTY 25 /* Not a typewriter */ +#define L_ETXTBSY 26 /* Text file busy */ +#define L_EFBIG 27 /* File too large */ +#define L_ENOSPC 28 /* No space left on device */ +#define L_ESPIPE 29 /* Illegal seek */ +#define L_EROFS 30 /* Read-only file system */ +#define L_EMLINK 31 /* Too many links */ +#define L_EPIPE 32 /* Broken pipe */ +#define L_EDOM 33 /* Math argument out of domain of func */ +#define L_ERANGE 34 /* Math result not representable */ +#define L_EDEADLK 35 /* Resource deadlock would occur */ +#define L_ENAMETOOLONG 36 /* File name too long */ +#define L_ENOLCK 37 /* No record locks available */ +#define L_ENOSYS 38 /* Function not implemented */ +#define L_ENOTEMPTY 39 /* Directory not empty */ +#define L_ELOOP 40 /* Too many symbolic links encountered */ +#define L_ENOMSG 42 /* No message of desired type */ +#define L_EIDRM 43 /* Identifier removed */ +#define L_ECHRNG 44 /* Channel number out of range */ +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ +#define L_EL3HLT 46 /* Level 3 halted */ +#define L_EL3RST 47 /* Level 3 reset */ +#define L_ELNRNG 48 /* Link number out of range */ +#define L_EUNATCH 49 /* Protocol driver not attached */ +#define L_ENOCSI 50 /* No CSI structure available */ +#define L_EL2HLT 51 /* Level 2 halted */ +#define L_EBADE 52 /* Invalid exchange */ +#define L_EBADR 53 /* Invalid request descriptor */ +#define L_EXFULL 54 /* Exchange full */ +#define L_ENOANO 55 /* No anode */ +#define L_EBADRQC 56 /* Invalid request code */ +#define L_EBADSLT 57 /* Invalid slot */ +#define L_EBFONT 58 /* Bad font file format */ +#define L_ENOSTR 59 /* Device not a stream */ +#define L_ENODATA 61 /* No data available */ +#define L_ETIME 62 /* Timer expired */ +#define L_ENOSR 63 /* Out of streams resources */ +#define L_ENONET 64 /* Machine is not on the network */ +#define L_ENOPKG 65 /* Package not installed */ +#define L_EREMOTE 66 /* Object is remote */ +#define L_ENOLINK 67 /* Link has been severed */ +#define L_EADV 68 /* Advertise error */ +#define L_ESRMNT 69 /* Srmount error */ +#define L_ECOMM 70 /* Communication error on send */ +#define L_EPROTO 71 /* Protocol error */ +#define L_EMULTIHOP 72 /* Multihop attempted */ +#define L_EDOTDOT 73 /* RFS specific error */ +#define L_EBADMSG 74 /* Not a data message */ +#define L_EOVERFLOW 75 /* Value too large for defined data type */ +#define L_ENOTUNIQ 76 /* Name not unique on network */ +#define L_EBADFD 77 /* File descriptor in bad state */ +#define L_EREMCHG 78 /* Remote address changed */ +#define L_ELIBACC 79 /* Can not access a needed shared library */ +#define L_ELIBBAD 80 /* Accessing a corrupted shared library */ +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ +#define L_ELIBMAX 82 /* Attempting to link in too many shared libs */ +#define L_ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define L_EILSEQ 84 /* Illegal byte sequence */ +#define L_ERESTART 85 /* Interrupted system call should be restarted */ +#define L_ESTRPIPE 86 /* Streams pipe error */ +#define L_EUSERS 87 /* Too many users */ +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ +#define L_EDESTADDRREQ 89 /* Destination address required */ +#define L_EMSGSIZE 90 /* Message too long */ +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define L_ENOPROTOOPT 92 /* Protocol not available */ +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define L_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define L_EPFNOSUPPORT 96 /* Protocol family not supported */ +#define L_EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define L_EADDRINUSE 98 /* Address already in use */ +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define L_ENETDOWN 100 /* Network is down */ +#define L_ENETUNREACH 101 /* Network is unreachable */ +#define L_ENETRESET 102 /* Network dropped connection because of reset */ +#define L_ECONNABORTED 103 /* Software caused connection abort */ +#define L_ECONNRESET 104 /* Connection reset by peer */ +#define L_ENOBUFS 105 /* No buffer space available */ +#define L_EISCONN 106 /* Transport endpoint is already connected */ +#define L_ENOTCONN 107 /* Transport endpoint is not connected */ +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define L_ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define L_ETIMEDOUT 110 /* Connection timed out */ +#define L_ECONNREFUSED 111 /* Connection refused */ +#define L_EHOSTDOWN 112 /* Host is down */ +#define L_EHOSTUNREACH 113 /* No route to host */ +#define L_EALREADY 114 /* Operation already in progress */ +#define L_EINPROGRESS 115 /* Operation now in progress */ +#define L_ESTALE 116 /* Stale NFS file handle */ +#define L_EUCLEAN 117 /* Structure needs cleaning */ +#define L_ENOTNAM 118 /* Not a XENIX named type file */ +#define L_ENAVAIL 119 /* No XENIX semaphores available */ +#define L_EISNAM 120 /* Is a named type file */ +#define L_EREMOTEIO 121 /* Remote I/O error */ +#define L_EDQUOT 122 /* Quota exceeded */ +#define L_ENOMEDIUM 123 /* No medium found */ +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ +#define L_ECANCELED 125 /* Operation Canceled */ +#define L_ENOKEY 126 /* Required key not available */ +#define L_EKEYEXPIRED 127 /* Key has expired */ +#define L_EKEYREVOKED 128 /* Key has been revoked */ +#define L_EKEYREJECTED 129 /* Key was rejected by service */ +#define L_EOWNERDEAD 130 /* Owner died */ +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ + +#endif /* QEMU_9P_LINUX_ERRNO_H */ diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h index 55ea4b7883..4c75876e19 100644 --- a/hw/9pfs/9p-local.h +++ b/hw/9pfs/9p-local.h @@ -31,6 +31,10 @@ static inline bool local_is_mapped_file_metadata(FsContext *fs_ctx, int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, mode_t mode); +#ifndef CONFIG_WIN32 int local_opendir_nofollow(FsContext *fs_ctx, const char *path); +#else +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); +#endif #endif diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 97e681e167..6eadb38e1d 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) errno = serrno; } +#ifndef CONFIG_WIN32 static inline int openat_dir(int dirfd, const char *name) { return openat(dirfd, name, @@ -89,7 +90,9 @@ again: errno = serrno; return fd; } +#endif /* !CONFIG_WIN32 */ +#ifndef CONFIG_WIN32 ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size); int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename, char *list, size_t size); ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, const char *name); +#else + +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, + const char *name, void *value, size_t size); +int fsetxattrat_nofollow(const char *dirname, const char *filename, + const char *name, void *value, size_t size, + int flags); +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, + char *list, size_t size); +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, + const char *name); + +int qemu_statfs(const char *fs_root, struct statfs *stbuf); + +static inline char *merge_fs_path(const char *path1, const char *path2) +{ + char *full_fs_path; + size_t full_fs_path_len; + + full_fs_path_len = strlen(path1) + strlen(path2) + 2; + full_fs_path = g_malloc(full_fs_path_len); + strcpy(full_fs_path, path1); + strcat(full_fs_path, "\\"); + strcat(full_fs_path, path2); + + return full_fs_path; +} + +#endif /* !CONFIG_WIN32 */ + +#ifndef CONFIG_WIN32 /* * Darwin has d_seekoff, which appears to function similarly to d_off. * However, it does not appear to be supported on all file systems, @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) return dent->d_off; #endif } +#endif /* !CONFIG_WIN32 */ /** * qemu_dirent_dup() - Duplicate directory entry @dent. @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct dirent *dent) #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP int pthread_fchdir_np(int fd) __attribute__((weak_import)); #endif + +#ifndef CONFIG_WIN32 int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); +#else +int qemu_mknodat(const char *dirname, const char *filename, + mode_t mode, dev_t dev); +#endif #endif diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 994f952600..87e8eac840 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -3,13 +3,36 @@ #include <dirent.h> #include <utime.h> +#ifndef CONFIG_WIN32 #include <sys/resource.h> +#endif #include "fsdev/file-op-9p.h" #include "fsdev/9p-iov-marshal.h" #include "qemu/thread.h" #include "qemu/coroutine.h" #include "qemu/qht.h" +#ifdef CONFIG_WIN32 + +#define O_NOCTTY 0 +#define O_NDELAY 0 +#define O_NONBLOCK 0 +#define O_DSYNC 0 +#define O_DIRECT 0 +#define O_NOFOLLOW 0 +#define O_NOATIME 0 +#define O_SYNC 0 +#define O_ASYNC 0 +#define O_DIRECTORY 02000000 + +#define FASYNC 0 + +#define AT_REMOVEDIR 1 + +#define NAME_MAX 260 + +#endif + enum { P9_TLERROR = 6, P9_RLERROR, diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c new file mode 100644 index 0000000000..aab7c9f7b5 --- /dev/null +++ b/hw/9pfs/9p-local-win32.c @@ -0,0 +1,1242 @@ +/* + * 9p Windows callback + * + * Copyright (c) 2022 Wind River Systems, Inc. + * + * Based on hw/9pfs/9p-local.c + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +/* + * Not so fast! You might want to read the 9p developer docs first: + * https://wiki.qemu.org/Documentation/9p + */ + +#include "qemu/osdep.h" +#include <windows.h> +#include <dirent.h> +#include "9p.h" +#include "9p-local.h" +#include "9p-xattr.h" +#include "9p-util.h" +#include "fsdev/qemu-fsdev.h" /* local_ops */ +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include <libgen.h> + +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, + int flags, mode_t mode) +{ + char *full_file_name; + int fd; + + full_file_name = merge_fs_path(fs_ctx->fs_root, name); + fd = open(full_file_name, flags | _O_BINARY, mode); + g_free(full_file_name); + + return fd; +} + +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) +{ + char *full_file_name; + DIR *dir; + + full_file_name = merge_fs_path(fs_ctx->fs_root, name); + dir = opendir(full_file_name); + g_free(full_file_name); + return dir; +} + +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, + mode_t mode) +{ + int fd; + + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { + /* Windows does not allow call open() for a directory */ + fd = -1; + } else { + fd = openfile_with_ctx(fs_ctx, path, flags, mode); + } + + return fd; +} + +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) +{ + return opendir_with_ctx(fs_ctx, path); +} + +static FILE *local_fopenat(const char *dirname, const char *name, + const char *mode) +{ + char *full_file_name; + char modestr[3] = {0}; + FILE *fp; + + /* + * only supports two modes + */ + if (mode[0] == 'r') { + modestr[0] = 'r'; + } else if (mode[0] == 'w') { + modestr[0] = 'w'; + } else { + return NULL; + } + /* Windows host needs 'b' flag */ + modestr[1] = 'b'; + + full_file_name = merge_fs_path(dirname, name); + fp = fopen(full_file_name, modestr); + g_free(full_file_name); + + return fp; +} + +static void local_mapped_file_attr(const char *dirpath, const char *name, + struct stat *stbuf) +{ + FILE *fp; + char buf[ATTR_MAX]; + char *full_file_name; + + if (strcmp(name, ".") != 0) { + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); + fp = local_fopenat(full_file_name, name, "r"); + g_free(full_file_name); + } else { + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); + } + if (!fp) { + return; + } + + memset(buf, 0, ATTR_MAX); + while (fgets(buf, ATTR_MAX, fp)) { + if (!strncmp(buf, "virtfs.uid", 10)) { + stbuf->st_uid = atoi(buf + 11); + } else if (!strncmp(buf, "virtfs.gid", 10)) { + stbuf->st_gid = atoi(buf + 11); + } else if (!strncmp(buf, "virtfs.mode", 11)) { + stbuf->st_mode = (stbuf->st_mode & ~0777); + stbuf->st_mode |= (atoi(buf + 12) & 0777); + } else if (!strncmp(buf, "virtfs.rdev", 11)) { + stbuf->st_rdev = atoi(buf + 12); + } + memset(buf, 0, ATTR_MAX); + } + fclose(fp); +} + +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) +{ + int err = -1; + char *full_dir_name, *full_file_name; + char *dirpath = g_path_get_dirname(fs_path->data); + char *name = g_path_get_basename(fs_path->data); + + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); + full_file_name = merge_fs_path(full_dir_name, name); + err = stat(full_file_name, stbuf); + + if (err == 0 && strcmp(fs_path->data, ".") == 0) { + /* + * Hard code for root directory on Windows host. + * This will root directory have a special inode number, + * then guest OS can detect it is a special directory. + */ + stbuf->st_ino = 2; + } + + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + /* Actual credentials are part of extended attrs */ + uid_t tmp_uid; + gid_t tmp_gid; + mode_t tmp_mode; + dev_t tmp_dev; + + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", + &tmp_uid, sizeof(uid_t)) > 0) { + stbuf->st_uid = le32_to_cpu(tmp_uid); + } + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", + &tmp_gid, sizeof(gid_t)) > 0) { + stbuf->st_gid = le32_to_cpu(tmp_gid); + } + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { + stbuf->st_mode = (stbuf->st_mode & ~0777); + stbuf->st_mode |= le32_to_cpu(tmp_mode); + } + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", + &tmp_dev, sizeof(dev_t)) > 0) { + stbuf->st_rdev = le64_to_cpu(tmp_dev); + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + local_mapped_file_attr(full_dir_name, name, stbuf); + } + + g_free(full_file_name); + g_free(full_dir_name); + + if (err) { + goto err_out; + } + +err_out: + g_free(name); + g_free(dirpath); + return err; +} + +static int local_set_mapped_file_attrat(const char *dirname, const char *name, + FsCred *credp) +{ + FILE *fp; + int ret; + char buf[ATTR_MAX]; + int uid = -1, gid = -1, mode = -1, rdev = -1; + bool is_root = !strcmp(name, "."); + char *full_dir_name; + + if (is_root) { + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); + if (!fp) { + if (errno == ENOENT) { + goto update_map_file; + } else { + return -1; + } + } + } else { + /* + * mapped-file: + * <sub_file> attribute stored to: + * <directory> + VIRTFS_META_DIR + <sub_file_name> + */ + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); + ret = mkdir(full_dir_name); + + if (ret < 0 && errno != EEXIST) { + g_free(full_dir_name); + return -1; + } + + fp = local_fopenat(full_dir_name, name, "r"); + if (!fp) { + if (errno == ENOENT) { + goto update_map_file; + } else { + g_free(full_dir_name); + return -1; + } + } + } + + memset(buf, 0, ATTR_MAX); + while (fgets(buf, ATTR_MAX, fp)) { + if (!strncmp(buf, "virtfs.uid", 10)) { + uid = atoi(buf + 11); + } else if (!strncmp(buf, "virtfs.gid", 10)) { + gid = atoi(buf + 11); + } else if (!strncmp(buf, "virtfs.mode", 11)) { + mode = atoi(buf + 12); + } else if (!strncmp(buf, "virtfs.rdev", 11)) { + rdev = atoi(buf + 12); + } + memset(buf, 0, ATTR_MAX); + } + fclose(fp); + +update_map_file: + if (is_root) { + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); + } else { + fp = local_fopenat(full_dir_name, name, "w"); + g_free(full_dir_name); + } + if (!fp) { + return -1; + } + + if (credp->fc_uid != -1) { + uid = credp->fc_uid; + } + if (credp->fc_gid != -1) { + gid = credp->fc_gid; + } + if (credp->fc_mode != (mode_t)-1) { + mode = credp->fc_mode; + } + if (credp->fc_rdev != -1) { + rdev = credp->fc_rdev; + } + + if (uid != -1) { + fprintf(fp, "virtfs.uid=%d\n", uid); + } + if (gid != -1) { + fprintf(fp, "virtfs.gid=%d\n", gid); + } + if (mode != -1) { + fprintf(fp, "virtfs.mode=%d\n", mode); + } + if (rdev != -1) { + fprintf(fp, "virtfs.rdev=%d\n", rdev); + } + fclose(fp); + + return 0; +} + +static int local_set_xattrat(const char *dirname, const char *path, + FsCred *credp) +{ + int err; + + if (credp->fc_uid != -1) { + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", + &tmp_uid, sizeof(uid_t), 0); + if (err) { + return err; + } + } + if (credp->fc_gid != -1) { + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", + &tmp_gid, sizeof(gid_t), 0); + if (err) { + return err; + } + } + if (credp->fc_mode != (mode_t)-1) { + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", + &tmp_mode, sizeof(mode_t), 0); + if (err) { + return err; + } + } + if (credp->fc_rdev != -1) { + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", + &tmp_rdev, sizeof(dev_t), 0); + if (err) { + return err; + } + } + return 0; +} + +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, + char *buf, size_t bufsz) +{ + return -1; +} + +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) +{ + return close(fs->fd); +} + +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return closedir(fs->dir.stream); +} + +static int local_open(FsContext *ctx, V9fsPath *fs_path, + int flags, V9fsFidOpenState *fs) +{ + int fd; + + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); + if (fd == -1) { + return -1; + } + fs->fd = fd; + return fs->fd; +} + +static int local_opendir(FsContext *ctx, + V9fsPath *fs_path, V9fsFidOpenState *fs) +{ + DIR *stream; + char *full_file_name; + + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); + stream = opendir(full_file_name); + g_free(full_file_name); + + if (!stream) { + return -1; + } + + fs->dir.stream = stream; + return 0; +} + +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) +{ + rewinddir(fs->dir.stream); +} + +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) +{ + return telldir(fs->dir.stream); +} + +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) +{ + struct dirent *entry; + +again: + entry = readdir(fs->dir.stream); + if (!entry) { + return NULL; + } + + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { + /* skip the meta data */ + goto again; + } + } + + return entry; +} + +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) +{ + off_t count; + struct dirent *findentry; + struct dirent *entry; + size_t namelen[3] = {0}; + off_t direntoff[3] = {-1, -1, -1}; + char *d_name[3]; + int i; + + /* + * MinGW's seekdir() requires directory is not modified. If guest OS is + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", + * then MinGW's seekdir() will seek to a wrong offset. + * + * This function saves some old offset directory entry name, + * and lookup current entry again, and compare the offset. + * + * If new offset is less than old offset, that means someone is deleting + * files in the directory, thus we need to seek offset backward. + * + * If new offset is larger than old offset, that means someone is creating + * files in the directory, thus we need to seek offset forward. + */ + + direntoff[0] = telldir(fs->dir.stream); + + /* do nothing if current offset is 0 or EOF */ + if (direntoff[0] == 0 || direntoff[0] < 0) { + seekdir(fs->dir.stream, off); + return ; + } + + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); + d_name[1] = d_name[0] + sizeof(entry->d_name); + d_name[2] = d_name[1] + sizeof(entry->d_name); + + /* save 3 nearest entries and offsets */ + for (i = 0; i < 3; i++) { + entry = &fs->dir.stream->dd_dir; + + memcpy(d_name[i], entry->d_name, entry->d_namlen); + namelen[i] = strlen(d_name[i]) + 1; + + direntoff[i] = telldir(fs->dir.stream); + + entry = readdir(fs->dir.stream); + if (entry == NULL) { + break; + } + } + + /* lookup saved entries again */ + for (i = 0; i < 3 && direntoff[i] != -1; i++) { + rewinddir(fs->dir.stream); + count = 0; + while ((findentry = readdir(fs->dir.stream)) != NULL) { + count++; + + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { + if (count + i == direntoff[i]) { + seekdir(fs->dir.stream, off); + goto out; + } else if (count + i < direntoff[i]) { + off = off - (direntoff[i] - count) - i; + if (off <= 0) { + off = 0; + } + seekdir(fs->dir.stream, off); + goto out; + } else { + off = off + (count - direntoff[i]) - i; + seekdir(fs->dir.stream, off); + goto out; + } + } + } + } + /* can not get anything, seek backward */ + off = off - 1; + + seekdir(fs->dir.stream, off); +out: + g_free(d_name[0]); + return ; +} + +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ +#ifdef CONFIG_PREADV + return preadv(fs->fd, iov, iovcnt, offset); +#else + int err = lseek(fs->fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + return readv(fs->fd, iov, iovcnt); + } +#endif +} + +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, + const struct iovec *iov, + int iovcnt, off_t offset) +{ + ssize_t ret; +#ifdef CONFIG_PREADV + ret = pwritev(fs->fd, iov, iovcnt, offset); +#else + int err = lseek(fs->fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + ret = writev(fs->fd, iov, iovcnt); + } +#endif +#ifdef CONFIG_SYNC_FILE_RANGE + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { + /* + * Initiate a writeback. This is not a data integrity sync. + * We want to ensure that we don't leave dirty pages in the cache + * after write when writeout=immediate is sepcified. + */ + sync_file_range(fs->fd, offset, ret, + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); + } +#endif + return ret; +} + +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + char *dirpath = g_path_get_dirname(fs_path->data); + char *name = g_path_get_basename(fs_path->data); + int ret = -1; + char *full_file_name = NULL; + DIR *dir; + dir = local_opendir_nofollow(fs_ctx, dirpath); + if (dir == NULL) { + goto out; + } + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); + + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + ret = local_set_xattrat(full_file_name, name, credp); + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + ret = local_set_mapped_file_attrat(full_file_name, name, credp); + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || + fs_ctx->export_flags & V9FS_SM_NONE) { + ret = -1; + errno = ENOTSUP; + } + closedir(dir); + +out: + if (full_file_name != NULL) { + g_free(full_file_name); + } + + g_free(dirpath); + g_free(name); + return ret; +} + +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + return -1; +} + +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, + const char *name, FsCred *credp) +{ + int err = -1; + char *full_file_name; + char *full_dir_name; + DIR *dir; + + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && + local_is_mapped_file_metadata(fs_ctx, name)) { + errno = EINVAL; + return -1; + } + + dir = local_opendir_nofollow(fs_ctx, dir_path->data); + if (dir == NULL) { + return -1; + } + closedir(dir); + + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); + full_file_name = merge_fs_path(full_dir_name, name); + + if (fs_ctx->export_flags & V9FS_SM_MAPPED || + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + err = mkdir(full_file_name); + if (err == -1) { + goto out; + } + credp->fc_mode = credp->fc_mode; + + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + err = local_set_xattrat(full_dir_name, name, credp); + } else { + err = local_set_mapped_file_attrat(full_dir_name, name, credp); + } + if (err == -1) { + rmdir(full_file_name); + } + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || + fs_ctx->export_flags & V9FS_SM_NONE) { + err = mkdir(full_file_name); + if (err == -1) { + goto out; + } + /* Windows does not support chmod, do nothing here */ + } + + goto out; + +out: + g_free(full_dir_name); + g_free(full_file_name); + return err; +} + +static int local_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + + int err, fd; + char filename[NAME_MAX]; + char *dirpath; + char *name; + HANDLE hFile; + DWORD dwRet; + + if (fid_type == P9_FID_DIR) { + /* Windows does not support open directory */ + return -1; + } else { + fd = fs->fd; + } + + err = fstat(fd, stbuf); + if (err) { + return err; + } + + /* get real file name by fd */ + hFile = (HANDLE)_get_osfhandle(fd); + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); + + if (dwRet >= NAME_MAX) { + return -1; + } + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ + memmove(filename, filename + 4, NAME_MAX - 4); + + dirpath = g_path_get_dirname(filename); + name = g_path_get_basename(filename); + + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + /* Actual credentials are part of extended attrs */ + uid_t tmp_uid; + gid_t tmp_gid; + mode_t tmp_mode; + dev_t tmp_dev; + + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", + &tmp_uid, sizeof(uid_t)) > 0) { + stbuf->st_uid = le32_to_cpu(tmp_uid); + } + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", + &tmp_gid, sizeof(gid_t)) > 0) { + stbuf->st_gid = le32_to_cpu(tmp_gid); + } + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", + &tmp_mode, sizeof(mode_t)) > 0) { + stbuf->st_mode = (stbuf->st_mode & ~0777); + stbuf->st_mode |= le32_to_cpu(tmp_mode); + } + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", + &tmp_dev, sizeof(dev_t)) > 0) { + stbuf->st_rdev = le64_to_cpu(tmp_dev); + } + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + errno = EOPNOTSUPP; + g_free(dirpath); + g_free(name); + return -1; + } + + g_free(dirpath); + g_free(name); + + return err; +} + +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, + int flags, FsCred *credp, V9fsFidOpenState *fs) +{ + int fd = -1; + int err = -1; + char *full_file_name; + + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && + local_is_mapped_file_metadata(fs_ctx, name)) { + errno = EINVAL; + return -1; + } + + full_file_name = merge_fs_path(dir_path->data, name); + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); + g_free(full_file_name); + + err = fd; + fs->fd = fd; + goto out; + + close_preserve_errno(fd); +out: + return err; +} + + +static int local_symlink(FsContext *fs_ctx, const char *oldpath, + V9fsPath *dir_path, const char *name, FsCred *credp) +{ + return -1; +} + +static int local_link(FsContext *ctx, V9fsPath *oldpath, + V9fsPath *dirpath, const char *name) +{ + return -1; +} + +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) +{ + int fd, ret; + + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); + if (fd == -1) { + return -1; + } + ret = ftruncate(fd, size); + close_preserve_errno(fd); + return ret; +} + +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) +{ + char *full_file_name; + char *dirpath = g_path_get_dirname(fs_path->data); + char *name = g_path_get_basename(fs_path->data); + int ret = -1; + DIR *dir; + + dir = local_opendir_nofollow(fs_ctx, dirpath); + if (dir == NULL) { + goto out; + } + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); + + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || + (fs_ctx->export_flags & V9FS_SM_NONE)) { + /* Windows does not support chown() */ + ret = -1; + errno = ENOTSUP; + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { + ret = local_set_xattrat(full_file_name, name, credp); + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { + ret = local_set_mapped_file_attrat(full_file_name, name, credp); + } + g_free(full_file_name); + closedir(dir); +out: + g_free(name); + g_free(dirpath); + return ret; +} + +static int local_utimensat(FsContext *s, V9fsPath *fs_path, + const struct timespec *buf) +{ + struct utimbuf tm; + char *full_file_name; + int err; + + tm.actime = buf[0].tv_sec; + tm.modtime = buf[1].tv_sec; + + full_file_name = merge_fs_path(s->fs_root, fs_path->data); + err = utime(full_file_name, &tm); + g_free(full_file_name); + + return err; +} + +static int local_unlinkat_common(FsContext *ctx, const char *dirname, + const char *name, int flags) +{ + int ret; + char *full_file_name; + char *full_dir_name; + + full_dir_name = merge_fs_path(ctx->fs_root, dirname); + full_file_name = merge_fs_path(full_dir_name, name); + + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + char *full_meta_dir_name; + char *full_meta_file_name; + + /* + * We need to remove the metadata as well: + * - the metadata directory if we're removing a directory + * - the metadata file in the parent's metadata directory + * + * If any of these are missing (ie, ENOENT) then we're probably + * trying to remove something that wasn't created in mapped-file + * mode. We just ignore the error. + */ + + if ((flags & AT_REMOVEDIR) != 0) { + full_meta_dir_name = merge_fs_path(full_file_name, VIRTFS_META_DIR); + ret = rmdir(full_meta_dir_name); + g_free(full_meta_dir_name); + + if (ret < 0 && errno != ENOENT) { + g_free(full_file_name); + g_free(full_dir_name); + return -1; + } + } + + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); + ret = remove(full_meta_file_name); + g_free(full_meta_dir_name); + g_free(full_meta_file_name); + + if (ret < 0 && errno != ENOENT) { + g_free(full_dir_name); + g_free(full_file_name); + + return -1; + } + } + + if ((flags & AT_REMOVEDIR) != 0) { + ret = rmdir(full_file_name); + } else { + ret = remove(full_file_name); + } + + g_free(full_dir_name); + g_free(full_file_name); + + return ret; +} + +static int local_remove(FsContext *ctx, const char *path) +{ + int err; + DIR *stream; + char *full_file_name; + char *dirpath = g_path_get_dirname(path); + char *name = g_path_get_basename(path); + int flags = 0; + + full_file_name = merge_fs_path(ctx->fs_root, path); + stream = opendir(full_file_name); + if (stream != NULL) { + closedir(stream); + flags |= AT_REMOVEDIR; + } + err = local_unlinkat_common(ctx, dirpath, name, flags); + + g_free(name); + g_free(dirpath); + g_free(full_file_name); + return err; +} + +static int local_fsync(FsContext *ctx, int fid_type, + V9fsFidOpenState *fs, int datasync) +{ + if (fid_type != P9_FID_DIR) { + return _commit(fs->fd); + } + return 0; +} + +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) +{ + int ret; + ret = qemu_statfs(s->fs_root, stbuf); + if (ret == 0) { + /* use context address as fsid */ + memcpy(&stbuf->f_fsid, s, sizeof(long)); + } + + return ret; +} + +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, + const char *name, void *value, size_t size) +{ + return -1; +} + +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, + void *value, size_t size) +{ + return -1; +} + +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, + void *value, size_t size, int flags) +{ + return -1; +} + +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, + const char *name) +{ + return -1; +} + +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, + const char *name, V9fsPath *target) +{ + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && + local_is_mapped_file_metadata(ctx, name)) { + errno = EINVAL; + return -1; + } + + if (dir_path) { + if (!strcmp(name, ".")) { + /* "." relative to "foo/bar" is "foo/bar" */ + v9fs_path_copy(target, dir_path); + } else if (!strcmp(name, "..")) { + if (!strcmp(dir_path->data, ".")) { + /* ".." relative to the root is "." */ + v9fs_path_sprintf(target, "."); + } else { + char *tmp = g_path_get_dirname(dir_path->data); + /* + * Symbolic links are resolved by the client. We can assume + * that ".." relative to "foo/bar" is equivalent to "foo" + */ + v9fs_path_sprintf(target, "%s", tmp); + g_free(tmp); + } + } else { + assert(!strchr(name, '/')); + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); + } + } else if (!strcmp(name, "/") || !strcmp(name, ".") || + !strcmp(name, "..")) { + /* This is the root fid */ + v9fs_path_sprintf(target, "."); + } else { + assert(!strchr(name, '/')); + v9fs_path_sprintf(target, "./%s", name); + } + return 0; +} + +static int local_renameat(FsContext *ctx, V9fsPath *olddir, + const char *old_name, V9fsPath *newdir, + const char *new_name) +{ + return -1; +} + +static int local_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + int err; + + char *full_old_name; + char *full_new_name; + + full_old_name = merge_fs_path(ctx->fs_root, oldpath); + full_new_name = merge_fs_path(ctx->fs_root, newpath); + + err = rename(full_old_name, full_new_name); + + g_free(full_old_name); + g_free(full_new_name); + + return err; +} + +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, + const char *name, int flags) +{ + int ret; + + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && + local_is_mapped_file_metadata(ctx, name)) { + errno = EINVAL; + return -1; + } + + ret = local_unlinkat_common(ctx, dir->data, name, flags); + + return ret; +} + +static int check_filesystem_type(char *fs_root, int export_flags) +{ + HANDLE hFile; + wchar_t FsName[MAX_PATH + 1] = {0}; + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; + + if ((export_flags & V9FS_SM_MAPPED) == 0) { + return 0; + } + + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return -1; + } + + /* Get file system type name */ + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, + FsName, MAX_PATH + 1) == 0) { + CloseHandle(hFile); + return -1; + } + CloseHandle(hFile); + + if (wcscmp(FsName, NtfsName) != 0) { + return -1; + } + + return 0; +} + +static int local_init(FsContext *ctx, Error **errp) +{ + LocalData *data = g_malloc(sizeof(*data)); + + struct stat StatBuf; + + if (stat(ctx->fs_root, &StatBuf) != 0) { + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); + goto err; + } + + /* + * security_model=mapped(-xattr) requires a fileystem on Windows that + * supports Alternate Data Stream (ADS). NTFS is one of them, and is + * probably most popular on Windows. It is fair enough to assume + * Windows users to use NTFS for the mapped security model. + */ + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { + error_setg_errno(errp, EINVAL, "require NTFS file system when " + "security_model is mapped or mapped-xattr"); + goto err; + } + + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { + ctx->xops = passthrough_xattr_ops; + } else if (ctx->export_flags & V9FS_SM_MAPPED) { + ctx->xops = mapped_xattr_ops; + } else if (ctx->export_flags & V9FS_SM_NONE) { + ctx->xops = none_xattr_ops; + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { + /* + * xattr operation for mapped-file and passthrough + * remain same. + */ + ctx->xops = passthrough_xattr_ops; + } + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; + + ctx->private = data; + return 0; + +err: + g_free(data); + return -1; +} + +static void local_cleanup(FsContext *ctx) +{ + LocalData *data = ctx->private; + + if (!data) { + return; + } + + close(data->mountfd); + g_free(data); +} + +static void error_append_security_model_hint(Error *const *errp) +{ + error_append_hint(errp, "Valid options are: security_model=" + "[passthrough|mapped-xattr|mapped-file|none]\n"); +} + +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) +{ + ERRP_GUARD(); + const char *sec_model = qemu_opt_get(opts, "security_model"); + const char *path = qemu_opt_get(opts, "path"); + const char *multidevs = qemu_opt_get(opts, "multidevs"); + + if (!sec_model) { + error_setg(errp, "security_model property not set"); + error_append_security_model_hint(errp); + return -1; + } + + if (!strcmp(sec_model, "passthrough")) { + fse->export_flags |= V9FS_SM_PASSTHROUGH; + } else if (!strcmp(sec_model, "mapped") || + !strcmp(sec_model, "mapped-xattr")) { + fse->export_flags |= V9FS_SM_MAPPED; + } else if (!strcmp(sec_model, "none")) { + fse->export_flags |= V9FS_SM_NONE; + } else if (!strcmp(sec_model, "mapped-file")) { + fse->export_flags |= V9FS_SM_MAPPED_FILE; + } else { + error_setg(errp, "invalid security_model property '%s'", sec_model); + error_append_security_model_hint(errp); + return -1; + } + + if (multidevs) { + if (!strcmp(multidevs, "remap")) { + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; + fse->export_flags |= V9FS_REMAP_INODES; + } else if (!strcmp(multidevs, "forbid")) { + fse->export_flags &= ~V9FS_REMAP_INODES; + fse->export_flags |= V9FS_FORBID_MULTIDEVS; + } else if (!strcmp(multidevs, "warn")) { + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; + fse->export_flags &= ~V9FS_REMAP_INODES; + } else { + error_setg(errp, "invalid multidevs property '%s'", + multidevs); + error_append_hint(errp, "Valid options are: multidevs=" + "[remap|forbid|warn]\n"); + return -1; + } + } + + if (!path) { + error_setg(errp, "path property not set"); + return -1; + } + + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { + error_prepend(errp, "invalid throttle configuration: "); + return -1; + } + + if (fse->export_flags & V9FS_SM_MAPPED || + fse->export_flags & V9FS_SM_MAPPED_FILE) { + fse->fmode = + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; + fse->dmode = + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; + } else { + if (qemu_opt_find(opts, "fmode")) { + error_setg(errp, "fmode is only valid for mapped security modes"); + return -1; + } + if (qemu_opt_find(opts, "dmode")) { + error_setg(errp, "dmode is only valid for mapped security modes"); + return -1; + } + } + + fse->path = g_strdup(path); + + return 0; +} + +FileOperations local_ops = { + .parse_opts = local_parse_opts, + .init = local_init, + .cleanup = local_cleanup, + .lstat = local_lstat, + .readlink = local_readlink, + .close = local_close, + .closedir = local_closedir, + .open = local_open, + .opendir = local_opendir, + .rewinddir = local_rewinddir, + .telldir = local_telldir, + .readdir = local_readdir, + .seekdir = local_seekdir, + .preadv = local_preadv, + .pwritev = local_pwritev, + .chmod = local_chmod, + .mknod = local_mknod, + .mkdir = local_mkdir, + .fstat = local_fstat, + .open2 = local_open2, + .symlink = local_symlink, + .link = local_link, + .truncate = local_truncate, + .rename = local_rename, + .chown = local_chown, + .utimensat = local_utimensat, + .remove = local_remove, + .fsync = local_fsync, + .statfs = local_statfs, + .lgetxattr = local_lgetxattr, + .llistxattr = local_llistxattr, + .lsetxattr = local_lsetxattr, + .lremovexattr = local_lremovexattr, + .name_to_path = local_name_to_path, + .renameat = local_renameat, + .unlinkat = local_unlinkat, +}; diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c new file mode 100644 index 0000000000..d9b35e7425 --- /dev/null +++ b/hw/9pfs/9p-util-win32.c @@ -0,0 +1,303 @@ +/* + * 9p utilities (Windows Implementation) + * + * Copyright (c) 2022 Wind River Systems, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "9p.h" +#include "9p-util.h" +#include "9p-linux-errno.h" +#include <windows.h> +#include <dirent.h> + +#ifndef V9FS_MAGIC +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ +#endif + +static int build_ads_name(char *namebuf, size_t namebuflen, + const char *dirname, const char *filename, + const char *ads_name) +{ + size_t totalsize; + + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; + if (totalsize > namebuflen) { + return -1; + } + + /* + * NTFS ADS (Alternate Data Streams) name format: + * filename:ads_name + * e.g. + * d:\1.txt:my_ads_name + */ + strcpy(namebuf, dirname); + strcat(namebuf, "\\"); + strcat(namebuf, filename); + strcat(namebuf, ":"); + strcat(namebuf, ads_name); + + return 0; +} + +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, + char *fulladsname) +{ + char *p1, *p2; + + /* + * NTFS ADS (Alternate Data Streams) name from emurate data format: + * :ads_name:$DATA + * e.g. + * :my_ads_name:$DATA + * + * ADS name from FindNextStreamW() always have ":$DATA" string at the end + * + * This function copy ADS name to namebuf. + */ + + p1 = strchr(fulladsname, ':'); + if (p1 == NULL) { + return -1; + } + + p2 = strchr(p1 + 1, ':'); + if (p2 == NULL) { + return -1; + } + + /* skip empty ads name */ + if (p2 - p1 == 1) { + return 0; + } + + if (p2 - p1 + 1 > namebuflen) { + return -1; + } + + memcpy(namebuf, p1 + 1, p2 - p1 - 1); + namebuf[p2 - p1 - 1] = '\0'; + + return p2 - p1; +} + +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, + const char *name, void *value, size_t size) +{ + HANDLE hStream; + char ADSFileName[NAME_MAX + 1] = {0}; + DWORD dwBytesRead; + + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { + errno = EIO; + return -1; + } + + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hStream == INVALID_HANDLE_VALUE && + GetLastError() == ERROR_FILE_NOT_FOUND) { + errno = ENODATA; + return -1; + } + + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { + errno = EIO; + CloseHandle(hStream); + return -1; + } + + CloseHandle(hStream); + + return dwBytesRead; +} + +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, + char *list, size_t size) +{ + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; + char fulladsname[NAME_MAX + 1]; + char *full_fs_name = merge_fs_path(dirname, filename); + int ret; + HANDLE hFind; + WIN32_FIND_STREAM_DATA fsd; + BOOL bFindNext; + char *listptr = list; + size_t listleftsize = size; + + /* + * ADS emurate function only have WCHAR version, need to covert filename + * to WCHAR string. + */ + + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, + strlen(full_fs_name), WideCharStr, NAME_MAX); + g_free(full_fs_name); + if (ret == 0) { + errno = EIO; + return -1; + } + + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); + if (hFind == INVALID_HANDLE_VALUE) { + errno = ENODATA; + return -1; + } + + do { + memset(fulladsname, 0, sizeof(fulladsname)); + + /* + * ADS emurate function only have WCHAR version, need to covert + * cStreamName to utf-8 string. + */ + + ret = WideCharToMultiByte(CP_UTF8, 0, + fsd.cStreamName, wcslen(fsd.cStreamName) + 1, + fulladsname, sizeof(fulladsname) - 1, + NULL, NULL); + + if (ret == 0) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + errno = ERANGE; + } + CloseHandle(hFind); + return -1; + } + + ret = copy_ads_name(listptr, listleftsize, fulladsname); + if (ret < 0) { + errno = ERANGE; + CloseHandle(hFind); + return -1; + } + + listptr = listptr + ret; + listleftsize = listleftsize - ret; + + bFindNext = FindNextStreamW(hFind, &fsd); + } while (bFindNext); + + CloseHandle(hFind); + + return size - listleftsize; +} + +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, + const char *name) +{ + char ADSFileName[NAME_MAX + 1] = {0}; + + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { + errno = EIO; + return -1; + } + + if (DeleteFile(ADSFileName) != 0) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + errno = ENODATA; + return -1; + } + } + + return 0; +} + +int fsetxattrat_nofollow(const char *dirname, const char *filename, + const char *name, void *value, size_t size, int flags) +{ + HANDLE hStream; + char ADSFileName[NAME_MAX + 1] = {0}; + DWORD dwBytesWrite; + + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { + errno = EIO; + return -1; + } + + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hStream == INVALID_HANDLE_VALUE) { + errno = EIO; + return -1; + } + + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { + errno = EIO; + CloseHandle(hStream); + return -1; + } + + CloseHandle(hStream); + + return 0; +} + +int qemu_mknodat(const char *dirname, const char *filename, + mode_t mode, dev_t dev) +{ + errno = ENOTSUP; + return -1; +} + +int qemu_statfs(const char *fs_root, struct statfs *stbuf) +{ + HANDLE hFile; + char RealPath[NAME_MAX + 1]; + unsigned long SectorsPerCluster; + unsigned long BytesPerSector; + unsigned long NumberOfFreeClusters; + unsigned long TotalNumberOfClusters; + + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + errno = EIO; + return -1; + } + + /* get real path of root */ + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), + FILE_NAME_NORMALIZED) == 0) { + errno = EIO; + CloseHandle(hFile); + return -1; + } + + CloseHandle(hFile); + + /* + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". + * "C:\\123" will be "\\\\?\\C:\\123" + * Skip first 4 bytes and truncate the string at offset 7, it will get + * the real root directory like "C:\\", this is parameter GetDiskFreeSpace + * needed. + */ + + RealPath[7] = '\0'; + + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, + &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) { + errno = EIO; + return -1; + } + + stbuf->f_type = V9FS_MAGIC; + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; + stbuf->f_files = -1; + stbuf->f_ffree = -1; + stbuf->f_namelen = NAME_MAX; + stbuf->f_frsize = 0; + stbuf->f_flags = 0; + + return 0; +} diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c index 9ae69dd8db..5623f0e2ef 100644 --- a/hw/9pfs/9p-xattr.c +++ b/hw/9pfs/9p-xattr.c @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, char *orig_value, *orig_value_start; ssize_t xattr_len, parsed_len = 0, attr_len; char *dirpath, *name; +#ifdef CONFIG_WIN32 + char *full_dir_path; + DIR *dir; +#else int dirfd; +#endif /* Get the actual len */ dirpath = g_path_get_dirname(path); + +#ifdef CONFIG_WIN32 + dir = local_opendir_nofollow(ctx, dirpath); + if (dir == NULL) { + return -1; + } + closedir(dir); + + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); + g_free(dirpath); + + name = g_path_get_basename(path); + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); + if (xattr_len <= 0) { + g_free(name); + g_free(full_dir_path); + return xattr_len; + } + + /* Now fetch the xattr and find the actual size */ + orig_value = g_malloc(xattr_len); + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, + xattr_len); + g_free(name); + g_free(full_dir_path); + if (xattr_len < 0) { + g_free(orig_value); + return -1; + } +#else dirfd = local_opendir_nofollow(ctx, dirpath); g_free(dirpath); if (dirfd == -1) { @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, g_free(orig_value); return -1; } +#endif /* store the orig pointer */ orig_value_start = orig_value; @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, const char *name, void *value, size_t size) { +#ifdef CONFIG_WIN32 + char *dirpath = g_path_get_dirname(path); + char *filename = g_path_get_basename(path); + char *full_dir_path; + DIR *dir; + ssize_t ret = -1; + + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); + + dir = local_opendir_nofollow(ctx, dirpath); + if (dir == NULL) { + goto out; + } + closedir(dir); + + ret = fgetxattrat_nofollow(full_dir_path, filename, + name, value, size); + +out: + g_free(full_dir_path); + g_free(dirpath); + g_free(filename); + + return ret; +#else char *dirpath = g_path_get_dirname(path); char *filename = g_path_get_basename(path); int dirfd; @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, } ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); + close_preserve_errno(dirfd); out: g_free(dirpath); g_free(filename); return ret; +#endif } ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) { +#ifdef CONFIG_WIN32 + char *dirpath = g_path_get_dirname(path); + char *filename = g_path_get_basename(path); + char *full_dir_path; + DIR *dir; + ssize_t ret = -1; + + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); + + dir = local_opendir_nofollow(ctx, dirpath); + if (dir == NULL) { + goto out; + } + closedir(dir); + + ret = fsetxattrat_nofollow(full_dir_path, filename, name, + value, size, flags); +out: + g_free(full_dir_path); + g_free(dirpath); + g_free(filename); + return ret; + +#else char *dirpath = g_path_get_dirname(path); char *filename = g_path_get_basename(path); int dirfd; @@ -210,6 +297,7 @@ out: g_free(dirpath); g_free(filename); return ret; +#endif } int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, const char *name) { +#ifdef CONFIG_WIN32 + char *dirpath = g_path_get_dirname(path); + char *filename = g_path_get_basename(path); + char *full_dir_path; + DIR *dir; + ssize_t ret = -1; + + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); + + dir = local_opendir_nofollow(ctx, dirpath); + if (dir == NULL) { + goto out; + } + closedir(dir); + + ret = fremovexattrat_nofollow(full_dir_path, filename, name); + +out: + g_free(full_dir_path); + g_free(dirpath); + g_free(filename); + return ret; + +#else char *dirpath = g_path_get_dirname(path); char *filename = g_path_get_basename(path); int dirfd; @@ -237,6 +349,7 @@ out: g_free(dirpath); g_free(filename); return ret; +#endif } int pt_removexattr(FsContext *ctx, const char *path, const char *name) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 225f31fc31..a04889c1d6 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -38,6 +38,10 @@ #include "migration/blocker.h" #include "qemu/xxhash.h" #include <math.h> +#ifdef CONFIG_WIN32 +#define UTIME_NOW ((1l << 30) - 1l) +#define UTIME_OMIT ((1l << 30) - 2l) +#endif int open_fd_hw; int total_open_fd; @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp) if (S_ISDIR(stbuf->st_mode)) { qidp->type |= P9_QID_TYPE_DIR; } +#ifndef CONFIG_WIN32 if (S_ISLNK(stbuf->st_mode)) { qidp->type |= P9_QID_TYPE_SYMLINK; } +#endif return 0; } @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) ret |= S_IFDIR; } +#ifndef CONFIG_WIN32 if (mode & P9_STAT_MODE_SYMLINK) { ret |= S_IFLNK; } @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) if (mode & P9_STAT_MODE_NAMED_PIPE) { ret |= S_IFIFO; } +#endif if (mode & P9_STAT_MODE_DEVICE) { if (extension->size && extension->data[0] == 'c') { ret |= S_IFCHR; @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) ret |= S_IFREG; } +#ifndef CONFIG_WIN32 if (mode & P9_STAT_MODE_SETUID) { ret |= S_ISUID; } @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) if (mode & P9_STAT_MODE_SETVTX) { ret |= S_ISVTX; } +#endif return ret; } @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) mode |= P9_STAT_MODE_DIR; } +#ifndef CONFIG_WIN32 if (S_ISLNK(stbuf->st_mode)) { mode |= P9_STAT_MODE_SYMLINK; } @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) if (S_ISFIFO(stbuf->st_mode)) { mode |= P9_STAT_MODE_NAMED_PIPE; } +#endif if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { mode |= P9_STAT_MODE_DEVICE; } +#ifndef CONFIG_WIN32 if (stbuf->st_mode & S_ISUID) { mode |= P9_STAT_MODE_SETUID; } @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) if (stbuf->st_mode & S_ISVTX) { mode |= P9_STAT_MODE_SETVTX; } +#endif return mode; } @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, return err; } } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { +#ifndef CONFIG_WIN32 v9fs_string_sprintf(&v9stat->extension, "%c %u %u", S_ISCHR(stbuf->st_mode) ? 'c' : 'b', major(stbuf->st_rdev), minor(stbuf->st_rdev)); +#else + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', + 0, 0); +#endif + } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { v9fs_string_sprintf(&v9stat->extension, "%s %lu", "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, int32_t blksize) static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) { +#ifndef CONFIG_WIN32 return blksize_to_iounit(pdu, stbuf->st_blksize); +#else + return blksize_to_iounit(pdu, 0); +#endif } static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, v9lstat->st_gid = stbuf->st_gid; v9lstat->st_rdev = stbuf->st_rdev; v9lstat->st_size = stbuf->st_size; + +#ifdef CONFIG_WIN32 + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); + v9lstat->st_blocks = 0; + v9lstat->st_atime_sec = stbuf->st_atime; + v9lstat->st_mtime_sec = stbuf->st_mtime; + v9lstat->st_ctime_sec = stbuf->st_ctime; +#else /* !CONFIG_WIN32 */ v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); v9lstat->st_blocks = stbuf->st_blocks; v9lstat->st_atime_sec = stbuf->st_atime; @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; #endif +#endif /* CONFIG_WIN32 */ + /* Currently we only support BASIC fields in stat */ v9lstat->st_result_mask = P9_STATS_BASIC; @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) static bool same_stat_id(const struct stat *a, const struct stat *b) { +#ifdef CONFIG_WIN32 + return 0; +#else return a->st_dev == b->st_dev && a->st_ino == b->st_ino; +#endif /* CONFIG_WIN32 */ } static void coroutine_fn v9fs_walk(void *opaque) @@ -2300,7 +2339,11 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu, count += len; v9fs_stat_free(&v9stat); v9fs_path_free(&path); +#ifndef CONFIG_WIN32 saved_dir_pos = qemu_dirent_off(dent); +#else + saved_dir_pos = v9fs_co_telldir(pdu, fidp); +#endif } v9fs_readdir_unlock(&fidp->fs.dir); @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp, qid.version = 0; } +#ifdef CONFIG_WIN32 + /* + * Windows does not have dent->d_off, get offset by calling telldir() + * manually. + */ + off = v9fs_co_telldir(pdu, fidp); +#else off = qemu_dirent_off(dent); +#endif v9fs_string_init(&name); v9fs_string_sprintf(&name, "%s", dent->d_name); +#ifdef CONFIG_WIN32 + /* + * Windows does not have dent->d_type + */ + + len = pdu_marshal(pdu, 11 + count, "Qqbs", + &qid, off, + 0, &name); +#else /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ len = pdu_marshal(pdu, 11 + count, "Qqbs", &qid, off, dent->d_type, &name); +#endif v9fs_string_free(&name); @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) } nmode |= perm & 0777; + +#ifndef CONFIG_WIN32 err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, makedev(major, minor), nmode, &stbuf); +#else + err = -ENOTSUP; +#endif + if (err < 0) { goto out; } @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) v9fs_path_copy(&fidp->path, &path); v9fs_path_unlock(s); } else if (perm & P9_STAT_MODE_SOCKET) { +#ifndef CONFIG_WIN32 err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, 0, S_IFSOCK | (perm & 0777), &stbuf); +#else + err = -ENOTSUP; +#endif if (err < 0) { goto out; } @@ -3600,6 +3671,7 @@ out_nofid: static void coroutine_fn v9fs_mknod(void *opaque) { +#ifndef CONFIG_WIN32 int mode; gid_t gid; int32_t fid; @@ -3656,6 +3728,11 @@ out: out_nofid: pdu_complete(pdu, err); v9fs_string_free(&name); +#else + V9fsPDU *pdu = opaque; + + pdu_complete(pdu, -1); +#endif } /* @@ -3928,7 +4005,7 @@ out_nofid: #if defined(CONFIG_LINUX) /* Currently, only Linux has XATTR_SIZE_MAX */ #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX -#elif defined(CONFIG_DARWIN) +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) /* * Darwin doesn't seem to define a maximum xattr size in its user * space header, so manually configure it across platforms as 64k. @@ -3945,6 +4022,7 @@ out_nofid: static void coroutine_fn v9fs_xattrcreate(void *opaque) { +#ifndef CONFIG_WIN32 int flags, rflags = 0; int32_t fid; uint64_t size; @@ -4006,10 +4084,15 @@ out_put_fid: out_nofid: pdu_complete(pdu, err); v9fs_string_free(&name); +#else + V9fsPDU *pdu = opaque; + pdu_complete(pdu, -1); +#endif } static void coroutine_fn v9fs_readlink(void *opaque) { +#ifndef CONFIG_WIN32 V9fsPDU *pdu = opaque; size_t offset = 7; V9fsString target; @@ -4045,6 +4128,10 @@ out: put_fid(pdu, fidp); out_nofid: pdu_complete(pdu, err); +#else + V9fsPDU *pdu = opaque; + pdu_complete(pdu, -1); +#endif } static CoroutineEntry *pdu_co_handlers[] = { @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) { +#ifndef CONFIG_WIN32 struct rlimit rlim; if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { error_report("Failed to get the resource limit"); @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) } open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); open_fd_rc = rlim.rlim_cur / 2; +#endif } diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index 93ba44fb75..2fbe7b831b 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, int len, err = 0; int32_t size = 0; off_t saved_dir_pos; +#ifdef CONFIG_WIN32 + off_t next_dir_pos; +#endif struct dirent *dent; struct V9fsDirEnt *e = NULL; V9fsPath path; @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, break; } +#ifdef CONFIG_WIN32 + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); + if (next_dir_pos < 0) { + err = next_dir_pos; + goto out; + } +#endif + /* * stop this loop as soon as it would exceed the allowed maximum * response message size for the directory entries collected so far, @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, } size += len; +#ifndef CONFIG_WIN32 saved_dir_pos = qemu_dirent_off(dent); +#else + saved_dir_pos = next_dir_pos; +#endif } /* restore (last) saved position */ -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-04-25 14:27 ` [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows Bin Meng @ 2022-05-04 18:01 ` Christian Schoenebeck 2022-05-04 19:34 ` Shi, Guohuai 2022-05-09 14:29 ` Greg Kurz 1 sibling, 1 reply; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-04 18:01 UTC (permalink / raw) To: qemu-devel; +Cc: Greg Kurz, Bin Meng, Guohuai Shi, Bin Meng On Montag, 25. April 2022 16:27:01 CEST Bin Meng wrote: > From: Guohuai Shi <guohuai.shi@windriver.com> > > Add a 9p local file system backend driver to support Windows, > including open, read, write, close, rename, remove, etc. > > All security models are supported. The mapped (mapped-xattr) > security model is implemented using NTFS Alternate Data Stream > (ADS) so the 9p export path shall be on an NTFS partition. > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > Signed-off-by: Bin Meng <bin.meng@windriver.com> > --- > > hw/9pfs/9p-linux-errno.h | 151 +++++ > hw/9pfs/9p-local.h | 4 + > hw/9pfs/9p-util.h | 41 ++ > hw/9pfs/9p.h | 23 + > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > hw/9pfs/9p-xattr.c | 113 ++++ > hw/9pfs/9p.c | 91 ++- > hw/9pfs/codir.c | 15 + > 9 files changed, 1982 insertions(+), 1 deletion(-) > create mode 100644 hw/9pfs/9p-linux-errno.h > create mode 100644 hw/9pfs/9p-local-win32.c > create mode 100644 hw/9pfs/9p-util-win32.c This patch is definitely too huge and should be split up into a huge bunch of separate patches! > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > new file mode 100644 > index 0000000000..b0d6ac45ac > --- /dev/null > +++ b/hw/9pfs/9p-linux-errno.h This file definitely deserves a patch on its own. As for its filename: Following our current filename scheme, it would probably be better be named 9p-errno-linux.h or 9p-errno-dotl.h as this is probably a 9p protocol version thing. > @@ -0,0 +1,151 @@ > +/* > + * 9p Linux errno translation definition > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. + * See the COPYING file in the top-level directory. > + */ > + > +#include <errno.h> > + > +#ifndef QEMU_9P_LINUX_ERRNO_H > +#define QEMU_9P_LINUX_ERRNO_H > + > +/* > + * This file contains the Linux errno definitions to translate errnos set > by + * the 9P server (running on Windows) to a corresponding errno value. + > * > + * This list should be periodically reviewed and updated; particularly for > + * errnos that might be set as a result of a file system operation. > + */ > + I would just import the already existing sys/errno.h from the Linux kernel, with all its copyright header etc. and then with a 2nd patch just prefix the individual macros with DOTL_* > +#define L_EPERM 1 /* Operation not permitted */ > +#define L_ENOENT 2 /* No such file or directory */ > +#define L_ESRCH 3 /* No such process */ > +#define L_EINTR 4 /* Interrupted system call */ > +#define L_EIO 5 /* I/O error */ > +#define L_ENXIO 6 /* No such device or address */ > +#define L_E2BIG 7 /* Argument list too long */ > +#define L_ENOEXEC 8 /* Exec format error */ > +#define L_EBADF 9 /* Bad file number */ > +#define L_ECHILD 10 /* No child processes */ > +#define L_EAGAIN 11 /* Try again */ > +#define L_ENOMEM 12 /* Out of memory */ > +#define L_EACCES 13 /* Permission denied */ > +#define L_EFAULT 14 /* Bad address */ > +#define L_ENOTBLK 15 /* Block device required */ > +#define L_EBUSY 16 /* Device or resource busy */ > +#define L_EEXIST 17 /* File exists */ > +#define L_EXDEV 18 /* Cross-device link */ > +#define L_ENODEV 19 /* No such device */ > +#define L_ENOTDIR 20 /* Not a directory */ > +#define L_EISDIR 21 /* Is a directory */ > +#define L_EINVAL 22 /* Invalid argument */ > +#define L_ENFILE 23 /* File table overflow */ > +#define L_EMFILE 24 /* Too many open files */ > +#define L_ENOTTY 25 /* Not a typewriter */ > +#define L_ETXTBSY 26 /* Text file busy */ > +#define L_EFBIG 27 /* File too large */ > +#define L_ENOSPC 28 /* No space left on device */ > +#define L_ESPIPE 29 /* Illegal seek */ > +#define L_EROFS 30 /* Read-only file system */ > +#define L_EMLINK 31 /* Too many links */ > +#define L_EPIPE 32 /* Broken pipe */ > +#define L_EDOM 33 /* Math argument out of domain of func */ > +#define L_ERANGE 34 /* Math result not representable */ > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > +#define L_ENAMETOOLONG 36 /* File name too long */ > +#define L_ENOLCK 37 /* No record locks available */ > +#define L_ENOSYS 38 /* Function not implemented */ > +#define L_ENOTEMPTY 39 /* Directory not empty */ > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > +#define L_ENOMSG 42 /* No message of desired type */ > +#define L_EIDRM 43 /* Identifier removed */ > +#define L_ECHRNG 44 /* Channel number out of range */ > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > +#define L_EL3HLT 46 /* Level 3 halted */ > +#define L_EL3RST 47 /* Level 3 reset */ > +#define L_ELNRNG 48 /* Link number out of range */ > +#define L_EUNATCH 49 /* Protocol driver not attached */ > +#define L_ENOCSI 50 /* No CSI structure available */ > +#define L_EL2HLT 51 /* Level 2 halted */ > +#define L_EBADE 52 /* Invalid exchange */ > +#define L_EBADR 53 /* Invalid request descriptor */ > +#define L_EXFULL 54 /* Exchange full */ > +#define L_ENOANO 55 /* No anode */ > +#define L_EBADRQC 56 /* Invalid request code */ > +#define L_EBADSLT 57 /* Invalid slot */ > +#define L_EBFONT 58 /* Bad font file format */ > +#define L_ENOSTR 59 /* Device not a stream */ > +#define L_ENODATA 61 /* No data available */ > +#define L_ETIME 62 /* Timer expired */ > +#define L_ENOSR 63 /* Out of streams resources */ > +#define L_ENONET 64 /* Machine is not on the network */ > +#define L_ENOPKG 65 /* Package not installed */ > +#define L_EREMOTE 66 /* Object is remote */ > +#define L_ENOLINK 67 /* Link has been severed */ > +#define L_EADV 68 /* Advertise error */ > +#define L_ESRMNT 69 /* Srmount error */ > +#define L_ECOMM 70 /* Communication error on send */ > +#define L_EPROTO 71 /* Protocol error */ > +#define L_EMULTIHOP 72 /* Multihop attempted */ > +#define L_EDOTDOT 73 /* RFS specific error */ > +#define L_EBADMSG 74 /* Not a data message */ > +#define L_EOVERFLOW 75 /* Value too large for defined data type */ > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > +#define L_EBADFD 77 /* File descriptor in bad state */ > +#define L_EREMCHG 78 /* Remote address changed */ > +#define L_ELIBACC 79 /* Can not access a needed shared library > */ +#define L_ELIBBAD 80 /* Accessing a corrupted shared library > */ +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > +#define L_ELIBMAX 82 /* Attempting to link in too many shared > libs */ +#define L_ELIBEXEC 83 /* Cannot exec a shared library > directly */ +#define L_EILSEQ 84 /* Illegal byte sequence */ > +#define L_ERESTART 85 /* Interrupted system call should be > restarted */ +#define L_ESTRPIPE 86 /* Streams pipe error */ > +#define L_EUSERS 87 /* Too many users */ > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > +#define L_EDESTADDRREQ 89 /* Destination address required */ > +#define L_EMSGSIZE 90 /* Message too long */ > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport > endpoint */ +#define L_EPFNOSUPPORT 96 /* Protocol family not > supported */ +#define L_EAFNOSUPPORT 97 /* Address family not > supported by protocol */ +#define L_EADDRINUSE 98 /* Address > already in use */ > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > +#define L_ENETDOWN 100 /* Network is down */ > +#define L_ENETUNREACH 101 /* Network is unreachable */ > +#define L_ENETRESET 102 /* Network dropped connection because of > reset */ +#define L_ECONNABORTED 103 /* Software caused connection > abort */ +#define L_ECONNRESET 104 /* Connection reset by peer */ > +#define L_ENOBUFS 105 /* No buffer space available */ > +#define L_EISCONN 106 /* Transport endpoint is already connected > */ +#define L_ENOTCONN 107 /* Transport endpoint is not connected > */ +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint > shutdown */ +#define L_ETOOMANYREFS 109 /* Too many references: cannot > splice */ +#define L_ETIMEDOUT 110 /* Connection timed out */ > +#define L_ECONNREFUSED 111 /* Connection refused */ > +#define L_EHOSTDOWN 112 /* Host is down */ > +#define L_EHOSTUNREACH 113 /* No route to host */ > +#define L_EALREADY 114 /* Operation already in progress */ > +#define L_EINPROGRESS 115 /* Operation now in progress */ > +#define L_ESTALE 116 /* Stale NFS file handle */ > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > +#define L_EISNAM 120 /* Is a named type file */ > +#define L_EREMOTEIO 121 /* Remote I/O error */ > +#define L_EDQUOT 122 /* Quota exceeded */ > +#define L_ENOMEDIUM 123 /* No medium found */ > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > +#define L_ECANCELED 125 /* Operation Canceled */ > +#define L_ENOKEY 126 /* Required key not available */ > +#define L_EKEYEXPIRED 127 /* Key has expired */ > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > +#define L_EOWNERDEAD 130 /* Owner died */ > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > + > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > index 55ea4b7883..4c75876e19 100644 > --- a/hw/9pfs/9p-local.h > +++ b/hw/9pfs/9p-local.h > @@ -31,6 +31,10 @@ static inline bool > local_is_mapped_file_metadata(FsContext *fs_ctx, > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > mode_t mode); > +#ifndef CONFIG_WIN32 > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > +#else > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > +#endif > > #endif Same goes for all the rest ... this should all be split up into individual patches, some of the changes of a file probably with separate patches as well. As this looks now, it's simply not clear what all these hunks of this huge patch are needed for. Stopping here for now. > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > index 97e681e167..6eadb38e1d 100644 > --- a/hw/9pfs/9p-util.h > +++ b/hw/9pfs/9p-util.h > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > errno = serrno; > } > > +#ifndef CONFIG_WIN32 > static inline int openat_dir(int dirfd, const char *name) > { > return openat(dirfd, name, > @@ -89,7 +90,9 @@ again: > errno = serrno; > return fd; > } > +#endif /* !CONFIG_WIN32 */ > > +#ifndef CONFIG_WIN32 > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > void *value, size_t size); > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char > *filename, char *list, size_t size); > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > const char *name); > +#else > + > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size); > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size, > + int flags); > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > + char *list, size_t size); > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > + const char *name); > + > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > + > +static inline char *merge_fs_path(const char *path1, const char *path2) > +{ > + char *full_fs_path; > + size_t full_fs_path_len; > + > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > + full_fs_path = g_malloc(full_fs_path_len); > > + strcpy(full_fs_path, path1); > + strcat(full_fs_path, "\\"); > + strcat(full_fs_path, path2); > + > + return full_fs_path; > +} > + > +#endif /* !CONFIG_WIN32 */ > + > +#ifndef CONFIG_WIN32 > /* > * Darwin has d_seekoff, which appears to function similarly to d_off. > * However, it does not appear to be supported on all file systems, > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > return dent->d_off; > #endif > } > +#endif /* !CONFIG_WIN32 */ > > /** > * qemu_dirent_dup() - Duplicate directory entry @dent. > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct > dirent *dent) #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > #endif > + > +#ifndef CONFIG_WIN32 > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > +#else > +int qemu_mknodat(const char *dirname, const char *filename, > + mode_t mode, dev_t dev); > +#endif > > #endif > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > index 994f952600..87e8eac840 100644 > --- a/hw/9pfs/9p.h > +++ b/hw/9pfs/9p.h > @@ -3,13 +3,36 @@ > > #include <dirent.h> > #include <utime.h> > +#ifndef CONFIG_WIN32 > #include <sys/resource.h> > +#endif > #include "fsdev/file-op-9p.h" > #include "fsdev/9p-iov-marshal.h" > #include "qemu/thread.h" > #include "qemu/coroutine.h" > #include "qemu/qht.h" > > +#ifdef CONFIG_WIN32 > + > +#define O_NOCTTY 0 > +#define O_NDELAY 0 > +#define O_NONBLOCK 0 > +#define O_DSYNC 0 > +#define O_DIRECT 0 > +#define O_NOFOLLOW 0 > +#define O_NOATIME 0 > +#define O_SYNC 0 > +#define O_ASYNC 0 > +#define O_DIRECTORY 02000000 Again, these are POSIX types. I don't think declaring them belongs into this header file. > + > +#define FASYNC 0 > + > +#define AT_REMOVEDIR 1 > + > +#define NAME_MAX 260 > + > +#endif > + > enum { > P9_TLERROR = 6, > P9_RLERROR, > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > new file mode 100644 > index 0000000000..aab7c9f7b5 > --- /dev/null > +++ b/hw/9pfs/9p-local-win32.c > @@ -0,0 +1,1242 @@ > +/* > + * 9p Windows callback > + * > + * Copyright (c) 2022 Wind River Systems, Inc. > + * > + * Based on hw/9pfs/9p-local.c > + * > + * This work is licensed under the terms of the GNU GPL, version 2. See > + * the COPYING file in the top-level directory. > + */ > + > +/* > + * Not so fast! You might want to read the 9p developer docs first: > + * https://wiki.qemu.org/Documentation/9p > + */ > + > +#include "qemu/osdep.h" > +#include <windows.h> > +#include <dirent.h> > +#include "9p.h" > +#include "9p-local.h" > +#include "9p-xattr.h" > +#include "9p-util.h" > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > +#include "qapi/error.h" > +#include "qemu/cutils.h" > +#include "qemu/error-report.h" > +#include "qemu/option.h" > +#include <libgen.h> I'm not sure whether all of this (i.e. 9p-local-win32.c in general) is really needed. I mean yes, it probably does the job, but you basically add a complete separate 'local' backend implementation just for the Windows host platform. My expectation would be to stick to 9p-local.c being OS-agnostic, and only define OS-specific functionality in 9p-util-windows.c if needed. And most importantly: don't duplicate code as far as possible! I mean somebody would need to also review and maintain all of this. > + > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > + int flags, mode_t mode) > +{ > + char *full_file_name; > + int fd; > + > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > + fd = open(full_file_name, flags | _O_BINARY, mode); > + g_free(full_file_name); > + > + return fd; > +} > + > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > +{ > + char *full_file_name; > + DIR *dir; > + > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > + dir = opendir(full_file_name); > + g_free(full_file_name); > + return dir; > +} > + > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > + mode_t mode) > +{ > + int fd; > + > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { > + /* Windows does not allow call open() for a directory */ > + fd = -1; > + } else { > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); > + } > + > + return fd; > +} > + > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > +{ > + return opendir_with_ctx(fs_ctx, path); > +} > + > +static FILE *local_fopenat(const char *dirname, const char *name, > + const char *mode) > +{ > + char *full_file_name; > + char modestr[3] = {0}; > + FILE *fp; > + > + /* > + * only supports two modes > + */ > + if (mode[0] == 'r') { > + modestr[0] = 'r'; > + } else if (mode[0] == 'w') { > + modestr[0] = 'w'; > + } else { > + return NULL; > + } > + /* Windows host needs 'b' flag */ > + modestr[1] = 'b'; > + > + full_file_name = merge_fs_path(dirname, name); > + fp = fopen(full_file_name, modestr); > + g_free(full_file_name); > + > + return fp; > +} > + > +static void local_mapped_file_attr(const char *dirpath, const char *name, > + struct stat *stbuf) > +{ > + FILE *fp; > + char buf[ATTR_MAX]; > + char *full_file_name; > + > + if (strcmp(name, ".") != 0) { > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > + fp = local_fopenat(full_file_name, name, "r"); > + g_free(full_file_name); > + } else { > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > + } > + if (!fp) { > + return; > + } > + > + memset(buf, 0, ATTR_MAX); > + while (fgets(buf, ATTR_MAX, fp)) { > + if (!strncmp(buf, "virtfs.uid", 10)) { > + stbuf->st_uid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > + stbuf->st_gid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > + stbuf->st_mode = (stbuf->st_mode & ~0777); > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > + stbuf->st_rdev = atoi(buf + 12); > + } > + memset(buf, 0, ATTR_MAX); > + } > + fclose(fp); > +} > + > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat > *stbuf) +{ > + int err = -1; > + char *full_dir_name, *full_file_name; > + char *dirpath = g_path_get_dirname(fs_path->data); > + char *name = g_path_get_basename(fs_path->data); > + > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > + full_file_name = merge_fs_path(full_dir_name, name); > + err = stat(full_file_name, stbuf); > + > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > + /* > + * Hard code for root directory on Windows host. > + * This will root directory have a special inode number, > + * then guest OS can detect it is a special directory. > + */ > + stbuf->st_ino = 2; > + } > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + /* Actual credentials are part of extended attrs */ > + uid_t tmp_uid; > + gid_t tmp_gid; > + mode_t tmp_mode; > + dev_t tmp_dev; > + > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > + &tmp_uid, sizeof(uid_t)) > 0) { > + stbuf->st_uid = le32_to_cpu(tmp_uid); > + } > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > + &tmp_gid, sizeof(gid_t)) > 0) { > + stbuf->st_gid = le32_to_cpu(tmp_gid); > + } > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > + &tmp_mode, sizeof(mode_t)) > 0) { > + stbuf->st_mode = (stbuf->st_mode & ~0777); > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > + } > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > + &tmp_dev, sizeof(dev_t)) > 0) { > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > + } > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + local_mapped_file_attr(full_dir_name, name, stbuf); > + } > + > + g_free(full_file_name); > + g_free(full_dir_name); > + > + if (err) { > + goto err_out; > + } > + > +err_out: > + g_free(name); > + g_free(dirpath); > + return err; > +} > + > +static int local_set_mapped_file_attrat(const char *dirname, const char > *name, + FsCred *credp) > +{ > + FILE *fp; > + int ret; > + char buf[ATTR_MAX]; > + int uid = -1, gid = -1, mode = -1, rdev = -1; > + bool is_root = !strcmp(name, "."); > + char *full_dir_name; > + > + if (is_root) { > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > + if (!fp) { > + if (errno == ENOENT) { > + goto update_map_file; > + } else { > + return -1; > + } > + } > + } else { > + /* > + * mapped-file: > + * <sub_file> attribute stored to: > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > + */ > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > + ret = mkdir(full_dir_name); > + > + if (ret < 0 && errno != EEXIST) { > + g_free(full_dir_name); > + return -1; > + } > + > + fp = local_fopenat(full_dir_name, name, "r"); > + if (!fp) { > + if (errno == ENOENT) { > + goto update_map_file; > + } else { > + g_free(full_dir_name); > + return -1; > + } > + } > + } > + > + memset(buf, 0, ATTR_MAX); > + while (fgets(buf, ATTR_MAX, fp)) { > + if (!strncmp(buf, "virtfs.uid", 10)) { > + uid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > + gid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > + mode = atoi(buf + 12); > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > + rdev = atoi(buf + 12); > + } > + memset(buf, 0, ATTR_MAX); > + } > + fclose(fp); > + > +update_map_file: > + if (is_root) { > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > + } else { > + fp = local_fopenat(full_dir_name, name, "w"); > + g_free(full_dir_name); > + } > + if (!fp) { > + return -1; > + } > + > + if (credp->fc_uid != -1) { > + uid = credp->fc_uid; > + } > + if (credp->fc_gid != -1) { > + gid = credp->fc_gid; > + } > + if (credp->fc_mode != (mode_t)-1) { > + mode = credp->fc_mode; > + } > + if (credp->fc_rdev != -1) { > + rdev = credp->fc_rdev; > + } > + > + if (uid != -1) { > + fprintf(fp, "virtfs.uid=%d\n", uid); > + } > + if (gid != -1) { > + fprintf(fp, "virtfs.gid=%d\n", gid); > + } > + if (mode != -1) { > + fprintf(fp, "virtfs.mode=%d\n", mode); > + } > + if (rdev != -1) { > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > + } > + fclose(fp); > + > + return 0; > +} > + > +static int local_set_xattrat(const char *dirname, const char *path, > + FsCred *credp) > +{ > + int err; > + > + if (credp->fc_uid != -1) { > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > + &tmp_uid, sizeof(uid_t), 0); > + if (err) { > + return err; > + } > + } > + if (credp->fc_gid != -1) { > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > + &tmp_gid, sizeof(gid_t), 0); > + if (err) { > + return err; > + } > + } > + if (credp->fc_mode != (mode_t)-1) { > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > + &tmp_mode, sizeof(mode_t), 0); > + if (err) { > + return err; > + } > + } > + if (credp->fc_rdev != -1) { > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > + &tmp_rdev, sizeof(dev_t), 0); > + if (err) { > + return err; > + } > + } > + return 0; > +} > + > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > + char *buf, size_t bufsz) > +{ > + return -1; > +} > + > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + return close(fs->fd); > +} > + > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + return closedir(fs->dir.stream); > +} > + > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > + int flags, V9fsFidOpenState *fs) > +{ > + int fd; > + > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > + if (fd == -1) { > + return -1; > + } > + fs->fd = fd; > + return fs->fd; > +} > + > +static int local_opendir(FsContext *ctx, > + V9fsPath *fs_path, V9fsFidOpenState *fs) > +{ > + DIR *stream; > + char *full_file_name; > + > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > + stream = opendir(full_file_name); > + g_free(full_file_name); > + > + if (!stream) { > + return -1; > + } > + > + fs->dir.stream = stream; > + return 0; > +} > + > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + rewinddir(fs->dir.stream); > +} > + > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + return telldir(fs->dir.stream); > +} > + > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + struct dirent *entry; > + > +again: > + entry = readdir(fs->dir.stream); > + if (!entry) { > + return NULL; > + } > + > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > + /* skip the meta data */ > + goto again; > + } > + } > + > + return entry; > +} > + > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) > +{ > + off_t count; > + struct dirent *findentry; > + struct dirent *entry; > + size_t namelen[3] = {0}; > + off_t direntoff[3] = {-1, -1, -1}; > + char *d_name[3]; > + int i; > + > + /* > + * MinGW's seekdir() requires directory is not modified. If guest OS is > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", + > * then MinGW's seekdir() will seek to a wrong offset. > + * > + * This function saves some old offset directory entry name, > + * and lookup current entry again, and compare the offset. > + * > + * If new offset is less than old offset, that means someone is > deleting + * files in the directory, thus we need to seek offset > backward. + * > + * If new offset is larger than old offset, that means someone is > creating + * files in the directory, thus we need to seek offset > forward. + */ > + > + direntoff[0] = telldir(fs->dir.stream); > + > + /* do nothing if current offset is 0 or EOF */ > + if (direntoff[0] == 0 || direntoff[0] < 0) { > + seekdir(fs->dir.stream, off); > + return ; > + } > + > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > + d_name[1] = d_name[0] + sizeof(entry->d_name); > + d_name[2] = d_name[1] + sizeof(entry->d_name); > + > + /* save 3 nearest entries and offsets */ > + for (i = 0; i < 3; i++) { > + entry = &fs->dir.stream->dd_dir; > + > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > + namelen[i] = strlen(d_name[i]) + 1; > + > + direntoff[i] = telldir(fs->dir.stream); > + > + entry = readdir(fs->dir.stream); > + if (entry == NULL) { > + break; > + } > + } > + > + /* lookup saved entries again */ > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > + rewinddir(fs->dir.stream); > + count = 0; > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > + count++; > + > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > + if (count + i == direntoff[i]) { > + seekdir(fs->dir.stream, off); > + goto out; > + } else if (count + i < direntoff[i]) { > + off = off - (direntoff[i] - count) - i; > + if (off <= 0) { > + off = 0; > + } > + seekdir(fs->dir.stream, off); > + goto out; > + } else { > + off = off + (count - direntoff[i]) - i; > + seekdir(fs->dir.stream, off); > + goto out; > + } > + } > + } > + } > + /* can not get anything, seek backward */ > + off = off - 1; > + > + seekdir(fs->dir.stream, off); > +out: > + g_free(d_name[0]); > + return ; > +} > + > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > + const struct iovec *iov, > + int iovcnt, off_t offset) > +{ > +#ifdef CONFIG_PREADV > + return preadv(fs->fd, iov, iovcnt, offset); > +#else > + int err = lseek(fs->fd, offset, SEEK_SET); > + if (err == -1) { > + return err; > + } else { > + return readv(fs->fd, iov, iovcnt); > + } > +#endif > +} > + > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > + const struct iovec *iov, > + int iovcnt, off_t offset) > +{ > + ssize_t ret; > +#ifdef CONFIG_PREADV > + ret = pwritev(fs->fd, iov, iovcnt, offset); > +#else > + int err = lseek(fs->fd, offset, SEEK_SET); > + if (err == -1) { > + return err; > + } else { > + ret = writev(fs->fd, iov, iovcnt); > + } > +#endif > +#ifdef CONFIG_SYNC_FILE_RANGE > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > + /* > + * Initiate a writeback. This is not a data integrity sync. > + * We want to ensure that we don't leave dirty pages in the cache > + * after write when writeout=immediate is sepcified. > + */ > + sync_file_range(fs->fd, offset, ret, > + SYNC_FILE_RANGE_WAIT_BEFORE | > SYNC_FILE_RANGE_WRITE); + } > +#endif > + return ret; > +} > + > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > +{ > + char *dirpath = g_path_get_dirname(fs_path->data); > + char *name = g_path_get_basename(fs_path->data); > + int ret = -1; > + char *full_file_name = NULL; > + DIR *dir; > + dir = local_opendir_nofollow(fs_ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + ret = local_set_xattrat(full_file_name, name, credp); > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > + fs_ctx->export_flags & V9FS_SM_NONE) { > + ret = -1; > + errno = ENOTSUP; > + } > + closedir(dir); > + > +out: > + if (full_file_name != NULL) { > + g_free(full_file_name); > + } > + > + g_free(dirpath); > + g_free(name); > + return ret; > +} > + > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > + const char *name, FsCred *credp) > +{ > + return -1; > +} > + > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > + const char *name, FsCred *credp) > +{ > + int err = -1; > + char *full_file_name; > + char *full_dir_name; > + DIR *dir; > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(fs_ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > + if (dir == NULL) { > + return -1; > + } > + closedir(dir); > + > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > + full_file_name = merge_fs_path(full_dir_name, name); > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + err = mkdir(full_file_name); > + if (err == -1) { > + goto out; > + } > + credp->fc_mode = credp->fc_mode; > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + err = local_set_xattrat(full_dir_name, name, credp); > + } else { > + err = local_set_mapped_file_attrat(full_dir_name, name, credp); > + } > + if (err == -1) { > + rmdir(full_file_name); > + } > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > + fs_ctx->export_flags & V9FS_SM_NONE) { > + err = mkdir(full_file_name); > + if (err == -1) { > + goto out; > + } > + /* Windows does not support chmod, do nothing here */ > + } > + > + goto out; > + > +out: > + g_free(full_dir_name); > + g_free(full_file_name); > + return err; > +} > + > +static int local_fstat(FsContext *fs_ctx, int fid_type, > + V9fsFidOpenState *fs, struct stat *stbuf) > +{ > + > + int err, fd; > + char filename[NAME_MAX]; > + char *dirpath; > + char *name; > + HANDLE hFile; > + DWORD dwRet; > + > + if (fid_type == P9_FID_DIR) { > + /* Windows does not support open directory */ > + return -1; > + } else { > + fd = fs->fd; > + } > + > + err = fstat(fd, stbuf); > + if (err) { > + return err; > + } > + > + /* get real file name by fd */ > + hFile = (HANDLE)_get_osfhandle(fd); > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); > + > + if (dwRet >= NAME_MAX) { > + return -1; > + } > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > + memmove(filename, filename + 4, NAME_MAX - 4); > + > + dirpath = g_path_get_dirname(filename); > + name = g_path_get_basename(filename); > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + /* Actual credentials are part of extended attrs */ > + uid_t tmp_uid; > + gid_t tmp_gid; > + mode_t tmp_mode; > + dev_t tmp_dev; > + > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > + &tmp_uid, sizeof(uid_t)) > 0) { > + stbuf->st_uid = le32_to_cpu(tmp_uid); > + } > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > + &tmp_gid, sizeof(gid_t)) > 0) { > + stbuf->st_gid = le32_to_cpu(tmp_gid); > + } > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > + &tmp_mode, sizeof(mode_t)) > 0) { > + stbuf->st_mode = (stbuf->st_mode & ~0777); > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > + } > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > + &tmp_dev, sizeof(dev_t)) > 0) { > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > + } > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + errno = EOPNOTSUPP; > + g_free(dirpath); > + g_free(name); > + return -1; > + } > + > + g_free(dirpath); > + g_free(name); > + > + return err; > +} > + > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char > *name, + int flags, FsCred *credp, V9fsFidOpenState > *fs) +{ > + int fd = -1; > + int err = -1; > + char *full_file_name; > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(fs_ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + full_file_name = merge_fs_path(dir_path->data, name); > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > + g_free(full_file_name); > + > + err = fd; > + fs->fd = fd; > + goto out; > + > + close_preserve_errno(fd); > +out: > + return err; > +} > + > + > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > + V9fsPath *dir_path, const char *name, FsCred > *credp) +{ > + return -1; > +} > + > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > + V9fsPath *dirpath, const char *name) > +{ > + return -1; > +} > + > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > +{ > + int fd, ret; > + > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > + if (fd == -1) { > + return -1; > + } > + ret = ftruncate(fd, size); > + close_preserve_errno(fd); > + return ret; > +} > + > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > +{ > + char *full_file_name; > + char *dirpath = g_path_get_dirname(fs_path->data); > + char *name = g_path_get_basename(fs_path->data); > + int ret = -1; > + DIR *dir; > + > + dir = local_opendir_nofollow(fs_ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > + > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > + /* Windows does not support chown() */ > + ret = -1; > + errno = ENOTSUP; > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + ret = local_set_xattrat(full_file_name, name, credp); > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > + } > + g_free(full_file_name); > + closedir(dir); > +out: > + g_free(name); > + g_free(dirpath); > + return ret; > +} > + > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > + const struct timespec *buf) > +{ > + struct utimbuf tm; > + char *full_file_name; > + int err; > + > + tm.actime = buf[0].tv_sec; > + tm.modtime = buf[1].tv_sec; > + > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > + err = utime(full_file_name, &tm); > + g_free(full_file_name); > + > + return err; > +} > + > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > + const char *name, int flags) > +{ > + int ret; > + char *full_file_name; > + char *full_dir_name; > + > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > + full_file_name = merge_fs_path(full_dir_name, name); > + > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + char *full_meta_dir_name; > + char *full_meta_file_name; > + > + /* > + * We need to remove the metadata as well: > + * - the metadata directory if we're removing a directory > + * - the metadata file in the parent's metadata directory > + * > + * If any of these are missing (ie, ENOENT) then we're probably > + * trying to remove something that wasn't created in mapped-file > + * mode. We just ignore the error. > + */ > + > + if ((flags & AT_REMOVEDIR) != 0) { > + full_meta_dir_name = merge_fs_path(full_file_name, > VIRTFS_META_DIR); + ret = rmdir(full_meta_dir_name); > + g_free(full_meta_dir_name); > + > + if (ret < 0 && errno != ENOENT) { > + g_free(full_file_name); > + g_free(full_dir_name); > + return -1; > + } > + } > + > + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); + > ret = remove(full_meta_file_name); > + g_free(full_meta_dir_name); > + g_free(full_meta_file_name); > + > + if (ret < 0 && errno != ENOENT) { > + g_free(full_dir_name); > + g_free(full_file_name); > + > + return -1; > + } > + } > + > + if ((flags & AT_REMOVEDIR) != 0) { > + ret = rmdir(full_file_name); > + } else { > + ret = remove(full_file_name); > + } > + > + g_free(full_dir_name); > + g_free(full_file_name); > + > + return ret; > +} > + > +static int local_remove(FsContext *ctx, const char *path) > +{ > + int err; > + DIR *stream; > + char *full_file_name; > + char *dirpath = g_path_get_dirname(path); > + char *name = g_path_get_basename(path); > + int flags = 0; > + > + full_file_name = merge_fs_path(ctx->fs_root, path); > + stream = opendir(full_file_name); > + if (stream != NULL) { > + closedir(stream); > + flags |= AT_REMOVEDIR; > + } > + err = local_unlinkat_common(ctx, dirpath, name, flags); > + > + g_free(name); > + g_free(dirpath); > + g_free(full_file_name); > + return err; > +} > + > +static int local_fsync(FsContext *ctx, int fid_type, > + V9fsFidOpenState *fs, int datasync) > +{ > + if (fid_type != P9_FID_DIR) { > + return _commit(fs->fd); > + } > + return 0; > +} > + > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs > *stbuf) +{ > + int ret; > + ret = qemu_statfs(s->fs_root, stbuf); > + if (ret == 0) { > + /* use context address as fsid */ > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > + } > + > + return ret; > +} > + > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > + const char *name, void *value, size_t size) > +{ > + return -1; > +} > + > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > + void *value, size_t size) > +{ > + return -1; > +} > + > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char > *name, + void *value, size_t size, int flags) > +{ > + return -1; > +} > + > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > + const char *name) > +{ > + return -1; > +} > + > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > + const char *name, V9fsPath *target) > +{ > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + if (dir_path) { > + if (!strcmp(name, ".")) { > + /* "." relative to "foo/bar" is "foo/bar" */ > + v9fs_path_copy(target, dir_path); > + } else if (!strcmp(name, "..")) { > + if (!strcmp(dir_path->data, ".")) { > + /* ".." relative to the root is "." */ > + v9fs_path_sprintf(target, "."); > + } else { > + char *tmp = g_path_get_dirname(dir_path->data); > + /* > + * Symbolic links are resolved by the client. We can assume > + * that ".." relative to "foo/bar" is equivalent to "foo" > + */ > + v9fs_path_sprintf(target, "%s", tmp); > + g_free(tmp); > + } > + } else { > + assert(!strchr(name, '/')); > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > + } > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > + !strcmp(name, "..")) { > + /* This is the root fid */ > + v9fs_path_sprintf(target, "."); > + } else { > + assert(!strchr(name, '/')); > + v9fs_path_sprintf(target, "./%s", name); > + } > + return 0; > +} > + > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > + const char *old_name, V9fsPath *newdir, > + const char *new_name) > +{ > + return -1; > +} > + > +static int local_rename(FsContext *ctx, const char *oldpath, > + const char *newpath) > +{ > + int err; > + > + char *full_old_name; > + char *full_new_name; > + > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > + > + err = rename(full_old_name, full_new_name); > + > + g_free(full_old_name); > + g_free(full_new_name); > + > + return err; > +} > + > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > + const char *name, int flags) > +{ > + int ret; > + > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > + > + return ret; > +} > + > +static int check_filesystem_type(char *fs_root, int export_flags) > +{ > + HANDLE hFile; > + wchar_t FsName[MAX_PATH + 1] = {0}; > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > + > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > + return 0; > + } > + > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > + if (hFile == INVALID_HANDLE_VALUE) { > + return -1; > + } > + > + /* Get file system type name */ > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > + FsName, MAX_PATH + 1) == 0) { > + CloseHandle(hFile); > + return -1; > + } > + CloseHandle(hFile); > + > + if (wcscmp(FsName, NtfsName) != 0) { > + return -1; > + } > + > + return 0; > +} > + > +static int local_init(FsContext *ctx, Error **errp) > +{ > + LocalData *data = g_malloc(sizeof(*data)); > + > + struct stat StatBuf; > + > + if (stat(ctx->fs_root, &StatBuf) != 0) { > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > + goto err; > + } > + > + /* > + * security_model=mapped(-xattr) requires a fileystem on Windows that > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > + * probably most popular on Windows. It is fair enough to assume > + * Windows users to use NTFS for the mapped security model. > + */ > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > + "security_model is mapped or mapped-xattr"); > + goto err; > + } > + > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > + ctx->xops = passthrough_xattr_ops; > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > + ctx->xops = mapped_xattr_ops; > + } else if (ctx->export_flags & V9FS_SM_NONE) { > + ctx->xops = none_xattr_ops; > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + /* > + * xattr operation for mapped-file and passthrough > + * remain same. > + */ > + ctx->xops = passthrough_xattr_ops; > + } > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > + > + ctx->private = data; > + return 0; > + > +err: > + g_free(data); > + return -1; > +} > + > +static void local_cleanup(FsContext *ctx) > +{ > + LocalData *data = ctx->private; > + > + if (!data) { > + return; > + } > + > + close(data->mountfd); > + g_free(data); > +} > + > +static void error_append_security_model_hint(Error *const *errp) > +{ > + error_append_hint(errp, "Valid options are: security_model=" > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > +} > + > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error > **errp) +{ > + ERRP_GUARD(); > + const char *sec_model = qemu_opt_get(opts, "security_model"); > + const char *path = qemu_opt_get(opts, "path"); > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > + > + if (!sec_model) { > + error_setg(errp, "security_model property not set"); > + error_append_security_model_hint(errp); > + return -1; > + } > + > + if (!strcmp(sec_model, "passthrough")) { > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > + } else if (!strcmp(sec_model, "mapped") || > + !strcmp(sec_model, "mapped-xattr")) { > + fse->export_flags |= V9FS_SM_MAPPED; > + } else if (!strcmp(sec_model, "none")) { > + fse->export_flags |= V9FS_SM_NONE; > + } else if (!strcmp(sec_model, "mapped-file")) { > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > + } else { > + error_setg(errp, "invalid security_model property '%s'", > sec_model); + error_append_security_model_hint(errp); > + return -1; > + } > + > + if (multidevs) { > + if (!strcmp(multidevs, "remap")) { > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > + fse->export_flags |= V9FS_REMAP_INODES; > + } else if (!strcmp(multidevs, "forbid")) { > + fse->export_flags &= ~V9FS_REMAP_INODES; > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > + } else if (!strcmp(multidevs, "warn")) { > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > + fse->export_flags &= ~V9FS_REMAP_INODES; > + } else { > + error_setg(errp, "invalid multidevs property '%s'", > + multidevs); > + error_append_hint(errp, "Valid options are: multidevs=" > + "[remap|forbid|warn]\n"); > + return -1; > + } > + } > + > + if (!path) { > + error_setg(errp, "path property not set"); > + return -1; > + } > + > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > + error_prepend(errp, "invalid throttle configuration: "); > + return -1; > + } > + > + if (fse->export_flags & V9FS_SM_MAPPED || > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > + fse->fmode = > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > + fse->dmode = > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & > 0777; + } else { > + if (qemu_opt_find(opts, "fmode")) { > + error_setg(errp, "fmode is only valid for mapped security > modes"); + return -1; > + } > + if (qemu_opt_find(opts, "dmode")) { > + error_setg(errp, "dmode is only valid for mapped security > modes"); + return -1; > + } > + } > + > + fse->path = g_strdup(path); > + > + return 0; > +} > + > +FileOperations local_ops = { > + .parse_opts = local_parse_opts, > + .init = local_init, > + .cleanup = local_cleanup, > + .lstat = local_lstat, > + .readlink = local_readlink, > + .close = local_close, > + .closedir = local_closedir, > + .open = local_open, > + .opendir = local_opendir, > + .rewinddir = local_rewinddir, > + .telldir = local_telldir, > + .readdir = local_readdir, > + .seekdir = local_seekdir, > + .preadv = local_preadv, > + .pwritev = local_pwritev, > + .chmod = local_chmod, > + .mknod = local_mknod, > + .mkdir = local_mkdir, > + .fstat = local_fstat, > + .open2 = local_open2, > + .symlink = local_symlink, > + .link = local_link, > + .truncate = local_truncate, > + .rename = local_rename, > + .chown = local_chown, > + .utimensat = local_utimensat, > + .remove = local_remove, > + .fsync = local_fsync, > + .statfs = local_statfs, > + .lgetxattr = local_lgetxattr, > + .llistxattr = local_llistxattr, > + .lsetxattr = local_lsetxattr, > + .lremovexattr = local_lremovexattr, > + .name_to_path = local_name_to_path, > + .renameat = local_renameat, > + .unlinkat = local_unlinkat, > +}; > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > new file mode 100644 > index 0000000000..d9b35e7425 > --- /dev/null > +++ b/hw/9pfs/9p-util-win32.c > @@ -0,0 +1,303 @@ > +/* > + * 9p utilities (Windows Implementation) > + * > + * Copyright (c) 2022 Wind River Systems, Inc. > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. + * See the COPYING file in the top-level directory. > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qemu/error-report.h" > +#include "9p.h" > +#include "9p-util.h" > +#include "9p-linux-errno.h" > +#include <windows.h> > +#include <dirent.h> > + > +#ifndef V9FS_MAGIC > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > +#endif > + > +static int build_ads_name(char *namebuf, size_t namebuflen, > + const char *dirname, const char *filename, > + const char *ads_name) > +{ > + size_t totalsize; > + > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; > + if (totalsize > namebuflen) { > + return -1; > + } > + > + /* > + * NTFS ADS (Alternate Data Streams) name format: > + * filename:ads_name > + * e.g. > + * d:\1.txt:my_ads_name > + */ > + strcpy(namebuf, dirname); > + strcat(namebuf, "\\"); > + strcat(namebuf, filename); > + strcat(namebuf, ":"); > + strcat(namebuf, ads_name); > + > + return 0; > +} > + > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > + char *fulladsname) > +{ > + char *p1, *p2; > + > + /* > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > + * :ads_name:$DATA > + * e.g. > + * :my_ads_name:$DATA > + * > + * ADS name from FindNextStreamW() always have ":$DATA" string at the > end + * > + * This function copy ADS name to namebuf. > + */ > + > + p1 = strchr(fulladsname, ':'); > + if (p1 == NULL) { > + return -1; > + } > + > + p2 = strchr(p1 + 1, ':'); > + if (p2 == NULL) { > + return -1; > + } > + > + /* skip empty ads name */ > + if (p2 - p1 == 1) { > + return 0; > + } > + > + if (p2 - p1 + 1 > namebuflen) { > + return -1; > + } > + > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > + namebuf[p2 - p1 - 1] = '\0'; > + > + return p2 - p1; > +} > + > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size) > +{ > + HANDLE hStream; > + char ADSFileName[NAME_MAX + 1] = {0}; > + DWORD dwBytesRead; > + > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) > { + errno = EIO; > + return -1; > + } > + > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > + if (hStream == INVALID_HANDLE_VALUE && > + GetLastError() == ERROR_FILE_NOT_FOUND) { > + errno = ENODATA; > + return -1; > + } > + > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > + errno = EIO; > + CloseHandle(hStream); > + return -1; > + } > + > + CloseHandle(hStream); > + > + return dwBytesRead; > +} > + > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > + char *list, size_t size) > +{ > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > + char fulladsname[NAME_MAX + 1]; > + char *full_fs_name = merge_fs_path(dirname, filename); > + int ret; > + HANDLE hFind; > + WIN32_FIND_STREAM_DATA fsd; > + BOOL bFindNext; > + char *listptr = list; > + size_t listleftsize = size; > + > + /* > + * ADS emurate function only have WCHAR version, need to covert > filename + * to WCHAR string. > + */ > + > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > + strlen(full_fs_name), WideCharStr, NAME_MAX); > + g_free(full_fs_name); > + if (ret == 0) { > + errno = EIO; > + return -1; > + } > + > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); > + if (hFind == INVALID_HANDLE_VALUE) { > + errno = ENODATA; > + return -1; > + } > + > + do { > + memset(fulladsname, 0, sizeof(fulladsname)); > + > + /* > + * ADS emurate function only have WCHAR version, need to covert > + * cStreamName to utf-8 string. > + */ > + > + ret = WideCharToMultiByte(CP_UTF8, 0, > + fsd.cStreamName, wcslen(fsd.cStreamName) > + 1, + fulladsname, sizeof(fulladsname) - > 1, + NULL, NULL); > + > + if (ret == 0) { > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > + errno = ERANGE; > + } > + CloseHandle(hFind); > + return -1; > + } > + > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > + if (ret < 0) { > + errno = ERANGE; > + CloseHandle(hFind); > + return -1; > + } > + > + listptr = listptr + ret; > + listleftsize = listleftsize - ret; > + > + bFindNext = FindNextStreamW(hFind, &fsd); > + } while (bFindNext); > + > + CloseHandle(hFind); > + > + return size - listleftsize; > +} > + > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > + const char *name) > +{ > + char ADSFileName[NAME_MAX + 1] = {0}; > + > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) > { + errno = EIO; > + return -1; > + } > + > + if (DeleteFile(ADSFileName) != 0) { > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > + errno = ENODATA; > + return -1; > + } > + } > + > + return 0; > +} > + > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size, int > flags) +{ > + HANDLE hStream; > + char ADSFileName[NAME_MAX + 1] = {0}; > + DWORD dwBytesWrite; > + > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) > { + errno = EIO; > + return -1; > + } > + > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + > if (hStream == INVALID_HANDLE_VALUE) { > + errno = EIO; > + return -1; > + } > + > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { > + errno = EIO; > + CloseHandle(hStream); > + return -1; > + } > + > + CloseHandle(hStream); > + > + return 0; > +} > + > +int qemu_mknodat(const char *dirname, const char *filename, > + mode_t mode, dev_t dev) > +{ > + errno = ENOTSUP; > + return -1; > +} > + > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > +{ > + HANDLE hFile; > + char RealPath[NAME_MAX + 1]; > + unsigned long SectorsPerCluster; > + unsigned long BytesPerSector; > + unsigned long NumberOfFreeClusters; > + unsigned long TotalNumberOfClusters; > + > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > + if (hFile == INVALID_HANDLE_VALUE) { > + errno = EIO; > + return -1; > + } > + > + /* get real path of root */ > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > + FILE_NAME_NORMALIZED) == 0) { > + errno = EIO; > + CloseHandle(hFile); > + return -1; > + } > + > + CloseHandle(hFile); > + > + /* > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > + * "C:\\123" will be "\\\\?\\C:\\123" > + * Skip first 4 bytes and truncate the string at offset 7, it will get > + * the real root directory like "C:\\", this is parameter > GetDiskFreeSpace + * needed. > + */ > + > + RealPath[7] = '\0'; > + > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, > + &NumberOfFreeClusters, &TotalNumberOfClusters) == > 0) { + errno = EIO; > + return -1; > + } > + > + stbuf->f_type = V9FS_MAGIC; > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > + stbuf->f_files = -1; > + stbuf->f_ffree = -1; > + stbuf->f_namelen = NAME_MAX; > + stbuf->f_frsize = 0; > + stbuf->f_flags = 0; > + > + return 0; > +} > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > index 9ae69dd8db..5623f0e2ef 100644 > --- a/hw/9pfs/9p-xattr.c > +++ b/hw/9pfs/9p-xattr.c > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char > *path, char *orig_value, *orig_value_start; > ssize_t xattr_len, parsed_len = 0, attr_len; > char *dirpath, *name; > +#ifdef CONFIG_WIN32 > + char *full_dir_path; > + DIR *dir; > +#else > int dirfd; > +#endif > > /* Get the actual len */ > dirpath = g_path_get_dirname(path); > + > +#ifdef CONFIG_WIN32 > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + return -1; > + } > + closedir(dir); > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + g_free(dirpath); > + > + name = g_path_get_basename(path); > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > + if (xattr_len <= 0) { > + g_free(name); > + g_free(full_dir_path); > + return xattr_len; > + } > + > + /* Now fetch the xattr and find the actual size */ > + orig_value = g_malloc(xattr_len); > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > + xattr_len); > + g_free(name); > + g_free(full_dir_path); > + if (xattr_len < 0) { > + g_free(orig_value); > + return -1; > + } > +#else > dirfd = local_opendir_nofollow(ctx, dirpath); > g_free(dirpath); > if (dirfd == -1) { > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char > *path, g_free(orig_value); > return -1; > } > +#endif > > /* store the orig pointer */ > orig_value_start = orig_value; > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > const char *name, void *value, size_t size) > { > +#ifdef CONFIG_WIN32 > + char *dirpath = g_path_get_dirname(path); > + char *filename = g_path_get_basename(path); > + char *full_dir_path; > + DIR *dir; > + ssize_t ret = -1; > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + closedir(dir); > + > + ret = fgetxattrat_nofollow(full_dir_path, filename, > + name, value, size); > + > +out: > + g_free(full_dir_path); > + g_free(dirpath); > + g_free(filename); > + > + return ret; > +#else > char *dirpath = g_path_get_dirname(path); > char *filename = g_path_get_basename(path); > int dirfd; > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const > char *path, } > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > + > close_preserve_errno(dirfd); > out: > g_free(dirpath); > g_free(filename); > return ret; > +#endif > } > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const > char *path, const char *name, void *value, size_t size, int flags) > { > +#ifdef CONFIG_WIN32 > + char *dirpath = g_path_get_dirname(path); > + char *filename = g_path_get_basename(path); > + char *full_dir_path; > + DIR *dir; > + ssize_t ret = -1; > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + closedir(dir); > + > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > + value, size, flags); > +out: > + g_free(full_dir_path); > + g_free(dirpath); > + g_free(filename); > + return ret; > + > +#else > char *dirpath = g_path_get_dirname(path); > char *filename = g_path_get_basename(path); > int dirfd; > @@ -210,6 +297,7 @@ out: > g_free(dirpath); > g_free(filename); > return ret; > +#endif > } > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void > *value, @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char > *path, const char *name, void *value, ssize_t > local_removexattr_nofollow(FsContext *ctx, const char *path, const char > *name) > { > +#ifdef CONFIG_WIN32 > + char *dirpath = g_path_get_dirname(path); > + char *filename = g_path_get_basename(path); > + char *full_dir_path; > + DIR *dir; > + ssize_t ret = -1; > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + closedir(dir); > + > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > + > +out: > + g_free(full_dir_path); > + g_free(dirpath); > + g_free(filename); > + return ret; > + > +#else > char *dirpath = g_path_get_dirname(path); > char *filename = g_path_get_basename(path); > int dirfd; > @@ -237,6 +349,7 @@ out: > g_free(dirpath); > g_free(filename); > return ret; > +#endif > } > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > index 225f31fc31..a04889c1d6 100644 > --- a/hw/9pfs/9p.c > +++ b/hw/9pfs/9p.c > @@ -38,6 +38,10 @@ > #include "migration/blocker.h" > #include "qemu/xxhash.h" > #include <math.h> > +#ifdef CONFIG_WIN32 > +#define UTIME_NOW ((1l << 30) - 1l) > +#define UTIME_OMIT ((1l << 30) - 2l) > +#endif > > int open_fd_hw; > int total_open_fd; > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat > *stbuf, V9fsQID *qidp) if (S_ISDIR(stbuf->st_mode)) { > qidp->type |= P9_QID_TYPE_DIR; > } > +#ifndef CONFIG_WIN32 > if (S_ISLNK(stbuf->st_mode)) { > qidp->type |= P9_QID_TYPE_SYMLINK; > } > +#endif > > return 0; > } > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) ret |= S_IFDIR; > } > > +#ifndef CONFIG_WIN32 > if (mode & P9_STAT_MODE_SYMLINK) { > ret |= S_IFLNK; > } > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) if (mode & P9_STAT_MODE_NAMED_PIPE) { > ret |= S_IFIFO; > } > +#endif > if (mode & P9_STAT_MODE_DEVICE) { > if (extension->size && extension->data[0] == 'c') { > ret |= S_IFCHR; > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) ret |= S_IFREG; > } > > +#ifndef CONFIG_WIN32 > if (mode & P9_STAT_MODE_SETUID) { > ret |= S_ISUID; > } > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) if (mode & P9_STAT_MODE_SETVTX) { > ret |= S_ISVTX; > } > +#endif > > return ret; > } > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat > *stbuf) mode |= P9_STAT_MODE_DIR; > } > > +#ifndef CONFIG_WIN32 > if (S_ISLNK(stbuf->st_mode)) { > mode |= P9_STAT_MODE_SYMLINK; > } > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat > *stbuf) if (S_ISFIFO(stbuf->st_mode)) { > mode |= P9_STAT_MODE_NAMED_PIPE; > } > +#endif > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > mode |= P9_STAT_MODE_DEVICE; > } > > +#ifndef CONFIG_WIN32 > if (stbuf->st_mode & S_ISUID) { > mode |= P9_STAT_MODE_SETUID; > } > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat > *stbuf) if (stbuf->st_mode & S_ISVTX) { > mode |= P9_STAT_MODE_SETVTX; > } > +#endif > > return mode; > } > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, > V9fsPath *path, return err; > } > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > +#ifndef CONFIG_WIN32 > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > +#else > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > + 0, 0); > +#endif > + > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, > int32_t blksize) > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > { > +#ifndef CONFIG_WIN32 > return blksize_to_iounit(pdu, stbuf->st_blksize); > +#else > + return blksize_to_iounit(pdu, 0); > +#endif > } > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > struct stat *stbuf, v9lstat->st_gid = stbuf->st_gid; > v9lstat->st_rdev = stbuf->st_rdev; > v9lstat->st_size = stbuf->st_size; > + > +#ifdef CONFIG_WIN32 > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > + v9lstat->st_blocks = 0; > + v9lstat->st_atime_sec = stbuf->st_atime; > + v9lstat->st_mtime_sec = stbuf->st_mtime; > + v9lstat->st_ctime_sec = stbuf->st_ctime; > +#else /* !CONFIG_WIN32 */ > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > v9lstat->st_blocks = stbuf->st_blocks; > v9lstat->st_atime_sec = stbuf->st_atime; > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > struct stat *stbuf, v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > #endif > +#endif /* CONFIG_WIN32 */ > + > /* Currently we only support BASIC fields in stat */ > v9lstat->st_result_mask = P9_STATS_BASIC; > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > static bool same_stat_id(const struct stat *a, const struct stat *b) > { > +#ifdef CONFIG_WIN32 > + return 0; > +#else > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > +#endif /* CONFIG_WIN32 */ > } > > static void coroutine_fn v9fs_walk(void *opaque) > @@ -2300,7 +2339,11 @@ static int coroutine_fn > v9fs_do_readdir_with_stat(V9fsPDU *pdu, count += len; > v9fs_stat_free(&v9stat); > v9fs_path_free(&path); > +#ifndef CONFIG_WIN32 > saved_dir_pos = qemu_dirent_off(dent); > +#else > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > +#endif > } > > v9fs_readdir_unlock(&fidp->fs.dir); > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU > *pdu, V9fsFidState *fidp, qid.version = 0; > } > > +#ifdef CONFIG_WIN32 > + /* > + * Windows does not have dent->d_off, get offset by calling > telldir() + * manually. > + */ > + off = v9fs_co_telldir(pdu, fidp); > +#else > off = qemu_dirent_off(dent); > +#endif > v9fs_string_init(&name); > v9fs_string_sprintf(&name, "%s", dent->d_name); > > +#ifdef CONFIG_WIN32 > + /* > + * Windows does not have dent->d_type > + */ > + > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > + &qid, off, > + 0, &name); > +#else > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > len = pdu_marshal(pdu, 11 + count, "Qqbs", > &qid, off, > dent->d_type, &name); > +#endif > > v9fs_string_free(&name); > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > } > > nmode |= perm & 0777; > + > +#ifndef CONFIG_WIN32 > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > makedev(major, minor), nmode, &stbuf); > +#else > + err = -ENOTSUP; > +#endif > + > if (err < 0) { > goto out; > } > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > v9fs_path_copy(&fidp->path, &path); > v9fs_path_unlock(s); > } else if (perm & P9_STAT_MODE_SOCKET) { > +#ifndef CONFIG_WIN32 > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > 0, S_IFSOCK | (perm & 0777), &stbuf); > +#else > + err = -ENOTSUP; > +#endif > if (err < 0) { > goto out; > } > @@ -3600,6 +3671,7 @@ out_nofid: > static void coroutine_fn v9fs_mknod(void *opaque) > { > > +#ifndef CONFIG_WIN32 > int mode; > gid_t gid; > int32_t fid; > @@ -3656,6 +3728,11 @@ out: > out_nofid: > pdu_complete(pdu, err); > v9fs_string_free(&name); > +#else > + V9fsPDU *pdu = opaque; > + > + pdu_complete(pdu, -1); > +#endif > } > > /* > @@ -3928,7 +4005,7 @@ out_nofid: > #if defined(CONFIG_LINUX) > /* Currently, only Linux has XATTR_SIZE_MAX */ > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > -#elif defined(CONFIG_DARWIN) > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > /* > * Darwin doesn't seem to define a maximum xattr size in its user > * space header, so manually configure it across platforms as 64k. > @@ -3945,6 +4022,7 @@ out_nofid: > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > { > +#ifndef CONFIG_WIN32 > int flags, rflags = 0; > int32_t fid; > uint64_t size; > @@ -4006,10 +4084,15 @@ out_put_fid: > out_nofid: > pdu_complete(pdu, err); > v9fs_string_free(&name); > +#else > + V9fsPDU *pdu = opaque; > + pdu_complete(pdu, -1); > +#endif > } > > static void coroutine_fn v9fs_readlink(void *opaque) > { > +#ifndef CONFIG_WIN32 > V9fsPDU *pdu = opaque; > size_t offset = 7; > V9fsString target; > @@ -4045,6 +4128,10 @@ out: > put_fid(pdu, fidp); > out_nofid: > pdu_complete(pdu, err); > +#else > + V9fsPDU *pdu = opaque; > + pdu_complete(pdu, -1); > +#endif > } > > static CoroutineEntry *pdu_co_handlers[] = { > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > { > +#ifndef CONFIG_WIN32 > struct rlimit rlim; > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > error_report("Failed to get the resource limit"); > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) > v9fs_set_fd_limit(void) } > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > open_fd_rc = rlim.rlim_cur / 2; > +#endif > } > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > index 93ba44fb75..2fbe7b831b 100644 > --- a/hw/9pfs/codir.c > +++ b/hw/9pfs/codir.c > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > *fidp, int len, err = 0; > int32_t size = 0; > off_t saved_dir_pos; > +#ifdef CONFIG_WIN32 > + off_t next_dir_pos; > +#endif > struct dirent *dent; > struct V9fsDirEnt *e = NULL; > V9fsPath path; > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > *fidp, break; > } > > +#ifdef CONFIG_WIN32 > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > + if (next_dir_pos < 0) { > + err = next_dir_pos; > + goto out; > + } > +#endif > + > /* > * stop this loop as soon as it would exceed the allowed maximum > * response message size for the directory entries collected so > far, @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, > V9fsFidState *fidp, } > > size += len; > +#ifndef CONFIG_WIN32 > saved_dir_pos = qemu_dirent_off(dent); > +#else > + saved_dir_pos = next_dir_pos; > +#endif > } > > /* restore (last) saved position */ ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-04 18:01 ` Christian Schoenebeck @ 2022-05-04 19:34 ` Shi, Guohuai 2022-05-05 11:43 ` Christian Schoenebeck 0 siblings, 1 reply; 34+ messages in thread From: Shi, Guohuai @ 2022-05-04 19:34 UTC (permalink / raw) To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin, Bin Meng > -----Original Message----- > From: Christian Schoenebeck <qemu_oss@crudebyte.com> > Sent: Thursday, May 5, 2022 02:02 > To: qemu-devel@nongnu.org > Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi, > Guohuai <Guohuai.Shi@windriver.com>; Bin Meng <bmeng.cn@gmail.com> > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver > for Windows > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > On Montag, 25. April 2022 16:27:01 CEST Bin Meng wrote: > > From: Guohuai Shi <guohuai.shi@windriver.com> > > > > Add a 9p local file system backend driver to support Windows, > > including open, read, write, close, rename, remove, etc. > > > > All security models are supported. The mapped (mapped-xattr) > > security model is implemented using NTFS Alternate Data Stream > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > --- > > > > hw/9pfs/9p-linux-errno.h | 151 +++++ > > hw/9pfs/9p-local.h | 4 + > > hw/9pfs/9p-util.h | 41 ++ > > hw/9pfs/9p.h | 23 + > > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > > hw/9pfs/9p-xattr.c | 113 ++++ > > hw/9pfs/9p.c | 91 ++- > > hw/9pfs/codir.c | 15 + > > 9 files changed, 1982 insertions(+), 1 deletion(-) > > create mode 100644 hw/9pfs/9p-linux-errno.h > > create mode 100644 hw/9pfs/9p-local-win32.c > > create mode 100644 hw/9pfs/9p-util-win32.c > > This patch is definitely too huge and should be split up into a huge bunch of > separate patches! > > > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > > new file mode 100644 > > index 0000000000..b0d6ac45ac > > --- /dev/null > > +++ b/hw/9pfs/9p-linux-errno.h > > This file definitely deserves a patch on its own. > > As for its filename: Following our current filename scheme, it would probably > be better be named 9p-errno-linux.h or 9p-errno-dotl.h as this is probably a > 9p protocol version thing. > > > @@ -0,0 +1,151 @@ > > +/* > > + * 9p Linux errno translation definition > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2 or > > later. + * See the COPYING file in the top-level directory. > > + */ > > + > > +#include <errno.h> > > + > > +#ifndef QEMU_9P_LINUX_ERRNO_H > > +#define QEMU_9P_LINUX_ERRNO_H > > + > > +/* > > + * This file contains the Linux errno definitions to translate errnos set > > by + * the 9P server (running on Windows) to a corresponding errno value. + > > * > > + * This list should be periodically reviewed and updated; particularly for > > + * errnos that might be set as a result of a file system operation. > > + */ > > + > > I would just import the already existing sys/errno.h from the Linux kernel, > with all its copyright header etc. and then with a 2nd patch just prefix the > individual macros with DOTL_* > > > +#define L_EPERM 1 /* Operation not permitted */ > > +#define L_ENOENT 2 /* No such file or directory */ > > +#define L_ESRCH 3 /* No such process */ > > +#define L_EINTR 4 /* Interrupted system call */ > > +#define L_EIO 5 /* I/O error */ > > +#define L_ENXIO 6 /* No such device or address */ > > +#define L_E2BIG 7 /* Argument list too long */ > > +#define L_ENOEXEC 8 /* Exec format error */ > > +#define L_EBADF 9 /* Bad file number */ > > +#define L_ECHILD 10 /* No child processes */ > > +#define L_EAGAIN 11 /* Try again */ > > +#define L_ENOMEM 12 /* Out of memory */ > > +#define L_EACCES 13 /* Permission denied */ > > +#define L_EFAULT 14 /* Bad address */ > > +#define L_ENOTBLK 15 /* Block device required */ > > +#define L_EBUSY 16 /* Device or resource busy */ > > +#define L_EEXIST 17 /* File exists */ > > +#define L_EXDEV 18 /* Cross-device link */ > > +#define L_ENODEV 19 /* No such device */ > > +#define L_ENOTDIR 20 /* Not a directory */ > > +#define L_EISDIR 21 /* Is a directory */ > > +#define L_EINVAL 22 /* Invalid argument */ > > +#define L_ENFILE 23 /* File table overflow */ > > +#define L_EMFILE 24 /* Too many open files */ > > +#define L_ENOTTY 25 /* Not a typewriter */ > > +#define L_ETXTBSY 26 /* Text file busy */ > > +#define L_EFBIG 27 /* File too large */ > > +#define L_ENOSPC 28 /* No space left on device */ > > +#define L_ESPIPE 29 /* Illegal seek */ > > +#define L_EROFS 30 /* Read-only file system */ > > +#define L_EMLINK 31 /* Too many links */ > > +#define L_EPIPE 32 /* Broken pipe */ > > +#define L_EDOM 33 /* Math argument out of domain of func */ > > +#define L_ERANGE 34 /* Math result not representable */ > > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > > +#define L_ENAMETOOLONG 36 /* File name too long */ > > +#define L_ENOLCK 37 /* No record locks available */ > > +#define L_ENOSYS 38 /* Function not implemented */ > > +#define L_ENOTEMPTY 39 /* Directory not empty */ > > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > > +#define L_ENOMSG 42 /* No message of desired type */ > > +#define L_EIDRM 43 /* Identifier removed */ > > +#define L_ECHRNG 44 /* Channel number out of range */ > > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > > +#define L_EL3HLT 46 /* Level 3 halted */ > > +#define L_EL3RST 47 /* Level 3 reset */ > > +#define L_ELNRNG 48 /* Link number out of range */ > > +#define L_EUNATCH 49 /* Protocol driver not attached */ > > +#define L_ENOCSI 50 /* No CSI structure available */ > > +#define L_EL2HLT 51 /* Level 2 halted */ > > +#define L_EBADE 52 /* Invalid exchange */ > > +#define L_EBADR 53 /* Invalid request descriptor */ > > +#define L_EXFULL 54 /* Exchange full */ > > +#define L_ENOANO 55 /* No anode */ > > +#define L_EBADRQC 56 /* Invalid request code */ > > +#define L_EBADSLT 57 /* Invalid slot */ > > +#define L_EBFONT 58 /* Bad font file format */ > > +#define L_ENOSTR 59 /* Device not a stream */ > > +#define L_ENODATA 61 /* No data available */ > > +#define L_ETIME 62 /* Timer expired */ > > +#define L_ENOSR 63 /* Out of streams resources */ > > +#define L_ENONET 64 /* Machine is not on the network */ > > +#define L_ENOPKG 65 /* Package not installed */ > > +#define L_EREMOTE 66 /* Object is remote */ > > +#define L_ENOLINK 67 /* Link has been severed */ > > +#define L_EADV 68 /* Advertise error */ > > +#define L_ESRMNT 69 /* Srmount error */ > > +#define L_ECOMM 70 /* Communication error on send */ > > +#define L_EPROTO 71 /* Protocol error */ > > +#define L_EMULTIHOP 72 /* Multihop attempted */ > > +#define L_EDOTDOT 73 /* RFS specific error */ > > +#define L_EBADMSG 74 /* Not a data message */ > > +#define L_EOVERFLOW 75 /* Value too large for defined data type > */ > > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > > +#define L_EBADFD 77 /* File descriptor in bad state */ > > +#define L_EREMCHG 78 /* Remote address changed */ > > +#define L_ELIBACC 79 /* Can not access a needed shared library > > */ +#define L_ELIBBAD 80 /* Accessing a corrupted shared library > > */ +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > > +#define L_ELIBMAX 82 /* Attempting to link in too many shared > > libs */ +#define L_ELIBEXEC 83 /* Cannot exec a shared library > > directly */ +#define L_EILSEQ 84 /* Illegal byte sequence */ > > +#define L_ERESTART 85 /* Interrupted system call should be > > restarted */ +#define L_ESTRPIPE 86 /* Streams pipe error */ > > +#define L_EUSERS 87 /* Too many users */ > > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > > +#define L_EDESTADDRREQ 89 /* Destination address required */ > > +#define L_EMSGSIZE 90 /* Message too long */ > > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport > > endpoint */ +#define L_EPFNOSUPPORT 96 /* Protocol family not > > supported */ +#define L_EAFNOSUPPORT 97 /* Address family not > > supported by protocol */ +#define L_EADDRINUSE 98 /* Address > > already in use */ > > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > > +#define L_ENETDOWN 100 /* Network is down */ > > +#define L_ENETUNREACH 101 /* Network is unreachable */ > > +#define L_ENETRESET 102 /* Network dropped connection because of > > reset */ +#define L_ECONNABORTED 103 /* Software caused connection > > abort */ +#define L_ECONNRESET 104 /* Connection reset by peer */ > > +#define L_ENOBUFS 105 /* No buffer space available */ > > +#define L_EISCONN 106 /* Transport endpoint is already connected > > */ +#define L_ENOTCONN 107 /* Transport endpoint is not connected > > */ +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint > > shutdown */ +#define L_ETOOMANYREFS 109 /* Too many references: cannot > > splice */ +#define L_ETIMEDOUT 110 /* Connection timed out */ > > +#define L_ECONNREFUSED 111 /* Connection refused */ > > +#define L_EHOSTDOWN 112 /* Host is down */ > > +#define L_EHOSTUNREACH 113 /* No route to host */ > > +#define L_EALREADY 114 /* Operation already in progress */ > > +#define L_EINPROGRESS 115 /* Operation now in progress */ > > +#define L_ESTALE 116 /* Stale NFS file handle */ > > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > > +#define L_EISNAM 120 /* Is a named type file */ > > +#define L_EREMOTEIO 121 /* Remote I/O error */ > > +#define L_EDQUOT 122 /* Quota exceeded */ > > +#define L_ENOMEDIUM 123 /* No medium found */ > > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > > +#define L_ECANCELED 125 /* Operation Canceled */ > > +#define L_ENOKEY 126 /* Required key not available */ > > +#define L_EKEYEXPIRED 127 /* Key has expired */ > > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > > +#define L_EOWNERDEAD 130 /* Owner died */ > > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > > + > > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > > index 55ea4b7883..4c75876e19 100644 > > --- a/hw/9pfs/9p-local.h > > +++ b/hw/9pfs/9p-local.h > > @@ -31,6 +31,10 @@ static inline bool > > local_is_mapped_file_metadata(FsContext *fs_ctx, > > > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > mode_t mode); > > +#ifndef CONFIG_WIN32 > > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > +#else > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > +#endif > > > > #endif > > Same goes for all the rest ... this should all be split up into individual > patches, some of the changes of a file probably with separate patches as well. > As this looks now, it's simply not clear what all these hunks of this huge > patch are needed for. > > Stopping here for now. > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > > index 97e681e167..6eadb38e1d 100644 > > --- a/hw/9pfs/9p-util.h > > +++ b/hw/9pfs/9p-util.h > > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > > errno = serrno; > > } > > > > +#ifndef CONFIG_WIN32 > > static inline int openat_dir(int dirfd, const char *name) > > { > > return openat(dirfd, name, > > @@ -89,7 +90,9 @@ again: > > errno = serrno; > > return fd; > > } > > +#endif /* !CONFIG_WIN32 */ > > > > +#ifndef CONFIG_WIN32 > > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > > void *value, size_t size); > > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char > > *filename, char *list, size_t size); > > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > > const char *name); > > +#else > > + > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size); > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size, > > + int flags); > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > + char *list, size_t size); > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > + const char *name); > > + > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > > + > > +static inline char *merge_fs_path(const char *path1, const char *path2) > > +{ > > + char *full_fs_path; > > + size_t full_fs_path_len; > > + > > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > > + full_fs_path = g_malloc(full_fs_path_len); > > > > + strcpy(full_fs_path, path1); > > + strcat(full_fs_path, "\\"); > > + strcat(full_fs_path, path2); > > + > > + return full_fs_path; > > +} > > + > > +#endif /* !CONFIG_WIN32 */ > > + > > +#ifndef CONFIG_WIN32 > > /* > > * Darwin has d_seekoff, which appears to function similarly to d_off. > > * However, it does not appear to be supported on all file systems, > > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > > return dent->d_off; > > #endif > > } > > +#endif /* !CONFIG_WIN32 */ > > > > /** > > * qemu_dirent_dup() - Duplicate directory entry @dent. > > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct > > dirent *dent) #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > > #endif > > + > > +#ifndef CONFIG_WIN32 > > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > > +#else > > +int qemu_mknodat(const char *dirname, const char *filename, > > + mode_t mode, dev_t dev); > > +#endif > > > > #endif > > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > > index 994f952600..87e8eac840 100644 > > --- a/hw/9pfs/9p.h > > +++ b/hw/9pfs/9p.h > > @@ -3,13 +3,36 @@ > > > > #include <dirent.h> > > #include <utime.h> > > +#ifndef CONFIG_WIN32 > > #include <sys/resource.h> > > +#endif > > #include "fsdev/file-op-9p.h" > > #include "fsdev/9p-iov-marshal.h" > > #include "qemu/thread.h" > > #include "qemu/coroutine.h" > > #include "qemu/qht.h" > > > > +#ifdef CONFIG_WIN32 > > + > > +#define O_NOCTTY 0 > > +#define O_NDELAY 0 > > +#define O_NONBLOCK 0 > > +#define O_DSYNC 0 > > +#define O_DIRECT 0 > > +#define O_NOFOLLOW 0 > > +#define O_NOATIME 0 > > +#define O_SYNC 0 > > +#define O_ASYNC 0 > > +#define O_DIRECTORY 02000000 > > Again, these are POSIX types. I don't think declaring them belongs into this > header file. > > > + > > +#define FASYNC 0 > > + > > +#define AT_REMOVEDIR 1 > > + > > +#define NAME_MAX 260 > > + > > +#endif > > + > > enum { > > P9_TLERROR = 6, > > P9_RLERROR, > > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > > new file mode 100644 > > index 0000000000..aab7c9f7b5 > > --- /dev/null > > +++ b/hw/9pfs/9p-local-win32.c > > @@ -0,0 +1,1242 @@ > > +/* > > + * 9p Windows callback > > + * > > + * Copyright (c) 2022 Wind River Systems, Inc. > > + * > > + * Based on hw/9pfs/9p-local.c > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2. See > > + * the COPYING file in the top-level directory. > > + */ > > + > > +/* > > + * Not so fast! You might want to read the 9p developer docs first: > > + * https://wiki.qemu.org/Documentation/9p > > + */ > > + > > +#include "qemu/osdep.h" > > +#include <windows.h> > > +#include <dirent.h> > > +#include "9p.h" > > +#include "9p-local.h" > > +#include "9p-xattr.h" > > +#include "9p-util.h" > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > +#include "qapi/error.h" > > +#include "qemu/cutils.h" > > +#include "qemu/error-report.h" > > +#include "qemu/option.h" > > +#include <libgen.h> > > I'm not sure whether all of this (i.e. 9p-local-win32.c in general) is really > needed. I mean yes, it probably does the job, but you basically add a > complete > separate 'local' backend implementation just for the Windows host platform. > > My expectation would be to stick to 9p-local.c being OS-agnostic, and only > define OS-specific functionality in 9p-util-windows.c if needed. > > And most importantly: don't duplicate code as far as possible! I mean > somebody > would need to also review and maintain all of this. Actually, almost all FileOperations functions are re-written for Windows host. Here is the comparison statistics for 9p-local.c and 9p-local-win32.c: Total lines : 1611 Changed lines: 590 Inserted lines: 138 Removed lines: 414 For function level difference count: Changed function: 39 Unchanged function: 13 If use "#ifdef _WIN32" to sperate Windows host code, it may need to be inserted about 150 code blocks (or 39 code blocks for all changed functions). I am not sure if it is a good idea to insert so many "#ifdef _WIN32", it may cause this file not readable. If stick to 9p-local.c being OS-agnostic, I think it is better to create two new files: 9p-local-linux.c and 9p-local-win32.c > > > + > > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > > + int flags, mode_t mode) > > +{ > > + char *full_file_name; > > + int fd; > > + > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > + fd = open(full_file_name, flags | _O_BINARY, mode); > > + g_free(full_file_name); > > + > > + return fd; > > +} > > + > > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > > +{ > > + char *full_file_name; > > + DIR *dir; > > + > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > + dir = opendir(full_file_name); > > + g_free(full_file_name); > > + return dir; > > +} > > + > > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > + mode_t mode) > > +{ > > + int fd; > > + > > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { > > + /* Windows does not allow call open() for a directory */ > > + fd = -1; > > + } else { > > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); > > + } > > + > > + return fd; > > +} > > + > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > > +{ > > + return opendir_with_ctx(fs_ctx, path); > > +} > > + > > +static FILE *local_fopenat(const char *dirname, const char *name, > > + const char *mode) > > +{ > > + char *full_file_name; > > + char modestr[3] = {0}; > > + FILE *fp; > > + > > + /* > > + * only supports two modes > > + */ > > + if (mode[0] == 'r') { > > + modestr[0] = 'r'; > > + } else if (mode[0] == 'w') { > > + modestr[0] = 'w'; > > + } else { > > + return NULL; > > + } > > + /* Windows host needs 'b' flag */ > > + modestr[1] = 'b'; > > + > > + full_file_name = merge_fs_path(dirname, name); > > + fp = fopen(full_file_name, modestr); > > + g_free(full_file_name); > > + > > + return fp; > > +} > > + > > +static void local_mapped_file_attr(const char *dirpath, const char *name, > > + struct stat *stbuf) > > +{ > > + FILE *fp; > > + char buf[ATTR_MAX]; > > + char *full_file_name; > > + > > + if (strcmp(name, ".") != 0) { > > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > > + fp = local_fopenat(full_file_name, name, "r"); > > + g_free(full_file_name); > > + } else { > > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > > + } > > + if (!fp) { > > + return; > > + } > > + > > + memset(buf, 0, ATTR_MAX); > > + while (fgets(buf, ATTR_MAX, fp)) { > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > + stbuf->st_uid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > + stbuf->st_gid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > + stbuf->st_rdev = atoi(buf + 12); > > + } > > + memset(buf, 0, ATTR_MAX); > > + } > > + fclose(fp); > > +} > > + > > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat > > *stbuf) +{ > > + int err = -1; > > + char *full_dir_name, *full_file_name; > > + char *dirpath = g_path_get_dirname(fs_path->data); > > + char *name = g_path_get_basename(fs_path->data); > > + > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > + full_file_name = merge_fs_path(full_dir_name, name); > > + err = stat(full_file_name, stbuf); > > + > > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > > + /* > > + * Hard code for root directory on Windows host. > > + * This will root directory have a special inode number, > > + * then guest OS can detect it is a special directory. > > + */ > > + stbuf->st_ino = 2; > > + } > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + /* Actual credentials are part of extended attrs */ > > + uid_t tmp_uid; > > + gid_t tmp_gid; > > + mode_t tmp_mode; > > + dev_t tmp_dev; > > + > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > > + &tmp_uid, sizeof(uid_t)) > 0) { > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > + } > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > > + &tmp_gid, sizeof(gid_t)) > 0) { > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > + } > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > > + &tmp_mode, sizeof(mode_t)) > 0) { > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > + } > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > > + &tmp_dev, sizeof(dev_t)) > 0) { > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > + } > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + local_mapped_file_attr(full_dir_name, name, stbuf); > > + } > > + > > + g_free(full_file_name); > > + g_free(full_dir_name); > > + > > + if (err) { > > + goto err_out; > > + } > > + > > +err_out: > > + g_free(name); > > + g_free(dirpath); > > + return err; > > +} > > + > > +static int local_set_mapped_file_attrat(const char *dirname, const char > > *name, + FsCred *credp) > > +{ > > + FILE *fp; > > + int ret; > > + char buf[ATTR_MAX]; > > + int uid = -1, gid = -1, mode = -1, rdev = -1; > > + bool is_root = !strcmp(name, "."); > > + char *full_dir_name; > > + > > + if (is_root) { > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > > + if (!fp) { > > + if (errno == ENOENT) { > > + goto update_map_file; > > + } else { > > + return -1; > > + } > > + } > > + } else { > > + /* > > + * mapped-file: > > + * <sub_file> attribute stored to: > > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > > + */ > > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > > + ret = mkdir(full_dir_name); > > + > > + if (ret < 0 && errno != EEXIST) { > > + g_free(full_dir_name); > > + return -1; > > + } > > + > > + fp = local_fopenat(full_dir_name, name, "r"); > > + if (!fp) { > > + if (errno == ENOENT) { > > + goto update_map_file; > > + } else { > > + g_free(full_dir_name); > > + return -1; > > + } > > + } > > + } > > + > > + memset(buf, 0, ATTR_MAX); > > + while (fgets(buf, ATTR_MAX, fp)) { > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > + uid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > + gid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > + mode = atoi(buf + 12); > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > + rdev = atoi(buf + 12); > > + } > > + memset(buf, 0, ATTR_MAX); > > + } > > + fclose(fp); > > + > > +update_map_file: > > + if (is_root) { > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > > + } else { > > + fp = local_fopenat(full_dir_name, name, "w"); > > + g_free(full_dir_name); > > + } > > + if (!fp) { > > + return -1; > > + } > > + > > + if (credp->fc_uid != -1) { > > + uid = credp->fc_uid; > > + } > > + if (credp->fc_gid != -1) { > > + gid = credp->fc_gid; > > + } > > + if (credp->fc_mode != (mode_t)-1) { > > + mode = credp->fc_mode; > > + } > > + if (credp->fc_rdev != -1) { > > + rdev = credp->fc_rdev; > > + } > > + > > + if (uid != -1) { > > + fprintf(fp, "virtfs.uid=%d\n", uid); > > + } > > + if (gid != -1) { > > + fprintf(fp, "virtfs.gid=%d\n", gid); > > + } > > + if (mode != -1) { > > + fprintf(fp, "virtfs.mode=%d\n", mode); > > + } > > + if (rdev != -1) { > > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > > + } > > + fclose(fp); > > + > > + return 0; > > +} > > + > > +static int local_set_xattrat(const char *dirname, const char *path, > > + FsCred *credp) > > +{ > > + int err; > > + > > + if (credp->fc_uid != -1) { > > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > > + &tmp_uid, sizeof(uid_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + if (credp->fc_gid != -1) { > > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > > + &tmp_gid, sizeof(gid_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + if (credp->fc_mode != (mode_t)-1) { > > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > > + &tmp_mode, sizeof(mode_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + if (credp->fc_rdev != -1) { > > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > > + &tmp_rdev, sizeof(dev_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + return 0; > > +} > > + > > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > > + char *buf, size_t bufsz) > > +{ > > + return -1; > > +} > > + > > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + return close(fs->fd); > > +} > > + > > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + return closedir(fs->dir.stream); > > +} > > + > > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > > + int flags, V9fsFidOpenState *fs) > > +{ > > + int fd; > > + > > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > > + if (fd == -1) { > > + return -1; > > + } > > + fs->fd = fd; > > + return fs->fd; > > +} > > + > > +static int local_opendir(FsContext *ctx, > > + V9fsPath *fs_path, V9fsFidOpenState *fs) > > +{ > > + DIR *stream; > > + char *full_file_name; > > + > > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > > + stream = opendir(full_file_name); > > + g_free(full_file_name); > > + > > + if (!stream) { > > + return -1; > > + } > > + > > + fs->dir.stream = stream; > > + return 0; > > +} > > + > > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + rewinddir(fs->dir.stream); > > +} > > + > > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + return telldir(fs->dir.stream); > > +} > > + > > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + struct dirent *entry; > > + > > +again: > > + entry = readdir(fs->dir.stream); > > + if (!entry) { > > + return NULL; > > + } > > + > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > > + /* skip the meta data */ > > + goto again; > > + } > > + } > > + > > + return entry; > > +} > > + > > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) > > +{ > > + off_t count; > > + struct dirent *findentry; > > + struct dirent *entry; > > + size_t namelen[3] = {0}; > > + off_t direntoff[3] = {-1, -1, -1}; > > + char *d_name[3]; > > + int i; > > + > > + /* > > + * MinGW's seekdir() requires directory is not modified. If guest OS > is > > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", + > > * then MinGW's seekdir() will seek to a wrong offset. > > + * > > + * This function saves some old offset directory entry name, > > + * and lookup current entry again, and compare the offset. > > + * > > + * If new offset is less than old offset, that means someone is > > deleting + * files in the directory, thus we need to seek offset > > backward. + * > > + * If new offset is larger than old offset, that means someone is > > creating + * files in the directory, thus we need to seek offset > > forward. + */ > > + > > + direntoff[0] = telldir(fs->dir.stream); > > + > > + /* do nothing if current offset is 0 or EOF */ > > + if (direntoff[0] == 0 || direntoff[0] < 0) { > > + seekdir(fs->dir.stream, off); > > + return ; > > + } > > + > > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > > + d_name[1] = d_name[0] + sizeof(entry->d_name); > > + d_name[2] = d_name[1] + sizeof(entry->d_name); > > + > > + /* save 3 nearest entries and offsets */ > > + for (i = 0; i < 3; i++) { > > + entry = &fs->dir.stream->dd_dir; > > + > > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > > + namelen[i] = strlen(d_name[i]) + 1; > > + > > + direntoff[i] = telldir(fs->dir.stream); > > + > > + entry = readdir(fs->dir.stream); > > + if (entry == NULL) { > > + break; > > + } > > + } > > + > > + /* lookup saved entries again */ > > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > > + rewinddir(fs->dir.stream); > > + count = 0; > > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > > + count++; > > + > > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > > + if (count + i == direntoff[i]) { > > + seekdir(fs->dir.stream, off); > > + goto out; > > + } else if (count + i < direntoff[i]) { > > + off = off - (direntoff[i] - count) - i; > > + if (off <= 0) { > > + off = 0; > > + } > > + seekdir(fs->dir.stream, off); > > + goto out; > > + } else { > > + off = off + (count - direntoff[i]) - i; > > + seekdir(fs->dir.stream, off); > > + goto out; > > + } > > + } > > + } > > + } > > + /* can not get anything, seek backward */ > > + off = off - 1; > > + > > + seekdir(fs->dir.stream, off); > > +out: > > + g_free(d_name[0]); > > + return ; > > +} > > + > > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > > + const struct iovec *iov, > > + int iovcnt, off_t offset) > > +{ > > +#ifdef CONFIG_PREADV > > + return preadv(fs->fd, iov, iovcnt, offset); > > +#else > > + int err = lseek(fs->fd, offset, SEEK_SET); > > + if (err == -1) { > > + return err; > > + } else { > > + return readv(fs->fd, iov, iovcnt); > > + } > > +#endif > > +} > > + > > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > > + const struct iovec *iov, > > + int iovcnt, off_t offset) > > +{ > > + ssize_t ret; > > +#ifdef CONFIG_PREADV > > + ret = pwritev(fs->fd, iov, iovcnt, offset); > > +#else > > + int err = lseek(fs->fd, offset, SEEK_SET); > > + if (err == -1) { > > + return err; > > + } else { > > + ret = writev(fs->fd, iov, iovcnt); > > + } > > +#endif > > +#ifdef CONFIG_SYNC_FILE_RANGE > > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > > + /* > > + * Initiate a writeback. This is not a data integrity sync. > > + * We want to ensure that we don't leave dirty pages in the cache > > + * after write when writeout=immediate is sepcified. > > + */ > > + sync_file_range(fs->fd, offset, ret, > > + SYNC_FILE_RANGE_WAIT_BEFORE | > > SYNC_FILE_RANGE_WRITE); + } > > +#endif > > + return ret; > > +} > > + > > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > +{ > > + char *dirpath = g_path_get_dirname(fs_path->data); > > + char *name = g_path_get_basename(fs_path->data); > > + int ret = -1; > > + char *full_file_name = NULL; > > + DIR *dir; > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + ret = local_set_xattrat(full_file_name, name, credp); > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > + ret = -1; > > + errno = ENOTSUP; > > + } > > + closedir(dir); > > + > > +out: > > + if (full_file_name != NULL) { > > + g_free(full_file_name); > > + } > > + > > + g_free(dirpath); > > + g_free(name); > > + return ret; > > +} > > + > > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > > + const char *name, FsCred *credp) > > +{ > > + return -1; > > +} > > + > > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > > + const char *name, FsCred *credp) > > +{ > > + int err = -1; > > + char *full_file_name; > > + char *full_dir_name; > > + DIR *dir; > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > > + if (dir == NULL) { > > + return -1; > > + } > > + closedir(dir); > > + > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > > + full_file_name = merge_fs_path(full_dir_name, name); > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + err = mkdir(full_file_name); > > + if (err == -1) { > > + goto out; > > + } > > + credp->fc_mode = credp->fc_mode; > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + err = local_set_xattrat(full_dir_name, name, credp); > > + } else { > > + err = local_set_mapped_file_attrat(full_dir_name, name, credp); > > + } > > + if (err == -1) { > > + rmdir(full_file_name); > > + } > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > + err = mkdir(full_file_name); > > + if (err == -1) { > > + goto out; > > + } > > + /* Windows does not support chmod, do nothing here */ > > + } > > + > > + goto out; > > + > > +out: > > + g_free(full_dir_name); > > + g_free(full_file_name); > > + return err; > > +} > > + > > +static int local_fstat(FsContext *fs_ctx, int fid_type, > > + V9fsFidOpenState *fs, struct stat *stbuf) > > +{ > > + > > + int err, fd; > > + char filename[NAME_MAX]; > > + char *dirpath; > > + char *name; > > + HANDLE hFile; > > + DWORD dwRet; > > + > > + if (fid_type == P9_FID_DIR) { > > + /* Windows does not support open directory */ > > + return -1; > > + } else { > > + fd = fs->fd; > > + } > > + > > + err = fstat(fd, stbuf); > > + if (err) { > > + return err; > > + } > > + > > + /* get real file name by fd */ > > + hFile = (HANDLE)_get_osfhandle(fd); > > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); > > + > > + if (dwRet >= NAME_MAX) { > > + return -1; > > + } > > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > > + memmove(filename, filename + 4, NAME_MAX - 4); > > + > > + dirpath = g_path_get_dirname(filename); > > + name = g_path_get_basename(filename); > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + /* Actual credentials are part of extended attrs */ > > + uid_t tmp_uid; > > + gid_t tmp_gid; > > + mode_t tmp_mode; > > + dev_t tmp_dev; > > + > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > > + &tmp_uid, sizeof(uid_t)) > 0) { > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > + } > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > > + &tmp_gid, sizeof(gid_t)) > 0) { > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > + } > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > > + &tmp_mode, sizeof(mode_t)) > 0) { > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > + } > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > > + &tmp_dev, sizeof(dev_t)) > 0) { > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > + } > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + errno = EOPNOTSUPP; > > + g_free(dirpath); > > + g_free(name); > > + return -1; > > + } > > + > > + g_free(dirpath); > > + g_free(name); > > + > > + return err; > > +} > > + > > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char > > *name, + int flags, FsCred *credp, V9fsFidOpenState > > *fs) +{ > > + int fd = -1; > > + int err = -1; > > + char *full_file_name; > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + full_file_name = merge_fs_path(dir_path->data, name); > > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > > + g_free(full_file_name); > > + > > + err = fd; > > + fs->fd = fd; > > + goto out; > > + > > + close_preserve_errno(fd); > > +out: > > + return err; > > +} > > + > > + > > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > > + V9fsPath *dir_path, const char *name, FsCred > > *credp) +{ > > + return -1; > > +} > > + > > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > > + V9fsPath *dirpath, const char *name) > > +{ > > + return -1; > > +} > > + > > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > > +{ > > + int fd, ret; > > + > > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > > + if (fd == -1) { > > + return -1; > > + } > > + ret = ftruncate(fd, size); > > + close_preserve_errno(fd); > > + return ret; > > +} > > + > > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > +{ > > + char *full_file_name; > > + char *dirpath = g_path_get_dirname(fs_path->data); > > + char *name = g_path_get_basename(fs_path->data); > > + int ret = -1; > > + DIR *dir; > > + > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > + > > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > > + /* Windows does not support chown() */ > > + ret = -1; > > + errno = ENOTSUP; > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + ret = local_set_xattrat(full_file_name, name, credp); > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > + } > > + g_free(full_file_name); > > + closedir(dir); > > +out: > > + g_free(name); > > + g_free(dirpath); > > + return ret; > > +} > > + > > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > > + const struct timespec *buf) > > +{ > > + struct utimbuf tm; > > + char *full_file_name; > > + int err; > > + > > + tm.actime = buf[0].tv_sec; > > + tm.modtime = buf[1].tv_sec; > > + > > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > > + err = utime(full_file_name, &tm); > > + g_free(full_file_name); > > + > > + return err; > > +} > > + > > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > > + const char *name, int flags) > > +{ > > + int ret; > > + char *full_file_name; > > + char *full_dir_name; > > + > > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > > + full_file_name = merge_fs_path(full_dir_name, name); > > + > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + char *full_meta_dir_name; > > + char *full_meta_file_name; > > + > > + /* > > + * We need to remove the metadata as well: > > + * - the metadata directory if we're removing a directory > > + * - the metadata file in the parent's metadata directory > > + * > > + * If any of these are missing (ie, ENOENT) then we're probably > > + * trying to remove something that wasn't created in mapped-file > > + * mode. We just ignore the error. > > + */ > > + > > + if ((flags & AT_REMOVEDIR) != 0) { > > + full_meta_dir_name = merge_fs_path(full_file_name, > > VIRTFS_META_DIR); + ret = rmdir(full_meta_dir_name); > > + g_free(full_meta_dir_name); > > + > > + if (ret < 0 && errno != ENOENT) { > > + g_free(full_file_name); > > + g_free(full_dir_name); > > + return -1; > > + } > > + } > > + > > + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); > > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); + > > ret = remove(full_meta_file_name); > > + g_free(full_meta_dir_name); > > + g_free(full_meta_file_name); > > + > > + if (ret < 0 && errno != ENOENT) { > > + g_free(full_dir_name); > > + g_free(full_file_name); > > + > > + return -1; > > + } > > + } > > + > > + if ((flags & AT_REMOVEDIR) != 0) { > > + ret = rmdir(full_file_name); > > + } else { > > + ret = remove(full_file_name); > > + } > > + > > + g_free(full_dir_name); > > + g_free(full_file_name); > > + > > + return ret; > > +} > > + > > +static int local_remove(FsContext *ctx, const char *path) > > +{ > > + int err; > > + DIR *stream; > > + char *full_file_name; > > + char *dirpath = g_path_get_dirname(path); > > + char *name = g_path_get_basename(path); > > + int flags = 0; > > + > > + full_file_name = merge_fs_path(ctx->fs_root, path); > > + stream = opendir(full_file_name); > > + if (stream != NULL) { > > + closedir(stream); > > + flags |= AT_REMOVEDIR; > > + } > > + err = local_unlinkat_common(ctx, dirpath, name, flags); > > + > > + g_free(name); > > + g_free(dirpath); > > + g_free(full_file_name); > > + return err; > > +} > > + > > +static int local_fsync(FsContext *ctx, int fid_type, > > + V9fsFidOpenState *fs, int datasync) > > +{ > > + if (fid_type != P9_FID_DIR) { > > + return _commit(fs->fd); > > + } > > + return 0; > > +} > > + > > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs > > *stbuf) +{ > > + int ret; > > + ret = qemu_statfs(s->fs_root, stbuf); > > + if (ret == 0) { > > + /* use context address as fsid */ > > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > > + } > > + > > + return ret; > > +} > > + > > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > > + const char *name, void *value, size_t size) > > +{ > > + return -1; > > +} > > + > > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > > + void *value, size_t size) > > +{ > > + return -1; > > +} > > + > > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char > > *name, + void *value, size_t size, int flags) > > +{ > > + return -1; > > +} > > + > > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > > + const char *name) > > +{ > > + return -1; > > +} > > + > > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > > + const char *name, V9fsPath *target) > > +{ > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + if (dir_path) { > > + if (!strcmp(name, ".")) { > > + /* "." relative to "foo/bar" is "foo/bar" */ > > + v9fs_path_copy(target, dir_path); > > + } else if (!strcmp(name, "..")) { > > + if (!strcmp(dir_path->data, ".")) { > > + /* ".." relative to the root is "." */ > > + v9fs_path_sprintf(target, "."); > > + } else { > > + char *tmp = g_path_get_dirname(dir_path->data); > > + /* > > + * Symbolic links are resolved by the client. We can > assume > > + * that ".." relative to "foo/bar" is equivalent to "foo" > > + */ > > + v9fs_path_sprintf(target, "%s", tmp); > > + g_free(tmp); > > + } > > + } else { > > + assert(!strchr(name, '/')); > > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > > + } > > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > > + !strcmp(name, "..")) { > > + /* This is the root fid */ > > + v9fs_path_sprintf(target, "."); > > + } else { > > + assert(!strchr(name, '/')); > > + v9fs_path_sprintf(target, "./%s", name); > > + } > > + return 0; > > +} > > + > > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > > + const char *old_name, V9fsPath *newdir, > > + const char *new_name) > > +{ > > + return -1; > > +} > > + > > +static int local_rename(FsContext *ctx, const char *oldpath, > > + const char *newpath) > > +{ > > + int err; > > + > > + char *full_old_name; > > + char *full_new_name; > > + > > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > > + > > + err = rename(full_old_name, full_new_name); > > + > > + g_free(full_old_name); > > + g_free(full_new_name); > > + > > + return err; > > +} > > + > > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > > + const char *name, int flags) > > +{ > > + int ret; > > + > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > > + > > + return ret; > > +} > > + > > +static int check_filesystem_type(char *fs_root, int export_flags) > > +{ > > + HANDLE hFile; > > + wchar_t FsName[MAX_PATH + 1] = {0}; > > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > > + > > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > > + return 0; > > + } > > + > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > + if (hFile == INVALID_HANDLE_VALUE) { > > + return -1; > > + } > > + > > + /* Get file system type name */ > > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > > + FsName, MAX_PATH + 1) == 0) { > > + CloseHandle(hFile); > > + return -1; > > + } > > + CloseHandle(hFile); > > + > > + if (wcscmp(FsName, NtfsName) != 0) { > > + return -1; > > + } > > + > > + return 0; > > +} > > + > > +static int local_init(FsContext *ctx, Error **errp) > > +{ > > + LocalData *data = g_malloc(sizeof(*data)); > > + > > + struct stat StatBuf; > > + > > + if (stat(ctx->fs_root, &StatBuf) != 0) { > > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > > + goto err; > > + } > > + > > + /* > > + * security_model=mapped(-xattr) requires a fileystem on Windows that > > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > > + * probably most popular on Windows. It is fair enough to assume > > + * Windows users to use NTFS for the mapped security model. > > + */ > > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > > + "security_model is mapped or mapped-xattr"); > > + goto err; > > + } > > + > > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > > + ctx->xops = passthrough_xattr_ops; > > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > > + ctx->xops = mapped_xattr_ops; > > + } else if (ctx->export_flags & V9FS_SM_NONE) { > > + ctx->xops = none_xattr_ops; > > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + /* > > + * xattr operation for mapped-file and passthrough > > + * remain same. > > + */ > > + ctx->xops = passthrough_xattr_ops; > > + } > > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > > + > > + ctx->private = data; > > + return 0; > > + > > +err: > > + g_free(data); > > + return -1; > > +} > > + > > +static void local_cleanup(FsContext *ctx) > > +{ > > + LocalData *data = ctx->private; > > + > > + if (!data) { > > + return; > > + } > > + > > + close(data->mountfd); > > + g_free(data); > > +} > > + > > +static void error_append_security_model_hint(Error *const *errp) > > +{ > > + error_append_hint(errp, "Valid options are: security_model=" > > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > > +} > > + > > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error > > **errp) +{ > > + ERRP_GUARD(); > > + const char *sec_model = qemu_opt_get(opts, "security_model"); > > + const char *path = qemu_opt_get(opts, "path"); > > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > > + > > + if (!sec_model) { > > + error_setg(errp, "security_model property not set"); > > + error_append_security_model_hint(errp); > > + return -1; > > + } > > + > > + if (!strcmp(sec_model, "passthrough")) { > > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > > + } else if (!strcmp(sec_model, "mapped") || > > + !strcmp(sec_model, "mapped-xattr")) { > > + fse->export_flags |= V9FS_SM_MAPPED; > > + } else if (!strcmp(sec_model, "none")) { > > + fse->export_flags |= V9FS_SM_NONE; > > + } else if (!strcmp(sec_model, "mapped-file")) { > > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > > + } else { > > + error_setg(errp, "invalid security_model property '%s'", > > sec_model); + error_append_security_model_hint(errp); > > + return -1; > > + } > > + > > + if (multidevs) { > > + if (!strcmp(multidevs, "remap")) { > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > + fse->export_flags |= V9FS_REMAP_INODES; > > + } else if (!strcmp(multidevs, "forbid")) { > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > > + } else if (!strcmp(multidevs, "warn")) { > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > + } else { > > + error_setg(errp, "invalid multidevs property '%s'", > > + multidevs); > > + error_append_hint(errp, "Valid options are: multidevs=" > > + "[remap|forbid|warn]\n"); > > + return -1; > > + } > > + } > > + > > + if (!path) { > > + error_setg(errp, "path property not set"); > > + return -1; > > + } > > + > > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > > + error_prepend(errp, "invalid throttle configuration: "); > > + return -1; > > + } > > + > > + if (fse->export_flags & V9FS_SM_MAPPED || > > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > > + fse->fmode = > > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > > + fse->dmode = > > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & > > 0777; + } else { > > + if (qemu_opt_find(opts, "fmode")) { > > + error_setg(errp, "fmode is only valid for mapped security > > modes"); + return -1; > > + } > > + if (qemu_opt_find(opts, "dmode")) { > > + error_setg(errp, "dmode is only valid for mapped security > > modes"); + return -1; > > + } > > + } > > + > > + fse->path = g_strdup(path); > > + > > + return 0; > > +} > > + > > +FileOperations local_ops = { > > + .parse_opts = local_parse_opts, > > + .init = local_init, > > + .cleanup = local_cleanup, > > + .lstat = local_lstat, > > + .readlink = local_readlink, > > + .close = local_close, > > + .closedir = local_closedir, > > + .open = local_open, > > + .opendir = local_opendir, > > + .rewinddir = local_rewinddir, > > + .telldir = local_telldir, > > + .readdir = local_readdir, > > + .seekdir = local_seekdir, > > + .preadv = local_preadv, > > + .pwritev = local_pwritev, > > + .chmod = local_chmod, > > + .mknod = local_mknod, > > + .mkdir = local_mkdir, > > + .fstat = local_fstat, > > + .open2 = local_open2, > > + .symlink = local_symlink, > > + .link = local_link, > > + .truncate = local_truncate, > > + .rename = local_rename, > > + .chown = local_chown, > > + .utimensat = local_utimensat, > > + .remove = local_remove, > > + .fsync = local_fsync, > > + .statfs = local_statfs, > > + .lgetxattr = local_lgetxattr, > > + .llistxattr = local_llistxattr, > > + .lsetxattr = local_lsetxattr, > > + .lremovexattr = local_lremovexattr, > > + .name_to_path = local_name_to_path, > > + .renameat = local_renameat, > > + .unlinkat = local_unlinkat, > > +}; > > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > > new file mode 100644 > > index 0000000000..d9b35e7425 > > --- /dev/null > > +++ b/hw/9pfs/9p-util-win32.c > > @@ -0,0 +1,303 @@ > > +/* > > + * 9p utilities (Windows Implementation) > > + * > > + * Copyright (c) 2022 Wind River Systems, Inc. > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2 or > > later. + * See the COPYING file in the top-level directory. > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "qapi/error.h" > > +#include "qemu/error-report.h" > > +#include "9p.h" > > +#include "9p-util.h" > > +#include "9p-linux-errno.h" > > +#include <windows.h> > > +#include <dirent.h> > > + > > +#ifndef V9FS_MAGIC > > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > > +#endif > > + > > +static int build_ads_name(char *namebuf, size_t namebuflen, > > + const char *dirname, const char *filename, > > + const char *ads_name) > > +{ > > + size_t totalsize; > > + > > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; > > + if (totalsize > namebuflen) { > > + return -1; > > + } > > + > > + /* > > + * NTFS ADS (Alternate Data Streams) name format: > > + * filename:ads_name > > + * e.g. > > + * d:\1.txt:my_ads_name > > + */ > > + strcpy(namebuf, dirname); > > + strcat(namebuf, "\\"); > > + strcat(namebuf, filename); > > + strcat(namebuf, ":"); > > + strcat(namebuf, ads_name); > > + > > + return 0; > > +} > > + > > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > > + char *fulladsname) > > +{ > > + char *p1, *p2; > > + > > + /* > > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > > + * :ads_name:$DATA > > + * e.g. > > + * :my_ads_name:$DATA > > + * > > + * ADS name from FindNextStreamW() always have ":$DATA" string at the > > end + * > > + * This function copy ADS name to namebuf. > > + */ > > + > > + p1 = strchr(fulladsname, ':'); > > + if (p1 == NULL) { > > + return -1; > > + } > > + > > + p2 = strchr(p1 + 1, ':'); > > + if (p2 == NULL) { > > + return -1; > > + } > > + > > + /* skip empty ads name */ > > + if (p2 - p1 == 1) { > > + return 0; > > + } > > + > > + if (p2 - p1 + 1 > namebuflen) { > > + return -1; > > + } > > + > > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > > + namebuf[p2 - p1 - 1] = '\0'; > > + > > + return p2 - p1; > > +} > > + > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size) > > +{ > > + HANDLE hStream; > > + char ADSFileName[NAME_MAX + 1] = {0}; > > + DWORD dwBytesRead; > > + > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) > > { + errno = EIO; > > + return -1; > > + } > > + > > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > > + if (hStream == INVALID_HANDLE_VALUE && > > + GetLastError() == ERROR_FILE_NOT_FOUND) { > > + errno = ENODATA; > > + return -1; > > + } > > + > > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > > + errno = EIO; > > + CloseHandle(hStream); > > + return -1; > > + } > > + > > + CloseHandle(hStream); > > + > > + return dwBytesRead; > > +} > > + > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > + char *list, size_t size) > > +{ > > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > > + char fulladsname[NAME_MAX + 1]; > > + char *full_fs_name = merge_fs_path(dirname, filename); > > + int ret; > > + HANDLE hFind; > > + WIN32_FIND_STREAM_DATA fsd; > > + BOOL bFindNext; > > + char *listptr = list; > > + size_t listleftsize = size; > > + > > + /* > > + * ADS emurate function only have WCHAR version, need to covert > > filename + * to WCHAR string. > > + */ > > + > > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > > + strlen(full_fs_name), WideCharStr, NAME_MAX); > > + g_free(full_fs_name); > > + if (ret == 0) { > > + errno = EIO; > > + return -1; > > + } > > + > > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); > > + if (hFind == INVALID_HANDLE_VALUE) { > > + errno = ENODATA; > > + return -1; > > + } > > + > > + do { > > + memset(fulladsname, 0, sizeof(fulladsname)); > > + > > + /* > > + * ADS emurate function only have WCHAR version, need to covert > > + * cStreamName to utf-8 string. > > + */ > > + > > + ret = WideCharToMultiByte(CP_UTF8, 0, > > + fsd.cStreamName, wcslen(fsd.cStreamName) > > + 1, + fulladsname, sizeof(fulladsname) - > > 1, + NULL, NULL); > > + > > + if (ret == 0) { > > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > > + errno = ERANGE; > > + } > > + CloseHandle(hFind); > > + return -1; > > + } > > + > > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > > + if (ret < 0) { > > + errno = ERANGE; > > + CloseHandle(hFind); > > + return -1; > > + } > > + > > + listptr = listptr + ret; > > + listleftsize = listleftsize - ret; > > + > > + bFindNext = FindNextStreamW(hFind, &fsd); > > + } while (bFindNext); > > + > > + CloseHandle(hFind); > > + > > + return size - listleftsize; > > +} > > + > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > + const char *name) > > +{ > > + char ADSFileName[NAME_MAX + 1] = {0}; > > + > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) > > { + errno = EIO; > > + return -1; > > + } > > + > > + if (DeleteFile(ADSFileName) != 0) { > > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > > + errno = ENODATA; > > + return -1; > > + } > > + } > > + > > + return 0; > > +} > > + > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size, int > > flags) +{ > > + HANDLE hStream; > > + char ADSFileName[NAME_MAX + 1] = {0}; > > + DWORD dwBytesWrite; > > + > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) > > { + errno = EIO; > > + return -1; > > + } > > + > > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, > > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + > > if (hStream == INVALID_HANDLE_VALUE) { > > + errno = EIO; > > + return -1; > > + } > > + > > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { > > + errno = EIO; > > + CloseHandle(hStream); > > + return -1; > > + } > > + > > + CloseHandle(hStream); > > + > > + return 0; > > +} > > + > > +int qemu_mknodat(const char *dirname, const char *filename, > > + mode_t mode, dev_t dev) > > +{ > > + errno = ENOTSUP; > > + return -1; > > +} > > + > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > > +{ > > + HANDLE hFile; > > + char RealPath[NAME_MAX + 1]; > > + unsigned long SectorsPerCluster; > > + unsigned long BytesPerSector; > > + unsigned long NumberOfFreeClusters; > > + unsigned long TotalNumberOfClusters; > > + > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > + if (hFile == INVALID_HANDLE_VALUE) { > > + errno = EIO; > > + return -1; > > + } > > + > > + /* get real path of root */ > > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > > + FILE_NAME_NORMALIZED) == 0) { > > + errno = EIO; > > + CloseHandle(hFile); > > + return -1; > > + } > > + > > + CloseHandle(hFile); > > + > > + /* > > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > > + * "C:\\123" will be "\\\\?\\C:\\123" > > + * Skip first 4 bytes and truncate the string at offset 7, it will get > > + * the real root directory like "C:\\", this is parameter > > GetDiskFreeSpace + * needed. > > + */ > > + > > + RealPath[7] = '\0'; > > + > > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, > > + &NumberOfFreeClusters, &TotalNumberOfClusters) == > > 0) { + errno = EIO; > > + return -1; > > + } > > + > > + stbuf->f_type = V9FS_MAGIC; > > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > > + stbuf->f_files = -1; > > + stbuf->f_ffree = -1; > > + stbuf->f_namelen = NAME_MAX; > > + stbuf->f_frsize = 0; > > + stbuf->f_flags = 0; > > + > > + return 0; > > +} > > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > > index 9ae69dd8db..5623f0e2ef 100644 > > --- a/hw/9pfs/9p-xattr.c > > +++ b/hw/9pfs/9p-xattr.c > > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char > > *path, char *orig_value, *orig_value_start; > > ssize_t xattr_len, parsed_len = 0, attr_len; > > char *dirpath, *name; > > +#ifdef CONFIG_WIN32 > > + char *full_dir_path; > > + DIR *dir; > > +#else > > int dirfd; > > +#endif > > > > /* Get the actual len */ > > dirpath = g_path_get_dirname(path); > > + > > +#ifdef CONFIG_WIN32 > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + return -1; > > + } > > + closedir(dir); > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + g_free(dirpath); > > + > > + name = g_path_get_basename(path); > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > > + if (xattr_len <= 0) { > > + g_free(name); > > + g_free(full_dir_path); > > + return xattr_len; > > + } > > + > > + /* Now fetch the xattr and find the actual size */ > > + orig_value = g_malloc(xattr_len); > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > > + xattr_len); > > + g_free(name); > > + g_free(full_dir_path); > > + if (xattr_len < 0) { > > + g_free(orig_value); > > + return -1; > > + } > > +#else > > dirfd = local_opendir_nofollow(ctx, dirpath); > > g_free(dirpath); > > if (dirfd == -1) { > > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char > > *path, g_free(orig_value); > > return -1; > > } > > +#endif > > > > /* store the orig pointer */ > > orig_value_start = orig_value; > > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > > const char *name, void *value, size_t size) > > { > > +#ifdef CONFIG_WIN32 > > + char *dirpath = g_path_get_dirname(path); > > + char *filename = g_path_get_basename(path); > > + char *full_dir_path; > > + DIR *dir; > > + ssize_t ret = -1; > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + closedir(dir); > > + > > + ret = fgetxattrat_nofollow(full_dir_path, filename, > > + name, value, size); > > + > > +out: > > + g_free(full_dir_path); > > + g_free(dirpath); > > + g_free(filename); > > + > > + return ret; > > +#else > > char *dirpath = g_path_get_dirname(path); > > char *filename = g_path_get_basename(path); > > int dirfd; > > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const > > char *path, } > > > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > > + > > close_preserve_errno(dirfd); > > out: > > g_free(dirpath); > > g_free(filename); > > return ret; > > +#endif > > } > > > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const > > char *path, const char *name, void *value, size_t size, int flags) > > { > > +#ifdef CONFIG_WIN32 > > + char *dirpath = g_path_get_dirname(path); > > + char *filename = g_path_get_basename(path); > > + char *full_dir_path; > > + DIR *dir; > > + ssize_t ret = -1; > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + closedir(dir); > > + > > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > > + value, size, flags); > > +out: > > + g_free(full_dir_path); > > + g_free(dirpath); > > + g_free(filename); > > + return ret; > > + > > +#else > > char *dirpath = g_path_get_dirname(path); > > char *filename = g_path_get_basename(path); > > int dirfd; > > @@ -210,6 +297,7 @@ out: > > g_free(dirpath); > > g_free(filename); > > return ret; > > +#endif > > } > > > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void > > *value, @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char > > *path, const char *name, void *value, ssize_t > > local_removexattr_nofollow(FsContext *ctx, const char *path, const char > > *name) > > { > > +#ifdef CONFIG_WIN32 > > + char *dirpath = g_path_get_dirname(path); > > + char *filename = g_path_get_basename(path); > > + char *full_dir_path; > > + DIR *dir; > > + ssize_t ret = -1; > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + closedir(dir); > > + > > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > > + > > +out: > > + g_free(full_dir_path); > > + g_free(dirpath); > > + g_free(filename); > > + return ret; > > + > > +#else > > char *dirpath = g_path_get_dirname(path); > > char *filename = g_path_get_basename(path); > > int dirfd; > > @@ -237,6 +349,7 @@ out: > > g_free(dirpath); > > g_free(filename); > > return ret; > > +#endif > > } > > > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > > index 225f31fc31..a04889c1d6 100644 > > --- a/hw/9pfs/9p.c > > +++ b/hw/9pfs/9p.c > > @@ -38,6 +38,10 @@ > > #include "migration/blocker.h" > > #include "qemu/xxhash.h" > > #include <math.h> > > +#ifdef CONFIG_WIN32 > > +#define UTIME_NOW ((1l << 30) - 1l) > > +#define UTIME_OMIT ((1l << 30) - 2l) > > +#endif > > > > int open_fd_hw; > > int total_open_fd; > > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat > > *stbuf, V9fsQID *qidp) if (S_ISDIR(stbuf->st_mode)) { > > qidp->type |= P9_QID_TYPE_DIR; > > } > > +#ifndef CONFIG_WIN32 > > if (S_ISLNK(stbuf->st_mode)) { > > qidp->type |= P9_QID_TYPE_SYMLINK; > > } > > +#endif > > > > return 0; > > } > > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, > V9fsString > > *extension) ret |= S_IFDIR; > > } > > > > +#ifndef CONFIG_WIN32 > > if (mode & P9_STAT_MODE_SYMLINK) { > > ret |= S_IFLNK; > > } > > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, > V9fsString > > *extension) if (mode & P9_STAT_MODE_NAMED_PIPE) { > > ret |= S_IFIFO; > > } > > +#endif > > if (mode & P9_STAT_MODE_DEVICE) { > > if (extension->size && extension->data[0] == 'c') { > > ret |= S_IFCHR; > > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, > V9fsString > > *extension) ret |= S_IFREG; > > } > > > > +#ifndef CONFIG_WIN32 > > if (mode & P9_STAT_MODE_SETUID) { > > ret |= S_ISUID; > > } > > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, > V9fsString > > *extension) if (mode & P9_STAT_MODE_SETVTX) { > > ret |= S_ISVTX; > > } > > +#endif > > > > return ret; > > } > > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat > > *stbuf) mode |= P9_STAT_MODE_DIR; > > } > > > > +#ifndef CONFIG_WIN32 > > if (S_ISLNK(stbuf->st_mode)) { > > mode |= P9_STAT_MODE_SYMLINK; > > } > > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat > > *stbuf) if (S_ISFIFO(stbuf->st_mode)) { > > mode |= P9_STAT_MODE_NAMED_PIPE; > > } > > +#endif > > > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > > mode |= P9_STAT_MODE_DEVICE; > > } > > > > +#ifndef CONFIG_WIN32 > > if (stbuf->st_mode & S_ISUID) { > > mode |= P9_STAT_MODE_SETUID; > > } > > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat > > *stbuf) if (stbuf->st_mode & S_ISVTX) { > > mode |= P9_STAT_MODE_SETVTX; > > } > > +#endif > > > > return mode; > > } > > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, > > V9fsPath *path, return err; > > } > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > > +#ifndef CONFIG_WIN32 > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > > +#else > > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > + 0, 0); > > +#endif > > + > > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, > > int32_t blksize) > > > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > > { > > +#ifndef CONFIG_WIN32 > > return blksize_to_iounit(pdu, stbuf->st_blksize); > > +#else > > + return blksize_to_iounit(pdu, 0); > > +#endif > > } > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > > struct stat *stbuf, v9lstat->st_gid = stbuf->st_gid; > > v9lstat->st_rdev = stbuf->st_rdev; > > v9lstat->st_size = stbuf->st_size; > > + > > +#ifdef CONFIG_WIN32 > > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > + v9lstat->st_blocks = 0; > > + v9lstat->st_atime_sec = stbuf->st_atime; > > + v9lstat->st_mtime_sec = stbuf->st_mtime; > > + v9lstat->st_ctime_sec = stbuf->st_ctime; > > +#else /* !CONFIG_WIN32 */ > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > v9lstat->st_blocks = stbuf->st_blocks; > > v9lstat->st_atime_sec = stbuf->st_atime; > > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > > struct stat *stbuf, v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > > #endif > > +#endif /* CONFIG_WIN32 */ > > + > > /* Currently we only support BASIC fields in stat */ > > v9lstat->st_result_mask = P9_STATS_BASIC; > > > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > > > static bool same_stat_id(const struct stat *a, const struct stat *b) > > { > > +#ifdef CONFIG_WIN32 > > + return 0; > > +#else > > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > > +#endif /* CONFIG_WIN32 */ > > } > > > > static void coroutine_fn v9fs_walk(void *opaque) > > @@ -2300,7 +2339,11 @@ static int coroutine_fn > > v9fs_do_readdir_with_stat(V9fsPDU *pdu, count += len; > > v9fs_stat_free(&v9stat); > > v9fs_path_free(&path); > > +#ifndef CONFIG_WIN32 > > saved_dir_pos = qemu_dirent_off(dent); > > +#else > > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > > +#endif > > } > > > > v9fs_readdir_unlock(&fidp->fs.dir); > > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU > > *pdu, V9fsFidState *fidp, qid.version = 0; > > } > > > > +#ifdef CONFIG_WIN32 > > + /* > > + * Windows does not have dent->d_off, get offset by calling > > telldir() + * manually. > > + */ > > + off = v9fs_co_telldir(pdu, fidp); > > +#else > > off = qemu_dirent_off(dent); > > +#endif > > v9fs_string_init(&name); > > v9fs_string_sprintf(&name, "%s", dent->d_name); > > > > +#ifdef CONFIG_WIN32 > > + /* > > + * Windows does not have dent->d_type > > + */ > > + > > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > > + &qid, off, > > + 0, &name); > > +#else > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > > len = pdu_marshal(pdu, 11 + count, "Qqbs", > > &qid, off, > > dent->d_type, &name); > > +#endif > > > > v9fs_string_free(&name); > > > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > > } > > > > nmode |= perm & 0777; > > + > > +#ifndef CONFIG_WIN32 > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > makedev(major, minor), nmode, &stbuf); > > +#else > > + err = -ENOTSUP; > > +#endif > > + > > if (err < 0) { > > goto out; > > } > > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > > v9fs_path_copy(&fidp->path, &path); > > v9fs_path_unlock(s); > > } else if (perm & P9_STAT_MODE_SOCKET) { > > +#ifndef CONFIG_WIN32 > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > 0, S_IFSOCK | (perm & 0777), &stbuf); > > +#else > > + err = -ENOTSUP; > > +#endif > > if (err < 0) { > > goto out; > > } > > @@ -3600,6 +3671,7 @@ out_nofid: > > static void coroutine_fn v9fs_mknod(void *opaque) > > { > > > > +#ifndef CONFIG_WIN32 > > int mode; > > gid_t gid; > > int32_t fid; > > @@ -3656,6 +3728,11 @@ out: > > out_nofid: > > pdu_complete(pdu, err); > > v9fs_string_free(&name); > > +#else > > + V9fsPDU *pdu = opaque; > > + > > + pdu_complete(pdu, -1); > > +#endif > > } > > > > /* > > @@ -3928,7 +4005,7 @@ out_nofid: > > #if defined(CONFIG_LINUX) > > /* Currently, only Linux has XATTR_SIZE_MAX */ > > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > > -#elif defined(CONFIG_DARWIN) > > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > > /* > > * Darwin doesn't seem to define a maximum xattr size in its user > > * space header, so manually configure it across platforms as 64k. > > @@ -3945,6 +4022,7 @@ out_nofid: > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > > { > > +#ifndef CONFIG_WIN32 > > int flags, rflags = 0; > > int32_t fid; > > uint64_t size; > > @@ -4006,10 +4084,15 @@ out_put_fid: > > out_nofid: > > pdu_complete(pdu, err); > > v9fs_string_free(&name); > > +#else > > + V9fsPDU *pdu = opaque; > > + pdu_complete(pdu, -1); > > +#endif > > } > > > > static void coroutine_fn v9fs_readlink(void *opaque) > > { > > +#ifndef CONFIG_WIN32 > > V9fsPDU *pdu = opaque; > > size_t offset = 7; > > V9fsString target; > > @@ -4045,6 +4128,10 @@ out: > > put_fid(pdu, fidp); > > out_nofid: > > pdu_complete(pdu, err); > > +#else > > + V9fsPDU *pdu = opaque; > > + pdu_complete(pdu, -1); > > +#endif > > } > > > > static CoroutineEntry *pdu_co_handlers[] = { > > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > > { > > +#ifndef CONFIG_WIN32 > > struct rlimit rlim; > > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > > error_report("Failed to get the resource limit"); > > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) > > v9fs_set_fd_limit(void) } > > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > > open_fd_rc = rlim.rlim_cur / 2; > > +#endif > > } > > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > > index 93ba44fb75..2fbe7b831b 100644 > > --- a/hw/9pfs/codir.c > > +++ b/hw/9pfs/codir.c > > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > > *fidp, int len, err = 0; > > int32_t size = 0; > > off_t saved_dir_pos; > > +#ifdef CONFIG_WIN32 > > + off_t next_dir_pos; > > +#endif > > struct dirent *dent; > > struct V9fsDirEnt *e = NULL; > > V9fsPath path; > > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > > *fidp, break; > > } > > > > +#ifdef CONFIG_WIN32 > > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > > + if (next_dir_pos < 0) { > > + err = next_dir_pos; > > + goto out; > > + } > > +#endif > > + > > /* > > * stop this loop as soon as it would exceed the allowed maximum > > * response message size for the directory entries collected so > > far, @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, > > V9fsFidState *fidp, } > > > > size += len; > > +#ifndef CONFIG_WIN32 > > saved_dir_pos = qemu_dirent_off(dent); > > +#else > > + saved_dir_pos = next_dir_pos; > > +#endif > > } > > > > /* restore (last) saved position */ > ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-04 19:34 ` Shi, Guohuai @ 2022-05-05 11:43 ` Christian Schoenebeck 2022-05-06 6:46 ` Shi, Guohuai 0 siblings, 1 reply; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-05 11:43 UTC (permalink / raw) To: qemu-devel, Shi, Guohuai, Greg Kurz; +Cc: Meng, Bin, Bin Meng On Mittwoch, 4. Mai 2022 21:34:22 CEST Shi, Guohuai wrote: [...] > > > index 0000000000..aab7c9f7b5 > > > --- /dev/null > > > +++ b/hw/9pfs/9p-local-win32.c > > > @@ -0,0 +1,1242 @@ > > > +/* > > > + * 9p Windows callback > > > + * > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > + * > > > + * Based on hw/9pfs/9p-local.c > > > + * > > > + * This work is licensed under the terms of the GNU GPL, version 2. > > > See > > > + * the COPYING file in the top-level directory. > > > + */ > > > + > > > +/* > > > + * Not so fast! You might want to read the 9p developer docs first: > > > + * https://wiki.qemu.org/Documentation/9p > > > + */ > > > + > > > +#include "qemu/osdep.h" > > > +#include <windows.h> > > > +#include <dirent.h> > > > +#include "9p.h" > > > +#include "9p-local.h" > > > +#include "9p-xattr.h" > > > +#include "9p-util.h" > > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > > +#include "qapi/error.h" > > > +#include "qemu/cutils.h" > > > +#include "qemu/error-report.h" > > > +#include "qemu/option.h" > > > +#include <libgen.h> > > > > I'm not sure whether all of this (i.e. 9p-local-win32.c in general) is > > really needed. I mean yes, it probably does the job, but you basically > > add a complete > > separate 'local' backend implementation just for the Windows host > > platform. > > > > My expectation would be to stick to 9p-local.c being OS-agnostic, and only > > define OS-specific functionality in 9p-util-windows.c if needed. > > > > And most importantly: don't duplicate code as far as possible! I mean > > somebody > > would need to also review and maintain all of this. > > Actually, almost all FileOperations functions are re-written for Windows > host. > > Here is the comparison statistics for 9p-local.c and 9p-local-win32.c: > Total lines : 1611 > Changed lines: 590 > Inserted lines: 138 > Removed lines: 414 > > For function level difference count: > > Changed function: 39 > Unchanged function: 13 > > If use "#ifdef _WIN32" to sperate Windows host code, it may need to be > inserted about 150 code blocks (or 39 code blocks for all changed > functions). > > I am not sure if it is a good idea to insert so many "#ifdef _WIN32", it may > cause this file not readable. > > If stick to 9p-local.c being OS-agnostic, I think it is better to create two > new files: 9p-local-linux.c and 9p-local-win32.c The thing is, as this is presented right now, I can hardly even see where deviating behaviour for Windows would be, where not, and I'm missing any explanations and reasons for the individual deviations right now. Chances are that you are unnecessarilly adding duplicate code and unnecessary code deviations. For me this 9p-local-win32.c approach looks overly complex and not appropriately abstracted on first sight. I suggest waiting for Greg to give his opinion on this as well before continuing. Best regards, Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-05 11:43 ` Christian Schoenebeck @ 2022-05-06 6:46 ` Shi, Guohuai 0 siblings, 0 replies; 34+ messages in thread From: Shi, Guohuai @ 2022-05-06 6:46 UTC (permalink / raw) To: Christian Schoenebeck, qemu-devel, Greg Kurz; +Cc: Meng, Bin, Bin Meng > -----Original Message----- > From: Christian Schoenebeck <qemu_oss@crudebyte.com> > Sent: 2022年5月5日 19:44 > To: qemu-devel@nongnu.org; Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz > <groug@kaod.org> > Cc: Meng, Bin <Bin.Meng@windriver.com>; Bin Meng <bmeng.cn@gmail.com> > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > Windows > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > On Mittwoch, 4. Mai 2022 21:34:22 CEST Shi, Guohuai wrote: > [...] > > > > index 0000000000..aab7c9f7b5 > > > > --- /dev/null > > > > +++ b/hw/9pfs/9p-local-win32.c > > > > @@ -0,0 +1,1242 @@ > > > > +/* > > > > + * 9p Windows callback > > > > + * > > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > > + * > > > > + * Based on hw/9pfs/9p-local.c > > > > + * > > > > + * This work is licensed under the terms of the GNU GPL, version 2. > > > > See > > > > + * the COPYING file in the top-level directory. > > > > + */ > > > > + > > > > +/* > > > > + * Not so fast! You might want to read the 9p developer docs first: > > > > + * https://wiki.qemu.org/Documentation/9p > > > > + */ > > > > + > > > > +#include "qemu/osdep.h" > > > > +#include <windows.h> > > > > +#include <dirent.h> > > > > +#include "9p.h" > > > > +#include "9p-local.h" > > > > +#include "9p-xattr.h" > > > > +#include "9p-util.h" > > > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > > > +#include "qapi/error.h" > > > > +#include "qemu/cutils.h" > > > > +#include "qemu/error-report.h" > > > > +#include "qemu/option.h" > > > > +#include <libgen.h> > > > > > > I'm not sure whether all of this (i.e. 9p-local-win32.c in general) > > > is really needed. I mean yes, it probably does the job, but you > > > basically add a complete separate 'local' backend implementation > > > just for the Windows host platform. > > > > > > My expectation would be to stick to 9p-local.c being OS-agnostic, > > > and only define OS-specific functionality in 9p-util-windows.c if needed. > > > > > > And most importantly: don't duplicate code as far as possible! I > > > mean somebody would need to also review and maintain all of this. > > > > Actually, almost all FileOperations functions are re-written for > > Windows host. > > > > Here is the comparison statistics for 9p-local.c and 9p-local-win32.c: > > Total lines : 1611 > > Changed lines: 590 > > Inserted lines: 138 > > Removed lines: 414 > > > > For function level difference count: > > > > Changed function: 39 > > Unchanged function: 13 > > > > If use "#ifdef _WIN32" to sperate Windows host code, it may need to be > > inserted about 150 code blocks (or 39 code blocks for all changed > > functions). > > > > I am not sure if it is a good idea to insert so many "#ifdef _WIN32", > > it may cause this file not readable. > > > > If stick to 9p-local.c being OS-agnostic, I think it is better to > > create two new files: 9p-local-linux.c and 9p-local-win32.c > > The thing is, as this is presented right now, I can hardly even see where deviating > behaviour for Windows would be, where not, and I'm missing any explanations and > reasons for the individual deviations right now. Chances are that you are > unnecessarilly adding duplicate code and unnecessary code deviations. For me this > 9p-local-win32.c approach looks overly complex and not appropriately abstracted > on first sight. > > I suggest waiting for Greg to give his opinion on this as well before continuing. > > Best regards, > Christian Schoenebeck > I can make this commit to be split into several commits: Commit #1: only make Windows host built successfully. Commit #2: provide some basic file system operations and leave other functions not workable (return -1). Commit #3: provide full file system operations. Commit #4: provide security mode "mapped" by NTFS ADS Commit #5: provide security mode "mapped-file" Commit #6: fix Windows (MinGW) directory access API compatible issues However, I think I may still need a standalone file "9p-local-win32.c". Some functions could be moved (or merged) back to 9p-local.c. But there are still some functions are too complex to be merged. Thanks Guohuai ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-04-25 14:27 ` [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows Bin Meng 2022-05-04 18:01 ` Christian Schoenebeck @ 2022-05-09 14:29 ` Greg Kurz 2022-05-09 15:09 ` Shi, Guohuai 1 sibling, 1 reply; 34+ messages in thread From: Greg Kurz @ 2022-05-09 14:29 UTC (permalink / raw) To: Bin Meng; +Cc: Christian Schoenebeck, qemu-devel, Guohuai Shi, Bin Meng On Mon, 25 Apr 2022 22:27:01 +0800 Bin Meng <bmeng.cn@gmail.com> wrote: > From: Guohuai Shi <guohuai.shi@windriver.com> > > Add a 9p local file system backend driver to support Windows, > including open, read, write, close, rename, remove, etc. > > All security models are supported. The mapped (mapped-xattr) > security model is implemented using NTFS Alternate Data Stream > (ADS) so the 9p export path shall be on an NTFS partition. > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > Signed-off-by: Bin Meng <bin.meng@windriver.com> > --- > Hi ! I tend to agree with Christian's remarks that this patch is too big and that the choice of introducing right away a new implementation of 9p-local for windows hosts is too bold to start with. We need to clearly understand what's diverging between windows and linux in order to make such a decision. You should first try to introduce the required abstractions to cope with these differences, so that we can review. Some inlined remarks below anyway. > hw/9pfs/9p-linux-errno.h | 151 +++++ > hw/9pfs/9p-local.h | 4 + > hw/9pfs/9p-util.h | 41 ++ > hw/9pfs/9p.h | 23 + > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > hw/9pfs/9p-xattr.c | 113 ++++ > hw/9pfs/9p.c | 91 ++- > hw/9pfs/codir.c | 15 + > 9 files changed, 1982 insertions(+), 1 deletion(-) > create mode 100644 hw/9pfs/9p-linux-errno.h > create mode 100644 hw/9pfs/9p-local-win32.c > create mode 100644 hw/9pfs/9p-util-win32.c > > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > new file mode 100644 > index 0000000000..b0d6ac45ac > --- /dev/null > +++ b/hw/9pfs/9p-linux-errno.h > @@ -0,0 +1,151 @@ > +/* > + * 9p Linux errno translation definition > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + */ > + > +#include <errno.h> > + > +#ifndef QEMU_9P_LINUX_ERRNO_H > +#define QEMU_9P_LINUX_ERRNO_H > + > +/* > + * This file contains the Linux errno definitions to translate errnos set by > + * the 9P server (running on Windows) to a corresponding errno value. > + * > + * This list should be periodically reviewed and updated; particularly for > + * errnos that might be set as a result of a file system operation. > + */ > + > +#define L_EPERM 1 /* Operation not permitted */ > +#define L_ENOENT 2 /* No such file or directory */ > +#define L_ESRCH 3 /* No such process */ > +#define L_EINTR 4 /* Interrupted system call */ > +#define L_EIO 5 /* I/O error */ > +#define L_ENXIO 6 /* No such device or address */ > +#define L_E2BIG 7 /* Argument list too long */ > +#define L_ENOEXEC 8 /* Exec format error */ > +#define L_EBADF 9 /* Bad file number */ > +#define L_ECHILD 10 /* No child processes */ > +#define L_EAGAIN 11 /* Try again */ > +#define L_ENOMEM 12 /* Out of memory */ > +#define L_EACCES 13 /* Permission denied */ > +#define L_EFAULT 14 /* Bad address */ > +#define L_ENOTBLK 15 /* Block device required */ > +#define L_EBUSY 16 /* Device or resource busy */ > +#define L_EEXIST 17 /* File exists */ > +#define L_EXDEV 18 /* Cross-device link */ > +#define L_ENODEV 19 /* No such device */ > +#define L_ENOTDIR 20 /* Not a directory */ > +#define L_EISDIR 21 /* Is a directory */ > +#define L_EINVAL 22 /* Invalid argument */ > +#define L_ENFILE 23 /* File table overflow */ > +#define L_EMFILE 24 /* Too many open files */ > +#define L_ENOTTY 25 /* Not a typewriter */ > +#define L_ETXTBSY 26 /* Text file busy */ > +#define L_EFBIG 27 /* File too large */ > +#define L_ENOSPC 28 /* No space left on device */ > +#define L_ESPIPE 29 /* Illegal seek */ > +#define L_EROFS 30 /* Read-only file system */ > +#define L_EMLINK 31 /* Too many links */ > +#define L_EPIPE 32 /* Broken pipe */ > +#define L_EDOM 33 /* Math argument out of domain of func */ > +#define L_ERANGE 34 /* Math result not representable */ > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > +#define L_ENAMETOOLONG 36 /* File name too long */ > +#define L_ENOLCK 37 /* No record locks available */ > +#define L_ENOSYS 38 /* Function not implemented */ > +#define L_ENOTEMPTY 39 /* Directory not empty */ > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > +#define L_ENOMSG 42 /* No message of desired type */ > +#define L_EIDRM 43 /* Identifier removed */ > +#define L_ECHRNG 44 /* Channel number out of range */ > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > +#define L_EL3HLT 46 /* Level 3 halted */ > +#define L_EL3RST 47 /* Level 3 reset */ > +#define L_ELNRNG 48 /* Link number out of range */ > +#define L_EUNATCH 49 /* Protocol driver not attached */ > +#define L_ENOCSI 50 /* No CSI structure available */ > +#define L_EL2HLT 51 /* Level 2 halted */ > +#define L_EBADE 52 /* Invalid exchange */ > +#define L_EBADR 53 /* Invalid request descriptor */ > +#define L_EXFULL 54 /* Exchange full */ > +#define L_ENOANO 55 /* No anode */ > +#define L_EBADRQC 56 /* Invalid request code */ > +#define L_EBADSLT 57 /* Invalid slot */ > +#define L_EBFONT 58 /* Bad font file format */ > +#define L_ENOSTR 59 /* Device not a stream */ > +#define L_ENODATA 61 /* No data available */ > +#define L_ETIME 62 /* Timer expired */ > +#define L_ENOSR 63 /* Out of streams resources */ > +#define L_ENONET 64 /* Machine is not on the network */ > +#define L_ENOPKG 65 /* Package not installed */ > +#define L_EREMOTE 66 /* Object is remote */ > +#define L_ENOLINK 67 /* Link has been severed */ > +#define L_EADV 68 /* Advertise error */ > +#define L_ESRMNT 69 /* Srmount error */ > +#define L_ECOMM 70 /* Communication error on send */ > +#define L_EPROTO 71 /* Protocol error */ > +#define L_EMULTIHOP 72 /* Multihop attempted */ > +#define L_EDOTDOT 73 /* RFS specific error */ > +#define L_EBADMSG 74 /* Not a data message */ > +#define L_EOVERFLOW 75 /* Value too large for defined data type */ > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > +#define L_EBADFD 77 /* File descriptor in bad state */ > +#define L_EREMCHG 78 /* Remote address changed */ > +#define L_ELIBACC 79 /* Can not access a needed shared library */ > +#define L_ELIBBAD 80 /* Accessing a corrupted shared library */ > +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > +#define L_ELIBMAX 82 /* Attempting to link in too many shared libs */ > +#define L_ELIBEXEC 83 /* Cannot exec a shared library directly */ > +#define L_EILSEQ 84 /* Illegal byte sequence */ > +#define L_ERESTART 85 /* Interrupted system call should be restarted */ > +#define L_ESTRPIPE 86 /* Streams pipe error */ > +#define L_EUSERS 87 /* Too many users */ > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > +#define L_EDESTADDRREQ 89 /* Destination address required */ > +#define L_EMSGSIZE 90 /* Message too long */ > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ > +#define L_EPFNOSUPPORT 96 /* Protocol family not supported */ > +#define L_EAFNOSUPPORT 97 /* Address family not supported by protocol */ > +#define L_EADDRINUSE 98 /* Address already in use */ > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > +#define L_ENETDOWN 100 /* Network is down */ > +#define L_ENETUNREACH 101 /* Network is unreachable */ > +#define L_ENETRESET 102 /* Network dropped connection because of reset */ > +#define L_ECONNABORTED 103 /* Software caused connection abort */ > +#define L_ECONNRESET 104 /* Connection reset by peer */ > +#define L_ENOBUFS 105 /* No buffer space available */ > +#define L_EISCONN 106 /* Transport endpoint is already connected */ > +#define L_ENOTCONN 107 /* Transport endpoint is not connected */ > +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ > +#define L_ETOOMANYREFS 109 /* Too many references: cannot splice */ > +#define L_ETIMEDOUT 110 /* Connection timed out */ > +#define L_ECONNREFUSED 111 /* Connection refused */ > +#define L_EHOSTDOWN 112 /* Host is down */ > +#define L_EHOSTUNREACH 113 /* No route to host */ > +#define L_EALREADY 114 /* Operation already in progress */ > +#define L_EINPROGRESS 115 /* Operation now in progress */ > +#define L_ESTALE 116 /* Stale NFS file handle */ > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > +#define L_EISNAM 120 /* Is a named type file */ > +#define L_EREMOTEIO 121 /* Remote I/O error */ > +#define L_EDQUOT 122 /* Quota exceeded */ > +#define L_ENOMEDIUM 123 /* No medium found */ > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > +#define L_ECANCELED 125 /* Operation Canceled */ > +#define L_ENOKEY 126 /* Required key not available */ > +#define L_EKEYEXPIRED 127 /* Key has expired */ > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > +#define L_EOWNERDEAD 130 /* Owner died */ > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > + > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > index 55ea4b7883..4c75876e19 100644 > --- a/hw/9pfs/9p-local.h > +++ b/hw/9pfs/9p-local.h > @@ -31,6 +31,10 @@ static inline bool local_is_mapped_file_metadata(FsContext *fs_ctx, > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > mode_t mode); > +#ifndef CONFIG_WIN32 > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > +#else > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > +#endif > Hrm... Returning a DIR * instead of a file descriptor looks like this is a completely different function with different users, but you're keeping the same name. What's happening here ? > #endif > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > index 97e681e167..6eadb38e1d 100644 > --- a/hw/9pfs/9p-util.h > +++ b/hw/9pfs/9p-util.h > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > errno = serrno; > } > > +#ifndef CONFIG_WIN32 > static inline int openat_dir(int dirfd, const char *name) > { > return openat(dirfd, name, > @@ -89,7 +90,9 @@ again: > errno = serrno; > return fd; > } > +#endif /* !CONFIG_WIN32 */ > > +#ifndef CONFIG_WIN32 > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > void *value, size_t size); > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename, > char *list, size_t size); > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > const char *name); > +#else > + > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size); > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size, > + int flags); > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > + char *list, size_t size); > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > + const char *name); > + > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > + > +static inline char *merge_fs_path(const char *path1, const char *path2) > +{ > + char *full_fs_path; > + size_t full_fs_path_len; > + > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > + full_fs_path = g_malloc(full_fs_path_len); > > + strcpy(full_fs_path, path1); > + strcat(full_fs_path, "\\"); > + strcat(full_fs_path, path2); > + > + return full_fs_path; > +} > + > +#endif /* !CONFIG_WIN32 */ > + > +#ifndef CONFIG_WIN32 > /* > * Darwin has d_seekoff, which appears to function similarly to d_off. > * However, it does not appear to be supported on all file systems, > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > return dent->d_off; > #endif > } > +#endif /* !CONFIG_WIN32 */ > > /** > * qemu_dirent_dup() - Duplicate directory entry @dent. > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct dirent *dent) > #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > #endif > + > +#ifndef CONFIG_WIN32 > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > +#else > +int qemu_mknodat(const char *dirname, const char *filename, > + mode_t mode, dev_t dev); Same remark as local_opendir_nofollow(). Especially, this clearly deviates from the mknodat(2) semantics : dirfd is supposed to be a handle on an existing directory, while dirname cannot provide such a guarantee. > +#endif > > #endif > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > index 994f952600..87e8eac840 100644 > --- a/hw/9pfs/9p.h > +++ b/hw/9pfs/9p.h > @@ -3,13 +3,36 @@ > > #include <dirent.h> > #include <utime.h> > +#ifndef CONFIG_WIN32 > #include <sys/resource.h> > +#endif > #include "fsdev/file-op-9p.h" > #include "fsdev/9p-iov-marshal.h" > #include "qemu/thread.h" > #include "qemu/coroutine.h" > #include "qemu/qht.h" > > +#ifdef CONFIG_WIN32 > + > +#define O_NOCTTY 0 > +#define O_NDELAY 0 > +#define O_NONBLOCK 0 > +#define O_DSYNC 0 > +#define O_DIRECT 0 > +#define O_NOFOLLOW 0 > +#define O_NOATIME 0 > +#define O_SYNC 0 > +#define O_ASYNC 0 > +#define O_DIRECTORY 02000000 > + > +#define FASYNC 0 > + > +#define AT_REMOVEDIR 1 > + > +#define NAME_MAX 260 > + > +#endif > + > enum { > P9_TLERROR = 6, > P9_RLERROR, > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > new file mode 100644 > index 0000000000..aab7c9f7b5 > --- /dev/null > +++ b/hw/9pfs/9p-local-win32.c > @@ -0,0 +1,1242 @@ > +/* > + * 9p Windows callback > + * > + * Copyright (c) 2022 Wind River Systems, Inc. > + * > + * Based on hw/9pfs/9p-local.c > + * > + * This work is licensed under the terms of the GNU GPL, version 2. See > + * the COPYING file in the top-level directory. > + */ > + > +/* > + * Not so fast! You might want to read the 9p developer docs first: > + * https://wiki.qemu.org/Documentation/9p > + */ > + > +#include "qemu/osdep.h" > +#include <windows.h> > +#include <dirent.h> > +#include "9p.h" > +#include "9p-local.h" > +#include "9p-xattr.h" > +#include "9p-util.h" > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > +#include "qapi/error.h" > +#include "qemu/cutils.h" > +#include "qemu/error-report.h" > +#include "qemu/option.h" > +#include <libgen.h> > + > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > + int flags, mode_t mode) > +{ > + char *full_file_name; > + int fd; > + > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > + fd = open(full_file_name, flags | _O_BINARY, mode); Hmm... in order to fix CVE-2016-9602, we had to ban path based syscalls because a malicious guest could easily escape the file system with a symlink attack. So now we basically split all paths we get from the client so that we only call *at() syscalls with a single path element and a NOFOLLOW flag. This function seems to be doing the very opposite, which is clearly a no go. > + g_free(full_file_name); > + > + return fd; > +} > + > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > +{ > + char *full_file_name; > + DIR *dir; > + > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > + dir = opendir(full_file_name); Same remark here. > + g_free(full_file_name); > + return dir; > +} > + > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > + mode_t mode) > +{ > + int fd; > + > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { Why special casing '/' ? > + /* Windows does not allow call open() for a directory */ > + fd = -1; This -1 will ultimately cause a Rlerror message to be returned to the client : an errno should be set here... but anyway, this isn't the way to do it : you should just call open() and let windows set the errno. Now, the major problem is that we really need to be able to open directories because of CVE-2016-9602. Isn't it possible to achieve it in some other way, e.g. opendir() and dirfd() ? > + } else { > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); Does this honor the NOFOLLOW semantics ? It seems not...which is again a no go. You cannot blindly assume how the underlying file system handles symlinks. > + } > + > + return fd; > +} > + > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > +{ > + return opendir_with_ctx(fs_ctx, path); > +} > + > +static FILE *local_fopenat(const char *dirname, const char *name, > + const char *mode) > +{ > + char *full_file_name; > + char modestr[3] = {0}; > + FILE *fp; > + > + /* > + * only supports two modes > + */ > + if (mode[0] == 'r') { > + modestr[0] = 'r'; > + } else if (mode[0] == 'w') { > + modestr[0] = 'w'; > + } else { > + return NULL; > + } > + /* Windows host needs 'b' flag */ > + modestr[1] = 'b'; > + > + full_file_name = merge_fs_path(dirname, name); > + fp = fopen(full_file_name, modestr); > + g_free(full_file_name); > + > + return fp; > +} > + > +static void local_mapped_file_attr(const char *dirpath, const char *name, > + struct stat *stbuf) > +{ > + FILE *fp; > + char buf[ATTR_MAX]; > + char *full_file_name; > + > + if (strcmp(name, ".") != 0) { > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > + fp = local_fopenat(full_file_name, name, "r"); > + g_free(full_file_name); > + } else { > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > + } > + if (!fp) { > + return; > + } > + > + memset(buf, 0, ATTR_MAX); > + while (fgets(buf, ATTR_MAX, fp)) { > + if (!strncmp(buf, "virtfs.uid", 10)) { > + stbuf->st_uid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > + stbuf->st_gid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > + stbuf->st_mode = (stbuf->st_mode & ~0777); > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > + stbuf->st_rdev = atoi(buf + 12); > + } > + memset(buf, 0, ATTR_MAX); > + } > + fclose(fp); > +} > + > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) > +{ > + int err = -1; > + char *full_dir_name, *full_file_name; > + char *dirpath = g_path_get_dirname(fs_path->data); > + char *name = g_path_get_basename(fs_path->data); > + > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > + full_file_name = merge_fs_path(full_dir_name, name); > + err = stat(full_file_name, stbuf); > + > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > + /* > + * Hard code for root directory on Windows host. > + * This will root directory have a special inode number, > + * then guest OS can detect it is a special directory. > + */ > + stbuf->st_ino = 2; > + } > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + /* Actual credentials are part of extended attrs */ > + uid_t tmp_uid; > + gid_t tmp_gid; > + mode_t tmp_mode; > + dev_t tmp_dev; > + > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > + &tmp_uid, sizeof(uid_t)) > 0) { > + stbuf->st_uid = le32_to_cpu(tmp_uid); > + } > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > + &tmp_gid, sizeof(gid_t)) > 0) { > + stbuf->st_gid = le32_to_cpu(tmp_gid); > + } > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > + &tmp_mode, sizeof(mode_t)) > 0) { > + stbuf->st_mode = (stbuf->st_mode & ~0777); > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > + } > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > + &tmp_dev, sizeof(dev_t)) > 0) { > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > + } > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + local_mapped_file_attr(full_dir_name, name, stbuf); > + } > + > + g_free(full_file_name); > + g_free(full_dir_name); > + > + if (err) { > + goto err_out; > + } > + > +err_out: > + g_free(name); > + g_free(dirpath); > + return err; > +} > + > +static int local_set_mapped_file_attrat(const char *dirname, const char *name, > + FsCred *credp) > +{ > + FILE *fp; > + int ret; > + char buf[ATTR_MAX]; > + int uid = -1, gid = -1, mode = -1, rdev = -1; > + bool is_root = !strcmp(name, "."); > + char *full_dir_name; > + > + if (is_root) { > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > + if (!fp) { > + if (errno == ENOENT) { > + goto update_map_file; > + } else { > + return -1; > + } > + } > + } else { > + /* > + * mapped-file: > + * <sub_file> attribute stored to: > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > + */ > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > + ret = mkdir(full_dir_name); > + > + if (ret < 0 && errno != EEXIST) { > + g_free(full_dir_name); > + return -1; > + } > + > + fp = local_fopenat(full_dir_name, name, "r"); > + if (!fp) { > + if (errno == ENOENT) { > + goto update_map_file; > + } else { > + g_free(full_dir_name); > + return -1; > + } > + } > + } > + > + memset(buf, 0, ATTR_MAX); > + while (fgets(buf, ATTR_MAX, fp)) { > + if (!strncmp(buf, "virtfs.uid", 10)) { > + uid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > + gid = atoi(buf + 11); > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > + mode = atoi(buf + 12); > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > + rdev = atoi(buf + 12); > + } > + memset(buf, 0, ATTR_MAX); > + } > + fclose(fp); > + > +update_map_file: > + if (is_root) { > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > + } else { > + fp = local_fopenat(full_dir_name, name, "w"); > + g_free(full_dir_name); > + } > + if (!fp) { > + return -1; > + } > + > + if (credp->fc_uid != -1) { > + uid = credp->fc_uid; > + } > + if (credp->fc_gid != -1) { > + gid = credp->fc_gid; > + } > + if (credp->fc_mode != (mode_t)-1) { > + mode = credp->fc_mode; > + } > + if (credp->fc_rdev != -1) { > + rdev = credp->fc_rdev; > + } > + > + if (uid != -1) { > + fprintf(fp, "virtfs.uid=%d\n", uid); > + } > + if (gid != -1) { > + fprintf(fp, "virtfs.gid=%d\n", gid); > + } > + if (mode != -1) { > + fprintf(fp, "virtfs.mode=%d\n", mode); > + } > + if (rdev != -1) { > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > + } > + fclose(fp); > + > + return 0; > +} > + > +static int local_set_xattrat(const char *dirname, const char *path, > + FsCred *credp) > +{ > + int err; > + > + if (credp->fc_uid != -1) { > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > + &tmp_uid, sizeof(uid_t), 0); > + if (err) { > + return err; > + } > + } > + if (credp->fc_gid != -1) { > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > + &tmp_gid, sizeof(gid_t), 0); > + if (err) { > + return err; > + } > + } > + if (credp->fc_mode != (mode_t)-1) { > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > + &tmp_mode, sizeof(mode_t), 0); > + if (err) { > + return err; > + } > + } > + if (credp->fc_rdev != -1) { > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > + &tmp_rdev, sizeof(dev_t), 0); > + if (err) { > + return err; > + } > + } > + return 0; > +} > + > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > + char *buf, size_t bufsz) > +{ > + return -1; > +} > + > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + return close(fs->fd); > +} > + > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + return closedir(fs->dir.stream); > +} > + > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > + int flags, V9fsFidOpenState *fs) > +{ > + int fd; > + > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > + if (fd == -1) { > + return -1; > + } > + fs->fd = fd; > + return fs->fd; > +} > + > +static int local_opendir(FsContext *ctx, > + V9fsPath *fs_path, V9fsFidOpenState *fs) > +{ > + DIR *stream; > + char *full_file_name; > + > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > + stream = opendir(full_file_name); > + g_free(full_file_name); > + > + if (!stream) { > + return -1;local_opendir_nofollowlocal_opendir_nofollow > + } > + > + fs->dir.stream = stream; > + return 0; > +} > + > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + rewinddir(fs->dir.stream); > +} > + > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > +{ > + return telldir(fs->dir.stream); > +} > + > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > +{local_opendir_nofollow > + struct dirent *entry; > + > +again: > + entry = readdir(fs->dir.stream); > + if (!entry) { > + return NULL; > + } > + > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > + /* skip the meta data */ > + goto again; > + } > + } > + > + return entry; > +} > + > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) > +{ > + off_t count; > + struct dirent *findentry; > + struct dirent *entry; > + size_t namelen[3] = {0}; > + off_t direntoff[3] = {-1, -1, -1}; > + char *d_name[3]; > + int i; > + > + /* > + * MinGW's seekdir() requires directory is not modified. If guest OS is > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", > + * then MinGW's seekdir() will seek to a wrong offset. > + * > + * This function saves some old offset directory entry name, > + * and lookup current entry again, and compare the offset. > + * > + * If new offset is less than old offset, that means someone is deleting > + * files in the directory, thus we need to seek offset backward. > + * > + * If new offset is larger than old offset, that means someone is creating > + * files in the directory, thus we need to seek offset forward. > + */ > + > + direntoff[0] = telldir(fs->dir.stream); > + > + /* do nothing if current offset is 0 or EOF */ > + if (direntoff[0] == 0 || direntoff[0] < 0) { > + seekdir(fs->dir.stream, off); > + return ; > + } > + > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > + d_name[1] = d_name[0] + sizeof(entry->d_name); > + d_name[2] = d_name[1] + sizeof(entry->d_name); > + > + /* save 3 nearest entries and offsets */ > + for (i = 0; i < 3; i++) { > + entry = &fs->dir.stream->dd_dir; > + > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > + namelen[i] = strlen(d_name[i]) + 1; > + > + direntoff[i] = telldir(fs->dir.stream); > + > + entry = readdir(fs->dir.stream); > + if (entry == NULL) { > + break; > + } > + } > + > + /* lookup saved entries again */ > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > + rewinddir(fs->dir.stream); > + count = 0; > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > + count++; > + > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > + if (count + i == direntoff[i]) { > + seekdir(fs->dir.stream, off); > + goto out; > + } else if (count + i < direntoff[i]) { > + off = off - (direntoff[i] - count) - i; > + if (off <= 0) { > + off = 0; > + } > + seekdir(fs->dir.stream, off); > + goto out; > + } else { > + off = off + (count - direntoff[i]) - i; > + seekdir(fs->dir.stream, off); > + goto out; > + } > + }local_opendir_nofollow > + } > + } > + /* can not get anything, seek backward */ > + off = off - 1; > + > + seekdir(fs->dir.stream, off); > +out: > + g_free(d_name[0]); > + return ; > +} > + > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > + const struct iovec *iov, > + int iovcnt, off_t offset) > +{ > +#ifdef CONFIG_PREADV > + return preadv(fs->fd, iov, iovcnt, offset); > +#else > + int err = lseek(fs->fd, offset, SEEK_SET); > + if (err == -1) { > + return err; > + } else { > + return readv(fs->fd, iov, iovcnt); > + } > +#endif > +} > + > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > + const struct iovec *iov, > + int iovcnt, off_t offset) > +{ > + ssize_t ret; > +#ifdef CONFIG_PREADV > + ret = pwritev(fs->fd, iov, iovcnt, offset); > +#else > + int err = lseek(fs->fd, offset, SEEK_SET); > + if (err == -1) { > + return err; > + } else { > + ret = writev(fs->fd, iov, iovcnt); > + } > +#endif > +#ifdef CONFIG_SYNC_FILE_RANGE > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > + /* > + * Initiate a writeback. This is not a data integrity sync. > + * We want to ensure that we don't leave dirty pages in the cache > + * after write when writeout=immediate is sepcified. > + */ > + sync_file_range(fs->fd, offset, ret, > + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); > + } > +#endif > + return ret; > +} > + > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > +{ > + char *dirpath = g_path_get_dirname(fs_path->data); > + char *name = g_path_get_basename(fs_path->data); > + int ret = -1; > + char *full_file_name = NULL; > + DIR *dir; > + dir = local_opendir_nofollow(fs_ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + ret = local_set_xattrat(full_file_name, name, credp); > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > + fs_ctx->export_flags & V9FS_SM_NONE) { > + ret = -1; > + errno = ENOTSUP; > + } > + closedir(dir); > + > +out: > + if (full_file_name != NULL) { > + g_free(full_file_name); > + } > + > + g_free(dirpath); > + g_free(name); > + return ret; > +} > + > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > + const char *name, FsCred *credp) > +{ > + return -1; > +} > + > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > + const char *name, FsCred *credp) > +{ > + int err = -1; > + char *full_file_name; > + char *full_dir_name; > + DIR *dir; > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(fs_ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > + if (dir == NULL) { > + return -1; > + } > + closedir(dir); > + > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > + full_file_name = merge_fs_path(full_dir_name, name); > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + err = mkdir(full_file_name); > + if (err == -1) { > + goto out; > + } > + credp->fc_mode = credp->fc_mode; > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + err = local_set_xattrat(full_dir_name, name, credp); > + } else { > + err = local_set_mapped_file_attrat(full_dir_name, name, credp); > + } > + if (err == -1) { > + rmdir(full_file_name); > + } > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > + fs_ctx->export_flags & V9FS_SM_NONE) { > + err = mkdir(full_file_name); > + if (err == -1) { > + goto out; > + } > + /* Windows does not support chmod, do nothing here */ > + } > + > + goto out; > + > +out: > + g_free(full_dir_name); > + g_free(full_file_name); > + return err; > +} > + > +static int local_fstat(FsContext *fs_ctx, int fid_type, > + V9fsFidOpenState *fs, struct stat *stbuf) > +{ > + > + int err, fd; > + char filename[NAME_MAX]; > + char *dirpath; > + char *name; > + HANDLE hFile; > + DWORD dwRet; > + > + if (fid_type == P9_FID_DIR) { > + /* Windows does not support open directory */ > + return -1; > + } else { > + fd = fs->fd; > + } > + > + err = fstat(fd, stbuf); > + if (err) { > + return err; > + } > + > + /* get real file name by fd */ > + hFile = (HANDLE)_get_osfhandle(fd); > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); > + > + if (dwRet >= NAME_MAX) { > + return -1; > + } > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > + memmove(filename, filename + 4, NAME_MAX - 4); > + > + dirpath = g_path_get_dirname(filename); > + name = g_path_get_basename(filename); > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + /* Actual credentials are part of extended attrs */ > + uid_t tmp_uid; > + gid_t tmp_gid; > + mode_t tmp_mode; > + dev_t tmp_dev; > + > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > + &tmp_uid, sizeof(uid_t)) > 0) { > + stbuf->st_uid = le32_to_cpu(tmp_uid); > + } > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > + &tmp_gid, sizeof(gid_t)) > 0) { > + stbuf->st_gid = le32_to_cpu(tmp_gid); > + } > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > + &tmp_mode, sizeof(mode_t)) > 0) { > + stbuf->st_mode = (stbuf->st_mode & ~0777); > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > + } > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > + &tmp_dev, sizeof(dev_t)) > 0) { > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > + } > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + errno = EOPNOTSUPP; > + g_free(dirpath); > + g_free(name); > + return -1; > + } > + > + g_free(dirpath); > + g_free(name); > + > + return err; > +} > + > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, > + int flags, FsCred *credp, V9fsFidOpenState *fs) > +{ > + int fd = -1; > + int err = -1; > + char *full_file_name; > + > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(fs_ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + full_file_name = merge_fs_path(dir_path->data, name); > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > + g_free(full_file_name); > + > + err = fd; > + fs->fd = fd; > + goto out; > + > + close_preserve_errno(fd); > +out: > + return err; > +} > + > + > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > + V9fsPath *dir_path, const char *name, FsCred *credp) > +{ > + return -1; Why ? This would require a detailed comment and also to set errno. > +} > + > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > + V9fsPath *dirpath, const char *name) > +{ > + return -1; Same remark. Before re-posting, you really need to come up with a detailed plan around symlinks. Cheers, -- Greg > +} > + > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > +{ > + int fd, ret; > + > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > + if (fd == -1) { > + return -1; > + } > + ret = ftruncate(fd, size); > + close_preserve_errno(fd); > + return ret; > +} > + > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > +{ > + char *full_file_name; > + char *dirpath = g_path_get_dirname(fs_path->data); > + char *name = g_path_get_basename(fs_path->data); > + int ret = -1; > + DIR *dir; > + > + dir = local_opendir_nofollow(fs_ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > + > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > + /* Windows does not support chown() */ > + ret = -1; > + errno = ENOTSUP; > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > + ret = local_set_xattrat(full_file_name, name, credp); > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > + } > + g_free(full_file_name); > + closedir(dir); > +out: > + g_free(name); > + g_free(dirpath); > + return ret; > +} > + > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > + const struct timespec *buf) > +{ > + struct utimbuf tm; > + char *full_file_name; > + int err; > + > + tm.actime = buf[0].tv_sec; > + tm.modtime = buf[1].tv_sec; > + > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > + err = utime(full_file_name, &tm); > + g_free(full_file_name); > + > + return err; > +} > + > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > + const char *name, int flags) > +{ > + int ret; > + char *full_file_name; > + char *full_dir_name; > + > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > + full_file_name = merge_fs_path(full_dir_name, name); > + > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + char *full_meta_dir_name; > + char *full_meta_file_name; > + > + /* > + * We need to remove the metadata as well: > + * - the metadata directory if we're removing a directory > + * - the metadata file in the parent's metadata directory > + * > + * If any of these are missing (ie, ENOENT) then we're probably > + * trying to remove something that wasn't created in mapped-file > + * mode. We just ignore the error. > + */ > + > + if ((flags & AT_REMOVEDIR) != 0) { > + full_meta_dir_name = merge_fs_path(full_file_name, VIRTFS_META_DIR); > + ret = rmdir(full_meta_dir_name); > + g_free(full_meta_dir_name); > + > + if (ret < 0 && errno != ENOENT) { > + g_free(full_file_name); > + g_free(full_dir_name); > + return -1; > + } > + } > + > + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); > + ret = remove(full_meta_file_name); > + g_free(full_meta_dir_name); > + g_free(full_meta_file_name); > + > + if (ret < 0 && errno != ENOENT) { > + g_free(full_dir_name); > + g_free(full_file_name); > + > + return -1; > + } > + } > + > + if ((flags & AT_REMOVEDIR) != 0) { > + ret = rmdir(full_file_name); > + } else { > + ret = remove(full_file_name); > + } > + > + g_free(full_dir_name); > + g_free(full_file_name); > + > + return ret; > +} > + > +static int local_remove(FsContext *ctx, const char *path) > +{ > + int err; > + DIR *stream; > + char *full_file_name; > + char *dirpath = g_path_get_dirname(path); > + char *name = g_path_get_basename(path); > + int flags = 0; > + > + full_file_name = merge_fs_path(ctx->fs_root, path); > + stream = opendir(full_file_name); > + if (stream != NULL) { > + closedir(stream); > + flags |= AT_REMOVEDIR; > + } > + err = local_unlinkat_common(ctx, dirpath, name, flags); > + > + g_free(name); > + g_free(dirpath); > + g_free(full_file_name); > + return err; > +} > + > +static int local_fsync(FsContext *ctx, int fid_type, > + V9fsFidOpenState *fs, int datasync) > +{ > + if (fid_type != P9_FID_DIR) { > + return _commit(fs->fd); > + } > + return 0; > +} > + > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) > +{ > + int ret; > + ret = qemu_statfs(s->fs_root, stbuf); > + if (ret == 0) { > + /* use context address as fsid */ > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > + } > + > + return ret; > +} > + > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > + const char *name, void *value, size_t size) > +{ > + return -1; > +} > + > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > + void *value, size_t size) > +{ > + return -1; > +} > + > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, > + void *value, size_t size, int flags) > +{ > + return -1; > +} > + > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > + const char *name) > +{ > + return -1; > +} > + > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > + const char *name, V9fsPath *target) > +{ > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + if (dir_path) { > + if (!strcmp(name, ".")) { > + /* "." relative to "foo/bar" is "foo/bar" */ > + v9fs_path_copy(target, dir_path); > + } else if (!strcmp(name, "..")) { > + if (!strcmp(dir_path->data, ".")) { > + /* ".." relative to the root is "." */ > + v9fs_path_sprintf(target, "."); > + } else { > + char *tmp = g_path_get_dirname(dir_path->data); > + /* > + * Symbolic links are resolved by the client. We can assume > + * that ".." relative to "foo/bar" is equivalent to "foo" > + */ > + v9fs_path_sprintf(target, "%s", tmp); > + g_free(tmp); > + } > + } else { > + assert(!strchr(name, '/')); > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > + } > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > + !strcmp(name, "..")) { > + /* This is the root fid */ > + v9fs_path_sprintf(target, "."); > + } else { > + assert(!strchr(name, '/')); > + v9fs_path_sprintf(target, "./%s", name); > + } > + return 0; > +} > + > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > + const char *old_name, V9fsPath *newdir, > + const char *new_name) > +{ > + return -1; > +} > + > +static int local_rename(FsContext *ctx, const char *oldpath, > + const char *newpath) > +{ > + int err; > + > + char *full_old_name; > + char *full_new_name; > + > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > + > + err = rename(full_old_name, full_new_name); > + > + g_free(full_old_name); > + g_free(full_new_name); > + > + return err; > +} > + > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > + const char *name, int flags) > +{ > + int ret; > + > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > + local_is_mapped_file_metadata(ctx, name)) { > + errno = EINVAL; > + return -1; > + } > + > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > + > + return ret; > +} > + > +static int check_filesystem_type(char *fs_root, int export_flags) > +{ > + HANDLE hFile; > + wchar_t FsName[MAX_PATH + 1] = {0}; > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > + > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > + return 0; > + } > + > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > + if (hFile == INVALID_HANDLE_VALUE) { > + return -1; > + } > + > + /* Get file system type name */ > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > + FsName, MAX_PATH + 1) == 0) { > + CloseHandle(hFile); > + return -1; > + } > + CloseHandle(hFile); > + > + if (wcscmp(FsName, NtfsName) != 0) { > + return -1; > + } > + > + return 0; > +} > + > +static int local_init(FsContext *ctx, Error **errp) > +{ > + LocalData *data = g_malloc(sizeof(*data)); > + > + struct stat StatBuf; > + > + if (stat(ctx->fs_root, &StatBuf) != 0) { > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > + goto err; > + } > + > + /* > + * security_model=mapped(-xattr) requires a fileystem on Windows that > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > + * probably most popular on Windows. It is fair enough to assume > + * Windows users to use NTFS for the mapped security model. > + */ > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > + "security_model is mapped or mapped-xattr"); > + goto err; > + } > + > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > + ctx->xops = passthrough_xattr_ops; > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > + ctx->xops = mapped_xattr_ops; > + } else if (ctx->export_flags & V9FS_SM_NONE) { > + ctx->xops = none_xattr_ops; > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > + /* > + * xattr operation for mapped-file and passthrough > + * remain same. > + */ > + ctx->xops = passthrough_xattr_ops; > + } > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > + > + ctx->private = data; > + return 0; > + > +err: > + g_free(data); > + return -1; > +} > + > +static void local_cleanup(FsContext *ctx) > +{ > + LocalData *data = ctx->private; > + > + if (!data) { > + return; > + } > + > + close(data->mountfd); > + g_free(data); > +} > + > +static void error_append_security_model_hint(Error *const *errp) > +{ > + error_append_hint(errp, "Valid options are: security_model=" > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > +} > + > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) > +{ > + ERRP_GUARD(); > + const char *sec_model = qemu_opt_get(opts, "security_model"); > + const char *path = qemu_opt_get(opts, "path"); > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > + > + if (!sec_model) { > + error_setg(errp, "security_model property not set"); > + error_append_security_model_hint(errp); > + return -1; > + } > + > + if (!strcmp(sec_model, "passthrough")) { > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > + } else if (!strcmp(sec_model, "mapped") || > + !strcmp(sec_model, "mapped-xattr")) { > + fse->export_flags |= V9FS_SM_MAPPED; > + } else if (!strcmp(sec_model, "none")) { > + fse->export_flags |= V9FS_SM_NONE; > + } else if (!strcmp(sec_model, "mapped-file")) { > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > + } else { > + error_setg(errp, "invalid security_model property '%s'", sec_model); > + error_append_security_model_hint(errp); > + return -1; > + } > + > + if (multidevs) { > + if (!strcmp(multidevs, "remap")) { > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > + fse->export_flags |= V9FS_REMAP_INODES; > + } else if (!strcmp(multidevs, "forbid")) { > + fse->export_flags &= ~V9FS_REMAP_INODES; > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > + } else if (!strcmp(multidevs, "warn")) { > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > + fse->export_flags &= ~V9FS_REMAP_INODES; > + } else { > + error_setg(errp, "invalid multidevs property '%s'", > + multidevs); > + error_append_hint(errp, "Valid options are: multidevs=" > + "[remap|forbid|warn]\n"); > + return -1; > + } > + } > + > + if (!path) { > + error_setg(errp, "path property not set"); > + return -1; > + } > + > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > + error_prepend(errp, "invalid throttle configuration: "); > + return -1; > + } > + > + if (fse->export_flags & V9FS_SM_MAPPED || > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > + fse->fmode = > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > + fse->dmode = > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; > + } else { > + if (qemu_opt_find(opts, "fmode")) { > + error_setg(errp, "fmode is only valid for mapped security modes"); > + return -1; > + } > + if (qemu_opt_find(opts, "dmode")) { > + error_setg(errp, "dmode is only valid for mapped security modes"); > + return -1; > + } > + } > + > + fse->path = g_strdup(path); > + > + return 0; > +} > + > +FileOperations local_ops = { > + .parse_opts = local_parse_opts, > + .init = local_init, > + .cleanup = local_cleanup, > + .lstat = local_lstat, > + .readlink = local_readlink, > + .close = local_close, > + .closedir = local_closedir, > + .open = local_open, > + .opendir = local_opendir, > + .rewinddir = local_rewinddir, > + .telldir = local_telldir, > + .readdir = local_readdir, > + .seekdir = local_seekdir, > + .preadv = local_preadv, > + .pwritev = local_pwritev, > + .chmod = local_chmod, > + .mknod = local_mknod, > + .mkdir = local_mkdir, > + .fstat = local_fstat, > + .open2 = local_open2, > + .symlink = local_symlink, > + .link = local_link, > + .truncate = local_truncate, > + .rename = local_rename, > + .chown = local_chown, > + .utimensat = local_utimensat, > + .remove = local_remove, > + .fsync = local_fsync, > + .statfs = local_statfs, > + .lgetxattr = local_lgetxattr, > + .llistxattr = local_llistxattr, > + .lsetxattr = local_lsetxattr, > + .lremovexattr = local_lremovexattr, > + .name_to_path = local_name_to_path, > + .renameat = local_renameat, > + .unlinkat = local_unlinkat, > +}; > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > new file mode 100644 > index 0000000000..d9b35e7425 > --- /dev/null > +++ b/hw/9pfs/9p-util-win32.c > @@ -0,0 +1,303 @@ > +/* > + * 9p utilities (Windows Implementation) > + * > + * Copyright (c) 2022 Wind River Systems, Inc. > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qemu/error-report.h" > +#include "9p.h" > +#include "9p-util.h" > +#include "9p-linux-errno.h" > +#include <windows.h> > +#include <dirent.h> > + > +#ifndef V9FS_MAGIC > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > +#endif > + > +static int build_ads_name(char *namebuf, size_t namebuflen, > + const char *dirname, const char *filename, > + const char *ads_name) > +{ > + size_t totalsize; > + > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; > + if (totalsize > namebuflen) { > + return -1; > + } > + > + /* > + * NTFS ADS (Alternate Data Streams) name format: > + * filename:ads_name > + * e.g. > + * d:\1.txt:my_ads_name > + */ > + strcpy(namebuf, dirname); > + strcat(namebuf, "\\"); > + strcat(namebuf, filename); > + strcat(namebuf, ":"); > + strcat(namebuf, ads_name); > + > + return 0; > +} > + > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > + char *fulladsname) > +{ > + char *p1, *p2; > + > + /* > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > + * :ads_name:$DATA > + * e.g. > + * :my_ads_name:$DATA > + * > + * ADS name from FindNextStreamW() always have ":$DATA" string at the end > + * > + * This function copy ADS name to namebuf. > + */ > + > + p1 = strchr(fulladsname, ':'); > + if (p1 == NULL) { > + return -1; > + } > + > + p2 = strchr(p1 + 1, ':'); > + if (p2 == NULL) { > + return -1; > + } > + > + /* skip empty ads name */ > + if (p2 - p1 == 1) { > + return 0; > + } > + > + if (p2 - p1 + 1 > namebuflen) { > + return -1; > + } > + > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > + namebuf[p2 - p1 - 1] = '\0'; > + > + return p2 - p1; > +} > + > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size) > +{ > + HANDLE hStream; > + char ADSFileName[NAME_MAX + 1] = {0}; > + DWORD dwBytesRead; > + > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > + errno = EIO; > + return -1; > + } > + > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > + if (hStream == INVALID_HANDLE_VALUE && > + GetLastError() == ERROR_FILE_NOT_FOUND) { > + errno = ENODATA; > + return -1; > + } > + > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > + errno = EIO; > + CloseHandle(hStream); > + return -1; > + } > + > + CloseHandle(hStream); > + > + return dwBytesRead; > +} > + > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > + char *list, size_t size) > +{ > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > + char fulladsname[NAME_MAX + 1]; > + char *full_fs_name = merge_fs_path(dirname, filename); > + int ret; > + HANDLE hFind; > + WIN32_FIND_STREAM_DATA fsd; > + BOOL bFindNext; > + char *listptr = list; > + size_t listleftsize = size; > + > + /* > + * ADS emurate function only have WCHAR version, need to covert filename > + * to WCHAR string. > + */ > + > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > + strlen(full_fs_name), WideCharStr, NAME_MAX); > + g_free(full_fs_name); > + if (ret == 0) { > + errno = EIO; > + return -1; > + } > + > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); > + if (hFind == INVALID_HANDLE_VALUE) { > + errno = ENODATA; > + return -1; > + } > + > + do { > + memset(fulladsname, 0, sizeof(fulladsname)); > + > + /* > + * ADS emurate function only have WCHAR version, need to covert > + * cStreamName to utf-8 string. > + */ > + > + ret = WideCharToMultiByte(CP_UTF8, 0, > + fsd.cStreamName, wcslen(fsd.cStreamName) + 1, > + fulladsname, sizeof(fulladsname) - 1, > + NULL, NULL); > + > + if (ret == 0) { > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > + errno = ERANGE; > + } > + CloseHandle(hFind); > + return -1; > + } > + > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > + if (ret < 0) { > + errno = ERANGE; > + CloseHandle(hFind); > + return -1; > + } > + > + listptr = listptr + ret; > + listleftsize = listleftsize - ret; > + > + bFindNext = FindNextStreamW(hFind, &fsd); > + } while (bFindNext); > + > + CloseHandle(hFind); > + > + return size - listleftsize; > +} > + > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > + const char *name) > +{ > + char ADSFileName[NAME_MAX + 1] = {0}; > + > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > + errno = EIO; > + return -1; > + } > + > + if (DeleteFile(ADSFileName) != 0) { > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > + errno = ENODATA; > + return -1; > + } > + } > + > + return 0; > +} > + > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > + const char *name, void *value, size_t size, int flags) > +{ > + HANDLE hStream; > + char ADSFileName[NAME_MAX + 1] = {0}; > + DWORD dwBytesWrite; > + > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > + errno = EIO; > + return -1; > + } > + > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); > + if (hStream == INVALID_HANDLE_VALUE) { > + errno = EIO; > + return -1; > + } > + > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { > + errno = EIO; > + CloseHandle(hStream); > + return -1; > + } > + > + CloseHandle(hStream); > + > + return 0; > +} > + > +int qemu_mknodat(const char *dirname, const char *filename, > + mode_t mode, dev_t dev) > +{ > + errno = ENOTSUP; > + return -1; > +} > + > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > +{ > + HANDLE hFile; > + char RealPath[NAME_MAX + 1]; > + unsigned long SectorsPerCluster; > + unsigned long BytesPerSector; > + unsigned long NumberOfFreeClusters; > + unsigned long TotalNumberOfClusters; > + > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > + if (hFile == INVALID_HANDLE_VALUE) { > + errno = EIO; > + return -1; > + } > + > + /* get real path of root */ > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > + FILE_NAME_NORMALIZED) == 0) { > + errno = EIO; > + CloseHandle(hFile); > + return -1; > + } > + > + CloseHandle(hFile); > + > + /* > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > + * "C:\\123" will be "\\\\?\\C:\\123" > + * Skip first 4 bytes and truncate the string at offset 7, it will get > + * the real root directory like "C:\\", this is parameter GetDiskFreeSpace > + * needed. > + */ > + > + RealPath[7] = '\0'; > + > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, > + &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) { > + errno = EIO; > + return -1; > + } > + > + stbuf->f_type = V9FS_MAGIC; > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > + stbuf->f_files = -1; > + stbuf->f_ffree = -1; > + stbuf->f_namelen = NAME_MAX; > + stbuf->f_frsize = 0; > + stbuf->f_flags = 0; > + > + return 0; > +} > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > index 9ae69dd8db..5623f0e2ef 100644 > --- a/hw/9pfs/9p-xattr.c > +++ b/hw/9pfs/9p-xattr.c > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > char *orig_value, *orig_value_start; > ssize_t xattr_len, parsed_len = 0, attr_len; > char *dirpath, *name; > +#ifdef CONFIG_WIN32 > + char *full_dir_path; > + DIR *dir; > +#else > int dirfd; > +#endif > > /* Get the actual len */ > dirpath = g_path_get_dirname(path); > + > +#ifdef CONFIG_WIN32 > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + return -1; > + } > + closedir(dir); > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + g_free(dirpath); > + > + name = g_path_get_basename(path); > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > + if (xattr_len <= 0) { > + g_free(name); > + g_free(full_dir_path); > + return xattr_len; > + } > + > + /* Now fetch the xattr and find the actual size */ > + orig_value = g_malloc(xattr_len); > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > + xattr_len); > + g_free(name); > + g_free(full_dir_path); > + if (xattr_len < 0) { > + g_free(orig_value); > + return -1; > + } > +#else > dirfd = local_opendir_nofollow(ctx, dirpath); > g_free(dirpath); > if (dirfd == -1) { > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > g_free(orig_value); > return -1; > } > +#endif > > /* store the orig pointer */ > orig_value_start = orig_value; > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > const char *name, void *value, size_t size) > { > +#ifdef CONFIG_WIN32 > + char *dirpath = g_path_get_dirname(path); > + char *filename = g_path_get_basename(path); > + char *full_dir_path; > + DIR *dir; > + ssize_t ret = -1; > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + closedir(dir); > + > + ret = fgetxattrat_nofollow(full_dir_path, filename, > + name, value, size); > + > +out: > + g_free(full_dir_path); > + g_free(dirpath); > + g_free(filename); > + > + return ret; > +#else > char *dirpath = g_path_get_dirname(path); > char *filename = g_path_get_basename(path); > int dirfd; > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > } > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > + > close_preserve_errno(dirfd); > out: > g_free(dirpath); > g_free(filename); > return ret; > +#endif > } > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path, > const char *name, void *value, size_t size, > int flags) > { > +#ifdef CONFIG_WIN32 > + char *dirpath = g_path_get_dirname(path); > + char *filename = g_path_get_basename(path); > + char *full_dir_path; > + DIR *dir; > + ssize_t ret = -1; > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + closedir(dir); > + > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > + value, size, flags); > +out: > + g_free(full_dir_path); > + g_free(dirpath); > + g_free(filename); > + return ret; > + > +#else > char *dirpath = g_path_get_dirname(path); > char *filename = g_path_get_basename(path); > int dirfd; > @@ -210,6 +297,7 @@ out: > g_free(dirpath); > g_free(filename); > return ret; > +#endif > } > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, > @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, > ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, > const char *name) > { > +#ifdef CONFIG_WIN32 > + char *dirpath = g_path_get_dirname(path); > + char *filename = g_path_get_basename(path); > + char *full_dir_path; > + DIR *dir; > + ssize_t ret = -1; > + > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > + > + dir = local_opendir_nofollow(ctx, dirpath); > + if (dir == NULL) { > + goto out; > + } > + closedir(dir); > + > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > + > +out: > + g_free(full_dir_path); > + g_free(dirpath); > + g_free(filename); > + return ret; > + > +#else > char *dirpath = g_path_get_dirname(path); > char *filename = g_path_get_basename(path); > int dirfd; > @@ -237,6 +349,7 @@ out: > g_free(dirpath); > g_free(filename); > return ret; > +#endif > } > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > index 225f31fc31..a04889c1d6 100644 > --- a/hw/9pfs/9p.c > +++ b/hw/9pfs/9p.c > @@ -38,6 +38,10 @@ > #include "migration/blocker.h" > #include "qemu/xxhash.h" > #include <math.h> > +#ifdef CONFIG_WIN32 > +#define UTIME_NOW ((1l << 30) - 1l) > +#define UTIME_OMIT ((1l << 30) - 2l) > +#endif > > int open_fd_hw; > int total_open_fd; > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp) > if (S_ISDIR(stbuf->st_mode)) { > qidp->type |= P9_QID_TYPE_DIR; > } > +#ifndef CONFIG_WIN32 > if (S_ISLNK(stbuf->st_mode)) { > qidp->type |= P9_QID_TYPE_SYMLINK; > } > +#endif > > return 0; > } > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) > ret |= S_IFDIR; > } > > +#ifndef CONFIG_WIN32 > if (mode & P9_STAT_MODE_SYMLINK) { > ret |= S_IFLNK; > } > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) > if (mode & P9_STAT_MODE_NAMED_PIPE) { > ret |= S_IFIFO; > } > +#endif > if (mode & P9_STAT_MODE_DEVICE) { > if (extension->size && extension->data[0] == 'c') { > ret |= S_IFCHR; > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) > ret |= S_IFREG; > } > > +#ifndef CONFIG_WIN32 > if (mode & P9_STAT_MODE_SETUID) { > ret |= S_ISUID; > } > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) > if (mode & P9_STAT_MODE_SETVTX) { > ret |= S_ISVTX; > } > +#endif > > return ret; > } > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > mode |= P9_STAT_MODE_DIR; > } > > +#ifndef CONFIG_WIN32 > if (S_ISLNK(stbuf->st_mode)) { > mode |= P9_STAT_MODE_SYMLINK; > } > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > if (S_ISFIFO(stbuf->st_mode)) { > mode |= P9_STAT_MODE_NAMED_PIPE; > } > +#endif > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > mode |= P9_STAT_MODE_DEVICE; > } > > +#ifndef CONFIG_WIN32 > if (stbuf->st_mode & S_ISUID) { > mode |= P9_STAT_MODE_SETUID; > } > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > if (stbuf->st_mode & S_ISVTX) { > mode |= P9_STAT_MODE_SETVTX; > } > +#endif > > return mode; > } > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, > return err; > } > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > +#ifndef CONFIG_WIN32 > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > +#else > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > + 0, 0); > +#endif > + > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, int32_t blksize) > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > { > +#ifndef CONFIG_WIN32 > return blksize_to_iounit(pdu, stbuf->st_blksize); > +#else > + return blksize_to_iounit(pdu, 0); > +#endif > } > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > v9lstat->st_gid = stbuf->st_gid; > v9lstat->st_rdev = stbuf->st_rdev; > v9lstat->st_size = stbuf->st_size; > + > +#ifdef CONFIG_WIN32 > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > + v9lstat->st_blocks = 0; > + v9lstat->st_atime_sec = stbuf->st_atime; > + v9lstat->st_mtime_sec = stbuf->st_mtime; > + v9lstat->st_ctime_sec = stbuf->st_ctime; > +#else /* !CONFIG_WIN32 */ > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > v9lstat->st_blocks = stbuf->st_blocks; > v9lstat->st_atime_sec = stbuf->st_atime; > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > #endif > +#endif /* CONFIG_WIN32 */ > + > /* Currently we only support BASIC fields in stat */ > v9lstat->st_result_mask = P9_STATS_BASIC; > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > static bool same_stat_id(const struct stat *a, const struct stat *b) > { > +#ifdef CONFIG_WIN32 > + return 0; > +#else > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > +#endif /* CONFIG_WIN32 */ > } > > static void coroutine_fn v9fs_walk(void *opaque) > @@ -2300,7 +2339,11 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu, > count += len; > v9fs_stat_free(&v9stat); > v9fs_path_free(&path); > +#ifndef CONFIG_WIN32 > saved_dir_pos = qemu_dirent_off(dent); > +#else > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > +#endif > } > > v9fs_readdir_unlock(&fidp->fs.dir); > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp, > qid.version = 0; > } > > +#ifdef CONFIG_WIN32 > + /* > + * Windows does not have dent->d_off, get offset by calling telldir() > + * manually. > + */ > + off = v9fs_co_telldir(pdu, fidp); > +#else > off = qemu_dirent_off(dent); > +#endif > v9fs_string_init(&name); > v9fs_string_sprintf(&name, "%s", dent->d_name); > > +#ifdef CONFIG_WIN32 > + /* > + * Windows does not have dent->d_type > + */ > + > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > + &qid, off, > + 0, &name); > +#else > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > len = pdu_marshal(pdu, 11 + count, "Qqbs", > &qid, off, > dent->d_type, &name); > +#endif > > v9fs_string_free(&name); > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > } > > nmode |= perm & 0777; > + > +#ifndef CONFIG_WIN32 > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > makedev(major, minor), nmode, &stbuf); > +#else > + err = -ENOTSUP; > +#endif > + > if (err < 0) { > goto out; > } > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > v9fs_path_copy(&fidp->path, &path); > v9fs_path_unlock(s); > } else if (perm & P9_STAT_MODE_SOCKET) { > +#ifndef CONFIG_WIN32 > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > 0, S_IFSOCK | (perm & 0777), &stbuf); > +#else > + err = -ENOTSUP; > +#endif > if (err < 0) { > goto out; > } > @@ -3600,6 +3671,7 @@ out_nofid: > static void coroutine_fn v9fs_mknod(void *opaque) > { > > +#ifndef CONFIG_WIN32 > int mode; > gid_t gid; > int32_t fid; > @@ -3656,6 +3728,11 @@ out: > out_nofid: > pdu_complete(pdu, err); > v9fs_string_free(&name); > +#else > + V9fsPDU *pdu = opaque; > + > + pdu_complete(pdu, -1); > +#endif > } > > /* > @@ -3928,7 +4005,7 @@ out_nofid: > #if defined(CONFIG_LINUX) > /* Currently, only Linux has XATTR_SIZE_MAX */ > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > -#elif defined(CONFIG_DARWIN) > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > /* > * Darwin doesn't seem to define a maximum xattr size in its user > * space header, so manually configure it across platforms as 64k. > @@ -3945,6 +4022,7 @@ out_nofid: > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > { > +#ifndef CONFIG_WIN32 > int flags, rflags = 0; > int32_t fid; > uint64_t size; > @@ -4006,10 +4084,15 @@ out_put_fid: > out_nofid: > pdu_complete(pdu, err); > v9fs_string_free(&name); > +#else > + V9fsPDU *pdu = opaque; > + pdu_complete(pdu, -1); > +#endif > } > > static void coroutine_fn v9fs_readlink(void *opaque) > { > +#ifndef CONFIG_WIN32 > V9fsPDU *pdu = opaque; > size_t offset = 7; > V9fsString target; > @@ -4045,6 +4128,10 @@ out: > put_fid(pdu, fidp); > out_nofid: > pdu_complete(pdu, err); > +#else > + V9fsPDU *pdu = opaque; > + pdu_complete(pdu, -1); > +#endif > } > > static CoroutineEntry *pdu_co_handlers[] = { > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > { > +#ifndef CONFIG_WIN32 > struct rlimit rlim; > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > error_report("Failed to get the resource limit"); > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > } > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > open_fd_rc = rlim.rlim_cur / 2; > +#endif > } > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > index 93ba44fb75..2fbe7b831b 100644 > --- a/hw/9pfs/codir.c > +++ b/hw/9pfs/codir.c > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > int len, err = 0; > int32_t size = 0; > off_t saved_dir_pos; > +#ifdef CONFIG_WIN32 > + off_t next_dir_pos; > +#endif > struct dirent *dent; > struct V9fsDirEnt *e = NULL; > V9fsPath path; > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > break; > } > > +#ifdef CONFIG_WIN32 > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > + if (next_dir_pos < 0) { > + err = next_dir_pos; > + goto out; > + } > +#endif > + > /* > * stop this loop as soon as it would exceed the allowed maximum > * response message size for the directory entries collected so far, > @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > } > > size += len; > +#ifndef CONFIG_WIN32 > saved_dir_pos = qemu_dirent_off(dent); > +#else > + saved_dir_pos = next_dir_pos; > +#endif > } > > /* restore (last) saved position */ ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-09 14:29 ` Greg Kurz @ 2022-05-09 15:09 ` Shi, Guohuai 2022-05-09 16:20 ` Greg Kurz 0 siblings, 1 reply; 34+ messages in thread From: Shi, Guohuai @ 2022-05-09 15:09 UTC (permalink / raw) To: Greg Kurz, Bin Meng; +Cc: Christian Schoenebeck, qemu-devel, Meng, Bin > -----Original Message----- > From: Greg Kurz <groug@kaod.org> > Sent: 2022年5月9日 22:29 > To: Bin Meng <bmeng.cn@gmail.com> > Cc: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org; Shi, > Guohuai <Guohuai.Shi@windriver.com>; Meng, Bin <Bin.Meng@windriver.com> > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > Windows > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > On Mon, 25 Apr 2022 22:27:01 +0800 > Bin Meng <bmeng.cn@gmail.com> wrote: > > > From: Guohuai Shi <guohuai.shi@windriver.com> > > > > Add a 9p local file system backend driver to support Windows, > > including open, read, write, close, rename, remove, etc. > > > > All security models are supported. The mapped (mapped-xattr) > > security model is implemented using NTFS Alternate Data Stream > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > --- > > > > Hi ! > > I tend to agree with Christian's remarks that this patch is too big > and that the choice of introducing right away a new implementation > of 9p-local for windows hosts is too bold to start with. We need to > clearly understand what's diverging between windows and linux in order > to make such a decision. You should first try to introduce the required > abstractions to cope with these differences, so that we can review. > Here is the basic introductions of 9PFS for Windows development: Windows always returns -1 when try to call open() for a directory. Windows (actually MinGW library) only allows opendir() for a directory. Windows does not support APIs like "*at" (openat(), renameat(), etc.) So 9PFS can not use any openat() for opening a sub file or directory in 9P mount directory. This commit use merge_fs_path() to build up full filename by string concatenation. I know that may have a risk of security, but Windows does fully support POSIX APIs. > Some inlined remarks below anyway. > > > hw/9pfs/9p-linux-errno.h | 151 +++++ > > hw/9pfs/9p-local.h | 4 + > > hw/9pfs/9p-util.h | 41 ++ > > hw/9pfs/9p.h | 23 + > > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > > hw/9pfs/9p-xattr.c | 113 ++++ > > hw/9pfs/9p.c | 91 ++- > > hw/9pfs/codir.c | 15 + > > 9 files changed, 1982 insertions(+), 1 deletion(-) > > create mode 100644 hw/9pfs/9p-linux-errno.h > > create mode 100644 hw/9pfs/9p-local-win32.c > > create mode 100644 hw/9pfs/9p-util-win32.c > > > > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > > new file mode 100644 > > index 0000000000..b0d6ac45ac > > --- /dev/null > > +++ b/hw/9pfs/9p-linux-errno.h > > @@ -0,0 +1,151 @@ > > +/* > > + * 9p Linux errno translation definition > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > + * See the COPYING file in the top-level directory. > > + */ > > + > > +#include <errno.h> > > + > > +#ifndef QEMU_9P_LINUX_ERRNO_H > > +#define QEMU_9P_LINUX_ERRNO_H > > + > > +/* > > + * This file contains the Linux errno definitions to translate errnos set by > > + * the 9P server (running on Windows) to a corresponding errno value. > > + * > > + * This list should be periodically reviewed and updated; particularly for > > + * errnos that might be set as a result of a file system operation. > > + */ > > + > > +#define L_EPERM 1 /* Operation not permitted */ > > +#define L_ENOENT 2 /* No such file or directory */ > > +#define L_ESRCH 3 /* No such process */ > > +#define L_EINTR 4 /* Interrupted system call */ > > +#define L_EIO 5 /* I/O error */ > > +#define L_ENXIO 6 /* No such device or address */ > > +#define L_E2BIG 7 /* Argument list too long */ > > +#define L_ENOEXEC 8 /* Exec format error */ > > +#define L_EBADF 9 /* Bad file number */ > > +#define L_ECHILD 10 /* No child processes */ > > +#define L_EAGAIN 11 /* Try again */ > > +#define L_ENOMEM 12 /* Out of memory */ > > +#define L_EACCES 13 /* Permission denied */ > > +#define L_EFAULT 14 /* Bad address */ > > +#define L_ENOTBLK 15 /* Block device required */ > > +#define L_EBUSY 16 /* Device or resource busy */ > > +#define L_EEXIST 17 /* File exists */ > > +#define L_EXDEV 18 /* Cross-device link */ > > +#define L_ENODEV 19 /* No such device */ > > +#define L_ENOTDIR 20 /* Not a directory */ > > +#define L_EISDIR 21 /* Is a directory */ > > +#define L_EINVAL 22 /* Invalid argument */ > > +#define L_ENFILE 23 /* File table overflow */ > > +#define L_EMFILE 24 /* Too many open files */ > > +#define L_ENOTTY 25 /* Not a typewriter */ > > +#define L_ETXTBSY 26 /* Text file busy */ > > +#define L_EFBIG 27 /* File too large */ > > +#define L_ENOSPC 28 /* No space left on device */ > > +#define L_ESPIPE 29 /* Illegal seek */ > > +#define L_EROFS 30 /* Read-only file system */ > > +#define L_EMLINK 31 /* Too many links */ > > +#define L_EPIPE 32 /* Broken pipe */ > > +#define L_EDOM 33 /* Math argument out of domain of func */ > > +#define L_ERANGE 34 /* Math result not representable */ > > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > > +#define L_ENAMETOOLONG 36 /* File name too long */ > > +#define L_ENOLCK 37 /* No record locks available */ > > +#define L_ENOSYS 38 /* Function not implemented */ > > +#define L_ENOTEMPTY 39 /* Directory not empty */ > > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > > +#define L_ENOMSG 42 /* No message of desired type */ > > +#define L_EIDRM 43 /* Identifier removed */ > > +#define L_ECHRNG 44 /* Channel number out of range */ > > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > > +#define L_EL3HLT 46 /* Level 3 halted */ > > +#define L_EL3RST 47 /* Level 3 reset */ > > +#define L_ELNRNG 48 /* Link number out of range */ > > +#define L_EUNATCH 49 /* Protocol driver not attached */ > > +#define L_ENOCSI 50 /* No CSI structure available */ > > +#define L_EL2HLT 51 /* Level 2 halted */ > > +#define L_EBADE 52 /* Invalid exchange */ > > +#define L_EBADR 53 /* Invalid request descriptor */ > > +#define L_EXFULL 54 /* Exchange full */ > > +#define L_ENOANO 55 /* No anode */ > > +#define L_EBADRQC 56 /* Invalid request code */ > > +#define L_EBADSLT 57 /* Invalid slot */ > > +#define L_EBFONT 58 /* Bad font file format */ > > +#define L_ENOSTR 59 /* Device not a stream */ > > +#define L_ENODATA 61 /* No data available */ > > +#define L_ETIME 62 /* Timer expired */ > > +#define L_ENOSR 63 /* Out of streams resources */ > > +#define L_ENONET 64 /* Machine is not on the network */ > > +#define L_ENOPKG 65 /* Package not installed */ > > +#define L_EREMOTE 66 /* Object is remote */ > > +#define L_ENOLINK 67 /* Link has been severed */ > > +#define L_EADV 68 /* Advertise error */ > > +#define L_ESRMNT 69 /* Srmount error */ > > +#define L_ECOMM 70 /* Communication error on send */ > > +#define L_EPROTO 71 /* Protocol error */ > > +#define L_EMULTIHOP 72 /* Multihop attempted */ > > +#define L_EDOTDOT 73 /* RFS specific error */ > > +#define L_EBADMSG 74 /* Not a data message */ > > +#define L_EOVERFLOW 75 /* Value too large for defined data type */ > > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > > +#define L_EBADFD 77 /* File descriptor in bad state */ > > +#define L_EREMCHG 78 /* Remote address changed */ > > +#define L_ELIBACC 79 /* Can not access a needed shared library */ > > +#define L_ELIBBAD 80 /* Accessing a corrupted shared library */ > > +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > > +#define L_ELIBMAX 82 /* Attempting to link in too many shared libs > */ > > +#define L_ELIBEXEC 83 /* Cannot exec a shared library directly */ > > +#define L_EILSEQ 84 /* Illegal byte sequence */ > > +#define L_ERESTART 85 /* Interrupted system call should be restarted > */ > > +#define L_ESTRPIPE 86 /* Streams pipe error */ > > +#define L_EUSERS 87 /* Too many users */ > > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > > +#define L_EDESTADDRREQ 89 /* Destination address required */ > > +#define L_EMSGSIZE 90 /* Message too long */ > > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport endpoint > */ > > +#define L_EPFNOSUPPORT 96 /* Protocol family not supported */ > > +#define L_EAFNOSUPPORT 97 /* Address family not supported by protocol */ > > +#define L_EADDRINUSE 98 /* Address already in use */ > > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > > +#define L_ENETDOWN 100 /* Network is down */ > > +#define L_ENETUNREACH 101 /* Network is unreachable */ > > +#define L_ENETRESET 102 /* Network dropped connection because of reset > */ > > +#define L_ECONNABORTED 103 /* Software caused connection abort */ > > +#define L_ECONNRESET 104 /* Connection reset by peer */ > > +#define L_ENOBUFS 105 /* No buffer space available */ > > +#define L_EISCONN 106 /* Transport endpoint is already connected */ > > +#define L_ENOTCONN 107 /* Transport endpoint is not connected */ > > +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown > */ > > +#define L_ETOOMANYREFS 109 /* Too many references: cannot splice */ > > +#define L_ETIMEDOUT 110 /* Connection timed out */ > > +#define L_ECONNREFUSED 111 /* Connection refused */ > > +#define L_EHOSTDOWN 112 /* Host is down */ > > +#define L_EHOSTUNREACH 113 /* No route to host */ > > +#define L_EALREADY 114 /* Operation already in progress */ > > +#define L_EINPROGRESS 115 /* Operation now in progress */ > > +#define L_ESTALE 116 /* Stale NFS file handle */ > > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > > +#define L_EISNAM 120 /* Is a named type file */ > > +#define L_EREMOTEIO 121 /* Remote I/O error */ > > +#define L_EDQUOT 122 /* Quota exceeded */ > > +#define L_ENOMEDIUM 123 /* No medium found */ > > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > > +#define L_ECANCELED 125 /* Operation Canceled */ > > +#define L_ENOKEY 126 /* Required key not available */ > > +#define L_EKEYEXPIRED 127 /* Key has expired */ > > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > > +#define L_EOWNERDEAD 130 /* Owner died */ > > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > > + > > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > > index 55ea4b7883..4c75876e19 100644 > > --- a/hw/9pfs/9p-local.h > > +++ b/hw/9pfs/9p-local.h > > @@ -31,6 +31,10 @@ static inline bool local_is_mapped_file_metadata(FsContext > *fs_ctx, > > > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > mode_t mode); > > +#ifndef CONFIG_WIN32 > > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > +#else > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > +#endif > > > > Hrm... Returning a DIR * instead of a file descriptor looks like this > is a completely different function with different users, but you're > keeping the same name. What's happening here ? > As I mentioned above, Windows does not allow open() for a directory (return int type file descriptor), only allows opendir() for a directory (return DIR*) > > #endif > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > > index 97e681e167..6eadb38e1d 100644 > > --- a/hw/9pfs/9p-util.h > > +++ b/hw/9pfs/9p-util.h > > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > > errno = serrno; > > } > > > > +#ifndef CONFIG_WIN32 > > static inline int openat_dir(int dirfd, const char *name) > > { > > return openat(dirfd, name, > > @@ -89,7 +90,9 @@ again: > > errno = serrno; > > return fd; > > } > > +#endif /* !CONFIG_WIN32 */ > > > > +#ifndef CONFIG_WIN32 > > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > > void *value, size_t size); > > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char > *filename, > > char *list, size_t size); > > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > > const char *name); > > +#else > > + > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size); > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size, > > + int flags); > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > + char *list, size_t size); > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > + const char *name); > > + > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > > + > > +static inline char *merge_fs_path(const char *path1, const char *path2) > > +{ > > + char *full_fs_path; > > + size_t full_fs_path_len; > > + > > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > > + full_fs_path = g_malloc(full_fs_path_len); > > > > + strcpy(full_fs_path, path1); > > + strcat(full_fs_path, "\\"); > > + strcat(full_fs_path, path2); > > + > > + return full_fs_path; > > +} > > + > > +#endif /* !CONFIG_WIN32 */ > > + > > +#ifndef CONFIG_WIN32 > > /* > > * Darwin has d_seekoff, which appears to function similarly to d_off. > > * However, it does not appear to be supported on all file systems, > > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > > return dent->d_off; > > #endif > > } > > +#endif /* !CONFIG_WIN32 */ > > > > /** > > * qemu_dirent_dup() - Duplicate directory entry @dent. > > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct dirent > *dent) > > #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > > #endif > > + > > +#ifndef CONFIG_WIN32 > > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > > +#else > > +int qemu_mknodat(const char *dirname, const char *filename, > > + mode_t mode, dev_t dev); > > Same remark as local_opendir_nofollow(). Especially, this clearly deviates > from the mknodat(2) semantics : dirfd is supposed to be a handle on an > existing directory, while dirname cannot provide such a guarantee. Windows does not have APIs like *at: openat(), mknodat(), etc. > > > +#endif > > > > #endif > > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > > index 994f952600..87e8eac840 100644 > > --- a/hw/9pfs/9p.h > > +++ b/hw/9pfs/9p.h > > @@ -3,13 +3,36 @@ > > > > #include <dirent.h> > > #include <utime.h> > > +#ifndef CONFIG_WIN32 > > #include <sys/resource.h> > > +#endif > > #include "fsdev/file-op-9p.h" > > #include "fsdev/9p-iov-marshal.h" > > #include "qemu/thread.h" > > #include "qemu/coroutine.h" > > #include "qemu/qht.h" > > > > +#ifdef CONFIG_WIN32 > > + > > +#define O_NOCTTY 0 > > +#define O_NDELAY 0 > > +#define O_NONBLOCK 0 > > +#define O_DSYNC 0 > > +#define O_DIRECT 0 > > +#define O_NOFOLLOW 0 > > +#define O_NOATIME 0 > > +#define O_SYNC 0 > > +#define O_ASYNC 0 > > +#define O_DIRECTORY 02000000 > > + > > +#define FASYNC 0 > > + > > +#define AT_REMOVEDIR 1 > > + > > +#define NAME_MAX 260 > > + > > +#endif > > + > > enum { > > P9_TLERROR = 6, > > P9_RLERROR, > > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > > new file mode 100644 > > index 0000000000..aab7c9f7b5 > > --- /dev/null > > +++ b/hw/9pfs/9p-local-win32.c > > @@ -0,0 +1,1242 @@ > > +/* > > + * 9p Windows callback > > + * > > + * Copyright (c) 2022 Wind River Systems, Inc. > > + * > > + * Based on hw/9pfs/9p-local.c > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2. See > > + * the COPYING file in the top-level directory. > > + */ > > + > > +/* > > + * Not so fast! You might want to read the 9p developer docs first: > > + * https://wiki.qemu.org/Documentation/9p > > + */ > > + > > +#include "qemu/osdep.h" > > +#include <windows.h> > > +#include <dirent.h> > > +#include "9p.h" > > +#include "9p-local.h" > > +#include "9p-xattr.h" > > +#include "9p-util.h" > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > +#include "qapi/error.h" > > +#include "qemu/cutils.h" > > +#include "qemu/error-report.h" > > +#include "qemu/option.h" > > +#include <libgen.h> > > + > > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > > + int flags, mode_t mode) > > +{ > > + char *full_file_name; > > + int fd; > > + > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > + fd = open(full_file_name, flags | _O_BINARY, mode); > > Hmm... in order to fix CVE-2016-9602, we had to ban path based syscalls > because a malicious guest could easily escape the file system with a > symlink attack. So now we basically split all paths we get from the > client so that we only call *at() syscalls with a single path element > and a NOFOLLOW flag. > > This function seems to be doing the very opposite, which is clearly > a no go. Windows does not have openat(), so the only way is to build up full filename by merge_fs_path(). Additionally, Windows does not support symbolic link APIs in POSIX library, so there may not be any security risks. > > > + g_free(full_file_name); > > + > > + return fd; > > +} > > + > > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > > +{ > > + char *full_file_name; > > + DIR *dir; > > + > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > + dir = opendir(full_file_name); > > Same remark here. Same as above. > > > + g_free(full_file_name); > > + return dir; > > +} > > + > > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > + mode_t mode) > > +{ > > + int fd; > > + > > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { > > Why special casing '/' ? Windows does not allow open() for a directory, or any filename format like a directory. > > > + /* Windows does not allow call open() for a directory */ > > + fd = -1; > > This -1 will ultimately cause a Rlerror message to be returned > to the client : an errno should be set here... but anyway, this > isn't the way to do it : you should just call open() and let > windows set the errno. > > Now, the major problem is that we really need to be able to > open directories because of CVE-2016-9602. Isn't it possible > to achieve it in some other way, e.g. opendir() and dirfd() ? As I mentioned above, Windows does not support fd (file descriptor) for directory open(). > > > + } else { > > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); > > Does this honor the NOFOLLOW semantics ? It seems not...which is again > a no go. You cannot blindly assume how the underlying file system > handles symlinks. > Windows does not support symbolic link in POSIX APIs, > > + } > > + > > + return fd; > > +} > > + > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > > +{ > > + return opendir_with_ctx(fs_ctx, path); > > +} > > + > > +static FILE *local_fopenat(const char *dirname, const char *name, > > + const char *mode) > > +{ > > + char *full_file_name; > > + char modestr[3] = {0}; > > + FILE *fp; > > + > > + /* > > + * only supports two modes > > + */ > > + if (mode[0] == 'r') { > > + modestr[0] = 'r'; > > + } else if (mode[0] == 'w') { > > + modestr[0] = 'w'; > > + } else { > > + return NULL; > > + } > > + /* Windows host needs 'b' flag */ > > + modestr[1] = 'b'; > > + > > + full_file_name = merge_fs_path(dirname, name); > > + fp = fopen(full_file_name, modestr); > > + g_free(full_file_name); > > + > > + return fp; > > +} > > + > > +static void local_mapped_file_attr(const char *dirpath, const char *name, > > + struct stat *stbuf) > > +{ > > + FILE *fp; > > + char buf[ATTR_MAX]; > > + char *full_file_name; > > + > > + if (strcmp(name, ".") != 0) { > > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > > + fp = local_fopenat(full_file_name, name, "r"); > > + g_free(full_file_name); > > + } else { > > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > > + } > > + if (!fp) { > > + return; > > + } > > + > > + memset(buf, 0, ATTR_MAX); > > + while (fgets(buf, ATTR_MAX, fp)) { > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > + stbuf->st_uid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > + stbuf->st_gid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > + stbuf->st_rdev = atoi(buf + 12); > > + } > > + memset(buf, 0, ATTR_MAX); > > + } > > + fclose(fp); > > +} > > + > > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat > *stbuf) > > +{ > > + int err = -1; > > + char *full_dir_name, *full_file_name; > > + char *dirpath = g_path_get_dirname(fs_path->data); > > + char *name = g_path_get_basename(fs_path->data); > > + > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > + full_file_name = merge_fs_path(full_dir_name, name); > > + err = stat(full_file_name, stbuf); > > + > > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > > + /* > > + * Hard code for root directory on Windows host. > > + * This will root directory have a special inode number, > > + * then guest OS can detect it is a special directory. > > + */ > > + stbuf->st_ino = 2; > > + } > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + /* Actual credentials are part of extended attrs */ > > + uid_t tmp_uid; > > + gid_t tmp_gid; > > + mode_t tmp_mode; > > + dev_t tmp_dev; > > + > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > > + &tmp_uid, sizeof(uid_t)) > 0) { > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > + } > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > > + &tmp_gid, sizeof(gid_t)) > 0) { > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > + } > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > > + &tmp_mode, sizeof(mode_t)) > 0) { > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > + } > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > > + &tmp_dev, sizeof(dev_t)) > 0) { > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > + } > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + local_mapped_file_attr(full_dir_name, name, stbuf); > > + } > > + > > + g_free(full_file_name); > > + g_free(full_dir_name); > > + > > + if (err) { > > + goto err_out; > > + } > > + > > +err_out: > > + g_free(name); > > + g_free(dirpath); > > + return err; > > +} > > + > > +static int local_set_mapped_file_attrat(const char *dirname, const char *name, > > + FsCred *credp) > > +{ > > + FILE *fp; > > + int ret; > > + char buf[ATTR_MAX]; > > + int uid = -1, gid = -1, mode = -1, rdev = -1; > > + bool is_root = !strcmp(name, "."); > > + char *full_dir_name; > > + > > + if (is_root) { > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > > + if (!fp) { > > + if (errno == ENOENT) { > > + goto update_map_file; > > + } else { > > + return -1; > > + } > > + } > > + } else { > > + /* > > + * mapped-file: > > + * <sub_file> attribute stored to: > > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > > + */ > > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > > + ret = mkdir(full_dir_name); > > + > > + if (ret < 0 && errno != EEXIST) { > > + g_free(full_dir_name); > > + return -1; > > + } > > + > > + fp = local_fopenat(full_dir_name, name, "r"); > > + if (!fp) { > > + if (errno == ENOENT) { > > + goto update_map_file; > > + } else { > > + g_free(full_dir_name); > > + return -1; > > + } > > + } > > + } > > + > > + memset(buf, 0, ATTR_MAX); > > + while (fgets(buf, ATTR_MAX, fp)) { > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > + uid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > + gid = atoi(buf + 11); > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > + mode = atoi(buf + 12); > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > + rdev = atoi(buf + 12); > > + } > > + memset(buf, 0, ATTR_MAX); > > + } > > + fclose(fp); > > + > > +update_map_file: > > + if (is_root) { > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > > + } else { > > + fp = local_fopenat(full_dir_name, name, "w"); > > + g_free(full_dir_name); > > + } > > + if (!fp) { > > + return -1; > > + } > > + > > + if (credp->fc_uid != -1) { > > + uid = credp->fc_uid; > > + } > > + if (credp->fc_gid != -1) { > > + gid = credp->fc_gid; > > + } > > + if (credp->fc_mode != (mode_t)-1) { > > + mode = credp->fc_mode; > > + } > > + if (credp->fc_rdev != -1) { > > + rdev = credp->fc_rdev; > > + } > > + > > + if (uid != -1) { > > + fprintf(fp, "virtfs.uid=%d\n", uid); > > + } > > + if (gid != -1) { > > + fprintf(fp, "virtfs.gid=%d\n", gid); > > + } > > + if (mode != -1) { > > + fprintf(fp, "virtfs.mode=%d\n", mode); > > + } > > + if (rdev != -1) { > > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > > + } > > + fclose(fp); > > + > > + return 0; > > +} > > + > > +static int local_set_xattrat(const char *dirname, const char *path, > > + FsCred *credp) > > +{ > > + int err; > > + > > + if (credp->fc_uid != -1) { > > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > > + &tmp_uid, sizeof(uid_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + if (credp->fc_gid != -1) { > > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > > + &tmp_gid, sizeof(gid_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + if (credp->fc_mode != (mode_t)-1) { > > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > > + &tmp_mode, sizeof(mode_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + if (credp->fc_rdev != -1) { > > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > > + &tmp_rdev, sizeof(dev_t), 0); > > + if (err) { > > + return err; > > + } > > + } > > + return 0; > > +} > > + > > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > > + char *buf, size_t bufsz) > > +{ > > + return -1; > > +} > > + > > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + return close(fs->fd); > > +} > > + > > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + return closedir(fs->dir.stream); > > +} > > + > > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > > + int flags, V9fsFidOpenState *fs) > > +{ > > + int fd; > > + > > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > > + if (fd == -1) { > > + return -1; > > + } > > + fs->fd = fd; > > + return fs->fd; > > +} > > + > > +static int local_opendir(FsContext *ctx, > > + V9fsPath *fs_path, V9fsFidOpenState *fs) > > +{ > > + DIR *stream; > > + char *full_file_name; > > + > > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > > + stream = opendir(full_file_name); > > + g_free(full_file_name); > > + > > + if (!stream) { > > + return -1;local_opendir_nofollowlocal_opendir_nofollow > > + } > > + > > + fs->dir.stream = stream; > > + return 0; > > +} > > + > > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + rewinddir(fs->dir.stream); > > +} > > + > > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > > +{ > > + return telldir(fs->dir.stream); > > +} > > + > > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > > +{local_opendir_nofollow > > + struct dirent *entry; > > + > > +again: > > + entry = readdir(fs->dir.stream); > > + if (!entry) { > > + return NULL; > > + } > > + > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > > + /* skip the meta data */ > > + goto again; > > + } > > + } > > + > > + return entry; > > +} > > + > > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) > > +{ > > + off_t count; > > + struct dirent *findentry; > > + struct dirent *entry; > > + size_t namelen[3] = {0}; > > + off_t direntoff[3] = {-1, -1, -1}; > > + char *d_name[3]; > > + int i; > > + > > + /* > > + * MinGW's seekdir() requires directory is not modified. If guest OS is > > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", > > + * then MinGW's seekdir() will seek to a wrong offset. > > + * > > + * This function saves some old offset directory entry name, > > + * and lookup current entry again, and compare the offset. > > + * > > + * If new offset is less than old offset, that means someone is deleting > > + * files in the directory, thus we need to seek offset backward. > > + * > > + * If new offset is larger than old offset, that means someone is creating > > + * files in the directory, thus we need to seek offset forward. > > + */ > > + > > + direntoff[0] = telldir(fs->dir.stream); > > + > > + /* do nothing if current offset is 0 or EOF */ > > + if (direntoff[0] == 0 || direntoff[0] < 0) { > > + seekdir(fs->dir.stream, off); > > + return ; > > + } > > + > > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > > + d_name[1] = d_name[0] + sizeof(entry->d_name); > > + d_name[2] = d_name[1] + sizeof(entry->d_name); > > + > > + /* save 3 nearest entries and offsets */ > > + for (i = 0; i < 3; i++) { > > + entry = &fs->dir.stream->dd_dir; > > + > > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > > + namelen[i] = strlen(d_name[i]) + 1; > > + > > + direntoff[i] = telldir(fs->dir.stream); > > + > > + entry = readdir(fs->dir.stream); > > + if (entry == NULL) { > > + break; > > + } > > + } > > + > > + /* lookup saved entries again */ > > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > > + rewinddir(fs->dir.stream); > > + count = 0; > > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > > + count++; > > + > > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > > + if (count + i == direntoff[i]) { > > + seekdir(fs->dir.stream, off); > > + goto out; > > + } else if (count + i < direntoff[i]) { > > + off = off - (direntoff[i] - count) - i; > > + if (off <= 0) { > > + off = 0; > > + } > > + seekdir(fs->dir.stream, off); > > + goto out; > > + } else { > > + off = off + (count - direntoff[i]) - i; > > + seekdir(fs->dir.stream, off); > > + goto out; > > + } > > + }local_opendir_nofollow > > + } > > + } > > + /* can not get anything, seek backward */ > > + off = off - 1; > > + > > + seekdir(fs->dir.stream, off); > > +out: > > + g_free(d_name[0]); > > + return ; > > +} > > + > > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > > + const struct iovec *iov, > > + int iovcnt, off_t offset) > > +{ > > +#ifdef CONFIG_PREADV > > + return preadv(fs->fd, iov, iovcnt, offset); > > +#else > > + int err = lseek(fs->fd, offset, SEEK_SET); > > + if (err == -1) { > > + return err; > > + } else { > > + return readv(fs->fd, iov, iovcnt); > > + } > > +#endif > > +} > > + > > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > > + const struct iovec *iov, > > + int iovcnt, off_t offset) > > +{ > > + ssize_t ret; > > +#ifdef CONFIG_PREADV > > + ret = pwritev(fs->fd, iov, iovcnt, offset); > > +#else > > + int err = lseek(fs->fd, offset, SEEK_SET); > > + if (err == -1) { > > + return err; > > + } else { > > + ret = writev(fs->fd, iov, iovcnt); > > + } > > +#endif > > +#ifdef CONFIG_SYNC_FILE_RANGE > > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > > + /* > > + * Initiate a writeback. This is not a data integrity sync. > > + * We want to ensure that we don't leave dirty pages in the cache > > + * after write when writeout=immediate is sepcified. > > + */ > > + sync_file_range(fs->fd, offset, ret, > > + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); > > + } > > +#endif > > + return ret; > > +} > > + > > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > +{ > > + char *dirpath = g_path_get_dirname(fs_path->data); > > + char *name = g_path_get_basename(fs_path->data); > > + int ret = -1; > > + char *full_file_name = NULL; > > + DIR *dir; > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + ret = local_set_xattrat(full_file_name, name, credp); > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > + ret = -1; > > + errno = ENOTSUP; > > + } > > + closedir(dir); > > + > > +out: > > + if (full_file_name != NULL) { > > + g_free(full_file_name); > > + } > > + > > + g_free(dirpath); > > + g_free(name); > > + return ret; > > +} > > + > > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > > + const char *name, FsCred *credp) > > +{ > > + return -1; > > +} > > + > > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > > + const char *name, FsCred *credp) > > +{ > > + int err = -1; > > + char *full_file_name; > > + char *full_dir_name; > > + DIR *dir; > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > > + if (dir == NULL) { > > + return -1; > > + } > > + closedir(dir); > > + > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > > + full_file_name = merge_fs_path(full_dir_name, name); > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + err = mkdir(full_file_name); > > + if (err == -1) { > > + goto out; > > + } > > + credp->fc_mode = credp->fc_mode; > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + err = local_set_xattrat(full_dir_name, name, credp); > > + } else { > > + err = local_set_mapped_file_attrat(full_dir_name, name, credp); > > + } > > + if (err == -1) { > > + rmdir(full_file_name); > > + } > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > + err = mkdir(full_file_name); > > + if (err == -1) { > > + goto out; > > + } > > + /* Windows does not support chmod, do nothing here */ > > + } > > + > > + goto out; > > + > > +out: > > + g_free(full_dir_name); > > + g_free(full_file_name); > > + return err; > > +} > > + > > +static int local_fstat(FsContext *fs_ctx, int fid_type, > > + V9fsFidOpenState *fs, struct stat *stbuf) > > +{ > > + > > + int err, fd; > > + char filename[NAME_MAX]; > > + char *dirpath; > > + char *name; > > + HANDLE hFile; > > + DWORD dwRet; > > + > > + if (fid_type == P9_FID_DIR) { > > + /* Windows does not support open directory */ > > + return -1; > > + } else { > > + fd = fs->fd; > > + } > > + > > + err = fstat(fd, stbuf); > > + if (err) { > > + return err; > > + } > > + > > + /* get real file name by fd */ > > + hFile = (HANDLE)_get_osfhandle(fd); > > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); > > + > > + if (dwRet >= NAME_MAX) { > > + return -1; > > + } > > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > > + memmove(filename, filename + 4, NAME_MAX - 4); > > + > > + dirpath = g_path_get_dirname(filename); > > + name = g_path_get_basename(filename); > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + /* Actual credentials are part of extended attrs */ > > + uid_t tmp_uid; > > + gid_t tmp_gid; > > + mode_t tmp_mode; > > + dev_t tmp_dev; > > + > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > > + &tmp_uid, sizeof(uid_t)) > 0) { > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > + } > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > > + &tmp_gid, sizeof(gid_t)) > 0) { > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > + } > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > > + &tmp_mode, sizeof(mode_t)) > 0) { > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > + } > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > > + &tmp_dev, sizeof(dev_t)) > 0) { > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > + } > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + errno = EOPNOTSUPP; > > + g_free(dirpath); > > + g_free(name); > > + return -1; > > + } > > + > > + g_free(dirpath); > > + g_free(name); > > + > > + return err; > > +} > > + > > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, > > + int flags, FsCred *credp, V9fsFidOpenState *fs) > > +{ > > + int fd = -1; > > + int err = -1; > > + char *full_file_name; > > + > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + full_file_name = merge_fs_path(dir_path->data, name); > > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > > + g_free(full_file_name); > > + > > + err = fd; > > + fs->fd = fd; > > + goto out; > > + > > + close_preserve_errno(fd); > > +out: > > + return err; > > +} > > + > > + > > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > > + V9fsPath *dir_path, const char *name, FsCred *credp) > > +{ > > + return -1; > > Why ? This would require a detailed comment and also to set errno. An error number set is missing here, should be ENOTSUP. > > > +} > > + > > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > > + V9fsPath *dirpath, const char *name) > > +{ > > + return -1; > > Same remark. > > Before re-posting, you really need to come up with a detailed plan > around symlinks. > > Cheers, > > -- > Greg An error number set is missing here, should be ENOTSUP. Windows does not support symbolic link in its POSIX API library. It also require Administrator privileges and NTFS (need to turn on symbolic link support). Currently, I think it is very hard to support symbolic link on Windows host. > > > +} > > + > > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > > +{ > > + int fd, ret; > > + > > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > > + if (fd == -1) { > > + return -1; > > + } > > + ret = ftruncate(fd, size); > > + close_preserve_errno(fd); > > + return ret; > > +} > > + > > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > +{ > > + char *full_file_name; > > + char *dirpath = g_path_get_dirname(fs_path->data); > > + char *name = g_path_get_basename(fs_path->data); > > + int ret = -1; > > + DIR *dir; > > + > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > + > > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > > + /* Windows does not support chown() */ > > + ret = -1; > > + errno = ENOTSUP; > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > + ret = local_set_xattrat(full_file_name, name, credp); > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > + } > > + g_free(full_file_name); > > + closedir(dir); > > +out: > > + g_free(name); > > + g_free(dirpath); > > + return ret; > > +} > > + > > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > > + const struct timespec *buf) > > +{ > > + struct utimbuf tm; > > + char *full_file_name; > > + int err; > > + > > + tm.actime = buf[0].tv_sec; > > + tm.modtime = buf[1].tv_sec; > > + > > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > > + err = utime(full_file_name, &tm); > > + g_free(full_file_name); > > + > > + return err; > > +} > > + > > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > > + const char *name, int flags) > > +{ > > + int ret; > > + char *full_file_name; > > + char *full_dir_name; > > + > > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > > + full_file_name = merge_fs_path(full_dir_name, name); > > + > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + char *full_meta_dir_name; > > + char *full_meta_file_name; > > + > > + /* > > + * We need to remove the metadata as well: > > + * - the metadata directory if we're removing a directory > > + * - the metadata file in the parent's metadata directory > > + * > > + * If any of these are missing (ie, ENOENT) then we're probably > > + * trying to remove something that wasn't created in mapped-file > > + * mode. We just ignore the error. > > + */ > > + > > + if ((flags & AT_REMOVEDIR) != 0) { > > + full_meta_dir_name = merge_fs_path(full_file_name, > VIRTFS_META_DIR); > > + ret = rmdir(full_meta_dir_name); > > + g_free(full_meta_dir_name); > > + > > + if (ret < 0 && errno != ENOENT) { > > + g_free(full_file_name); > > + g_free(full_dir_name); > > + return -1; > > + } > > + } > > + > > + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); > > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); > > + ret = remove(full_meta_file_name); > > + g_free(full_meta_dir_name); > > + g_free(full_meta_file_name); > > + > > + if (ret < 0 && errno != ENOENT) { > > + g_free(full_dir_name); > > + g_free(full_file_name); > > + > > + return -1; > > + } > > + } > > + > > + if ((flags & AT_REMOVEDIR) != 0) { > > + ret = rmdir(full_file_name); > > + } else { > > + ret = remove(full_file_name); > > + } > > + > > + g_free(full_dir_name); > > + g_free(full_file_name); > > + > > + return ret; > > +} > > + > > +static int local_remove(FsContext *ctx, const char *path) > > +{ > > + int err; > > + DIR *stream; > > + char *full_file_name; > > + char *dirpath = g_path_get_dirname(path); > > + char *name = g_path_get_basename(path); > > + int flags = 0; > > + > > + full_file_name = merge_fs_path(ctx->fs_root, path); > > + stream = opendir(full_file_name); > > + if (stream != NULL) { > > + closedir(stream); > > + flags |= AT_REMOVEDIR; > > + } > > + err = local_unlinkat_common(ctx, dirpath, name, flags); > > + > > + g_free(name); > > + g_free(dirpath); > > + g_free(full_file_name); > > + return err; > > +} > > + > > +static int local_fsync(FsContext *ctx, int fid_type, > > + V9fsFidOpenState *fs, int datasync) > > +{ > > + if (fid_type != P9_FID_DIR) { > > + return _commit(fs->fd); > > + } > > + return 0; > > +} > > + > > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) > > +{ > > + int ret; > > + ret = qemu_statfs(s->fs_root, stbuf); > > + if (ret == 0) { > > + /* use context address as fsid */ > > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > > + } > > + > > + return ret; > > +} > > + > > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > > + const char *name, void *value, size_t size) > > +{ > > + return -1; > > +} > > + > > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > > + void *value, size_t size) > > +{ > > + return -1; > > +} > > + > > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, > > + void *value, size_t size, int flags) > > +{ > > + return -1; > > +} > > + > > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > > + const char *name) > > +{ > > + return -1; > > +} > > + > > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > > + const char *name, V9fsPath *target) > > +{ > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + if (dir_path) { > > + if (!strcmp(name, ".")) { > > + /* "." relative to "foo/bar" is "foo/bar" */ > > + v9fs_path_copy(target, dir_path); > > + } else if (!strcmp(name, "..")) { > > + if (!strcmp(dir_path->data, ".")) { > > + /* ".." relative to the root is "." */ > > + v9fs_path_sprintf(target, "."); > > + } else { > > + char *tmp = g_path_get_dirname(dir_path->data); > > + /* > > + * Symbolic links are resolved by the client. We can assume > > + * that ".." relative to "foo/bar" is equivalent to "foo" > > + */ > > + v9fs_path_sprintf(target, "%s", tmp); > > + g_free(tmp); > > + } > > + } else { > > + assert(!strchr(name, '/')); > > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > > + } > > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > > + !strcmp(name, "..")) { > > + /* This is the root fid */ > > + v9fs_path_sprintf(target, "."); > > + } else { > > + assert(!strchr(name, '/')); > > + v9fs_path_sprintf(target, "./%s", name); > > + } > > + return 0; > > +} > > + > > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > > + const char *old_name, V9fsPath *newdir, > > + const char *new_name) > > +{ > > + return -1; > > +} > > + > > +static int local_rename(FsContext *ctx, const char *oldpath, > > + const char *newpath) > > +{ > > + int err; > > + > > + char *full_old_name; > > + char *full_new_name; > > + > > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > > + > > + err = rename(full_old_name, full_new_name); > > + > > + g_free(full_old_name); > > + g_free(full_new_name); > > + > > + return err; > > +} > > + > > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > > + const char *name, int flags) > > +{ > > + int ret; > > + > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > + local_is_mapped_file_metadata(ctx, name)) { > > + errno = EINVAL; > > + return -1; > > + } > > + > > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > > + > > + return ret; > > +} > > + > > +static int check_filesystem_type(char *fs_root, int export_flags) > > +{ > > + HANDLE hFile; > > + wchar_t FsName[MAX_PATH + 1] = {0}; > > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > > + > > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > > + return 0; > > + } > > + > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > + if (hFile == INVALID_HANDLE_VALUE) { > > + return -1; > > + } > > + > > + /* Get file system type name */ > > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > > + FsName, MAX_PATH + 1) == 0) { > > + CloseHandle(hFile); > > + return -1; > > + } > > + CloseHandle(hFile); > > + > > + if (wcscmp(FsName, NtfsName) != 0) { > > + return -1; > > + } > > + > > + return 0; > > +} > > + > > +static int local_init(FsContext *ctx, Error **errp) > > +{ > > + LocalData *data = g_malloc(sizeof(*data)); > > + > > + struct stat StatBuf; > > + > > + if (stat(ctx->fs_root, &StatBuf) != 0) { > > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > > + goto err; > > + } > > + > > + /* > > + * security_model=mapped(-xattr) requires a fileystem on Windows that > > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > > + * probably most popular on Windows. It is fair enough to assume > > + * Windows users to use NTFS for the mapped security model. > > + */ > > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > > + "security_model is mapped or mapped-xattr"); > > + goto err; > > + } > > + > > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > > + ctx->xops = passthrough_xattr_ops; > > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > > + ctx->xops = mapped_xattr_ops; > > + } else if (ctx->export_flags & V9FS_SM_NONE) { > > + ctx->xops = none_xattr_ops; > > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > + /* > > + * xattr operation for mapped-file and passthrough > > + * remain same. > > + */ > > + ctx->xops = passthrough_xattr_ops; > > + } > > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > > + > > + ctx->private = data; > > + return 0; > > + > > +err: > > + g_free(data); > > + return -1; > > +} > > + > > +static void local_cleanup(FsContext *ctx) > > +{ > > + LocalData *data = ctx->private; > > + > > + if (!data) { > > + return; > > + } > > + > > + close(data->mountfd); > > + g_free(data); > > +} > > + > > +static void error_append_security_model_hint(Error *const *errp) > > +{ > > + error_append_hint(errp, "Valid options are: security_model=" > > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > > +} > > + > > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) > > +{ > > + ERRP_GUARD(); > > + const char *sec_model = qemu_opt_get(opts, "security_model"); > > + const char *path = qemu_opt_get(opts, "path"); > > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > > + > > + if (!sec_model) { > > + error_setg(errp, "security_model property not set"); > > + error_append_security_model_hint(errp); > > + return -1; > > + } > > + > > + if (!strcmp(sec_model, "passthrough")) { > > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > > + } else if (!strcmp(sec_model, "mapped") || > > + !strcmp(sec_model, "mapped-xattr")) { > > + fse->export_flags |= V9FS_SM_MAPPED; > > + } else if (!strcmp(sec_model, "none")) { > > + fse->export_flags |= V9FS_SM_NONE; > > + } else if (!strcmp(sec_model, "mapped-file")) { > > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > > + } else { > > + error_setg(errp, "invalid security_model property '%s'", sec_model); > > + error_append_security_model_hint(errp); > > + return -1; > > + } > > + > > + if (multidevs) { > > + if (!strcmp(multidevs, "remap")) { > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > + fse->export_flags |= V9FS_REMAP_INODES; > > + } else if (!strcmp(multidevs, "forbid")) { > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > > + } else if (!strcmp(multidevs, "warn")) { > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > + } else { > > + error_setg(errp, "invalid multidevs property '%s'", > > + multidevs); > > + error_append_hint(errp, "Valid options are: multidevs=" > > + "[remap|forbid|warn]\n"); > > + return -1; > > + } > > + } > > + > > + if (!path) { > > + error_setg(errp, "path property not set"); > > + return -1; > > + } > > + > > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > > + error_prepend(errp, "invalid throttle configuration: "); > > + return -1; > > + } > > + > > + if (fse->export_flags & V9FS_SM_MAPPED || > > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > > + fse->fmode = > > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > > + fse->dmode = > > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; > > + } else { > > + if (qemu_opt_find(opts, "fmode")) { > > + error_setg(errp, "fmode is only valid for mapped security modes"); > > + return -1; > > + } > > + if (qemu_opt_find(opts, "dmode")) { > > + error_setg(errp, "dmode is only valid for mapped security modes"); > > + return -1; > > + } > > + } > > + > > + fse->path = g_strdup(path); > > + > > + return 0; > > +} > > + > > +FileOperations local_ops = { > > + .parse_opts = local_parse_opts, > > + .init = local_init, > > + .cleanup = local_cleanup, > > + .lstat = local_lstat, > > + .readlink = local_readlink, > > + .close = local_close, > > + .closedir = local_closedir, > > + .open = local_open, > > + .opendir = local_opendir, > > + .rewinddir = local_rewinddir, > > + .telldir = local_telldir, > > + .readdir = local_readdir, > > + .seekdir = local_seekdir, > > + .preadv = local_preadv, > > + .pwritev = local_pwritev, > > + .chmod = local_chmod, > > + .mknod = local_mknod, > > + .mkdir = local_mkdir, > > + .fstat = local_fstat, > > + .open2 = local_open2, > > + .symlink = local_symlink, > > + .link = local_link, > > + .truncate = local_truncate, > > + .rename = local_rename, > > + .chown = local_chown, > > + .utimensat = local_utimensat, > > + .remove = local_remove, > > + .fsync = local_fsync, > > + .statfs = local_statfs, > > + .lgetxattr = local_lgetxattr, > > + .llistxattr = local_llistxattr, > > + .lsetxattr = local_lsetxattr, > > + .lremovexattr = local_lremovexattr, > > + .name_to_path = local_name_to_path, > > + .renameat = local_renameat, > > + .unlinkat = local_unlinkat, > > +}; > > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > > new file mode 100644 > > index 0000000000..d9b35e7425 > > --- /dev/null > > +++ b/hw/9pfs/9p-util-win32.c > > @@ -0,0 +1,303 @@ > > +/* > > + * 9p utilities (Windows Implementation) > > + * > > + * Copyright (c) 2022 Wind River Systems, Inc. > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > + * See the COPYING file in the top-level directory. > > + */ > > + > > +#include "qemu/osdep.h" > > +#include "qapi/error.h" > > +#include "qemu/error-report.h" > > +#include "9p.h" > > +#include "9p-util.h" > > +#include "9p-linux-errno.h" > > +#include <windows.h> > > +#include <dirent.h> > > + > > +#ifndef V9FS_MAGIC > > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > > +#endif > > + > > +static int build_ads_name(char *namebuf, size_t namebuflen, > > + const char *dirname, const char *filename, > > + const char *ads_name) > > +{ > > + size_t totalsize; > > + > > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; > > + if (totalsize > namebuflen) { > > + return -1; > > + } > > + > > + /* > > + * NTFS ADS (Alternate Data Streams) name format: > > + * filename:ads_name > > + * e.g. > > + * d:\1.txt:my_ads_name > > + */ > > + strcpy(namebuf, dirname); > > + strcat(namebuf, "\\"); > > + strcat(namebuf, filename); > > + strcat(namebuf, ":"); > > + strcat(namebuf, ads_name); > > + > > + return 0; > > +} > > + > > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > > + char *fulladsname) > > +{ > > + char *p1, *p2; > > + > > + /* > > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > > + * :ads_name:$DATA > > + * e.g. > > + * :my_ads_name:$DATA > > + * > > + * ADS name from FindNextStreamW() always have ":$DATA" string at the end > > + * > > + * This function copy ADS name to namebuf. > > + */ > > + > > + p1 = strchr(fulladsname, ':'); > > + if (p1 == NULL) { > > + return -1; > > + } > > + > > + p2 = strchr(p1 + 1, ':'); > > + if (p2 == NULL) { > > + return -1; > > + } > > + > > + /* skip empty ads name */ > > + if (p2 - p1 == 1) { > > + return 0; > > + } > > + > > + if (p2 - p1 + 1 > namebuflen) { > > + return -1; > > + } > > + > > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > > + namebuf[p2 - p1 - 1] = '\0'; > > + > > + return p2 - p1; > > +} > > + > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size) > > +{ > > + HANDLE hStream; > > + char ADSFileName[NAME_MAX + 1] = {0}; > > + DWORD dwBytesRead; > > + > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > > + errno = EIO; > > + return -1; > > + } > > + > > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > > + if (hStream == INVALID_HANDLE_VALUE && > > + GetLastError() == ERROR_FILE_NOT_FOUND) { > > + errno = ENODATA; > > + return -1; > > + } > > + > > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > > + errno = EIO; > > + CloseHandle(hStream); > > + return -1; > > + } > > + > > + CloseHandle(hStream); > > + > > + return dwBytesRead; > > +} > > + > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > + char *list, size_t size) > > +{ > > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > > + char fulladsname[NAME_MAX + 1]; > > + char *full_fs_name = merge_fs_path(dirname, filename); > > + int ret; > > + HANDLE hFind; > > + WIN32_FIND_STREAM_DATA fsd; > > + BOOL bFindNext; > > + char *listptr = list; > > + size_t listleftsize = size; > > + > > + /* > > + * ADS emurate function only have WCHAR version, need to covert filename > > + * to WCHAR string. > > + */ > > + > > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > > + strlen(full_fs_name), WideCharStr, NAME_MAX); > > + g_free(full_fs_name); > > + if (ret == 0) { > > + errno = EIO; > > + return -1; > > + } > > + > > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); > > + if (hFind == INVALID_HANDLE_VALUE) { > > + errno = ENODATA; > > + return -1; > > + } > > + > > + do { > > + memset(fulladsname, 0, sizeof(fulladsname)); > > + > > + /* > > + * ADS emurate function only have WCHAR version, need to covert > > + * cStreamName to utf-8 string. > > + */ > > + > > + ret = WideCharToMultiByte(CP_UTF8, 0, > > + fsd.cStreamName, wcslen(fsd.cStreamName) + 1, > > + fulladsname, sizeof(fulladsname) - 1, > > + NULL, NULL); > > + > > + if (ret == 0) { > > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > > + errno = ERANGE; > > + } > > + CloseHandle(hFind); > > + return -1; > > + } > > + > > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > > + if (ret < 0) { > > + errno = ERANGE; > > + CloseHandle(hFind); > > + return -1; > > + } > > + > > + listptr = listptr + ret; > > + listleftsize = listleftsize - ret; > > + > > + bFindNext = FindNextStreamW(hFind, &fsd); > > + } while (bFindNext); > > + > > + CloseHandle(hFind); > > + > > + return size - listleftsize; > > +} > > + > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > + const char *name) > > +{ > > + char ADSFileName[NAME_MAX + 1] = {0}; > > + > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > > + errno = EIO; > > + return -1; > > + } > > + > > + if (DeleteFile(ADSFileName) != 0) { > > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > > + errno = ENODATA; > > + return -1; > > + } > > + } > > + > > + return 0; > > +} > > + > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > + const char *name, void *value, size_t size, int flags) > > +{ > > + HANDLE hStream; > > + char ADSFileName[NAME_MAX + 1] = {0}; > > + DWORD dwBytesWrite; > > + > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > > + errno = EIO; > > + return -1; > > + } > > + > > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, > > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); > > + if (hStream == INVALID_HANDLE_VALUE) { > > + errno = EIO; > > + return -1; > > + } > > + > > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { > > + errno = EIO; > > + CloseHandle(hStream); > > + return -1; > > + } > > + > > + CloseHandle(hStream); > > + > > + return 0; > > +} > > + > > +int qemu_mknodat(const char *dirname, const char *filename, > > + mode_t mode, dev_t dev) > > +{ > > + errno = ENOTSUP; > > + return -1; > > +} > > + > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > > +{ > > + HANDLE hFile; > > + char RealPath[NAME_MAX + 1]; > > + unsigned long SectorsPerCluster; > > + unsigned long BytesPerSector; > > + unsigned long NumberOfFreeClusters; > > + unsigned long TotalNumberOfClusters; > > + > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > + if (hFile == INVALID_HANDLE_VALUE) { > > + errno = EIO; > > + return -1; > > + } > > + > > + /* get real path of root */ > > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > > + FILE_NAME_NORMALIZED) == 0) { > > + errno = EIO; > > + CloseHandle(hFile); > > + return -1; > > + } > > + > > + CloseHandle(hFile); > > + > > + /* > > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > > + * "C:\\123" will be "\\\\?\\C:\\123" > > + * Skip first 4 bytes and truncate the string at offset 7, it will get > > + * the real root directory like "C:\\", this is parameter GetDiskFreeSpace > > + * needed. > > + */ > > + > > + RealPath[7] = '\0'; > > + > > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, > > + &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) { > > + errno = EIO; > > + return -1; > > + } > > + > > + stbuf->f_type = V9FS_MAGIC; > > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > > + stbuf->f_files = -1; > > + stbuf->f_ffree = -1; > > + stbuf->f_namelen = NAME_MAX; > > + stbuf->f_frsize = 0; > > + stbuf->f_flags = 0; > > + > > + return 0; > > +} > > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > > index 9ae69dd8db..5623f0e2ef 100644 > > --- a/hw/9pfs/9p-xattr.c > > +++ b/hw/9pfs/9p-xattr.c > > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > > char *orig_value, *orig_value_start; > > ssize_t xattr_len, parsed_len = 0, attr_len; > > char *dirpath, *name; > > +#ifdef CONFIG_WIN32 > > + char *full_dir_path; > > + DIR *dir; > > +#else > > int dirfd; > > +#endif > > > > /* Get the actual len */ > > dirpath = g_path_get_dirname(path); > > + > > +#ifdef CONFIG_WIN32 > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + return -1; > > + } > > + closedir(dir); > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + g_free(dirpath); > > + > > + name = g_path_get_basename(path); > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > > + if (xattr_len <= 0) { > > + g_free(name); > > + g_free(full_dir_path); > > + return xattr_len; > > + } > > + > > + /* Now fetch the xattr and find the actual size */ > > + orig_value = g_malloc(xattr_len); > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > > + xattr_len); > > + g_free(name); > > + g_free(full_dir_path); > > + if (xattr_len < 0) { > > + g_free(orig_value); > > + return -1; > > + } > > +#else > > dirfd = local_opendir_nofollow(ctx, dirpath); > > g_free(dirpath); > > if (dirfd == -1) { > > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > > g_free(orig_value); > > return -1; > > } > > +#endif > > > > /* store the orig pointer */ > > orig_value_start = orig_value; > > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > > const char *name, void *value, size_t size) > > { > > +#ifdef CONFIG_WIN32 > > + char *dirpath = g_path_get_dirname(path); > > + char *filename = g_path_get_basename(path); > > + char *full_dir_path; > > + DIR *dir; > > + ssize_t ret = -1; > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + closedir(dir); > > + > > + ret = fgetxattrat_nofollow(full_dir_path, filename, > > + name, value, size); > > + > > +out: > > + g_free(full_dir_path); > > + g_free(dirpath); > > + g_free(filename); > > + > > + return ret; > > +#else > > char *dirpath = g_path_get_dirname(path); > > char *filename = g_path_get_basename(path); > > int dirfd; > > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const char > *path, > > } > > > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > > + > > close_preserve_errno(dirfd); > > out: > > g_free(dirpath); > > g_free(filename); > > return ret; > > +#endif > > } > > > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const char > *path, > > const char *name, void *value, size_t size, > > int flags) > > { > > +#ifdef CONFIG_WIN32 > > + char *dirpath = g_path_get_dirname(path); > > + char *filename = g_path_get_basename(path); > > + char *full_dir_path; > > + DIR *dir; > > + ssize_t ret = -1; > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + closedir(dir); > > + > > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > > + value, size, flags); > > +out: > > + g_free(full_dir_path); > > + g_free(dirpath); > > + g_free(filename); > > + return ret; > > + > > +#else > > char *dirpath = g_path_get_dirname(path); > > char *filename = g_path_get_basename(path); > > int dirfd; > > @@ -210,6 +297,7 @@ out: > > g_free(dirpath); > > g_free(filename); > > return ret; > > +#endif > > } > > > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, > > @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char *path, const > char *name, void *value, > > ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, > > const char *name) > > { > > +#ifdef CONFIG_WIN32 > > + char *dirpath = g_path_get_dirname(path); > > + char *filename = g_path_get_basename(path); > > + char *full_dir_path; > > + DIR *dir; > > + ssize_t ret = -1; > > + > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > + > > + dir = local_opendir_nofollow(ctx, dirpath); > > + if (dir == NULL) { > > + goto out; > > + } > > + closedir(dir); > > + > > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > > + > > +out: > > + g_free(full_dir_path); > > + g_free(dirpath); > > + g_free(filename); > > + return ret; > > + > > +#else > > char *dirpath = g_path_get_dirname(path); > > char *filename = g_path_get_basename(path); > > int dirfd; > > @@ -237,6 +349,7 @@ out: > > g_free(dirpath); > > g_free(filename); > > return ret; > > +#endif > > } > > > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > > index 225f31fc31..a04889c1d6 100644 > > --- a/hw/9pfs/9p.c > > +++ b/hw/9pfs/9p.c > > @@ -38,6 +38,10 @@ > > #include "migration/blocker.h" > > #include "qemu/xxhash.h" > > #include <math.h> > > +#ifdef CONFIG_WIN32 > > +#define UTIME_NOW ((1l << 30) - 1l) > > +#define UTIME_OMIT ((1l << 30) - 2l) > > +#endif > > > > int open_fd_hw; > > int total_open_fd; > > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, > V9fsQID *qidp) > > if (S_ISDIR(stbuf->st_mode)) { > > qidp->type |= P9_QID_TYPE_DIR; > > } > > +#ifndef CONFIG_WIN32 > > if (S_ISLNK(stbuf->st_mode)) { > > qidp->type |= P9_QID_TYPE_SYMLINK; > > } > > +#endif > > > > return 0; > > } > > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) > > ret |= S_IFDIR; > > } > > > > +#ifndef CONFIG_WIN32 > > if (mode & P9_STAT_MODE_SYMLINK) { > > ret |= S_IFLNK; > > } > > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) > > if (mode & P9_STAT_MODE_NAMED_PIPE) { > > ret |= S_IFIFO; > > } > > +#endif > > if (mode & P9_STAT_MODE_DEVICE) { > > if (extension->size && extension->data[0] == 'c') { > > ret |= S_IFCHR; > > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) > > ret |= S_IFREG; > > } > > > > +#ifndef CONFIG_WIN32 > > if (mode & P9_STAT_MODE_SETUID) { > > ret |= S_ISUID; > > } > > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > *extension) > > if (mode & P9_STAT_MODE_SETVTX) { > > ret |= S_ISVTX; > > } > > +#endif > > > > return ret; > > } > > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > mode |= P9_STAT_MODE_DIR; > > } > > > > +#ifndef CONFIG_WIN32 > > if (S_ISLNK(stbuf->st_mode)) { > > mode |= P9_STAT_MODE_SYMLINK; > > } > > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > if (S_ISFIFO(stbuf->st_mode)) { > > mode |= P9_STAT_MODE_NAMED_PIPE; > > } > > +#endif > > > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > > mode |= P9_STAT_MODE_DEVICE; > > } > > > > +#ifndef CONFIG_WIN32 > > if (stbuf->st_mode & S_ISUID) { > > mode |= P9_STAT_MODE_SETUID; > > } > > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > if (stbuf->st_mode & S_ISVTX) { > > mode |= P9_STAT_MODE_SETVTX; > > } > > +#endif > > > > return mode; > > } > > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, > V9fsPath *path, > > return err; > > } > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > > +#ifndef CONFIG_WIN32 > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > > +#else > > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > + 0, 0); > > +#endif > > + > > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, > int32_t blksize) > > > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > > { > > +#ifndef CONFIG_WIN32 > > return blksize_to_iounit(pdu, stbuf->st_blksize); > > +#else > > + return blksize_to_iounit(pdu, 0); > > +#endif > > } > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct > stat *stbuf, > > v9lstat->st_gid = stbuf->st_gid; > > v9lstat->st_rdev = stbuf->st_rdev; > > v9lstat->st_size = stbuf->st_size; > > + > > +#ifdef CONFIG_WIN32 > > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > + v9lstat->st_blocks = 0; > > + v9lstat->st_atime_sec = stbuf->st_atime; > > + v9lstat->st_mtime_sec = stbuf->st_mtime; > > + v9lstat->st_ctime_sec = stbuf->st_ctime; > > +#else /* !CONFIG_WIN32 */ > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > v9lstat->st_blocks = stbuf->st_blocks; > > v9lstat->st_atime_sec = stbuf->st_atime; > > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct > stat *stbuf, > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > > #endif > > +#endif /* CONFIG_WIN32 */ > > + > > /* Currently we only support BASIC fields in stat */ > > v9lstat->st_result_mask = P9_STATS_BASIC; > > > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > > > static bool same_stat_id(const struct stat *a, const struct stat *b) > > { > > +#ifdef CONFIG_WIN32 > > + return 0; > > +#else > > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > > +#endif /* CONFIG_WIN32 */ > > } > > > > static void coroutine_fn v9fs_walk(void *opaque) > > @@ -2300,7 +2339,11 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU > *pdu, > > count += len; > > v9fs_stat_free(&v9stat); > > v9fs_path_free(&path); > > +#ifndef CONFIG_WIN32 > > saved_dir_pos = qemu_dirent_off(dent); > > +#else > > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > > +#endif > > } > > > > v9fs_readdir_unlock(&fidp->fs.dir); > > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, > V9fsFidState *fidp, > > qid.version = 0; > > } > > > > +#ifdef CONFIG_WIN32 > > + /* > > + * Windows does not have dent->d_off, get offset by calling telldir() > > + * manually. > > + */ > > + off = v9fs_co_telldir(pdu, fidp); > > +#else > > off = qemu_dirent_off(dent); > > +#endif > > v9fs_string_init(&name); > > v9fs_string_sprintf(&name, "%s", dent->d_name); > > > > +#ifdef CONFIG_WIN32 > > + /* > > + * Windows does not have dent->d_type > > + */ > > + > > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > > + &qid, off, > > + 0, &name); > > +#else > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > > len = pdu_marshal(pdu, 11 + count, "Qqbs", > > &qid, off, > > dent->d_type, &name); > > +#endif > > > > v9fs_string_free(&name); > > > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > > } > > > > nmode |= perm & 0777; > > + > > +#ifndef CONFIG_WIN32 > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > makedev(major, minor), nmode, &stbuf); > > +#else > > + err = -ENOTSUP; > > +#endif > > + > > if (err < 0) { > > goto out; > > } > > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > > v9fs_path_copy(&fidp->path, &path); > > v9fs_path_unlock(s); > > } else if (perm & P9_STAT_MODE_SOCKET) { > > +#ifndef CONFIG_WIN32 > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > 0, S_IFSOCK | (perm & 0777), &stbuf); > > +#else > > + err = -ENOTSUP; > > +#endif > > if (err < 0) { > > goto out; > > } > > @@ -3600,6 +3671,7 @@ out_nofid: > > static void coroutine_fn v9fs_mknod(void *opaque) > > { > > > > +#ifndef CONFIG_WIN32 > > int mode; > > gid_t gid; > > int32_t fid; > > @@ -3656,6 +3728,11 @@ out: > > out_nofid: > > pdu_complete(pdu, err); > > v9fs_string_free(&name); > > +#else > > + V9fsPDU *pdu = opaque; > > + > > + pdu_complete(pdu, -1); > > +#endif > > } > > > > /* > > @@ -3928,7 +4005,7 @@ out_nofid: > > #if defined(CONFIG_LINUX) > > /* Currently, only Linux has XATTR_SIZE_MAX */ > > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > > -#elif defined(CONFIG_DARWIN) > > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > > /* > > * Darwin doesn't seem to define a maximum xattr size in its user > > * space header, so manually configure it across platforms as 64k. > > @@ -3945,6 +4022,7 @@ out_nofid: > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > > { > > +#ifndef CONFIG_WIN32 > > int flags, rflags = 0; > > int32_t fid; > > uint64_t size; > > @@ -4006,10 +4084,15 @@ out_put_fid: > > out_nofid: > > pdu_complete(pdu, err); > > v9fs_string_free(&name); > > +#else > > + V9fsPDU *pdu = opaque; > > + pdu_complete(pdu, -1); > > +#endif > > } > > > > static void coroutine_fn v9fs_readlink(void *opaque) > > { > > +#ifndef CONFIG_WIN32 > > V9fsPDU *pdu = opaque; > > size_t offset = 7; > > V9fsString target; > > @@ -4045,6 +4128,10 @@ out: > > put_fid(pdu, fidp); > > out_nofid: > > pdu_complete(pdu, err); > > +#else > > + V9fsPDU *pdu = opaque; > > + pdu_complete(pdu, -1); > > +#endif > > } > > > > static CoroutineEntry *pdu_co_handlers[] = { > > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > > { > > +#ifndef CONFIG_WIN32 > > struct rlimit rlim; > > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > > error_report("Failed to get the resource limit"); > > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) > v9fs_set_fd_limit(void) > > } > > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > > open_fd_rc = rlim.rlim_cur / 2; > > +#endif > > } > > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > > index 93ba44fb75..2fbe7b831b 100644 > > --- a/hw/9pfs/codir.c > > +++ b/hw/9pfs/codir.c > > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > int len, err = 0; > > int32_t size = 0; > > off_t saved_dir_pos; > > +#ifdef CONFIG_WIN32 > > + off_t next_dir_pos; > > +#endif > > struct dirent *dent; > > struct V9fsDirEnt *e = NULL; > > V9fsPath path; > > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > break; > > } > > > > +#ifdef CONFIG_WIN32 > > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > > + if (next_dir_pos < 0) { > > + err = next_dir_pos; > > + goto out; > > + } > > +#endif > > + > > /* > > * stop this loop as soon as it would exceed the allowed maximum > > * response message size for the directory entries collected so far, > > @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > } > > > > size += len; > > +#ifndef CONFIG_WIN32 > > saved_dir_pos = qemu_dirent_off(dent); > > +#else > > + saved_dir_pos = next_dir_pos; > > +#endif > > } > > > > /* restore (last) saved position */ ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-09 15:09 ` Shi, Guohuai @ 2022-05-09 16:20 ` Greg Kurz 2022-05-10 2:13 ` Shi, Guohuai 0 siblings, 1 reply; 34+ messages in thread From: Greg Kurz @ 2022-05-09 16:20 UTC (permalink / raw) To: Shi, Guohuai; +Cc: Bin Meng, Christian Schoenebeck, qemu-devel, Meng, Bin On Mon, 9 May 2022 15:09:46 +0000 "Shi, Guohuai" <Guohuai.Shi@windriver.com> wrote: > > > > -----Original Message----- > > From: Greg Kurz <groug@kaod.org> > > Sent: 2022年5月9日 22:29 > > To: Bin Meng <bmeng.cn@gmail.com> > > Cc: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org; Shi, > > Guohuai <Guohuai.Shi@windriver.com>; Meng, Bin <Bin.Meng@windriver.com> > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > > Windows > > > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > > > On Mon, 25 Apr 2022 22:27:01 +0800 > > Bin Meng <bmeng.cn@gmail.com> wrote: > > > > > From: Guohuai Shi <guohuai.shi@windriver.com> > > > > > > Add a 9p local file system backend driver to support Windows, > > > including open, read, write, close, rename, remove, etc. > > > > > > All security models are supported. The mapped (mapped-xattr) > > > security model is implemented using NTFS Alternate Data Stream > > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > > --- > > > > > > > Hi ! > > > > I tend to agree with Christian's remarks that this patch is too big > > and that the choice of introducing right away a new implementation > > of 9p-local for windows hosts is too bold to start with. We need to > > clearly understand what's diverging between windows and linux in order > > to make such a decision. You should first try to introduce the required > > abstractions to cope with these differences, so that we can review. > > > > Here is the basic introductions of 9PFS for Windows development: > > Windows always returns -1 when try to call open() for a directory. > Windows (actually MinGW library) only allows opendir() for a directory. Does MinGW have dirfd() ? > Windows does not support APIs like "*at" (openat(), renameat(), etc.) Ouch... > So 9PFS can not use any openat() for opening a sub file or directory in 9P mount directory. > This commit use merge_fs_path() to build up full filename by string concatenation. > I know that may have a risk of security, but Windows does fully support POSIX APIs. > s/does/doesn't ? I understand from your various answers that symlinks aren't currently supported by window's POSIX API. Is this forever ? Google do mentions symlinks in windows 10. What's the story there ? How do they behave ? How would they be exposed to the client ? Be aware that, even if the client cannot create symlinks, an existing symlink could be used to escape with rename(). If the code "may have a risk of security" then it must be fixed or avoided in some way before being merged upstream. Other thing that comes to mind is that windows hosts should maybe use the mapped or mapped-file security modes since they emulate symlinks with a simple file hidden in the VIRTFS_META_DIR directory. Cheers, -- Greg > > Some inlined remarks below anyway. > > > > > hw/9pfs/9p-linux-errno.h | 151 +++++ > > > hw/9pfs/9p-local.h | 4 + > > > hw/9pfs/9p-util.h | 41 ++ > > > hw/9pfs/9p.h | 23 + > > > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > > > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > > > hw/9pfs/9p-xattr.c | 113 ++++ > > > hw/9pfs/9p.c | 91 ++- > > > hw/9pfs/codir.c | 15 + > > > 9 files changed, 1982 insertions(+), 1 deletion(-) > > > create mode 100644 hw/9pfs/9p-linux-errno.h > > > create mode 100644 hw/9pfs/9p-local-win32.c > > > create mode 100644 hw/9pfs/9p-util-win32.c > > > > > > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > > > new file mode 100644 > > > index 0000000000..b0d6ac45ac > > > --- /dev/null > > > +++ b/hw/9pfs/9p-linux-errno.h > > > @@ -0,0 +1,151 @@ > > > +/* > > > + * 9p Linux errno translation definition > > > + * > > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > > + * See the COPYING file in the top-level directory. > > > + */ > > > + > > > +#include <errno.h> > > > + > > > +#ifndef QEMU_9P_LINUX_ERRNO_H > > > +#define QEMU_9P_LINUX_ERRNO_H > > > + > > > +/* > > > + * This file contains the Linux errno definitions to translate errnos set by > > > + * the 9P server (running on Windows) to a corresponding errno value. > > > + * > > > + * This list should be periodically reviewed and updated; particularly for > > > + * errnos that might be set as a result of a file system operation. > > > + */ > > > + > > > +#define L_EPERM 1 /* Operation not permitted */ > > > +#define L_ENOENT 2 /* No such file or directory */ > > > +#define L_ESRCH 3 /* No such process */ > > > +#define L_EINTR 4 /* Interrupted system call */ > > > +#define L_EIO 5 /* I/O error */ > > > +#define L_ENXIO 6 /* No such device or address */ > > > +#define L_E2BIG 7 /* Argument list too long */ > > > +#define L_ENOEXEC 8 /* Exec format error */ > > > +#define L_EBADF 9 /* Bad file number */ > > > +#define L_ECHILD 10 /* No child processes */ > > > +#define L_EAGAIN 11 /* Try again */ > > > +#define L_ENOMEM 12 /* Out of memory */ > > > +#define L_EACCES 13 /* Permission denied */ > > > +#define L_EFAULT 14 /* Bad address */ > > > +#define L_ENOTBLK 15 /* Block device required */ > > > +#define L_EBUSY 16 /* Device or resource busy */ > > > +#define L_EEXIST 17 /* File exists */ > > > +#define L_EXDEV 18 /* Cross-device link */ > > > +#define L_ENODEV 19 /* No such device */ > > > +#define L_ENOTDIR 20 /* Not a directory */ > > > +#define L_EISDIR 21 /* Is a directory */ > > > +#define L_EINVAL 22 /* Invalid argument */ > > > +#define L_ENFILE 23 /* File table overflow */ > > > +#define L_EMFILE 24 /* Too many open files */ > > > +#define L_ENOTTY 25 /* Not a typewriter */ > > > +#define L_ETXTBSY 26 /* Text file busy */ > > > +#define L_EFBIG 27 /* File too large */ > > > +#define L_ENOSPC 28 /* No space left on device */ > > > +#define L_ESPIPE 29 /* Illegal seek */ > > > +#define L_EROFS 30 /* Read-only file system */ > > > +#define L_EMLINK 31 /* Too many links */ > > > +#define L_EPIPE 32 /* Broken pipe */ > > > +#define L_EDOM 33 /* Math argument out of domain of func */ > > > +#define L_ERANGE 34 /* Math result not representable */ > > > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > > > +#define L_ENAMETOOLONG 36 /* File name too long */ > > > +#define L_ENOLCK 37 /* No record locks available */ > > > +#define L_ENOSYS 38 /* Function not implemented */ > > > +#define L_ENOTEMPTY 39 /* Directory not empty */ > > > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > > > +#define L_ENOMSG 42 /* No message of desired type */ > > > +#define L_EIDRM 43 /* Identifier removed */ > > > +#define L_ECHRNG 44 /* Channel number out of range */ > > > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > > > +#define L_EL3HLT 46 /* Level 3 halted */ > > > +#define L_EL3RST 47 /* Level 3 reset */ > > > +#define L_ELNRNG 48 /* Link number out of range */ > > > +#define L_EUNATCH 49 /* Protocol driver not attached */ > > > +#define L_ENOCSI 50 /* No CSI structure available */ > > > +#define L_EL2HLT 51 /* Level 2 halted */ > > > +#define L_EBADE 52 /* Invalid exchange */ > > > +#define L_EBADR 53 /* Invalid request descriptor */ > > > +#define L_EXFULL 54 /* Exchange full */ > > > +#define L_ENOANO 55 /* No anode */ > > > +#define L_EBADRQC 56 /* Invalid request code */ > > > +#define L_EBADSLT 57 /* Invalid slot */ > > > +#define L_EBFONT 58 /* Bad font file format */ > > > +#define L_ENOSTR 59 /* Device not a stream */ > > > +#define L_ENODATA 61 /* No data available */ > > > +#define L_ETIME 62 /* Timer expired */ > > > +#define L_ENOSR 63 /* Out of streams resources */ > > > +#define L_ENONET 64 /* Machine is not on the network */ > > > +#define L_ENOPKG 65 /* Package not installed */ > > > +#define L_EREMOTE 66 /* Object is remote */ > > > +#define L_ENOLINK 67 /* Link has been severed */ > > > +#define L_EADV 68 /* Advertise error */ > > > +#define L_ESRMNT 69 /* Srmount error */ > > > +#define L_ECOMM 70 /* Communication error on send */ > > > +#define L_EPROTO 71 /* Protocol error */ > > > +#define L_EMULTIHOP 72 /* Multihop attempted */ > > > +#define L_EDOTDOT 73 /* RFS specific error */ > > > +#define L_EBADMSG 74 /* Not a data message */ > > > +#define L_EOVERFLOW 75 /* Value too large for defined data type */ > > > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > > > +#define L_EBADFD 77 /* File descriptor in bad state */ > > > +#define L_EREMCHG 78 /* Remote address changed */ > > > +#define L_ELIBACC 79 /* Can not access a needed shared library */ > > > +#define L_ELIBBAD 80 /* Accessing a corrupted shared library */ > > > +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > > > +#define L_ELIBMAX 82 /* Attempting to link in too many shared libs > > */ > > > +#define L_ELIBEXEC 83 /* Cannot exec a shared library directly */ > > > +#define L_EILSEQ 84 /* Illegal byte sequence */ > > > +#define L_ERESTART 85 /* Interrupted system call should be restarted > > */ > > > +#define L_ESTRPIPE 86 /* Streams pipe error */ > > > +#define L_EUSERS 87 /* Too many users */ > > > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > > > +#define L_EDESTADDRREQ 89 /* Destination address required */ > > > +#define L_EMSGSIZE 90 /* Message too long */ > > > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > > > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > > > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > > > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > > > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport endpoint > > */ > > > +#define L_EPFNOSUPPORT 96 /* Protocol family not supported */ > > > +#define L_EAFNOSUPPORT 97 /* Address family not supported by protocol */ > > > +#define L_EADDRINUSE 98 /* Address already in use */ > > > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > > > +#define L_ENETDOWN 100 /* Network is down */ > > > +#define L_ENETUNREACH 101 /* Network is unreachable */ > > > +#define L_ENETRESET 102 /* Network dropped connection because of reset > > */ > > > +#define L_ECONNABORTED 103 /* Software caused connection abort */ > > > +#define L_ECONNRESET 104 /* Connection reset by peer */ > > > +#define L_ENOBUFS 105 /* No buffer space available */ > > > +#define L_EISCONN 106 /* Transport endpoint is already connected */ > > > +#define L_ENOTCONN 107 /* Transport endpoint is not connected */ > > > +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown > > */ > > > +#define L_ETOOMANYREFS 109 /* Too many references: cannot splice */ > > > +#define L_ETIMEDOUT 110 /* Connection timed out */ > > > +#define L_ECONNREFUSED 111 /* Connection refused */ > > > +#define L_EHOSTDOWN 112 /* Host is down */ > > > +#define L_EHOSTUNREACH 113 /* No route to host */ > > > +#define L_EALREADY 114 /* Operation already in progress */ > > > +#define L_EINPROGRESS 115 /* Operation now in progress */ > > > +#define L_ESTALE 116 /* Stale NFS file handle */ > > > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > > > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > > > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > > > +#define L_EISNAM 120 /* Is a named type file */ > > > +#define L_EREMOTEIO 121 /* Remote I/O error */ > > > +#define L_EDQUOT 122 /* Quota exceeded */ > > > +#define L_ENOMEDIUM 123 /* No medium found */ > > > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > > > +#define L_ECANCELED 125 /* Operation Canceled */ > > > +#define L_ENOKEY 126 /* Required key not available */ > > > +#define L_EKEYEXPIRED 127 /* Key has expired */ > > > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > > > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > > > +#define L_EOWNERDEAD 130 /* Owner died */ > > > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > > > + > > > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > > > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > > > index 55ea4b7883..4c75876e19 100644 > > > --- a/hw/9pfs/9p-local.h > > > +++ b/hw/9pfs/9p-local.h > > > @@ -31,6 +31,10 @@ static inline bool local_is_mapped_file_metadata(FsContext > > *fs_ctx, > > > > > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > > mode_t mode); > > > +#ifndef CONFIG_WIN32 > > > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > > +#else > > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > > +#endif > > > > > > > Hrm... Returning a DIR * instead of a file descriptor looks like this > > is a completely different function with different users, but you're > > keeping the same name. What's happening here ? > > > > As I mentioned above, Windows does not allow open() for a directory (return int type file descriptor), only allows opendir() for a directory (return DIR*) > > > > #endif > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > > > index 97e681e167..6eadb38e1d 100644 > > > --- a/hw/9pfs/9p-util.h > > > +++ b/hw/9pfs/9p-util.h > > > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > > > errno = serrno; > > > } > > > > > > +#ifndef CONFIG_WIN32 > > > static inline int openat_dir(int dirfd, const char *name) > > > { > > > return openat(dirfd, name, > > > @@ -89,7 +90,9 @@ again: > > > errno = serrno; > > > return fd; > > > } > > > +#endif /* !CONFIG_WIN32 */ > > > > > > +#ifndef CONFIG_WIN32 > > > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > > > void *value, size_t size); > > > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > > > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char > > *filename, > > > char *list, size_t size); > > > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > > > const char *name); > > > +#else > > > + > > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > > + const char *name, void *value, size_t size); > > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > > + const char *name, void *value, size_t size, > > > + int flags); > > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > > + char *list, size_t size); > > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > > + const char *name); > > > + > > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > > > + > > > +static inline char *merge_fs_path(const char *path1, const char *path2) > > > +{ > > > + char *full_fs_path; > > > + size_t full_fs_path_len; > > > + > > > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > > > + full_fs_path = g_malloc(full_fs_path_len); > > > > > > + strcpy(full_fs_path, path1); > > > + strcat(full_fs_path, "\\"); > > > + strcat(full_fs_path, path2); > > > + > > > + return full_fs_path; > > > +} > > > + > > > +#endif /* !CONFIG_WIN32 */ > > > + > > > +#ifndef CONFIG_WIN32 > > > /* > > > * Darwin has d_seekoff, which appears to function similarly to d_off. > > > * However, it does not appear to be supported on all file systems, > > > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > > > return dent->d_off; > > > #endif > > > } > > > +#endif /* !CONFIG_WIN32 */ > > > > > > /** > > > * qemu_dirent_dup() - Duplicate directory entry @dent. > > > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct dirent > > *dent) > > > #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > > > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > > > #endif > > > + > > > +#ifndef CONFIG_WIN32 > > > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > > > +#else > > > +int qemu_mknodat(const char *dirname, const char *filename, > > > + mode_t mode, dev_t dev); > > > > Same remark as local_opendir_nofollow(). Especially, this clearly deviates > > from the mknodat(2) semantics : dirfd is supposed to be a handle on an > > existing directory, while dirname cannot provide such a guarantee. > > Windows does not have APIs like *at: openat(), mknodat(), etc. > > > > > > +#endif > > > > > > #endif > > > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > > > index 994f952600..87e8eac840 100644 > > > --- a/hw/9pfs/9p.h > > > +++ b/hw/9pfs/9p.h > > > @@ -3,13 +3,36 @@ > > > > > > #include <dirent.h> > > > #include <utime.h> > > > +#ifndef CONFIG_WIN32 > > > #include <sys/resource.h> > > > +#endif > > > #include "fsdev/file-op-9p.h" > > > #include "fsdev/9p-iov-marshal.h" > > > #include "qemu/thread.h" > > > #include "qemu/coroutine.h" > > > #include "qemu/qht.h" > > > > > > +#ifdef CONFIG_WIN32 > > > + > > > +#define O_NOCTTY 0 > > > +#define O_NDELAY 0 > > > +#define O_NONBLOCK 0 > > > +#define O_DSYNC 0 > > > +#define O_DIRECT 0 > > > +#define O_NOFOLLOW 0 > > > +#define O_NOATIME 0 > > > +#define O_SYNC 0 > > > +#define O_ASYNC 0 > > > +#define O_DIRECTORY 02000000 > > > + > > > +#define FASYNC 0 > > > + > > > +#define AT_REMOVEDIR 1 > > > + > > > +#define NAME_MAX 260 > > > + > > > +#endif > > > + > > > enum { > > > P9_TLERROR = 6, > > > P9_RLERROR, > > > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > > > new file mode 100644 > > > index 0000000000..aab7c9f7b5 > > > --- /dev/null > > > +++ b/hw/9pfs/9p-local-win32.c > > > @@ -0,0 +1,1242 @@ > > > +/* > > > + * 9p Windows callback > > > + * > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > + * > > > + * Based on hw/9pfs/9p-local.c > > > + * > > > + * This work is licensed under the terms of the GNU GPL, version 2. See > > > + * the COPYING file in the top-level directory. > > > + */ > > > + > > > +/* > > > + * Not so fast! You might want to read the 9p developer docs first: > > > + * https://wiki.qemu.org/Documentation/9p > > > + */ > > > + > > > +#include "qemu/osdep.h" > > > +#include <windows.h> > > > +#include <dirent.h> > > > +#include "9p.h" > > > +#include "9p-local.h" > > > +#include "9p-xattr.h" > > > +#include "9p-util.h" > > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > > +#include "qapi/error.h" > > > +#include "qemu/cutils.h" > > > +#include "qemu/error-report.h" > > > +#include "qemu/option.h" > > > +#include <libgen.h> > > > + > > > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > > > + int flags, mode_t mode) > > > +{ > > > + char *full_file_name; > > > + int fd; > > > + > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > > + fd = open(full_file_name, flags | _O_BINARY, mode); > > > > Hmm... in order to fix CVE-2016-9602, we had to ban path based syscalls > > because a malicious guest could easily escape the file system with a > > symlink attack. So now we basically split all paths we get from the > > client so that we only call *at() syscalls with a single path element > > and a NOFOLLOW flag. > > > > This function seems to be doing the very opposite, which is clearly > > a no go. > > Windows does not have openat(), so the only way is to build up full filename by merge_fs_path(). > Additionally, Windows does not support symbolic link APIs in POSIX library, so there may not be any security risks. > > > > > > + g_free(full_file_name); > > > + > > > + return fd; > > > +} > > > + > > > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > > > +{ > > > + char *full_file_name; > > > + DIR *dir; > > > + > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > > + dir = opendir(full_file_name); > > > > Same remark here. > > Same as above. > > > > > > + g_free(full_file_name); > > > + return dir; > > > +} > > > + > > > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > > + mode_t mode) > > > +{ > > > + int fd; > > > + > > > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { > > > > Why special casing '/' ? > > Windows does not allow open() for a directory, or any filename format like a directory. > > > > > > + /* Windows does not allow call open() for a directory */ > > > + fd = -1; > > > > This -1 will ultimately cause a Rlerror message to be returned > > to the client : an errno should be set here... but anyway, this > > isn't the way to do it : you should just call open() and let > > windows set the errno. > > > > Now, the major problem is that we really need to be able to > > open directories because of CVE-2016-9602. Isn't it possible > > to achieve it in some other way, e.g. opendir() and dirfd() ? > > As I mentioned above, Windows does not support fd (file descriptor) for directory open(). > > > > > > + } else { > > > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); > > > > Does this honor the NOFOLLOW semantics ? It seems not...which is again > > a no go. You cannot blindly assume how the underlying file system > > handles symlinks. > > > > Windows does not support symbolic link in POSIX APIs, > > > > + } > > > + > > > + return fd; > > > +} > > > + > > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > > > +{ > > > + return opendir_with_ctx(fs_ctx, path); > > > +} > > > + > > > +static FILE *local_fopenat(const char *dirname, const char *name, > > > + const char *mode) > > > +{ > > > + char *full_file_name; > > > + char modestr[3] = {0}; > > > + FILE *fp; > > > + > > > + /* > > > + * only supports two modes > > > + */ > > > + if (mode[0] == 'r') { > > > + modestr[0] = 'r'; > > > + } else if (mode[0] == 'w') { > > > + modestr[0] = 'w'; > > > + } else { > > > + return NULL; > > > + } > > > + /* Windows host needs 'b' flag */ > > > + modestr[1] = 'b'; > > > + > > > + full_file_name = merge_fs_path(dirname, name); > > > + fp = fopen(full_file_name, modestr); > > > + g_free(full_file_name); > > > + > > > + return fp; > > > +} > > > + > > > +static void local_mapped_file_attr(const char *dirpath, const char *name, > > > + struct stat *stbuf) > > > +{ > > > + FILE *fp; > > > + char buf[ATTR_MAX]; > > > + char *full_file_name; > > > + > > > + if (strcmp(name, ".") != 0) { > > > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > > > + fp = local_fopenat(full_file_name, name, "r"); > > > + g_free(full_file_name); > > > + } else { > > > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > > > + } > > > + if (!fp) { > > > + return; > > > + } > > > + > > > + memset(buf, 0, ATTR_MAX); > > > + while (fgets(buf, ATTR_MAX, fp)) { > > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > > + stbuf->st_uid = atoi(buf + 11); > > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > > + stbuf->st_gid = atoi(buf + 11); > > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > > + stbuf->st_rdev = atoi(buf + 12); > > > + } > > > + memset(buf, 0, ATTR_MAX); > > > + } > > > + fclose(fp); > > > +} > > > + > > > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat > > *stbuf) > > > +{ > > > + int err = -1; > > > + char *full_dir_name, *full_file_name; > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > + char *name = g_path_get_basename(fs_path->data); > > > + > > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > + err = stat(full_file_name, stbuf); > > > + > > > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > > > + /* > > > + * Hard code for root directory on Windows host. > > > + * This will root directory have a special inode number, > > > + * then guest OS can detect it is a special directory. > > > + */ > > > + stbuf->st_ino = 2; > > > + } > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > + /* Actual credentials are part of extended attrs */ > > > + uid_t tmp_uid; > > > + gid_t tmp_gid; > > > + mode_t tmp_mode; > > > + dev_t tmp_dev; > > > + > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > > > + &tmp_uid, sizeof(uid_t)) > 0) { > > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > > + } > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > > > + &tmp_gid, sizeof(gid_t)) > 0) { > > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > > + } > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > > > + &tmp_mode, sizeof(mode_t)) > 0) { > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > > + } > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > > > + &tmp_dev, sizeof(dev_t)) > 0) { > > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > > + } > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + local_mapped_file_attr(full_dir_name, name, stbuf); > > > + } > > > + > > > + g_free(full_file_name); > > > + g_free(full_dir_name); > > > + > > > + if (err) { > > > + goto err_out; > > > + } > > > + > > > +err_out: > > > + g_free(name); > > > + g_free(dirpath); > > > + return err; > > > +} > > > + > > > +static int local_set_mapped_file_attrat(const char *dirname, const char *name, > > > + FsCred *credp) > > > +{ > > > + FILE *fp; > > > + int ret; > > > + char buf[ATTR_MAX]; > > > + int uid = -1, gid = -1, mode = -1, rdev = -1; > > > + bool is_root = !strcmp(name, "."); > > > + char *full_dir_name; > > > + > > > + if (is_root) { > > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > > > + if (!fp) { > > > + if (errno == ENOENT) { > > > + goto update_map_file; > > > + } else { > > > + return -1; > > > + } > > > + } > > > + } else { > > > + /* > > > + * mapped-file: > > > + * <sub_file> attribute stored to: > > > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > > > + */ > > > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > > > + ret = mkdir(full_dir_name); > > > + > > > + if (ret < 0 && errno != EEXIST) { > > > + g_free(full_dir_name); > > > + return -1; > > > + } > > > + > > > + fp = local_fopenat(full_dir_name, name, "r"); > > > + if (!fp) { > > > + if (errno == ENOENT) { > > > + goto update_map_file; > > > + } else { > > > + g_free(full_dir_name); > > > + return -1; > > > + } > > > + } > > > + } > > > + > > > + memset(buf, 0, ATTR_MAX); > > > + while (fgets(buf, ATTR_MAX, fp)) { > > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > > + uid = atoi(buf + 11); > > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > > + gid = atoi(buf + 11); > > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > > + mode = atoi(buf + 12); > > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > > + rdev = atoi(buf + 12); > > > + } > > > + memset(buf, 0, ATTR_MAX); > > > + } > > > + fclose(fp); > > > + > > > +update_map_file: > > > + if (is_root) { > > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > > > + } else { > > > + fp = local_fopenat(full_dir_name, name, "w"); > > > + g_free(full_dir_name); > > > + } > > > + if (!fp) { > > > + return -1; > > > + } > > > + > > > + if (credp->fc_uid != -1) { > > > + uid = credp->fc_uid; > > > + } > > > + if (credp->fc_gid != -1) { > > > + gid = credp->fc_gid; > > > + } > > > + if (credp->fc_mode != (mode_t)-1) { > > > + mode = credp->fc_mode; > > > + } > > > + if (credp->fc_rdev != -1) { > > > + rdev = credp->fc_rdev; > > > + } > > > + > > > + if (uid != -1) { > > > + fprintf(fp, "virtfs.uid=%d\n", uid); > > > + } > > > + if (gid != -1) { > > > + fprintf(fp, "virtfs.gid=%d\n", gid); > > > + } > > > + if (mode != -1) { > > > + fprintf(fp, "virtfs.mode=%d\n", mode); > > > + } > > > + if (rdev != -1) { > > > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > > > + } > > > + fclose(fp); > > > + > > > + return 0; > > > +} > > > + > > > +static int local_set_xattrat(const char *dirname, const char *path, > > > + FsCred *credp) > > > +{ > > > + int err; > > > + > > > + if (credp->fc_uid != -1) { > > > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > > > + &tmp_uid, sizeof(uid_t), 0); > > > + if (err) { > > > + return err; > > > + } > > > + } > > > + if (credp->fc_gid != -1) { > > > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > > > + &tmp_gid, sizeof(gid_t), 0); > > > + if (err) { > > > + return err; > > > + } > > > + } > > > + if (credp->fc_mode != (mode_t)-1) { > > > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > > > + &tmp_mode, sizeof(mode_t), 0); > > > + if (err) { > > > + return err; > > > + } > > > + } > > > + if (credp->fc_rdev != -1) { > > > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > > > + &tmp_rdev, sizeof(dev_t), 0); > > > + if (err) { > > > + return err; > > > + } > > > + } > > > + return 0; > > > +} > > > + > > > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > > > + char *buf, size_t bufsz) > > > +{ > > > + return -1; > > > +} > > > + > > > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > > > +{ > > > + return close(fs->fd); > > > +} > > > + > > > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > > > +{ > > > + return closedir(fs->dir.stream); > > > +} > > > + > > > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > > > + int flags, V9fsFidOpenState *fs) > > > +{ > > > + int fd; > > > + > > > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > > > + if (fd == -1) { > > > + return -1; > > > + } > > > + fs->fd = fd; > > > + return fs->fd; > > > +} > > > + > > > +static int local_opendir(FsContext *ctx, > > > + V9fsPath *fs_path, V9fsFidOpenState *fs) > > > +{ > > > + DIR *stream; > > > + char *full_file_name; > > > + > > > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > > > + stream = opendir(full_file_name); > > > + g_free(full_file_name); > > > + > > > + if (!stream) { > > > + return -1;local_opendir_nofollowlocal_opendir_nofollow > > > + } > > > + > > > + fs->dir.stream = stream; > > > + return 0; > > > +} > > > + > > > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > > > +{ > > > + rewinddir(fs->dir.stream); > > > +} > > > + > > > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > > > +{ > > > + return telldir(fs->dir.stream); > > > +} > > > + > > > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > > > +{local_opendir_nofollow > > > + struct dirent *entry; > > > + > > > +again: > > > + entry = readdir(fs->dir.stream); > > > + if (!entry) { > > > + return NULL; > > > + } > > > + > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > > > + /* skip the meta data */ > > > + goto again; > > > + } > > > + } > > > + > > > + return entry; > > > +} > > > + > > > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) > > > +{ > > > + off_t count; > > > + struct dirent *findentry; > > > + struct dirent *entry; > > > + size_t namelen[3] = {0}; > > > + off_t direntoff[3] = {-1, -1, -1}; > > > + char *d_name[3]; > > > + int i; > > > + > > > + /* > > > + * MinGW's seekdir() requires directory is not modified. If guest OS is > > > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", > > > + * then MinGW's seekdir() will seek to a wrong offset. > > > + * > > > + * This function saves some old offset directory entry name, > > > + * and lookup current entry again, and compare the offset. > > > + * > > > + * If new offset is less than old offset, that means someone is deleting > > > + * files in the directory, thus we need to seek offset backward. > > > + * > > > + * If new offset is larger than old offset, that means someone is creating > > > + * files in the directory, thus we need to seek offset forward. > > > + */ > > > + > > > + direntoff[0] = telldir(fs->dir.stream); > > > + > > > + /* do nothing if current offset is 0 or EOF */ > > > + if (direntoff[0] == 0 || direntoff[0] < 0) { > > > + seekdir(fs->dir.stream, off); > > > + return ; > > > + } > > > + > > > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > > > + d_name[1] = d_name[0] + sizeof(entry->d_name); > > > + d_name[2] = d_name[1] + sizeof(entry->d_name); > > > + > > > + /* save 3 nearest entries and offsets */ > > > + for (i = 0; i < 3; i++) { > > > + entry = &fs->dir.stream->dd_dir; > > > + > > > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > > > + namelen[i] = strlen(d_name[i]) + 1; > > > + > > > + direntoff[i] = telldir(fs->dir.stream); > > > + > > > + entry = readdir(fs->dir.stream); > > > + if (entry == NULL) { > > > + break; > > > + } > > > + } > > > + > > > + /* lookup saved entries again */ > > > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > > > + rewinddir(fs->dir.stream); > > > + count = 0; > > > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > > > + count++; > > > + > > > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > > > + if (count + i == direntoff[i]) { > > > + seekdir(fs->dir.stream, off); > > > + goto out; > > > + } else if (count + i < direntoff[i]) { > > > + off = off - (direntoff[i] - count) - i; > > > + if (off <= 0) { > > > + off = 0; > > > + } > > > + seekdir(fs->dir.stream, off); > > > + goto out; > > > + } else { > > > + off = off + (count - direntoff[i]) - i; > > > + seekdir(fs->dir.stream, off); > > > + goto out; > > > + } > > > + }local_opendir_nofollow > > > + } > > > + } > > > + /* can not get anything, seek backward */ > > > + off = off - 1; > > > + > > > + seekdir(fs->dir.stream, off); > > > +out: > > > + g_free(d_name[0]); > > > + return ; > > > +} > > > + > > > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > > > + const struct iovec *iov, > > > + int iovcnt, off_t offset) > > > +{ > > > +#ifdef CONFIG_PREADV > > > + return preadv(fs->fd, iov, iovcnt, offset); > > > +#else > > > + int err = lseek(fs->fd, offset, SEEK_SET); > > > + if (err == -1) { > > > + return err; > > > + } else { > > > + return readv(fs->fd, iov, iovcnt); > > > + } > > > +#endif > > > +} > > > + > > > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > > > + const struct iovec *iov, > > > + int iovcnt, off_t offset) > > > +{ > > > + ssize_t ret; > > > +#ifdef CONFIG_PREADV > > > + ret = pwritev(fs->fd, iov, iovcnt, offset); > > > +#else > > > + int err = lseek(fs->fd, offset, SEEK_SET); > > > + if (err == -1) { > > > + return err; > > > + } else { > > > + ret = writev(fs->fd, iov, iovcnt); > > > + } > > > +#endif > > > +#ifdef CONFIG_SYNC_FILE_RANGE > > > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > > > + /* > > > + * Initiate a writeback. This is not a data integrity sync. > > > + * We want to ensure that we don't leave dirty pages in the cache > > > + * after write when writeout=immediate is sepcified. > > > + */ > > > + sync_file_range(fs->fd, offset, ret, > > > + SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); > > > + } > > > +#endif > > > + return ret; > > > +} > > > + > > > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > > +{ > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > + char *name = g_path_get_basename(fs_path->data); > > > + int ret = -1; > > > + char *full_file_name = NULL; > > > + DIR *dir; > > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > > + if (dir == NULL) { > > > + goto out; > > > + } > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > + ret = local_set_xattrat(full_file_name, name, credp); > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > > + ret = -1; > > > + errno = ENOTSUP; > > > + } > > > + closedir(dir); > > > + > > > +out: > > > + if (full_file_name != NULL) { > > > + g_free(full_file_name); > > > + } > > > + > > > + g_free(dirpath); > > > + g_free(name); > > > + return ret; > > > +} > > > + > > > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > > > + const char *name, FsCred *credp) > > > +{ > > > + return -1; > > > +} > > > + > > > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > > > + const char *name, FsCred *credp) > > > +{ > > > + int err = -1; > > > + char *full_file_name; > > > + char *full_dir_name; > > > + DIR *dir; > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > > + errno = EINVAL; > > > + return -1; > > > + } > > > + > > > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > > > + if (dir == NULL) { > > > + return -1; > > > + } > > > + closedir(dir); > > > + > > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > > > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + err = mkdir(full_file_name); > > > + if (err == -1) { > > > + goto out; > > > + } > > > + credp->fc_mode = credp->fc_mode; > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > + err = local_set_xattrat(full_dir_name, name, credp); > > > + } else { > > > + err = local_set_mapped_file_attrat(full_dir_name, name, credp); > > > + } > > > + if (err == -1) { > > > + rmdir(full_file_name); > > > + } > > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > > + err = mkdir(full_file_name); > > > + if (err == -1) { > > > + goto out; > > > + } > > > + /* Windows does not support chmod, do nothing here */ > > > + } > > > + > > > + goto out; > > > + > > > +out: > > > + g_free(full_dir_name); > > > + g_free(full_file_name); > > > + return err; > > > +} > > > + > > > +static int local_fstat(FsContext *fs_ctx, int fid_type, > > > + V9fsFidOpenState *fs, struct stat *stbuf) > > > +{ > > > + > > > + int err, fd; > > > + char filename[NAME_MAX]; > > > + char *dirpath; > > > + char *name; > > > + HANDLE hFile; > > > + DWORD dwRet; > > > + > > > + if (fid_type == P9_FID_DIR) { > > > + /* Windows does not support open directory */ > > > + return -1; > > > + } else { > > > + fd = fs->fd; > > > + } > > > + > > > + err = fstat(fd, stbuf); > > > + if (err) { > > > + return err; > > > + } > > > + > > > + /* get real file name by fd */ > > > + hFile = (HANDLE)_get_osfhandle(fd); > > > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); > > > + > > > + if (dwRet >= NAME_MAX) { > > > + return -1; > > > + } > > > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > > > + memmove(filename, filename + 4, NAME_MAX - 4); > > > + > > > + dirpath = g_path_get_dirname(filename); > > > + name = g_path_get_basename(filename); > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > + /* Actual credentials are part of extended attrs */ > > > + uid_t tmp_uid; > > > + gid_t tmp_gid; > > > + mode_t tmp_mode; > > > + dev_t tmp_dev; > > > + > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > > > + &tmp_uid, sizeof(uid_t)) > 0) { > > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > > + } > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > > > + &tmp_gid, sizeof(gid_t)) > 0) { > > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > > + } > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > > > + &tmp_mode, sizeof(mode_t)) > 0) { > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > > + } > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > > > + &tmp_dev, sizeof(dev_t)) > 0) { > > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > > + } > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + errno = EOPNOTSUPP; > > > + g_free(dirpath); > > > + g_free(name); > > > + return -1; > > > + } > > > + > > > + g_free(dirpath); > > > + g_free(name); > > > + > > > + return err; > > > +} > > > + > > > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, > > > + int flags, FsCred *credp, V9fsFidOpenState *fs) > > > +{ > > > + int fd = -1; > > > + int err = -1; > > > + char *full_file_name; > > > + > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > > + errno = EINVAL; > > > + return -1; > > > + } > > > + > > > + full_file_name = merge_fs_path(dir_path->data, name); > > > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > > > + g_free(full_file_name); > > > + > > > + err = fd; > > > + fs->fd = fd; > > > + goto out; > > > + > > > + close_preserve_errno(fd); > > > +out: > > > + return err; > > > +} > > > + > > > + > > > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > > > + V9fsPath *dir_path, const char *name, FsCred *credp) > > > +{ > > > + return -1; > > > > Why ? This would require a detailed comment and also to set errno. > > An error number set is missing here, should be ENOTSUP. > > > > > > +} > > > + > > > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > > > + V9fsPath *dirpath, const char *name) > > > +{ > > > + return -1; > > > > Same remark. > > > > Before re-posting, you really need to come up with a detailed plan > > around symlinks. > > > > Cheers, > > > > -- > > Greg > > An error number set is missing here, should be ENOTSUP. > Windows does not support symbolic link in its POSIX API library. > It also require Administrator privileges and NTFS (need to turn on symbolic link support). > > Currently, I think it is very hard to support symbolic link on Windows host. > > > > > > +} > > > + > > > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > > > +{ > > > + int fd, ret; > > > + > > > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > > > + if (fd == -1) { > > > + return -1; > > > + } > > > + ret = ftruncate(fd, size); > > > + close_preserve_errno(fd); > > > + return ret; > > > +} > > > + > > > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > > +{ > > > + char *full_file_name; > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > + char *name = g_path_get_basename(fs_path->data); > > > + int ret = -1; > > > + DIR *dir; > > > + > > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > > + if (dir == NULL) { > > > + goto out; > > > + } > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > + > > > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > > > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > > > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > > > + /* Windows does not support chown() */ > > > + ret = -1; > > > + errno = ENOTSUP; > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > + ret = local_set_xattrat(full_file_name, name, credp); > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > > + } > > > + g_free(full_file_name); > > > + closedir(dir); > > > +out: > > > + g_free(name); > > > + g_free(dirpath); > > > + return ret; > > > +} > > > + > > > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > > > + const struct timespec *buf) > > > +{ > > > + struct utimbuf tm; > > > + char *full_file_name; > > > + int err; > > > + > > > + tm.actime = buf[0].tv_sec; > > > + tm.modtime = buf[1].tv_sec; > > > + > > > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > > > + err = utime(full_file_name, &tm); > > > + g_free(full_file_name); > > > + > > > + return err; > > > +} > > > + > > > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > > > + const char *name, int flags) > > > +{ > > > + int ret; > > > + char *full_file_name; > > > + char *full_dir_name; > > > + > > > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > + > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + char *full_meta_dir_name; > > > + char *full_meta_file_name; > > > + > > > + /* > > > + * We need to remove the metadata as well: > > > + * - the metadata directory if we're removing a directory > > > + * - the metadata file in the parent's metadata directory > > > + * > > > + * If any of these are missing (ie, ENOENT) then we're probably > > > + * trying to remove something that wasn't created in mapped-file > > > + * mode. We just ignore the error. > > > + */ > > > + > > > + if ((flags & AT_REMOVEDIR) != 0) { > > > + full_meta_dir_name = merge_fs_path(full_file_name, > > VIRTFS_META_DIR); > > > + ret = rmdir(full_meta_dir_name); > > > + g_free(full_meta_dir_name); > > > + > > > + if (ret < 0 && errno != ENOENT) { > > > + g_free(full_file_name); > > > + g_free(full_dir_name); > > > + return -1; > > > + } > > > + } > > > + > > > + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); > > > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); > > > + ret = remove(full_meta_file_name); > > > + g_free(full_meta_dir_name); > > > + g_free(full_meta_file_name); > > > + > > > + if (ret < 0 && errno != ENOENT) { > > > + g_free(full_dir_name); > > > + g_free(full_file_name); > > > + > > > + return -1; > > > + } > > > + } > > > + > > > + if ((flags & AT_REMOVEDIR) != 0) { > > > + ret = rmdir(full_file_name); > > > + } else { > > > + ret = remove(full_file_name); > > > + } > > > + > > > + g_free(full_dir_name); > > > + g_free(full_file_name); > > > + > > > + return ret; > > > +} > > > + > > > +static int local_remove(FsContext *ctx, const char *path) > > > +{ > > > + int err; > > > + DIR *stream; > > > + char *full_file_name; > > > + char *dirpath = g_path_get_dirname(path); > > > + char *name = g_path_get_basename(path); > > > + int flags = 0; > > > + > > > + full_file_name = merge_fs_path(ctx->fs_root, path); > > > + stream = opendir(full_file_name); > > > + if (stream != NULL) { > > > + closedir(stream); > > > + flags |= AT_REMOVEDIR; > > > + } > > > + err = local_unlinkat_common(ctx, dirpath, name, flags); > > > + > > > + g_free(name); > > > + g_free(dirpath); > > > + g_free(full_file_name); > > > + return err; > > > +} > > > + > > > +static int local_fsync(FsContext *ctx, int fid_type, > > > + V9fsFidOpenState *fs, int datasync) > > > +{ > > > + if (fid_type != P9_FID_DIR) { > > > + return _commit(fs->fd); > > > + } > > > + return 0; > > > +} > > > + > > > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) > > > +{ > > > + int ret; > > > + ret = qemu_statfs(s->fs_root, stbuf); > > > + if (ret == 0) { > > > + /* use context address as fsid */ > > > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > > > + } > > > + > > > + return ret; > > > +} > > > + > > > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > > > + const char *name, void *value, size_t size) > > > +{ > > > + return -1; > > > +} > > > + > > > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > > > + void *value, size_t size) > > > +{ > > > + return -1; > > > +} > > > + > > > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, > > > + void *value, size_t size, int flags) > > > +{ > > > + return -1; > > > +} > > > + > > > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > > > + const char *name) > > > +{ > > > + return -1; > > > +} > > > + > > > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > > > + const char *name, V9fsPath *target) > > > +{ > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > + local_is_mapped_file_metadata(ctx, name)) { > > > + errno = EINVAL; > > > + return -1; > > > + } > > > + > > > + if (dir_path) { > > > + if (!strcmp(name, ".")) { > > > + /* "." relative to "foo/bar" is "foo/bar" */ > > > + v9fs_path_copy(target, dir_path); > > > + } else if (!strcmp(name, "..")) { > > > + if (!strcmp(dir_path->data, ".")) { > > > + /* ".." relative to the root is "." */ > > > + v9fs_path_sprintf(target, "."); > > > + } else { > > > + char *tmp = g_path_get_dirname(dir_path->data); > > > + /* > > > + * Symbolic links are resolved by the client. We can assume > > > + * that ".." relative to "foo/bar" is equivalent to "foo" > > > + */ > > > + v9fs_path_sprintf(target, "%s", tmp); > > > + g_free(tmp); > > > + } > > > + } else { > > > + assert(!strchr(name, '/')); > > > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > > > + } > > > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > > > + !strcmp(name, "..")) { > > > + /* This is the root fid */ > > > + v9fs_path_sprintf(target, "."); > > > + } else { > > > + assert(!strchr(name, '/')); > > > + v9fs_path_sprintf(target, "./%s", name); > > > + } > > > + return 0; > > > +} > > > + > > > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > > > + const char *old_name, V9fsPath *newdir, > > > + const char *new_name) > > > +{ > > > + return -1; > > > +} > > > + > > > +static int local_rename(FsContext *ctx, const char *oldpath, > > > + const char *newpath) > > > +{ > > > + int err; > > > + > > > + char *full_old_name; > > > + char *full_new_name; > > > + > > > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > > > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > > > + > > > + err = rename(full_old_name, full_new_name); > > > + > > > + g_free(full_old_name); > > > + g_free(full_new_name); > > > + > > > + return err; > > > +} > > > + > > > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > > > + const char *name, int flags) > > > +{ > > > + int ret; > > > + > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > + local_is_mapped_file_metadata(ctx, name)) { > > > + errno = EINVAL; > > > + return -1; > > > + } > > > + > > > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > > > + > > > + return ret; > > > +} > > > + > > > +static int check_filesystem_type(char *fs_root, int export_flags) > > > +{ > > > + HANDLE hFile; > > > + wchar_t FsName[MAX_PATH + 1] = {0}; > > > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > > > + > > > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > > > + return 0; > > > + } > > > + > > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > > + if (hFile == INVALID_HANDLE_VALUE) { > > > + return -1; > > > + } > > > + > > > + /* Get file system type name */ > > > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > > > + FsName, MAX_PATH + 1) == 0) { > > > + CloseHandle(hFile); > > > + return -1; > > > + } > > > + CloseHandle(hFile); > > > + > > > + if (wcscmp(FsName, NtfsName) != 0) { > > > + return -1; > > > + } > > > + > > > + return 0; > > > +} > > > + > > > +static int local_init(FsContext *ctx, Error **errp) > > > +{ > > > + LocalData *data = g_malloc(sizeof(*data)); > > > + > > > + struct stat StatBuf; > > > + > > > + if (stat(ctx->fs_root, &StatBuf) != 0) { > > > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > > > + goto err; > > > + } > > > + > > > + /* > > > + * security_model=mapped(-xattr) requires a fileystem on Windows that > > > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > > > + * probably most popular on Windows. It is fair enough to assume > > > + * Windows users to use NTFS for the mapped security model. > > > + */ > > > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > > > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > > > + "security_model is mapped or mapped-xattr"); > > > + goto err; > > > + } > > > + > > > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > > > + ctx->xops = passthrough_xattr_ops; > > > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > > > + ctx->xops = mapped_xattr_ops; > > > + } else if (ctx->export_flags & V9FS_SM_NONE) { > > > + ctx->xops = none_xattr_ops; > > > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > + /* > > > + * xattr operation for mapped-file and passthrough > > > + * remain same. > > > + */ > > > + ctx->xops = passthrough_xattr_ops; > > > + } > > > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > > > + > > > + ctx->private = data; > > > + return 0; > > > + > > > +err: > > > + g_free(data); > > > + return -1; > > > +} > > > + > > > +static void local_cleanup(FsContext *ctx) > > > +{ > > > + LocalData *data = ctx->private; > > > + > > > + if (!data) { > > > + return; > > > + } > > > + > > > + close(data->mountfd); > > > + g_free(data); > > > +} > > > + > > > +static void error_append_security_model_hint(Error *const *errp) > > > +{ > > > + error_append_hint(errp, "Valid options are: security_model=" > > > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > > > +} > > > + > > > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) > > > +{ > > > + ERRP_GUARD(); > > > + const char *sec_model = qemu_opt_get(opts, "security_model"); > > > + const char *path = qemu_opt_get(opts, "path"); > > > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > > > + > > > + if (!sec_model) { > > > + error_setg(errp, "security_model property not set"); > > > + error_append_security_model_hint(errp); > > > + return -1; > > > + } > > > + > > > + if (!strcmp(sec_model, "passthrough")) { > > > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > > > + } else if (!strcmp(sec_model, "mapped") || > > > + !strcmp(sec_model, "mapped-xattr")) { > > > + fse->export_flags |= V9FS_SM_MAPPED; > > > + } else if (!strcmp(sec_model, "none")) { > > > + fse->export_flags |= V9FS_SM_NONE; > > > + } else if (!strcmp(sec_model, "mapped-file")) { > > > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > > > + } else { > > > + error_setg(errp, "invalid security_model property '%s'", sec_model); > > > + error_append_security_model_hint(errp); > > > + return -1; > > > + } > > > + > > > + if (multidevs) { > > > + if (!strcmp(multidevs, "remap")) { > > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > > + fse->export_flags |= V9FS_REMAP_INODES; > > > + } else if (!strcmp(multidevs, "forbid")) { > > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > > > + } else if (!strcmp(multidevs, "warn")) { > > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > > + } else { > > > + error_setg(errp, "invalid multidevs property '%s'", > > > + multidevs); > > > + error_append_hint(errp, "Valid options are: multidevs=" > > > + "[remap|forbid|warn]\n"); > > > + return -1; > > > + } > > > + } > > > + > > > + if (!path) { > > > + error_setg(errp, "path property not set"); > > > + return -1; > > > + } > > > + > > > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > > > + error_prepend(errp, "invalid throttle configuration: "); > > > + return -1; > > > + } > > > + > > > + if (fse->export_flags & V9FS_SM_MAPPED || > > > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > > > + fse->fmode = > > > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > > > + fse->dmode = > > > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777; > > > + } else { > > > + if (qemu_opt_find(opts, "fmode")) { > > > + error_setg(errp, "fmode is only valid for mapped security modes"); > > > + return -1; > > > + } > > > + if (qemu_opt_find(opts, "dmode")) { > > > + error_setg(errp, "dmode is only valid for mapped security modes"); > > > + return -1; > > > + } > > > + } > > > + > > > + fse->path = g_strdup(path); > > > + > > > + return 0; > > > +} > > > + > > > +FileOperations local_ops = { > > > + .parse_opts = local_parse_opts, > > > + .init = local_init, > > > + .cleanup = local_cleanup, > > > + .lstat = local_lstat, > > > + .readlink = local_readlink, > > > + .close = local_close, > > > + .closedir = local_closedir, > > > + .open = local_open, > > > + .opendir = local_opendir, > > > + .rewinddir = local_rewinddir, > > > + .telldir = local_telldir, > > > + .readdir = local_readdir, > > > + .seekdir = local_seekdir, > > > + .preadv = local_preadv, > > > + .pwritev = local_pwritev, > > > + .chmod = local_chmod, > > > + .mknod = local_mknod, > > > + .mkdir = local_mkdir, > > > + .fstat = local_fstat, > > > + .open2 = local_open2, > > > + .symlink = local_symlink, > > > + .link = local_link, > > > + .truncate = local_truncate, > > > + .rename = local_rename, > > > + .chown = local_chown, > > > + .utimensat = local_utimensat, > > > + .remove = local_remove, > > > + .fsync = local_fsync, > > > + .statfs = local_statfs, > > > + .lgetxattr = local_lgetxattr, > > > + .llistxattr = local_llistxattr, > > > + .lsetxattr = local_lsetxattr, > > > + .lremovexattr = local_lremovexattr, > > > + .name_to_path = local_name_to_path, > > > + .renameat = local_renameat, > > > + .unlinkat = local_unlinkat, > > > +}; > > > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > > > new file mode 100644 > > > index 0000000000..d9b35e7425 > > > --- /dev/null > > > +++ b/hw/9pfs/9p-util-win32.c > > > @@ -0,0 +1,303 @@ > > > +/* > > > + * 9p utilities (Windows Implementation) > > > + * > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > + * > > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > > + * See the COPYING file in the top-level directory. > > > + */ > > > + > > > +#include "qemu/osdep.h" > > > +#include "qapi/error.h" > > > +#include "qemu/error-report.h" > > > +#include "9p.h" > > > +#include "9p-util.h" > > > +#include "9p-linux-errno.h" > > > +#include <windows.h> > > > +#include <dirent.h> > > > + > > > +#ifndef V9FS_MAGIC > > > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > > > +#endif > > > + > > > +static int build_ads_name(char *namebuf, size_t namebuflen, > > > + const char *dirname, const char *filename, > > > + const char *ads_name) > > > +{ > > > + size_t totalsize; > > > + > > > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; > > > + if (totalsize > namebuflen) { > > > + return -1; > > > + } > > > + > > > + /* > > > + * NTFS ADS (Alternate Data Streams) name format: > > > + * filename:ads_name > > > + * e.g. > > > + * d:\1.txt:my_ads_name > > > + */ > > > + strcpy(namebuf, dirname); > > > + strcat(namebuf, "\\"); > > > + strcat(namebuf, filename); > > > + strcat(namebuf, ":"); > > > + strcat(namebuf, ads_name); > > > + > > > + return 0; > > > +} > > > + > > > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > > > + char *fulladsname) > > > +{ > > > + char *p1, *p2; > > > + > > > + /* > > > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > > > + * :ads_name:$DATA > > > + * e.g. > > > + * :my_ads_name:$DATA > > > + * > > > + * ADS name from FindNextStreamW() always have ":$DATA" string at the end > > > + * > > > + * This function copy ADS name to namebuf. > > > + */ > > > + > > > + p1 = strchr(fulladsname, ':'); > > > + if (p1 == NULL) { > > > + return -1; > > > + } > > > + > > > + p2 = strchr(p1 + 1, ':'); > > > + if (p2 == NULL) { > > > + return -1; > > > + } > > > + > > > + /* skip empty ads name */ > > > + if (p2 - p1 == 1) { > > > + return 0; > > > + } > > > + > > > + if (p2 - p1 + 1 > namebuflen) { > > > + return -1; > > > + } > > > + > > > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > > > + namebuf[p2 - p1 - 1] = '\0'; > > > + > > > + return p2 - p1; > > > +} > > > + > > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > > + const char *name, void *value, size_t size) > > > +{ > > > + HANDLE hStream; > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > + DWORD dwBytesRead; > > > + > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > > > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > > > + if (hStream == INVALID_HANDLE_VALUE && > > > + GetLastError() == ERROR_FILE_NOT_FOUND) { > > > + errno = ENODATA; > > > + return -1; > > > + } > > > + > > > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > > > + errno = EIO; > > > + CloseHandle(hStream); > > > + return -1; > > > + } > > > + > > > + CloseHandle(hStream); > > > + > > > + return dwBytesRead; > > > +} > > > + > > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > > + char *list, size_t size) > > > +{ > > > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > > > + char fulladsname[NAME_MAX + 1]; > > > + char *full_fs_name = merge_fs_path(dirname, filename); > > > + int ret; > > > + HANDLE hFind; > > > + WIN32_FIND_STREAM_DATA fsd; > > > + BOOL bFindNext; > > > + char *listptr = list; > > > + size_t listleftsize = size; > > > + > > > + /* > > > + * ADS emurate function only have WCHAR version, need to covert filename > > > + * to WCHAR string. > > > + */ > > > + > > > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > > > + strlen(full_fs_name), WideCharStr, NAME_MAX); > > > + g_free(full_fs_name); > > > + if (ret == 0) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); > > > + if (hFind == INVALID_HANDLE_VALUE) { > > > + errno = ENODATA; > > > + return -1; > > > + } > > > + > > > + do { > > > + memset(fulladsname, 0, sizeof(fulladsname)); > > > + > > > + /* > > > + * ADS emurate function only have WCHAR version, need to covert > > > + * cStreamName to utf-8 string. > > > + */ > > > + > > > + ret = WideCharToMultiByte(CP_UTF8, 0, > > > + fsd.cStreamName, wcslen(fsd.cStreamName) + 1, > > > + fulladsname, sizeof(fulladsname) - 1, > > > + NULL, NULL); > > > + > > > + if (ret == 0) { > > > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > > > + errno = ERANGE; > > > + } > > > + CloseHandle(hFind); > > > + return -1; > > > + } > > > + > > > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > > > + if (ret < 0) { > > > + errno = ERANGE; > > > + CloseHandle(hFind); > > > + return -1; > > > + } > > > + > > > + listptr = listptr + ret; > > > + listleftsize = listleftsize - ret; > > > + > > > + bFindNext = FindNextStreamW(hFind, &fsd); > > > + } while (bFindNext); > > > + > > > + CloseHandle(hFind); > > > + > > > + return size - listleftsize; > > > +} > > > + > > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > > + const char *name) > > > +{ > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > + > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + if (DeleteFile(ADSFileName) != 0) { > > > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > > > + errno = ENODATA; > > > + return -1; > > > + } > > > + } > > > + > > > + return 0; > > > +} > > > + > > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > > + const char *name, void *value, size_t size, int flags) > > > +{ > > > + HANDLE hStream; > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > + DWORD dwBytesWrite; > > > + > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < 0) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, > > > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); > > > + if (hStream == INVALID_HANDLE_VALUE) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { > > > + errno = EIO; > > > + CloseHandle(hStream); > > > + return -1; > > > + } > > > + > > > + CloseHandle(hStream); > > > + > > > + return 0; > > > +} > > > + > > > +int qemu_mknodat(const char *dirname, const char *filename, > > > + mode_t mode, dev_t dev) > > > +{ > > > + errno = ENOTSUP; > > > + return -1; > > > +} > > > + > > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > > > +{ > > > + HANDLE hFile; > > > + char RealPath[NAME_MAX + 1]; > > > + unsigned long SectorsPerCluster; > > > + unsigned long BytesPerSector; > > > + unsigned long NumberOfFreeClusters; > > > + unsigned long TotalNumberOfClusters; > > > + > > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > > + if (hFile == INVALID_HANDLE_VALUE) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + /* get real path of root */ > > > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > > > + FILE_NAME_NORMALIZED) == 0) { > > > + errno = EIO; > > > + CloseHandle(hFile); > > > + return -1; > > > + } > > > + > > > + CloseHandle(hFile); > > > + > > > + /* > > > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > > > + * "C:\\123" will be "\\\\?\\C:\\123" > > > + * Skip first 4 bytes and truncate the string at offset 7, it will get > > > + * the real root directory like "C:\\", this is parameter GetDiskFreeSpace > > > + * needed. > > > + */ > > > + > > > + RealPath[7] = '\0'; > > > + > > > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, > > > + &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) { > > > + errno = EIO; > > > + return -1; > > > + } > > > + > > > + stbuf->f_type = V9FS_MAGIC; > > > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > > > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > > > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > > > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > > > + stbuf->f_files = -1; > > > + stbuf->f_ffree = -1; > > > + stbuf->f_namelen = NAME_MAX; > > > + stbuf->f_frsize = 0; > > > + stbuf->f_flags = 0; > > > + > > > + return 0; > > > +} > > > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > > > index 9ae69dd8db..5623f0e2ef 100644 > > > --- a/hw/9pfs/9p-xattr.c > > > +++ b/hw/9pfs/9p-xattr.c > > > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > > > char *orig_value, *orig_value_start; > > > ssize_t xattr_len, parsed_len = 0, attr_len; > > > char *dirpath, *name; > > > +#ifdef CONFIG_WIN32 > > > + char *full_dir_path; > > > + DIR *dir; > > > +#else > > > int dirfd; > > > +#endif > > > > > > /* Get the actual len */ > > > dirpath = g_path_get_dirname(path); > > > + > > > +#ifdef CONFIG_WIN32 > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > + if (dir == NULL) { > > > + return -1; > > > + } > > > + closedir(dir); > > > + > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > + g_free(dirpath); > > > + > > > + name = g_path_get_basename(path); > > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > > > + if (xattr_len <= 0) { > > > + g_free(name); > > > + g_free(full_dir_path); > > > + return xattr_len; > > > + } > > > + > > > + /* Now fetch the xattr and find the actual size */ > > > + orig_value = g_malloc(xattr_len); > > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > > > + xattr_len); > > > + g_free(name); > > > + g_free(full_dir_path); > > > + if (xattr_len < 0) { > > > + g_free(orig_value); > > > + return -1; > > > + } > > > +#else > > > dirfd = local_opendir_nofollow(ctx, dirpath); > > > g_free(dirpath); > > > if (dirfd == -1) { > > > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > > > g_free(orig_value); > > > return -1; > > > } > > > +#endif > > > > > > /* store the orig pointer */ > > > orig_value_start = orig_value; > > > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > > > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > > > const char *name, void *value, size_t size) > > > { > > > +#ifdef CONFIG_WIN32 > > > + char *dirpath = g_path_get_dirname(path); > > > + char *filename = g_path_get_basename(path); > > > + char *full_dir_path; > > > + DIR *dir; > > > + ssize_t ret = -1; > > > + > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > + > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > + if (dir == NULL) { > > > + goto out; > > > + } > > > + closedir(dir); > > > + > > > + ret = fgetxattrat_nofollow(full_dir_path, filename, > > > + name, value, size); > > > + > > > +out: > > > + g_free(full_dir_path); > > > + g_free(dirpath); > > > + g_free(filename); > > > + > > > + return ret; > > > +#else > > > char *dirpath = g_path_get_dirname(path); > > > char *filename = g_path_get_basename(path); > > > int dirfd; > > > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const char > > *path, > > > } > > > > > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > > > + > > > close_preserve_errno(dirfd); > > > out: > > > g_free(dirpath); > > > g_free(filename); > > > return ret; > > > +#endif > > > } > > > > > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > > > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const char > > *path, > > > const char *name, void *value, size_t size, > > > int flags) > > > { > > > +#ifdef CONFIG_WIN32 > > > + char *dirpath = g_path_get_dirname(path); > > > + char *filename = g_path_get_basename(path); > > > + char *full_dir_path; > > > + DIR *dir; > > > + ssize_t ret = -1; > > > + > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > + > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > + if (dir == NULL) { > > > + goto out; > > > + } > > > + closedir(dir); > > > + > > > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > > > + value, size, flags); > > > +out: > > > + g_free(full_dir_path); > > > + g_free(dirpath); > > > + g_free(filename); > > > + return ret; > > > + > > > +#else > > > char *dirpath = g_path_get_dirname(path); > > > char *filename = g_path_get_basename(path); > > > int dirfd; > > > @@ -210,6 +297,7 @@ out: > > > g_free(dirpath); > > > g_free(filename); > > > return ret; > > > +#endif > > > } > > > > > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, > > > @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char *path, const > > char *name, void *value, > > > ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, > > > const char *name) > > > { > > > +#ifdef CONFIG_WIN32 > > > + char *dirpath = g_path_get_dirname(path); > > > + char *filename = g_path_get_basename(path); > > > + char *full_dir_path; > > > + DIR *dir; > > > + ssize_t ret = -1; > > > + > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > + > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > + if (dir == NULL) { > > > + goto out; > > > + } > > > + closedir(dir); > > > + > > > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > > > + > > > +out: > > > + g_free(full_dir_path); > > > + g_free(dirpath); > > > + g_free(filename); > > > + return ret; > > > + > > > +#else > > > char *dirpath = g_path_get_dirname(path); > > > char *filename = g_path_get_basename(path); > > > int dirfd; > > > @@ -237,6 +349,7 @@ out: > > > g_free(dirpath); > > > g_free(filename); > > > return ret; > > > +#endif > > > } > > > > > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > > > index 225f31fc31..a04889c1d6 100644 > > > --- a/hw/9pfs/9p.c > > > +++ b/hw/9pfs/9p.c > > > @@ -38,6 +38,10 @@ > > > #include "migration/blocker.h" > > > #include "qemu/xxhash.h" > > > #include <math.h> > > > +#ifdef CONFIG_WIN32 > > > +#define UTIME_NOW ((1l << 30) - 1l) > > > +#define UTIME_OMIT ((1l << 30) - 2l) > > > +#endif > > > > > > int open_fd_hw; > > > int total_open_fd; > > > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, > > V9fsQID *qidp) > > > if (S_ISDIR(stbuf->st_mode)) { > > > qidp->type |= P9_QID_TYPE_DIR; > > > } > > > +#ifndef CONFIG_WIN32 > > > if (S_ISLNK(stbuf->st_mode)) { > > > qidp->type |= P9_QID_TYPE_SYMLINK; > > > } > > > +#endif > > > > > > return 0; > > > } > > > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > *extension) > > > ret |= S_IFDIR; > > > } > > > > > > +#ifndef CONFIG_WIN32 > > > if (mode & P9_STAT_MODE_SYMLINK) { > > > ret |= S_IFLNK; > > > } > > > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > *extension) > > > if (mode & P9_STAT_MODE_NAMED_PIPE) { > > > ret |= S_IFIFO; > > > } > > > +#endif > > > if (mode & P9_STAT_MODE_DEVICE) { > > > if (extension->size && extension->data[0] == 'c') { > > > ret |= S_IFCHR; > > > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > *extension) > > > ret |= S_IFREG; > > > } > > > > > > +#ifndef CONFIG_WIN32 > > > if (mode & P9_STAT_MODE_SETUID) { > > > ret |= S_ISUID; > > > } > > > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > *extension) > > > if (mode & P9_STAT_MODE_SETVTX) { > > > ret |= S_ISVTX; > > > } > > > +#endif > > > > > > return ret; > > > } > > > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > > mode |= P9_STAT_MODE_DIR; > > > } > > > > > > +#ifndef CONFIG_WIN32 > > > if (S_ISLNK(stbuf->st_mode)) { > > > mode |= P9_STAT_MODE_SYMLINK; > > > } > > > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > > if (S_ISFIFO(stbuf->st_mode)) { > > > mode |= P9_STAT_MODE_NAMED_PIPE; > > > } > > > +#endif > > > > > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > > > mode |= P9_STAT_MODE_DEVICE; > > > } > > > > > > +#ifndef CONFIG_WIN32 > > > if (stbuf->st_mode & S_ISUID) { > > > mode |= P9_STAT_MODE_SETUID; > > > } > > > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > > if (stbuf->st_mode & S_ISVTX) { > > > mode |= P9_STAT_MODE_SETVTX; > > > } > > > +#endif > > > > > > return mode; > > > } > > > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, > > V9fsPath *path, > > > return err; > > > } > > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > > > +#ifndef CONFIG_WIN32 > > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > > > +#else > > > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > > + 0, 0); > > > +#endif > > > + > > > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > > > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > > > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > > > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, > > int32_t blksize) > > > > > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > > > { > > > +#ifndef CONFIG_WIN32 > > > return blksize_to_iounit(pdu, stbuf->st_blksize); > > > +#else > > > + return blksize_to_iounit(pdu, 0); > > > +#endif > > > } > > > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > > > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct > > stat *stbuf, > > > v9lstat->st_gid = stbuf->st_gid; > > > v9lstat->st_rdev = stbuf->st_rdev; > > > v9lstat->st_size = stbuf->st_size; > > > + > > > +#ifdef CONFIG_WIN32 > > > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > > + v9lstat->st_blocks = 0; > > > + v9lstat->st_atime_sec = stbuf->st_atime; > > > + v9lstat->st_mtime_sec = stbuf->st_mtime; > > > + v9lstat->st_ctime_sec = stbuf->st_ctime; > > > +#else /* !CONFIG_WIN32 */ > > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > > v9lstat->st_blocks = stbuf->st_blocks; > > > v9lstat->st_atime_sec = stbuf->st_atime; > > > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct > > stat *stbuf, > > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > > > #endif > > > +#endif /* CONFIG_WIN32 */ > > > + > > > /* Currently we only support BASIC fields in stat */ > > > v9lstat->st_result_mask = P9_STATS_BASIC; > > > > > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > > > > > static bool same_stat_id(const struct stat *a, const struct stat *b) > > > { > > > +#ifdef CONFIG_WIN32 > > > + return 0; > > > +#else > > > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > > > +#endif /* CONFIG_WIN32 */ > > > } > > > > > > static void coroutine_fn v9fs_walk(void *opaque) > > > @@ -2300,7 +2339,11 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU > > *pdu, > > > count += len; > > > v9fs_stat_free(&v9stat); > > > v9fs_path_free(&path); > > > +#ifndef CONFIG_WIN32 > > > saved_dir_pos = qemu_dirent_off(dent); > > > +#else > > > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > > > +#endif > > > } > > > > > > v9fs_readdir_unlock(&fidp->fs.dir); > > > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, > > V9fsFidState *fidp, > > > qid.version = 0; > > > } > > > > > > +#ifdef CONFIG_WIN32 > > > + /* > > > + * Windows does not have dent->d_off, get offset by calling telldir() > > > + * manually. > > > + */ > > > + off = v9fs_co_telldir(pdu, fidp); > > > +#else > > > off = qemu_dirent_off(dent); > > > +#endif > > > v9fs_string_init(&name); > > > v9fs_string_sprintf(&name, "%s", dent->d_name); > > > > > > +#ifdef CONFIG_WIN32 > > > + /* > > > + * Windows does not have dent->d_type > > > + */ > > > + > > > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > > > + &qid, off, > > > + 0, &name); > > > +#else > > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > > > len = pdu_marshal(pdu, 11 + count, "Qqbs", > > > &qid, off, > > > dent->d_type, &name); > > > +#endif > > > > > > v9fs_string_free(&name); > > > > > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > > > } > > > > > > nmode |= perm & 0777; > > > + > > > +#ifndef CONFIG_WIN32 > > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > > makedev(major, minor), nmode, &stbuf); > > > +#else > > > + err = -ENOTSUP; > > > +#endif > > > + > > > if (err < 0) { > > > goto out; > > > } > > > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > > > v9fs_path_copy(&fidp->path, &path); > > > v9fs_path_unlock(s); > > > } else if (perm & P9_STAT_MODE_SOCKET) { > > > +#ifndef CONFIG_WIN32 > > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > > 0, S_IFSOCK | (perm & 0777), &stbuf); > > > +#else > > > + err = -ENOTSUP; > > > +#endif > > > if (err < 0) { > > > goto out; > > > } > > > @@ -3600,6 +3671,7 @@ out_nofid: > > > static void coroutine_fn v9fs_mknod(void *opaque) > > > { > > > > > > +#ifndef CONFIG_WIN32 > > > int mode; > > > gid_t gid; > > > int32_t fid; > > > @@ -3656,6 +3728,11 @@ out: > > > out_nofid: > > > pdu_complete(pdu, err); > > > v9fs_string_free(&name); > > > +#else > > > + V9fsPDU *pdu = opaque; > > > + > > > + pdu_complete(pdu, -1); > > > +#endif > > > } > > > > > > /* > > > @@ -3928,7 +4005,7 @@ out_nofid: > > > #if defined(CONFIG_LINUX) > > > /* Currently, only Linux has XATTR_SIZE_MAX */ > > > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > > > -#elif defined(CONFIG_DARWIN) > > > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > > > /* > > > * Darwin doesn't seem to define a maximum xattr size in its user > > > * space header, so manually configure it across platforms as 64k. > > > @@ -3945,6 +4022,7 @@ out_nofid: > > > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > > > { > > > +#ifndef CONFIG_WIN32 > > > int flags, rflags = 0; > > > int32_t fid; > > > uint64_t size; > > > @@ -4006,10 +4084,15 @@ out_put_fid: > > > out_nofid: > > > pdu_complete(pdu, err); > > > v9fs_string_free(&name); > > > +#else > > > + V9fsPDU *pdu = opaque; > > > + pdu_complete(pdu, -1); > > > +#endif > > > } > > > > > > static void coroutine_fn v9fs_readlink(void *opaque) > > > { > > > +#ifndef CONFIG_WIN32 > > > V9fsPDU *pdu = opaque; > > > size_t offset = 7; > > > V9fsString target; > > > @@ -4045,6 +4128,10 @@ out: > > > put_fid(pdu, fidp); > > > out_nofid: > > > pdu_complete(pdu, err); > > > +#else > > > + V9fsPDU *pdu = opaque; > > > + pdu_complete(pdu, -1); > > > +#endif > > > } > > > > > > static CoroutineEntry *pdu_co_handlers[] = { > > > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > > > > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > > > { > > > +#ifndef CONFIG_WIN32 > > > struct rlimit rlim; > > > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > > > error_report("Failed to get the resource limit"); > > > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) > > v9fs_set_fd_limit(void) > > > } > > > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > > > open_fd_rc = rlim.rlim_cur / 2; > > > +#endif > > > } > > > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > > > index 93ba44fb75..2fbe7b831b 100644 > > > --- a/hw/9pfs/codir.c > > > +++ b/hw/9pfs/codir.c > > > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > > int len, err = 0; > > > int32_t size = 0; > > > off_t saved_dir_pos; > > > +#ifdef CONFIG_WIN32 > > > + off_t next_dir_pos; > > > +#endif > > > struct dirent *dent; > > > struct V9fsDirEnt *e = NULL; > > > V9fsPath path; > > > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > > break; > > > } > > > > > > +#ifdef CONFIG_WIN32 > > > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > > > + if (next_dir_pos < 0) { > > > + err = next_dir_pos; > > > + goto out; > > > + } > > > +#endif > > > + > > > /* > > > * stop this loop as soon as it would exceed the allowed maximum > > > * response message size for the directory entries collected so far, > > > @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > > } > > > > > > size += len; > > > +#ifndef CONFIG_WIN32 > > > saved_dir_pos = qemu_dirent_off(dent); > > > +#else > > > + saved_dir_pos = next_dir_pos; > > > +#endif > > > } > > > > > > /* restore (last) saved position */ > ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-09 16:20 ` Greg Kurz @ 2022-05-10 2:13 ` Shi, Guohuai 2022-05-10 2:17 ` Shi, Guohuai 0 siblings, 1 reply; 34+ messages in thread From: Shi, Guohuai @ 2022-05-10 2:13 UTC (permalink / raw) To: Greg Kurz; +Cc: Bin Meng, Christian Schoenebeck, qemu-devel, Meng, Bin > -----Original Message----- > From: Greg Kurz <groug@kaod.org> > Sent: 2022年5月10日 0:20 > To: Shi, Guohuai <Guohuai.Shi@windriver.com> > Cc: Bin Meng <bmeng.cn@gmail.com>; Christian Schoenebeck <qemu_oss@crudebyte.com>; > qemu-devel@nongnu.org; Meng, Bin <Bin.Meng@windriver.com> > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > Windows > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > On Mon, 9 May 2022 15:09:46 +0000 > "Shi, Guohuai" <Guohuai.Shi@windriver.com> wrote: > > > > > > > > -----Original Message----- > > > From: Greg Kurz <groug@kaod.org> > > > Sent: 2022年5月9日 22:29 > > > To: Bin Meng <bmeng.cn@gmail.com> > > > Cc: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org; > Shi, > > > Guohuai <Guohuai.Shi@windriver.com>; Meng, Bin <Bin.Meng@windriver.com> > > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver > for > > > Windows > > > > > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > > > > > On Mon, 25 Apr 2022 22:27:01 +0800 > > > Bin Meng <bmeng.cn@gmail.com> wrote: > > > > > > > From: Guohuai Shi <guohuai.shi@windriver.com> > > > > > > > > Add a 9p local file system backend driver to support Windows, > > > > including open, read, write, close, rename, remove, etc. > > > > > > > > All security models are supported. The mapped (mapped-xattr) > > > > security model is implemented using NTFS Alternate Data Stream > > > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > > > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > > > --- > > > > > > > > > > Hi ! > > > > > > I tend to agree with Christian's remarks that this patch is too big > > > and that the choice of introducing right away a new implementation > > > of 9p-local for windows hosts is too bold to start with. We need to > > > clearly understand what's diverging between windows and linux in order > > > to make such a decision. You should first try to introduce the required > > > abstractions to cope with these differences, so that we can review. > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > Windows always returns -1 when try to call open() for a directory. > > Windows (actually MinGW library) only allows opendir() for a directory. > > Does MinGW have dirfd() ? No. MinGW does not open any directory. Here is opendir() source code of MinGW: https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/dirent.c#L42 So MinGW do not have a fd associated to a directory. > > > Windows does not support APIs like "*at" (openat(), renameat(), etc.) > > Ouch... > > > So 9PFS can not use any openat() for opening a sub file or directory in 9P mount > directory. > > This commit use merge_fs_path() to build up full filename by string concatenation. > > I know that may have a risk of security, but Windows does fully support POSIX > APIs. > > > > s/does/doesn't ? > > I understand from your various answers that symlinks aren't > currently supported by window's POSIX API. Is this forever ? > Google do mentions symlinks in windows 10. What's the story > there ? How do they behave ? How would they be exposed to the > client ? Be aware that, even if the client cannot create symlinks, > an existing symlink could be used to escape with rename(). > > If the code "may have a risk of security" then it must be > fixed or avoided in some way before being merged upstream. > > Other thing that comes to mind is that windows hosts should > maybe use the mapped or mapped-file security modes since > they emulate symlinks with a simple file hidden in the > VIRTFS_META_DIR directory. > > Cheers, > > -- > Greg > Windows native API support symbolic link file start from Windows Vista: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinka I mean Windows POSIX APIs do not support symbolic link (MinGW use Win32 POSIX APIs) So we can not create symbolic link by MinGW. Anyway, there is another solution: re-work whole 9PFS code: not only 9p-local.c, but also every file in 9p driver. Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, close), by Windows Native APIs (e.g. open -> CreateFile, lseek -> SetFilePointer, read -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) Then 9P can use Windows symbolic link feature. However, I do think it is a good idea to replace everything. Thanks Guohuai > > > Some inlined remarks below anyway. > > > > > > > hw/9pfs/9p-linux-errno.h | 151 +++++ > > > > hw/9pfs/9p-local.h | 4 + > > > > hw/9pfs/9p-util.h | 41 ++ > > > > hw/9pfs/9p.h | 23 + > > > > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > > > > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > > > > hw/9pfs/9p-xattr.c | 113 ++++ > > > > hw/9pfs/9p.c | 91 ++- > > > > hw/9pfs/codir.c | 15 + > > > > 9 files changed, 1982 insertions(+), 1 deletion(-) > > > > create mode 100644 hw/9pfs/9p-linux-errno.h > > > > create mode 100644 hw/9pfs/9p-local-win32.c > > > > create mode 100644 hw/9pfs/9p-util-win32.c > > > > > > > > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > > > > new file mode 100644 > > > > index 0000000000..b0d6ac45ac > > > > --- /dev/null > > > > +++ b/hw/9pfs/9p-linux-errno.h > > > > @@ -0,0 +1,151 @@ > > > > +/* > > > > + * 9p Linux errno translation definition > > > > + * > > > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > > > + * See the COPYING file in the top-level directory. > > > > + */ > > > > + > > > > +#include <errno.h> > > > > + > > > > +#ifndef QEMU_9P_LINUX_ERRNO_H > > > > +#define QEMU_9P_LINUX_ERRNO_H > > > > + > > > > +/* > > > > + * This file contains the Linux errno definitions to translate errnos set > by > > > > + * the 9P server (running on Windows) to a corresponding errno value. > > > > + * > > > > + * This list should be periodically reviewed and updated; particularly for > > > > + * errnos that might be set as a result of a file system operation. > > > > + */ > > > > + > > > > +#define L_EPERM 1 /* Operation not permitted */ > > > > +#define L_ENOENT 2 /* No such file or directory */ > > > > +#define L_ESRCH 3 /* No such process */ > > > > +#define L_EINTR 4 /* Interrupted system call */ > > > > +#define L_EIO 5 /* I/O error */ > > > > +#define L_ENXIO 6 /* No such device or address */ > > > > +#define L_E2BIG 7 /* Argument list too long */ > > > > +#define L_ENOEXEC 8 /* Exec format error */ > > > > +#define L_EBADF 9 /* Bad file number */ > > > > +#define L_ECHILD 10 /* No child processes */ > > > > +#define L_EAGAIN 11 /* Try again */ > > > > +#define L_ENOMEM 12 /* Out of memory */ > > > > +#define L_EACCES 13 /* Permission denied */ > > > > +#define L_EFAULT 14 /* Bad address */ > > > > +#define L_ENOTBLK 15 /* Block device required */ > > > > +#define L_EBUSY 16 /* Device or resource busy */ > > > > +#define L_EEXIST 17 /* File exists */ > > > > +#define L_EXDEV 18 /* Cross-device link */ > > > > +#define L_ENODEV 19 /* No such device */ > > > > +#define L_ENOTDIR 20 /* Not a directory */ > > > > +#define L_EISDIR 21 /* Is a directory */ > > > > +#define L_EINVAL 22 /* Invalid argument */ > > > > +#define L_ENFILE 23 /* File table overflow */ > > > > +#define L_EMFILE 24 /* Too many open files */ > > > > +#define L_ENOTTY 25 /* Not a typewriter */ > > > > +#define L_ETXTBSY 26 /* Text file busy */ > > > > +#define L_EFBIG 27 /* File too large */ > > > > +#define L_ENOSPC 28 /* No space left on device */ > > > > +#define L_ESPIPE 29 /* Illegal seek */ > > > > +#define L_EROFS 30 /* Read-only file system */ > > > > +#define L_EMLINK 31 /* Too many links */ > > > > +#define L_EPIPE 32 /* Broken pipe */ > > > > +#define L_EDOM 33 /* Math argument out of domain of func */ > > > > +#define L_ERANGE 34 /* Math result not representable */ > > > > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > > > > +#define L_ENAMETOOLONG 36 /* File name too long */ > > > > +#define L_ENOLCK 37 /* No record locks available */ > > > > +#define L_ENOSYS 38 /* Function not implemented */ > > > > +#define L_ENOTEMPTY 39 /* Directory not empty */ > > > > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > > > > +#define L_ENOMSG 42 /* No message of desired type */ > > > > +#define L_EIDRM 43 /* Identifier removed */ > > > > +#define L_ECHRNG 44 /* Channel number out of range */ > > > > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > > > > +#define L_EL3HLT 46 /* Level 3 halted */ > > > > +#define L_EL3RST 47 /* Level 3 reset */ > > > > +#define L_ELNRNG 48 /* Link number out of range */ > > > > +#define L_EUNATCH 49 /* Protocol driver not attached */ > > > > +#define L_ENOCSI 50 /* No CSI structure available */ > > > > +#define L_EL2HLT 51 /* Level 2 halted */ > > > > +#define L_EBADE 52 /* Invalid exchange */ > > > > +#define L_EBADR 53 /* Invalid request descriptor */ > > > > +#define L_EXFULL 54 /* Exchange full */ > > > > +#define L_ENOANO 55 /* No anode */ > > > > +#define L_EBADRQC 56 /* Invalid request code */ > > > > +#define L_EBADSLT 57 /* Invalid slot */ > > > > +#define L_EBFONT 58 /* Bad font file format */ > > > > +#define L_ENOSTR 59 /* Device not a stream */ > > > > +#define L_ENODATA 61 /* No data available */ > > > > +#define L_ETIME 62 /* Timer expired */ > > > > +#define L_ENOSR 63 /* Out of streams resources */ > > > > +#define L_ENONET 64 /* Machine is not on the network */ > > > > +#define L_ENOPKG 65 /* Package not installed */ > > > > +#define L_EREMOTE 66 /* Object is remote */ > > > > +#define L_ENOLINK 67 /* Link has been severed */ > > > > +#define L_EADV 68 /* Advertise error */ > > > > +#define L_ESRMNT 69 /* Srmount error */ > > > > +#define L_ECOMM 70 /* Communication error on send */ > > > > +#define L_EPROTO 71 /* Protocol error */ > > > > +#define L_EMULTIHOP 72 /* Multihop attempted */ > > > > +#define L_EDOTDOT 73 /* RFS specific error */ > > > > +#define L_EBADMSG 74 /* Not a data message */ > > > > +#define L_EOVERFLOW 75 /* Value too large for defined data type */ > > > > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > > > > +#define L_EBADFD 77 /* File descriptor in bad state */ > > > > +#define L_EREMCHG 78 /* Remote address changed */ > > > > +#define L_ELIBACC 79 /* Can not access a needed shared library > */ > > > > +#define L_ELIBBAD 80 /* Accessing a corrupted shared library */ > > > > +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > > > > +#define L_ELIBMAX 82 /* Attempting to link in too many shared libs > > > */ > > > > +#define L_ELIBEXEC 83 /* Cannot exec a shared library directly */ > > > > +#define L_EILSEQ 84 /* Illegal byte sequence */ > > > > +#define L_ERESTART 85 /* Interrupted system call should be restarted > > > */ > > > > +#define L_ESTRPIPE 86 /* Streams pipe error */ > > > > +#define L_EUSERS 87 /* Too many users */ > > > > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > > > > +#define L_EDESTADDRREQ 89 /* Destination address required */ > > > > +#define L_EMSGSIZE 90 /* Message too long */ > > > > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > > > > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > > > > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > > > > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > > > > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport > endpoint > > > */ > > > > +#define L_EPFNOSUPPORT 96 /* Protocol family not supported */ > > > > +#define L_EAFNOSUPPORT 97 /* Address family not supported by protocol > */ > > > > +#define L_EADDRINUSE 98 /* Address already in use */ > > > > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > > > > +#define L_ENETDOWN 100 /* Network is down */ > > > > +#define L_ENETUNREACH 101 /* Network is unreachable */ > > > > +#define L_ENETRESET 102 /* Network dropped connection because of > reset > > > */ > > > > +#define L_ECONNABORTED 103 /* Software caused connection abort */ > > > > +#define L_ECONNRESET 104 /* Connection reset by peer */ > > > > +#define L_ENOBUFS 105 /* No buffer space available */ > > > > +#define L_EISCONN 106 /* Transport endpoint is already connected > */ > > > > +#define L_ENOTCONN 107 /* Transport endpoint is not connected */ > > > > +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint > shutdown > > > */ > > > > +#define L_ETOOMANYREFS 109 /* Too many references: cannot splice */ > > > > +#define L_ETIMEDOUT 110 /* Connection timed out */ > > > > +#define L_ECONNREFUSED 111 /* Connection refused */ > > > > +#define L_EHOSTDOWN 112 /* Host is down */ > > > > +#define L_EHOSTUNREACH 113 /* No route to host */ > > > > +#define L_EALREADY 114 /* Operation already in progress */ > > > > +#define L_EINPROGRESS 115 /* Operation now in progress */ > > > > +#define L_ESTALE 116 /* Stale NFS file handle */ > > > > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > > > > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > > > > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > > > > +#define L_EISNAM 120 /* Is a named type file */ > > > > +#define L_EREMOTEIO 121 /* Remote I/O error */ > > > > +#define L_EDQUOT 122 /* Quota exceeded */ > > > > +#define L_ENOMEDIUM 123 /* No medium found */ > > > > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > > > > +#define L_ECANCELED 125 /* Operation Canceled */ > > > > +#define L_ENOKEY 126 /* Required key not available */ > > > > +#define L_EKEYEXPIRED 127 /* Key has expired */ > > > > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > > > > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > > > > +#define L_EOWNERDEAD 130 /* Owner died */ > > > > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > > > > + > > > > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > > > > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > > > > index 55ea4b7883..4c75876e19 100644 > > > > --- a/hw/9pfs/9p-local.h > > > > +++ b/hw/9pfs/9p-local.h > > > > @@ -31,6 +31,10 @@ static inline bool local_is_mapped_file_metadata(FsContext > > > *fs_ctx, > > > > > > > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > > > mode_t mode); > > > > +#ifndef CONFIG_WIN32 > > > > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > > > +#else > > > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > > > +#endif > > > > > > > > > > Hrm... Returning a DIR * instead of a file descriptor looks like this > > > is a completely different function with different users, but you're > > > keeping the same name. What's happening here ? > > > > > > > As I mentioned above, Windows does not allow open() for a directory (return int > type file descriptor), only allows opendir() for a directory (return DIR*) > > > > > > #endif > > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > > > > index 97e681e167..6eadb38e1d 100644 > > > > --- a/hw/9pfs/9p-util.h > > > > +++ b/hw/9pfs/9p-util.h > > > > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > > > > errno = serrno; > > > > } > > > > > > > > +#ifndef CONFIG_WIN32 > > > > static inline int openat_dir(int dirfd, const char *name) > > > > { > > > > return openat(dirfd, name, > > > > @@ -89,7 +90,9 @@ again: > > > > errno = serrno; > > > > return fd; > > > > } > > > > +#endif /* !CONFIG_WIN32 */ > > > > > > > > +#ifndef CONFIG_WIN32 > > > > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > > > > void *value, size_t size); > > > > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > > > > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char > > > *filename, > > > > char *list, size_t size); > > > > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > > > > const char *name); > > > > +#else > > > > + > > > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > > > + const char *name, void *value, size_t size); > > > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > > > + const char *name, void *value, size_t size, > > > > + int flags); > > > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > > > + char *list, size_t size); > > > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > > > + const char *name); > > > > + > > > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > > > > + > > > > +static inline char *merge_fs_path(const char *path1, const char *path2) > > > > +{ > > > > + char *full_fs_path; > > > > + size_t full_fs_path_len; > > > > + > > > > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > > > > + full_fs_path = g_malloc(full_fs_path_len); > > > > > > > > + strcpy(full_fs_path, path1); > > > > + strcat(full_fs_path, "\\"); > > > > + strcat(full_fs_path, path2); > > > > + > > > > + return full_fs_path; > > > > +} > > > > + > > > > +#endif /* !CONFIG_WIN32 */ > > > > + > > > > +#ifndef CONFIG_WIN32 > > > > /* > > > > * Darwin has d_seekoff, which appears to function similarly to d_off. > > > > * However, it does not appear to be supported on all file systems, > > > > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > > > > return dent->d_off; > > > > #endif > > > > } > > > > +#endif /* !CONFIG_WIN32 */ > > > > > > > > /** > > > > * qemu_dirent_dup() - Duplicate directory entry @dent. > > > > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct > dirent > > > *dent) > > > > #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > > > > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > > > > #endif > > > > + > > > > +#ifndef CONFIG_WIN32 > > > > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > > > > +#else > > > > +int qemu_mknodat(const char *dirname, const char *filename, > > > > + mode_t mode, dev_t dev); > > > > > > Same remark as local_opendir_nofollow(). Especially, this clearly deviates > > > from the mknodat(2) semantics : dirfd is supposed to be a handle on an > > > existing directory, while dirname cannot provide such a guarantee. > > > > Windows does not have APIs like *at: openat(), mknodat(), etc. > > > > > > > > > +#endif > > > > > > > > #endif > > > > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > > > > index 994f952600..87e8eac840 100644 > > > > --- a/hw/9pfs/9p.h > > > > +++ b/hw/9pfs/9p.h > > > > @@ -3,13 +3,36 @@ > > > > > > > > #include <dirent.h> > > > > #include <utime.h> > > > > +#ifndef CONFIG_WIN32 > > > > #include <sys/resource.h> > > > > +#endif > > > > #include "fsdev/file-op-9p.h" > > > > #include "fsdev/9p-iov-marshal.h" > > > > #include "qemu/thread.h" > > > > #include "qemu/coroutine.h" > > > > #include "qemu/qht.h" > > > > > > > > +#ifdef CONFIG_WIN32 > > > > + > > > > +#define O_NOCTTY 0 > > > > +#define O_NDELAY 0 > > > > +#define O_NONBLOCK 0 > > > > +#define O_DSYNC 0 > > > > +#define O_DIRECT 0 > > > > +#define O_NOFOLLOW 0 > > > > +#define O_NOATIME 0 > > > > +#define O_SYNC 0 > > > > +#define O_ASYNC 0 > > > > +#define O_DIRECTORY 02000000 > > > > + > > > > +#define FASYNC 0 > > > > + > > > > +#define AT_REMOVEDIR 1 > > > > + > > > > +#define NAME_MAX 260 > > > > + > > > > +#endif > > > > + > > > > enum { > > > > P9_TLERROR = 6, > > > > P9_RLERROR, > > > > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > > > > new file mode 100644 > > > > index 0000000000..aab7c9f7b5 > > > > --- /dev/null > > > > +++ b/hw/9pfs/9p-local-win32.c > > > > @@ -0,0 +1,1242 @@ > > > > +/* > > > > + * 9p Windows callback > > > > + * > > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > > + * > > > > + * Based on hw/9pfs/9p-local.c > > > > + * > > > > + * This work is licensed under the terms of the GNU GPL, version 2. See > > > > + * the COPYING file in the top-level directory. > > > > + */ > > > > + > > > > +/* > > > > + * Not so fast! You might want to read the 9p developer docs first: > > > > + * https://wiki.qemu.org/Documentation/9p > > > > + */ > > > > + > > > > +#include "qemu/osdep.h" > > > > +#include <windows.h> > > > > +#include <dirent.h> > > > > +#include "9p.h" > > > > +#include "9p-local.h" > > > > +#include "9p-xattr.h" > > > > +#include "9p-util.h" > > > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > > > +#include "qapi/error.h" > > > > +#include "qemu/cutils.h" > > > > +#include "qemu/error-report.h" > > > > +#include "qemu/option.h" > > > > +#include <libgen.h> > > > > + > > > > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > > > > + int flags, mode_t mode) > > > > +{ > > > > + char *full_file_name; > > > > + int fd; > > > > + > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > > > + fd = open(full_file_name, flags | _O_BINARY, mode); > > > > > > Hmm... in order to fix CVE-2016-9602, we had to ban path based syscalls > > > because a malicious guest could easily escape the file system with a > > > symlink attack. So now we basically split all paths we get from the > > > client so that we only call *at() syscalls with a single path element > > > and a NOFOLLOW flag. > > > > > > This function seems to be doing the very opposite, which is clearly > > > a no go. > > > > Windows does not have openat(), so the only way is to build up full filename > by merge_fs_path(). > > Additionally, Windows does not support symbolic link APIs in POSIX library, so > there may not be any security risks. > > > > > > > > > + g_free(full_file_name); > > > > + > > > > + return fd; > > > > +} > > > > + > > > > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > > > > +{ > > > > + char *full_file_name; > > > > + DIR *dir; > > > > + > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > > > + dir = opendir(full_file_name); > > > > > > Same remark here. > > > > Same as above. > > > > > > > > > + g_free(full_file_name); > > > > + return dir; > > > > +} > > > > + > > > > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > > > + mode_t mode) > > > > +{ > > > > + int fd; > > > > + > > > > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { > > > > > > Why special casing '/' ? > > > > Windows does not allow open() for a directory, or any filename format like a > directory. > > > > > > > > > + /* Windows does not allow call open() for a directory */ > > > > + fd = -1; > > > > > > This -1 will ultimately cause a Rlerror message to be returned > > > to the client : an errno should be set here... but anyway, this > > > isn't the way to do it : you should just call open() and let > > > windows set the errno. > > > > > > Now, the major problem is that we really need to be able to > > > open directories because of CVE-2016-9602. Isn't it possible > > > to achieve it in some other way, e.g. opendir() and dirfd() ? > > > > As I mentioned above, Windows does not support fd (file descriptor) for directory > open(). > > > > > > > > > + } else { > > > > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); > > > > > > Does this honor the NOFOLLOW semantics ? It seems not...which is again > > > a no go. You cannot blindly assume how the underlying file system > > > handles symlinks. > > > > > > > Windows does not support symbolic link in POSIX APIs, > > > > > > + } > > > > + > > > > + return fd; > > > > +} > > > > + > > > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > > > > > +{ > > > > + return opendir_with_ctx(fs_ctx, path); > > > > +} > > > > + > > > > +static FILE *local_fopenat(const char *dirname, const char *name, > > > > + const char *mode) > > > > +{ > > > > + char *full_file_name; > > > > + char modestr[3] = {0}; > > > > + FILE *fp; > > > > + > > > > + /* > > > > + * only supports two modes > > > > + */ > > > > + if (mode[0] == 'r') { > > > > + modestr[0] = 'r'; > > > > + } else if (mode[0] == 'w') { > > > > + modestr[0] = 'w'; > > > > + } else { > > > > + return NULL; > > > > + } > > > > + /* Windows host needs 'b' flag */ > > > > + modestr[1] = 'b'; > > > > + > > > > + full_file_name = merge_fs_path(dirname, name); > > > > + fp = fopen(full_file_name, modestr); > > > > > + g_free(full_file_name); > > > > + > > > > + return fp; > > > > +} > > > > + > > > > +static void local_mapped_file_attr(const char *dirpath, const char *name, > > > > + struct stat *stbuf) > > > > +{ > > > > + FILE *fp; > > > > + char buf[ATTR_MAX]; > > > > + char *full_file_name; > > > > + > > > > + if (strcmp(name, ".") != 0) { > > > > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > > > > + fp = local_fopenat(full_file_name, name, "r"); > > > > + g_free(full_file_name); > > > > + } else { > > > > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > > > > + } > > > > + if (!fp) { > > > > + return; > > > > + } > > > > + > > > > + memset(buf, 0, ATTR_MAX); > > > > + while (fgets(buf, ATTR_MAX, fp)) { > > > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > > > + stbuf->st_uid = atoi(buf + 11); > > > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > > > + stbuf->st_gid = atoi(buf + 11); > > > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > > > > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > > > + stbuf->st_rdev = atoi(buf + 12); > > > > + } > > > > + memset(buf, 0, ATTR_MAX); > > > > + } > > > > + fclose(fp); > > > > +} > > > > + > > > > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat > > > *stbuf) > > > > +{ > > > > + int err = -1; > > > > + char *full_dir_name, *full_file_name; > > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > > + char *name = g_path_get_basename(fs_path->data); > > > > + > > > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > > + err = stat(full_file_name, stbuf); > > > > + > > > > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > > > > + /* > > > > + * Hard code for root directory on Windows host. > > > > + * This will root directory have a special inode number, > > > > + * then guest OS can detect it is a special directory. > > > > + */ > > > > + stbuf->st_ino = 2; > > > > + } > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > + /* Actual credentials are part of extended attrs */ > > > > > + uid_t tmp_uid; > > > > + gid_t tmp_gid; > > > > + mode_t tmp_mode; > > > > + dev_t tmp_dev; > > > > + > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > > > > + &tmp_uid, sizeof(uid_t)) > 0) { > > > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > > > + } > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > > > > + &tmp_gid, sizeof(gid_t)) > 0) { > > > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > > > + } > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > > > > + &tmp_mode, sizeof(mode_t)) > 0) { > > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > > > + } > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > > > > + &tmp_dev, sizeof(dev_t)) > 0) { > > > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > > > + } > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + local_mapped_file_attr(full_dir_name, name, stbuf); > > > > + } > > > > + > > > > + g_free(full_file_name); > > > > + g_free(full_dir_name); > > > > > + > > > > + if (err) { > > > > + goto err_out; > > > > + } > > > > + > > > > +err_out: > > > > + g_free(name); > > > > + g_free(dirpath); > > > > + return err; > > > > +} > > > > + > > > > +static int local_set_mapped_file_attrat(const char *dirname, const char > *name, > > > > + FsCred *credp) > > > > +{ > > > > + FILE *fp; > > > > + int ret; > > > > + char buf[ATTR_MAX]; > > > > + int uid = -1, gid = -1, mode = -1, rdev = -1; > > > > + bool is_root = !strcmp(name, "."); > > > > + char *full_dir_name; > > > > + > > > > > + if (is_root) { > > > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > > > > + if (!fp) { > > > > + if (errno == ENOENT) { > > > > + goto update_map_file; > > > > + } else { > > > > + return -1; > > > > + } > > > > + } > > > > + } else { > > > > + /* > > > > + * mapped-file: > > > > + * <sub_file> attribute stored to: > > > > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > > > > + */ > > > > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > > > > + ret = mkdir(full_dir_name); > > > > + > > > > + if (ret < 0 && errno != EEXIST) { > > > > + g_free(full_dir_name); > > > > + return -1; > > > > + } > > > > + > > > > + fp = local_fopenat(full_dir_name, name, "r"); > > > > + if (!fp) { > > > > + if (errno == ENOENT) { > > > > + goto update_map_file; > > > > + } else { > > > > + g_free(full_dir_name); > > > > + return -1; > > > > + } > > > > + } > > > > + } > > > > + > > > > + memset(buf, 0, ATTR_MAX); > > > > + while (fgets(buf, ATTR_MAX, fp)) { > > > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > > > + uid = atoi(buf + 11); > > > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > > > + gid = atoi(buf + 11); > > > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > > > + mode = atoi(buf + 12); > > > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > > > + rdev = atoi(buf + 12); > > > > + } > > > > + memset(buf, 0, ATTR_MAX); > > > > + } > > > > + fclose(fp); > > > > + > > > > +update_map_file: > > > > + if (is_root) { > > > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > > > > + } else { > > > > + fp = local_fopenat(full_dir_name, name, "w"); > > > > + g_free(full_dir_name); > > > > + } > > > > + if (!fp) { > > > > + return -1; > > > > + } > > > > + > > > > + if (credp->fc_uid != -1) { > > > > + uid = credp->fc_uid; > > > > + } > > > > + if (credp->fc_gid != -1) { > > > > + gid = credp->fc_gid; > > > > + } > > > > + if (credp->fc_mode != (mode_t)-1) { > > > > + mode = credp->fc_mode; > > > > + } > > > > + if (credp->fc_rdev != -1) { > > > > + rdev = credp->fc_rdev; > > > > + } > > > > + > > > > + if (uid != -1) { > > > > + fprintf(fp, "virtfs.uid=%d\n", uid); > > > > + } > > > > + if (gid != -1) { > > > > + fprintf(fp, "virtfs.gid=%d\n", gid); > > > > + } > > > > + if (mode != -1) { > > > > + fprintf(fp, "virtfs.mode=%d\n", mode); > > > > + } > > > > + if (rdev != -1) { > > > > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > > > > + } > > > > + fclose(fp); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static int local_set_xattrat(const char *dirname, const char *path, > > > > + FsCred *credp) > > > > +{ > > > > + int err; > > > > + > > > > + if (credp->fc_uid != -1) { > > > > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > > > > + &tmp_uid, sizeof(uid_t), 0); > > > > + if (err) { > > > > + return err; > > > > + } > > > > + } > > > > + if (credp->fc_gid != -1) { > > > > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > > > > + &tmp_gid, sizeof(gid_t), 0); > > > > + if (err) { > > > > + return err; > > > > + } > > > > + } > > > > + if (credp->fc_mode != (mode_t)-1) { > > > > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > > > > + &tmp_mode, sizeof(mode_t), 0); > > > > + if (err) { > > > > + return err; > > > > + } > > > > + } > > > > + if (credp->fc_rdev != -1) { > > > > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > > > > + &tmp_rdev, sizeof(dev_t), 0); > > > > + if (err) { > > > > + return err; > > > > + } > > > > + } > > > > + return 0; > > > > +} > > > > + > > > > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > > > > + char *buf, size_t bufsz) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > > > > +{ > > > > + return close(fs->fd); > > > > +} > > > > + > > > > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > > > > +{ > > > > + return closedir(fs->dir.stream); > > > > +} > > > > + > > > > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > > > > + int flags, V9fsFidOpenState *fs) > > > > +{ > > > > + int fd; > > > > + > > > > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > > > > + if (fd == -1) { > > > > + return -1; > > > > + } > > > > + fs->fd = fd; > > > > + return fs->fd; > > > > +} > > > > + > > > > +static int local_opendir(FsContext *ctx, > > > > + V9fsPath *fs_path, V9fsFidOpenState *fs) > > > > +{ > > > > + DIR *stream; > > > > + char *full_file_name; > > > > + > > > > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > > > > + stream = opendir(full_file_name); > > > > + g_free(full_file_name); > > > > + > > > > + if (!stream) { > > > > + return -1;local_opendir_nofollowlocal_opendir_nofollow > > > > + } > > > > + > > > > + fs->dir.stream = stream; > > > > + return 0; > > > > +} > > > > + > > > > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > > > > +{ > > > > + rewinddir(fs->dir.stream); > > > > +} > > > > + > > > > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > > > > +{ > > > > + return telldir(fs->dir.stream); > > > > +} > > > > + > > > > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > > > > +{local_opendir_nofollow > > > > + struct dirent *entry; > > > > + > > > > +again: > > > > + entry = readdir(fs->dir.stream); > > > > + if (!entry) { > > > > + return NULL; > > > > + } > > > > + > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > > > > + /* skip the meta data */ > > > > + goto again; > > > > + } > > > > + } > > > > + > > > > + return entry; > > > > +} > > > > + > > > > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) > > > > +{ > > > > + off_t count; > > > > + struct dirent *findentry; > > > > + struct dirent *entry; > > > > + size_t namelen[3] = {0}; > > > > + off_t direntoff[3] = {-1, -1, -1}; > > > > + char *d_name[3]; > > > > + int i; > > > > + > > > > + /* > > > > + * MinGW's seekdir() requires directory is not modified. If guest OS > is > > > > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", > > > > + * then MinGW's seekdir() will seek to a wrong offset. > > > > + * > > > > + * This function saves some old offset directory entry name, > > > > + * and lookup current entry again, and compare the offset. > > > > + * > > > > + * If new offset is less than old offset, that means someone is deleting > > > > + * files in the directory, thus we need to seek offset backward. > > > > + * > > > > + * If new offset is larger than old offset, that means someone is creating > > > > + * files in the directory, thus we need to seek offset forward. > > > > + */ > > > > + > > > > + direntoff[0] = telldir(fs->dir.stream); > > > > + > > > > + /* do nothing if current offset is 0 or EOF */ > > > > + if (direntoff[0] == 0 || direntoff[0] < 0) { > > > > + seekdir(fs->dir.stream, off); > > > > + return ; > > > > + } > > > > + > > > > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > > > > + d_name[1] = d_name[0] + sizeof(entry->d_name); > > > > + d_name[2] = d_name[1] + sizeof(entry->d_name); > > > > + > > > > + /* save 3 nearest entries and offsets */ > > > > + for (i = 0; i < 3; i++) { > > > > + entry = &fs->dir.stream->dd_dir; > > > > + > > > > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > > > > + namelen[i] = strlen(d_name[i]) + 1; > > > > + > > > > + direntoff[i] = telldir(fs->dir.stream); > > > > + > > > > + entry = readdir(fs->dir.stream); > > > > + if (entry == NULL) { > > > > + break; > > > > + } > > > > + } > > > > + > > > > + /* lookup saved entries again */ > > > > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > > > > + rewinddir(fs->dir.stream); > > > > + count = 0; > > > > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > > > > + count++; > > > > + > > > > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > > > > + if (count + i == direntoff[i]) { > > > > + seekdir(fs->dir.stream, off); > > > > + goto out; > > > > + } else if (count + i < direntoff[i]) { > > > > + off = off - (direntoff[i] - count) - i; > > > > + if (off <= 0) { > > > > + off = 0; > > > > + } > > > > + seekdir(fs->dir.stream, off); > > > > + goto out; > > > > + } else { > > > > + off = off + (count - direntoff[i]) - i; > > > > + seekdir(fs->dir.stream, off); > > > > + goto out; > > > > + } > > > > + }local_opendir_nofollow > > > > + } > > > > + } > > > > + /* can not get anything, seek backward */ > > > > + off = off - 1; > > > > + > > > > + seekdir(fs->dir.stream, off); > > > > +out: > > > > + g_free(d_name[0]); > > > > + return ; > > > > +} > > > > + > > > > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > > > > + const struct iovec *iov, > > > > + int iovcnt, off_t offset) > > > > +{ > > > > +#ifdef CONFIG_PREADV > > > > + return preadv(fs->fd, iov, iovcnt, offset); > > > > +#else > > > > + int err = lseek(fs->fd, offset, SEEK_SET); > > > > + if (err == -1) { > > > > + return err; > > > > + } else { > > > > + return readv(fs->fd, iov, iovcnt); > > > > + } > > > > +#endif > > > > +} > > > > + > > > > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > > > > + const struct iovec *iov, > > > > + int iovcnt, off_t offset) > > > > +{ > > > > + ssize_t ret; > > > > +#ifdef CONFIG_PREADV > > > > + ret = pwritev(fs->fd, iov, iovcnt, offset); > > > > +#else > > > > + int err = lseek(fs->fd, offset, SEEK_SET); > > > > + if (err == -1) { > > > > + return err; > > > > + } else { > > > > + ret = writev(fs->fd, iov, iovcnt); > > > > + } > > > > +#endif > > > > +#ifdef CONFIG_SYNC_FILE_RANGE > > > > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > > > > + /* > > > > + * Initiate a writeback. This is not a data integrity sync. > > > > + * We want to ensure that we don't leave dirty pages in the cache > > > > + * after write when writeout=immediate is sepcified. > > > > + */ > > > > + sync_file_range(fs->fd, offset, ret, > > > > + SYNC_FILE_RANGE_WAIT_BEFORE | > SYNC_FILE_RANGE_WRITE); > > > > + } > > > > +#endif > > > > + return ret; > > > > +} > > > > + > > > > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > > > +{ > > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > > + char *name = g_path_get_basename(fs_path->data); > > > > + int ret = -1; > > > > + char *full_file_name = NULL; > > > > + DIR *dir; > > > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > > > + if (dir == NULL) { > > > > + goto out; > > > > + } > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > + ret = local_set_xattrat(full_file_name, name, credp); > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > > > + ret = -1; > > > > + errno = ENOTSUP; > > > > + } > > > > + closedir(dir); > > > > + > > > > +out: > > > > + if (full_file_name != NULL) { > > > > + g_free(full_file_name); > > > > + } > > > > + > > > > + g_free(dirpath); > > > > + g_free(name); > > > > + return ret; > > > > +} > > > > + > > > > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > > > > + const char *name, FsCred *credp) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > > > > + const char *name, FsCred *credp) > > > > +{ > > > > + int err = -1; > > > > + char *full_file_name; > > > > + char *full_dir_name; > > > > + DIR *dir; > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > > > + errno = EINVAL; > > > > + return -1; > > > > + } > > > > + > > > > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > > > > + if (dir == NULL) { > > > > + return -1; > > > > + } > > > > + closedir(dir); > > > > + > > > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > > > > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + err = mkdir(full_file_name); > > > > + if (err == -1) { > > > > + goto out; > > > > + } > > > > + credp->fc_mode = credp->fc_mode; > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > + err = local_set_xattrat(full_dir_name, name, credp); > > > > + } else { > > > > + err = local_set_mapped_file_attrat(full_dir_name, name, credp); > > > > + } > > > > + if (err == -1) { > > > > + rmdir(full_file_name); > > > > + } > > > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > > > + err = mkdir(full_file_name); > > > > + if (err == -1) { > > > > + goto out; > > > > + } > > > > + /* Windows does not support chmod, do nothing here */ > > > > + } > > > > + > > > > + goto out; > > > > + > > > > +out: > > > > + g_free(full_dir_name); > > > > + g_free(full_file_name); > > > > + return err; > > > > +} > > > > + > > > > +static int local_fstat(FsContext *fs_ctx, int fid_type, > > > > + V9fsFidOpenState *fs, struct stat *stbuf) > > > > +{ > > > > + > > > > + int err, fd; > > > > + char filename[NAME_MAX]; > > > > + char *dirpath; > > > > + char *name; > > > > + HANDLE hFile; > > > > + DWORD dwRet; > > > > + > > > > + if (fid_type == P9_FID_DIR) { > > > > + /* Windows does not support open directory */ > > > > + return -1; > > > > + } else { > > > > + fd = fs->fd; > > > > + } > > > > + > > > > + err = fstat(fd, stbuf); > > > > + if (err) { > > > > + return err; > > > > + } > > > > + > > > > + /* get real file name by fd */ > > > > + hFile = (HANDLE)_get_osfhandle(fd); > > > > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), 0); > > > > + > > > > + if (dwRet >= NAME_MAX) { > > > > + return -1; > > > > + } > > > > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > > > > + memmove(filename, filename + 4, NAME_MAX - 4); > > > > + > > > > + dirpath = g_path_get_dirname(filename); > > > > + name = g_path_get_basename(filename); > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > + /* Actual credentials are part of extended attrs */ > > > > + uid_t tmp_uid; > > > > + gid_t tmp_gid; > > > > + mode_t tmp_mode; > > > > + dev_t tmp_dev; > > > > + > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > > > > + &tmp_uid, sizeof(uid_t)) > 0) { > > > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > > > + } > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > > > > + &tmp_gid, sizeof(gid_t)) > 0) { > > > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > > > + } > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > > > > + &tmp_mode, sizeof(mode_t)) > 0) { > > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > > > + } > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > > > > + &tmp_dev, sizeof(dev_t)) > 0) { > > > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > > > + } > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + errno = EOPNOTSUPP; > > > > + g_free(dirpath); > > > > + g_free(name); > > > > + return -1; > > > > + } > > > > + > > > > + g_free(dirpath); > > > > + g_free(name); > > > > + > > > > + return err; > > > > +} > > > > + > > > > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char > *name, > > > > + int flags, FsCred *credp, V9fsFidOpenState *fs) > > > > +{ > > > > + int fd = -1; > > > > + int err = -1; > > > > + char *full_file_name; > > > > + > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > > > + errno = EINVAL; > > > > + return -1; > > > > + } > > > > + > > > > + full_file_name = merge_fs_path(dir_path->data, name); > > > > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > > > > + g_free(full_file_name); > > > > + > > > > + err = fd; > > > > + fs->fd = fd; > > > > + goto out; > > > > + > > > > + close_preserve_errno(fd); > > > > +out: > > > > + return err; > > > > +} > > > > + > > > > + > > > > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > > > > + V9fsPath *dir_path, const char *name, FsCred *credp) > > > > +{ > > > > + return -1; > > > > > > Why ? This would require a detailed comment and also to set errno. > > > > An error number set is missing here, should be ENOTSUP. > > > > > > > > > +} > > > > + > > > > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > > > > + V9fsPath *dirpath, const char *name) > > > > +{ > > > > + return -1; > > > > > > Same remark. > > > > > > Before re-posting, you really need to come up with a detailed plan > > > around symlinks. > > > > > > Cheers, > > > > > > -- > > > Greg > > > > An error number set is missing here, should be ENOTSUP. > > Windows does not support symbolic link in its POSIX API library. > > It also require Administrator privileges and NTFS (need to turn on symbolic link > support). > > > > Currently, I think it is very hard to support symbolic link on Windows host. > > > > > > > > > +} > > > > + > > > > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > > > > +{ > > > > + int fd, ret; > > > > + > > > > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > > > > + if (fd == -1) { > > > > + return -1; > > > > + } > > > > + ret = ftruncate(fd, size); > > > > + close_preserve_errno(fd); > > > > + return ret; > > > > +} > > > > + > > > > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) > > > > +{ > > > > + char *full_file_name; > > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > > + char *name = g_path_get_basename(fs_path->data); > > > > + int ret = -1; > > > > + DIR *dir; > > > > + > > > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > > > + if (dir == NULL) { > > > > + goto out; > > > > + } > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > > + > > > > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > > > > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > > > > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > > > > + /* Windows does not support chown() */ > > > > + ret = -1; > > > > + errno = ENOTSUP; > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > + ret = local_set_xattrat(full_file_name, name, credp); > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > > > + } > > > > + g_free(full_file_name); > > > > + closedir(dir); > > > > +out: > > > > + g_free(name); > > > > + g_free(dirpath); > > > > + return ret; > > > > +} > > > > + > > > > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > > > > + const struct timespec *buf) > > > > +{ > > > > + struct utimbuf tm; > > > > + char *full_file_name; > > > > + int err; > > > > + > > > > + tm.actime = buf[0].tv_sec; > > > > + tm.modtime = buf[1].tv_sec; > > > > + > > > > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > > > > + err = utime(full_file_name, &tm); > > > > + g_free(full_file_name); > > > > + > > > > + return err; > > > > +} > > > > + > > > > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > > > > + const char *name, int flags) > > > > +{ > > > > + int ret; > > > > + char *full_file_name; > > > > + char *full_dir_name; > > > > + > > > > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > > + > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + char *full_meta_dir_name; > > > > + char *full_meta_file_name; > > > > + > > > > + /* > > > > + * We need to remove the metadata as well: > > > > + * - the metadata directory if we're removing a directory > > > > + * - the metadata file in the parent's metadata directory > > > > + * > > > > + * If any of these are missing (ie, ENOENT) then we're probably > > > > + * trying to remove something that wasn't created in mapped-file > > > > + * mode. We just ignore the error. > > > > + */ > > > > + > > > > + if ((flags & AT_REMOVEDIR) != 0) { > > > > + full_meta_dir_name = merge_fs_path(full_file_name, > > > VIRTFS_META_DIR); > > > > + ret = rmdir(full_meta_dir_name); > > > > + g_free(full_meta_dir_name); > > > > + > > > > + if (ret < 0 && errno != ENOENT) { > > > > + g_free(full_file_name); > > > > + g_free(full_dir_name); > > > > + return -1; > > > > + } > > > > + } > > > > + > > > > + full_meta_dir_name = merge_fs_path(full_dir_name, VIRTFS_META_DIR); > > > > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); > > > > + ret = remove(full_meta_file_name); > > > > + g_free(full_meta_dir_name); > > > > + g_free(full_meta_file_name); > > > > + > > > > + if (ret < 0 && errno != ENOENT) { > > > > + g_free(full_dir_name); > > > > + g_free(full_file_name); > > > > + > > > > + return -1; > > > > + } > > > > + } > > > > + > > > > + if ((flags & AT_REMOVEDIR) != 0) { > > > > + ret = rmdir(full_file_name); > > > > + } else { > > > > + ret = remove(full_file_name); > > > > + } > > > > + > > > > + g_free(full_dir_name); > > > > + g_free(full_file_name); > > > > + > > > > + return ret; > > > > +} > > > > + > > > > +static int local_remove(FsContext *ctx, const char *path) > > > > +{ > > > > + int err; > > > > + DIR *stream; > > > > + char *full_file_name; > > > > + char *dirpath = g_path_get_dirname(path); > > > > + char *name = g_path_get_basename(path); > > > > + int flags = 0; > > > > + > > > > + full_file_name = merge_fs_path(ctx->fs_root, path); > > > > + stream = opendir(full_file_name); > > > > + if (stream != NULL) { > > > > + closedir(stream); > > > > + flags |= AT_REMOVEDIR; > > > > + } > > > > + err = local_unlinkat_common(ctx, dirpath, name, flags); > > > > + > > > > + g_free(name); > > > > + g_free(dirpath); > > > > + g_free(full_file_name); > > > > + return err; > > > > +} > > > > + > > > > +static int local_fsync(FsContext *ctx, int fid_type, > > > > + V9fsFidOpenState *fs, int datasync) > > > > +{ > > > > + if (fid_type != P9_FID_DIR) { > > > > + return _commit(fs->fd); > > > > + } > > > > + return 0; > > > > +} > > > > + > > > > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs > *stbuf) > > > > +{ > > > > + int ret; > > > > + ret = qemu_statfs(s->fs_root, stbuf); > > > > + if (ret == 0) { > > > > + /* use context address as fsid */ > > > > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > > > > + } > > > > + > > > > + return ret; > > > > +} > > > > + > > > > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > > > > + const char *name, void *value, size_t size) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > > > > + void *value, size_t size) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char > *name, > > > > + void *value, size_t size, int flags) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > > > > + const char *name) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > > > > + const char *name, V9fsPath *target) > > > > +{ > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > + local_is_mapped_file_metadata(ctx, name)) { > > > > + errno = EINVAL; > > > > + return -1; > > > > + } > > > > + > > > > + if (dir_path) { > > > > + if (!strcmp(name, ".")) { > > > > + /* "." relative to "foo/bar" is "foo/bar" */ > > > > + v9fs_path_copy(target, dir_path); > > > > + } else if (!strcmp(name, "..")) { > > > > + if (!strcmp(dir_path->data, ".")) { > > > > + /* ".." relative to the root is "." */ > > > > + v9fs_path_sprintf(target, "."); > > > > + } else { > > > > + char *tmp = g_path_get_dirname(dir_path->data); > > > > + /* > > > > + * Symbolic links are resolved by the client. We can assume > > > > + * that ".." relative to "foo/bar" is equivalent to "foo" > > > > + */ > > > > + v9fs_path_sprintf(target, "%s", tmp); > > > > + g_free(tmp); > > > > + } > > > > + } else { > > > > + assert(!strchr(name, '/')); > > > > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > > > > + } > > > > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > > > > + !strcmp(name, "..")) { > > > > + /* This is the root fid */ > > > > + v9fs_path_sprintf(target, "."); > > > > + } else { > > > > + assert(!strchr(name, '/')); > > > > + v9fs_path_sprintf(target, "./%s", name); > > > > + } > > > > + return 0; > > > > +} > > > > + > > > > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > > > > + const char *old_name, V9fsPath *newdir, > > > > + const char *new_name) > > > > +{ > > > > + return -1; > > > > +} > > > > + > > > > +static int local_rename(FsContext *ctx, const char *oldpath, > > > > + const char *newpath) > > > > +{ > > > > + int err; > > > > + > > > > + char *full_old_name; > > > > + char *full_new_name; > > > > + > > > > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > > > > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > > > > + > > > > + err = rename(full_old_name, full_new_name); > > > > + > > > > + g_free(full_old_name); > > > > + g_free(full_new_name); > > > > + > > > > + return err; > > > > +} > > > > + > > > > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > > > > + const char *name, int flags) > > > > +{ > > > > + int ret; > > > > + > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > + local_is_mapped_file_metadata(ctx, name)) { > > > > + errno = EINVAL; > > > > + return -1; > > > > + } > > > > + > > > > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > > > > + > > > > + return ret; > > > > +} > > > > + > > > > +static int check_filesystem_type(char *fs_root, int export_flags) > > > > +{ > > > > + HANDLE hFile; > > > > + wchar_t FsName[MAX_PATH + 1] = {0}; > > > > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > > > > + > > > > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > > > > + return 0; > > > > + } > > > > + > > > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > > > + if (hFile == INVALID_HANDLE_VALUE) { > > > > + return -1; > > > > + } > > > > + > > > > + /* Get file system type name */ > > > > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > > > > + FsName, MAX_PATH + 1) == 0) { > > > > + CloseHandle(hFile); > > > > + return -1; > > > > + } > > > > + CloseHandle(hFile); > > > > + > > > > + if (wcscmp(FsName, NtfsName) != 0) { > > > > + return -1; > > > > + } > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static int local_init(FsContext *ctx, Error **errp) > > > > +{ > > > > + LocalData *data = g_malloc(sizeof(*data)); > > > > + > > > > + struct stat StatBuf; > > > > + > > > > + if (stat(ctx->fs_root, &StatBuf) != 0) { > > > > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > > > > + goto err; > > > > + } > > > > + > > > > + /* > > > > + * security_model=mapped(-xattr) requires a fileystem on Windows that > > > > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > > > > + * probably most popular on Windows. It is fair enough to assume > > > > + * Windows users to use NTFS for the mapped security model. > > > > + */ > > > > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > > > > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > > > > + "security_model is mapped or mapped-xattr"); > > > > + goto err; > > > > + } > > > > + > > > > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > > > > + ctx->xops = passthrough_xattr_ops; > > > > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > > > > + ctx->xops = mapped_xattr_ops; > > > > + } else if (ctx->export_flags & V9FS_SM_NONE) { > > > > + ctx->xops = none_xattr_ops; > > > > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + /* > > > > + * xattr operation for mapped-file and passthrough > > > > + * remain same. > > > > + */ > > > > + ctx->xops = passthrough_xattr_ops; > > > > + } > > > > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > > > > + > > > > + ctx->private = data; > > > > + return 0; > > > > + > > > > +err: > > > > + g_free(data); > > > > + return -1; > > > > +} > > > > + > > > > +static void local_cleanup(FsContext *ctx) > > > > +{ > > > > + LocalData *data = ctx->private; > > > > + > > > > + if (!data) { > > > > + return; > > > > + } > > > > + > > > > + close(data->mountfd); > > > > + g_free(data); > > > > +} > > > > + > > > > +static void error_append_security_model_hint(Error *const *errp) > > > > +{ > > > > + error_append_hint(errp, "Valid options are: security_model=" > > > > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > > > > +} > > > > + > > > > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error > **errp) > > > > +{ > > > > + ERRP_GUARD(); > > > > + const char *sec_model = qemu_opt_get(opts, "security_model"); > > > > + const char *path = qemu_opt_get(opts, "path"); > > > > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > > > > + > > > > + if (!sec_model) { > > > > + error_setg(errp, "security_model property not set"); > > > > + error_append_security_model_hint(errp); > > > > + return -1; > > > > + } > > > > + > > > > + if (!strcmp(sec_model, "passthrough")) { > > > > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > > > > + } else if (!strcmp(sec_model, "mapped") || > > > > + !strcmp(sec_model, "mapped-xattr")) { > > > > + fse->export_flags |= V9FS_SM_MAPPED; > > > > + } else if (!strcmp(sec_model, "none")) { > > > > + fse->export_flags |= V9FS_SM_NONE; > > > > + } else if (!strcmp(sec_model, "mapped-file")) { > > > > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > > > > + } else { > > > > + error_setg(errp, "invalid security_model property '%s'", sec_model); > > > > + error_append_security_model_hint(errp); > > > > + return -1; > > > > + } > > > > + > > > > + if (multidevs) { > > > > + if (!strcmp(multidevs, "remap")) { > > > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > > > + fse->export_flags |= V9FS_REMAP_INODES; > > > > + } else if (!strcmp(multidevs, "forbid")) { > > > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > > > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > > > > + } else if (!strcmp(multidevs, "warn")) { > > > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > > > + } else { > > > > + error_setg(errp, "invalid multidevs property '%s'", > > > > + multidevs); > > > > + error_append_hint(errp, "Valid options are: multidevs=" > > > > + "[remap|forbid|warn]\n"); > > > > + return -1; > > > > + } > > > > + } > > > > + > > > > + if (!path) { > > > > + error_setg(errp, "path property not set"); > > > > + return -1; > > > > + } > > > > + > > > > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > > > > + error_prepend(errp, "invalid throttle configuration: "); > > > > + return -1; > > > > + } > > > > + > > > > + if (fse->export_flags & V9FS_SM_MAPPED || > > > > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > > > > + fse->fmode = > > > > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > > > > + fse->dmode = > > > > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & > 0777; > > > > + } else { > > > > + if (qemu_opt_find(opts, "fmode")) { > > > > + error_setg(errp, "fmode is only valid for mapped security modes"); > > > > + return -1; > > > > + } > > > > + if (qemu_opt_find(opts, "dmode")) { > > > > + error_setg(errp, "dmode is only valid for mapped security modes"); > > > > + return -1; > > > > + } > > > > + } > > > > + > > > > + fse->path = g_strdup(path); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +FileOperations local_ops = { > > > > + .parse_opts = local_parse_opts, > > > > + .init = local_init, > > > > + .cleanup = local_cleanup, > > > > + .lstat = local_lstat, > > > > + .readlink = local_readlink, > > > > + .close = local_close, > > > > + .closedir = local_closedir, > > > > + .open = local_open, > > > > + .opendir = local_opendir, > > > > + .rewinddir = local_rewinddir, > > > > + .telldir = local_telldir, > > > > + .readdir = local_readdir, > > > > + .seekdir = local_seekdir, > > > > + .preadv = local_preadv, > > > > + .pwritev = local_pwritev, > > > > + .chmod = local_chmod, > > > > + .mknod = local_mknod, > > > > + .mkdir = local_mkdir, > > > > + .fstat = local_fstat, > > > > + .open2 = local_open2, > > > > + .symlink = local_symlink, > > > > + .link = local_link, > > > > + .truncate = local_truncate, > > > > + .rename = local_rename, > > > > + .chown = local_chown, > > > > + .utimensat = local_utimensat, > > > > + .remove = local_remove, > > > > + .fsync = local_fsync, > > > > + .statfs = local_statfs, > > > > + .lgetxattr = local_lgetxattr, > > > > + .llistxattr = local_llistxattr, > > > > + .lsetxattr = local_lsetxattr, > > > > + .lremovexattr = local_lremovexattr, > > > > + .name_to_path = local_name_to_path, > > > > + .renameat = local_renameat, > > > > + .unlinkat = local_unlinkat, > > > > +}; > > > > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > > > > new file mode 100644 > > > > index 0000000000..d9b35e7425 > > > > --- /dev/null > > > > +++ b/hw/9pfs/9p-util-win32.c > > > > @@ -0,0 +1,303 @@ > > > > +/* > > > > + * 9p utilities (Windows Implementation) > > > > + * > > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > > + * > > > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > > > + * See the COPYING file in the top-level directory. > > > > + */ > > > > + > > > > +#include "qemu/osdep.h" > > > > +#include "qapi/error.h" > > > > +#include "qemu/error-report.h" > > > > +#include "9p.h" > > > > +#include "9p-util.h" > > > > +#include "9p-linux-errno.h" > > > > +#include <windows.h> > > > > +#include <dirent.h> > > > > + > > > > +#ifndef V9FS_MAGIC > > > > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > > > > +#endif > > > > + > > > > +static int build_ads_name(char *namebuf, size_t namebuflen, > > > > + const char *dirname, const char *filename, > > > > + const char *ads_name) > > > > +{ > > > > + size_t totalsize; > > > > + > > > > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + 3; > > > > + if (totalsize > namebuflen) { > > > > + return -1; > > > > + } > > > > + > > > > + /* > > > > + * NTFS ADS (Alternate Data Streams) name format: > > > > + * filename:ads_name > > > > + * e.g. > > > > + * d:\1.txt:my_ads_name > > > > + */ > > > > + strcpy(namebuf, dirname); > > > > + strcat(namebuf, "\\"); > > > > + strcat(namebuf, filename); > > > > + strcat(namebuf, ":"); > > > > + strcat(namebuf, ads_name); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > > > > + char *fulladsname) > > > > +{ > > > > + char *p1, *p2; > > > > + > > > > + /* > > > > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > > > > + * :ads_name:$DATA > > > > + * e.g. > > > > + * :my_ads_name:$DATA > > > > + * > > > > + * ADS name from FindNextStreamW() always have ":$DATA" string at the > end > > > > + * > > > > + * This function copy ADS name to namebuf. > > > > + */ > > > > + > > > > + p1 = strchr(fulladsname, ':'); > > > > + if (p1 == NULL) { > > > > + return -1; > > > > + } > > > > + > > > > + p2 = strchr(p1 + 1, ':'); > > > > + if (p2 == NULL) { > > > > + return -1; > > > > + } > > > > + > > > > + /* skip empty ads name */ > > > > + if (p2 - p1 == 1) { > > > > + return 0; > > > > + } > > > > + > > > > + if (p2 - p1 + 1 > namebuflen) { > > > > + return -1; > > > > + } > > > > + > > > > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > > > > + namebuf[p2 - p1 - 1] = '\0'; > > > > + > > > > + return p2 - p1; > > > > +} > > > > + > > > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > > > + const char *name, void *value, size_t size) > > > > +{ > > > > + HANDLE hStream; > > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > > + DWORD dwBytesRead; > > > > + > > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < > 0) { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > > > > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > > > > + if (hStream == INVALID_HANDLE_VALUE && > > > > + GetLastError() == ERROR_FILE_NOT_FOUND) { > > > > + errno = ENODATA; > > > > + return -1; > > > > + } > > > > + > > > > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > > > > + errno = EIO; > > > > + CloseHandle(hStream); > > > > + return -1; > > > > + } > > > > + > > > > + CloseHandle(hStream); > > > > + > > > > + return dwBytesRead; > > > > +} > > > > + > > > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > > > + char *list, size_t size) > > > > +{ > > > > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > > > > + char fulladsname[NAME_MAX + 1]; > > > > + char *full_fs_name = merge_fs_path(dirname, filename); > > > > + int ret; > > > > + HANDLE hFind; > > > > + WIN32_FIND_STREAM_DATA fsd; > > > > + BOOL bFindNext; > > > > + char *listptr = list; > > > > + size_t listleftsize = size; > > > > + > > > > + /* > > > > + * ADS emurate function only have WCHAR version, need to covert filename > > > > + * to WCHAR string. > > > > + */ > > > > + > > > > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > > > > + strlen(full_fs_name), WideCharStr, NAME_MAX); > > > > + g_free(full_fs_name); > > > > + if (ret == 0) { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0); > > > > + if (hFind == INVALID_HANDLE_VALUE) { > > > > + errno = ENODATA; > > > > + return -1; > > > > + } > > > > + > > > > + do { > > > > + memset(fulladsname, 0, sizeof(fulladsname)); > > > > + > > > > + /* > > > > + * ADS emurate function only have WCHAR version, need to covert > > > > + * cStreamName to utf-8 string. > > > > + */ > > > > + > > > > + ret = WideCharToMultiByte(CP_UTF8, 0, > > > > + fsd.cStreamName, wcslen(fsd.cStreamName) + > 1, > > > > + fulladsname, sizeof(fulladsname) - 1, > > > > + NULL, NULL); > > > > + > > > > + if (ret == 0) { > > > > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > > > > + errno = ERANGE; > > > > + } > > > > + CloseHandle(hFind); > > > > + return -1; > > > > + } > > > > + > > > > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > > > > + if (ret < 0) { > > > > + errno = ERANGE; > > > > + CloseHandle(hFind); > > > > + return -1; > > > > + } > > > > + > > > > + listptr = listptr + ret; > > > > + listleftsize = listleftsize - ret; > > > > + > > > > + bFindNext = FindNextStreamW(hFind, &fsd); > > > > + } while (bFindNext); > > > > + > > > > + CloseHandle(hFind); > > > > + > > > > + return size - listleftsize; > > > > +} > > > > + > > > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > > > + const char *name) > > > > +{ > > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > > + > > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < > 0) { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + if (DeleteFile(ADSFileName) != 0) { > > > > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > > > > + errno = ENODATA; > > > > + return -1; > > > > + } > > > > + } > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > > > + const char *name, void *value, size_t size, int flags) > > > > +{ > > > > + HANDLE hStream; > > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > > + DWORD dwBytesWrite; > > > > + > > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) < > 0) { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, > > > > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); > > > > + if (hStream == INVALID_HANDLE_VALUE) { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) { > > > > + errno = EIO; > > > > + CloseHandle(hStream); > > > > + return -1; > > > > + } > > > > + > > > > + CloseHandle(hStream); > > > > + > > > > + return 0; > > > > +} > > > > + > > > > +int qemu_mknodat(const char *dirname, const char *filename, > > > > + mode_t mode, dev_t dev) > > > > +{ > > > > + errno = ENOTSUP; > > > > + return -1; > > > > +} > > > > + > > > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > > > > +{ > > > > + HANDLE hFile; > > > > + char RealPath[NAME_MAX + 1]; > > > > + unsigned long SectorsPerCluster; > > > > + unsigned long BytesPerSector; > > > > + unsigned long NumberOfFreeClusters; > > > > + unsigned long TotalNumberOfClusters; > > > > + > > > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > > > + if (hFile == INVALID_HANDLE_VALUE) { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + /* get real path of root */ > > > > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > > > > + FILE_NAME_NORMALIZED) == 0) { > > > > + errno = EIO; > > > > + CloseHandle(hFile); > > > > + return -1; > > > > + } > > > > + > > > > + CloseHandle(hFile); > > > > + > > > > + /* > > > > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > > > > + * "C:\\123" will be "\\\\?\\C:\\123" > > > > + * Skip first 4 bytes and truncate the string at offset 7, it will get > > > > + * the real root directory like "C:\\", this is parameter GetDiskFreeSpace > > > > + * needed. > > > > + */ > > > > + > > > > + RealPath[7] = '\0'; > > > > + > > > > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, &BytesPerSector, > > > > + &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) > { > > > > + errno = EIO; > > > > + return -1; > > > > + } > > > > + > > > > + stbuf->f_type = V9FS_MAGIC; > > > > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > > > > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > > > > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > > > > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > > > > + stbuf->f_files = -1; > > > > + stbuf->f_ffree = -1; > > > > + stbuf->f_namelen = NAME_MAX; > > > > + stbuf->f_frsize = 0; > > > > + stbuf->f_flags = 0; > > > > + > > > > + return 0; > > > > +} > > > > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > > > > index 9ae69dd8db..5623f0e2ef 100644 > > > > --- a/hw/9pfs/9p-xattr.c > > > > +++ b/hw/9pfs/9p-xattr.c > > > > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > > > > char *orig_value, *orig_value_start; > > > > ssize_t xattr_len, parsed_len = 0, attr_len; > > > > char *dirpath, *name; > > > > +#ifdef CONFIG_WIN32 > > > > + char *full_dir_path; > > > > + DIR *dir; > > > > +#else > > > > int dirfd; > > > > +#endif > > > > > > > > /* Get the actual len */ > > > > dirpath = g_path_get_dirname(path); > > > > + > > > > +#ifdef CONFIG_WIN32 > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > + if (dir == NULL) { > > > > + return -1; > > > > + } > > > > + closedir(dir); > > > > + > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > + g_free(dirpath); > > > > + > > > > + name = g_path_get_basename(path); > > > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > > > > + if (xattr_len <= 0) { > > > > + g_free(name); > > > > + g_free(full_dir_path); > > > > + return xattr_len; > > > > + } > > > > + > > > > + /* Now fetch the xattr and find the actual size */ > > > > + orig_value = g_malloc(xattr_len); > > > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > > > > + xattr_len); > > > > + g_free(name); > > > > + g_free(full_dir_path); > > > > + if (xattr_len < 0) { > > > > + g_free(orig_value); > > > > + return -1; > > > > + } > > > > +#else > > > > dirfd = local_opendir_nofollow(ctx, dirpath); > > > > g_free(dirpath); > > > > if (dirfd == -1) { > > > > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, > > > > g_free(orig_value); > > > > return -1; > > > > } > > > > +#endif > > > > > > > > /* store the orig pointer */ > > > > orig_value_start = orig_value; > > > > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > > > > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > > > > const char *name, void *value, size_t size) > > > > { > > > > +#ifdef CONFIG_WIN32 > > > > + char *dirpath = g_path_get_dirname(path); > > > > + char *filename = g_path_get_basename(path); > > > > + char *full_dir_path; > > > > + DIR *dir; > > > > + ssize_t ret = -1; > > > > + > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > + > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > + if (dir == NULL) { > > > > + goto out; > > > > + } > > > > + closedir(dir); > > > > + > > > > + ret = fgetxattrat_nofollow(full_dir_path, filename, > > > > + name, value, size); > > > > + > > > > +out: > > > > + g_free(full_dir_path); > > > > + g_free(dirpath); > > > > + g_free(filename); > > > > + > > > > + return ret; > > > > +#else > > > > char *dirpath = g_path_get_dirname(path); > > > > char *filename = g_path_get_basename(path); > > > > int dirfd; > > > > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const > char > > > *path, > > > > } > > > > > > > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > > > > + > > > > close_preserve_errno(dirfd); > > > > out: > > > > g_free(dirpath); > > > > g_free(filename); > > > > return ret; > > > > +#endif > > > > } > > > > > > > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > > > > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const > char > > > *path, > > > > const char *name, void *value, size_t size, > > > > int flags) > > > > { > > > > +#ifdef CONFIG_WIN32 > > > > + char *dirpath = g_path_get_dirname(path); > > > > + char *filename = g_path_get_basename(path); > > > > + char *full_dir_path; > > > > + DIR *dir; > > > > + ssize_t ret = -1; > > > > + > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > + > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > + if (dir == NULL) { > > > > + goto out; > > > > + } > > > > + closedir(dir); > > > > + > > > > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > > > > + value, size, flags); > > > > +out: > > > > + g_free(full_dir_path); > > > > + g_free(dirpath); > > > > + g_free(filename); > > > > + return ret; > > > > + > > > > +#else > > > > char *dirpath = g_path_get_dirname(path); > > > > char *filename = g_path_get_basename(path); > > > > int dirfd; > > > > @@ -210,6 +297,7 @@ out: > > > > g_free(dirpath); > > > > g_free(filename); > > > > return ret; > > > > +#endif > > > > } > > > > > > > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void > *value, > > > > @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char *path, const > > > char *name, void *value, > > > > ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, > > > > const char *name) > > > > { > > > > +#ifdef CONFIG_WIN32 > > > > + char *dirpath = g_path_get_dirname(path); > > > > + char *filename = g_path_get_basename(path); > > > > + char *full_dir_path; > > > > + DIR *dir; > > > > + ssize_t ret = -1; > > > > + > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > + > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > + if (dir == NULL) { > > > > + goto out; > > > > + } > > > > + closedir(dir); > > > > + > > > > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > > > > + > > > > +out: > > > > + g_free(full_dir_path); > > > > + g_free(dirpath); > > > > + g_free(filename); > > > > + return ret; > > > > + > > > > +#else > > > > char *dirpath = g_path_get_dirname(path); > > > > char *filename = g_path_get_basename(path); > > > > int dirfd; > > > > @@ -237,6 +349,7 @@ out: > > > > g_free(dirpath); > > > > g_free(filename); > > > > return ret; > > > > +#endif > > > > } > > > > > > > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > > > > index 225f31fc31..a04889c1d6 100644 > > > > --- a/hw/9pfs/9p.c > > > > +++ b/hw/9pfs/9p.c > > > > @@ -38,6 +38,10 @@ > > > > #include "migration/blocker.h" > > > > #include "qemu/xxhash.h" > > > > #include <math.h> > > > > +#ifdef CONFIG_WIN32 > > > > +#define UTIME_NOW ((1l << 30) - 1l) > > > > +#define UTIME_OMIT ((1l << 30) - 2l) > > > > +#endif > > > > > > > > int open_fd_hw; > > > > int total_open_fd; > > > > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat > *stbuf, > > > V9fsQID *qidp) > > > > if (S_ISDIR(stbuf->st_mode)) { > > > > qidp->type |= P9_QID_TYPE_DIR; > > > > } > > > > +#ifndef CONFIG_WIN32 > > > > if (S_ISLNK(stbuf->st_mode)) { > > > > qidp->type |= P9_QID_TYPE_SYMLINK; > > > > } > > > > +#endif > > > > > > > > return 0; > > > > } > > > > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > *extension) > > > > ret |= S_IFDIR; > > > > } > > > > > > > > +#ifndef CONFIG_WIN32 > > > > if (mode & P9_STAT_MODE_SYMLINK) { > > > > ret |= S_IFLNK; > > > > } > > > > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > *extension) > > > > if (mode & P9_STAT_MODE_NAMED_PIPE) { > > > > ret |= S_IFIFO; > > > > } > > > > +#endif > > > > if (mode & P9_STAT_MODE_DEVICE) { > > > > if (extension->size && extension->data[0] == 'c') { > > > > ret |= S_IFCHR; > > > > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > *extension) > > > > ret |= S_IFREG; > > > > } > > > > > > > > +#ifndef CONFIG_WIN32 > > > > if (mode & P9_STAT_MODE_SETUID) { > > > > ret |= S_ISUID; > > > > } > > > > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > *extension) > > > > if (mode & P9_STAT_MODE_SETVTX) { > > > > ret |= S_ISVTX; > > > > } > > > > +#endif > > > > > > > > return ret; > > > > } > > > > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > > > mode |= P9_STAT_MODE_DIR; > > > > } > > > > > > > > +#ifndef CONFIG_WIN32 > > > > if (S_ISLNK(stbuf->st_mode)) { > > > > mode |= P9_STAT_MODE_SYMLINK; > > > > } > > > > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat > *stbuf) > > > > if (S_ISFIFO(stbuf->st_mode)) { > > > > mode |= P9_STAT_MODE_NAMED_PIPE; > > > > } > > > > +#endif > > > > > > > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > > > > mode |= P9_STAT_MODE_DEVICE; > > > > } > > > > > > > > +#ifndef CONFIG_WIN32 > > > > if (stbuf->st_mode & S_ISUID) { > > > > mode |= P9_STAT_MODE_SETUID; > > > > } > > > > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf) > > > > if (stbuf->st_mode & S_ISVTX) { > > > > mode |= P9_STAT_MODE_SETVTX; > > > > } > > > > +#endif > > > > > > > > return mode; > > > > } > > > > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, > > > V9fsPath *path, > > > > return err; > > > > } > > > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > > > > +#ifndef CONFIG_WIN32 > > > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > > > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > > > > +#else > > > > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > > > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > > > + 0, 0); > > > > +#endif > > > > + > > > > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > > > > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > > > > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > > > > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, > > > int32_t blksize) > > > > > > > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > > > > { > > > > +#ifndef CONFIG_WIN32 > > > > return blksize_to_iounit(pdu, stbuf->st_blksize); > > > > +#else > > > > + return blksize_to_iounit(pdu, 0); > > > > +#endif > > > > } > > > > > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > > > > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > struct > > > stat *stbuf, > > > > v9lstat->st_gid = stbuf->st_gid; > > > > v9lstat->st_rdev = stbuf->st_rdev; > > > > v9lstat->st_size = stbuf->st_size; > > > > + > > > > +#ifdef CONFIG_WIN32 > > > > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > > > + v9lstat->st_blocks = 0; > > > > + v9lstat->st_atime_sec = stbuf->st_atime; > > > > + v9lstat->st_mtime_sec = stbuf->st_mtime; > > > > + v9lstat->st_ctime_sec = stbuf->st_ctime; > > > > +#else /* !CONFIG_WIN32 */ > > > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > > > v9lstat->st_blocks = stbuf->st_blocks; > > > > v9lstat->st_atime_sec = stbuf->st_atime; > > > > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > struct > > > stat *stbuf, > > > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > > > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > > > > #endif > > > > +#endif /* CONFIG_WIN32 */ > > > > + > > > > /* Currently we only support BASIC fields in stat */ > > > > v9lstat->st_result_mask = P9_STATS_BASIC; > > > > > > > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > > > > > > > static bool same_stat_id(const struct stat *a, const struct stat *b) > > > > { > > > > +#ifdef CONFIG_WIN32 > > > > + return 0; > > > > +#else > > > > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > > > > +#endif /* CONFIG_WIN32 */ > > > > } > > > > > > > > static void coroutine_fn v9fs_walk(void *opaque) > > > > @@ -2300,7 +2339,11 @@ static int coroutine_fn > v9fs_do_readdir_with_stat(V9fsPDU > > > *pdu, > > > > count += len; > > > > v9fs_stat_free(&v9stat); > > > > v9fs_path_free(&path); > > > > +#ifndef CONFIG_WIN32 > > > > saved_dir_pos = qemu_dirent_off(dent); > > > > +#else > > > > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > > > > +#endif > > > > } > > > > > > > > v9fs_readdir_unlock(&fidp->fs.dir); > > > > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, > > > V9fsFidState *fidp, > > > > qid.version = 0; > > > > } > > > > > > > > +#ifdef CONFIG_WIN32 > > > > + /* > > > > + * Windows does not have dent->d_off, get offset by calling telldir() > > > > + * manually. > > > > + */ > > > > + off = v9fs_co_telldir(pdu, fidp); > > > > +#else > > > > off = qemu_dirent_off(dent); > > > > +#endif > > > > v9fs_string_init(&name); > > > > v9fs_string_sprintf(&name, "%s", dent->d_name); > > > > > > > > +#ifdef CONFIG_WIN32 > > > > + /* > > > > + * Windows does not have dent->d_type > > > > + */ > > > > + > > > > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > > > > + &qid, off, > > > > + 0, &name); > > > > +#else > > > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > > > > len = pdu_marshal(pdu, 11 + count, "Qqbs", > > > > &qid, off, > > > > dent->d_type, &name); > > > > +#endif > > > > > > > > v9fs_string_free(&name); > > > > > > > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > > > > } > > > > > > > > nmode |= perm & 0777; > > > > + > > > > +#ifndef CONFIG_WIN32 > > > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > > > makedev(major, minor), nmode, &stbuf); > > > > +#else > > > > + err = -ENOTSUP; > > > > +#endif > > > > + > > > > if (err < 0) { > > > > goto out; > > > > } > > > > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > > > > v9fs_path_copy(&fidp->path, &path); > > > > v9fs_path_unlock(s); > > > > } else if (perm & P9_STAT_MODE_SOCKET) { > > > > +#ifndef CONFIG_WIN32 > > > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > > > 0, S_IFSOCK | (perm & 0777), &stbuf); > > > > +#else > > > > + err = -ENOTSUP; > > > > +#endif > > > > if (err < 0) { > > > > goto out; > > > > } > > > > @@ -3600,6 +3671,7 @@ out_nofid: > > > > static void coroutine_fn v9fs_mknod(void *opaque) > > > > { > > > > > > > > +#ifndef CONFIG_WIN32 > > > > int mode; > > > > gid_t gid; > > > > int32_t fid; > > > > @@ -3656,6 +3728,11 @@ out: > > > > out_nofid: > > > > pdu_complete(pdu, err); > > > > v9fs_string_free(&name); > > > > +#else > > > > + V9fsPDU *pdu = opaque; > > > > + > > > > + pdu_complete(pdu, -1); > > > > +#endif > > > > } > > > > > > > > /* > > > > @@ -3928,7 +4005,7 @@ out_nofid: > > > > #if defined(CONFIG_LINUX) > > > > /* Currently, only Linux has XATTR_SIZE_MAX */ > > > > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > > > > -#elif defined(CONFIG_DARWIN) > > > > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > > > > /* > > > > * Darwin doesn't seem to define a maximum xattr size in its user > > > > * space header, so manually configure it across platforms as 64k. > > > > @@ -3945,6 +4022,7 @@ out_nofid: > > > > > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > > > > { > > > > +#ifndef CONFIG_WIN32 > > > > int flags, rflags = 0; > > > > int32_t fid; > > > > uint64_t size; > > > > @@ -4006,10 +4084,15 @@ out_put_fid: > > > > out_nofid: > > > > pdu_complete(pdu, err); > > > > v9fs_string_free(&name); > > > > +#else > > > > + V9fsPDU *pdu = opaque; > > > > + pdu_complete(pdu, -1); > > > > +#endif > > > > } > > > > > > > > static void coroutine_fn v9fs_readlink(void *opaque) > > > > { > > > > +#ifndef CONFIG_WIN32 > > > > V9fsPDU *pdu = opaque; > > > > size_t offset = 7; > > > > V9fsString target; > > > > @@ -4045,6 +4128,10 @@ out: > > > > put_fid(pdu, fidp); > > > > out_nofid: > > > > pdu_complete(pdu, err); > > > > +#else > > > > + V9fsPDU *pdu = opaque; > > > > + pdu_complete(pdu, -1); > > > > +#endif > > > > } > > > > > > > > static CoroutineEntry *pdu_co_handlers[] = { > > > > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > > > > > > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > > > > { > > > > +#ifndef CONFIG_WIN32 > > > > struct rlimit rlim; > > > > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > > > > error_report("Failed to get the resource limit"); > > > > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) > > > v9fs_set_fd_limit(void) > > > > } > > > > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > > > > open_fd_rc = rlim.rlim_cur / 2; > > > > +#endif > > > > } > > > > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > > > > index 93ba44fb75..2fbe7b831b 100644 > > > > --- a/hw/9pfs/codir.c > > > > +++ b/hw/9pfs/codir.c > > > > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp, > > > > int len, err = 0; > > > > int32_t size = 0; > > > > off_t saved_dir_pos; > > > > +#ifdef CONFIG_WIN32 > > > > + off_t next_dir_pos; > > > > +#endif > > > > struct dirent *dent; > > > > struct V9fsDirEnt *e = NULL; > > > > V9fsPath path; > > > > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > *fidp, > > > > break; > > > > } > > > > > > > > +#ifdef CONFIG_WIN32 > > > > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > > > > + if (next_dir_pos < 0) { > > > > + err = next_dir_pos; > > > > + goto out; > > > > + } > > > > +#endif > > > > + > > > > /* > > > > * stop this loop as soon as it would exceed the allowed maximum > > > > * response message size for the directory entries collected so far, > > > > @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > *fidp, > > > > } > > > > > > > > size += len; > > > > +#ifndef CONFIG_WIN32 > > > > saved_dir_pos = qemu_dirent_off(dent); > > > > +#else > > > > + saved_dir_pos = next_dir_pos; > > > > +#endif > > > > } > > > > > > > > /* restore (last) saved position */ > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 2:13 ` Shi, Guohuai @ 2022-05-10 2:17 ` Shi, Guohuai 2022-05-10 10:18 ` Christian Schoenebeck 0 siblings, 1 reply; 34+ messages in thread From: Shi, Guohuai @ 2022-05-10 2:17 UTC (permalink / raw) To: Greg Kurz; +Cc: Bin Meng, Christian Schoenebeck, qemu-devel, Meng, Bin > -----Original Message----- > From: Shi, Guohuai > Sent: 2022年5月10日 10:13 > To: Greg Kurz <groug@kaod.org> > Cc: Bin Meng <bmeng.cn@gmail.com>; Christian Schoenebeck <qemu_oss@crudebyte.com>; > qemu-devel@nongnu.org; Meng, Bin <Bin.Meng@windriver.com> > Subject: RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > Windows > > > > > -----Original Message----- > > From: Greg Kurz <groug@kaod.org> > > Sent: 2022年5月10日 0:20 > > To: Shi, Guohuai <Guohuai.Shi@windriver.com> > > Cc: Bin Meng <bmeng.cn@gmail.com>; Christian Schoenebeck > <qemu_oss@crudebyte.com>; > > qemu-devel@nongnu.org; Meng, Bin <Bin.Meng@windriver.com> > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > > Windows > > > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > > > On Mon, 9 May 2022 15:09:46 +0000 > > "Shi, Guohuai" <Guohuai.Shi@windriver.com> wrote: > > > > > > > > > > > > -----Original Message----- > > > > From: Greg Kurz <groug@kaod.org> > > > > Sent: 2022年5月9日 22:29 > > > > To: Bin Meng <bmeng.cn@gmail.com> > > > > Cc: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org; > > Shi, > > > > Guohuai <Guohuai.Shi@windriver.com>; Meng, Bin <Bin.Meng@windriver.com> > > > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver > > for > > > > Windows > > > > > > > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > > > > > > > On Mon, 25 Apr 2022 22:27:01 +0800 > > > > Bin Meng <bmeng.cn@gmail.com> wrote: > > > > > > > > > From: Guohuai Shi <guohuai.shi@windriver.com> > > > > > > > > > > Add a 9p local file system backend driver to support Windows, > > > > > including open, read, write, close, rename, remove, etc. > > > > > > > > > > All security models are supported. The mapped (mapped-xattr) > > > > > security model is implemented using NTFS Alternate Data Stream > > > > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > > > > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > > > > > Signed-off-by: Bin Meng <bin.meng@windriver.com> > > > > > --- > > > > > > > > > > > > > Hi ! > > > > > > > > I tend to agree with Christian's remarks that this patch is too big > > > > and that the choice of introducing right away a new implementation > > > > of 9p-local for windows hosts is too bold to start with. We need to > > > > clearly understand what's diverging between windows and linux in order > > > > to make such a decision. You should first try to introduce the required > > > > abstractions to cope with these differences, so that we can review. > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > Windows always returns -1 when try to call open() for a directory. > > > Windows (actually MinGW library) only allows opendir() for a directory. > > > > Does MinGW have dirfd() ? > > No. > MinGW does not open any directory. > Here is opendir() source code of MinGW: > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/dirent.c#L > 42 > > So MinGW do not have a fd associated to a directory. > > > > > > > Windows does not support APIs like "*at" (openat(), renameat(), etc.) > > > > Ouch... > > > > > So 9PFS can not use any openat() for opening a sub file or directory in 9P > mount > > directory. > > > This commit use merge_fs_path() to build up full filename by string > concatenation. > > > I know that may have a risk of security, but Windows does fully support POSIX > > APIs. > > > > > > > s/does/doesn't ? > > > > I understand from your various answers that symlinks aren't > > currently supported by window's POSIX API. Is this forever ? > > Google do mentions symlinks in windows 10. What's the story > > there ? How do they behave ? How would they be exposed to the > > client ? Be aware that, even if the client cannot create symlinks, > > an existing symlink could be used to escape with rename(). > > > > If the code "may have a risk of security" then it must be > > fixed or avoided in some way before being merged upstream. > > > > Other thing that comes to mind is that windows hosts should > > maybe use the mapped or mapped-file security modes since > > they emulate symlinks with a simple file hidden in the > > VIRTFS_META_DIR directory. > > > > Cheers, > > > > -- > > Greg > > > > Windows native API support symbolic link file start from Windows Vista: > https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-creates > ymboliclinka > > I mean Windows POSIX APIs do not support symbolic link (MinGW use Win32 POSIX APIs) > So we can not create symbolic link by MinGW. > > Anyway, there is another solution: re-work whole 9PFS code: not only 9p-local.c, > but also every file in 9p driver. > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, close), > by Windows Native APIs (e.g. open -> CreateFile, lseek -> SetFilePointer, read > -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) > Then 9P can use Windows symbolic link feature. > However, I do think it is a good idea to replace everything. TYPO: it NOT is a good idea to replace everything. > > Thanks > Guohuai > > > > > Some inlined remarks below anyway. > > > > > > > > > hw/9pfs/9p-linux-errno.h | 151 +++++ > > > > > hw/9pfs/9p-local.h | 4 + > > > > > hw/9pfs/9p-util.h | 41 ++ > > > > > hw/9pfs/9p.h | 23 + > > > > > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > > > > > hw/9pfs/9p-util-win32.c | 303 ++++++++++ > > > > > hw/9pfs/9p-xattr.c | 113 ++++ > > > > > hw/9pfs/9p.c | 91 ++- > > > > > hw/9pfs/codir.c | 15 + > > > > > 9 files changed, 1982 insertions(+), 1 deletion(-) > > > > > create mode 100644 hw/9pfs/9p-linux-errno.h > > > > > create mode 100644 hw/9pfs/9p-local-win32.c > > > > > create mode 100644 hw/9pfs/9p-util-win32.c > > > > > > > > > > diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h > > > > > new file mode 100644 > > > > > index 0000000000..b0d6ac45ac > > > > > --- /dev/null > > > > > +++ b/hw/9pfs/9p-linux-errno.h > > > > > @@ -0,0 +1,151 @@ > > > > > +/* > > > > > + * 9p Linux errno translation definition > > > > > + * > > > > > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. > > > > > + * See the COPYING file in the top-level directory. > > > > > + */ > > > > > + > > > > > +#include <errno.h> > > > > > + > > > > > +#ifndef QEMU_9P_LINUX_ERRNO_H > > > > > +#define QEMU_9P_LINUX_ERRNO_H > > > > > + > > > > > +/* > > > > > + * This file contains the Linux errno definitions to translate errnos > set > > by > > > > > + * the 9P server (running on Windows) to a corresponding errno value. > > > > > + * > > > > > + * This list should be periodically reviewed and updated; particularly > for > > > > > + * errnos that might be set as a result of a file system operation. > > > > > + */ > > > > > + > > > > > +#define L_EPERM 1 /* Operation not permitted */ > > > > > +#define L_ENOENT 2 /* No such file or directory */ > > > > > +#define L_ESRCH 3 /* No such process */ > > > > > +#define L_EINTR 4 /* Interrupted system call */ > > > > > +#define L_EIO 5 /* I/O error */ > > > > > +#define L_ENXIO 6 /* No such device or address */ > > > > > +#define L_E2BIG 7 /* Argument list too long */ > > > > > +#define L_ENOEXEC 8 /* Exec format error */ > > > > > +#define L_EBADF 9 /* Bad file number */ > > > > > +#define L_ECHILD 10 /* No child processes */ > > > > > +#define L_EAGAIN 11 /* Try again */ > > > > > +#define L_ENOMEM 12 /* Out of memory */ > > > > > +#define L_EACCES 13 /* Permission denied */ > > > > > +#define L_EFAULT 14 /* Bad address */ > > > > > +#define L_ENOTBLK 15 /* Block device required */ > > > > > +#define L_EBUSY 16 /* Device or resource busy */ > > > > > +#define L_EEXIST 17 /* File exists */ > > > > > +#define L_EXDEV 18 /* Cross-device link */ > > > > > +#define L_ENODEV 19 /* No such device */ > > > > > +#define L_ENOTDIR 20 /* Not a directory */ > > > > > +#define L_EISDIR 21 /* Is a directory */ > > > > > +#define L_EINVAL 22 /* Invalid argument */ > > > > > +#define L_ENFILE 23 /* File table overflow */ > > > > > +#define L_EMFILE 24 /* Too many open files */ > > > > > +#define L_ENOTTY 25 /* Not a typewriter */ > > > > > +#define L_ETXTBSY 26 /* Text file busy */ > > > > > +#define L_EFBIG 27 /* File too large */ > > > > > +#define L_ENOSPC 28 /* No space left on device */ > > > > > +#define L_ESPIPE 29 /* Illegal seek */ > > > > > +#define L_EROFS 30 /* Read-only file system */ > > > > > +#define L_EMLINK 31 /* Too many links */ > > > > > +#define L_EPIPE 32 /* Broken pipe */ > > > > > +#define L_EDOM 33 /* Math argument out of domain of func */ > > > > > +#define L_ERANGE 34 /* Math result not representable */ > > > > > +#define L_EDEADLK 35 /* Resource deadlock would occur */ > > > > > +#define L_ENAMETOOLONG 36 /* File name too long */ > > > > > +#define L_ENOLCK 37 /* No record locks available */ > > > > > +#define L_ENOSYS 38 /* Function not implemented */ > > > > > +#define L_ENOTEMPTY 39 /* Directory not empty */ > > > > > +#define L_ELOOP 40 /* Too many symbolic links encountered */ > > > > > +#define L_ENOMSG 42 /* No message of desired type */ > > > > > +#define L_EIDRM 43 /* Identifier removed */ > > > > > +#define L_ECHRNG 44 /* Channel number out of range */ > > > > > +#define L_EL2NSYNC 45 /* Level 2 not synchronized */ > > > > > +#define L_EL3HLT 46 /* Level 3 halted */ > > > > > +#define L_EL3RST 47 /* Level 3 reset */ > > > > > +#define L_ELNRNG 48 /* Link number out of range */ > > > > > +#define L_EUNATCH 49 /* Protocol driver not attached */ > > > > > +#define L_ENOCSI 50 /* No CSI structure available */ > > > > > +#define L_EL2HLT 51 /* Level 2 halted */ > > > > > +#define L_EBADE 52 /* Invalid exchange */ > > > > > +#define L_EBADR 53 /* Invalid request descriptor */ > > > > > +#define L_EXFULL 54 /* Exchange full */ > > > > > +#define L_ENOANO 55 /* No anode */ > > > > > +#define L_EBADRQC 56 /* Invalid request code */ > > > > > +#define L_EBADSLT 57 /* Invalid slot */ > > > > > +#define L_EBFONT 58 /* Bad font file format */ > > > > > +#define L_ENOSTR 59 /* Device not a stream */ > > > > > +#define L_ENODATA 61 /* No data available */ > > > > > +#define L_ETIME 62 /* Timer expired */ > > > > > +#define L_ENOSR 63 /* Out of streams resources */ > > > > > +#define L_ENONET 64 /* Machine is not on the network */ > > > > > +#define L_ENOPKG 65 /* Package not installed */ > > > > > +#define L_EREMOTE 66 /* Object is remote */ > > > > > +#define L_ENOLINK 67 /* Link has been severed */ > > > > > +#define L_EADV 68 /* Advertise error */ > > > > > +#define L_ESRMNT 69 /* Srmount error */ > > > > > +#define L_ECOMM 70 /* Communication error on send */ > > > > > +#define L_EPROTO 71 /* Protocol error */ > > > > > +#define L_EMULTIHOP 72 /* Multihop attempted */ > > > > > +#define L_EDOTDOT 73 /* RFS specific error */ > > > > > +#define L_EBADMSG 74 /* Not a data message */ > > > > > +#define L_EOVERFLOW 75 /* Value too large for defined data type > */ > > > > > +#define L_ENOTUNIQ 76 /* Name not unique on network */ > > > > > +#define L_EBADFD 77 /* File descriptor in bad state */ > > > > > +#define L_EREMCHG 78 /* Remote address changed */ > > > > > +#define L_ELIBACC 79 /* Can not access a needed shared library > > */ > > > > > +#define L_ELIBBAD 80 /* Accessing a corrupted shared library > */ > > > > > +#define L_ELIBSCN 81 /* .lib section in a.out corrupted */ > > > > > +#define L_ELIBMAX 82 /* Attempting to link in too many shared > libs > > > > */ > > > > > +#define L_ELIBEXEC 83 /* Cannot exec a shared library directly > */ > > > > > +#define L_EILSEQ 84 /* Illegal byte sequence */ > > > > > +#define L_ERESTART 85 /* Interrupted system call should be > restarted > > > > */ > > > > > +#define L_ESTRPIPE 86 /* Streams pipe error */ > > > > > +#define L_EUSERS 87 /* Too many users */ > > > > > +#define L_ENOTSOCK 88 /* Socket operation on non-socket */ > > > > > +#define L_EDESTADDRREQ 89 /* Destination address required */ > > > > > +#define L_EMSGSIZE 90 /* Message too long */ > > > > > +#define L_EPROTOTYPE 91 /* Protocol wrong type for socket */ > > > > > +#define L_ENOPROTOOPT 92 /* Protocol not available */ > > > > > +#define L_EPROTONOSUPPORT 93 /* Protocol not supported */ > > > > > +#define L_ESOCKTNOSUPPORT 94 /* Socket type not supported */ > > > > > +#define L_EOPNOTSUPP 95 /* Operation not supported on transport > > endpoint > > > > */ > > > > > +#define L_EPFNOSUPPORT 96 /* Protocol family not supported */ > > > > > +#define L_EAFNOSUPPORT 97 /* Address family not supported by protocol > > */ > > > > > +#define L_EADDRINUSE 98 /* Address already in use */ > > > > > +#define L_EADDRNOTAVAIL 99 /* Cannot assign requested address */ > > > > > +#define L_ENETDOWN 100 /* Network is down */ > > > > > +#define L_ENETUNREACH 101 /* Network is unreachable */ > > > > > +#define L_ENETRESET 102 /* Network dropped connection because of > > reset > > > > */ > > > > > +#define L_ECONNABORTED 103 /* Software caused connection abort */ > > > > > +#define L_ECONNRESET 104 /* Connection reset by peer */ > > > > > +#define L_ENOBUFS 105 /* No buffer space available */ > > > > > +#define L_EISCONN 106 /* Transport endpoint is already connected > > */ > > > > > +#define L_ENOTCONN 107 /* Transport endpoint is not connected */ > > > > > +#define L_ESHUTDOWN 108 /* Cannot send after transport endpoint > > shutdown > > > > */ > > > > > +#define L_ETOOMANYREFS 109 /* Too many references: cannot splice */ > > > > > +#define L_ETIMEDOUT 110 /* Connection timed out */ > > > > > +#define L_ECONNREFUSED 111 /* Connection refused */ > > > > > +#define L_EHOSTDOWN 112 /* Host is down */ > > > > > +#define L_EHOSTUNREACH 113 /* No route to host */ > > > > > +#define L_EALREADY 114 /* Operation already in progress */ > > > > > +#define L_EINPROGRESS 115 /* Operation now in progress */ > > > > > +#define L_ESTALE 116 /* Stale NFS file handle */ > > > > > +#define L_EUCLEAN 117 /* Structure needs cleaning */ > > > > > +#define L_ENOTNAM 118 /* Not a XENIX named type file */ > > > > > +#define L_ENAVAIL 119 /* No XENIX semaphores available */ > > > > > +#define L_EISNAM 120 /* Is a named type file */ > > > > > +#define L_EREMOTEIO 121 /* Remote I/O error */ > > > > > +#define L_EDQUOT 122 /* Quota exceeded */ > > > > > +#define L_ENOMEDIUM 123 /* No medium found */ > > > > > +#define L_EMEDIUMTYPE 124 /* Wrong medium type */ > > > > > +#define L_ECANCELED 125 /* Operation Canceled */ > > > > > +#define L_ENOKEY 126 /* Required key not available */ > > > > > +#define L_EKEYEXPIRED 127 /* Key has expired */ > > > > > +#define L_EKEYREVOKED 128 /* Key has been revoked */ > > > > > +#define L_EKEYREJECTED 129 /* Key was rejected by service */ > > > > > +#define L_EOWNERDEAD 130 /* Owner died */ > > > > > +#define L_ENOTRECOVERABLE 131 /* State not recoverable */ > > > > > + > > > > > +#endif /* QEMU_9P_LINUX_ERRNO_H */ > > > > > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h > > > > > index 55ea4b7883..4c75876e19 100644 > > > > > --- a/hw/9pfs/9p-local.h > > > > > +++ b/hw/9pfs/9p-local.h > > > > > @@ -31,6 +31,10 @@ static inline bool > local_is_mapped_file_metadata(FsContext > > > > *fs_ctx, > > > > > > > > > > int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > > > > mode_t mode); > > > > > +#ifndef CONFIG_WIN32 > > > > > int local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > > > > +#else > > > > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path); > > > > > +#endif > > > > > > > > > > > > > Hrm... Returning a DIR * instead of a file descriptor looks like this > > > > is a completely different function with different users, but you're > > > > keeping the same name. What's happening here ? > > > > > > > > > > As I mentioned above, Windows does not allow open() for a directory (return > int > > type file descriptor), only allows opendir() for a directory (return DIR*) > > > > > > > > #endif > > > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h > > > > > index 97e681e167..6eadb38e1d 100644 > > > > > --- a/hw/9pfs/9p-util.h > > > > > +++ b/hw/9pfs/9p-util.h > > > > > @@ -43,6 +43,7 @@ static inline void close_preserve_errno(int fd) > > > > > errno = serrno; > > > > > } > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > static inline int openat_dir(int dirfd, const char *name) > > > > > { > > > > > return openat(dirfd, name, > > > > > @@ -89,7 +90,9 @@ again: > > > > > errno = serrno; > > > > > return fd; > > > > > } > > > > > +#endif /* !CONFIG_WIN32 */ > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, > > > > > void *value, size_t size); > > > > > int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, > > > > > @@ -98,7 +101,38 @@ ssize_t flistxattrat_nofollow(int dirfd, const char > > > > *filename, > > > > > char *list, size_t size); > > > > > ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, > > > > > const char *name); > > > > > +#else > > > > > + > > > > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > > > > + const char *name, void *value, size_t size); > > > > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > > > > + const char *name, void *value, size_t size, > > > > > + int flags); > > > > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > > > > + char *list, size_t size); > > > > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > > > > + const char *name); > > > > > + > > > > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf); > > > > > + > > > > > +static inline char *merge_fs_path(const char *path1, const char *path2) > > > > > +{ > > > > > + char *full_fs_path; > > > > > + size_t full_fs_path_len; > > > > > + > > > > > + full_fs_path_len = strlen(path1) + strlen(path2) + 2; > > > > > + full_fs_path = g_malloc(full_fs_path_len); > > > > > > > > > > + strcpy(full_fs_path, path1); > > > > > + strcat(full_fs_path, "\\"); > > > > > + strcat(full_fs_path, path2); > > > > > + > > > > > + return full_fs_path; > > > > > +} > > > > > + > > > > > +#endif /* !CONFIG_WIN32 */ > > > > > + > > > > > +#ifndef CONFIG_WIN32 > > > > > /* > > > > > * Darwin has d_seekoff, which appears to function similarly to d_off. > > > > > * However, it does not appear to be supported on all file systems, > > > > > @@ -113,6 +147,7 @@ static inline off_t qemu_dirent_off(struct dirent *dent) > > > > > return dent->d_off; > > > > > #endif > > > > > } > > > > > +#endif /* !CONFIG_WIN32 */ > > > > > > > > > > /** > > > > > * qemu_dirent_dup() - Duplicate directory entry @dent. > > > > > @@ -154,6 +189,12 @@ static inline struct dirent *qemu_dirent_dup(struct > > dirent > > > > *dent) > > > > > #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP > > > > > int pthread_fchdir_np(int fd) __attribute__((weak_import)); > > > > > #endif > > > > > + > > > > > +#ifndef CONFIG_WIN32 > > > > > int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); > > > > > +#else > > > > > +int qemu_mknodat(const char *dirname, const char *filename, > > > > > + mode_t mode, dev_t dev); > > > > > > > > Same remark as local_opendir_nofollow(). Especially, this clearly deviates > > > > from the mknodat(2) semantics : dirfd is supposed to be a handle on an > > > > existing directory, while dirname cannot provide such a guarantee. > > > > > > Windows does not have APIs like *at: openat(), mknodat(), etc. > > > > > > > > > > > > +#endif > > > > > > > > > > #endif > > > > > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > > > > > index 994f952600..87e8eac840 100644 > > > > > --- a/hw/9pfs/9p.h > > > > > +++ b/hw/9pfs/9p.h > > > > > @@ -3,13 +3,36 @@ > > > > > > > > > > #include <dirent.h> > > > > > #include <utime.h> > > > > > +#ifndef CONFIG_WIN32 > > > > > #include <sys/resource.h> > > > > > +#endif > > > > > #include "fsdev/file-op-9p.h" > > > > > #include "fsdev/9p-iov-marshal.h" > > > > > #include "qemu/thread.h" > > > > > #include "qemu/coroutine.h" > > > > > #include "qemu/qht.h" > > > > > > > > > > +#ifdef CONFIG_WIN32 > > > > > + > > > > > +#define O_NOCTTY 0 > > > > > +#define O_NDELAY 0 > > > > > +#define O_NONBLOCK 0 > > > > > +#define O_DSYNC 0 > > > > > +#define O_DIRECT 0 > > > > > +#define O_NOFOLLOW 0 > > > > > +#define O_NOATIME 0 > > > > > +#define O_SYNC 0 > > > > > +#define O_ASYNC 0 > > > > > +#define O_DIRECTORY 02000000 > > > > > + > > > > > +#define FASYNC 0 > > > > > + > > > > > +#define AT_REMOVEDIR 1 > > > > > + > > > > > +#define NAME_MAX 260 > > > > > + > > > > > +#endif > > > > > + > > > > > enum { > > > > > P9_TLERROR = 6, > > > > > P9_RLERROR, > > > > > diff --git a/hw/9pfs/9p-local-win32.c b/hw/9pfs/9p-local-win32.c > > > > > new file mode 100644 > > > > > index 0000000000..aab7c9f7b5 > > > > > --- /dev/null > > > > > +++ b/hw/9pfs/9p-local-win32.c > > > > > @@ -0,0 +1,1242 @@ > > > > > +/* > > > > > + * 9p Windows callback > > > > > + * > > > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > > > + * > > > > > + * Based on hw/9pfs/9p-local.c > > > > > + * > > > > > + * This work is licensed under the terms of the GNU GPL, version 2. See > > > > > + * the COPYING file in the top-level directory. > > > > > + */ > > > > > + > > > > > +/* > > > > > + * Not so fast! You might want to read the 9p developer docs first: > > > > > + * https://wiki.qemu.org/Documentation/9p > > > > > + */ > > > > > + > > > > > +#include "qemu/osdep.h" > > > > > +#include <windows.h> > > > > > +#include <dirent.h> > > > > > +#include "9p.h" > > > > > +#include "9p-local.h" > > > > > +#include "9p-xattr.h" > > > > > +#include "9p-util.h" > > > > > +#include "fsdev/qemu-fsdev.h" /* local_ops */ > > > > > +#include "qapi/error.h" > > > > > +#include "qemu/cutils.h" > > > > > +#include "qemu/error-report.h" > > > > > +#include "qemu/option.h" > > > > > +#include <libgen.h> > > > > > + > > > > > +static inline int openfile_with_ctx(FsContext *fs_ctx, const char *name, > > > > > + int flags, mode_t mode) > > > > > +{ > > > > > + char *full_file_name; > > > > > + int fd; > > > > > + > > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > > > > + fd = open(full_file_name, flags | _O_BINARY, mode); > > > > > > > > Hmm... in order to fix CVE-2016-9602, we had to ban path based syscalls > > > > because a malicious guest could easily escape the file system with a > > > > symlink attack. So now we basically split all paths we get from the > > > > client so that we only call *at() syscalls with a single path element > > > > and a NOFOLLOW flag. > > > > > > > > This function seems to be doing the very opposite, which is clearly > > > > a no go. > > > > > > Windows does not have openat(), so the only way is to build up full filename > > by merge_fs_path(). > > > Additionally, Windows does not support symbolic link APIs in POSIX library, > so > > there may not be any security risks. > > > > > > > > > > > > + g_free(full_file_name); > > > > > + > > > > > + return fd; > > > > > +} > > > > > + > > > > > +static inline DIR *opendir_with_ctx(FsContext *fs_ctx, const char *name) > > > > > +{ > > > > > + char *full_file_name; > > > > > + DIR *dir; > > > > > + > > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, name); > > > > > + dir = opendir(full_file_name); > > > > > > > > Same remark here. > > > > > > Same as above. > > > > > > > > > > > > + g_free(full_file_name); > > > > > + return dir; > > > > > +} > > > > > + > > > > > +int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, > > > > > + mode_t mode) > > > > > +{ > > > > > + int fd; > > > > > + > > > > > + if (path[strlen(path) - 1] == '/' || (flags & O_DIRECTORY) != 0) { > > > > > > > > Why special casing '/' ? > > > > > > Windows does not allow open() for a directory, or any filename format like > a > > directory. > > > > > > > > > > > > + /* Windows does not allow call open() for a directory */ > > > > > + fd = -1; > > > > > > > > This -1 will ultimately cause a Rlerror message to be returned > > > > to the client : an errno should be set here... but anyway, this > > > > isn't the way to do it : you should just call open() and let > > > > windows set the errno. > > > > > > > > Now, the major problem is that we really need to be able to > > > > open directories because of CVE-2016-9602. Isn't it possible > > > > to achieve it in some other way, e.g. opendir() and dirfd() ? > > > > > > As I mentioned above, Windows does not support fd (file descriptor) for directory > > open(). > > > > > > > > > > > > + } else { > > > > > + fd = openfile_with_ctx(fs_ctx, path, flags, mode); > > > > > > > > Does this honor the NOFOLLOW semantics ? It seems not...which is again > > > > a no go. You cannot blindly assume how the underlying file system > > > > handles symlinks. > > > > > > > > > > Windows does not support symbolic link in POSIX APIs, > > > > > > > > + } > > > > > + > > > > > + return fd; > > > > > +} > > > > > + > > > > > +DIR *local_opendir_nofollow(FsContext *fs_ctx, const char *path) > > > > > > > +{ > > > > > + return opendir_with_ctx(fs_ctx, path); > > > > > +} > > > > > + > > > > > +static FILE *local_fopenat(const char *dirname, const char *name, > > > > > + const char *mode) > > > > > +{ > > > > > + char *full_file_name; > > > > > + char modestr[3] = {0}; > > > > > + FILE *fp; > > > > > + > > > > > + /* > > > > > + * only supports two modes > > > > > + */ > > > > > + if (mode[0] == 'r') { > > > > > + modestr[0] = 'r'; > > > > > + } else if (mode[0] == 'w') { > > > > > + modestr[0] = 'w'; > > > > > + } else { > > > > > + return NULL; > > > > > + } > > > > > + /* Windows host needs 'b' flag */ > > > > > + modestr[1] = 'b'; > > > > > + > > > > > + full_file_name = merge_fs_path(dirname, name); > > > > > + fp = fopen(full_file_name, modestr); > > > > > > > + g_free(full_file_name); > > > > > + > > > > > + return fp; > > > > > +} > > > > > + > > > > > +static void local_mapped_file_attr(const char *dirpath, const char *name, > > > > > + struct stat *stbuf) > > > > > +{ > > > > > + FILE *fp; > > > > > + char buf[ATTR_MAX]; > > > > > + char *full_file_name; > > > > > + > > > > > + if (strcmp(name, ".") != 0) { > > > > > + full_file_name = merge_fs_path(dirpath, VIRTFS_META_DIR); > > > > > + fp = local_fopenat(full_file_name, name, "r"); > > > > > + g_free(full_file_name); > > > > > + } else { > > > > > + fp = local_fopenat(dirpath, VIRTFS_META_ROOT_FILE, "r"); > > > > > + } > > > > > + if (!fp) { > > > > > + return; > > > > > + } > > > > > + > > > > > + memset(buf, 0, ATTR_MAX); > > > > > + while (fgets(buf, ATTR_MAX, fp)) { > > > > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > > > > + stbuf->st_uid = atoi(buf + 11); > > > > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > > > > + stbuf->st_gid = atoi(buf + 11); > > > > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > > > + stbuf->st_mode |= (atoi(buf + 12) & 0777); > > > > > > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > > > > + stbuf->st_rdev = atoi(buf + 12); > > > > > + } > > > > > + memset(buf, 0, ATTR_MAX); > > > > > + } > > > > > + fclose(fp); > > > > > +} > > > > > + > > > > > +static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat > > > > *stbuf) > > > > > +{ > > > > > + int err = -1; > > > > > + char *full_dir_name, *full_file_name; > > > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > > > + char *name = g_path_get_basename(fs_path->data); > > > > > + > > > > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > > > + err = stat(full_file_name, stbuf); > > > > > + > > > > > + if (err == 0 && strcmp(fs_path->data, ".") == 0) { > > > > > + /* > > > > > + * Hard code for root directory on Windows host. > > > > > + * This will root directory have a special inode number, > > > > > + * then guest OS can detect it is a special directory. > > > > > + */ > > > > > + stbuf->st_ino = 2; > > > > > + } > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > > + /* Actual credentials are part of extended attrs */ > > > > > > > + uid_t tmp_uid; > > > > > + gid_t tmp_gid; > > > > > + mode_t tmp_mode; > > > > > + dev_t tmp_dev; > > > > > + > > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.uid", > > > > > + &tmp_uid, sizeof(uid_t)) > 0) { > > > > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > > > > + } > > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.gid", > > > > > + &tmp_gid, sizeof(gid_t)) > 0) { > > > > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > > > > + } > > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.mode", > > > > > + &tmp_mode, sizeof(mode_t)) > 0) { > > > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > > > > + } > > > > > + if (fgetxattrat_nofollow(full_dir_name, name, "user.virtfs.rdev", > > > > > + &tmp_dev, sizeof(dev_t)) > 0) { > > > > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > > > > + } > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + local_mapped_file_attr(full_dir_name, name, stbuf); > > > > > + } > > > > > + > > > > > + g_free(full_file_name); > > > > > + g_free(full_dir_name); > > > > > > > + > > > > > + if (err) { > > > > > + goto err_out; > > > > > + } > > > > > + > > > > > +err_out: > > > > > + g_free(name); > > > > > + g_free(dirpath); > > > > > + return err; > > > > > +} > > > > > + > > > > > +static int local_set_mapped_file_attrat(const char *dirname, const char > > *name, > > > > > + FsCred *credp) > > > > > +{ > > > > > + FILE *fp; > > > > > + int ret; > > > > > + char buf[ATTR_MAX]; > > > > > + int uid = -1, gid = -1, mode = -1, rdev = -1; > > > > > + bool is_root = !strcmp(name, "."); > > > > > + char *full_dir_name; > > > > > + > > > > > > > + if (is_root) { > > > > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "r"); > > > > > + if (!fp) { > > > > > + if (errno == ENOENT) { > > > > > + goto update_map_file; > > > > > + } else { > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + } else { > > > > > + /* > > > > > + * mapped-file: > > > > > + * <sub_file> attribute stored to: > > > > > + * <directory> + VIRTFS_META_DIR + <sub_file_name> > > > > > + */ > > > > > + full_dir_name = merge_fs_path(dirname, VIRTFS_META_DIR); > > > > > + ret = mkdir(full_dir_name); > > > > > + > > > > > + if (ret < 0 && errno != EEXIST) { > > > > > + g_free(full_dir_name); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + fp = local_fopenat(full_dir_name, name, "r"); > > > > > + if (!fp) { > > > > > + if (errno == ENOENT) { > > > > > + goto update_map_file; > > > > > + } else { > > > > > + g_free(full_dir_name); > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + } > > > > > + > > > > > + memset(buf, 0, ATTR_MAX); > > > > > + while (fgets(buf, ATTR_MAX, fp)) { > > > > > + if (!strncmp(buf, "virtfs.uid", 10)) { > > > > > + uid = atoi(buf + 11); > > > > > + } else if (!strncmp(buf, "virtfs.gid", 10)) { > > > > > + gid = atoi(buf + 11); > > > > > + } else if (!strncmp(buf, "virtfs.mode", 11)) { > > > > > + mode = atoi(buf + 12); > > > > > + } else if (!strncmp(buf, "virtfs.rdev", 11)) { > > > > > + rdev = atoi(buf + 12); > > > > > + } > > > > > + memset(buf, 0, ATTR_MAX); > > > > > + } > > > > > + fclose(fp); > > > > > + > > > > > +update_map_file: > > > > > + if (is_root) { > > > > > + fp = local_fopenat(dirname, VIRTFS_META_ROOT_FILE, "w"); > > > > > + } else { > > > > > + fp = local_fopenat(full_dir_name, name, "w"); > > > > > + g_free(full_dir_name); > > > > > + } > > > > > + if (!fp) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (credp->fc_uid != -1) { > > > > > + uid = credp->fc_uid; > > > > > + } > > > > > + if (credp->fc_gid != -1) { > > > > > + gid = credp->fc_gid; > > > > > + } > > > > > + if (credp->fc_mode != (mode_t)-1) { > > > > > + mode = credp->fc_mode; > > > > > + } > > > > > + if (credp->fc_rdev != -1) { > > > > > + rdev = credp->fc_rdev; > > > > > + } > > > > > + > > > > > + if (uid != -1) { > > > > > + fprintf(fp, "virtfs.uid=%d\n", uid); > > > > > + } > > > > > + if (gid != -1) { > > > > > + fprintf(fp, "virtfs.gid=%d\n", gid); > > > > > + } > > > > > + if (mode != -1) { > > > > > + fprintf(fp, "virtfs.mode=%d\n", mode); > > > > > + } > > > > > + if (rdev != -1) { > > > > > + fprintf(fp, "virtfs.rdev=%d\n", rdev); > > > > > + } > > > > > + fclose(fp); > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static int local_set_xattrat(const char *dirname, const char *path, > > > > > + FsCred *credp) > > > > > +{ > > > > > + int err; > > > > > + > > > > > + if (credp->fc_uid != -1) { > > > > > + uint32_t tmp_uid = cpu_to_le32(credp->fc_uid); > > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.uid", > > > > > + &tmp_uid, sizeof(uid_t), 0); > > > > > + if (err) { > > > > > + return err; > > > > > + } > > > > > + } > > > > > + if (credp->fc_gid != -1) { > > > > > + uint32_t tmp_gid = cpu_to_le32(credp->fc_gid); > > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.gid", > > > > > + &tmp_gid, sizeof(gid_t), 0); > > > > > + if (err) { > > > > > + return err; > > > > > + } > > > > > + } > > > > > + if (credp->fc_mode != (mode_t)-1) { > > > > > + uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); > > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.mode", > > > > > + &tmp_mode, sizeof(mode_t), 0); > > > > > + if (err) { > > > > > + return err; > > > > > + } > > > > > + } > > > > > + if (credp->fc_rdev != -1) { > > > > > + uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev); > > > > > + err = fsetxattrat_nofollow(dirname, path, "user.virtfs.rdev", > > > > > + &tmp_rdev, sizeof(dev_t), 0); > > > > > + if (err) { > > > > > + return err; > > > > > + } > > > > > + } > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, > > > > > + char *buf, size_t bufsz) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static int local_close(FsContext *ctx, V9fsFidOpenState *fs) > > > > > +{ > > > > > + return close(fs->fd); > > > > > +} > > > > > + > > > > > +static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) > > > > > +{ > > > > > + return closedir(fs->dir.stream); > > > > > +} > > > > > + > > > > > +static int local_open(FsContext *ctx, V9fsPath *fs_path, > > > > > + int flags, V9fsFidOpenState *fs) > > > > > +{ > > > > > + int fd; > > > > > + > > > > > + fd = local_open_nofollow(ctx, fs_path->data, flags, 0); > > > > > + if (fd == -1) { > > > > > + return -1; > > > > > + } > > > > > + fs->fd = fd; > > > > > + return fs->fd; > > > > > +} > > > > > + > > > > > +static int local_opendir(FsContext *ctx, > > > > > + V9fsPath *fs_path, V9fsFidOpenState *fs) > > > > > +{ > > > > > + DIR *stream; > > > > > + char *full_file_name; > > > > > + > > > > > + full_file_name = merge_fs_path(ctx->fs_root, fs_path->data); > > > > > + stream = opendir(full_file_name); > > > > > + g_free(full_file_name); > > > > > + > > > > > + if (!stream) { > > > > > + return -1;local_opendir_nofollowlocal_opendir_nofollow > > > > > + } > > > > > + > > > > > + fs->dir.stream = stream; > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) > > > > > +{ > > > > > + rewinddir(fs->dir.stream); > > > > > +} > > > > > + > > > > > +static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) > > > > > +{ > > > > > + return telldir(fs->dir.stream); > > > > > +} > > > > > + > > > > > +static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs) > > > > > +{local_opendir_nofollow > > > > > + struct dirent *entry; > > > > > + > > > > > +again: > > > > > + entry = readdir(fs->dir.stream); > > > > > + if (!entry) { > > > > > + return NULL; > > > > > + } > > > > > + > > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + if (local_is_mapped_file_metadata(ctx, entry->d_name)) { > > > > > + /* skip the meta data */ > > > > > + goto again; > > > > > + } > > > > > + } > > > > > + > > > > > + return entry; > > > > > +} > > > > > + > > > > > +static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t > off) > > > > > +{ > > > > > + off_t count; > > > > > + struct dirent *findentry; > > > > > + struct dirent *entry; > > > > > + size_t namelen[3] = {0}; > > > > > + off_t direntoff[3] = {-1, -1, -1}; > > > > > + char *d_name[3]; > > > > > + int i; > > > > > + > > > > > + /* > > > > > + * MinGW's seekdir() requires directory is not modified. If guest OS > > is > > > > > + * modifying the directory when calling seekdir(), e.g.: "rm -rf *", > > > > > + * then MinGW's seekdir() will seek to a wrong offset. > > > > > + * > > > > > + * This function saves some old offset directory entry name, > > > > > + * and lookup current entry again, and compare the offset. > > > > > + * > > > > > + * If new offset is less than old offset, that means someone is deleting > > > > > + * files in the directory, thus we need to seek offset backward. > > > > > + * > > > > > + * If new offset is larger than old offset, that means someone is creating > > > > > + * files in the directory, thus we need to seek offset forward. > > > > > + */ > > > > > + > > > > > + direntoff[0] = telldir(fs->dir.stream); > > > > > + > > > > > + /* do nothing if current offset is 0 or EOF */ > > > > > + if (direntoff[0] == 0 || direntoff[0] < 0) { > > > > > + seekdir(fs->dir.stream, off); > > > > > + return ; > > > > > + } > > > > > + > > > > > + d_name[0] = g_malloc0(sizeof(entry->d_name) * 3); > > > > > + d_name[1] = d_name[0] + sizeof(entry->d_name); > > > > > + d_name[2] = d_name[1] + sizeof(entry->d_name); > > > > > + > > > > > + /* save 3 nearest entries and offsets */ > > > > > + for (i = 0; i < 3; i++) { > > > > > + entry = &fs->dir.stream->dd_dir; > > > > > + > > > > > + memcpy(d_name[i], entry->d_name, entry->d_namlen); > > > > > + namelen[i] = strlen(d_name[i]) + 1; > > > > > + > > > > > + direntoff[i] = telldir(fs->dir.stream); > > > > > + > > > > > + entry = readdir(fs->dir.stream); > > > > > + if (entry == NULL) { > > > > > + break; > > > > > + } > > > > > + } > > > > > + > > > > > + /* lookup saved entries again */ > > > > > + for (i = 0; i < 3 && direntoff[i] != -1; i++) { > > > > > + rewinddir(fs->dir.stream); > > > > > + count = 0; > > > > > + while ((findentry = readdir(fs->dir.stream)) != NULL) { > > > > > + count++; > > > > > + > > > > > + if (memcmp(findentry->d_name, d_name[i], namelen[i]) == 0) { > > > > > + if (count + i == direntoff[i]) { > > > > > + seekdir(fs->dir.stream, off); > > > > > + goto out; > > > > > + } else if (count + i < direntoff[i]) { > > > > > + off = off - (direntoff[i] - count) - i; > > > > > + if (off <= 0) { > > > > > + off = 0; > > > > > + } > > > > > + seekdir(fs->dir.stream, off); > > > > > + goto out; > > > > > + } else { > > > > > + off = off + (count - direntoff[i]) - i; > > > > > + seekdir(fs->dir.stream, off); > > > > > + goto out; > > > > > + } > > > > > + }local_opendir_nofollow > > > > > + } > > > > > + } > > > > > + /* can not get anything, seek backward */ > > > > > + off = off - 1; > > > > > + > > > > > + seekdir(fs->dir.stream, off); > > > > > +out: > > > > > + g_free(d_name[0]); > > > > > + return ; > > > > > +} > > > > > + > > > > > +static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, > > > > > + const struct iovec *iov, > > > > > + int iovcnt, off_t offset) > > > > > +{ > > > > > +#ifdef CONFIG_PREADV > > > > > + return preadv(fs->fd, iov, iovcnt, offset); > > > > > +#else > > > > > + int err = lseek(fs->fd, offset, SEEK_SET); > > > > > + if (err == -1) { > > > > > + return err; > > > > > + } else { > > > > > + return readv(fs->fd, iov, iovcnt); > > > > > + } > > > > > +#endif > > > > > +} > > > > > + > > > > > +static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, > > > > > + const struct iovec *iov, > > > > > + int iovcnt, off_t offset) > > > > > +{ > > > > > + ssize_t ret; > > > > > +#ifdef CONFIG_PREADV > > > > > + ret = pwritev(fs->fd, iov, iovcnt, offset); > > > > > +#else > > > > > + int err = lseek(fs->fd, offset, SEEK_SET); > > > > > + if (err == -1) { > > > > > + return err; > > > > > + } else { > > > > > + ret = writev(fs->fd, iov, iovcnt); > > > > > + } > > > > > +#endif > > > > > +#ifdef CONFIG_SYNC_FILE_RANGE > > > > > + if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { > > > > > + /* > > > > > + * Initiate a writeback. This is not a data integrity sync. > > > > > + * We want to ensure that we don't leave dirty pages in the cache > > > > > + * after write when writeout=immediate is sepcified. > > > > > + */ > > > > > + sync_file_range(fs->fd, offset, ret, > > > > > + SYNC_FILE_RANGE_WAIT_BEFORE | > > SYNC_FILE_RANGE_WRITE); > > > > > + } > > > > > +#endif > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred > *credp) > > > > > +{ > > > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > > > + char *name = g_path_get_basename(fs_path->data); > > > > > + int ret = -1; > > > > > + char *full_file_name = NULL; > > > > > + DIR *dir; > > > > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > > > > + if (dir == NULL) { > > > > > + goto out; > > > > > + } > > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > > + ret = local_set_xattrat(full_file_name, name, credp); > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > > > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > > > > + ret = -1; > > > > > + errno = ENOTSUP; > > > > > + } > > > > > + closedir(dir); > > > > > + > > > > > +out: > > > > > + if (full_file_name != NULL) { > > > > > + g_free(full_file_name); > > > > > + } > > > > > + > > > > > + g_free(dirpath); > > > > > + g_free(name); > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, > > > > > + const char *name, FsCred *credp) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, > > > > > + const char *name, FsCred *credp) > > > > > +{ > > > > > + int err = -1; > > > > > + char *full_file_name; > > > > > + char *full_dir_name; > > > > > + DIR *dir; > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > > > > + errno = EINVAL; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + dir = local_opendir_nofollow(fs_ctx, dir_path->data); > > > > > + if (dir == NULL) { > > > > > + return -1; > > > > > + } > > > > > + closedir(dir); > > > > > + > > > > > + full_dir_name = merge_fs_path(fs_ctx->fs_root, dir_path->data); > > > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED || > > > > > + fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + err = mkdir(full_file_name); > > > > > + if (err == -1) { > > > > > + goto out; > > > > > + } > > > > > + credp->fc_mode = credp->fc_mode; > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > > + err = local_set_xattrat(full_dir_name, name, credp); > > > > > + } else { > > > > > + err = local_set_mapped_file_attrat(full_dir_name, name, > credp); > > > > > + } > > > > > + if (err == -1) { > > > > > + rmdir(full_file_name); > > > > > + } > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH || > > > > > + fs_ctx->export_flags & V9FS_SM_NONE) { > > > > > + err = mkdir(full_file_name); > > > > > + if (err == -1) { > > > > > + goto out; > > > > > + } > > > > > + /* Windows does not support chmod, do nothing here */ > > > > > + } > > > > > + > > > > > + goto out; > > > > > + > > > > > +out: > > > > > + g_free(full_dir_name); > > > > > + g_free(full_file_name); > > > > > + return err; > > > > > +} > > > > > + > > > > > +static int local_fstat(FsContext *fs_ctx, int fid_type, > > > > > + V9fsFidOpenState *fs, struct stat *stbuf) > > > > > +{ > > > > > + > > > > > + int err, fd; > > > > > + char filename[NAME_MAX]; > > > > > + char *dirpath; > > > > > + char *name; > > > > > + HANDLE hFile; > > > > > + DWORD dwRet; > > > > > + > > > > > + if (fid_type == P9_FID_DIR) { > > > > > + /* Windows does not support open directory */ > > > > > + return -1; > > > > > + } else { > > > > > + fd = fs->fd; > > > > > + } > > > > > + > > > > > + err = fstat(fd, stbuf); > > > > > + if (err) { > > > > > + return err; > > > > > + } > > > > > + > > > > > + /* get real file name by fd */ > > > > > + hFile = (HANDLE)_get_osfhandle(fd); > > > > > + dwRet = GetFinalPathNameByHandle(hFile, filename, sizeof(filename), > 0); > > > > > + > > > > > + if (dwRet >= NAME_MAX) { > > > > > + return -1; > > > > > + } > > > > > + /* skip string "\\\\?\\" return from GetFinalPathNameByHandle() */ > > > > > + memmove(filename, filename + 4, NAME_MAX - 4); > > > > > + > > > > > + dirpath = g_path_get_dirname(filename); > > > > > + name = g_path_get_basename(filename); > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > > + /* Actual credentials are part of extended attrs */ > > > > > + uid_t tmp_uid; > > > > > + gid_t tmp_gid; > > > > > + mode_t tmp_mode; > > > > > + dev_t tmp_dev; > > > > > + > > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.uid", > > > > > + &tmp_uid, sizeof(uid_t)) > 0) { > > > > > + stbuf->st_uid = le32_to_cpu(tmp_uid); > > > > > + } > > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.gid", > > > > > + &tmp_gid, sizeof(gid_t)) > 0) { > > > > > + stbuf->st_gid = le32_to_cpu(tmp_gid); > > > > > + } > > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.mode", > > > > > + &tmp_mode, sizeof(mode_t)) > 0) { > > > > > + stbuf->st_mode = (stbuf->st_mode & ~0777); > > > > > + stbuf->st_mode |= le32_to_cpu(tmp_mode); > > > > > + } > > > > > + if (fgetxattrat_nofollow(dirpath, name, "user.virtfs.rdev", > > > > > + &tmp_dev, sizeof(dev_t)) > 0) { > > > > > + stbuf->st_rdev = le64_to_cpu(tmp_dev); > > > > > + } > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + errno = EOPNOTSUPP; > > > > > + g_free(dirpath); > > > > > + g_free(name); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + g_free(dirpath); > > > > > + g_free(name); > > > > > + > > > > > + return err; > > > > > +} > > > > > + > > > > > +static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char > > *name, > > > > > + int flags, FsCred *credp, V9fsFidOpenState *fs) > > > > > +{ > > > > > + int fd = -1; > > > > > + int err = -1; > > > > > + char *full_file_name; > > > > > + > > > > > + if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > > + local_is_mapped_file_metadata(fs_ctx, name)) { > > > > > + errno = EINVAL; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + full_file_name = merge_fs_path(dir_path->data, name); > > > > > + fd = openfile_with_ctx(fs_ctx, full_file_name, flags, credp->fc_mode); > > > > > + g_free(full_file_name); > > > > > + > > > > > + err = fd; > > > > > + fs->fd = fd; > > > > > + goto out; > > > > > + > > > > > + close_preserve_errno(fd); > > > > > +out: > > > > > + return err; > > > > > +} > > > > > + > > > > > + > > > > > +static int local_symlink(FsContext *fs_ctx, const char *oldpath, > > > > > + V9fsPath *dir_path, const char *name, FsCred *credp) > > > > > +{ > > > > > + return -1; > > > > > > > > Why ? This would require a detailed comment and also to set errno. > > > > > > An error number set is missing here, should be ENOTSUP. > > > > > > > > > > > > +} > > > > > + > > > > > +static int local_link(FsContext *ctx, V9fsPath *oldpath, > > > > > + V9fsPath *dirpath, const char *name) > > > > > +{ > > > > > + return -1; > > > > > > > > Same remark. > > > > > > > > Before re-posting, you really need to come up with a detailed plan > > > > around symlinks. > > > > > > > > Cheers, > > > > > > > > -- > > > > Greg > > > > > > An error number set is missing here, should be ENOTSUP. > > > Windows does not support symbolic link in its POSIX API library. > > > It also require Administrator privileges and NTFS (need to turn on symbolic > link > > support). > > > > > > Currently, I think it is very hard to support symbolic link on Windows host. > > > > > > > > > > > > +} > > > > > + > > > > > +static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) > > > > > +{ > > > > > + int fd, ret; > > > > > + > > > > > + fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0); > > > > > + if (fd == -1) { > > > > > + return -1; > > > > > + } > > > > > + ret = ftruncate(fd, size); > > > > > + close_preserve_errno(fd); > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred > *credp) > > > > > +{ > > > > > + char *full_file_name; > > > > > + char *dirpath = g_path_get_dirname(fs_path->data); > > > > > + char *name = g_path_get_basename(fs_path->data); > > > > > + int ret = -1; > > > > > + DIR *dir; > > > > > + > > > > > + dir = local_opendir_nofollow(fs_ctx, dirpath); > > > > > + if (dir == NULL) { > > > > > + goto out; > > > > > + } > > > > > + full_file_name = merge_fs_path(fs_ctx->fs_root, dirpath); > > > > > + > > > > > + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || > > > > > + (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || > > > > > + (fs_ctx->export_flags & V9FS_SM_NONE)) { > > > > > + /* Windows does not support chown() */ > > > > > + ret = -1; > > > > > + errno = ENOTSUP; > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { > > > > > + ret = local_set_xattrat(full_file_name, name, credp); > > > > > + } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + ret = local_set_mapped_file_attrat(full_file_name, name, credp); > > > > > + } > > > > > + g_free(full_file_name); > > > > > + closedir(dir); > > > > > +out: > > > > > + g_free(name); > > > > > + g_free(dirpath); > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static int local_utimensat(FsContext *s, V9fsPath *fs_path, > > > > > + const struct timespec *buf) > > > > > +{ > > > > > + struct utimbuf tm; > > > > > + char *full_file_name; > > > > > + int err; > > > > > + > > > > > + tm.actime = buf[0].tv_sec; > > > > > + tm.modtime = buf[1].tv_sec; > > > > > + > > > > > + full_file_name = merge_fs_path(s->fs_root, fs_path->data); > > > > > + err = utime(full_file_name, &tm); > > > > > + g_free(full_file_name); > > > > > + > > > > > + return err; > > > > > +} > > > > > + > > > > > +static int local_unlinkat_common(FsContext *ctx, const char *dirname, > > > > > + const char *name, int flags) > > > > > +{ > > > > > + int ret; > > > > > + char *full_file_name; > > > > > + char *full_dir_name; > > > > > + > > > > > + full_dir_name = merge_fs_path(ctx->fs_root, dirname); > > > > > + full_file_name = merge_fs_path(full_dir_name, name); > > > > > + > > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + char *full_meta_dir_name; > > > > > + char *full_meta_file_name; > > > > > + > > > > > + /* > > > > > + * We need to remove the metadata as well: > > > > > + * - the metadata directory if we're removing a directory > > > > > + * - the metadata file in the parent's metadata directory > > > > > + * > > > > > + * If any of these are missing (ie, ENOENT) then we're probably > > > > > + * trying to remove something that wasn't created in mapped-file > > > > > + * mode. We just ignore the error. > > > > > + */ > > > > > + > > > > > + if ((flags & AT_REMOVEDIR) != 0) { > > > > > + full_meta_dir_name = merge_fs_path(full_file_name, > > > > VIRTFS_META_DIR); > > > > > + ret = rmdir(full_meta_dir_name); > > > > > + g_free(full_meta_dir_name); > > > > > + > > > > > + if (ret < 0 && errno != ENOENT) { > > > > > + g_free(full_file_name); > > > > > + g_free(full_dir_name); > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + > > > > > + full_meta_dir_name = merge_fs_path(full_dir_name, > VIRTFS_META_DIR); > > > > > + full_meta_file_name = merge_fs_path(full_meta_dir_name, name); > > > > > + ret = remove(full_meta_file_name); > > > > > + g_free(full_meta_dir_name); > > > > > + g_free(full_meta_file_name); > > > > > + > > > > > + if (ret < 0 && errno != ENOENT) { > > > > > + g_free(full_dir_name); > > > > > + g_free(full_file_name); > > > > > + > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + > > > > > + if ((flags & AT_REMOVEDIR) != 0) { > > > > > + ret = rmdir(full_file_name); > > > > > + } else { > > > > > + ret = remove(full_file_name); > > > > > + } > > > > > + > > > > > + g_free(full_dir_name); > > > > > + g_free(full_file_name); > > > > > + > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static int local_remove(FsContext *ctx, const char *path) > > > > > +{ > > > > > + int err; > > > > > + DIR *stream; > > > > > + char *full_file_name; > > > > > + char *dirpath = g_path_get_dirname(path); > > > > > + char *name = g_path_get_basename(path); > > > > > + int flags = 0; > > > > > + > > > > > + full_file_name = merge_fs_path(ctx->fs_root, path); > > > > > + stream = opendir(full_file_name); > > > > > + if (stream != NULL) { > > > > > + closedir(stream); > > > > > + flags |= AT_REMOVEDIR; > > > > > + } > > > > > + err = local_unlinkat_common(ctx, dirpath, name, flags); > > > > > + > > > > > + g_free(name); > > > > > + g_free(dirpath); > > > > > + g_free(full_file_name); > > > > > + return err; > > > > > +} > > > > > + > > > > > +static int local_fsync(FsContext *ctx, int fid_type, > > > > > + V9fsFidOpenState *fs, int datasync) > > > > > +{ > > > > > + if (fid_type != P9_FID_DIR) { > > > > > + return _commit(fs->fd); > > > > > + } > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs > > *stbuf) > > > > > +{ > > > > > + int ret; > > > > > + ret = qemu_statfs(s->fs_root, stbuf); > > > > > + if (ret == 0) { > > > > > + /* use context address as fsid */ > > > > > + memcpy(&stbuf->f_fsid, s, sizeof(long)); > > > > > + } > > > > > + > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, > > > > > + const char *name, void *value, size_t size) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, > > > > > + void *value, size_t size) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char > > *name, > > > > > + void *value, size_t size, int flags) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, > > > > > + const char *name) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, > > > > > + const char *name, V9fsPath *target) > > > > > +{ > > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > > + local_is_mapped_file_metadata(ctx, name)) { > > > > > + errno = EINVAL; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (dir_path) { > > > > > + if (!strcmp(name, ".")) { > > > > > + /* "." relative to "foo/bar" is "foo/bar" */ > > > > > + v9fs_path_copy(target, dir_path); > > > > > + } else if (!strcmp(name, "..")) { > > > > > + if (!strcmp(dir_path->data, ".")) { > > > > > + /* ".." relative to the root is "." */ > > > > > + v9fs_path_sprintf(target, "."); > > > > > + } else { > > > > > + char *tmp = g_path_get_dirname(dir_path->data); > > > > > + /* > > > > > + * Symbolic links are resolved by the client. We can assume > > > > > + * that ".." relative to "foo/bar" is equivalent to "foo" > > > > > + */ > > > > > + v9fs_path_sprintf(target, "%s", tmp); > > > > > + g_free(tmp); > > > > > + } > > > > > + } else { > > > > > + assert(!strchr(name, '/')); > > > > > + v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); > > > > > + } > > > > > + } else if (!strcmp(name, "/") || !strcmp(name, ".") || > > > > > + !strcmp(name, "..")) { > > > > > + /* This is the root fid */ > > > > > + v9fs_path_sprintf(target, "."); > > > > > + } else { > > > > > + assert(!strchr(name, '/')); > > > > > + v9fs_path_sprintf(target, "./%s", name); > > > > > + } > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static int local_renameat(FsContext *ctx, V9fsPath *olddir, > > > > > + const char *old_name, V9fsPath *newdir, > > > > > + const char *new_name) > > > > > +{ > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static int local_rename(FsContext *ctx, const char *oldpath, > > > > > + const char *newpath) > > > > > +{ > > > > > + int err; > > > > > + > > > > > + char *full_old_name; > > > > > + char *full_new_name; > > > > > + > > > > > + full_old_name = merge_fs_path(ctx->fs_root, oldpath); > > > > > + full_new_name = merge_fs_path(ctx->fs_root, newpath); > > > > > + > > > > > + err = rename(full_old_name, full_new_name); > > > > > + > > > > > + g_free(full_old_name); > > > > > + g_free(full_new_name); > > > > > + > > > > > + return err; > > > > > +} > > > > > + > > > > > +static int local_unlinkat(FsContext *ctx, V9fsPath *dir, > > > > > + const char *name, int flags) > > > > > +{ > > > > > + int ret; > > > > > + > > > > > + if (ctx->export_flags & V9FS_SM_MAPPED_FILE && > > > > > + local_is_mapped_file_metadata(ctx, name)) { > > > > > + errno = EINVAL; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + ret = local_unlinkat_common(ctx, dir->data, name, flags); > > > > > + > > > > > + return ret; > > > > > +} > > > > > + > > > > > +static int check_filesystem_type(char *fs_root, int export_flags) > > > > > +{ > > > > > + HANDLE hFile; > > > > > + wchar_t FsName[MAX_PATH + 1] = {0}; > > > > > + wchar_t NtfsName[5] = {'N', 'T', 'F', 'S'}; > > > > > + > > > > > + if ((export_flags & V9FS_SM_MAPPED) == 0) { > > > > > + return 0; > > > > > + } > > > > > + > > > > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > > > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > > > > + if (hFile == INVALID_HANDLE_VALUE) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + /* Get file system type name */ > > > > > + if (GetVolumeInformationByHandleW(hFile, NULL, 0, NULL, NULL, NULL, > > > > > + FsName, MAX_PATH + 1) == 0) { > > > > > + CloseHandle(hFile); > > > > > + return -1; > > > > > + } > > > > > + CloseHandle(hFile); > > > > > + > > > > > + if (wcscmp(FsName, NtfsName) != 0) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static int local_init(FsContext *ctx, Error **errp) > > > > > +{ > > > > > + LocalData *data = g_malloc(sizeof(*data)); > > > > > + > > > > > + struct stat StatBuf; > > > > > + > > > > > + if (stat(ctx->fs_root, &StatBuf) != 0) { > > > > > + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); > > > > > + goto err; > > > > > + } > > > > > + > > > > > + /* > > > > > + * security_model=mapped(-xattr) requires a fileystem on Windows that > > > > > + * supports Alternate Data Stream (ADS). NTFS is one of them, and is > > > > > + * probably most popular on Windows. It is fair enough to assume > > > > > + * Windows users to use NTFS for the mapped security model. > > > > > + */ > > > > > + if (check_filesystem_type(ctx->fs_root, ctx->export_flags) != 0) { > > > > > + error_setg_errno(errp, EINVAL, "require NTFS file system when " > > > > > + "security_model is mapped or mapped-xattr"); > > > > > + goto err; > > > > > + } > > > > > + > > > > > + if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { > > > > > + ctx->xops = passthrough_xattr_ops; > > > > > + } else if (ctx->export_flags & V9FS_SM_MAPPED) { > > > > > + ctx->xops = mapped_xattr_ops; > > > > > + } else if (ctx->export_flags & V9FS_SM_NONE) { > > > > > + ctx->xops = none_xattr_ops; > > > > > + } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + /* > > > > > + * xattr operation for mapped-file and passthrough > > > > > + * remain same. > > > > > + */ > > > > > + ctx->xops = passthrough_xattr_ops; > > > > > + } > > > > > + ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; > > > > > + > > > > > + ctx->private = data; > > > > > + return 0; > > > > > + > > > > > +err: > > > > > + g_free(data); > > > > > + return -1; > > > > > +} > > > > > + > > > > > +static void local_cleanup(FsContext *ctx) > > > > > +{ > > > > > + LocalData *data = ctx->private; > > > > > + > > > > > + if (!data) { > > > > > + return; > > > > > + } > > > > > + > > > > > + close(data->mountfd); > > > > > + g_free(data); > > > > > +} > > > > > + > > > > > +static void error_append_security_model_hint(Error *const *errp) > > > > > +{ > > > > > + error_append_hint(errp, "Valid options are: security_model=" > > > > > + "[passthrough|mapped-xattr|mapped-file|none]\n"); > > > > > +} > > > > > + > > > > > +static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error > > **errp) > > > > > +{ > > > > > + ERRP_GUARD(); > > > > > + const char *sec_model = qemu_opt_get(opts, "security_model"); > > > > > + const char *path = qemu_opt_get(opts, "path"); > > > > > + const char *multidevs = qemu_opt_get(opts, "multidevs"); > > > > > + > > > > > + if (!sec_model) { > > > > > + error_setg(errp, "security_model property not set"); > > > > > + error_append_security_model_hint(errp); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (!strcmp(sec_model, "passthrough")) { > > > > > + fse->export_flags |= V9FS_SM_PASSTHROUGH; > > > > > + } else if (!strcmp(sec_model, "mapped") || > > > > > + !strcmp(sec_model, "mapped-xattr")) { > > > > > + fse->export_flags |= V9FS_SM_MAPPED; > > > > > + } else if (!strcmp(sec_model, "none")) { > > > > > + fse->export_flags |= V9FS_SM_NONE; > > > > > + } else if (!strcmp(sec_model, "mapped-file")) { > > > > > + fse->export_flags |= V9FS_SM_MAPPED_FILE; > > > > > + } else { > > > > > + error_setg(errp, "invalid security_model property '%s'", > sec_model); > > > > > + error_append_security_model_hint(errp); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (multidevs) { > > > > > + if (!strcmp(multidevs, "remap")) { > > > > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > > > > + fse->export_flags |= V9FS_REMAP_INODES; > > > > > + } else if (!strcmp(multidevs, "forbid")) { > > > > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > > > > + fse->export_flags |= V9FS_FORBID_MULTIDEVS; > > > > > + } else if (!strcmp(multidevs, "warn")) { > > > > > + fse->export_flags &= ~V9FS_FORBID_MULTIDEVS; > > > > > + fse->export_flags &= ~V9FS_REMAP_INODES; > > > > > + } else { > > > > > + error_setg(errp, "invalid multidevs property '%s'", > > > > > + multidevs); > > > > > + error_append_hint(errp, "Valid options are: multidevs=" > > > > > + "[remap|forbid|warn]\n"); > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + > > > > > + if (!path) { > > > > > + error_setg(errp, "path property not set"); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) { > > > > > + error_prepend(errp, "invalid throttle configuration: "); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (fse->export_flags & V9FS_SM_MAPPED || > > > > > + fse->export_flags & V9FS_SM_MAPPED_FILE) { > > > > > + fse->fmode = > > > > > + qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777; > > > > > + fse->dmode = > > > > > + qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & > > 0777; > > > > > + } else { > > > > > + if (qemu_opt_find(opts, "fmode")) { > > > > > + error_setg(errp, "fmode is only valid for mapped security > modes"); > > > > > + return -1; > > > > > + } > > > > > + if (qemu_opt_find(opts, "dmode")) { > > > > > + error_setg(errp, "dmode is only valid for mapped security > modes"); > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + > > > > > + fse->path = g_strdup(path); > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +FileOperations local_ops = { > > > > > + .parse_opts = local_parse_opts, > > > > > + .init = local_init, > > > > > + .cleanup = local_cleanup, > > > > > + .lstat = local_lstat, > > > > > + .readlink = local_readlink, > > > > > + .close = local_close, > > > > > + .closedir = local_closedir, > > > > > + .open = local_open, > > > > > + .opendir = local_opendir, > > > > > + .rewinddir = local_rewinddir, > > > > > + .telldir = local_telldir, > > > > > + .readdir = local_readdir, > > > > > + .seekdir = local_seekdir, > > > > > + .preadv = local_preadv, > > > > > + .pwritev = local_pwritev, > > > > > + .chmod = local_chmod, > > > > > + .mknod = local_mknod, > > > > > + .mkdir = local_mkdir, > > > > > + .fstat = local_fstat, > > > > > + .open2 = local_open2, > > > > > + .symlink = local_symlink, > > > > > + .link = local_link, > > > > > + .truncate = local_truncate, > > > > > + .rename = local_rename, > > > > > + .chown = local_chown, > > > > > + .utimensat = local_utimensat, > > > > > + .remove = local_remove, > > > > > + .fsync = local_fsync, > > > > > + .statfs = local_statfs, > > > > > + .lgetxattr = local_lgetxattr, > > > > > + .llistxattr = local_llistxattr, > > > > > + .lsetxattr = local_lsetxattr, > > > > > + .lremovexattr = local_lremovexattr, > > > > > + .name_to_path = local_name_to_path, > > > > > + .renameat = local_renameat, > > > > > + .unlinkat = local_unlinkat, > > > > > +}; > > > > > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > > > > > new file mode 100644 > > > > > index 0000000000..d9b35e7425 > > > > > --- /dev/null > > > > > +++ b/hw/9pfs/9p-util-win32.c > > > > > @@ -0,0 +1,303 @@ > > > > > +/* > > > > > + * 9p utilities (Windows Implementation) > > > > > + * > > > > > + * Copyright (c) 2022 Wind River Systems, Inc. > > > > > + * > > > > > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. > > > > > + * See the COPYING file in the top-level directory. > > > > > + */ > > > > > + > > > > > +#include "qemu/osdep.h" > > > > > +#include "qapi/error.h" > > > > > +#include "qemu/error-report.h" > > > > > +#include "9p.h" > > > > > +#include "9p-util.h" > > > > > +#include "9p-linux-errno.h" > > > > > +#include <windows.h> > > > > > +#include <dirent.h> > > > > > + > > > > > +#ifndef V9FS_MAGIC > > > > > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > > > > > +#endif > > > > > + > > > > > +static int build_ads_name(char *namebuf, size_t namebuflen, > > > > > + const char *dirname, const char *filename, > > > > > + const char *ads_name) > > > > > +{ > > > > > + size_t totalsize; > > > > > + > > > > > + totalsize = strlen(dirname) + strlen(filename) + strlen(ads_name) + > 3; > > > > > + if (totalsize > namebuflen) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + /* > > > > > + * NTFS ADS (Alternate Data Streams) name format: > > > > > + * filename:ads_name > > > > > + * e.g. > > > > > + * d:\1.txt:my_ads_name > > > > > + */ > > > > > + strcpy(namebuf, dirname); > > > > > + strcat(namebuf, "\\"); > > > > > + strcat(namebuf, filename); > > > > > + strcat(namebuf, ":"); > > > > > + strcat(namebuf, ads_name); > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +static ssize_t copy_ads_name(char *namebuf, size_t namebuflen, > > > > > + char *fulladsname) > > > > > +{ > > > > > + char *p1, *p2; > > > > > + > > > > > + /* > > > > > + * NTFS ADS (Alternate Data Streams) name from emurate data format: > > > > > + * :ads_name:$DATA > > > > > + * e.g. > > > > > + * :my_ads_name:$DATA > > > > > + * > > > > > + * ADS name from FindNextStreamW() always have ":$DATA" string at the > > end > > > > > + * > > > > > + * This function copy ADS name to namebuf. > > > > > + */ > > > > > + > > > > > + p1 = strchr(fulladsname, ':'); > > > > > + if (p1 == NULL) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + p2 = strchr(p1 + 1, ':'); > > > > > + if (p2 == NULL) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + /* skip empty ads name */ > > > > > + if (p2 - p1 == 1) { > > > > > + return 0; > > > > > + } > > > > > + > > > > > + if (p2 - p1 + 1 > namebuflen) { > > > > > + return -1; > > > > > + } > > > > > + > > > > > + memcpy(namebuf, p1 + 1, p2 - p1 - 1); > > > > > + namebuf[p2 - p1 - 1] = '\0'; > > > > > + > > > > > + return p2 - p1; > > > > > +} > > > > > + > > > > > +ssize_t fgetxattrat_nofollow(const char *dirname, const char *filename, > > > > > + const char *name, void *value, size_t size) > > > > > +{ > > > > > + HANDLE hStream; > > > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > > > + DWORD dwBytesRead; > > > > > + > > > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) > < > > 0) { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + hStream = CreateFile(ADSFileName, GENERIC_READ, FILE_SHARE_READ, NULL, > > > > > + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); > > > > > + if (hStream == INVALID_HANDLE_VALUE && > > > > > + GetLastError() == ERROR_FILE_NOT_FOUND) { > > > > > + errno = ENODATA; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (ReadFile(hStream, value, size, &dwBytesRead, NULL) == FALSE) { > > > > > + errno = EIO; > > > > > + CloseHandle(hStream); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + CloseHandle(hStream); > > > > > + > > > > > + return dwBytesRead; > > > > > +} > > > > > + > > > > > +ssize_t flistxattrat_nofollow(const char *dirname, const char *filename, > > > > > + char *list, size_t size) > > > > > +{ > > > > > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 }; > > > > > + char fulladsname[NAME_MAX + 1]; > > > > > + char *full_fs_name = merge_fs_path(dirname, filename); > > > > > + int ret; > > > > > + HANDLE hFind; > > > > > + WIN32_FIND_STREAM_DATA fsd; > > > > > + BOOL bFindNext; > > > > > + char *listptr = list; > > > > > + size_t listleftsize = size; > > > > > + > > > > > + /* > > > > > + * ADS emurate function only have WCHAR version, need to covert filename > > > > > + * to WCHAR string. > > > > > + */ > > > > > + > > > > > + ret = MultiByteToWideChar(CP_UTF8, 0, full_fs_name, > > > > > + strlen(full_fs_name), WideCharStr, NAME_MAX); > > > > > + g_free(full_fs_name); > > > > > + if (ret == 0) { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, > 0); > > > > > + if (hFind == INVALID_HANDLE_VALUE) { > > > > > + errno = ENODATA; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + do { > > > > > + memset(fulladsname, 0, sizeof(fulladsname)); > > > > > + > > > > > + /* > > > > > + * ADS emurate function only have WCHAR version, need to covert > > > > > + * cStreamName to utf-8 string. > > > > > + */ > > > > > + > > > > > + ret = WideCharToMultiByte(CP_UTF8, 0, > > > > > + fsd.cStreamName, wcslen(fsd.cStreamName) > + > > 1, > > > > > + fulladsname, sizeof(fulladsname) - 1, > > > > > + NULL, NULL); > > > > > + > > > > > + if (ret == 0) { > > > > > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { > > > > > + errno = ERANGE; > > > > > + } > > > > > + CloseHandle(hFind); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + ret = copy_ads_name(listptr, listleftsize, fulladsname); > > > > > + if (ret < 0) { > > > > > + errno = ERANGE; > > > > > + CloseHandle(hFind); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + listptr = listptr + ret; > > > > > + listleftsize = listleftsize - ret; > > > > > + > > > > > + bFindNext = FindNextStreamW(hFind, &fsd); > > > > > + } while (bFindNext); > > > > > + > > > > > + CloseHandle(hFind); > > > > > + > > > > > + return size - listleftsize; > > > > > +} > > > > > + > > > > > +ssize_t fremovexattrat_nofollow(const char *dirname, const char *filename, > > > > > + const char *name) > > > > > +{ > > > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > > > + > > > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) > < > > 0) { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (DeleteFile(ADSFileName) != 0) { > > > > > + if (GetLastError() == ERROR_FILE_NOT_FOUND) { > > > > > + errno = ENODATA; > > > > > + return -1; > > > > > + } > > > > > + } > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +int fsetxattrat_nofollow(const char *dirname, const char *filename, > > > > > + const char *name, void *value, size_t size, int flags) > > > > > +{ > > > > > + HANDLE hStream; > > > > > + char ADSFileName[NAME_MAX + 1] = {0}; > > > > > + DWORD dwBytesWrite; > > > > > + > > > > > + if (build_ads_name(ADSFileName, NAME_MAX, dirname, filename, name) > < > > 0) { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + hStream = CreateFile(ADSFileName, GENERIC_WRITE, FILE_SHARE_READ, > NULL, > > > > > + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); > > > > > + if (hStream == INVALID_HANDLE_VALUE) { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + if (WriteFile(hStream, value, size, &dwBytesWrite, NULL) == FALSE) > { > > > > > + errno = EIO; > > > > > + CloseHandle(hStream); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + CloseHandle(hStream); > > > > > + > > > > > + return 0; > > > > > +} > > > > > + > > > > > +int qemu_mknodat(const char *dirname, const char *filename, > > > > > + mode_t mode, dev_t dev) > > > > > +{ > > > > > + errno = ENOTSUP; > > > > > + return -1; > > > > > +} > > > > > + > > > > > +int qemu_statfs(const char *fs_root, struct statfs *stbuf) > > > > > +{ > > > > > + HANDLE hFile; > > > > > + char RealPath[NAME_MAX + 1]; > > > > > + unsigned long SectorsPerCluster; > > > > > + unsigned long BytesPerSector; > > > > > + unsigned long NumberOfFreeClusters; > > > > > + unsigned long TotalNumberOfClusters; > > > > > + > > > > > + hFile = CreateFile(fs_root, GENERIC_READ, FILE_SHARE_READ, NULL, > > > > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); > > > > > + if (hFile == INVALID_HANDLE_VALUE) { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + /* get real path of root */ > > > > > + if (GetFinalPathNameByHandle(hFile, RealPath, sizeof(RealPath), > > > > > + FILE_NAME_NORMALIZED) == 0) { > > > > > + errno = EIO; > > > > > + CloseHandle(hFile); > > > > > + return -1; > > > > > + } > > > > > + > > > > > + CloseHandle(hFile); > > > > > + > > > > > + /* > > > > > + * GetFinalPathNameByHandle will return real path start with "\\\\?\\". > > > > > + * "C:\\123" will be "\\\\?\\C:\\123" > > > > > + * Skip first 4 bytes and truncate the string at offset 7, it will > get > > > > > + * the real root directory like "C:\\", this is parameter > GetDiskFreeSpace > > > > > + * needed. > > > > > + */ > > > > > + > > > > > + RealPath[7] = '\0'; > > > > > + > > > > > + if (GetDiskFreeSpace(RealPath + 4, &SectorsPerCluster, > &BytesPerSector, > > > > > + &NumberOfFreeClusters, &TotalNumberOfClusters) == > 0) > > { > > > > > + errno = EIO; > > > > > + return -1; > > > > > + } > > > > > + > > > > > + stbuf->f_type = V9FS_MAGIC; > > > > > + stbuf->f_bsize = (__fsword_t)(SectorsPerCluster * BytesPerSector); > > > > > + stbuf->f_blocks = (fsblkcnt_t)TotalNumberOfClusters; > > > > > + stbuf->f_bfree = (fsblkcnt_t)NumberOfFreeClusters; > > > > > + stbuf->f_bavail = (fsblkcnt_t)NumberOfFreeClusters; > > > > > + stbuf->f_files = -1; > > > > > + stbuf->f_ffree = -1; > > > > > + stbuf->f_namelen = NAME_MAX; > > > > > + stbuf->f_frsize = 0; > > > > > + stbuf->f_flags = 0; > > > > > + > > > > > + return 0; > > > > > +} > > > > > diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c > > > > > index 9ae69dd8db..5623f0e2ef 100644 > > > > > --- a/hw/9pfs/9p-xattr.c > > > > > +++ b/hw/9pfs/9p-xattr.c > > > > > @@ -78,10 +78,45 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char > *path, > > > > > char *orig_value, *orig_value_start; > > > > > ssize_t xattr_len, parsed_len = 0, attr_len; > > > > > char *dirpath, *name; > > > > > +#ifdef CONFIG_WIN32 > > > > > + char *full_dir_path; > > > > > + DIR *dir; > > > > > +#else > > > > > int dirfd; > > > > > +#endif > > > > > > > > > > /* Get the actual len */ > > > > > dirpath = g_path_get_dirname(path); > > > > > + > > > > > +#ifdef CONFIG_WIN32 > > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > > + if (dir == NULL) { > > > > > + return -1; > > > > > + } > > > > > + closedir(dir); > > > > > + > > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > > + g_free(dirpath); > > > > > + > > > > > + name = g_path_get_basename(path); > > > > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, value, 0); > > > > > + if (xattr_len <= 0) { > > > > > + g_free(name); > > > > > + g_free(full_dir_path); > > > > > + return xattr_len; > > > > > + } > > > > > + > > > > > + /* Now fetch the xattr and find the actual size */ > > > > > + orig_value = g_malloc(xattr_len); > > > > > + xattr_len = flistxattrat_nofollow(full_dir_path, name, orig_value, > > > > > + xattr_len); > > > > > + g_free(name); > > > > > + g_free(full_dir_path); > > > > > + if (xattr_len < 0) { > > > > > + g_free(orig_value); > > > > > + return -1; > > > > > + } > > > > > +#else > > > > > dirfd = local_opendir_nofollow(ctx, dirpath); > > > > > g_free(dirpath); > > > > > if (dirfd == -1) { > > > > > @@ -105,6 +140,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char > *path, > > > > > g_free(orig_value); > > > > > return -1; > > > > > } > > > > > +#endif > > > > > > > > > > /* store the orig pointer */ > > > > > orig_value_start = orig_value; > > > > > @@ -166,6 +202,31 @@ int v9fs_remove_xattr(FsContext *ctx, > > > > > ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, > > > > > const char *name, void *value, size_t size) > > > > > { > > > > > +#ifdef CONFIG_WIN32 > > > > > + char *dirpath = g_path_get_dirname(path); > > > > > + char *filename = g_path_get_basename(path); > > > > > + char *full_dir_path; > > > > > + DIR *dir; > > > > > + ssize_t ret = -1; > > > > > + > > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > > + > > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > > + if (dir == NULL) { > > > > > + goto out; > > > > > + } > > > > > + closedir(dir); > > > > > + > > > > > + ret = fgetxattrat_nofollow(full_dir_path, filename, > > > > > + name, value, size); > > > > > + > > > > > +out: > > > > > + g_free(full_dir_path); > > > > > + g_free(dirpath); > > > > > + g_free(filename); > > > > > + > > > > > + return ret; > > > > > +#else > > > > > char *dirpath = g_path_get_dirname(path); > > > > > char *filename = g_path_get_basename(path); > > > > > int dirfd; > > > > > @@ -177,11 +238,13 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const > > char > > > > *path, > > > > > } > > > > > > > > > > ret = fgetxattrat_nofollow(dirfd, filename, name, value, size); > > > > > + > > > > > close_preserve_errno(dirfd); > > > > > out: > > > > > g_free(dirpath); > > > > > g_free(filename); > > > > > return ret; > > > > > +#endif > > > > > } > > > > > > > > > > ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, > > > > > @@ -194,6 +257,30 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const > > char > > > > *path, > > > > > const char *name, void *value, size_t size, > > > > > int flags) > > > > > { > > > > > +#ifdef CONFIG_WIN32 > > > > > + char *dirpath = g_path_get_dirname(path); > > > > > + char *filename = g_path_get_basename(path); > > > > > + char *full_dir_path; > > > > > + DIR *dir; > > > > > + ssize_t ret = -1; > > > > > + > > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > > + > > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > > + if (dir == NULL) { > > > > > + goto out; > > > > > + } > > > > > + closedir(dir); > > > > > + > > > > > + ret = fsetxattrat_nofollow(full_dir_path, filename, name, > > > > > + value, size, flags); > > > > > +out: > > > > > + g_free(full_dir_path); > > > > > + g_free(dirpath); > > > > > + g_free(filename); > > > > > + return ret; > > > > > + > > > > > +#else > > > > > char *dirpath = g_path_get_dirname(path); > > > > > char *filename = g_path_get_basename(path); > > > > > int dirfd; > > > > > @@ -210,6 +297,7 @@ out: > > > > > g_free(dirpath); > > > > > g_free(filename); > > > > > return ret; > > > > > +#endif > > > > > } > > > > > > > > > > int pt_setxattr(FsContext *ctx, const char *path, const char *name, void > > *value, > > > > > @@ -221,6 +309,30 @@ int pt_setxattr(FsContext *ctx, const char *path, > const > > > > char *name, void *value, > > > > > ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, > > > > > const char *name) > > > > > { > > > > > +#ifdef CONFIG_WIN32 > > > > > + char *dirpath = g_path_get_dirname(path); > > > > > + char *filename = g_path_get_basename(path); > > > > > + char *full_dir_path; > > > > > + DIR *dir; > > > > > + ssize_t ret = -1; > > > > > + > > > > > + full_dir_path = merge_fs_path(ctx->fs_root, dirpath); > > > > > + > > > > > + dir = local_opendir_nofollow(ctx, dirpath); > > > > > + if (dir == NULL) { > > > > > + goto out; > > > > > + } > > > > > + closedir(dir); > > > > > + > > > > > + ret = fremovexattrat_nofollow(full_dir_path, filename, name); > > > > > + > > > > > +out: > > > > > + g_free(full_dir_path); > > > > > + g_free(dirpath); > > > > > + g_free(filename); > > > > > + return ret; > > > > > + > > > > > +#else > > > > > char *dirpath = g_path_get_dirname(path); > > > > > char *filename = g_path_get_basename(path); > > > > > int dirfd; > > > > > @@ -237,6 +349,7 @@ out: > > > > > g_free(dirpath); > > > > > g_free(filename); > > > > > return ret; > > > > > +#endif > > > > > } > > > > > > > > > > int pt_removexattr(FsContext *ctx, const char *path, const char *name) > > > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > > > > > index 225f31fc31..a04889c1d6 100644 > > > > > --- a/hw/9pfs/9p.c > > > > > +++ b/hw/9pfs/9p.c > > > > > @@ -38,6 +38,10 @@ > > > > > #include "migration/blocker.h" > > > > > #include "qemu/xxhash.h" > > > > > #include <math.h> > > > > > +#ifdef CONFIG_WIN32 > > > > > +#define UTIME_NOW ((1l << 30) - 1l) > > > > > +#define UTIME_OMIT ((1l << 30) - 2l) > > > > > +#endif > > > > > > > > > > int open_fd_hw; > > > > > int total_open_fd; > > > > > @@ -986,9 +990,11 @@ static int stat_to_qid(V9fsPDU *pdu, const struct > stat > > *stbuf, > > > > V9fsQID *qidp) > > > > > if (S_ISDIR(stbuf->st_mode)) { > > > > > qidp->type |= P9_QID_TYPE_DIR; > > > > > } > > > > > +#ifndef CONFIG_WIN32 > > > > > if (S_ISLNK(stbuf->st_mode)) { > > > > > qidp->type |= P9_QID_TYPE_SYMLINK; > > > > > } > > > > > +#endif > > > > > > > > > > return 0; > > > > > } > > > > > @@ -1095,6 +1101,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > > *extension) > > > > > ret |= S_IFDIR; > > > > > } > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > if (mode & P9_STAT_MODE_SYMLINK) { > > > > > ret |= S_IFLNK; > > > > > } > > > > > @@ -1104,6 +1111,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > > *extension) > > > > > if (mode & P9_STAT_MODE_NAMED_PIPE) { > > > > > ret |= S_IFIFO; > > > > > } > > > > > +#endif > > > > > if (mode & P9_STAT_MODE_DEVICE) { > > > > > if (extension->size && extension->data[0] == 'c') { > > > > > ret |= S_IFCHR; > > > > > @@ -1116,6 +1124,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > > *extension) > > > > > ret |= S_IFREG; > > > > > } > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > if (mode & P9_STAT_MODE_SETUID) { > > > > > ret |= S_ISUID; > > > > > } > > > > > @@ -1125,6 +1134,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString > > > > *extension) > > > > > if (mode & P9_STAT_MODE_SETVTX) { > > > > > ret |= S_ISVTX; > > > > > } > > > > > +#endif > > > > > > > > > > return ret; > > > > > } > > > > > @@ -1180,6 +1190,7 @@ static uint32_t stat_to_v9mode(const struct stat > *stbuf) > > > > > mode |= P9_STAT_MODE_DIR; > > > > > } > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > if (S_ISLNK(stbuf->st_mode)) { > > > > > mode |= P9_STAT_MODE_SYMLINK; > > > > > } > > > > > @@ -1191,11 +1202,13 @@ static uint32_t stat_to_v9mode(const struct stat > > *stbuf) > > > > > if (S_ISFIFO(stbuf->st_mode)) { > > > > > mode |= P9_STAT_MODE_NAMED_PIPE; > > > > > } > > > > > +#endif > > > > > > > > > > if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { > > > > > mode |= P9_STAT_MODE_DEVICE; > > > > > } > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > if (stbuf->st_mode & S_ISUID) { > > > > > mode |= P9_STAT_MODE_SETUID; > > > > > } > > > > > @@ -1207,6 +1220,7 @@ static uint32_t stat_to_v9mode(const struct stat > *stbuf) > > > > > if (stbuf->st_mode & S_ISVTX) { > > > > > mode |= P9_STAT_MODE_SETVTX; > > > > > } > > > > > +#endif > > > > > > > > > > return mode; > > > > > } > > > > > @@ -1245,9 +1259,16 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, > > > > V9fsPath *path, > > > > > return err; > > > > > } > > > > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { > > > > > +#ifndef CONFIG_WIN32 > > > > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > > > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > > > > major(stbuf->st_rdev), minor(stbuf->st_rdev)); > > > > > +#else > > > > > + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", > > > > > + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', > > > > > + 0, 0); > > > > > +#endif > > > > > + > > > > > } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { > > > > > v9fs_string_sprintf(&v9stat->extension, "%s %lu", > > > > > "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); > > > > > @@ -1315,7 +1336,11 @@ static int32_t blksize_to_iounit(const V9fsPDU *pdu, > > > > int32_t blksize) > > > > > > > > > > static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) > > > > > { > > > > > +#ifndef CONFIG_WIN32 > > > > > return blksize_to_iounit(pdu, stbuf->st_blksize); > > > > > +#else > > > > > + return blksize_to_iounit(pdu, 0); > > > > > +#endif > > > > > } > > > > > > > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, > > > > > @@ -1329,6 +1354,14 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > > struct > > > > stat *stbuf, > > > > > v9lstat->st_gid = stbuf->st_gid; > > > > > v9lstat->st_rdev = stbuf->st_rdev; > > > > > v9lstat->st_size = stbuf->st_size; > > > > > + > > > > > +#ifdef CONFIG_WIN32 > > > > > + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > > > > + v9lstat->st_blocks = 0; > > > > > + v9lstat->st_atime_sec = stbuf->st_atime; > > > > > + v9lstat->st_mtime_sec = stbuf->st_mtime; > > > > > + v9lstat->st_ctime_sec = stbuf->st_ctime; > > > > > +#else /* !CONFIG_WIN32 */ > > > > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); > > > > > v9lstat->st_blocks = stbuf->st_blocks; > > > > > v9lstat->st_atime_sec = stbuf->st_atime; > > > > > @@ -1343,6 +1376,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const > > struct > > > > stat *stbuf, > > > > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; > > > > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; > > > > > #endif > > > > > +#endif /* CONFIG_WIN32 */ > > > > > + > > > > > /* Currently we only support BASIC fields in stat */ > > > > > v9lstat->st_result_mask = P9_STATS_BASIC; > > > > > > > > > > @@ -1759,7 +1794,11 @@ static bool name_is_illegal(const char *name) > > > > > > > > > > static bool same_stat_id(const struct stat *a, const struct stat *b) > > > > > { > > > > > +#ifdef CONFIG_WIN32 > > > > > + return 0; > > > > > +#else > > > > > return a->st_dev == b->st_dev && a->st_ino == b->st_ino; > > > > > +#endif /* CONFIG_WIN32 */ > > > > > } > > > > > > > > > > static void coroutine_fn v9fs_walk(void *opaque) > > > > > @@ -2300,7 +2339,11 @@ static int coroutine_fn > > v9fs_do_readdir_with_stat(V9fsPDU > > > > *pdu, > > > > > count += len; > > > > > v9fs_stat_free(&v9stat); > > > > > v9fs_path_free(&path); > > > > > +#ifndef CONFIG_WIN32 > > > > > saved_dir_pos = qemu_dirent_off(dent); > > > > > +#else > > > > > + saved_dir_pos = v9fs_co_telldir(pdu, fidp); > > > > > +#endif > > > > > } > > > > > > > > > > v9fs_readdir_unlock(&fidp->fs.dir); > > > > > @@ -2501,14 +2544,32 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU > *pdu, > > > > V9fsFidState *fidp, > > > > > qid.version = 0; > > > > > } > > > > > > > > > > +#ifdef CONFIG_WIN32 > > > > > + /* > > > > > + * Windows does not have dent->d_off, get offset by calling telldir() > > > > > + * manually. > > > > > + */ > > > > > + off = v9fs_co_telldir(pdu, fidp); > > > > > +#else > > > > > off = qemu_dirent_off(dent); > > > > > +#endif > > > > > v9fs_string_init(&name); > > > > > v9fs_string_sprintf(&name, "%s", dent->d_name); > > > > > > > > > > +#ifdef CONFIG_WIN32 > > > > > + /* > > > > > + * Windows does not have dent->d_type > > > > > + */ > > > > > + > > > > > + len = pdu_marshal(pdu, 11 + count, "Qqbs", > > > > > + &qid, off, > > > > > + 0, &name); > > > > > +#else > > > > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */ > > > > > len = pdu_marshal(pdu, 11 + count, "Qqbs", > > > > > &qid, off, > > > > > dent->d_type, &name); > > > > > +#endif > > > > > > > > > > v9fs_string_free(&name); > > > > > > > > > > @@ -2838,8 +2899,14 @@ static void coroutine_fn v9fs_create(void *opaque) > > > > > } > > > > > > > > > > nmode |= perm & 0777; > > > > > + > > > > > +#ifndef CONFIG_WIN32 > > > > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > > > > makedev(major, minor), nmode, &stbuf); > > > > > +#else > > > > > + err = -ENOTSUP; > > > > > +#endif > > > > > + > > > > > if (err < 0) { > > > > > goto out; > > > > > } > > > > > @@ -2864,8 +2931,12 @@ static void coroutine_fn v9fs_create(void *opaque) > > > > > v9fs_path_copy(&fidp->path, &path); > > > > > v9fs_path_unlock(s); > > > > > } else if (perm & P9_STAT_MODE_SOCKET) { > > > > > +#ifndef CONFIG_WIN32 > > > > > err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, > > > > > 0, S_IFSOCK | (perm & 0777), &stbuf); > > > > > +#else > > > > > + err = -ENOTSUP; > > > > > +#endif > > > > > if (err < 0) { > > > > > goto out; > > > > > } > > > > > @@ -3600,6 +3671,7 @@ out_nofid: > > > > > static void coroutine_fn v9fs_mknod(void *opaque) > > > > > { > > > > > > > > > > +#ifndef CONFIG_WIN32 > > > > > int mode; > > > > > gid_t gid; > > > > > int32_t fid; > > > > > @@ -3656,6 +3728,11 @@ out: > > > > > out_nofid: > > > > > pdu_complete(pdu, err); > > > > > v9fs_string_free(&name); > > > > > +#else > > > > > + V9fsPDU *pdu = opaque; > > > > > + > > > > > + pdu_complete(pdu, -1); > > > > > +#endif > > > > > } > > > > > > > > > > /* > > > > > @@ -3928,7 +4005,7 @@ out_nofid: > > > > > #if defined(CONFIG_LINUX) > > > > > /* Currently, only Linux has XATTR_SIZE_MAX */ > > > > > #define P9_XATTR_SIZE_MAX XATTR_SIZE_MAX > > > > > -#elif defined(CONFIG_DARWIN) > > > > > +#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32) > > > > > /* > > > > > * Darwin doesn't seem to define a maximum xattr size in its user > > > > > * space header, so manually configure it across platforms as 64k. > > > > > @@ -3945,6 +4022,7 @@ out_nofid: > > > > > > > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) > > > > > { > > > > > +#ifndef CONFIG_WIN32 > > > > > int flags, rflags = 0; > > > > > int32_t fid; > > > > > uint64_t size; > > > > > @@ -4006,10 +4084,15 @@ out_put_fid: > > > > > out_nofid: > > > > > pdu_complete(pdu, err); > > > > > v9fs_string_free(&name); > > > > > +#else > > > > > + V9fsPDU *pdu = opaque; > > > > > + pdu_complete(pdu, -1); > > > > > +#endif > > > > > } > > > > > > > > > > static void coroutine_fn v9fs_readlink(void *opaque) > > > > > { > > > > > +#ifndef CONFIG_WIN32 > > > > > V9fsPDU *pdu = opaque; > > > > > size_t offset = 7; > > > > > V9fsString target; > > > > > @@ -4045,6 +4128,10 @@ out: > > > > > put_fid(pdu, fidp); > > > > > out_nofid: > > > > > pdu_complete(pdu, err); > > > > > +#else > > > > > + V9fsPDU *pdu = opaque; > > > > > + pdu_complete(pdu, -1); > > > > > +#endif > > > > > } > > > > > > > > > > static CoroutineEntry *pdu_co_handlers[] = { > > > > > @@ -4306,6 +4393,7 @@ void v9fs_reset(V9fsState *s) > > > > > > > > > > static void __attribute__((__constructor__)) v9fs_set_fd_limit(void) > > > > > { > > > > > +#ifndef CONFIG_WIN32 > > > > > struct rlimit rlim; > > > > > if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { > > > > > error_report("Failed to get the resource limit"); > > > > > @@ -4313,4 +4401,5 @@ static void __attribute__((__constructor__)) > > > > v9fs_set_fd_limit(void) > > > > > } > > > > > open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur / 3); > > > > > open_fd_rc = rlim.rlim_cur / 2; > > > > > +#endif > > > > > } > > > > > diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c > > > > > index 93ba44fb75..2fbe7b831b 100644 > > > > > --- a/hw/9pfs/codir.c > > > > > +++ b/hw/9pfs/codir.c > > > > > @@ -78,6 +78,9 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > *fidp, > > > > > int len, err = 0; > > > > > int32_t size = 0; > > > > > off_t saved_dir_pos; > > > > > +#ifdef CONFIG_WIN32 > > > > > + off_t next_dir_pos; > > > > > +#endif > > > > > struct dirent *dent; > > > > > struct V9fsDirEnt *e = NULL; > > > > > V9fsPath path; > > > > > @@ -124,6 +127,14 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > > *fidp, > > > > > break; > > > > > } > > > > > > > > > > +#ifdef CONFIG_WIN32 > > > > > + next_dir_pos = s->ops->telldir(&s->ctx, &fidp->fs); > > > > > + if (next_dir_pos < 0) { > > > > > + err = next_dir_pos; > > > > > + goto out; > > > > > + } > > > > > +#endif > > > > > + > > > > > /* > > > > > * stop this loop as soon as it would exceed the allowed maximum > > > > > * response message size for the directory entries collected so > far, > > > > > @@ -168,7 +179,11 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState > > *fidp, > > > > > } > > > > > > > > > > size += len; > > > > > +#ifndef CONFIG_WIN32 > > > > > saved_dir_pos = qemu_dirent_off(dent); > > > > > +#else > > > > > + saved_dir_pos = next_dir_pos; > > > > > +#endif > > > > > } > > > > > > > > > > /* restore (last) saved position */ > > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 2:17 ` Shi, Guohuai @ 2022-05-10 10:18 ` Christian Schoenebeck 2022-05-10 11:54 ` Christian Schoenebeck 0 siblings, 1 reply; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-10 10:18 UTC (permalink / raw) To: qemu-devel, Meng, Bin; +Cc: Greg Kurz, Bin Meng, Shi, Guohuai On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: [...] > > > > > I tend to agree with Christian's remarks that this patch is too big > > > > > and that the choice of introducing right away a new implementation > > > > > of 9p-local for windows hosts is too bold to start with. We need to > > > > > clearly understand what's diverging between windows and linux in > > > > > order > > > > > to make such a decision. You should first try to introduce the > > > > > required > > > > > abstractions to cope with these differences, so that we can review. > > > > > > > > > > > > > > > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a directory. > > > > Windows (actually MinGW library) only allows opendir() for a > > > > directory. That missing behaviour could be implemented in 9p-util-win.c, similar to the missing behaviours of mknodat() for macOS which did not support a bunch of things like creating a UNIX socket file and more: https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f7628643d3d8d > > > Does MinGW have dirfd() ? > > > > > > No. > > MinGW does not open any directory. > > Here is opendir() source code of MinGW: > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/dirent. > > c#L42 > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > > > > > > > Windows does not support APIs like "*at" (openat(), renameat(), etc.) Like already suggested before on your previous RFC version, it is possible to use the same workaround as we are using for macOS hosts already (which was missing mknodat()): pthread_fchdir_np(...) mknod(...) https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin.c#L84 So on Windows it would be viable to: chdir(...) open(...) The same approach could be used for any missing *at() function for Windows. > > > > > > > > > > > > Ouch... > > > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub file or directory > > > > in 9P > > > > mount > > > > > directory. > > > > > > > This commit use merge_fs_path() to build up full filename by string > > > > concatenation. > > > > > > I know that may have a risk of security, but Windows does fully > > > > support POSIX You will not find anybody merging code that's inherently insecure. > > > I understand from your various answers that symlinks aren't > > > currently supported by window's POSIX API. Is this forever ? > > > Google do mentions symlinks in windows 10. What's the story > > > there ? How do they behave ? How would they be exposed to the > > > client ? Be aware that, even if the client cannot create symlinks, > > > an existing symlink could be used to escape with rename(). > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > Other thing that comes to mind is that windows hosts should > > > maybe use the mapped or mapped-file security modes since > > > they emulate symlinks with a simple file hidden in the > > > VIRTFS_META_DIR directory. > > > > > > > > > > > > Cheers, > > > > > > > > > > > > -- > > > Greg > > > > > > > > > > > > Windows native API support symbolic link file start from Windows Vista: > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-crea > > tes ymboliclinka > > > > I mean Windows POSIX APIs do not support symbolic link (MinGW use Win32 > > POSIX APIs) So we can not create symbolic link by MinGW. A function with POSIX signature could be added to 9p-util-win.c which would call the native Windows function to create symlinks. > > Anyway, there is another solution: re-work whole 9PFS code: not only > > 9p-local.c, but also every file in 9p driver. > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, close), > > by Windows Native APIs (e.g. open -> CreateFile, lseek -> SetFilePointer, > > read -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) > > Then 9P can use Windows symbolic link feature. > > However, I do think it is a good idea to replace everything. > > > TYPO: it NOT is a good idea to replace everything. Right, that does not make sense. The way to go is adding and implementing missing system functions with POSIX signatures and POSIX behaviour for Windows. Not turning the entire code base upside down. Best regards, Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 10:18 ` Christian Schoenebeck @ 2022-05-10 11:54 ` Christian Schoenebeck 2022-05-10 13:40 ` Greg Kurz 0 siblings, 1 reply; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-10 11:54 UTC (permalink / raw) To: qemu-devel, Meng, Bin; +Cc: Greg Kurz, Bin Meng, Shi, Guohuai On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > [...] > > > > > > > I tend to agree with Christian's remarks that this patch is too > > > > > > big > > > > > > and that the choice of introducing right away a new implementation > > > > > > of 9p-local for windows hosts is too bold to start with. We need > > > > > > to > > > > > > clearly understand what's diverging between windows and linux in > > > > > > order > > > > > > to make such a decision. You should first try to introduce the > > > > > > required > > > > > > abstractions to cope with these differences, so that we can > > > > > > review. > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a directory. > > > > > Windows (actually MinGW library) only allows opendir() for a > > > > > directory. > > That missing behaviour could be implemented in 9p-util-win.c, similar to the > missing behaviours of mknodat() for macOS which did not support a bunch of > things like creating a UNIX socket file and more: > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f7628643d3d8d > > > > Does MinGW have dirfd() ? > > > > > > No. > > > MinGW does not open any directory. > > > Here is opendir() source code of MinGW: > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/diren > > > t. > > > c#L42 > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > Windows does not support APIs like "*at" (openat(), renameat(), > > > > > etc.) > > Like already suggested before on your previous RFC version, it is possible > to use the same workaround as we are using for macOS hosts already (which > was missing mknodat()): > > pthread_fchdir_np(...) > mknod(...) > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin.c#L84 > > So on Windows it would be viable to: > > chdir(...) > open(...) > > The same approach could be used for any missing *at() function for Windows. Problem though is that the chdir() functions on Windows all seem to have process-wide effect, we would need to change the current directory only for the current thread, because filesystem access of 9p server is multi-threaded. Protecting the chdir(); foo(); calls by a process wide global mutex isn't very appealing either. :/ > > > > Ouch... > > > > > > > > > So 9PFS can not use any openat() for opening a sub file or directory > > > > > in 9P > > > > > > mount > > > > > > > directory. > > > > > > > > > This commit use merge_fs_path() to build up full filename by string > > > > > > concatenation. > > > > > > > > I know that may have a risk of security, but Windows does fully > > > > > support POSIX > > You will not find anybody merging code that's inherently insecure. > > > > > I understand from your various answers that symlinks aren't > > > > currently supported by window's POSIX API. Is this forever ? > > > > Google do mentions symlinks in windows 10. What's the story > > > > there ? How do they behave ? How would they be exposed to the > > > > client ? Be aware that, even if the client cannot create symlinks, > > > > an existing symlink could be used to escape with rename(). > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts should > > > > maybe use the mapped or mapped-file security modes since > > > > they emulate symlinks with a simple file hidden in the > > > > VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > -- > > > > Greg > > > > > > Windows native API support symbolic link file start from Windows Vista: > > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-cr > > > ea > > > tes ymboliclinka > > > > > > I mean Windows POSIX APIs do not support symbolic link (MinGW use Win32 > > > POSIX APIs) So we can not create symbolic link by MinGW. > > A function with POSIX signature could be added to 9p-util-win.c which would > call the native Windows function to create symlinks. > > > > Anyway, there is another solution: re-work whole 9PFS code: not only > > > 9p-local.c, but also every file in 9p driver. > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, close), > > > by Windows Native APIs (e.g. open -> CreateFile, lseek -> > > > SetFilePointer, > > > read -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) > > > Then 9P can use Windows symbolic link feature. > > > However, I do think it is a good idea to replace everything. > > > > TYPO: it NOT is a good idea to replace everything. > > Right, that does not make sense. The way to go is adding and implementing > missing system functions with POSIX signatures and POSIX behaviour for > Windows. Not turning the entire code base upside down. > > Best regards, > Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 11:54 ` Christian Schoenebeck @ 2022-05-10 13:40 ` Greg Kurz 2022-05-10 14:04 ` Christian Schoenebeck 0 siblings, 1 reply; 34+ messages in thread From: Greg Kurz @ 2022-05-10 13:40 UTC (permalink / raw) To: Christian Schoenebeck; +Cc: qemu-devel, Meng, Bin, Bin Meng, Shi, Guohuai On Tue, 10 May 2022 13:54:46 +0200 Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > > [...] > > > > > > > > > I tend to agree with Christian's remarks that this patch is too > > > > > > > big > > > > > > > and that the choice of introducing right away a new implementation > > > > > > > of 9p-local for windows hosts is too bold to start with. We need > > > > > > > to > > > > > > > clearly understand what's diverging between windows and linux in > > > > > > > order > > > > > > > to make such a decision. You should first try to introduce the > > > > > > > required > > > > > > > abstractions to cope with these differences, so that we can > > > > > > > review. > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a directory. > > > > > > Windows (actually MinGW library) only allows opendir() for a > > > > > > directory. > > > > That missing behaviour could be implemented in 9p-util-win.c, similar to the > > missing behaviours of mknodat() for macOS which did not support a bunch of > > things like creating a UNIX socket file and more: > > > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f7628643d3d8d > > > > > Does MinGW have dirfd() ? > > > > > > > > No. > > > > MinGW does not open any directory. > > > > Here is opendir() source code of MinGW: > > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/diren > > > > t. > > > > c#L42 > > > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > Windows does not support APIs like "*at" (openat(), renameat(), > > > > > > etc.) > > > > Like already suggested before on your previous RFC version, it is possible > > to use the same workaround as we are using for macOS hosts already (which > > was missing mknodat()): > > > > pthread_fchdir_np(...) > > mknod(...) > > > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin.c#L84 > > > > So on Windows it would be viable to: > > > > chdir(...) > > open(...) > > > > The same approach could be used for any missing *at() function for Windows. > > Problem though is that the chdir() functions on Windows all seem to have > process-wide effect, we would need to change the current directory only for > the current thread, because filesystem access of 9p server is multi-threaded. > > Protecting the chdir(); foo(); calls by a process wide global mutex isn't very > appealing either. :/ > And it wouldn't be safe anyway because I'm pretty sure that the rest of the QEMU code assumes that the current directory is invariant, e.g. user could be very confused by 'drive_add file=./foo.img' not working. BTW duckduckgo gives: https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent-of-openat So yes it seems to be technically possible to implement *at() functions on windows. This is the only way to avoid CVE-2016-9602 in the QEMU process. Another option is to use the proxy backend : this offloads all fs accesses to an external process running virtfs-proxy-helper, that runs privileged and chroot() into the shared directory so that it can safely use path based syscalls. > > > > > Ouch... > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub file or directory > > > > > > in 9P > > > > > > > > mount > > > > > > > > > directory. > > > > > > > > > > > This commit use merge_fs_path() to build up full filename by string > > > > > > > > concatenation. > > > > > > > > > > I know that may have a risk of security, but Windows does fully > > > > > > support POSIX > > > > You will not find anybody merging code that's inherently insecure. > > > > > > > I understand from your various answers that symlinks aren't > > > > > currently supported by window's POSIX API. Is this forever ? > > > > > Google do mentions symlinks in windows 10. What's the story > > > > > there ? How do they behave ? How would they be exposed to the > > > > > client ? Be aware that, even if the client cannot create symlinks, > > > > > an existing symlink could be used to escape with rename(). > > > > > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts should > > > > > maybe use the mapped or mapped-file security modes since > > > > > they emulate symlinks with a simple file hidden in the > > > > > VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > -- > > > > > Greg > > > > > > > > Windows native API support symbolic link file start from Windows Vista: > > > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-cr > > > > ea > > > > tes ymboliclinka > > > > > > > > I mean Windows POSIX APIs do not support symbolic link (MinGW use Win32 > > > > POSIX APIs) So we can not create symbolic link by MinGW. > > > > A function with POSIX signature could be added to 9p-util-win.c which would > > call the native Windows function to create symlinks. > > > > > > Anyway, there is another solution: re-work whole 9PFS code: not only > > > > 9p-local.c, but also every file in 9p driver. > > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, close), > > > > by Windows Native APIs (e.g. open -> CreateFile, lseek -> > > > > SetFilePointer, > > > > read -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) > > > > Then 9P can use Windows symbolic link feature. > > > > However, I do think it is a good idea to replace everything. > > > > > > TYPO: it NOT is a good idea to replace everything. > > > > Right, that does not make sense. The way to go is adding and implementing > > missing system functions with POSIX signatures and POSIX behaviour for > > Windows. Not turning the entire code base upside down. > > > > Best regards, > > Christian Schoenebeck > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 13:40 ` Greg Kurz @ 2022-05-10 14:04 ` Christian Schoenebeck 2022-05-10 14:34 ` Greg Kurz 0 siblings, 1 reply; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-10 14:04 UTC (permalink / raw) To: qemu-devel; +Cc: Meng, Bin, Bin Meng, Shi, Guohuai, Greg Kurz On Dienstag, 10. Mai 2022 15:40:06 CEST Greg Kurz wrote: > On Tue, 10 May 2022 13:54:46 +0200 > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > > > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > > > [...] > > > > > > > > > > > I tend to agree with Christian's remarks that this patch is > > > > > > > > too > > > > > > > > big > > > > > > > > and that the choice of introducing right away a new > > > > > > > > implementation > > > > > > > > of 9p-local for windows hosts is too bold to start with. We > > > > > > > > need > > > > > > > > to > > > > > > > > clearly understand what's diverging between windows and linux > > > > > > > > in > > > > > > > > order > > > > > > > > to make such a decision. You should first try to introduce the > > > > > > > > required > > > > > > > > abstractions to cope with these differences, so that we can > > > > > > > > review. > > > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a > > > > > > > directory. > > > > > > > Windows (actually MinGW library) only allows opendir() for a > > > > > > > directory. > > > > > > That missing behaviour could be implemented in 9p-util-win.c, similar to > > > the missing behaviours of mknodat() for macOS which did not support a > > > bunch of things like creating a UNIX socket file and more: > > > > > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f7628643d > > > 3d8d> > > > > > > > Does MinGW have dirfd() ? > > > > > > > > > > No. > > > > > MinGW does not open any directory. > > > > > Here is opendir() source code of MinGW: > > > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/d > > > > > iren > > > > > t. > > > > > c#L42 > > > > > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > > > Windows does not support APIs like "*at" (openat(), renameat(), > > > > > > > etc.) > > > > > > Like already suggested before on your previous RFC version, it is > > > possible > > > to use the same workaround as we are using for macOS hosts already > > > (which > > > > > > was missing mknodat()): > > > pthread_fchdir_np(...) > > > mknod(...) > > > > > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin.c#L84 > > > > > > So on Windows it would be viable to: > > > chdir(...) > > > open(...) > > > > > > The same approach could be used for any missing *at() function for > > > Windows. > > > > Problem though is that the chdir() functions on Windows all seem to have > > process-wide effect, we would need to change the current directory only > > for > > the current thread, because filesystem access of 9p server is > > multi-threaded. > > > > Protecting the chdir(); foo(); calls by a process wide global mutex isn't > > very appealing either. :/ > > And it wouldn't be safe anyway because I'm pretty sure that the rest > of the QEMU code assumes that the current directory is invariant, e.g. > user could be very confused by 'drive_add file=./foo.img' not working. > > BTW duckduckgo gives: > > https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent-o > f-openat > > So yes it seems to be technically possible to implement *at() functions > on windows. This is the only way to avoid CVE-2016-9602 in the QEMU > process. +1 > Another option is to use the proxy backend : this offloads all fs > accesses to an external process running virtfs-proxy-helper, that > runs privileged and chroot() into the shared directory so that it > can safely use path based syscalls. As a very last resort, maybe. But just for the other two guys to know upfront: the proxy backend is very slow and not in good shape. There were plans to deprecate the proxy backend therefore, as it's more or less dead. > > > > > > Ouch... > > > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub file or > > > > > > > directory > > > > > > > in 9P > > > > > > > > > > mount > > > > > > > > > > > directory. > > > > > > > > > > > > > This commit use merge_fs_path() to build up full filename by > > > > > > > string > > > > > > > > > > concatenation. > > > > > > > > > > > > I know that may have a risk of security, but Windows does fully > > > > > > > support POSIX > > > > > > You will not find anybody merging code that's inherently insecure. > > > > > > > > > I understand from your various answers that symlinks aren't > > > > > > currently supported by window's POSIX API. Is this forever ? > > > > > > Google do mentions symlinks in windows 10. What's the story > > > > > > there ? How do they behave ? How would they be exposed to the > > > > > > client ? Be aware that, even if the client cannot create symlinks, > > > > > > an existing symlink could be used to escape with rename(). > > > > > > > > > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts should > > > > > > maybe use the mapped or mapped-file security modes since > > > > > > they emulate symlinks with a simple file hidden in the > > > > > > VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > > > > > -- > > > > > > Greg > > > > > > > > > > Windows native API support symbolic link file start from Windows > > > > > Vista: > > > > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbas > > > > > e-cr > > > > > ea > > > > > tes ymboliclinka > > > > > > > > > > I mean Windows POSIX APIs do not support symbolic link (MinGW use > > > > > Win32 > > > > > POSIX APIs) So we can not create symbolic link by MinGW. > > > > > > A function with POSIX signature could be added to 9p-util-win.c which > > > would > > > call the native Windows function to create symlinks. > > > > > > > > Anyway, there is another solution: re-work whole 9PFS code: not only > > > > > 9p-local.c, but also every file in 9p driver. > > > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, > > > > > close), > > > > > by Windows Native APIs (e.g. open -> CreateFile, lseek -> > > > > > SetFilePointer, > > > > > read -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) > > > > > Then 9P can use Windows symbolic link feature. > > > > > However, I do think it is a good idea to replace everything. > > > > > > > > TYPO: it NOT is a good idea to replace everything. > > > > > > Right, that does not make sense. The way to go is adding and > > > implementing > > > missing system functions with POSIX signatures and POSIX behaviour for > > > Windows. Not turning the entire code base upside down. > > > > > > Best regards, > > > Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 14:04 ` Christian Schoenebeck @ 2022-05-10 14:34 ` Greg Kurz 2022-05-10 15:35 ` Shi, Guohuai 0 siblings, 1 reply; 34+ messages in thread From: Greg Kurz @ 2022-05-10 14:34 UTC (permalink / raw) To: Christian Schoenebeck; +Cc: qemu-devel, Meng, Bin, Bin Meng, Shi, Guohuai On Tue, 10 May 2022 16:04:28 +0200 Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > On Dienstag, 10. Mai 2022 15:40:06 CEST Greg Kurz wrote: > > On Tue, 10 May 2022 13:54:46 +0200 > > > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > > > > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > > > > [...] > > > > > > > > > > > > > I tend to agree with Christian's remarks that this patch is > > > > > > > > > too > > > > > > > > > big > > > > > > > > > and that the choice of introducing right away a new > > > > > > > > > implementation > > > > > > > > > of 9p-local for windows hosts is too bold to start with. We > > > > > > > > > need > > > > > > > > > to > > > > > > > > > clearly understand what's diverging between windows and linux > > > > > > > > > in > > > > > > > > > order > > > > > > > > > to make such a decision. You should first try to introduce the > > > > > > > > > required > > > > > > > > > abstractions to cope with these differences, so that we can > > > > > > > > > review. > > > > > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a > > > > > > > > directory. > > > > > > > > Windows (actually MinGW library) only allows opendir() for a > > > > > > > > directory. > > > > > > > > That missing behaviour could be implemented in 9p-util-win.c, similar to > > > > the missing behaviours of mknodat() for macOS which did not support a > > > > bunch of things like creating a UNIX socket file and more: > > > > > > > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f7628643d > > > > 3d8d> > > > > > > > > Does MinGW have dirfd() ? > > > > > > > > > > > > No. > > > > > > MinGW does not open any directory. > > > > > > Here is opendir() source code of MinGW: > > > > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-crt/misc/d > > > > > > iren > > > > > > t. > > > > > > c#L42 > > > > > > > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > > > > > Windows does not support APIs like "*at" (openat(), renameat(), > > > > > > > > etc.) > > > > > > > > Like already suggested before on your previous RFC version, it is > > > > possible > > > > to use the same workaround as we are using for macOS hosts already > > > > (which > > > > > > > > was missing mknodat()): > > > > pthread_fchdir_np(...) > > > > mknod(...) > > > > > > > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin.c#L84 > > > > > > > > So on Windows it would be viable to: > > > > chdir(...) > > > > open(...) > > > > > > > > The same approach could be used for any missing *at() function for > > > > Windows. > > > > > > Problem though is that the chdir() functions on Windows all seem to have > > > process-wide effect, we would need to change the current directory only > > > for > > > the current thread, because filesystem access of 9p server is > > > multi-threaded. > > > > > > Protecting the chdir(); foo(); calls by a process wide global mutex isn't > > > very appealing either. :/ > > > > And it wouldn't be safe anyway because I'm pretty sure that the rest > > of the QEMU code assumes that the current directory is invariant, e.g. > > user could be very confused by 'drive_add file=./foo.img' not working. > > > > BTW duckduckgo gives: > > > > https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent-o > > f-openat > > > > So yes it seems to be technically possible to implement *at() functions > > on windows. This is the only way to avoid CVE-2016-9602 in the QEMU > > process. > > +1 > > > Another option is to use the proxy backend : this offloads all fs > > accesses to an external process running virtfs-proxy-helper, that > > runs privileged and chroot() into the shared directory so that it > > can safely use path based syscalls. > > As a very last resort, maybe. But just for the other two guys to know upfront: > the proxy backend is very slow and not in good shape. There were plans to > deprecate the proxy backend therefore, as it's more or less dead. > Yeah as mentioned before, the way to go now would be to come with a vhost-user implementation like virtiofsd. This would address all perf problems we have with proxy since the client would directly talk to the external process. This should also provide better perf than the local backend since it wouldn't have to do do the "at*()" dance thanks to chroot(). > > > > > > > Ouch... > > > > > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub file or > > > > > > > > directory > > > > > > > > in 9P > > > > > > > > > > > > mount > > > > > > > > > > > > > directory. > > > > > > > > > > > > > > > This commit use merge_fs_path() to build up full filename by > > > > > > > > string > > > > > > > > > > > > concatenation. > > > > > > > > > > > > > > I know that may have a risk of security, but Windows does fully > > > > > > > > support POSIX > > > > > > > > You will not find anybody merging code that's inherently insecure. > > > > > > > > > > > I understand from your various answers that symlinks aren't > > > > > > > currently supported by window's POSIX API. Is this forever ? > > > > > > > Google do mentions symlinks in windows 10. What's the story > > > > > > > there ? How do they behave ? How would they be exposed to the > > > > > > > client ? Be aware that, even if the client cannot create symlinks, > > > > > > > an existing symlink could be used to escape with rename(). > > > > > > > > > > > > > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > > > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts should > > > > > > > maybe use the mapped or mapped-file security modes since > > > > > > > they emulate symlinks with a simple file hidden in the > > > > > > > VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > > > > > > > > > -- > > > > > > > Greg > > > > > > > > > > > > Windows native API support symbolic link file start from Windows > > > > > > Vista: > > > > > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbas > > > > > > e-cr > > > > > > ea > > > > > > tes ymboliclinka > > > > > > > > > > > > I mean Windows POSIX APIs do not support symbolic link (MinGW use > > > > > > Win32 > > > > > > POSIX APIs) So we can not create symbolic link by MinGW. > > > > > > > > A function with POSIX signature could be added to 9p-util-win.c which > > > > would > > > > call the native Windows function to create symlinks. > > > > > > > > > > Anyway, there is another solution: re-work whole 9PFS code: not only > > > > > > 9p-local.c, but also every file in 9p driver. > > > > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, write, > > > > > > close), > > > > > > by Windows Native APIs (e.g. open -> CreateFile, lseek -> > > > > > > SetFilePointer, > > > > > > read -> ReadFile, write -> WriteFile, close -> CloseHandle, etc.) > > > > > > Then 9P can use Windows symbolic link feature. > > > > > > However, I do think it is a good idea to replace everything. > > > > > > > > > > TYPO: it NOT is a good idea to replace everything. > > > > > > > > Right, that does not make sense. The way to go is adding and > > > > implementing > > > > missing system functions with POSIX signatures and POSIX behaviour for > > > > Windows. Not turning the entire code base upside down. > > > > > > > > Best regards, > > > > Christian Schoenebeck > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 14:34 ` Greg Kurz @ 2022-05-10 15:35 ` Shi, Guohuai 2022-05-11 11:18 ` Christian Schoenebeck 2022-05-11 12:18 ` Greg Kurz 0 siblings, 2 replies; 34+ messages in thread From: Shi, Guohuai @ 2022-05-10 15:35 UTC (permalink / raw) To: Greg Kurz, Christian Schoenebeck; +Cc: qemu-devel, Meng, Bin, Bin Meng Let's force on the security issue: Firstly, this answer ( https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent-of-openat ) is useless for QEMU. It uses Windows native API NtCreateFile() and accesses files by Windows handle. But 9PFS is using Windows POSIX interface, handle can not be used in POSIX interface. Actually, Windows provide similar APIs like GetFinalPathNameByHandle()/GetFileInformationByHandle(). It can also get file information by Windows handle. Windows POSIX interface do not support NO_FOLLOW flags, that means, Windows POSIX open() always translate symbolic link. So everything are finally point to one limitation: Windows POSIX interfaces do not support symbolic link and always translate link. For the security reason, I think it is reasonable to disable symbolic link support on Windows host for 9PFS. I can re-work this patch to adding a symbolic link check during path-walk operation and stop it when get a symbolic link. Best Regards, Guohuai > -----Original Message----- > From: Greg Kurz <groug@kaod.org> > Sent: 2022年5月10日 22:35 > To: Christian Schoenebeck <qemu_oss@crudebyte.com> > Cc: qemu-devel@nongnu.org; Meng, Bin <Bin.Meng@windriver.com>; Bin Meng > <bmeng.cn@gmail.com>; Shi, Guohuai <Guohuai.Shi@windriver.com> > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > Windows > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > On Tue, 10 May 2022 16:04:28 +0200 > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > On Dienstag, 10. Mai 2022 15:40:06 CEST Greg Kurz wrote: > > > On Tue, 10 May 2022 13:54:46 +0200 > > > > > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > > On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > > > > > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > > > > > [...] > > > > > > > > > > > > > > > I tend to agree with Christian's remarks that this > > > > > > > > > > patch is too big and that the choice of introducing > > > > > > > > > > right away a new implementation of 9p-local for > > > > > > > > > > windows hosts is too bold to start with. We need to > > > > > > > > > > clearly understand what's diverging between windows > > > > > > > > > > and linux in order to make such a decision. You should > > > > > > > > > > first try to introduce the required abstractions to > > > > > > > > > > cope with these differences, so that we can review. > > > > > > > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a > > > > > > > > > directory. > > > > > > > > > Windows (actually MinGW library) only allows opendir() > > > > > > > > > for a directory. > > > > > > > > > > That missing behaviour could be implemented in 9p-util-win.c, > > > > > similar to the missing behaviours of mknodat() for macOS which > > > > > did not support a bunch of things like creating a UNIX socket file and > more: > > > > > > > > > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f > > > > > 7628643d > > > > > 3d8d> > > > > > > > > > Does MinGW have dirfd() ? > > > > > > > > > > > > > > No. > > > > > > > MinGW does not open any directory. > > > > > > > Here is opendir() source code of MinGW: > > > > > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-cr > > > > > > > t/misc/d > > > > > > > iren > > > > > > > t. > > > > > > > c#L42 > > > > > > > > > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > > > > > > > Windows does not support APIs like "*at" (openat(), > > > > > > > > > renameat(), > > > > > > > > > etc.) > > > > > > > > > > Like already suggested before on your previous RFC version, it > > > > > is possible to use the same workaround as we are using for macOS > > > > > hosts already (which > > > > > > > > > > was missing mknodat()): > > > > > pthread_fchdir_np(...) > > > > > mknod(...) > > > > > > > > > > > > > > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin. > > > > > c#L84 > > > > > > > > > > So on Windows it would be viable to: > > > > > chdir(...) > > > > > open(...) > > > > > > > > > > The same approach could be used for any missing *at() function > > > > > for Windows. > > > > > > > > Problem though is that the chdir() functions on Windows all seem > > > > to have process-wide effect, we would need to change the current > > > > directory only for the current thread, because filesystem access > > > > of 9p server is multi-threaded. > > > > > > > > Protecting the chdir(); foo(); calls by a process wide global > > > > mutex isn't very appealing either. :/ > > > > > > And it wouldn't be safe anyway because I'm pretty sure that the rest > > > of the QEMU code assumes that the current directory is invariant, e.g. > > > user could be very confused by 'drive_add file=./foo.img' not working. > > > > > > BTW duckduckgo gives: > > > > > > https://stackoverflow.com/questions/32138524/is-there-a-windows-equi > > > valent-o > > > f-openat > > > > > > So yes it seems to be technically possible to implement *at() > > > functions on windows. This is the only way to avoid CVE-2016-9602 in > > > the QEMU process. > > > > +1 > > > > > Another option is to use the proxy backend : this offloads all fs > > > accesses to an external process running virtfs-proxy-helper, that > > > runs privileged and chroot() into the shared directory so that it > > > can safely use path based syscalls. > > > > As a very last resort, maybe. But just for the other two guys to know upfront: > > the proxy backend is very slow and not in good shape. There were plans > > to deprecate the proxy backend therefore, as it's more or less dead. > > > > Yeah as mentioned before, the way to go now would be to come with a vhost-user > implementation like virtiofsd. This would address all perf problems we have with > proxy since the client would directly talk to the external process. This should > also provide better perf than the local backend since it wouldn't have to do do > the "at*()" > dance thanks to chroot(). > > > > > > > > > Ouch... > > > > > > > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub file > > > > > > > > > or directory in 9P > > > > > > > > > > > > > > mount > > > > > > > > > > > > > > > directory. > > > > > > > > > > > > > > > > > This commit use merge_fs_path() to build up full > > > > > > > > > filename by string > > > > > > > > > > > > > > concatenation. > > > > > > > > > > > > > > > > I know that may have a risk of security, but Windows > > > > > > > > > does fully support POSIX > > > > > > > > > > You will not find anybody merging code that's inherently insecure. > > > > > > > > > > > > > I understand from your various answers that symlinks > > > > > > > > aren't currently supported by window's POSIX API. Is this forever ? > > > > > > > > Google do mentions symlinks in windows 10. What's the > > > > > > > > story there ? How do they behave ? How would they be > > > > > > > > exposed to the client ? Be aware that, even if the client > > > > > > > > cannot create symlinks, an existing symlink could be used to escape > with rename(). > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > > > > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts > > > > > > > > should maybe use the mapped or mapped-file security modes > > > > > > > > since they emulate symlinks with a simple file hidden in > > > > > > > > the VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -- > > > > > > > > Greg > > > > > > > > > > > > > > Windows native API support symbolic link file start from > > > > > > > Windows > > > > > > > Vista: > > > > > > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/n > > > > > > > f-winbas > > > > > > > e-cr > > > > > > > ea > > > > > > > tes ymboliclinka > > > > > > > > > > > > > > I mean Windows POSIX APIs do not support symbolic link > > > > > > > (MinGW use > > > > > > > Win32 > > > > > > > POSIX APIs) So we can not create symbolic link by MinGW. > > > > > > > > > > A function with POSIX signature could be added to 9p-util-win.c > > > > > which would call the native Windows function to create symlinks. > > > > > > > > > > > > Anyway, there is another solution: re-work whole 9PFS code: > > > > > > > not only 9p-local.c, but also every file in 9p driver. > > > > > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, > > > > > > > write, close), by Windows Native APIs (e.g. open -> > > > > > > > CreateFile, lseek -> SetFilePointer, read -> ReadFile, write > > > > > > > -> WriteFile, close -> CloseHandle, etc.) Then 9P can use > > > > > > > Windows symbolic link feature. > > > > > > > However, I do think it is a good idea to replace everything. > > > > > > > > > > > > TYPO: it NOT is a good idea to replace everything. > > > > > > > > > > Right, that does not make sense. The way to go is adding and > > > > > implementing missing system functions with POSIX signatures and > > > > > POSIX behaviour for Windows. Not turning the entire code base > > > > > upside down. > > > > > > > > > > Best regards, > > > > > Christian Schoenebeck > > > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 15:35 ` Shi, Guohuai @ 2022-05-11 11:18 ` Christian Schoenebeck 2022-05-11 12:18 ` Greg Kurz 1 sibling, 0 replies; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-11 11:18 UTC (permalink / raw) To: qemu-devel, Meng, Bin; +Cc: Greg Kurz, Bin Meng, Shi, Guohuai On Dienstag, 10. Mai 2022 17:35:10 CEST Shi, Guohuai wrote: > Let's force on the security issue: > > Firstly, this answer ( > https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent-> of-openat ) is useless for QEMU. It uses Windows native API NtCreateFile() > and accesses files by Windows handle. But 9PFS is using Windows POSIX > interface, handle can not be used in POSIX interface. Actually, Windows > provide similar APIs like > GetFinalPathNameByHandle()/GetFileInformationByHandle(). It can also get > file information by Windows handle. I find "useless" quite exaggerated. You probably can't mix NT API calls with Mingw library calls, not sure, haven't checked the Mingw sources. If there is no way with Mingw to resolve NT handles, then it is still possible however to implement all the POSIX functions we need (using NT API exclusively) in 9p-util-win.c. Another option would be contributing the missing features to Mingw and in turn let QEMU's 9p feature depend on the appropriate minimum Mingw version. > Windows POSIX interface do not support NO_FOLLOW flags, that means, Windows > POSIX open() always translate symbolic link. > > So everything are finally point to one limitation: Windows POSIX interfaces > do not support symbolic link and always translate link. > > For the security reason, I think it is reasonable to disable symbolic link > support on Windows host for 9PFS. I can re-work this patch to adding a > symbolic link check during path-walk operation and stop it when get a > symbolic link. It is OK to drop support for native symlinks on Windows. Most people use security_model=mapped anyway where symlinks are emulated, so symlinks would still work for guests, even if Windows host would not support native symlinks. However insecure code is still a no go. So the issues identified so far still need to be resolved. And patches must be presented in a way that would allow them being reviewed. In their current form they are not. Best regards, Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-10 15:35 ` Shi, Guohuai 2022-05-11 11:18 ` Christian Schoenebeck @ 2022-05-11 12:18 ` Greg Kurz 2022-05-11 15:57 ` Shi, Guohuai 1 sibling, 1 reply; 34+ messages in thread From: Greg Kurz @ 2022-05-11 12:18 UTC (permalink / raw) To: Shi, Guohuai; +Cc: Christian Schoenebeck, qemu-devel, Meng, Bin, Bin Meng On Tue, 10 May 2022 15:35:10 +0000 "Shi, Guohuai" <Guohuai.Shi@windriver.com> wrote: > Let's force on the security issue: > Please don't top post, especially when all previous comments were made inline, so that someone who steps in this thread now has a chance to catch-up. > Firstly, this answer ( https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent-of-openat ) is useless for QEMU. > It uses Windows native API NtCreateFile() and accesses files by Windows handle. > But 9PFS is using Windows POSIX interface, handle can not be used in POSIX interface. > Actually, Windows provide similar APIs like GetFinalPathNameByHandle()/GetFileInformationByHandle(). > It can also get file information by Windows handle. > > Windows POSIX interface do not support NO_FOLLOW flags, that means, Windows POSIX open() always translate symbolic link. > This precludes any tentative to fix the issue at the QEMU level then. Maybe there are some knobs to control symlink behavior at the fs level but this certainly requires windows knowledge that I don't have. > So everything are finally point to one limitation: Windows POSIX interfaces do not support symbolic link and always translate link. > > For the security reason, I think it is reasonable to disable symbolic link support on Windows host for 9PFS. > I can re-work this patch to adding a symbolic link check during path-walk operation and stop it when get a symbolic link. > This would be useless because of TOCTOU : a directory could be replaced by a symlink between the check and the actual use of the file. O_NOFOLLOW provides the atomicity needed to safely error out on symlinks. Since O_NOFOLLOW only makes sense for the rightmost path element, paths from the client have to be broken down into a succession of *at() syscalls, one for each element. > Best Regards, > Guohuai > > > -----Original Message----- > > From: Greg Kurz <groug@kaod.org> > > Sent: 2022年5月10日 22:35 > > To: Christian Schoenebeck <qemu_oss@crudebyte.com> > > Cc: qemu-devel@nongnu.org; Meng, Bin <Bin.Meng@windriver.com>; Bin Meng > > <bmeng.cn@gmail.com>; Shi, Guohuai <Guohuai.Shi@windriver.com> > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > > Windows > > > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > > > On Tue, 10 May 2022 16:04:28 +0200 > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > > > On Dienstag, 10. Mai 2022 15:40:06 CEST Greg Kurz wrote: > > > > On Tue, 10 May 2022 13:54:46 +0200 > > > > > > > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > > > On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > > > > > > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > > > > > > [...] > > > > > > > > > > > > > > > > > I tend to agree with Christian's remarks that this > > > > > > > > > > > patch is too big and that the choice of introducing > > > > > > > > > > > right away a new implementation of 9p-local for > > > > > > > > > > > windows hosts is too bold to start with. We need to > > > > > > > > > > > clearly understand what's diverging between windows > > > > > > > > > > > and linux in order to make such a decision. You should > > > > > > > > > > > first try to introduce the required abstractions to > > > > > > > > > > > cope with these differences, so that we can review. > > > > > > > > > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() for a > > > > > > > > > > directory. > > > > > > > > > > Windows (actually MinGW library) only allows opendir() > > > > > > > > > > for a directory. > > > > > > > > > > > > That missing behaviour could be implemented in 9p-util-win.c, > > > > > > similar to the missing behaviours of mknodat() for macOS which > > > > > > did not support a bunch of things like creating a UNIX socket file and > > more: > > > > > > > > > > > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9de07f > > > > > > 7628643d > > > > > > 3d8d> > > > > > > > > > > Does MinGW have dirfd() ? > > > > > > > > > > > > > > > > No. > > > > > > > > MinGW does not open any directory. > > > > > > > > Here is opendir() source code of MinGW: > > > > > > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w64-cr > > > > > > > > t/misc/d > > > > > > > > iren > > > > > > > > t. > > > > > > > > c#L42 > > > > > > > > > > > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > > > > > > > > > Windows does not support APIs like "*at" (openat(), > > > > > > > > > > renameat(), > > > > > > > > > > etc.) > > > > > > > > > > > > Like already suggested before on your previous RFC version, it > > > > > > is possible to use the same workaround as we are using for macOS > > > > > > hosts already (which > > > > > > > > > > > > was missing mknodat()): > > > > > > pthread_fchdir_np(...) > > > > > > mknod(...) > > > > > > > > > > > > > > > > > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin. > > > > > > c#L84 > > > > > > > > > > > > So on Windows it would be viable to: > > > > > > chdir(...) > > > > > > open(...) > > > > > > > > > > > > The same approach could be used for any missing *at() function > > > > > > for Windows. > > > > > > > > > > Problem though is that the chdir() functions on Windows all seem > > > > > to have process-wide effect, we would need to change the current > > > > > directory only for the current thread, because filesystem access > > > > > of 9p server is multi-threaded. > > > > > > > > > > Protecting the chdir(); foo(); calls by a process wide global > > > > > mutex isn't very appealing either. :/ > > > > > > > > And it wouldn't be safe anyway because I'm pretty sure that the rest > > > > of the QEMU code assumes that the current directory is invariant, e.g. > > > > user could be very confused by 'drive_add file=./foo.img' not working. > > > > > > > > BTW duckduckgo gives: > > > > > > > > https://stackoverflow.com/questions/32138524/is-there-a-windows-equi > > > > valent-o > > > > f-openat > > > > > > > > So yes it seems to be technically possible to implement *at() > > > > functions on windows. This is the only way to avoid CVE-2016-9602 in > > > > the QEMU process. > > > > > > +1 > > > > > > > Another option is to use the proxy backend : this offloads all fs visit > > > > accesses to an external process running virtfs-proxy-helper, that > > > > runs privileged and chroot() into the shared directory so that it > > > > can safely use path based syscalls. > > > > > > As a very last resort, maybe. But just for the other two guys to know upfront: > > > the proxy backend is very slow and not in good shape. There were plans > > > to deprecate the proxy backend therefore, as it's more or less dead. > > > > > > > Yeah as mentioned before, the way to go now would be to come with a vhost-user > > implementation like virtiofsd. This would address all perf problems we have with > > proxy since the client would directly talk to the external process. This should > > also provide better perf than the local backend since it wouldn't have to do do > > the "at*()" > > dance thanks to chroot(). > > > > > > > > > > > Ouch... > > > > > > > > > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub file > > > > > > > > > > or directory in 9P > > > > > > > > > > > > > > > > mount > > > > > > > > > > > > > > > > > directory. > > > > > > > > > > > > > > > > > > > This commit use merge_fs_path() to build up full visit > > > > > > > > > > filename by string > > > > > > > > > > > > > > > > concatenation. > > > > > > > > > > > > > > > > > > I know that may have a risk of security, but Windows > > > > > > > > > > does fully support POSIX > > > > > > > > > > > > You will not find anybody merging code that's inherently insecure. > > > > > > > > > > > > > > > I understand from your various answers that symlinks > > > > > > > > > aren't currently supported by window's POSIX API. Is this forever ? > > > > > > > > > Google do mentions symlinks in windows 10. What's the > > > > > > > > > story there ? How do they behave ? How would they be > > > > > > > > > exposed to the client ? Be aware that, even if the client > > > > > > > > > cannot create symlinks, an existing symlink could be used to escape > > with rename(). > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must be > > > > > > > > > fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts > > > > > > > > > should maybe use the mapped or mapped-file security modes visit visit > > > > > > > > > since they emulate symlinks with a simple file hidden in > > > > > > > > > the VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -- > > > > > > > > > Greg > > > > > > > > > > > > > > > > Windows native API support symbolic link file start from > > > > > > > > Windows > > > > > > > > Vista: > > > > > > > > https://docs.microsoft.com/en-us/windows/win32/api/winbase/n > > > > > > > > f-winbas > > > > > > > > e-cr > > > > > > > > ea > > > > > > > > tes ymboliclinka > > > > > > > > > > > > > > > > I mean Windows POSIX APIs do not support symbolic link > > > > > > > > (MinGW use > > > > > > > > Win32 > > > > > > > > POSIX APIs) So we can not create symbolic link by MinGW. > > > > > > > > > > > > A function with POSIX signature could be added to 9p-util-win.c > > > > > > which would call the native Windows function to create symlinks. > > > > > > > > > > > > > > Anyway, there is another solution: re-work whole 9PFS code: > > > > > > > > not only 9p-local.c, but also every file in 9p driver. > > > > > > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, > > > > > > > > write, close), by Windows Native APIs (e.g. open -> > > > > > > > > CreateFile, lseek -> SetFilePointer, read -> ReadFile, write > > > > > > > > -> WriteFile, close -> CloseHandle, etc.) Then 9P can use > > > > > > > > Windows symbolic link feature. > > > > > > > > However, I do think it is a good idea to replace everything. > > > > > > > > > > > > > > TYPO: it NOT is a good idea to replace everything. > > > > > > > > > > > > Right, that does not make sense. The way to go is adding and > > > > > > implementing missing system functions with POSIX signatures and > > > > > > POSIX behaviour for Windows. Not turning the entire code base > > > > > > upside down. > > > > > > > > > > > > Best regards, > > > > > > Christian Schoenebeck > > > > > > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* RE: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-11 12:18 ` Greg Kurz @ 2022-05-11 15:57 ` Shi, Guohuai 2022-05-24 12:23 ` Christian Schoenebeck 0 siblings, 1 reply; 34+ messages in thread From: Shi, Guohuai @ 2022-05-11 15:57 UTC (permalink / raw) To: Greg Kurz; +Cc: Christian Schoenebeck, qemu-devel, Meng, Bin, Bin Meng > -----Original Message----- > From: Greg Kurz <groug@kaod.org> > Sent: 2022年5月11日 20:19 > To: Shi, Guohuai <Guohuai.Shi@windriver.com> > Cc: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org; Meng, > Bin <Bin.Meng@windriver.com>; Bin Meng <bmeng.cn@gmail.com> > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for > Windows > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > On Tue, 10 May 2022 15:35:10 +0000 > "Shi, Guohuai" <Guohuai.Shi@windriver.com> wrote: > > > Let's force on the security issue: > > > > Please don't top post, especially when all previous comments were made inline, > so that someone who steps in this thread now has a chance to catch-up. > > > Firstly, this answer > ( https://stackoverflow.com/questions/32138524/is-there-a-windows-equivalent- > of-openat ) is useless for QEMU. > > It uses Windows native API NtCreateFile() and accesses files by Windows handle. > > But 9PFS is using Windows POSIX interface, handle can not be used in POSIX > interface. > > Actually, Windows provide similar APIs like > GetFinalPathNameByHandle()/GetFileInformationByHandle(). > > It can also get file information by Windows handle. > > > > Windows POSIX interface do not support NO_FOLLOW flags, that means, Windows POSIX > open() always translate symbolic link. > > > > This precludes any tentative to fix the issue at the QEMU level then. > Maybe there are some knobs to control symlink behavior at the fs level but this > certainly requires windows knowledge that I don't have. > > > So everything are finally point to one limitation: Windows POSIX interfaces do > not support symbolic link and always translate link. > > > > For the security reason, I think it is reasonable to disable symbolic link support > on Windows host for 9PFS. > > I can re-work this patch to adding a symbolic link check during path-walk operation > and stop it when get a symbolic link. > > > > This would be useless because of TOCTOU : a directory could be replaced by a symlink > between the check and the actual use of the file. O_NOFOLLOW provides the atomicity > needed to safely error out on symlinks. Since O_NOFOLLOW only makes sense for the > rightmost path element, paths from the client have to be broken down into a > succession of *at() syscalls, one for each element. For Windows file system, it would be OK. Windows can not delete a opening file (this is different behavior between Windows file system driver and UNIX-like-inode-based file system). So when 9PFS try to open the final file, the following steps will keep it safe: 1. open the final file by Windows NT APIs and keep the open handle. 2. open the final file by MinGW open(). 3. close NT handle. Windows file system does not allow delete/rename/move a opening file. Even Windows provide "FILE_SHARE_DELETE" flag in its NT API CreateFile(). Windows allow to delete the opening file, but can not re-create same name. The following steps will be failure on Windows: 1. Open a directory by CreateFile() with "FILE_SHARE_DELETE" flag and keep the handle open. 2. Remove the directory. 3. Re-create same name directory/file/links. Windows will get failure on step #3. So I think checking if there is a link in filename would be safety on Window host. Best Regards, Guohuai > > > Best Regards, > > Guohuai > > > > > -----Original Message----- > > > From: Greg Kurz <groug@kaod.org> > > > Sent: 2022年5月10日 22:35 > > > To: Christian Schoenebeck <qemu_oss@crudebyte.com> > > > Cc: qemu-devel@nongnu.org; Meng, Bin <Bin.Meng@windriver.com>; Bin > > > Meng <bmeng.cn@gmail.com>; Shi, Guohuai <Guohuai.Shi@windriver.com> > > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend > > > driver for Windows > > > > > > [Please note: This e-mail is from an EXTERNAL e-mail address] > > > > > > On Tue, 10 May 2022 16:04:28 +0200 > > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > > > > > On Dienstag, 10. Mai 2022 15:40:06 CEST Greg Kurz wrote: > > > > > On Tue, 10 May 2022 13:54:46 +0200 > > > > > > > > > > Christian Schoenebeck <qemu_oss@crudebyte.com> wrote: > > > > > > On Dienstag, 10. Mai 2022 12:18:33 CEST Christian Schoenebeck wrote: > > > > > > > On Dienstag, 10. Mai 2022 04:17:44 CEST Shi, Guohuai wrote: > > > > > > > [...] > > > > > > > > > > > > > > > > > > > I tend to agree with Christian's remarks that this > > > > > > > > > > > > patch is too big and that the choice of > > > > > > > > > > > > introducing right away a new implementation of > > > > > > > > > > > > 9p-local for windows hosts is too bold to start > > > > > > > > > > > > with. We need to clearly understand what's > > > > > > > > > > > > diverging between windows and linux in order to > > > > > > > > > > > > make such a decision. You should first try to > > > > > > > > > > > > introduce the required abstractions to cope with these differences, > so that we can review. > > > > > > > > > > > > > > > > > > > > > > Here is the basic introductions of 9PFS for Windows development: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Windows always returns -1 when try to call open() > > > > > > > > > > > for a directory. > > > > > > > > > > > Windows (actually MinGW library) only allows > > > > > > > > > > > opendir() for a directory. > > > > > > > > > > > > > > That missing behaviour could be implemented in > > > > > > > 9p-util-win.c, similar to the missing behaviours of > > > > > > > mknodat() for macOS which did not support a bunch of things > > > > > > > like creating a UNIX socket file and > > > more: > > > > > > > > > > > > > > https://github.com/qemu/qemu/commit/055ab89327bab83f1bd07e9d > > > > > > > e07f > > > > > > > 7628643d > > > > > > > 3d8d> > > > > > > > > > > > Does MinGW have dirfd() ? > > > > > > > > > > > > > > > > > > No. > > > > > > > > > MinGW does not open any directory. > > > > > > > > > Here is opendir() source code of MinGW: > > > > > > > > > https://github.com/mirror/mingw-w64/blob/master/mingw-w6 > > > > > > > > > 4-cr > > > > > > > > > t/misc/d > > > > > > > > > iren > > > > > > > > > t. > > > > > > > > > c#L42 > > > > > > > > > > > > > > > > > > So MinGW do not have a fd associated to a directory. > > > > > > > > > > > > > > > > > > > > Windows does not support APIs like "*at" (openat(), > > > > > > > > > > > renameat(), > > > > > > > > > > > etc.) > > > > > > > > > > > > > > Like already suggested before on your previous RFC version, > > > > > > > it is possible to use the same workaround as we are using > > > > > > > for macOS hosts already (which > > > > > > > > > > > > > > was missing mknodat()): > > > > > > > pthread_fchdir_np(...) > > > > > > > mknod(...) > > > > > > > > > > > > > > > > > > > > > https://github.com/qemu/qemu/blob/master/hw/9pfs/9p-util-darwin. > > > > > > > c#L84 > > > > > > > > > > > > > > So on Windows it would be viable to: > > > > > > > chdir(...) > > > > > > > open(...) > > > > > > > > > > > > > > The same approach could be used for any missing *at() > > > > > > > function for Windows. > > > > > > > > > > > > Problem though is that the chdir() functions on Windows all > > > > > > seem to have process-wide effect, we would need to change the > > > > > > current directory only for the current thread, because > > > > > > filesystem access of 9p server is multi-threaded. > > > > > > > > > > > > Protecting the chdir(); foo(); calls by a process wide global > > > > > > mutex isn't very appealing either. :/ > > > > > > > > > > And it wouldn't be safe anyway because I'm pretty sure that the > > > > > rest of the QEMU code assumes that the current directory is invariant, > e.g. > > > > > user could be very confused by 'drive_add file=./foo.img' not working. > > > > > > > > > > BTW duckduckgo gives: > > > > > > > > > > https://stackoverflow.com/questions/32138524/is-there-a-windows- > > > > > equi > > > > > valent-o > > > > > f-openat > > > > > > > > > > So yes it seems to be technically possible to implement *at() > > > > > functions on windows. This is the only way to avoid > > > > > CVE-2016-9602 in the QEMU process. > > > > > > > > +1 > > > > > > > > > Another option is to use the proxy backend : this offloads all > > > > > fs visit accesses to an external process running > > > > > virtfs-proxy-helper, that runs privileged and chroot() into the > > > > > shared directory so that it can safely use path based syscalls. > > > > > > > > As a very last resort, maybe. But just for the other two guys to know upfront: > > > > the proxy backend is very slow and not in good shape. There were > > > > plans to deprecate the proxy backend therefore, as it's more or less dead. > > > > > > > > > > Yeah as mentioned before, the way to go now would be to come with a > > > vhost-user implementation like virtiofsd. This would address all > > > perf problems we have with proxy since the client would directly > > > talk to the external process. This should also provide better perf > > > than the local backend since it wouldn't have to do do the "at*()" > > > dance thanks to chroot(). > > > > > > > > > > > > > Ouch... > > > > > > > > > > > > > > > > > > > > > So 9PFS can not use any openat() for opening a sub > > > > > > > > > > > file or directory in 9P > > > > > > > > > > > > > > > > > > mount > > > > > > > > > > > > > > > > > > > directory. > > > > > > > > > > > > > > > > > > > > > This commit use merge_fs_path() to build up full > > > > > > > > > > > visit filename by string > > > > > > > > > > > > > > > > > > concatenation. > > > > > > > > > > > > > > > > > > > > I know that may have a risk of security, but Windows > > > > > > > > > > > does fully support POSIX > > > > > > > > > > > > > > You will not find anybody merging code that's inherently insecure. > > > > > > > > > > > > > > > > > I understand from your various answers that symlinks > > > > > > > > > > aren't currently supported by window's POSIX API. Is this forever ? > > > > > > > > > > Google do mentions symlinks in windows 10. What's the > > > > > > > > > > story there ? How do they behave ? How would they be > > > > > > > > > > exposed to the client ? Be aware that, even if the > > > > > > > > > > client cannot create symlinks, an existing symlink > > > > > > > > > > could be used to escape > > > with rename(). > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > If the code "may have a risk of security" then it must > > > > > > > > > > be fixed or avoided in some way before being merged upstream. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Other thing that comes to mind is that windows hosts > > > > > > > > > > should maybe use the mapped or mapped-file security > > > > > > > > > > modes visit visit since they emulate symlinks with a > > > > > > > > > > simple file hidden in the VIRTFS_META_DIR directory. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Cheers, > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -- > > > > > > > > > > Greg > > > > > > > > > > > > > > > > > > Windows native API support symbolic link file start from > > > > > > > > > Windows > > > > > > > > > Vista: > > > > > > > > > https://docs.microsoft.com/en-us/windows/win32/api/winba > > > > > > > > > se/n > > > > > > > > > f-winbas > > > > > > > > > e-cr > > > > > > > > > ea > > > > > > > > > tes ymboliclinka > > > > > > > > > > > > > > > > > > I mean Windows POSIX APIs do not support symbolic link > > > > > > > > > (MinGW use > > > > > > > > > Win32 > > > > > > > > > POSIX APIs) So we can not create symbolic link by MinGW. > > > > > > > > > > > > > > A function with POSIX signature could be added to > > > > > > > 9p-util-win.c which would call the native Windows function to create > symlinks. > > > > > > > > > > > > > > > > Anyway, there is another solution: re-work whole 9PFS code: > > > > > > > > > not only 9p-local.c, but also every file in 9p driver. > > > > > > > > > Replace every MinGW/POSIX APIs (e.g. open, lseek, read, > > > > > > > > > write, close), by Windows Native APIs (e.g. open -> > > > > > > > > > CreateFile, lseek -> SetFilePointer, read -> ReadFile, > > > > > > > > > write > > > > > > > > > -> WriteFile, close -> CloseHandle, etc.) Then 9P can > > > > > > > > > -> use > > > > > > > > > Windows symbolic link feature. > > > > > > > > > However, I do think it is a good idea to replace everything. > > > > > > > > > > > > > > > > TYPO: it NOT is a good idea to replace everything. > > > > > > > > > > > > > > Right, that does not make sense. The way to go is adding and > > > > > > > implementing missing system functions with POSIX signatures > > > > > > > and POSIX behaviour for Windows. Not turning the entire code > > > > > > > base upside down. > > > > > > > > > > > > > > Best regards, > > > > > > > Christian Schoenebeck > > > > > > > > > > ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows 2022-05-11 15:57 ` Shi, Guohuai @ 2022-05-24 12:23 ` Christian Schoenebeck 0 siblings, 0 replies; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-24 12:23 UTC (permalink / raw) To: Meng, Bin; +Cc: Greg Kurz, qemu-devel, Bin Meng, Shi, Guohuai On Mittwoch, 11. Mai 2022 17:57:08 CEST Shi, Guohuai wrote: > > -----Original Message----- > > From: Greg Kurz <groug@kaod.org> > > Sent: 2022年5月11日 20:19 > > To: Shi, Guohuai <Guohuai.Shi@windriver.com> > > Cc: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org; > > Meng, Bin <Bin.Meng@windriver.com>; Bin Meng <bmeng.cn@gmail.com> > > Subject: Re: [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver > > for Windows [...] > > This would be useless because of TOCTOU : a directory could be replaced by > > a symlink between the check and the actual use of the file. O_NOFOLLOW > > provides the atomicity needed to safely error out on symlinks. Since > > O_NOFOLLOW only makes sense for the rightmost path element, paths from > > the client have to be broken down into a succession of *at() syscalls, > > one for each element. > > > For Windows file system, it would be OK. > Windows can not delete a opening file (this is different behavior between > Windows file system driver and UNIX-like-inode-based file system). So when > 9PFS try to open the final file, the following steps will keep it safe: > 1. open the final file by Windows NT APIs and keep the open handle. > 2. open the final file by MinGW open(). > 3. close NT handle. > > Windows file system does not allow delete/rename/move a opening file. > Even Windows provide "FILE_SHARE_DELETE" flag in its NT API CreateFile(). > Windows allow to delete the opening file, but can not re-create same name. > The following steps will be failure on Windows: > > 1. Open a directory by CreateFile() with "FILE_SHARE_DELETE" flag and keep > the handle open. > 2. Remove the directory. > 3. Re-create same name directory/file/links. > > Windows will get failure on step #3. > > So I think checking if there is a link in filename would be safety on Window > host. Neither Greg nor me are working much with Windows. As this was a fundamental security issue though, one way to bring this issue forward would be to backup your claims with test case(s). Then we would also have a safety net e.g. via CI cloud alerts in case behaviour on Windows changes one day. Best regards, Christian Schoenebeck ^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH 6/9] hw/9pfs: Update 9p-synth.c for Windows build 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (4 preceding siblings ...) 2022-04-25 14:27 ` [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows Bin Meng @ 2022-04-25 14:27 ` Bin Meng 2022-04-25 14:27 ` [PATCH 7/9] fsdev: Enable 'local' file system driver backend for Windows Bin Meng ` (3 subsequent siblings) 9 siblings, 0 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:27 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> This updates 9p synth backend driver for Windows build. Funtionalities are not tested as test cases do not run on Windows at present. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- hw/9pfs/9p-synth.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index d99d263985..ce28943b2d 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -152,8 +152,10 @@ static void synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf) stbuf->st_gid = 0; stbuf->st_rdev = 0; stbuf->st_size = 0; +#ifndef CONFIG_WIN32 stbuf->st_blksize = 0; stbuf->st_blocks = 0; +#endif stbuf->st_atime = 0; stbuf->st_mtime = 0; stbuf->st_ctime = 0; @@ -236,7 +238,8 @@ static void synth_direntry(V9fsSynthNode *node, entry->d_ino = node->attr->inode; #ifdef CONFIG_DARWIN entry->d_seekoff = off + 1; -#else +#endif +#ifdef CONFIG_LINUX entry->d_off = off + 1; #endif } -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 7/9] fsdev: Enable 'local' file system driver backend for Windows 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (5 preceding siblings ...) 2022-04-25 14:27 ` [PATCH 6/9] hw/9pfs: Update 9p-synth.c for Windows build Bin Meng @ 2022-04-25 14:27 ` Bin Meng 2022-04-25 14:27 ` [PATCH 8/9] meson.build: Turn on virtfs for Windows host Bin Meng ` (2 subsequent siblings) 9 siblings, 0 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:27 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> Only 'local' file system driver will be supported for Windows host. 'proxy' or 'synth' are not supported. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- fsdev/qemu-fsdev.c | 2 ++ fsdev/meson.build | 1 + 2 files changed, 3 insertions(+) diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 3da64e9f72..e1cc677ad8 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -81,6 +81,7 @@ static FsDriverTable FsDrivers[] = { NULL }, }, +#ifndef CONFIG_WIN32 { .name = "synth", .ops = &synth_ops, @@ -100,6 +101,7 @@ static FsDriverTable FsDrivers[] = { NULL }, }, +#endif }; static int validate_opt(void *opaque, const char *name, const char *value, diff --git a/fsdev/meson.build b/fsdev/meson.build index b632b66348..2aad081aef 100644 --- a/fsdev/meson.build +++ b/fsdev/meson.build @@ -8,6 +8,7 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files( ), if_false: files('qemu-fsdev-dummy.c')) softmmu_ss.add_all(when: 'CONFIG_LINUX', if_true: fsdev_ss) softmmu_ss.add_all(when: 'CONFIG_DARWIN', if_true: fsdev_ss) +softmmu_ss.add_all(when: 'CONFIG_WIN32', if_true: fsdev_ss) if have_virtfs_proxy_helper executable('virtfs-proxy-helper', -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 8/9] meson.build: Turn on virtfs for Windows host 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (6 preceding siblings ...) 2022-04-25 14:27 ` [PATCH 7/9] fsdev: Enable 'local' file system driver backend for Windows Bin Meng @ 2022-04-25 14:27 ` Bin Meng 2022-04-25 14:27 ` [PATCH 9/9] hw/9p: win32: Translate Windows error number to Linux value Bin Meng 2022-04-26 1:41 ` [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng 9 siblings, 0 replies; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:27 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> Enable virtfs configuration option for Windows host. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- meson.build | 10 +++++----- hw/9pfs/meson.build | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index d083c6b7bf..46f61b2a9d 100644 --- a/meson.build +++ b/meson.build @@ -1465,16 +1465,16 @@ dbus_display = get_option('dbus_display') \ .allowed() have_virtfs = get_option('virtfs') \ - .require(targetos == 'linux' or targetos == 'darwin', - error_message: 'virtio-9p (virtfs) requires Linux or macOS') \ - .require(targetos == 'linux' or cc.has_function('pthread_fchdir_np'), + .require(targetos == 'linux' or targetos == 'darwin' or targetos == 'windows', + error_message: 'virtio-9p (virtfs) requires Linux or macOS or Windows') \ + .require(targetos == 'linux' or targetos == 'windows' or cc.has_function('pthread_fchdir_np'), error_message: 'virtio-9p (virtfs) on macOS requires the presence of pthread_fchdir_np') \ - .require(targetos == 'darwin' or (libattr.found() and libcap_ng.found()), + .require(targetos == 'darwin' or targetos == 'windows' or (libattr.found() and libcap_ng.found()), error_message: 'virtio-9p (virtfs) on Linux requires libcap-ng-devel and libattr-devel') \ .disable_auto_if(not have_tools and not have_system) \ .allowed() -have_virtfs_proxy_helper = targetos != 'darwin' and have_virtfs and have_tools +have_virtfs_proxy_helper = targetos != 'darwin' and targetos != 'windows' and have_virtfs and have_tools foreach k : get_option('trace_backends') config_host_data.set('CONFIG_TRACE_' + k.to_upper(), true) diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build index b4a8ff0913..50401848d9 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -17,6 +17,8 @@ fs_ss.add(when: 'CONFIG_LINUX', if_true: files('9p-local.c', fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-local.c', '9p-proxy.c', '9p-util-darwin.c')) +fs_ss.add(when: 'CONFIG_WIN32', if_true: files('9p-local-win32.c', + '9p-util-win32.c')) fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.c')) softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss) -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* [PATCH 9/9] hw/9p: win32: Translate Windows error number to Linux value 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (7 preceding siblings ...) 2022-04-25 14:27 ` [PATCH 8/9] meson.build: Turn on virtfs for Windows host Bin Meng @ 2022-04-25 14:27 ` Bin Meng 2022-05-04 18:15 ` Christian Schoenebeck 2022-04-26 1:41 ` [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng 9 siblings, 1 reply; 34+ messages in thread From: Bin Meng @ 2022-04-25 14:27 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel; +Cc: Bin Meng, Guohuai Shi From: Guohuai Shi <guohuai.shi@windriver.com> Some of Windows error numbers have different value from Linux ones. For example, ENOTEMPTY is defined to 39 in Linux, but is defined to 41 in Windows. So deleting a directory from a Linux guest on top of QEMU from a Windows host complains: # rmdir tmp rmdir: 'tmp': Unknown error 41 This commit provides error number traslation from Windows to Linux. It can make Linux guest OS happy with the error number when running on top of QEMU from a Windows host. This has a side effet that it requires all guest OSes' 9pfs drivers to use the same errno. It looks like macOS has different errno too so using 9p in a Linux on top of QEMU from a macOS host may also fail in the above case. I suspect we only tested 9p from a macOS guest on top of QEMU from a macOS host, so this issue was not exposed. I am not aware of Windows's native support for 9pfs so I think using the Linux errnor as the standard is probably okay, but I am open for suggestions. Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> --- hw/9pfs/9p.h | 4 ++++ hw/9pfs/9p-util-win32.c | 38 ++++++++++++++++++++++++++++++++++++++ hw/9pfs/9p.c | 7 +++++++ 3 files changed, 49 insertions(+) diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 87e8eac840..db2013d549 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -490,6 +490,10 @@ void pdu_free(V9fsPDU *pdu); void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr); void v9fs_reset(V9fsState *s); +#ifdef CONFIG_WIN32 +int errno_translate_win32(int errno_win32); +#endif + struct V9fsTransport { ssize_t (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap); diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c index d9b35e7425..c4f90c6503 100644 --- a/hw/9pfs/9p-util-win32.c +++ b/hw/9pfs/9p-util-win32.c @@ -20,6 +20,11 @@ #define V9FS_MAGIC 0x53465039 /* string "9PFS" */ #endif +struct translate_map { + int output; /* Linux error number */ + int input; /* Windows error number */ +}; + static int build_ads_name(char *namebuf, size_t namebuflen, const char *dirname, const char *filename, const char *ads_name) @@ -301,3 +306,36 @@ int qemu_statfs(const char *fs_root, struct statfs *stbuf) return 0; } + +int errno_translate_win32(int errno_win32) + { + unsigned int i; + + /* + * The translation table only contains values which could be returned + * as a result of a filesystem operation, i.e. network/socket related + * errno values need not be considered for translation. + */ + static struct translate_map errno_map[] = { + /* Linux errno Windows errno */ + { L_EDEADLK, EDEADLK }, + { L_ENAMETOOLONG, ENAMETOOLONG }, + { L_ENOLCK, ENOLCK }, + { L_ENOSYS, ENOSYS }, + { L_ENOTEMPTY, ENOTEMPTY }, + { L_EILSEQ, EILSEQ }, + { L_ELOOP, ELOOP }, + }; + + /* scan errno_win32 table for a matching Linux errno value */ + + for (i = 0; i < sizeof(errno_map) / sizeof(errno_map[0]); i++) { + if (errno_win32 == errno_map[i].input) { + return errno_map[i].output; + } + } + + /* no translation necessary */ + + return errno_win32; + } diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index a04889c1d6..0a9c0a509e 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1062,6 +1062,13 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) id = P9_RERROR; } +#ifdef CONFIG_WIN32 + /* + * Some Windows errnos have different value from Linux, + * and they need to be translated to the Linux value. + */ + err = errno_translate_win32(err); +#endif ret = pdu_marshal(pdu, len, "d", err); if (ret < 0) { goto out_notify; -- 2.25.1 ^ permalink raw reply related [flat|nested] 34+ messages in thread
* Re: [PATCH 9/9] hw/9p: win32: Translate Windows error number to Linux value 2022-04-25 14:27 ` [PATCH 9/9] hw/9p: win32: Translate Windows error number to Linux value Bin Meng @ 2022-05-04 18:15 ` Christian Schoenebeck 0 siblings, 0 replies; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-04 18:15 UTC (permalink / raw) To: qemu-devel; +Cc: Greg Kurz, Bin Meng, Guohuai Shi, Bin Meng On Montag, 25. April 2022 16:27:05 CEST Bin Meng wrote: > From: Guohuai Shi <guohuai.shi@windriver.com> > > Some of Windows error numbers have different value from Linux ones. > For example, ENOTEMPTY is defined to 39 in Linux, but is defined to > 41 in Windows. So deleting a directory from a Linux guest on top > of QEMU from a Windows host complains: > > # rmdir tmp > rmdir: 'tmp': Unknown error 41 > > This commit provides error number traslation from Windows to Linux. > It can make Linux guest OS happy with the error number when running > on top of QEMU from a Windows host. > > This has a side effet that it requires all guest OSes' 9pfs drivers > to use the same errno. > > It looks like macOS has different errno too so using 9p in a Linux > on top of QEMU from a macOS host may also fail in the above case. > I suspect we only tested 9p from a macOS guest on top of QEMU from > a macOS host, so this issue was not exposed. > > I am not aware of Windows's native support for 9pfs so I think using > the Linux errnor as the standard is probably okay, but I am open for > suggestions. > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com> > Signed-off-by: Bin Meng <bin.meng@windriver.com> > --- This patch collides with recent fixes for macOS hosts. Please rebase and use the already existing function errno_to_dotl(). > > hw/9pfs/9p.h | 4 ++++ > hw/9pfs/9p-util-win32.c | 38 ++++++++++++++++++++++++++++++++++++++ > hw/9pfs/9p.c | 7 +++++++ > 3 files changed, 49 insertions(+) > > diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h > index 87e8eac840..db2013d549 100644 > --- a/hw/9pfs/9p.h > +++ b/hw/9pfs/9p.h > @@ -490,6 +490,10 @@ void pdu_free(V9fsPDU *pdu); > void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr); > void v9fs_reset(V9fsState *s); > > +#ifdef CONFIG_WIN32 > +int errno_translate_win32(int errno_win32); > +#endif > + > struct V9fsTransport { > ssize_t (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, const char > *fmt, va_list ap); > diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c > index d9b35e7425..c4f90c6503 100644 > --- a/hw/9pfs/9p-util-win32.c > +++ b/hw/9pfs/9p-util-win32.c > @@ -20,6 +20,11 @@ > #define V9FS_MAGIC 0x53465039 /* string "9PFS" */ > #endif > > +struct translate_map { > + int output; /* Linux error number */ > + int input; /* Windows error number */ > +}; > + No need to define a structure for this. Your motivation was to define a sparse array. That safes you only a couple bytes and comes with runtime overhead OTOH. See below. > static int build_ads_name(char *namebuf, size_t namebuflen, > const char *dirname, const char *filename, > const char *ads_name) > @@ -301,3 +306,36 @@ int qemu_statfs(const char *fs_root, struct statfs > *stbuf) > > return 0; > } > + > +int errno_translate_win32(int errno_win32) > + { > + unsigned int i; > + > + /* > + * The translation table only contains values which could be returned > + * as a result of a filesystem operation, i.e. network/socket related > + * errno values need not be considered for translation. > + */ > + static struct translate_map errno_map[] = { > + /* Linux errno Windows errno */ > + { L_EDEADLK, EDEADLK }, > + { L_ENAMETOOLONG, ENAMETOOLONG }, > + { L_ENOLCK, ENOLCK }, > + { L_ENOSYS, ENOSYS }, > + { L_ENOTEMPTY, ENOTEMPTY }, > + { L_EILSEQ, EILSEQ }, > + { L_ELOOP, ELOOP }, > + }; So far we are using if .. else if ... blocks for macOS host errno translation, because I needed quick and small patches for qemu-stable for fixing macOS host support. So it is OK to use an array solution, but simply use a dense array and use GCC designated initializers ... > + > + /* scan errno_win32 table for a matching Linux errno value */ > + > + for (i = 0; i < sizeof(errno_map) / sizeof(errno_map[0]); i++) { > + if (errno_win32 == errno_map[i].input) { > + return errno_map[i].output; > + } > + } ... then this loop will become unnecessary, and you can just make a constant time array access with prior array range check. > + > + /* no translation necessary */ > + > + return errno_win32; > + } > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c > index a04889c1d6..0a9c0a509e 100644 > --- a/hw/9pfs/9p.c > +++ b/hw/9pfs/9p.c > @@ -1062,6 +1062,13 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, > ssize_t len) id = P9_RERROR; > } > > +#ifdef CONFIG_WIN32 > + /* > + * Some Windows errnos have different value from Linux, > + * and they need to be translated to the Linux value. > + */ > + err = errno_translate_win32(err); > +#endif This would translate it for all 9p protocol versions. We want translation only for 9p2000.L. Current git version does this already correctly. So after having rebased you should not need to change anything here. > ret = pdu_marshal(pdu, len, "d", err); > if (ret < 0) { > goto out_notify; ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 0/9] 9pfs: Add 9pfs support for Windows host 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng ` (8 preceding siblings ...) 2022-04-25 14:27 ` [PATCH 9/9] hw/9p: win32: Translate Windows error number to Linux value Bin Meng @ 2022-04-26 1:41 ` Bin Meng 2022-05-03 3:42 ` Bin Meng 9 siblings, 1 reply; 34+ messages in thread From: Bin Meng @ 2022-04-26 1:41 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel@nongnu.org Developers Cc: Mark Cave-Ayland, Guohuai Shi +Mark On Mon, Apr 25, 2022 at 10:27 PM Bin Meng <bmeng.cn@gmail.com> wrote: > > At present there is no Windows support for 9p file system. > This series adds initial Windows support for 9p file system. > > 'local' file system backend driver is supported on Windows, > including open, read, write, close, rename, remove, etc. > All security models are supported. The mapped (mapped-xattr) > security model is implemented using NTFS Alternate Data Stream > (ADS) so the 9p export path shall be on an NTFS partition. > > 'synth' backend driver is updated only to build sucessuflly > in a Windows build. Funtionalities are not tested as test > cases do not run on Windows at present. > > Example command line to test: > > "-fsdev local,path=c:\msys64,security_model=mapped,id=p9 -device virtio-9p-pci,fsdev=p9,mount_tag=p9fs" > > > Bin Meng (2): > hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS > qemu/xatth.h: Update for Windows build > > Guohuai Shi (7): > hw/9pfs: Extract common stuff to 9p-local.h > fsdev: Add missing definitions for Windows in file-op-9p.h > hw/9pfs: Add a 'local' file system backend driver for Windows > hw/9pfs: Update 9p-synth.c for Windows build > fsdev: Enable 'local' file system driver backend for Windows > meson.build: Turn on virtfs for Windows host > hw/9p: win32: Translate Windows error number to Linux value > > meson.build | 10 +- > fsdev/file-op-9p.h | 33 + > hw/9pfs/9p-linux-errno.h | 151 +++++ > hw/9pfs/9p-local.h | 20 + > hw/9pfs/9p-util.h | 41 ++ > hw/9pfs/9p.h | 27 + > include/qemu/xattr.h | 4 +- > fsdev/qemu-fsdev.c | 2 + > hw/9pfs/9p-local-win32.c | 1242 ++++++++++++++++++++++++++++++++++++++ > hw/9pfs/9p-local.c | 14 - > hw/9pfs/9p-synth.c | 5 +- > hw/9pfs/9p-util-win32.c | 341 +++++++++++ > hw/9pfs/9p-xattr.c | 113 ++++ > hw/9pfs/9p.c | 98 ++- > hw/9pfs/codir.c | 15 + > fsdev/meson.build | 1 + > hw/9pfs/meson.build | 12 +- > 17 files changed, 2103 insertions(+), 26 deletions(-) > create mode 100644 hw/9pfs/9p-linux-errno.h > create mode 100644 hw/9pfs/9p-local-win32.c > create mode 100644 hw/9pfs/9p-util-win32.c > > -- > 2.25.1 > ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 0/9] 9pfs: Add 9pfs support for Windows host 2022-04-26 1:41 ` [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng @ 2022-05-03 3:42 ` Bin Meng 2022-05-04 17:16 ` Christian Schoenebeck 0 siblings, 1 reply; 34+ messages in thread From: Bin Meng @ 2022-05-03 3:42 UTC (permalink / raw) To: Christian Schoenebeck, Greg Kurz, qemu-devel@nongnu.org Developers Cc: Guohuai Shi, Mark Cave-Ayland On Tue, Apr 26, 2022 at 9:41 AM Bin Meng <bmeng.cn@gmail.com> wrote: > > +Mark > > > On Mon, Apr 25, 2022 at 10:27 PM Bin Meng <bmeng.cn@gmail.com> wrote: > > > > At present there is no Windows support for 9p file system. > > This series adds initial Windows support for 9p file system. > > > > 'local' file system backend driver is supported on Windows, > > including open, read, write, close, rename, remove, etc. > > All security models are supported. The mapped (mapped-xattr) > > security model is implemented using NTFS Alternate Data Stream > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > 'synth' backend driver is updated only to build sucessuflly > > in a Windows build. Funtionalities are not tested as test > > cases do not run on Windows at present. > > > > Example command line to test: > > > > "-fsdev local,path=c:\msys64,security_model=mapped,id=p9 -device virtio-9p-pci,fsdev=p9,mount_tag=p9fs" > > Any comments to v1 before we send a v2 which will be rebased on master? Regards, Bin ^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH 0/9] 9pfs: Add 9pfs support for Windows host 2022-05-03 3:42 ` Bin Meng @ 2022-05-04 17:16 ` Christian Schoenebeck 0 siblings, 0 replies; 34+ messages in thread From: Christian Schoenebeck @ 2022-05-04 17:16 UTC (permalink / raw) To: qemu-devel; +Cc: Greg Kurz, Guohuai Shi, Mark Cave-Ayland, Bin Meng On Dienstag, 3. Mai 2022 05:42:03 CEST Bin Meng wrote: > On Tue, Apr 26, 2022 at 9:41 AM Bin Meng <bmeng.cn@gmail.com> wrote: > > +Mark > > > > On Mon, Apr 25, 2022 at 10:27 PM Bin Meng <bmeng.cn@gmail.com> wrote: > > > At present there is no Windows support for 9p file system. > > > This series adds initial Windows support for 9p file system. > > > > > > 'local' file system backend driver is supported on Windows, > > > including open, read, write, close, rename, remove, etc. > > > All security models are supported. The mapped (mapped-xattr) > > > security model is implemented using NTFS Alternate Data Stream > > > (ADS) so the 9p export path shall be on an NTFS partition. > > > > > > 'synth' backend driver is updated only to build sucessuflly > > > in a Windows build. Funtionalities are not tested as test > > > cases do not run on Windows at present. Like I mentioned in the previous RFC version; I don't really see a show stopper for the tests to run on Windows: https://lore.kernel.org/all/4649965.RNUEIdHhq1@silver/ > > > > > > Example command line to test: > > > "-fsdev local,path=c:\msys64,security_model=mapped,id=p9 -device > > > virtio-9p-pci,fsdev=p9,mount_tag=p9fs" > Any comments to v1 before we send a v2 which will be rebased on master? > > Regards, > Bin ^ permalink raw reply [flat|nested] 34+ messages in thread
end of thread, other threads:[~2022-05-24 12:39 UTC | newest] Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2022-04-25 14:26 [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng 2022-04-25 14:26 ` [PATCH 1/9] hw/9pfs: Compile 9p-local.c and 9p-proxy.c for Linux and macOS Bin Meng 2022-04-25 14:26 ` [PATCH 2/9] qemu/xatth.h: Update for Windows build Bin Meng 2022-04-25 14:26 ` [PATCH 3/9] hw/9pfs: Extract common stuff to 9p-local.h Bin Meng 2022-04-25 14:27 ` [PATCH 4/9] fsdev: Add missing definitions for Windows in file-op-9p.h Bin Meng 2022-05-04 17:35 ` Christian Schoenebeck 2022-04-25 14:27 ` [PATCH 5/9] hw/9pfs: Add a 'local' file system backend driver for Windows Bin Meng 2022-05-04 18:01 ` Christian Schoenebeck 2022-05-04 19:34 ` Shi, Guohuai 2022-05-05 11:43 ` Christian Schoenebeck 2022-05-06 6:46 ` Shi, Guohuai 2022-05-09 14:29 ` Greg Kurz 2022-05-09 15:09 ` Shi, Guohuai 2022-05-09 16:20 ` Greg Kurz 2022-05-10 2:13 ` Shi, Guohuai 2022-05-10 2:17 ` Shi, Guohuai 2022-05-10 10:18 ` Christian Schoenebeck 2022-05-10 11:54 ` Christian Schoenebeck 2022-05-10 13:40 ` Greg Kurz 2022-05-10 14:04 ` Christian Schoenebeck 2022-05-10 14:34 ` Greg Kurz 2022-05-10 15:35 ` Shi, Guohuai 2022-05-11 11:18 ` Christian Schoenebeck 2022-05-11 12:18 ` Greg Kurz 2022-05-11 15:57 ` Shi, Guohuai 2022-05-24 12:23 ` Christian Schoenebeck 2022-04-25 14:27 ` [PATCH 6/9] hw/9pfs: Update 9p-synth.c for Windows build Bin Meng 2022-04-25 14:27 ` [PATCH 7/9] fsdev: Enable 'local' file system driver backend for Windows Bin Meng 2022-04-25 14:27 ` [PATCH 8/9] meson.build: Turn on virtfs for Windows host Bin Meng 2022-04-25 14:27 ` [PATCH 9/9] hw/9p: win32: Translate Windows error number to Linux value Bin Meng 2022-05-04 18:15 ` Christian Schoenebeck 2022-04-26 1:41 ` [PATCH 0/9] 9pfs: Add 9pfs support for Windows host Bin Meng 2022-05-03 3:42 ` Bin Meng 2022-05-04 17:16 ` Christian Schoenebeck
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.