> -----Original Message----- > From: Greg Kurz <gr...@kaod.org> > Sent: 2022年5月10日 0:20 > To: Shi, Guohuai <guohuai....@windriver.com> > Cc: Bin Meng <bmeng...@gmail.com>; Christian Schoenebeck > <qemu_...@crudebyte.com>; > qemu-devel@nongnu.org; Meng, Bin <bin.m...@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....@windriver.com> wrote: > > > > > > > > -----Original Message----- > > > From: Greg Kurz <gr...@kaod.org> > > > Sent: 2022年5月9日 22:29 > > > To: Bin Meng <bmeng...@gmail.com> > > > Cc: Christian Schoenebeck <qemu_...@crudebyte.com>; qemu-devel@nongnu.org; > Shi, > > > Guohuai <guohuai....@windriver.com>; Meng, Bin <bin.m...@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...@gmail.com> wrote: > > > > > > > From: Guohuai Shi <guohuai....@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....@windriver.com> > > > > Signed-off-by: Bin Meng <bin.m...@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 */ > >