* [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows
@ 2022-10-24 4:57 Bin Meng
2022-10-24 4:57 ` [PATCH 01/16] qemu/xattr.h: Exclude <sys/xattr.h> " Bin Meng
` (16 more replies)
0 siblings, 17 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel
Cc: Christian Schoenebeck, Greg Kurz, Keno Fischer, Laurent Vivier,
Paolo Bonzini, Philippe Mathieu-Daudé,
Thomas Huth, Will Cohen
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' driver is adapted for Windows too so that we can now
run qtests on Windows for 9p related regression testing.
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 (5):
qemu/xattr.h: Exclude <sys/xattr.h> for Windows
hw/9pfs: Drop unnecessary *xattr wrapper API declarations
hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper
hw/9pfs: Introduce an opaque type 9P_FILE_ID
hw/9pfs: Update P9_FILE_ID to support Windows
Guohuai Shi (11):
hw/9pfs: Add missing definitions for Windows
hw/9pfs: Implement Windows specific utilities functions for 9pfs
hw/9pfs: Handle current directory offset for Windows
hw/9pfs: Disable unsupported flags and features for Windows
hw/9pfs: Update the local fs driver to support Windows
hw/9pfs: Add Linux error number definition
hw/9pfs: Translate Windows errno to Linux value
fsdev: Disable proxy fs driver on Windows
hw/9pfs: Update synth fs driver for Windows
tests/qtest: virtio-9p-test: Adapt the case for win32
meson.build: Turn on virtfs for Windows
meson.build | 10 +-
fsdev/file-op-9p.h | 33 ++
hw/9pfs/9p-file-id.h | 29 ++
hw/9pfs/9p-linux-errno.h | 151 ++++++
hw/9pfs/9p-local.h | 15 +-
hw/9pfs/9p-util.h | 158 +++++--
hw/9pfs/9p.h | 11 +
include/qemu/xattr.h | 4 +-
fsdev/qemu-fsdev.c | 2 +
hw/9pfs/9p-local.c | 532 ++++++++++++++++-----
hw/9pfs/9p-synth.c | 5 +-
hw/9pfs/9p-util-darwin.c | 14 +-
hw/9pfs/9p-util-linux.c | 14 +-
hw/9pfs/9p-util-win32.c | 885 +++++++++++++++++++++++++++++++++++
hw/9pfs/9p-xattr.c | 16 +-
hw/9pfs/9p.c | 106 ++++-
hw/9pfs/codir.c | 15 +
tests/qtest/virtio-9p-test.c | 7 +
fsdev/meson.build | 1 +
hw/9pfs/meson.build | 8 +-
20 files changed, 1822 insertions(+), 194 deletions(-)
create mode 100644 hw/9pfs/9p-file-id.h
create mode 100644 hw/9pfs/9p-linux-errno.h
create mode 100644 hw/9pfs/9p-util-win32.c
--
2.25.1
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 01/16] qemu/xattr.h: Exclude <sys/xattr.h> for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 02/16] hw/9pfs: Drop unnecessary *xattr wrapper API declarations Bin Meng
` (15 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel
Cc: Christian Schoenebeck, Keno Fischer, Philippe Mathieu-Daudé,
Will Cohen
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] 40+ messages in thread
* [PATCH 02/16] hw/9pfs: Drop unnecessary *xattr wrapper API declarations
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
2022-10-24 4:57 ` [PATCH 01/16] qemu/xattr.h: Exclude <sys/xattr.h> " Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 03/16] hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper Bin Meng
` (14 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Christian Schoenebeck, Greg Kurz
These are not used anywhere in the source tree. Drop them.
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-util.h | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index c3526144c9..ccfc8b1cb3 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -90,19 +90,8 @@ static inline int errno_to_dotl(int err) {
#ifdef CONFIG_DARWIN
#define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0)
-#define qemu_lgetxattr(...) getxattr(__VA_ARGS__, 0, XATTR_NOFOLLOW)
-#define qemu_llistxattr(...) listxattr(__VA_ARGS__, XATTR_NOFOLLOW)
-#define qemu_lremovexattr(...) removexattr(__VA_ARGS__, XATTR_NOFOLLOW)
-static inline int qemu_lsetxattr(const char *path, const char *name,
- const void *value, size_t size, int flags) {
- return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
-}
#else
#define qemu_fgetxattr fgetxattr
-#define qemu_lgetxattr lgetxattr
-#define qemu_llistxattr llistxattr
-#define qemu_lremovexattr lremovexattr
-#define qemu_lsetxattr lsetxattr
#endif
static inline void close_preserve_errno(int fd)
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 03/16] hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
2022-10-24 4:57 ` [PATCH 01/16] qemu/xattr.h: Exclude <sys/xattr.h> " Bin Meng
2022-10-24 4:57 ` [PATCH 02/16] hw/9pfs: Drop unnecessary *xattr wrapper API declarations Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 04/16] hw/9pfs: Introduce an opaque type 9P_FILE_ID Bin Meng
` (13 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Christian Schoenebeck, Greg Kurz
xxxat() APIs are only available on POSIX platforms. For future
extension to Windows, let's replace the direct call to xxxat()
APIs with a wrapper.
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-util.h | 15 +++++++++++----
hw/9pfs/9p-local.c | 32 ++++++++++++++++----------------
2 files changed, 27 insertions(+), 20 deletions(-)
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index ccfc8b1cb3..c314cf381d 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -94,6 +94,13 @@ static inline int errno_to_dotl(int err) {
#define qemu_fgetxattr fgetxattr
#endif
+#define qemu_openat openat
+#define qemu_fstatat fstatat
+#define qemu_mkdirat mkdirat
+#define qemu_renameat renameat
+#define qemu_utimensat utimensat
+#define qemu_unlinkat unlinkat
+
static inline void close_preserve_errno(int fd)
{
int serrno = errno;
@@ -103,8 +110,8 @@ static inline void close_preserve_errno(int fd)
static inline int openat_dir(int dirfd, const char *name)
{
- return openat(dirfd, name,
- O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL);
+ return qemu_openat(dirfd, name,
+ O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL);
}
static inline int openat_file(int dirfd, const char *name, int flags,
@@ -115,8 +122,8 @@ static inline int openat_file(int dirfd, const char *name, int flags,
#ifndef CONFIG_DARWIN
again:
#endif
- fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
- mode);
+ fd = qemu_openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
+ mode);
if (fd == -1) {
#ifndef CONFIG_DARWIN
if (errno == EPERM && (flags & O_NOATIME)) {
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index d42ce6d8b8..d2246a3d7e 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -103,14 +103,14 @@ static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
const char *npath)
{
int serrno = errno;
- renameat(odirfd, opath, ndirfd, npath);
+ qemu_renameat(odirfd, opath, ndirfd, npath);
errno = serrno;
}
static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
{
int serrno = errno;
- unlinkat(dirfd, path, flags);
+ qemu_unlinkat(dirfd, path, flags);
errno = serrno;
}
@@ -194,7 +194,7 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
goto out;
}
- err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
+ err = qemu_fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
if (err) {
goto err_out;
}
@@ -253,7 +253,7 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
}
}
} else {
- ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
+ ret = qemu_mkdirat(dirfd, VIRTFS_META_DIR, 0700);
if (ret < 0 && errno != EEXIST) {
return -1;
}
@@ -349,7 +349,7 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
*/
/* First, we clear non-racing symlinks out of the way. */
- if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) {
+ if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) {
return -1;
}
if (S_ISLNK(stbuf.st_mode)) {
@@ -734,7 +734,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
- err = mkdirat(dirfd, name, fs_ctx->dmode);
+ err = qemu_mkdirat(dirfd, name, fs_ctx->dmode);
if (err == -1) {
goto out;
}
@@ -750,7 +750,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
}
} else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
fs_ctx->export_flags & V9FS_SM_NONE) {
- err = mkdirat(dirfd, name, credp->fc_mode);
+ err = qemu_mkdirat(dirfd, name, credp->fc_mode);
if (err == -1) {
goto out;
}
@@ -990,7 +990,7 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
int omap_dirfd, nmap_dirfd;
- ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
+ ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
if (ret < 0 && errno != EEXIST) {
goto err_undo_link;
}
@@ -1085,7 +1085,7 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
goto out;
}
- ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
+ ret = qemu_utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
close_preserve_errno(dirfd);
out:
g_free(dirpath);
@@ -1116,7 +1116,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
if (fd == -1) {
return -1;
}
- ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
+ ret = qemu_unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
close_preserve_errno(fd);
if (ret < 0 && errno != ENOENT) {
return -1;
@@ -1124,7 +1124,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
}
map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
if (map_dirfd != -1) {
- ret = unlinkat(map_dirfd, name, 0);
+ ret = qemu_unlinkat(map_dirfd, name, 0);
close_preserve_errno(map_dirfd);
if (ret < 0 && errno != ENOENT) {
return -1;
@@ -1134,7 +1134,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
}
}
- return unlinkat(dirfd, name, flags);
+ return qemu_unlinkat(dirfd, name, flags);
}
static int local_remove(FsContext *ctx, const char *path)
@@ -1151,7 +1151,7 @@ static int local_remove(FsContext *ctx, const char *path)
goto out;
}
- if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (qemu_fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
goto err_out;
}
@@ -1296,7 +1296,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
return -1;
}
- ret = renameat(odirfd, old_name, ndirfd, new_name);
+ ret = qemu_renameat(odirfd, old_name, ndirfd, new_name);
if (ret < 0) {
goto out;
}
@@ -1304,7 +1304,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
int omap_dirfd, nmap_dirfd;
- ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
+ ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
if (ret < 0 && errno != EEXIST) {
goto err_undo_rename;
}
@@ -1321,7 +1321,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
}
/* rename the .virtfs_metadata files */
- ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
+ ret = qemu_renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
close_preserve_errno(nmap_dirfd);
close_preserve_errno(omap_dirfd);
if (ret < 0 && errno != ENOENT) {
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 04/16] hw/9pfs: Introduce an opaque type 9P_FILE_ID
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (2 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 03/16] hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-11-01 13:55 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 05/16] hw/9pfs: Update P9_FILE_ID to support Windows Bin Meng
` (12 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Christian Schoenebeck, Greg Kurz
Introduce an opaque type to represent a file in the 9pfs. This is
file descriptor on POSIX systems. In the upcoming patches, we can
extend it to support Windows.
With this new opaque type, it significantly reduces the number of
deviated code paths when adding Windows support.
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-file-id.h | 21 +++++
hw/9pfs/9p-local.h | 8 +-
hw/9pfs/9p-util.h | 28 ++++---
hw/9pfs/9p-local.c | 166 ++++++++++++++++++++-------------------
hw/9pfs/9p-util-darwin.c | 14 ++--
hw/9pfs/9p-util-linux.c | 14 ++--
hw/9pfs/9p-xattr.c | 16 ++--
7 files changed, 150 insertions(+), 117 deletions(-)
create mode 100644 hw/9pfs/9p-file-id.h
diff --git a/hw/9pfs/9p-file-id.h b/hw/9pfs/9p-file-id.h
new file mode 100644
index 0000000000..60cbfbf4dd
--- /dev/null
+++ b/hw/9pfs/9p-file-id.h
@@ -0,0 +1,21 @@
+/*
+ * 9p file representation for different hosts
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_9P_FILE_ID_H
+#define QEMU_9P_FILE_ID_H
+
+/*
+ * 9pfs file id
+ *
+ * This is file descriptor on POSIX platforms
+ */
+typedef int P9_FILE_ID;
+
+/* invalid value for P9_FILE_ID */
+#define P9_INVALID_FILE -1
+
+#endif
diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h
index 32c72749d9..c8404063e5 100644
--- a/hw/9pfs/9p-local.h
+++ b/hw/9pfs/9p-local.h
@@ -13,8 +13,10 @@
#ifndef QEMU_9P_LOCAL_H
#define QEMU_9P_LOCAL_H
-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);
+#include "9p-file-id.h"
+
+P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
+ mode_t mode);
+P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path);
#endif
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index c314cf381d..1e7dc76345 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -13,6 +13,8 @@
#ifndef QEMU_9P_UTIL_H
#define QEMU_9P_UTIL_H
+#include "9p-file-id.h"
+
#ifdef O_PATH
#define O_PATH_9P_UTIL O_PATH
#else
@@ -101,30 +103,31 @@ static inline int errno_to_dotl(int err) {
#define qemu_utimensat utimensat
#define qemu_unlinkat unlinkat
-static inline void close_preserve_errno(int fd)
+static inline void close_preserve_errno(P9_FILE_ID fd)
{
int serrno = errno;
close(fd);
errno = serrno;
}
-static inline int openat_dir(int dirfd, const char *name)
+static inline P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name)
{
return qemu_openat(dirfd, name,
O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL);
}
-static inline int openat_file(int dirfd, const char *name, int flags,
- mode_t mode)
+static inline P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name,
+ int flags, mode_t mode)
{
- int fd, serrno, ret;
+ int serrno, ret;
+ P9_FILE_ID fd;
#ifndef CONFIG_DARWIN
again:
#endif
fd = qemu_openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
mode);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
#ifndef CONFIG_DARWIN
if (errno == EPERM && (flags & O_NOATIME)) {
/*
@@ -155,13 +158,13 @@ again:
return fd;
}
-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,
+ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
+ const char *name, void *value, size_t size);
+int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *path, const char *name,
void *value, size_t size, int flags);
-ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
+ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
char *list, size_t size);
-ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
+ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
const char *name);
/*
@@ -219,6 +222,7 @@ 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
-int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev);
+int qemu_mknodat(P9_FILE_ID dirfd, const char *filename, mode_t mode,
+ dev_t dev);
#endif
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index d2246a3d7e..7e8d8492ea 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -54,18 +54,18 @@
#endif
typedef struct {
- int mountfd;
+ P9_FILE_ID mountfd;
} LocalData;
-int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
- mode_t mode)
+P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
+ mode_t mode)
{
LocalData *data = fs_ctx->private;
- int fd = data->mountfd;
+ P9_FILE_ID fd = data->mountfd;
- while (*path && fd != -1) {
+ while (*path && fd != P9_INVALID_FILE) {
const char *c;
- int next_fd;
+ P9_FILE_ID next_fd;
char *head;
/* Only relative paths without consecutive slashes */
@@ -94,20 +94,21 @@ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
return fd;
}
-int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
+P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path)
{
return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
}
-static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
- const char *npath)
+static void renameat_preserve_errno(P9_FILE_ID odirfd, const char *opath,
+ P9_FILE_ID ndirfd, const char *npath)
{
int serrno = errno;
qemu_renameat(odirfd, opath, ndirfd, npath);
errno = serrno;
}
-static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
+static void unlinkat_preserve_errno(P9_FILE_ID dirfd, const char *path,
+ int flags)
{
int serrno = errno;
qemu_unlinkat(dirfd, path, flags);
@@ -117,9 +118,10 @@ static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
#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)
+static FILE *local_fopenat(P9_FILE_ID dirfd, const char *name, const char *mode)
{
- int fd, o_mode = 0;
+ P9_FILE_ID fd;
+ int o_mode = 0;
FILE *fp;
int flags;
/*
@@ -134,7 +136,7 @@ static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
return NULL;
}
fd = openat_file(dirfd, name, flags, o_mode);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return NULL;
}
fp = fdopen(fd, mode);
@@ -145,16 +147,16 @@ static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
}
#define ATTR_MAX 100
-static void local_mapped_file_attr(int dirfd, const char *name,
+static void local_mapped_file_attr(P9_FILE_ID dirfd, const char *name,
struct stat *stbuf)
{
FILE *fp;
char buf[ATTR_MAX];
- int map_dirfd;
+ P9_FILE_ID map_dirfd;
if (strcmp(name, ".")) {
map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
- if (map_dirfd == -1) {
+ if (map_dirfd == P9_INVALID_FILE) {
return;
}
@@ -187,10 +189,10 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
int err = -1;
char *dirpath = g_path_get_dirname(fs_path->data);
char *name = g_path_get_basename(fs_path->data);
- int dirfd;
+ P9_FILE_ID dirfd;
dirfd = local_opendir_nofollow(fs_ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -233,14 +235,14 @@ out:
return err;
}
-static int local_set_mapped_file_attrat(int dirfd, const char *name,
+static int local_set_mapped_file_attrat(P9_FILE_ID dirfd, const char *name,
FsCred *credp)
{
FILE *fp;
int ret;
char buf[ATTR_MAX];
int uid = -1, gid = -1, mode = -1, rdev = -1;
- int map_dirfd = -1, map_fd;
+ P9_FILE_ID map_dirfd = P9_INVALID_FILE, map_fd;
bool is_root = !strcmp(name, ".");
if (is_root) {
@@ -259,7 +261,7 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
}
map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
- if (map_dirfd == -1) {
+ if (map_dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -296,7 +298,7 @@ update_map_file:
/* We can't go this far with map_dirfd not being a valid file descriptor
* but some versions of gcc aren't smart enough to see it.
*/
- if (map_dirfd != -1) {
+ if (map_dirfd != P9_INVALID_FILE) {
close_preserve_errno(map_dirfd);
}
}
@@ -305,7 +307,7 @@ update_map_file:
}
map_fd = fileno(fp);
- assert(map_fd != -1);
+ assert(map_fd != P9_INVALID_FILE);
ret = fchmod(map_fd, 0600);
assert(ret == 0);
@@ -339,10 +341,11 @@ update_map_file:
return 0;
}
-static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
+static int fchmodat_nofollow(P9_FILE_ID dirfd, const char *name, mode_t mode)
{
struct stat stbuf;
- int fd, ret;
+ P9_FILE_ID fd;
+ int ret;
/* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
* Unfortunately, the linux kernel doesn't implement it yet.
@@ -362,16 +365,16 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
/* Fallback for systems that don't support O_PATH: we depend on the file
* being readable or writable.
*/
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
/* In case the file is writable-only and isn't a directory. */
if (errno == EACCES) {
fd = openat_file(dirfd, name, O_WRONLY, 0);
}
- if (fd == -1 && errno == EISDIR) {
+ if (fd == P9_INVALID_FILE && errno == EISDIR) {
errno = EACCES;
}
}
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return -1;
}
ret = fchmod(fd, mode);
@@ -380,7 +383,7 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
* link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
* referring to the symbolic link.
*/
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return -1;
}
@@ -401,7 +404,7 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
return ret;
}
-static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
+static int local_set_xattrat(P9_FILE_ID dirfd, const char *path, FsCred *credp)
{
int err;
@@ -440,7 +443,7 @@ static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
return 0;
}
-static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
+static int local_set_cred_passthrough(FsContext *fs_ctx, P9_FILE_ID dirfd,
const char *name, FsCred *credp)
{
if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
@@ -478,10 +481,10 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
(fs_ctx->export_flags & V9FS_SM_NONE)) {
char *dirpath = g_path_get_dirname(fs_path->data);
char *name = g_path_get_basename(fs_path->data);
- int dirfd;
+ P9_FILE_ID dirfd;
dirfd = local_opendir_nofollow(fs_ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -507,10 +510,10 @@ static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
static int local_open(FsContext *ctx, V9fsPath *fs_path,
int flags, V9fsFidOpenState *fs)
{
- int fd;
+ P9_FILE_ID fd;
fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return -1;
}
fs->fd = fd;
@@ -520,11 +523,11 @@ static int local_open(FsContext *ctx, V9fsPath *fs_path,
static int local_opendir(FsContext *ctx,
V9fsPath *fs_path, V9fsFidOpenState *fs)
{
- int dirfd;
+ P9_FILE_ID dirfd;
DIR *stream;
dirfd = local_opendir_nofollow(ctx, fs_path->data);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -640,10 +643,10 @@ 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;
- int dirfd;
+ P9_FILE_ID dirfd;
dirfd = local_opendir_nofollow(fs_ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -667,7 +670,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, FsCred *credp)
{
int err = -1;
- int dirfd;
+ P9_FILE_ID dirfd;
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
local_is_mapped_file_metadata(fs_ctx, name)) {
@@ -676,7 +679,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
}
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -719,7 +722,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, FsCred *credp)
{
int err = -1;
- int dirfd;
+ P9_FILE_ID dirfd;
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
local_is_mapped_file_metadata(fs_ctx, name)) {
@@ -728,7 +731,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
}
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -816,9 +819,9 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
int flags, FsCred *credp, V9fsFidOpenState *fs)
{
- int fd = -1;
+ P9_FILE_ID fd = P9_INVALID_FILE;
int err = -1;
- int dirfd;
+ P9_FILE_ID dirfd;
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
local_is_mapped_file_metadata(fs_ctx, name)) {
@@ -832,7 +835,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
flags |= O_NOFOLLOW;
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -840,7 +843,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
fd = openat_file(dirfd, name, flags, fs_ctx->fmode);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
goto out;
}
credp->fc_mode = credp->fc_mode | S_IFREG;
@@ -856,7 +859,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
} else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
(fs_ctx->export_flags & V9FS_SM_NONE)) {
fd = openat_file(dirfd, name, flags, credp->fc_mode);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
goto out;
}
err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
@@ -882,7 +885,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
V9fsPath *dir_path, const char *name, FsCred *credp)
{
int err = -1;
- int dirfd;
+ P9_FILE_ID dirfd;
if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
local_is_mapped_file_metadata(fs_ctx, name)) {
@@ -891,7 +894,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
}
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -903,7 +906,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
fs_ctx->fmode);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
goto out;
}
/* Write the oldpath (target) to the file. */
@@ -962,7 +965,7 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
char *odirpath = g_path_get_dirname(oldpath->data);
char *oname = g_path_get_basename(oldpath->data);
int ret = -1;
- int odirfd, ndirfd;
+ P9_FILE_ID odirfd, ndirfd;
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
local_is_mapped_file_metadata(ctx, name)) {
@@ -971,12 +974,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
}
odirfd = local_opendir_nofollow(ctx, odirpath);
- if (odirfd == -1) {
+ if (odirfd == P9_INVALID_FILE) {
goto out;
}
ndirfd = local_opendir_nofollow(ctx, dirpath->data);
- if (ndirfd == -1) {
+ if (ndirfd == P9_INVALID_FILE) {
close_preserve_errno(odirfd);
goto out;
}
@@ -996,12 +999,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
}
omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
- if (omap_dirfd == -1) {
+ if (omap_dirfd == P9_INVALID_FILE) {
goto err;
}
nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
- if (nmap_dirfd == -1) {
+ if (nmap_dirfd == P9_INVALID_FILE) {
close_preserve_errno(omap_dirfd);
goto err;
}
@@ -1032,10 +1035,11 @@ out:
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
{
- int fd, ret;
+ P9_FILE_ID fd;
+ int ret;
fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return -1;
}
ret = ftruncate(fd, size);
@@ -1048,10 +1052,10 @@ static int local_chown(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;
- int dirfd;
+ P9_FILE_ID dirfd;
dirfd = local_opendir_nofollow(fs_ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -1078,10 +1082,11 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
{
char *dirpath = g_path_get_dirname(fs_path->data);
char *name = g_path_get_basename(fs_path->data);
- int dirfd, ret = -1;
+ P9_FILE_ID dirfd;
+ int ret = -1;
dirfd = local_opendir_nofollow(s, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -1093,13 +1098,13 @@ out:
return ret;
}
-static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
- int flags)
+static int local_unlinkat_common(FsContext *ctx, P9_FILE_ID dirfd,
+ const char *name, int flags)
{
int ret;
if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
- int map_dirfd;
+ P9_FILE_ID map_dirfd;
/* We need to remove the metadata as well:
* - the metadata directory if we're removing a directory
@@ -1110,10 +1115,10 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
* mode. We just ignore the error.
*/
if (flags == AT_REMOVEDIR) {
- int fd;
+ P9_FILE_ID fd;
fd = openat_dir(dirfd, name);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return -1;
}
ret = qemu_unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
@@ -1123,7 +1128,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
}
}
map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
- if (map_dirfd != -1) {
+ if (map_dirfd != P9_INVALID_FILE) {
ret = qemu_unlinkat(map_dirfd, name, 0);
close_preserve_errno(map_dirfd);
if (ret < 0 && errno != ENOENT) {
@@ -1143,11 +1148,11 @@ static int local_remove(FsContext *ctx, const char *path)
char *dirpath = g_path_get_dirname(path);
char *name = g_path_get_basename(path);
int flags = 0;
- int dirfd;
+ P9_FILE_ID dirfd;
int err = -1;
dirfd = local_opendir_nofollow(ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -1188,10 +1193,11 @@ static int local_fsync(FsContext *ctx, int fid_type,
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
{
- int fd, ret;
+ P9_FILE_ID fd;
+ int ret;
fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
- if (fd == -1) {
+ if (fd == P9_INVALID_FILE) {
return -1;
}
ret = fstatfs(fd, stbuf);
@@ -1276,7 +1282,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
const char *new_name)
{
int ret;
- int odirfd, ndirfd;
+ P9_FILE_ID odirfd, ndirfd;
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
(local_is_mapped_file_metadata(ctx, old_name) ||
@@ -1286,12 +1292,12 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
}
odirfd = local_opendir_nofollow(ctx, olddir->data);
- if (odirfd == -1) {
+ if (odirfd == P9_INVALID_FILE) {
return -1;
}
ndirfd = local_opendir_nofollow(ctx, newdir->data);
- if (ndirfd == -1) {
+ if (ndirfd == P9_INVALID_FILE) {
close_preserve_errno(odirfd);
return -1;
}
@@ -1302,7 +1308,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
}
if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
- int omap_dirfd, nmap_dirfd;
+ P9_FILE_ID omap_dirfd, nmap_dirfd;
ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
if (ret < 0 && errno != EEXIST) {
@@ -1310,12 +1316,12 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
}
omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
- if (omap_dirfd == -1) {
+ if (omap_dirfd == P9_INVALID_FILE) {
goto err;
}
nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
- if (nmap_dirfd == -1) {
+ if (nmap_dirfd == P9_INVALID_FILE) {
close_preserve_errno(omap_dirfd);
goto err;
}
@@ -1373,7 +1379,7 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
const char *name, int flags)
{
int ret;
- int dirfd;
+ P9_FILE_ID dirfd;
if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
local_is_mapped_file_metadata(ctx, name)) {
@@ -1382,7 +1388,7 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
}
dirfd = local_opendir_nofollow(ctx, dir->data);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
diff --git a/hw/9pfs/9p-util-darwin.c b/hw/9pfs/9p-util-darwin.c
index 95146e7354..c509034fb2 100644
--- a/hw/9pfs/9p-util-darwin.c
+++ b/hw/9pfs/9p-util-darwin.c
@@ -11,8 +11,8 @@
#include "qemu/error-report.h"
#include "9p-util.h"
-ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
- void *value, size_t size)
+ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
+ const char *name, void *value, size_t size)
{
int ret;
int fd = openat_file(dirfd, filename,
@@ -25,7 +25,7 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
return ret;
}
-ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
+ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
char *list, size_t size)
{
int ret;
@@ -39,7 +39,7 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
return ret;
}
-ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
+ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
const char *name)
{
int ret;
@@ -52,8 +52,8 @@ ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
return ret;
}
-int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
- void *value, size_t size, int flags)
+int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
+ const char *name, void *value, size_t size, int flags)
{
int ret;
int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
@@ -110,7 +110,7 @@ out:
return err;
}
-int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
+int qemu_mknodat(P9_FILE_ID dirfd, const char *filename, mode_t mode, dev_t dev)
{
int preserved_errno, err;
diff --git a/hw/9pfs/9p-util-linux.c b/hw/9pfs/9p-util-linux.c
index db451b0784..cb5e988354 100644
--- a/hw/9pfs/9p-util-linux.c
+++ b/hw/9pfs/9p-util-linux.c
@@ -19,8 +19,8 @@
#include "qemu/xattr.h"
#include "9p-util.h"
-ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
- void *value, size_t size)
+ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
+ const char *name, void *value, size_t size)
{
char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
int ret;
@@ -30,7 +30,7 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
return ret;
}
-ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
+ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
char *list, size_t size)
{
char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
@@ -41,7 +41,7 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
return ret;
}
-ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
+ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
const char *name)
{
char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
@@ -52,8 +52,8 @@ ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
return ret;
}
-int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
- void *value, size_t size, int flags)
+int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
+ const char *name, void *value, size_t size, int flags)
{
char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
int ret;
@@ -64,7 +64,7 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
}
-int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
+int qemu_mknodat(P9_FILE_ID dirfd, const char *filename, mode_t mode, dev_t dev)
{
return mknodat(dirfd, filename, mode, dev);
}
diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c
index 9ae69dd8db..aaa364ea4a 100644
--- a/hw/9pfs/9p-xattr.c
+++ b/hw/9pfs/9p-xattr.c
@@ -78,13 +78,13 @@ 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;
- int dirfd;
+ P9_FILE_ID dirfd;
/* Get the actual len */
dirpath = g_path_get_dirname(path);
dirfd = local_opendir_nofollow(ctx, dirpath);
g_free(dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
return -1;
}
@@ -168,11 +168,11 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
{
char *dirpath = g_path_get_dirname(path);
char *filename = g_path_get_basename(path);
- int dirfd;
+ P9_FILE_ID dirfd;
ssize_t ret = -1;
dirfd = local_opendir_nofollow(ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -196,11 +196,11 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
{
char *dirpath = g_path_get_dirname(path);
char *filename = g_path_get_basename(path);
- int dirfd;
+ P9_FILE_ID dirfd;
ssize_t ret = -1;
dirfd = local_opendir_nofollow(ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
@@ -223,11 +223,11 @@ ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
{
char *dirpath = g_path_get_dirname(path);
char *filename = g_path_get_basename(path);
- int dirfd;
+ P9_FILE_ID dirfd;
ssize_t ret = -1;
dirfd = local_opendir_nofollow(ctx, dirpath);
- if (dirfd == -1) {
+ if (dirfd == P9_INVALID_FILE) {
goto out;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 05/16] hw/9pfs: Update P9_FILE_ID to support Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (3 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 04/16] hw/9pfs: Introduce an opaque type 9P_FILE_ID Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 06/16] hw/9pfs: Add missing definitions for Windows Bin Meng
` (11 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Christian Schoenebeck, Greg Kurz
On Windows P9_FILE_ID points to a file handle.
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-file-id.h | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/hw/9pfs/9p-file-id.h b/hw/9pfs/9p-file-id.h
index 60cbfbf4dd..00903048e6 100644
--- a/hw/9pfs/9p-file-id.h
+++ b/hw/9pfs/9p-file-id.h
@@ -11,11 +11,19 @@
/*
* 9pfs file id
*
- * This is file descriptor on POSIX platforms
+ * This is file descriptor on POSIX platforms, handle on Windows
*/
+#ifndef CONFIG_WIN32
typedef int P9_FILE_ID;
+#else
+typedef HANDLE P9_FILE_ID;
+#endif
/* invalid value for P9_FILE_ID */
+#ifndef CONFIG_WIN32
#define P9_INVALID_FILE -1
+#else
+#define P9_INVALID_FILE INVALID_HANDLE_VALUE
+#endif
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 06/16] hw/9pfs: Add missing definitions for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (4 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 05/16] hw/9pfs: Update P9_FILE_ID to support Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs Bin Meng
` (10 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
Some definitions currently used by the 9pfs codes are only available
on POSIX platforms. Let's add our own ones in preparation to adding
9pfs support for Windows.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
fsdev/file-op-9p.h | 33 +++++++++++++++++++++++++++++++++
hw/9pfs/9p.h | 11 +++++++++++
2 files changed, 44 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
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 994f952600..670f5dc4fb 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -3,13 +3,24 @@
#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 NAME_MAX MAX_PATH
+
+/* macros required for build, values do not matter */
+#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links */
+#define AT_REMOVEDIR 0x200 /* Remove directory instead of file */
+#define O_DIRECTORY 02000000
+#endif
+
enum {
P9_TLERROR = 6,
P9_RLERROR,
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (5 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 06/16] hw/9pfs: Add missing definitions for Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-11-01 14:27 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 08/16] hw/9pfs: Handle current directory offset for Windows Bin Meng
` (9 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
Windows POSIX API and MinGW library do not provide the NO_FOLLOW
flag, and do not allow opening a directory by POSIX open(). This
causes all xxx_at() functions cannot work directly. However, we
can provide Windows handle based functions to emulate xxx_at()
functions (e.g.: openat_win32, utimensat_win32, etc.).
Windows does not support extended attributes. 9pfs for Windows uses
NTFS ADS (Alternate Data Streams) to emulate extended attributes.
Windows does not provide POSIX compatible readlink(), and symbolic
link feature in 9pfs will be disabled on Windows.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-local.h | 7 +
hw/9pfs/9p-util.h | 40 +-
hw/9pfs/9p-local.c | 4 -
hw/9pfs/9p-util-win32.c | 885 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 931 insertions(+), 5 deletions(-)
create mode 100644 hw/9pfs/9p-util-win32.c
diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h
index c8404063e5..02fd894ba3 100644
--- a/hw/9pfs/9p-local.h
+++ b/hw/9pfs/9p-local.h
@@ -15,6 +15,13 @@
#include "9p-file-id.h"
+typedef struct {
+ P9_FILE_ID mountfd;
+#ifdef CONFIG_WIN32
+ char *root_path;
+#endif
+} LocalData;
+
P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
mode_t mode);
P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path);
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 1e7dc76345..82b2d0c3e4 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -90,26 +90,61 @@ static inline int errno_to_dotl(int err) {
return err;
}
-#ifdef CONFIG_DARWIN
+#if defined(CONFIG_DARWIN)
#define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0)
+#elif defined(CONFIG_WIN32)
+#define qemu_fgetxattr fgetxattr_win32
#else
#define qemu_fgetxattr fgetxattr
#endif
+#ifdef CONFIG_WIN32
+#define qemu_openat openat_win32
+#define qemu_fstatat fstatat_win32
+#define qemu_mkdirat mkdirat_win32
+#define qemu_renameat renameat_win32
+#define qemu_utimensat utimensat_win32
+#define qemu_unlinkat unlinkat_win32
+#else
#define qemu_openat openat
#define qemu_fstatat fstatat
#define qemu_mkdirat mkdirat
#define qemu_renameat renameat
#define qemu_utimensat utimensat
#define qemu_unlinkat unlinkat
+#endif
+
+#ifdef CONFIG_WIN32
+char *get_full_path_win32(P9_FILE_ID fd, const char *name);
+ssize_t fgetxattr_win32(int fd, const char *name, void *value, size_t size);
+P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int flags,
+ mode_t mode);
+int fstatat_win32(P9_FILE_ID dirfd, const char *pathname,
+ struct stat *statbuf, int flags);
+int mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t mode);
+int renameat_win32(P9_FILE_ID olddirfd, const char *oldpath,
+ P9_FILE_ID newdirfd, const char *newpath);
+int utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
+ const struct timespec times[2], int flags);
+int unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags);
+int statfs_win32(const char *root_path, struct statfs *stbuf);
+P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name);
+P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
+ mode_t mode);
+#endif
static inline void close_preserve_errno(P9_FILE_ID fd)
{
int serrno = errno;
+#ifndef CONFIG_WIN32
close(fd);
+#else
+ CloseHandle(fd);
+#endif
errno = serrno;
}
+#ifndef CONFIG_WIN32
static inline P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name)
{
return qemu_openat(dirfd, name,
@@ -157,6 +192,7 @@ again:
errno = serrno;
return fd;
}
+#endif
ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
const char *name, void *value, size_t size);
@@ -167,6 +203,7 @@ ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
const char *name);
+#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,
@@ -181,6 +218,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.
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index 7e8d8492ea..d0d6d93549 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 {
- P9_FILE_ID mountfd;
-} LocalData;
-
P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
mode_t mode)
{
diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c
new file mode 100644
index 0000000000..953e7da6fa
--- /dev/null
+++ b/hw/9pfs/9p-util-win32.c
@@ -0,0 +1,885 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file contains Windows only functions for 9pfs.
+ *
+ * For 9pfs Windows host, the following features are different from Linux host:
+ *
+ * 1. Windows POSIX API does not provide the NO_FOLLOW flag, that means MinGW
+ * cannot detect if a path is a symbolic link or not. Also Windows do not
+ * provide POSIX compatible readlink(). Supporting symbolic link in 9pfs on
+ * Windows may cause security issues, so symbolic link support is disabled
+ * completely.
+ *
+ * 2. Windows file system does not support extended attributes directly. 9pfs
+ * for Windows uses NTFS ADS (Alternate Data Streams) to emulate extended
+ * attributes.
+ *
+ * 3. statfs() is not available on Windows. qemu_statfs() is used to emulate it.
+ *
+ * 4. On Windows trying to open a directory with the open() API will fail.
+ * This is because Windows does not allow opening directory in normal usage.
+ *
+ * As a result of this, all xxx_at() functions won't work directly on
+ * Windows, e.g.: openat(), unlinkat(), etc.
+ *
+ * As xxx_at() can prevent parent directory to be modified on Linux host,
+ * to support this and prevent security issue, all xxx_at() APIs are replaced
+ * by xxx_at_win32() and Windows handle is used to replace the directory fd.
+ *
+ * Windows file system does not allow replacing a file or directory if it is
+ * referenced by a handle. Keep the handle open will lock and protect the
+ * parent directory and make the access to files atomically.
+ *
+ * If we don't protect (lock) the parent directory, the parent directory may
+ * be replaced by others (e.g.: a symbolic link) and cause security issues.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "9p.h"
+#include "9p-util.h"
+#include "9p-local.h"
+
+#include <windows.h>
+#include <dirent.h>
+
+#define V9FS_MAGIC 0x53465039 /* string "9PFS" */
+
+/*
+ * build_ads_name - construct Windows ADS name
+ *
+ * This function constructs Windows NTFS ADS (Alternate Data Streams) name
+ * to <namebuf>.
+ */
+static int build_ads_name(char *namebuf, size_t namebuf_len,
+ const char *filename, const char *ads_name)
+{
+ size_t total_size;
+
+ total_size = strlen(filename) + strlen(ads_name) + 2;
+ if (total_size > namebuf_len) {
+ return -1;
+ }
+
+ /*
+ * NTFS ADS (Alternate Data Streams) name format: filename:ads_name
+ * e.g.: D:\1.txt:my_ads_name
+ */
+
+ strcpy(namebuf, filename);
+ strcat(namebuf, ":");
+ strcat(namebuf, ads_name);
+
+ return 0;
+}
+
+/*
+ * copy_ads_name - copy ADS name from buffer returned by FindNextStreamW()
+ *
+ * This function removes string "$DATA" in ADS name string returned by
+ * FindNextStreamW(), and copies the real ADS name to <namebuf>.
+ */
+static ssize_t copy_ads_name(char *namebuf, size_t namebuf_len,
+ char *full_ads_name)
+{
+ char *p1, *p2;
+
+ /*
+ * NTFS ADS (Alternate Data Streams) name from enumerate data format:
+ * :ads_name:$DATA, e.g.: :my_ads_name:$DATA
+ *
+ * ADS name from FindNextStreamW() always has ":$DATA" string at the end.
+ *
+ * This function copies ADS name to namebuf.
+ */
+
+ p1 = strchr(full_ads_name, ':');
+ 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 > namebuf_len) {
+ return -1;
+ }
+
+ memcpy(namebuf, p1 + 1, p2 - p1 - 1);
+ namebuf[p2 - p1 - 1] = '\0';
+
+ return p2 - p1;
+}
+
+/*
+ * get_full_path_win32 - get full file name base on a handle
+ *
+ * This function gets full file name based on a handle specified by <fd> to
+ * a file or directory.
+ *
+ * Caller function needs to free the file name string after use.
+ */
+char *get_full_path_win32(P9_FILE_ID fd, const char *name)
+{
+ g_autofree char *full_file_name = NULL;
+ DWORD total_size;
+ DWORD name_size;
+
+ full_file_name = g_malloc0(NAME_MAX);
+
+ /* get parent directory full file name */
+ name_size = GetFinalPathNameByHandle(fd, full_file_name,
+ NAME_MAX - 1, FILE_NAME_NORMALIZED);
+ if (name_size == 0 || name_size > NAME_MAX - 1) {
+ return NULL;
+ }
+
+ /* full path returned is the "\\?\" syntax, remove the lead string */
+ memmove(full_file_name, full_file_name + 4, NAME_MAX - 4);
+
+ if (name != NULL) {
+ total_size = strlen(full_file_name) + strlen(name) + 2;
+
+ if (total_size > NAME_MAX) {
+ return NULL;
+ }
+
+ /* build sub-directory file name */
+ strcat(full_file_name, "\\");
+ strcat(full_file_name, name);
+ }
+
+ return g_steal_pointer(&full_file_name);
+}
+
+/*
+ * fgetxattr_win32 - get extended attribute by fd
+ *
+ * This function gets extened attribute by <fd>. <fd> will be translated to
+ * Windows handle.
+ *
+ * This function emulates extended attribute by NTFS ADS.
+ */
+ssize_t fgetxattr_win32(int fd, const char *name, void *value, size_t size)
+{
+ g_autofree char *full_file_name = NULL;
+ char ads_file_name[NAME_MAX + 1] = {0};
+ DWORD dwBytesRead;
+ HANDLE hStream;
+ HANDLE hFile;
+
+ hFile = (HANDLE)_get_osfhandle(fd);
+
+ full_file_name = get_full_path_win32(hFile, NULL);
+ if (full_file_name == NULL) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ hStream = CreateFile(ads_file_name, 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;
+}
+
+/*
+ * openat_win32 - emulate openat()
+ *
+ * This function emulates openat().
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So openat_win32() has to use a directory handle instead of a directory fd.
+ *
+ * For symbolic access:
+ * 1. Parent directory handle <dirfd> should not be a symbolic link because
+ * it is opened by openat_dir() which can prevent from opening a link to
+ * a dirctory.
+ * 2. Link flag in <mode> is not set because Windows does not have this flag.
+ * Create a new symbolic link will be denied.
+ * 3. This function checks file symbolic link attribute after open.
+ *
+ * So symbolic link will not be accessed by 9p client.
+ */
+P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int flags,
+ mode_t mode)
+{
+ g_autofree char *full_file_name1 = NULL;
+ g_autofree char *full_file_name2 = NULL;
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ int fd;
+
+ full_file_name1 = get_full_path_win32(dirfd, pathname);
+ if (full_file_name1 == NULL) {
+ return hFile;
+ }
+
+ fd = open(full_file_name1, flags, mode);
+ if (fd > 0) {
+ DWORD attribute;
+ hFile = (HANDLE)_get_osfhandle(fd);
+
+ full_file_name2 = get_full_path_win32(hFile, NULL);
+ attribute = GetFileAttributes(full_file_name2);
+
+ /* check if it is a symbolic link */
+ if ((attribute == INVALID_FILE_ATTRIBUTES)
+ || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ errno = EACCES;
+ hFile = INVALID_HANDLE_VALUE;
+ close(fd);
+ }
+ }
+
+ return hFile;
+}
+
+/*
+ * fstatat_win32 - emulate fstatat()
+ *
+ * This function emulates fstatat().
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So fstatat_win32() has to use a directory handle instead of a directory fd.
+ *
+ * Access to a symbolic link will be denied to prevent security issues.
+ */
+int fstatat_win32(P9_FILE_ID dirfd, const char *pathname,
+ struct stat *statbuf, int flags)
+{
+ g_autofree char *full_file_name = NULL;
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ DWORD attribute;
+ int err = 0;
+ int ret = -1;
+
+ full_file_name = get_full_path_win32(dirfd, pathname);
+ if (full_file_name == NULL) {
+ return ret;
+ }
+
+ /* open file to lock it */
+ hFile = CreateFile(full_file_name, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS
+ | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ err = EACCES;
+ goto out;
+ }
+
+ attribute = GetFileAttributes(full_file_name);
+
+ /* check if it is a symbolic link */
+ if ((attribute == INVALID_FILE_ATTRIBUTES)
+ || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ errno = EACCES;
+ goto out;
+ }
+
+ ret = stat(full_file_name, statbuf);
+
+out:
+ if (hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hFile);
+ }
+
+ if (err != 0) {
+ errno = err;
+ }
+ return ret;
+}
+
+/*
+ * mkdirat_win32 - emulate mkdirat()
+ *
+ * This function emulates mkdirat().
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So mkdirat_win32() has to use a directory handle instead of a directory fd.
+ */
+int mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t mode)
+{
+ g_autofree char *full_file_name = NULL;
+ int ret = -1;
+
+ full_file_name = get_full_path_win32(dirfd, pathname);
+ if (full_file_name == NULL) {
+ return ret;
+ }
+
+ ret = mkdir(full_file_name);
+
+ return ret;
+}
+
+/*
+ * renameat_win32 - emulate renameat()
+ *
+ * This function emulates renameat().
+ *
+ * Windows POSIX API does not support openning a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So renameat_win32() has to use a directory handle instead of a directory fd.
+ *
+ * Access to a symbolic link will be denied to prevent security issues.
+ */
+int renameat_win32(HANDLE olddirfd, const char *oldpath,
+ HANDLE newdirfd, const char *newpath)
+{
+ g_autofree char *full_old_name = NULL;
+ g_autofree char *full_new_name = NULL;
+ HANDLE hFile;
+ DWORD attribute;
+ int err = 0;
+ int ret = -1;
+
+ full_old_name = get_full_path_win32(olddirfd, oldpath);
+ full_new_name = get_full_path_win32(newdirfd, newpath);
+ if (full_old_name == NULL || full_new_name == NULL) {
+ return ret;
+ }
+
+ /* open file to lock it */
+ hFile = CreateFile(full_old_name, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ attribute = GetFileAttributes(full_old_name);
+
+ /* check if it is a symbolic link */
+ if ((attribute == INVALID_FILE_ATTRIBUTES)
+ || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ err = EACCES;
+ goto out;
+ }
+
+ CloseHandle(hFile);
+
+ ret = rename(full_old_name, full_new_name);
+out:
+ if (err != 0) {
+ errno = err;
+ }
+ return ret;
+}
+
+/*
+ * utimensat_win32 - emulate utimensat()
+ *
+ * This function emulates utimensat().
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So utimensat_win32() has to use a directory handle instead of a directory fd.
+ *
+ * Access to a symbolic link will be denied to prevent security issues.
+ */
+int utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
+ const struct timespec times[2], int flags)
+{
+ g_autofree char *full_file_name = NULL;
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ DWORD attribute;
+ struct utimbuf tm;
+ int err = 0;
+ int ret = -1;
+
+ full_file_name = get_full_path_win32(dirfd, pathname);
+ if (full_file_name == NULL) {
+ return ret;
+ }
+
+ /* open file to lock it */
+ hFile = CreateFile(full_file_name, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS
+ | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ err = EACCES;
+ goto out;
+ }
+
+ attribute = GetFileAttributes(full_file_name);
+
+ /* check if it is a symbolic link */
+ if ((attribute == INVALID_FILE_ATTRIBUTES)
+ || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ errno = EACCES;
+ goto out;
+ }
+
+ tm.actime = times[0].tv_sec;
+ tm.modtime = times[1].tv_sec;
+
+ ret = utime(full_file_name, &tm);
+
+out:
+ if (hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hFile);
+ }
+
+ if (err != 0) {
+ errno = err;
+ }
+ return ret;
+}
+
+/*
+ * unlinkat_win32 - emulate unlinkat()
+ *
+ * This function emulates unlinkat().
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So unlinkat_win32() has to use a directory handle instead of a directory fd.
+ *
+ * Access to a symbolic link will be denied to prevent security issues.
+ */
+
+int unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags)
+{
+ g_autofree char *full_file_name = NULL;
+ HANDLE hFile;
+ DWORD attribute;
+ int err = 0;
+ int ret = -1;
+
+ full_file_name = get_full_path_win32(dirfd, pathname);
+ if (full_file_name == NULL) {
+ return ret;
+ }
+
+ /* open file to prevent other one modify it */
+ hFile = CreateFile(full_file_name, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ attribute = GetFileAttributes(full_file_name);
+
+ /* check if it is a symbolic link */
+ if ((attribute == INVALID_FILE_ATTRIBUTES)
+ || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ err = EACCES;
+ goto out;
+ }
+
+ if (flags == AT_REMOVEDIR) { /* remove directory */
+ if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ err = ENOTDIR;
+ goto out;
+ }
+ ret = rmdir(full_file_name);
+ } else { /* remove regular file */
+ if ((attribute & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ err = EISDIR;
+ goto out;
+ }
+ ret = remove(full_file_name);
+ }
+
+ /* after last handle closed, file will be removed */
+ CloseHandle(hFile);
+
+out:
+ if (err != 0) {
+ errno = err;
+ }
+ return ret;
+}
+
+/*
+ * statfs_win32 - statfs() on Windows
+ *
+ * This function emulates statfs() on Windows host.
+ */
+int statfs_win32(const char *path, struct statfs *stbuf)
+{
+ char RealPath[4] = { 0 };
+ unsigned long SectorsPerCluster;
+ unsigned long BytesPerSector;
+ unsigned long NumberOfFreeClusters;
+ unsigned long TotalNumberOfClusters;
+
+ /* only need first 3 bytes, e.g. "C:\ABC", only need "C:\" */
+ memcpy(RealPath, path, 3);
+
+ if (GetDiskFreeSpace(RealPath, &SectorsPerCluster, &BytesPerSector,
+ &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ stbuf->f_type = V9FS_MAGIC;
+ stbuf->f_bsize =
+ (__fsword_t)SectorsPerCluster * (__fsword_t)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;
+}
+
+/*
+ * openat_dir - emulate openat_dir()
+ *
+ * This function emulates openat_dir().
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * So openat_dir() has to use a directory handle instead of a directory fd.
+ *
+ * Access to a symbolic link will be denied to prevent security issues.
+ */
+P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name)
+{
+ g_autofree char *full_file_name = NULL;
+ HANDLE hSubDir;
+ DWORD attribute;
+
+ full_file_name = get_full_path_win32(dirfd, name);
+ if (full_file_name == NULL) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ attribute = GetFileAttributes(full_file_name);
+ if (attribute == INVALID_FILE_ATTRIBUTES) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* check if it is a directory */
+ if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* do not allow opening a symbolic link */
+ if ((attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* open it */
+ hSubDir = CreateFile(full_file_name, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ return hSubDir;
+}
+
+P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
+ mode_t mode)
+{
+ return openat_win32(dirfd, name, flags | _O_BINARY, mode);
+}
+
+/*
+ * fgetxattrat_nofollow - get extended attribute
+ *
+ * This function gets extended attribute from file <path> in the directory
+ * specified by <dirfd>. The extended atrribute name is specified by <name>
+ * and return value will be put in <value>.
+ *
+ * This function emulates extended attribute by NTFS ADS.
+ */
+ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
+ const char *name, void *value, size_t size)
+{
+ g_autofree char *full_file_name = NULL;
+ char ads_file_name[NAME_MAX + 1] = { 0 };
+ DWORD dwBytesRead;
+ HANDLE hStream;
+
+ full_file_name = get_full_path_win32(dirfd, path);
+ if (full_file_name == NULL) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ hStream = CreateFile(ads_file_name, 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;
+}
+
+/*
+ * fsetxattrat_nofollow - set extended attribute
+ *
+ * This function set extended attribute to file <path> in the directory
+ * specified by <dirfd>.
+ *
+ * This function emulates extended attribute by NTFS ADS.
+ */
+
+int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ g_autofree char *full_file_name = NULL;
+ char ads_file_name[NAME_MAX + 1] = { 0 };
+ DWORD dwBytesWrite;
+ HANDLE hStream;
+
+ full_file_name = get_full_path_win32(dirfd, path);
+ if (full_file_name == NULL) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ hStream = CreateFile(ads_file_name, 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;
+}
+
+/*
+ * flistxattrat_nofollow - list extended attribute
+ *
+ * This function gets extended attribute lists from file <filename> in the
+ * directory specified by <dirfd>. Lists returned will be put in <list>.
+ *
+ * This function emulates extended attribute by NTFS ADS.
+ */
+ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
+ char *list, size_t size)
+{
+ g_autofree char *full_file_name = NULL;
+ WCHAR WideCharStr[NAME_MAX + 1] = { 0 };
+ char full_ads_name[NAME_MAX + 1];
+ WIN32_FIND_STREAM_DATA fsd;
+ BOOL bFindNext;
+ char *list_ptr = list;
+ size_t list_left_size = size;
+ HANDLE hFind;
+ int ret;
+
+ full_file_name = get_full_path_win32(dirfd, filename);
+ if (full_file_name == NULL) {
+ errno = EIO;
+ return -1;
+ }
+
+ /*
+ * ADS enumerate function only has WCHAR version, so we need to
+ * covert filename to utf-8 string.
+ */
+ ret = MultiByteToWideChar(CP_UTF8, 0, full_file_name,
+ strlen(full_file_name), WideCharStr, NAME_MAX);
+ if (ret == 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0);
+ if (hFind == INVALID_HANDLE_VALUE) {
+ errno = ENODATA;
+ return -1;
+ }
+
+ do {
+ memset(full_ads_name, 0, sizeof(full_ads_name));
+
+ /*
+ * ADS enumerate function only has WCHAR version, so we need to
+ * covert cStreamName to utf-8 string.
+ */
+ ret = WideCharToMultiByte(CP_UTF8, 0,
+ fsd.cStreamName, wcslen(fsd.cStreamName) + 1,
+ full_ads_name, sizeof(full_ads_name) - 1,
+ NULL, NULL);
+ if (ret == 0) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ errno = ERANGE;
+ }
+ CloseHandle(hFind);
+ return -1;
+ }
+
+ ret = copy_ads_name(list_ptr, list_left_size, full_ads_name);
+ if (ret < 0) {
+ errno = ERANGE;
+ CloseHandle(hFind);
+ return -1;
+ }
+
+ list_ptr = list_ptr + ret;
+ list_left_size = list_left_size - ret;
+
+ bFindNext = FindNextStreamW(hFind, &fsd);
+ } while (bFindNext);
+
+ CloseHandle(hFind);
+
+ return size - list_left_size;
+}
+
+/*
+ * fremovexattrat_nofollow - remove extended attribute
+ *
+ * This function removes an extended attribute from file <filename> in the
+ * directory specified by <dirfd>.
+ *
+ * This function emulates extended attribute by NTFS ADS.
+ */
+ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
+ const char *name)
+{
+ g_autofree char *full_file_name = NULL;
+ char ads_file_name[NAME_MAX + 1] = { 0 };
+
+ full_file_name = get_full_path_win32(dirfd, filename);
+ if (full_file_name == NULL) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (build_ads_name(ads_file_name, NAME_MAX, filename, name) < 0) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (DeleteFile(ads_file_name) != 0) {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND) {
+ errno = ENODATA;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * local_opendir_nofollow - open a Windows directory
+ *
+ * This function returns a Windows file handle of the directory specified by
+ * <dirpath> based on 9pfs mount point.
+ *
+ * Windows POSIX API does not support opening a directory by open(). Only
+ * handle of directory can be opened by CreateFile().
+ *
+ * This function checks the resolved path of <dirpath>. If the resolved
+ * path is not in the scope of root directory (e.g. by symbolic link), then
+ * this function will fail to prevent any security issues.
+ */
+HANDLE local_opendir_nofollow(FsContext *fs_ctx, const char *dirpath)
+{
+ g_autofree char *full_file_name = NULL;
+ LocalData *data = fs_ctx->private;
+ HANDLE hDir;
+
+ hDir = openat_dir(data->mountfd, dirpath);
+ if (hDir == INVALID_HANDLE_VALUE) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ full_file_name = get_full_path_win32(hDir, NULL);
+ if (full_file_name == NULL) {
+ CloseHandle(hDir);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /*
+ * Check if the resolved path is in the root directory scope:
+ * data->root_path and full_file_name are full path with symbolic
+ * link resolved, so fs_ctx->root_path must be in the head of
+ * full_file_name. If not, that means guest OS tries to open a file not
+ * in the scope of mount point. This operation should be denied.
+ */
+ if (memcmp(full_file_name, data->root_path,
+ strlen(data->root_path)) != 0) {
+ CloseHandle(hDir);
+ hDir = INVALID_HANDLE_VALUE;
+ }
+
+ return hDir;
+}
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 08/16] hw/9pfs: Handle current directory offset for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (6 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-11-01 14:41 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 09/16] hw/9pfs: Disable unsupported flags and features " Bin Meng
` (8 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
On Windows 'struct dirent' does not have current directory offset.
We have to save current directory offset and update offset when
reading directory.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p.c | 16 ++++++++++++++++
hw/9pfs/codir.c | 15 +++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index aebadeaa03..6c4af86240 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -2319,7 +2319,15 @@ 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
+ /*
+ * Get offset by calling telldir() manually,
+ * as Windows does not have dent->d_off.
+ */
+ saved_dir_pos = v9fs_co_telldir(pdu, fidp);
+#endif
}
v9fs_readdir_unlock(&fidp->fs.dir);
@@ -2520,7 +2528,15 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
qid.version = 0;
}
+#ifndef CONFIG_WIN32
off = qemu_dirent_off(dent);
+#else
+ /*
+ * Get offset by calling telldir() manually,
+ * as Windows does not have dent->d_off.
+ */
+ off = v9fs_co_telldir(pdu, fidp);
+#endif
v9fs_string_init(&name);
v9fs_string_sprintf(&name, "%s", dent->d_name);
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] 40+ messages in thread
* [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (7 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 08/16] hw/9pfs: Handle current directory offset for Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-11-01 15:04 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 10/16] hw/9pfs: Update the local fs driver to support Windows Bin Meng
` (7 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
Some flags and features are not supported on Windows, like mknod,
readlink, file mode, etc. Update the codes for Windows.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-util.h | 6 +++-
hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 86 insertions(+), 10 deletions(-)
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 82b2d0c3e4..3d154e9103 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -53,8 +53,10 @@ static inline uint64_t makedev_dotl(uint32_t dev_major, uint32_t dev_minor)
*/
static inline uint64_t host_dev_to_dotl_dev(dev_t dev)
{
-#ifdef CONFIG_LINUX
+#if defined(CONFIG_LINUX)
return dev;
+#elif defined(CONFIG_WIN32)
+ return 0;
#else
return makedev_dotl(major(dev), minor(dev));
#endif
@@ -260,7 +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t mode,
dev_t dev);
+#endif
#endif
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 6c4af86240..771aab34ac 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -39,6 +39,11 @@
#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;
static int open_fd_rc;
@@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
DotlOpenflagMap dotl_oflag_map[] = {
{ P9_DOTL_CREATE, O_CREAT },
{ P9_DOTL_EXCL, O_EXCL },
+#ifndef CONFIG_WIN32
{ P9_DOTL_NOCTTY , O_NOCTTY },
+#endif
{ P9_DOTL_TRUNC, O_TRUNC },
{ P9_DOTL_APPEND, O_APPEND },
+#ifndef CONFIG_WIN32
{ P9_DOTL_NONBLOCK, O_NONBLOCK } ,
{ P9_DOTL_DSYNC, O_DSYNC },
{ P9_DOTL_FASYNC, FASYNC },
-#ifndef CONFIG_DARWIN
+#endif
+#ifdef CONFIG_LINUX
{ P9_DOTL_NOATIME, O_NOATIME },
/*
* On Darwin, we could map to F_NOCACHE, which is
@@ -151,8 +160,10 @@ static int dotl_to_open_flags(int flags)
#endif
{ P9_DOTL_LARGEFILE, O_LARGEFILE },
{ P9_DOTL_DIRECTORY, O_DIRECTORY },
+#ifndef CONFIG_WIN32
{ P9_DOTL_NOFOLLOW, O_NOFOLLOW },
{ P9_DOTL_SYNC, O_SYNC },
+#endif
};
for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
@@ -179,8 +190,11 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
* Filter the client open flags
*/
flags = dotl_to_open_flags(oflags);
- flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
-#ifndef CONFIG_DARWIN
+ flags &= ~(O_CREAT);
+#ifndef CONFIG_WIN32
+ flags &= ~(O_NOCTTY | O_ASYNC);
+#endif
+#ifdef CONFIG_LINUX
/*
* Ignore direct disk access hint until the server supports it.
*/
@@ -986,9 +1000,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;
}
@@ -1097,6 +1113,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;
}
@@ -1106,6 +1123,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;
@@ -1118,6 +1136,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;
}
@@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
if (mode & P9_STAT_MODE_SETVTX) {
ret |= S_ISVTX;
}
+#endif
return ret;
}
@@ -1182,6 +1202,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;
}
@@ -1193,11 +1214,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;
}
@@ -1209,6 +1232,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;
}
@@ -1247,9 +1271,17 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path,
return err;
}
} else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
+ unsigned maj, min;
+
+#ifndef CONFIG_WIN32
+ maj = major(stbuf->st_rdev);
+ min = minor(stbuf->st_rdev);
+#else
+ maj = min = 0;
+#endif
v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
- major(stbuf->st_rdev), minor(stbuf->st_rdev));
+ maj, min);
} 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);
@@ -1317,7 +1349,14 @@ 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)
{
- return blksize_to_iounit(pdu, stbuf->st_blksize);
+ int32_t blksize;
+
+#ifndef CONFIG_WIN32
+ blksize = stbuf->st_blksize);
+#else
+ blksize = 0;
+#endif
+ return blksize_to_iounit(pdu, blksize);
}
static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
@@ -1332,7 +1371,11 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
v9lstat->st_size = stbuf->st_size;
v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
+#ifndef CONFIG_WIN32
v9lstat->st_blocks = stbuf->st_blocks;
+#else
+ v9lstat->st_blocks = 0;
+#endif
v9lstat->st_atime_sec = stbuf->st_atime;
v9lstat->st_mtime_sec = stbuf->st_mtime;
v9lstat->st_ctime_sec = stbuf->st_ctime;
@@ -1340,7 +1383,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec;
-#else
+#endif
+#ifdef CONFIG_LINUX
v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
@@ -2471,6 +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
struct dirent *dent;
struct stat *st;
struct V9fsDirEnt *entries = NULL;
+ unsigned char d_type = 0;
/*
* inode remapping requires the device id, which in turn might be
@@ -2540,10 +2585,13 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
v9fs_string_init(&name);
v9fs_string_sprintf(&name, "%s", dent->d_name);
+#ifndef CONFIG_WIN32
+ d_type = dent->d_type;
+#endif
/* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
len = pdu_marshal(pdu, 11 + count, "Qqbs",
&qid, off,
- dent->d_type, &name);
+ d_type, &name);
v9fs_string_free(&name);
@@ -2873,8 +2921,12 @@ 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;
}
@@ -2899,8 +2951,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;
}
@@ -3634,7 +3690,7 @@ out_nofid:
static void coroutine_fn v9fs_mknod(void *opaque)
{
-
+#ifndef CONFIG_WIN32
int mode;
gid_t gid;
int32_t fid;
@@ -3691,6 +3747,10 @@ out:
out_nofid:
pdu_complete(pdu, err);
v9fs_string_free(&name);
+#else
+ V9fsPDU *pdu = opaque;
+ pdu_complete(pdu, -1);
+#endif
}
/*
@@ -3963,7 +4023,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.
@@ -3980,6 +4040,7 @@ out_nofid:
static void coroutine_fn v9fs_xattrcreate(void *opaque)
{
+#ifndef CONFIG_WIN32
int flags, rflags = 0;
int32_t fid;
uint64_t size;
@@ -4041,10 +4102,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;
@@ -4080,6 +4146,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[] = {
@@ -4341,6 +4411,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");
@@ -4348,4 +4419,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
}
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 10/16] hw/9pfs: Update the local fs driver to support Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (8 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 09/16] hw/9pfs: Disable unsupported flags and features " Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 11/16] hw/9pfs: Add Linux error number definition Bin Meng
` (6 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
At present there is no Windows support for 9p file system.
This commit 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.
Symbolic link and hard link are not supported because Windows
NTFS does not fully support them with POSIX compatability.
inode remap is always enabled because Windows file system does
not have inode number.
mknod() is not supported because Windows does not support it.
chown() and chmod() are not supported when 9pfs is configured
with security mode to 'none' or 'passthrough' because Windows
host does not support such type request.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-local.c | 336 +++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 327 insertions(+), 9 deletions(-)
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index d0d6d93549..b5680d046a 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -1,5 +1,5 @@
/*
- * 9p Posix callback
+ * 9p callback
*
* Copyright IBM, Corp. 2010
*
@@ -21,11 +21,13 @@
#include "9p-xattr.h"
#include "9p-util.h"
#include "fsdev/qemu-fsdev.h" /* local_ops */
+#ifndef CONFIG_WIN32
#include <arpa/inet.h>
#include <pwd.h>
#include <grp.h>
#include <sys/socket.h>
#include <sys/un.h>
+#endif
#include "qemu/xattr.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
@@ -38,7 +40,9 @@
#include <linux/magic.h>
#endif
#endif
+#ifndef CONFIG_WIN32
#include <sys/ioctl.h>
+#endif
#ifndef XFS_SUPER_MAGIC
#define XFS_SUPER_MAGIC 0x58465342
@@ -90,10 +94,12 @@ P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
return fd;
}
+#ifndef CONFIG_WIN32
P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path)
{
return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
}
+#endif
static void renameat_preserve_errno(P9_FILE_ID odirfd, const char *opath,
P9_FILE_ID ndirfd, const char *npath)
@@ -135,10 +141,20 @@ static FILE *local_fopenat(P9_FILE_ID dirfd, const char *name, const char *mode)
if (fd == P9_INVALID_FILE) {
return NULL;
}
+#ifdef CONFIG_WIN32
+ int _flags = mode[0] == 'r' ? O_RDONLY : 0;
+ int _fd = _open_osfhandle((intptr_t)fd, _flags);
+ assert(_fd != -1);
+ fp = fdopen(_fd, mode);
+ if (!fp) {
+ close(_fd);
+ }
+#else
fp = fdopen(fd, mode);
if (!fp) {
close(fd);
}
+#endif
return fp;
}
@@ -238,7 +254,7 @@ static int local_set_mapped_file_attrat(P9_FILE_ID dirfd, const char *name,
int ret;
char buf[ATTR_MAX];
int uid = -1, gid = -1, mode = -1, rdev = -1;
- P9_FILE_ID map_dirfd = P9_INVALID_FILE, map_fd;
+ P9_FILE_ID map_dirfd = P9_INVALID_FILE;
bool is_root = !strcmp(name, ".");
if (is_root) {
@@ -302,10 +318,12 @@ update_map_file:
return -1;
}
- map_fd = fileno(fp);
+#ifndef CONFIG_WIN32
+ P9_FILE_ID map_fd = fileno(fp);
assert(map_fd != P9_INVALID_FILE);
ret = fchmod(map_fd, 0600);
assert(ret == 0);
+#endif
if (credp->fc_uid != -1) {
uid = credp->fc_uid;
@@ -337,6 +355,7 @@ update_map_file:
return 0;
}
+#ifndef CONFIG_WIN32
static int fchmodat_nofollow(P9_FILE_ID dirfd, const char *name, mode_t mode)
{
struct stat stbuf;
@@ -399,6 +418,7 @@ static int fchmodat_nofollow(P9_FILE_ID dirfd, const char *name, mode_t mode)
close_preserve_errno(fd);
return ret;
}
+#endif
static int local_set_xattrat(P9_FILE_ID dirfd, const char *path, FsCred *credp)
{
@@ -439,6 +459,7 @@ static int local_set_xattrat(P9_FILE_ID dirfd, const char *path, FsCred *credp)
return 0;
}
+#ifndef CONFIG_WIN32
static int local_set_cred_passthrough(FsContext *fs_ctx, P9_FILE_ID dirfd,
const char *name, FsCred *credp)
{
@@ -455,10 +476,15 @@ static int local_set_cred_passthrough(FsContext *fs_ctx, P9_FILE_ID dirfd,
return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777);
}
+#endif
static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
char *buf, size_t bufsz)
{
+#ifdef CONFIG_WIN32
+ errno = ENOTSUP;
+ return -1;
+#else
ssize_t tsize = -1;
if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
@@ -491,6 +517,7 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
g_free(dirpath);
}
return tsize;
+#endif
}
static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
@@ -512,7 +539,13 @@ static int local_open(FsContext *ctx, V9fsPath *fs_path,
if (fd == P9_INVALID_FILE) {
return -1;
}
+#ifdef CONFIG_WIN32
+ int _fd = _open_osfhandle((intptr_t)fd, 0);
+ assert(_fd != -1);
+ fs->fd = _fd;
+#else
fs->fd = fd;
+#endif
return fs->fd;
}
@@ -527,9 +560,24 @@ static int local_opendir(FsContext *ctx,
return -1;
}
+#ifdef CONFIG_WIN32
+ char *full_file_name = get_full_path_win32(dirfd, NULL);
+ if (full_file_name == NULL) {
+ CloseHandle(dirfd);
+ return -1;
+ }
+ stream = opendir(full_file_name);
+ g_free(full_file_name);
+#else
stream = fdopendir(dirfd);
+#endif
+
if (!stream) {
+#ifdef CONFIG_WIN32
+ CloseHandle(dirfd);
+#else
close(dirfd);
+#endif
return -1;
}
fs->dir.stream = stream;
@@ -572,13 +620,17 @@ again:
#endif
if (ctx->export_flags & V9FS_SM_MAPPED) {
+#ifndef CONFIG_WIN32
entry->d_type = DT_UNKNOWN;
+#endif
} else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
if (local_is_mapped_file_metadata(ctx, entry->d_name)) {
/* skip the meta data */
goto again;
}
+#ifndef CONFIG_WIN32
entry->d_type = DT_UNKNOWN;
+#endif
}
return entry;
@@ -586,7 +638,94 @@ again:
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
{
+#ifndef CONFIG_WIN32
+ seekdir(fs->dir.stream, off);
+#else
+ 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 looks up current entry again, and compares the offset.
+ *
+ * If the new offset is less than the old offset, that means someone is
+ * deleting files in the directory, thus we need to seek offset backward.
+ *
+ * If the new offset is larger than the 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;
+ }
+ }
+
+ /* look up 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;
+ }
+ }
+ }
+ }
+
+ /* cannot get anything, seek backward */
+ off = off - 1;
seekdir(fs->dir.stream, off);
+
+out:
+ g_free(d_name[0]);
+ return;
+#endif
}
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
@@ -652,7 +791,12 @@ static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
ret = local_set_mapped_file_attrat(dirfd, name, credp);
} else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
fs_ctx->export_flags & V9FS_SM_NONE) {
+#ifdef CONFIG_WIN32
+ errno = ENOTSUP;
+ ret = -1;
+#else
ret = fchmodat_nofollow(dirfd, name, credp->fc_mode);
+#endif
}
close_preserve_errno(dirfd);
@@ -665,6 +809,10 @@ out:
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, FsCred *credp)
{
+#ifdef CONFIG_WIN32
+ errno = ENOTSUP;
+ return -1;
+#else
int err = -1;
P9_FILE_ID dirfd;
@@ -712,6 +860,7 @@ err_end:
out:
close_preserve_errno(dirfd);
return err;
+#endif
}
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
@@ -753,10 +902,12 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
if (err == -1) {
goto out;
}
+#ifndef CONFIG_WIN32
err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
if (err == -1) {
goto err_end;
}
+#endif
}
goto out;
@@ -773,7 +924,12 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
int err, fd;
if (fid_type == P9_FID_DIR) {
+#ifdef CONFIG_WIN32
+ errno = ENOTSUP;
+ return -1; /* Windows do not allow opening a directory by open() */
+#else
fd = dirfd(fs->dir.stream);
+#endif
} else {
fd = fs->fd;
}
@@ -825,10 +981,10 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
return -1;
}
- /*
- * Mark all the open to not follow symlinks
- */
+#ifndef CONFIG_WIN32
+ /* Mark all the open to not follow symlinks */
flags |= O_NOFOLLOW;
+#endif
dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
if (dirfd == P9_INVALID_FILE) {
@@ -858,13 +1014,22 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
if (fd == P9_INVALID_FILE) {
goto out;
}
+#ifndef CONFIG_WIN32
err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
if (err == -1) {
goto err_end;
}
+#endif
}
+#ifdef CONFIG_WIN32
+ int _fd = _open_osfhandle((intptr_t)fd, 0);
+ assert(_fd != -1);
+ err = _fd;
+ fs->fd = _fd;
+#else
err = fd;
fs->fd = fd;
+#endif
goto out;
err_end:
@@ -880,6 +1045,18 @@ out:
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
V9fsPath *dir_path, const char *name, FsCred *credp)
{
+#ifdef CONFIG_WIN32
+ /*
+ * Windows symbolic link requires administrator privilage.
+ * And Windows does not provide any interface like readlink().
+ * All symbolic links on Windows are always absolute paths.
+ * It's not 100% compatible with POSIX symbolic link.
+ *
+ * With above reasons, symbolic link feature is disabled on Windows host.
+ */
+ errno = ENOTSUP;
+ return -1;
+#else
int err = -1;
P9_FILE_ID dirfd;
@@ -953,11 +1130,16 @@ err_end:
out:
close_preserve_errno(dirfd);
return err;
+#endif
}
static int local_link(FsContext *ctx, V9fsPath *oldpath,
V9fsPath *dirpath, const char *name)
{
+#ifdef CONFIG_WIN32
+ errno = ENOTSUP;
+ return -1;
+#else
char *odirpath = g_path_get_dirname(oldpath->data);
char *oname = g_path_get_basename(oldpath->data);
int ret = -1;
@@ -1027,6 +1209,7 @@ out:
g_free(oname);
g_free(odirpath);
return ret;
+#endif
}
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
@@ -1038,7 +1221,13 @@ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
if (fd == P9_INVALID_FILE) {
return -1;
}
+#ifdef CONFIG_WIN32
+ int _fd = _open_osfhandle((intptr_t)fd, 0);
+ assert(_fd != -1);
+ ret = ftruncate(_fd, size);
+#else
ret = ftruncate(fd, size);
+#endif
close_preserve_errno(fd);
return ret;
}
@@ -1058,8 +1247,13 @@ static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
(fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
(fs_ctx->export_flags & V9FS_SM_NONE)) {
+#ifdef CONFIG_WIN32
+ errno = ENOTSUP;
+ ret = -1;
+#else
ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
AT_SYMLINK_NOFOLLOW);
+#endif
} else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
ret = local_set_xattrat(dirfd, name, credp);
} else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
@@ -1172,6 +1366,12 @@ out:
static int local_fsync(FsContext *ctx, int fid_type,
V9fsFidOpenState *fs, int datasync)
{
+#ifdef CONFIG_WIN32
+ if (fid_type != P9_FID_DIR) {
+ return _commit(fs->fd);
+ }
+ return 0;
+#else
int fd;
if (fid_type == P9_FID_DIR) {
@@ -1185,12 +1385,14 @@ static int local_fsync(FsContext *ctx, int fid_type,
} else {
return fsync(fd);
}
+#endif
}
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
{
- P9_FILE_ID fd;
int ret;
+#ifndef CONFIG_WIN32
+ P9_FILE_ID fd;
fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
if (fd == P9_INVALID_FILE) {
@@ -1198,39 +1400,65 @@ static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
}
ret = fstatfs(fd, stbuf);
close_preserve_errno(fd);
+#else
+ LocalData *data = (LocalData *)s->private;
+
+ ret = statfs_win32(data->root_path, stbuf);
+ if (ret == 0) {
+ /* use context address as fsid */
+ memcpy(&stbuf->f_fsid, s, sizeof(intptr_t));
+ }
+#endif
+
return ret;
}
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
const char *name, void *value, size_t size)
{
+#ifdef CONFIG_WIN32
+ return -1;
+#else
char *path = fs_path->data;
return v9fs_get_xattr(ctx, path, name, value, size);
+#endif
}
static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
void *value, size_t size)
{
+#ifdef CONFIG_WIN32
+ return -1;
+#else
char *path = fs_path->data;
return v9fs_list_xattr(ctx, path, value, size);
+#endif
}
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
void *value, size_t size, int flags)
{
+#ifdef CONFIG_WIN32
+ return -1;
+#else
char *path = fs_path->data;
return v9fs_set_xattr(ctx, path, name, value, size, flags);
+#endif
}
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
const char *name)
{
+#ifdef CONFIG_WIN32
+ return -1;
+#else
char *path = fs_path->data;
return v9fs_remove_xattr(ctx, path, name);
+#endif
}
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
@@ -1393,6 +1621,7 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
return ret;
}
+#ifndef CONFIG_WIN32
#ifdef FS_IOC_GETVERSION
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
mode_t st_mode, uint64_t *st_gen)
@@ -1442,11 +1671,72 @@ static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **er
#endif
return 0;
}
+#endif
-static int local_init(FsContext *ctx, Error **errp)
+#ifdef CONFIG_WIN32
+static int init_win32_root_directory(FsContext *ctx, LocalData *data,
+ Error **errp)
{
- LocalData *data = g_malloc(sizeof(*data));
+ HANDLE mountfd;
+ char *root_path;
+
+ mountfd = CreateFile(ctx->fs_root, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (mountfd == INVALID_HANDLE_VALUE) {
+ error_setg_errno(errp, EINVAL, "cannot open %s", ctx->fs_root);
+ return -1;
+ }
+
+ if ((ctx->export_flags & V9FS_SM_MAPPED) != 0) {
+ wchar_t fs_name[MAX_PATH + 1] = {0};
+ wchar_t ntfs_name[5] = {'N', 'T', 'F', 'S'};
+
+ /* Get file system type name */
+ if (GetVolumeInformationByHandleW(mountfd, NULL, 0, NULL, NULL, NULL,
+ fs_name, MAX_PATH + 1) == 0) {
+ error_setg_errno(errp, EINVAL,
+ "cannot get file system information");
+ CloseHandle(mountfd);
+ return -1;
+ }
+ /*
+ * 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 (wcscmp(fs_name, ntfs_name) != 0) {
+ CloseHandle(mountfd);
+ error_setg_errno(errp, EINVAL, "require NTFS file system");
+ return -1;
+ }
+ }
+
+ root_path = get_full_path_win32(mountfd, NULL);
+ if (root_path == NULL) {
+ CloseHandle(mountfd);
+ error_setg_errno(errp, EINVAL, "cannot get full root path");
+ return -1;
+ }
+
+ /*
+ * hold the root handle will prevent other one to delete or replace the
+ * root directory during runtime.
+ */
+ data->mountfd = mountfd;
+ data->root_path = root_path;
+
+ return 0;
+}
+#endif
+
+static int local_init(FsContext *ctx, Error **errp)
+{
+ LocalData *data = g_malloc0(sizeof(*data));
+#ifndef CONFIG_WIN32
data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
if (data->mountfd == -1) {
error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root);
@@ -1457,7 +1747,17 @@ static int local_init(FsContext *ctx, Error **errp)
close(data->mountfd);
goto err;
}
+#else
+ if (init_win32_root_directory(ctx, data, errp) != 0) {
+ goto err;
+ }
+ /*
+ * Always enable inode remap since Windows file system does not
+ * have inode number.
+ */
+ ctx->export_flags |= V9FS_REMAP_INODES;
+#endif
if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
ctx->xops = passthrough_xattr_ops;
} else if (ctx->export_flags & V9FS_SM_MAPPED) {
@@ -1477,6 +1777,15 @@ static int local_init(FsContext *ctx, Error **errp)
return 0;
err:
+#ifdef CONFIG_WIN32
+ if (data->root_path != NULL) {
+ g_free(data->root_path);
+ }
+
+ if (data->mountfd != 0 && data->mountfd != INVALID_HANDLE_VALUE) {
+ CloseHandle(data->mountfd);
+ }
+#endif
g_free(data);
return -1;
}
@@ -1488,8 +1797,17 @@ static void local_cleanup(FsContext *ctx)
if (!data) {
return;
}
+#ifdef CONFIG_WIN32
+ if (data->root_path != NULL) {
+ g_free(data->root_path);
+ }
+ if (data->mountfd != 0 && data->mountfd != INVALID_HANDLE_VALUE) {
+ CloseHandle(data->mountfd);
+ }
+#else
close(data->mountfd);
+#endif
g_free(data);
}
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 11/16] hw/9pfs: Add Linux error number definition
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (9 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 10/16] hw/9pfs: Update the local fs driver to support Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-11-01 15:21 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 12/16] hw/9pfs: Translate Windows errno to Linux value Bin Meng
` (5 subsequent siblings)
16 siblings, 1 reply; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
When using 9p2000.L protocol, the errno should use the Linux errno.
Currently magic numbers with comments are used. Replace these with
macros for future expansion.
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-util.h | 38 ++++++----
2 files changed, 176 insertions(+), 13 deletions(-)
create mode 100644 hw/9pfs/9p-linux-errno.h
diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h
new file mode 100644
index 0000000000..56c37fa293
--- /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 non-Linux hosts) 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-util.h b/hw/9pfs/9p-util.h
index 3d154e9103..281fdcbf8c 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -62,8 +62,11 @@ static inline uint64_t host_dev_to_dotl_dev(dev_t dev)
#endif
}
+#include "9p-linux-errno.h"
+
/* Translates errno from host -> Linux if needed */
-static inline int errno_to_dotl(int err) {
+static inline int errno_to_dotl(int err)
+{
#if defined(CONFIG_LINUX)
/* nothing to translate (Linux -> Linux) */
#elif defined(CONFIG_DARWIN)
@@ -73,18 +76,27 @@ static inline int errno_to_dotl(int err) {
* FIXME: Only most important errnos translated here yet, this should be
* extended to as many errnos being translated as possible in future.
*/
- if (err == ENAMETOOLONG) {
- err = 36; /* ==ENAMETOOLONG on Linux */
- } else if (err == ENOTEMPTY) {
- err = 39; /* ==ENOTEMPTY on Linux */
- } else if (err == ELOOP) {
- err = 40; /* ==ELOOP on Linux */
- } else if (err == ENOATTR) {
- err = 61; /* ==ENODATA on Linux */
- } else if (err == ENOTSUP) {
- err = 95; /* ==EOPNOTSUPP on Linux */
- } else if (err == EOPNOTSUPP) {
- err = 95; /* ==EOPNOTSUPP on Linux */
+ switch (err) {
+ case ENAMETOOLONG:
+ err = L_ENAMETOOLONG;
+ break;
+ case ENOTEMPTY:
+ err = L_ENOTEMPTY;
+ break;
+ case ELOOP:
+ err = L_ELOOP;
+ break;
+ case ENOATTR:
+ err = L_ENODATA;
+ break;
+ case ENOTSUP
+ err = L_EOPNOTSUPP;
+ break;
+ case EOPNOTSUPP:
+ err = L_EOPNOTSUPP;
+ break;
+ default:
+ break;
}
#else
#error Missing errno translation to Linux for this host system
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 12/16] hw/9pfs: Translate Windows errno to Linux value
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (10 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 11/16] hw/9pfs: Add Linux error number definition Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 13/16] fsdev: Disable proxy fs driver on Windows Bin Meng
` (4 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
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 translation 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.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
hw/9pfs/9p-util.h | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 281fdcbf8c..145a3117dc 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -69,9 +69,9 @@ static inline int errno_to_dotl(int err)
{
#if defined(CONFIG_LINUX)
/* nothing to translate (Linux -> Linux) */
-#elif defined(CONFIG_DARWIN)
+#elif defined(CONFIG_DARWIN) || defined(CONFIG_WIN32)
/*
- * translation mandatory for macOS hosts
+ * translation mandatory for different hosts
*
* FIXME: Only most important errnos translated here yet, this should be
* extended to as many errnos being translated as possible in future.
@@ -86,6 +86,7 @@ static inline int errno_to_dotl(int err)
case ELOOP:
err = L_ELOOP;
break;
+#ifdef CONFIG_DARWIN
case ENOATTR:
err = L_ENODATA;
break;
@@ -95,6 +96,21 @@ static inline int errno_to_dotl(int err)
case EOPNOTSUPP:
err = L_EOPNOTSUPP;
break;
+#endif
+#ifdef CONFIG_WIN32
+ case EDEADLK:
+ err = L_EDEADLK;
+ break;
+ case ENOLCK:
+ err = L_ENOLCK;
+ break;
+ case ENOSYS:
+ err = L_ENOSYS;
+ break;
+ case EILSEQ:
+ err = L_EILSEQ;
+ break;
+#endif
default:
break;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 13/16] fsdev: Disable proxy fs driver on Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (11 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 12/16] hw/9pfs: Translate Windows errno to Linux value Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 14/16] hw/9pfs: Update synth fs driver for Windows Bin Meng
` (3 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
We don't plan to support 'proxy' file system driver for 9pfs on
Windows. Disable it for Windows build.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
fsdev/qemu-fsdev.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index 3da64e9f72..58e0710fbb 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -89,6 +89,7 @@ static FsDriverTable FsDrivers[] = {
NULL
},
},
+#ifndef CONFIG_WIN32
{
.name = "proxy",
.ops = &proxy_ops,
@@ -100,6 +101,7 @@ static FsDriverTable FsDrivers[] = {
NULL
},
},
+#endif
};
static int validate_opt(void *opaque, const char *name, const char *value,
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 14/16] hw/9pfs: Update synth fs driver for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (12 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 13/16] fsdev: Disable proxy fs driver on Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-24 4:57 ` [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32 Bin Meng
` (2 subsequent siblings)
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
From: Guohuai Shi <guohuai.shi@windriver.com>
Adapt synth fs driver for Windows in preparation to running qtest
9p testing on Windows.
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 1c5813e4dd..6d33eb1cf3 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] 40+ messages in thread
* [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (13 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 14/16] hw/9pfs: Update synth fs driver for Windows Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-25 15:55 ` Thomas Huth
2022-11-01 15:32 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 16/16] meson.build: Turn on virtfs for Windows Bin Meng
2022-10-27 16:19 ` [PATCH 00/16] hw/9pfs: Add 9pfs support " Bin Meng
16 siblings, 2 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel
Cc: Guohuai Shi, Xuzhou Cheng, Christian Schoenebeck, Greg Kurz,
Laurent Vivier, Paolo Bonzini, Thomas Huth
From: Guohuai Shi <guohuai.shi@windriver.com>
Windows does not provide the getuid() API. Let's create a local
one and return a fixed value 0 as the uid for testing.
Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
Signed-off-by: Xuzhou Cheng <xuzhou.cheng@windriver.com>
Signed-off-by: Bin Meng <bin.meng@windriver.com>
---
tests/qtest/virtio-9p-test.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
index 25305a4cf7..e81e3e3709 100644
--- a/tests/qtest/virtio-9p-test.c
+++ b/tests/qtest/virtio-9p-test.c
@@ -34,6 +34,13 @@ static uint32_t genfid(void)
return fid_generator++;
}
+#ifdef CONFIG_WIN32
+static uint32_t getuid(void)
+{
+ return 0;
+}
+#endif
+
/**
* Splits the @a in string by @a delim into individual (non empty) strings
* and outputs them to @a out. The output array @a out is NULL terminated.
--
2.25.1
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 16/16] meson.build: Turn on virtfs for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (14 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32 Bin Meng
@ 2022-10-24 4:57 ` Bin Meng
2022-10-27 16:19 ` [PATCH 00/16] hw/9pfs: Add 9pfs support " Bin Meng
16 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-24 4:57 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Christian Schoenebeck, Greg Kurz
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 +++++-----
fsdev/meson.build | 1 +
hw/9pfs/meson.build | 8 +++++---
3 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/meson.build b/meson.build
index b686dfef75..4c23d18083 100644
--- a/meson.build
+++ b/meson.build
@@ -1728,16 +1728,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
if get_option('block_drv_ro_whitelist') == ''
config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '')
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',
diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
index 12443b6ad5..aaa50e71f7 100644
--- a/hw/9pfs/meson.build
+++ b/hw/9pfs/meson.build
@@ -2,7 +2,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 +12,11 @@ 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-proxy.c',
+ '9p-util-linux.c'))
+fs_ss.add(when: 'CONFIG_DARWIN', if_true: files('9p-proxy.c',
+ '9p-util-darwin.c'))
+fs_ss.add(when: 'CONFIG_WIN32', if_true: files('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] 40+ messages in thread
* Re: [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32
2022-10-24 4:57 ` [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32 Bin Meng
@ 2022-10-25 15:55 ` Thomas Huth
2022-11-01 15:32 ` Christian Schoenebeck
1 sibling, 0 replies; 40+ messages in thread
From: Thomas Huth @ 2022-10-25 15:55 UTC (permalink / raw)
To: Bin Meng, qemu-devel
Cc: Guohuai Shi, Xuzhou Cheng, Christian Schoenebeck, Greg Kurz,
Laurent Vivier, Paolo Bonzini
On 24/10/2022 06.57, Bin Meng wrote:
> From: Guohuai Shi <guohuai.shi@windriver.com>
>
> Windows does not provide the getuid() API. Let's create a local
> one and return a fixed value 0 as the uid for testing.
>
> Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> Signed-off-by: Xuzhou Cheng <xuzhou.cheng@windriver.com>
> Signed-off-by: Bin Meng <bin.meng@windriver.com>
> ---
>
> tests/qtest/virtio-9p-test.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
> index 25305a4cf7..e81e3e3709 100644
> --- a/tests/qtest/virtio-9p-test.c
> +++ b/tests/qtest/virtio-9p-test.c
> @@ -34,6 +34,13 @@ static uint32_t genfid(void)
> return fid_generator++;
> }
>
> +#ifdef CONFIG_WIN32
> +static uint32_t getuid(void)
> +{
> + return 0;
> +}
> +#endif
> +
> /**
> * Splits the @a in string by @a delim into individual (non empty) strings
> * and outputs them to @a out. The output array @a out is NULL terminated.
Acked-by: Thomas Huth <thuth@redhat.com>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
` (15 preceding siblings ...)
2022-10-24 4:57 ` [PATCH 16/16] meson.build: Turn on virtfs for Windows Bin Meng
@ 2022-10-27 16:19 ` Bin Meng
2022-10-27 16:30 ` Christian Schoenebeck
16 siblings, 1 reply; 40+ messages in thread
From: Bin Meng @ 2022-10-27 16:19 UTC (permalink / raw)
To: Christian Schoenebeck
Cc: qemu-devel, Greg Kurz, Keno Fischer, Laurent Vivier,
Paolo Bonzini, Philippe Mathieu-Daudé,
Thomas Huth, Will Cohen, Bin Meng
Hi Christian,
On Mon, Oct 24, 2022 at 1:16 PM Bin Meng <bin.meng@windriver.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' driver is adapted for Windows too so that we can now
> run qtests on Windows for 9p related regression testing.
>
> 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 (5):
> qemu/xattr.h: Exclude <sys/xattr.h> for Windows
> hw/9pfs: Drop unnecessary *xattr wrapper API declarations
> hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper
> hw/9pfs: Introduce an opaque type 9P_FILE_ID
> hw/9pfs: Update P9_FILE_ID to support Windows
>
> Guohuai Shi (11):
> hw/9pfs: Add missing definitions for Windows
> hw/9pfs: Implement Windows specific utilities functions for 9pfs
> hw/9pfs: Handle current directory offset for Windows
> hw/9pfs: Disable unsupported flags and features for Windows
> hw/9pfs: Update the local fs driver to support Windows
> hw/9pfs: Add Linux error number definition
> hw/9pfs: Translate Windows errno to Linux value
> fsdev: Disable proxy fs driver on Windows
> hw/9pfs: Update synth fs driver for Windows
> tests/qtest: virtio-9p-test: Adapt the case for win32
> meson.build: Turn on virtfs for Windows
>
With the latest 9p test case refactoring in the mainline, I will have
to cherry-pick the following 2 patches in this series, to v6 of
"tests/qtest: Enable running qtest on Windows" series [1], in order to
get qtest on Windows build successfully.
[06/16] hw/9pfs: Add missing definitions for Windows
[15/16] tests/qtest: virtio-9p-test: Adapt the case for win32
I will include the above 2 patches in the v6 qtest windows support series.
[1] http://patchwork.ozlabs.org/project/qemu-devel/list/?series=321695
Regards,
Bin
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows
2022-10-27 16:19 ` [PATCH 00/16] hw/9pfs: Add 9pfs support " Bin Meng
@ 2022-10-27 16:30 ` Christian Schoenebeck
2022-10-28 2:25 ` Bin Meng
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-10-27 16:30 UTC (permalink / raw)
To: Bin Meng
Cc: qemu-devel, Greg Kurz, Keno Fischer, Laurent Vivier,
Paolo Bonzini, Philippe Mathieu-Daudé,
Thomas Huth, Will Cohen, Bin Meng
On Thursday, October 27, 2022 6:19:27 PM CEST Bin Meng wrote:
> Hi Christian,
>
> On Mon, Oct 24, 2022 at 1:16 PM Bin Meng <bin.meng@windriver.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' driver is adapted for Windows too so that we can now
> > run qtests on Windows for 9p related regression testing.
> >
> > 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 (5):
> > qemu/xattr.h: Exclude <sys/xattr.h> for Windows
> > hw/9pfs: Drop unnecessary *xattr wrapper API declarations
> > hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper
> > hw/9pfs: Introduce an opaque type 9P_FILE_ID
> > hw/9pfs: Update P9_FILE_ID to support Windows
> >
> > Guohuai Shi (11):
> > hw/9pfs: Add missing definitions for Windows
> > hw/9pfs: Implement Windows specific utilities functions for 9pfs
> > hw/9pfs: Handle current directory offset for Windows
> > hw/9pfs: Disable unsupported flags and features for Windows
> > hw/9pfs: Update the local fs driver to support Windows
> > hw/9pfs: Add Linux error number definition
> > hw/9pfs: Translate Windows errno to Linux value
> > fsdev: Disable proxy fs driver on Windows
> > hw/9pfs: Update synth fs driver for Windows
> > tests/qtest: virtio-9p-test: Adapt the case for win32
> > meson.build: Turn on virtfs for Windows
> >
>
> With the latest 9p test case refactoring in the mainline, I will have
> to cherry-pick the following 2 patches in this series, to v6 of
> "tests/qtest: Enable running qtest on Windows" series [1], in order to
> get qtest on Windows build successfully.
>
> [06/16] hw/9pfs: Add missing definitions for Windows
> [15/16] tests/qtest: virtio-9p-test: Adapt the case for win32
>
> I will include the above 2 patches in the v6 qtest windows support series.
No need to add those patches as they are already being queued separately. Just
add appropriate tag(s) to the first patch:
Based-on: <MESSAGE-ID>
I already had a quick look on this version, will try to give feedback
tomorrow.
This feature won't make it into 7.2 release anyway, so patience please. ;-)
Best regards,
Christian Schoenebeck
> [1] http://patchwork.ozlabs.org/project/qemu-devel/list/?series=321695
>
> Regards,
> Bin
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows
2022-10-27 16:30 ` Christian Schoenebeck
@ 2022-10-28 2:25 ` Bin Meng
0 siblings, 0 replies; 40+ messages in thread
From: Bin Meng @ 2022-10-28 2:25 UTC (permalink / raw)
To: Christian Schoenebeck
Cc: qemu-devel, Greg Kurz, Keno Fischer, Laurent Vivier,
Paolo Bonzini, Philippe Mathieu-Daudé,
Thomas Huth, Will Cohen, Bin Meng
On Fri, Oct 28, 2022 at 12:31 AM Christian Schoenebeck
<qemu_oss@crudebyte.com> wrote:
>
> On Thursday, October 27, 2022 6:19:27 PM CEST Bin Meng wrote:
> > Hi Christian,
> >
> > On Mon, Oct 24, 2022 at 1:16 PM Bin Meng <bin.meng@windriver.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' driver is adapted for Windows too so that we can now
> > > run qtests on Windows for 9p related regression testing.
> > >
> > > 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 (5):
> > > qemu/xattr.h: Exclude <sys/xattr.h> for Windows
> > > hw/9pfs: Drop unnecessary *xattr wrapper API declarations
> > > hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper
> > > hw/9pfs: Introduce an opaque type 9P_FILE_ID
> > > hw/9pfs: Update P9_FILE_ID to support Windows
> > >
> > > Guohuai Shi (11):
> > > hw/9pfs: Add missing definitions for Windows
> > > hw/9pfs: Implement Windows specific utilities functions for 9pfs
> > > hw/9pfs: Handle current directory offset for Windows
> > > hw/9pfs: Disable unsupported flags and features for Windows
> > > hw/9pfs: Update the local fs driver to support Windows
> > > hw/9pfs: Add Linux error number definition
> > > hw/9pfs: Translate Windows errno to Linux value
> > > fsdev: Disable proxy fs driver on Windows
> > > hw/9pfs: Update synth fs driver for Windows
> > > tests/qtest: virtio-9p-test: Adapt the case for win32
> > > meson.build: Turn on virtfs for Windows
> > >
> >
> > With the latest 9p test case refactoring in the mainline, I will have
> > to cherry-pick the following 2 patches in this series, to v6 of
> > "tests/qtest: Enable running qtest on Windows" series [1], in order to
> > get qtest on Windows build successfully.
> >
> > [06/16] hw/9pfs: Add missing definitions for Windows
> > [15/16] tests/qtest: virtio-9p-test: Adapt the case for win32
> >
> > I will include the above 2 patches in the v6 qtest windows support series.
>
> No need to add those patches as they are already being queued separately. Just
> add appropriate tag(s) to the first patch:
>
> Based-on: <MESSAGE-ID>
Sure, but I really want to get the qtest windows support patch series
merged first.
I will disable the 9p test for Windows in my v6 then. We will enable
the 9p test later when it lands on the mainline.
>
> I already had a quick look on this version, will try to give feedback
> tomorrow.
>
> This feature won't make it into 7.2 release anyway, so patience please. ;-)
>
> Best regards,
> Christian Schoenebeck
>
> > [1] http://patchwork.ozlabs.org/project/qemu-devel/list/?series=321695
Regards,
Bin
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/16] hw/9pfs: Introduce an opaque type 9P_FILE_ID
2022-10-24 4:57 ` [PATCH 04/16] hw/9pfs: Introduce an opaque type 9P_FILE_ID Bin Meng
@ 2022-11-01 13:55 ` Christian Schoenebeck
0 siblings, 0 replies; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 13:55 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Bin Meng
On Monday, October 24, 2022 6:57:47 AM CET Bin Meng wrote:
> Introduce an opaque type to represent a file in the 9pfs. This is
> file descriptor on POSIX systems. In the upcoming patches, we can
> extend it to support Windows.
>
> With this new opaque type, it significantly reduces the number of
> deviated code paths when adding Windows support.
Strictly it is not an opaque type as you still have direct access to the
original value, not that I would care about this comment here, but ...
> Signed-off-by: Bin Meng <bin.meng@windriver.com>
> ---
>
> hw/9pfs/9p-file-id.h | 21 +++++
> hw/9pfs/9p-local.h | 8 +-
> hw/9pfs/9p-util.h | 28 ++++---
> hw/9pfs/9p-local.c | 166 ++++++++++++++++++++-------------------
> hw/9pfs/9p-util-darwin.c | 14 ++--
> hw/9pfs/9p-util-linux.c | 14 ++--
> hw/9pfs/9p-xattr.c | 16 ++--
> 7 files changed, 150 insertions(+), 117 deletions(-)
> create mode 100644 hw/9pfs/9p-file-id.h
>
> diff --git a/hw/9pfs/9p-file-id.h b/hw/9pfs/9p-file-id.h
> new file mode 100644
> index 0000000000..60cbfbf4dd
> --- /dev/null
> +++ b/hw/9pfs/9p-file-id.h
> @@ -0,0 +1,21 @@
> +/*
> + * 9p file representation for different hosts
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef QEMU_9P_FILE_ID_H
> +#define QEMU_9P_FILE_ID_H
> +
> +/*
> + * 9pfs file id
> + *
> + * This is file descriptor on POSIX platforms
> + */
> +typedef int P9_FILE_ID;
I would not call it a "file ID" as that's a value identical for all streams
and processes. A correct term was either file descriptor or a file handle.
And as far as I can see, common QEMU code style suggests camel-case. So
instead maybe "QemuFileDescr_t" or just "QemuFd_t" to not confuse that with
9p protocol stuff?
> +
> +/* invalid value for P9_FILE_ID */
> +#define P9_INVALID_FILE -1
... I would rather either add a function like qemu_fd_invalid() or a function-
like macro as QEMU_FD_INVALID(), because that would also work with some exotic
systems that use fully opaque file descriptors that require either a function
call or dereferencing a struct member. So that would make this code more
future proof.
> +
> +#endif
> diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h
> index 32c72749d9..c8404063e5 100644
> --- a/hw/9pfs/9p-local.h
> +++ b/hw/9pfs/9p-local.h
> @@ -13,8 +13,10 @@
> #ifndef QEMU_9P_LOCAL_H
> #define QEMU_9P_LOCAL_H
>
> -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);
> +#include "9p-file-id.h"
> +
> +P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
> + mode_t mode);
> +P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path);
>
> #endif
> diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
> index c314cf381d..1e7dc76345 100644
> --- a/hw/9pfs/9p-util.h
> +++ b/hw/9pfs/9p-util.h
> @@ -13,6 +13,8 @@
> #ifndef QEMU_9P_UTIL_H
> #define QEMU_9P_UTIL_H
>
> +#include "9p-file-id.h"
> +
> #ifdef O_PATH
> #define O_PATH_9P_UTIL O_PATH
> #else
> @@ -101,30 +103,31 @@ static inline int errno_to_dotl(int err) {
> #define qemu_utimensat utimensat
> #define qemu_unlinkat unlinkat
>
> -static inline void close_preserve_errno(int fd)
> +static inline void close_preserve_errno(P9_FILE_ID fd)
> {
> int serrno = errno;
> close(fd);
> errno = serrno;
> }
>
> -static inline int openat_dir(int dirfd, const char *name)
> +static inline P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name)
> {
> return qemu_openat(dirfd, name,
> O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL);
> }
>
> -static inline int openat_file(int dirfd, const char *name, int flags,
> - mode_t mode)
> +static inline P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name,
> + int flags, mode_t mode)
> {
> - int fd, serrno, ret;
> + int serrno, ret;
> + P9_FILE_ID fd;
>
> #ifndef CONFIG_DARWIN
> again:
> #endif
> fd = qemu_openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
> mode);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
So these checks would become something like:
if (QEMU_FD_INVALID(fd)) {
> #ifndef CONFIG_DARWIN
> if (errno == EPERM && (flags & O_NOATIME)) {
> /*
> @@ -155,13 +158,13 @@ again:
> return fd;
> }
>
> -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,
> +ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> + const char *name, void *value, size_t size);
> +int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *path, const char *name,
> void *value, size_t size, int flags);
> -ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> +ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> char *list, size_t size);
> -ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> +ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> const char *name);
>
> /*
> @@ -219,6 +222,7 @@ 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
> -int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev);
> +int qemu_mknodat(P9_FILE_ID dirfd, const char *filename, mode_t mode,
> + dev_t dev);
>
> #endif
> diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
> index d2246a3d7e..7e8d8492ea 100644
> --- a/hw/9pfs/9p-local.c
> +++ b/hw/9pfs/9p-local.c
> @@ -54,18 +54,18 @@
> #endif
>
> typedef struct {
> - int mountfd;
> + P9_FILE_ID mountfd;
> } LocalData;
>
> -int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
> - mode_t mode)
> +P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
> + mode_t mode)
> {
> LocalData *data = fs_ctx->private;
> - int fd = data->mountfd;
> + P9_FILE_ID fd = data->mountfd;
>
> - while (*path && fd != -1) {
> + while (*path && fd != P9_INVALID_FILE) {
> const char *c;
> - int next_fd;
> + P9_FILE_ID next_fd;
> char *head;
>
> /* Only relative paths without consecutive slashes */
> @@ -94,20 +94,21 @@ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
> return fd;
> }
>
> -int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
> +P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path)
> {
> return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
> }
>
> -static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
> - const char *npath)
> +static void renameat_preserve_errno(P9_FILE_ID odirfd, const char *opath,
> + P9_FILE_ID ndirfd, const char *npath)
> {
> int serrno = errno;
> qemu_renameat(odirfd, opath, ndirfd, npath);
> errno = serrno;
> }
>
> -static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
> +static void unlinkat_preserve_errno(P9_FILE_ID dirfd, const char *path,
> + int flags)
> {
> int serrno = errno;
> qemu_unlinkat(dirfd, path, flags);
> @@ -117,9 +118,10 @@ static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
> #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)
> +static FILE *local_fopenat(P9_FILE_ID dirfd, const char *name, const char *mode)
> {
> - int fd, o_mode = 0;
> + P9_FILE_ID fd;
> + int o_mode = 0;
> FILE *fp;
> int flags;
> /*
> @@ -134,7 +136,7 @@ static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
> return NULL;
> }
> fd = openat_file(dirfd, name, flags, o_mode);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return NULL;
> }
> fp = fdopen(fd, mode);
> @@ -145,16 +147,16 @@ static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
> }
>
> #define ATTR_MAX 100
> -static void local_mapped_file_attr(int dirfd, const char *name,
> +static void local_mapped_file_attr(P9_FILE_ID dirfd, const char *name,
> struct stat *stbuf)
> {
> FILE *fp;
> char buf[ATTR_MAX];
> - int map_dirfd;
> + P9_FILE_ID map_dirfd;
>
> if (strcmp(name, ".")) {
> map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
> - if (map_dirfd == -1) {
> + if (map_dirfd == P9_INVALID_FILE) {
> return;
> }
>
> @@ -187,10 +189,10 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
> int err = -1;
> char *dirpath = g_path_get_dirname(fs_path->data);
> char *name = g_path_get_basename(fs_path->data);
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> dirfd = local_opendir_nofollow(fs_ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -233,14 +235,14 @@ out:
> return err;
> }
>
> -static int local_set_mapped_file_attrat(int dirfd, const char *name,
> +static int local_set_mapped_file_attrat(P9_FILE_ID dirfd, const char *name,
> FsCred *credp)
> {
> FILE *fp;
> int ret;
> char buf[ATTR_MAX];
> int uid = -1, gid = -1, mode = -1, rdev = -1;
> - int map_dirfd = -1, map_fd;
> + P9_FILE_ID map_dirfd = P9_INVALID_FILE, map_fd;
> bool is_root = !strcmp(name, ".");
>
> if (is_root) {
> @@ -259,7 +261,7 @@ static int local_set_mapped_file_attrat(int dirfd, const char *name,
> }
>
> map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
> - if (map_dirfd == -1) {
> + if (map_dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -296,7 +298,7 @@ update_map_file:
> /* We can't go this far with map_dirfd not being a valid file descriptor
> * but some versions of gcc aren't smart enough to see it.
> */
> - if (map_dirfd != -1) {
> + if (map_dirfd != P9_INVALID_FILE) {
> close_preserve_errno(map_dirfd);
> }
> }
> @@ -305,7 +307,7 @@ update_map_file:
> }
>
> map_fd = fileno(fp);
> - assert(map_fd != -1);
> + assert(map_fd != P9_INVALID_FILE);
> ret = fchmod(map_fd, 0600);
> assert(ret == 0);
>
> @@ -339,10 +341,11 @@ update_map_file:
> return 0;
> }
>
> -static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
> +static int fchmodat_nofollow(P9_FILE_ID dirfd, const char *name, mode_t mode)
> {
> struct stat stbuf;
> - int fd, ret;
> + P9_FILE_ID fd;
> + int ret;
>
> /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
> * Unfortunately, the linux kernel doesn't implement it yet.
> @@ -362,16 +365,16 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
> /* Fallback for systems that don't support O_PATH: we depend on the file
> * being readable or writable.
> */
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> /* In case the file is writable-only and isn't a directory. */
> if (errno == EACCES) {
> fd = openat_file(dirfd, name, O_WRONLY, 0);
> }
> - if (fd == -1 && errno == EISDIR) {
> + if (fd == P9_INVALID_FILE && errno == EISDIR) {
> errno = EACCES;
> }
> }
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return -1;
> }
> ret = fchmod(fd, mode);
> @@ -380,7 +383,7 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
> * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
> * referring to the symbolic link.
> */
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -401,7 +404,7 @@ static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
> return ret;
> }
>
> -static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
> +static int local_set_xattrat(P9_FILE_ID dirfd, const char *path, FsCred *credp)
> {
> int err;
>
> @@ -440,7 +443,7 @@ static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
> return 0;
> }
>
> -static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
> +static int local_set_cred_passthrough(FsContext *fs_ctx, P9_FILE_ID dirfd,
> const char *name, FsCred *credp)
> {
> if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
> @@ -478,10 +481,10 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
> (fs_ctx->export_flags & V9FS_SM_NONE)) {
> char *dirpath = g_path_get_dirname(fs_path->data);
> char *name = g_path_get_basename(fs_path->data);
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> dirfd = local_opendir_nofollow(fs_ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -507,10 +510,10 @@ static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
> static int local_open(FsContext *ctx, V9fsPath *fs_path,
> int flags, V9fsFidOpenState *fs)
> {
> - int fd;
> + P9_FILE_ID fd;
>
> fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return -1;
> }
> fs->fd = fd;
> @@ -520,11 +523,11 @@ static int local_open(FsContext *ctx, V9fsPath *fs_path,
> static int local_opendir(FsContext *ctx,
> V9fsPath *fs_path, V9fsFidOpenState *fs)
> {
> - int dirfd;
> + P9_FILE_ID dirfd;
> DIR *stream;
>
> dirfd = local_opendir_nofollow(ctx, fs_path->data);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -640,10 +643,10 @@ 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;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> dirfd = local_opendir_nofollow(fs_ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -667,7 +670,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
> const char *name, FsCred *credp)
> {
> int err = -1;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> local_is_mapped_file_metadata(fs_ctx, name)) {
> @@ -676,7 +679,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
> }
>
> dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -719,7 +722,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
> const char *name, FsCred *credp)
> {
> int err = -1;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> local_is_mapped_file_metadata(fs_ctx, name)) {
> @@ -728,7 +731,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
> }
>
> dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -816,9 +819,9 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
> static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
> int flags, FsCred *credp, V9fsFidOpenState *fs)
> {
> - int fd = -1;
> + P9_FILE_ID fd = P9_INVALID_FILE;
> int err = -1;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> local_is_mapped_file_metadata(fs_ctx, name)) {
> @@ -832,7 +835,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
> flags |= O_NOFOLLOW;
>
> dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -840,7 +843,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
> if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
> fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
> fd = openat_file(dirfd, name, flags, fs_ctx->fmode);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> goto out;
> }
> credp->fc_mode = credp->fc_mode | S_IFREG;
> @@ -856,7 +859,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
> } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
> (fs_ctx->export_flags & V9FS_SM_NONE)) {
> fd = openat_file(dirfd, name, flags, credp->fc_mode);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> goto out;
> }
> err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
> @@ -882,7 +885,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
> V9fsPath *dir_path, const char *name, FsCred *credp)
> {
> int err = -1;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> local_is_mapped_file_metadata(fs_ctx, name)) {
> @@ -891,7 +894,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
> }
>
> dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -903,7 +906,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
>
> fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
> fs_ctx->fmode);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> goto out;
> }
> /* Write the oldpath (target) to the file. */
> @@ -962,7 +965,7 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
> char *odirpath = g_path_get_dirname(oldpath->data);
> char *oname = g_path_get_basename(oldpath->data);
> int ret = -1;
> - int odirfd, ndirfd;
> + P9_FILE_ID odirfd, ndirfd;
>
> if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> local_is_mapped_file_metadata(ctx, name)) {
> @@ -971,12 +974,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
> }
>
> odirfd = local_opendir_nofollow(ctx, odirpath);
> - if (odirfd == -1) {
> + if (odirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> ndirfd = local_opendir_nofollow(ctx, dirpath->data);
> - if (ndirfd == -1) {
> + if (ndirfd == P9_INVALID_FILE) {
> close_preserve_errno(odirfd);
> goto out;
> }
> @@ -996,12 +999,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
> }
>
> omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
> - if (omap_dirfd == -1) {
> + if (omap_dirfd == P9_INVALID_FILE) {
> goto err;
> }
>
> nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
> - if (nmap_dirfd == -1) {
> + if (nmap_dirfd == P9_INVALID_FILE) {
> close_preserve_errno(omap_dirfd);
> goto err;
> }
> @@ -1032,10 +1035,11 @@ out:
>
> static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
> {
> - int fd, ret;
> + P9_FILE_ID fd;
> + int ret;
>
> fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return -1;
> }
> ret = ftruncate(fd, size);
> @@ -1048,10 +1052,10 @@ static int local_chown(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;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> dirfd = local_opendir_nofollow(fs_ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -1078,10 +1082,11 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
> {
> char *dirpath = g_path_get_dirname(fs_path->data);
> char *name = g_path_get_basename(fs_path->data);
> - int dirfd, ret = -1;
> + P9_FILE_ID dirfd;
> + int ret = -1;
>
> dirfd = local_opendir_nofollow(s, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -1093,13 +1098,13 @@ out:
> return ret;
> }
>
> -static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
> - int flags)
> +static int local_unlinkat_common(FsContext *ctx, P9_FILE_ID dirfd,
> + const char *name, int flags)
> {
> int ret;
>
> if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
> - int map_dirfd;
> + P9_FILE_ID map_dirfd;
>
> /* We need to remove the metadata as well:
> * - the metadata directory if we're removing a directory
> @@ -1110,10 +1115,10 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
> * mode. We just ignore the error.
> */
> if (flags == AT_REMOVEDIR) {
> - int fd;
> + P9_FILE_ID fd;
>
> fd = openat_dir(dirfd, name);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return -1;
> }
> ret = qemu_unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
> @@ -1123,7 +1128,7 @@ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
> }
> }
> map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
> - if (map_dirfd != -1) {
> + if (map_dirfd != P9_INVALID_FILE) {
> ret = qemu_unlinkat(map_dirfd, name, 0);
> close_preserve_errno(map_dirfd);
> if (ret < 0 && errno != ENOENT) {
> @@ -1143,11 +1148,11 @@ static int local_remove(FsContext *ctx, const char *path)
> char *dirpath = g_path_get_dirname(path);
> char *name = g_path_get_basename(path);
> int flags = 0;
> - int dirfd;
> + P9_FILE_ID dirfd;
> int err = -1;
>
> dirfd = local_opendir_nofollow(ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -1188,10 +1193,11 @@ static int local_fsync(FsContext *ctx, int fid_type,
>
> static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
> {
> - int fd, ret;
> + P9_FILE_ID fd;
> + int ret;
>
> fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
> - if (fd == -1) {
> + if (fd == P9_INVALID_FILE) {
> return -1;
> }
> ret = fstatfs(fd, stbuf);
> @@ -1276,7 +1282,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
> const char *new_name)
> {
> int ret;
> - int odirfd, ndirfd;
> + P9_FILE_ID odirfd, ndirfd;
>
> if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> (local_is_mapped_file_metadata(ctx, old_name) ||
> @@ -1286,12 +1292,12 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
> }
>
> odirfd = local_opendir_nofollow(ctx, olddir->data);
> - if (odirfd == -1) {
> + if (odirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> ndirfd = local_opendir_nofollow(ctx, newdir->data);
> - if (ndirfd == -1) {
> + if (ndirfd == P9_INVALID_FILE) {
> close_preserve_errno(odirfd);
> return -1;
> }
> @@ -1302,7 +1308,7 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
> }
>
> if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
> - int omap_dirfd, nmap_dirfd;
> + P9_FILE_ID omap_dirfd, nmap_dirfd;
>
> ret = qemu_mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
> if (ret < 0 && errno != EEXIST) {
> @@ -1310,12 +1316,12 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
> }
>
> omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
> - if (omap_dirfd == -1) {
> + if (omap_dirfd == P9_INVALID_FILE) {
> goto err;
> }
>
> nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
> - if (nmap_dirfd == -1) {
> + if (nmap_dirfd == P9_INVALID_FILE) {
> close_preserve_errno(omap_dirfd);
> goto err;
> }
> @@ -1373,7 +1379,7 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
> const char *name, int flags)
> {
> int ret;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
> local_is_mapped_file_metadata(ctx, name)) {
> @@ -1382,7 +1388,7 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
> }
>
> dirfd = local_opendir_nofollow(ctx, dir->data);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> diff --git a/hw/9pfs/9p-util-darwin.c b/hw/9pfs/9p-util-darwin.c
> index 95146e7354..c509034fb2 100644
> --- a/hw/9pfs/9p-util-darwin.c
> +++ b/hw/9pfs/9p-util-darwin.c
> @@ -11,8 +11,8 @@
> #include "qemu/error-report.h"
> #include "9p-util.h"
>
> -ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
> - void *value, size_t size)
> +ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> + const char *name, void *value, size_t size)
> {
> int ret;
> int fd = openat_file(dirfd, filename,
> @@ -25,7 +25,7 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
> return ret;
> }
>
> -ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> +ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> char *list, size_t size)
> {
> int ret;
> @@ -39,7 +39,7 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> return ret;
> }
>
> -ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> +ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> const char *name)
> {
> int ret;
> @@ -52,8 +52,8 @@ ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> return ret;
> }
>
> -int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
> - void *value, size_t size, int flags)
> +int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> + const char *name, void *value, size_t size, int flags)
> {
> int ret;
> int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
> @@ -110,7 +110,7 @@ out:
> return err;
> }
>
> -int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
> +int qemu_mknodat(P9_FILE_ID dirfd, const char *filename, mode_t mode, dev_t dev)
> {
> int preserved_errno, err;
>
> diff --git a/hw/9pfs/9p-util-linux.c b/hw/9pfs/9p-util-linux.c
> index db451b0784..cb5e988354 100644
> --- a/hw/9pfs/9p-util-linux.c
> +++ b/hw/9pfs/9p-util-linux.c
> @@ -19,8 +19,8 @@
> #include "qemu/xattr.h"
> #include "9p-util.h"
>
> -ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
> - void *value, size_t size)
> +ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> + const char *name, void *value, size_t size)
> {
> char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
> int ret;
> @@ -30,7 +30,7 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
> return ret;
> }
>
> -ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> +ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> char *list, size_t size)
> {
> char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
> @@ -41,7 +41,7 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
> return ret;
> }
>
> -ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> +ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> const char *name)
> {
> char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
> @@ -52,8 +52,8 @@ ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
> return ret;
> }
>
> -int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
> - void *value, size_t size, int flags)
> +int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> + const char *name, void *value, size_t size, int flags)
> {
> char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
> int ret;
> @@ -64,7 +64,7 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
>
> }
>
> -int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
> +int qemu_mknodat(P9_FILE_ID dirfd, const char *filename, mode_t mode, dev_t dev)
> {
> return mknodat(dirfd, filename, mode, dev);
> }
> diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c
> index 9ae69dd8db..aaa364ea4a 100644
> --- a/hw/9pfs/9p-xattr.c
> +++ b/hw/9pfs/9p-xattr.c
> @@ -78,13 +78,13 @@ 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;
> - int dirfd;
> + P9_FILE_ID dirfd;
>
> /* Get the actual len */
> dirpath = g_path_get_dirname(path);
> dirfd = local_opendir_nofollow(ctx, dirpath);
> g_free(dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> return -1;
> }
>
> @@ -168,11 +168,11 @@ ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
> {
> char *dirpath = g_path_get_dirname(path);
> char *filename = g_path_get_basename(path);
> - int dirfd;
> + P9_FILE_ID dirfd;
> ssize_t ret = -1;
>
> dirfd = local_opendir_nofollow(ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -196,11 +196,11 @@ ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
> {
> char *dirpath = g_path_get_dirname(path);
> char *filename = g_path_get_basename(path);
> - int dirfd;
> + P9_FILE_ID dirfd;
> ssize_t ret = -1;
>
> dirfd = local_opendir_nofollow(ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
> @@ -223,11 +223,11 @@ ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
> {
> char *dirpath = g_path_get_dirname(path);
> char *filename = g_path_get_basename(path);
> - int dirfd;
> + P9_FILE_ID dirfd;
> ssize_t ret = -1;
>
> dirfd = local_opendir_nofollow(ctx, dirpath);
> - if (dirfd == -1) {
> + if (dirfd == P9_INVALID_FILE) {
> goto out;
> }
>
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-10-24 4:57 ` [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs Bin Meng
@ 2022-11-01 14:27 ` Christian Schoenebeck
2022-11-01 15:13 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 14:27 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Greg Kurz, Bin Meng
On Monday, October 24, 2022 6:57:50 AM CET Bin Meng wrote:
> From: Guohuai Shi <guohuai.shi@windriver.com>
>
> Windows POSIX API and MinGW library do not provide the NO_FOLLOW
> flag, and do not allow opening a directory by POSIX open(). This
> causes all xxx_at() functions cannot work directly. However, we
> can provide Windows handle based functions to emulate xxx_at()
> functions (e.g.: openat_win32, utimensat_win32, etc.).
>
> Windows does not support extended attributes. 9pfs for Windows uses
> NTFS ADS (Alternate Data Streams) to emulate extended attributes.
>
> Windows does not provide POSIX compatible readlink(), and symbolic
> link feature in 9pfs will be disabled on Windows.
Wouldn't it be more user friendly if the relevant error locations would use
something like error_report_once() and suggesting to enable mapped(-xattr) to
make 9p symlinks on guest working if desired by the user?
Probably this error case would need to wrapped into a dedicated function,
otherwise I guess error_report_once() would fire several times by different
callers.
> Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> Signed-off-by: Bin Meng <bin.meng@windriver.com>
> ---
>
> hw/9pfs/9p-local.h | 7 +
> hw/9pfs/9p-util.h | 40 +-
> hw/9pfs/9p-local.c | 4 -
> hw/9pfs/9p-util-win32.c | 885 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 931 insertions(+), 5 deletions(-)
> create mode 100644 hw/9pfs/9p-util-win32.c
>
> diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h
> index c8404063e5..02fd894ba3 100644
> --- a/hw/9pfs/9p-local.h
> +++ b/hw/9pfs/9p-local.h
> @@ -15,6 +15,13 @@
>
> #include "9p-file-id.h"
>
> +typedef struct {
> + P9_FILE_ID mountfd;
> +#ifdef CONFIG_WIN32
> + char *root_path;
> +#endif
> +} LocalData;
> +
> P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
> mode_t mode);
> P9_FILE_ID local_opendir_nofollow(FsContext *fs_ctx, const char *path);
> diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
> index 1e7dc76345..82b2d0c3e4 100644
> --- a/hw/9pfs/9p-util.h
> +++ b/hw/9pfs/9p-util.h
> @@ -90,26 +90,61 @@ static inline int errno_to_dotl(int err) {
> return err;
> }
>
> -#ifdef CONFIG_DARWIN
> +#if defined(CONFIG_DARWIN)
> #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0)
> +#elif defined(CONFIG_WIN32)
> +#define qemu_fgetxattr fgetxattr_win32
> #else
> #define qemu_fgetxattr fgetxattr
> #endif
>
> +#ifdef CONFIG_WIN32
> +#define qemu_openat openat_win32
> +#define qemu_fstatat fstatat_win32
> +#define qemu_mkdirat mkdirat_win32
> +#define qemu_renameat renameat_win32
> +#define qemu_utimensat utimensat_win32
> +#define qemu_unlinkat unlinkat_win32
> +#else
> #define qemu_openat openat
> #define qemu_fstatat fstatat
> #define qemu_mkdirat mkdirat
> #define qemu_renameat renameat
> #define qemu_utimensat utimensat
> #define qemu_unlinkat unlinkat
> +#endif
> +
> +#ifdef CONFIG_WIN32
> +char *get_full_path_win32(P9_FILE_ID fd, const char *name);
> +ssize_t fgetxattr_win32(int fd, const char *name, void *value, size_t size);
> +P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int flags,
> + mode_t mode);
> +int fstatat_win32(P9_FILE_ID dirfd, const char *pathname,
> + struct stat *statbuf, int flags);
> +int mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t mode);
> +int renameat_win32(P9_FILE_ID olddirfd, const char *oldpath,
> + P9_FILE_ID newdirfd, const char *newpath);
> +int utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
> + const struct timespec times[2], int flags);
> +int unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags);
> +int statfs_win32(const char *root_path, struct statfs *stbuf);
> +P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name);
> +P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
> + mode_t mode);
> +#endif
>
> static inline void close_preserve_errno(P9_FILE_ID fd)
> {
> int serrno = errno;
> +#ifndef CONFIG_WIN32
> close(fd);
> +#else
> + CloseHandle(fd);
> +#endif
> errno = serrno;
> }
>
> +#ifndef CONFIG_WIN32
> static inline P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name)
> {
> return qemu_openat(dirfd, name,
> @@ -157,6 +192,7 @@ again:
> errno = serrno;
> return fd;
> }
> +#endif
>
> ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> const char *name, void *value, size_t size);
> @@ -167,6 +203,7 @@ ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> const char *name);
>
> +#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,
> @@ -181,6 +218,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.
> diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
> index 7e8d8492ea..d0d6d93549 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 {
> - P9_FILE_ID mountfd;
> -} LocalData;
> -
> P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
> mode_t mode)
> {
> diff --git a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c
> new file mode 100644
> index 0000000000..953e7da6fa
> --- /dev/null
> +++ b/hw/9pfs/9p-util-win32.c
> @@ -0,0 +1,885 @@
> +/*
> + * 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.
> + */
> +
> +/*
> + * This file contains Windows only functions for 9pfs.
> + *
> + * For 9pfs Windows host, the following features are different from Linux host:
> + *
> + * 1. Windows POSIX API does not provide the NO_FOLLOW flag, that means MinGW
> + * cannot detect if a path is a symbolic link or not. Also Windows do not
> + * provide POSIX compatible readlink(). Supporting symbolic link in 9pfs on
> + * Windows may cause security issues, so symbolic link support is disabled
> + * completely.
> + *
> + * 2. Windows file system does not support extended attributes directly. 9pfs
> + * for Windows uses NTFS ADS (Alternate Data Streams) to emulate extended
> + * attributes.
> + *
> + * 3. statfs() is not available on Windows. qemu_statfs() is used to emulate it.
> + *
> + * 4. On Windows trying to open a directory with the open() API will fail.
> + * This is because Windows does not allow opening directory in normal usage.
> + *
> + * As a result of this, all xxx_at() functions won't work directly on
> + * Windows, e.g.: openat(), unlinkat(), etc.
> + *
> + * As xxx_at() can prevent parent directory to be modified on Linux host,
> + * to support this and prevent security issue, all xxx_at() APIs are replaced
> + * by xxx_at_win32() and Windows handle is used to replace the directory fd.
> + *
> + * Windows file system does not allow replacing a file or directory if it is
> + * referenced by a handle. Keep the handle open will lock and protect the
> + * parent directory and make the access to files atomically.
> + *
> + * If we don't protect (lock) the parent directory, the parent directory may
> + * be replaced by others (e.g.: a symbolic link) and cause security issues.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/error-report.h"
> +#include "9p.h"
> +#include "9p-util.h"
> +#include "9p-local.h"
> +
> +#include <windows.h>
> +#include <dirent.h>
> +
> +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */
> +
> +/*
> + * build_ads_name - construct Windows ADS name
> + *
> + * This function constructs Windows NTFS ADS (Alternate Data Streams) name
> + * to <namebuf>.
> + */
> +static int build_ads_name(char *namebuf, size_t namebuf_len,
> + const char *filename, const char *ads_name)
> +{
> + size_t total_size;
> +
> + total_size = strlen(filename) + strlen(ads_name) + 2;
> + if (total_size > namebuf_len) {
> + return -1;
> + }
> +
> + /*
> + * NTFS ADS (Alternate Data Streams) name format: filename:ads_name
> + * e.g.: D:\1.txt:my_ads_name
> + */
> +
> + strcpy(namebuf, filename);
> + strcat(namebuf, ":");
> + strcat(namebuf, ads_name);
> +
> + return 0;
> +}
> +
> +/*
> + * copy_ads_name - copy ADS name from buffer returned by FindNextStreamW()
> + *
> + * This function removes string "$DATA" in ADS name string returned by
> + * FindNextStreamW(), and copies the real ADS name to <namebuf>.
> + */
> +static ssize_t copy_ads_name(char *namebuf, size_t namebuf_len,
> + char *full_ads_name)
> +{
> + char *p1, *p2;
> +
> + /*
> + * NTFS ADS (Alternate Data Streams) name from enumerate data format:
> + * :ads_name:$DATA, e.g.: :my_ads_name:$DATA
> + *
> + * ADS name from FindNextStreamW() always has ":$DATA" string at the end.
> + *
> + * This function copies ADS name to namebuf.
> + */
> +
> + p1 = strchr(full_ads_name, ':');
> + 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 > namebuf_len) {
> + return -1;
> + }
> +
> + memcpy(namebuf, p1 + 1, p2 - p1 - 1);
> + namebuf[p2 - p1 - 1] = '\0';
> +
> + return p2 - p1;
> +}
> +
> +/*
> + * get_full_path_win32 - get full file name base on a handle
> + *
> + * This function gets full file name based on a handle specified by <fd> to
> + * a file or directory.
> + *
> + * Caller function needs to free the file name string after use.
> + */
> +char *get_full_path_win32(P9_FILE_ID fd, const char *name)
> +{
> + g_autofree char *full_file_name = NULL;
> + DWORD total_size;
> + DWORD name_size;
> +
> + full_file_name = g_malloc0(NAME_MAX);
> +
> + /* get parent directory full file name */
> + name_size = GetFinalPathNameByHandle(fd, full_file_name,
> + NAME_MAX - 1, FILE_NAME_NORMALIZED);
> + if (name_size == 0 || name_size > NAME_MAX - 1) {
> + return NULL;
> + }
> +
> + /* full path returned is the "\\?\" syntax, remove the lead string */
> + memmove(full_file_name, full_file_name + 4, NAME_MAX - 4);
> +
> + if (name != NULL) {
> + total_size = strlen(full_file_name) + strlen(name) + 2;
> +
> + if (total_size > NAME_MAX) {
> + return NULL;
> + }
> +
> + /* build sub-directory file name */
> + strcat(full_file_name, "\\");
> + strcat(full_file_name, name);
> + }
> +
> + return g_steal_pointer(&full_file_name);
> +}
> +
> +/*
> + * fgetxattr_win32 - get extended attribute by fd
> + *
> + * This function gets extened attribute by <fd>. <fd> will be translated to
> + * Windows handle.
> + *
> + * This function emulates extended attribute by NTFS ADS.
> + */
> +ssize_t fgetxattr_win32(int fd, const char *name, void *value, size_t size)
> +{
> + g_autofree char *full_file_name = NULL;
> + char ads_file_name[NAME_MAX + 1] = {0};
> + DWORD dwBytesRead;
> + HANDLE hStream;
> + HANDLE hFile;
> +
> + hFile = (HANDLE)_get_osfhandle(fd);
> +
> + full_file_name = get_full_path_win32(hFile, NULL);
> + if (full_file_name == NULL) {
> + errno = EIO;
> + return -1;
> + }
> +
> + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0) {
> + errno = EIO;
> + return -1;
> + }
> +
> + hStream = CreateFile(ads_file_name, 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;
> +}
> +
> +/*
> + * openat_win32 - emulate openat()
> + *
> + * This function emulates openat().
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So openat_win32() has to use a directory handle instead of a directory fd.
> + *
> + * For symbolic access:
> + * 1. Parent directory handle <dirfd> should not be a symbolic link because
> + * it is opened by openat_dir() which can prevent from opening a link to
> + * a dirctory.
> + * 2. Link flag in <mode> is not set because Windows does not have this flag.
> + * Create a new symbolic link will be denied.
> + * 3. This function checks file symbolic link attribute after open.
> + *
> + * So symbolic link will not be accessed by 9p client.
> + */
> +P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int flags,
> + mode_t mode)
> +{
> + g_autofree char *full_file_name1 = NULL;
> + g_autofree char *full_file_name2 = NULL;
> + HANDLE hFile = INVALID_HANDLE_VALUE;
> + int fd;
> +
> + full_file_name1 = get_full_path_win32(dirfd, pathname);
> + if (full_file_name1 == NULL) {
> + return hFile;
> + }
> +
> + fd = open(full_file_name1, flags, mode);
> + if (fd > 0) {
> + DWORD attribute;
> + hFile = (HANDLE)_get_osfhandle(fd);
> +
> + full_file_name2 = get_full_path_win32(hFile, NULL);
> + attribute = GetFileAttributes(full_file_name2);
> +
> + /* check if it is a symbolic link */
> + if ((attribute == INVALID_FILE_ATTRIBUTES)
> + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> + errno = EACCES;
> + hFile = INVALID_HANDLE_VALUE;
> + close(fd);
> + }
> + }
> +
> + return hFile;
> +}
> +
> +/*
> + * fstatat_win32 - emulate fstatat()
> + *
> + * This function emulates fstatat().
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So fstatat_win32() has to use a directory handle instead of a directory fd.
> + *
> + * Access to a symbolic link will be denied to prevent security issues.
> + */
> +int fstatat_win32(P9_FILE_ID dirfd, const char *pathname,
> + struct stat *statbuf, int flags)
> +{
> + g_autofree char *full_file_name = NULL;
> + HANDLE hFile = INVALID_HANDLE_VALUE;
> + DWORD attribute;
> + int err = 0;
> + int ret = -1;
> +
> + full_file_name = get_full_path_win32(dirfd, pathname);
> + if (full_file_name == NULL) {
> + return ret;
> + }
> +
> + /* open file to lock it */
> + hFile = CreateFile(full_file_name, GENERIC_READ,
> + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
> + NULL,
> + OPEN_EXISTING,
> + FILE_FLAG_BACKUP_SEMANTICS
> + | FILE_FLAG_OPEN_REPARSE_POINT,
> + NULL);
> +
> + if (hFile == INVALID_HANDLE_VALUE) {
> + err = EACCES;
> + goto out;
> + }
> +
> + attribute = GetFileAttributes(full_file_name);
> +
> + /* check if it is a symbolic link */
> + if ((attribute == INVALID_FILE_ATTRIBUTES)
> + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> + errno = EACCES;
> + goto out;
> + }
> +
> + ret = stat(full_file_name, statbuf);
> +
> +out:
> + if (hFile != INVALID_HANDLE_VALUE) {
> + CloseHandle(hFile);
> + }
> +
> + if (err != 0) {
> + errno = err;
> + }
> + return ret;
> +}
> +
> +/*
> + * mkdirat_win32 - emulate mkdirat()
> + *
> + * This function emulates mkdirat().
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So mkdirat_win32() has to use a directory handle instead of a directory fd.
> + */
> +int mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t mode)
> +{
> + g_autofree char *full_file_name = NULL;
> + int ret = -1;
> +
> + full_file_name = get_full_path_win32(dirfd, pathname);
> + if (full_file_name == NULL) {
> + return ret;
> + }
> +
> + ret = mkdir(full_file_name);
> +
> + return ret;
> +}
> +
> +/*
> + * renameat_win32 - emulate renameat()
> + *
> + * This function emulates renameat().
> + *
> + * Windows POSIX API does not support openning a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So renameat_win32() has to use a directory handle instead of a directory fd.
> + *
> + * Access to a symbolic link will be denied to prevent security issues.
> + */
> +int renameat_win32(HANDLE olddirfd, const char *oldpath,
> + HANDLE newdirfd, const char *newpath)
> +{
> + g_autofree char *full_old_name = NULL;
> + g_autofree char *full_new_name = NULL;
> + HANDLE hFile;
> + DWORD attribute;
> + int err = 0;
> + int ret = -1;
> +
> + full_old_name = get_full_path_win32(olddirfd, oldpath);
> + full_new_name = get_full_path_win32(newdirfd, newpath);
> + if (full_old_name == NULL || full_new_name == NULL) {
> + return ret;
> + }
> +
> + /* open file to lock it */
> + hFile = CreateFile(full_old_name, GENERIC_READ,
> + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
> + NULL,
> + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
> +
> + attribute = GetFileAttributes(full_old_name);
> +
> + /* check if it is a symbolic link */
> + if ((attribute == INVALID_FILE_ATTRIBUTES)
> + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> + err = EACCES;
> + goto out;
> + }
> +
> + CloseHandle(hFile);
> +
> + ret = rename(full_old_name, full_new_name);
> +out:
> + if (err != 0) {
> + errno = err;
> + }
> + return ret;
> +}
> +
> +/*
> + * utimensat_win32 - emulate utimensat()
> + *
> + * This function emulates utimensat().
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So utimensat_win32() has to use a directory handle instead of a directory fd.
> + *
> + * Access to a symbolic link will be denied to prevent security issues.
> + */
> +int utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
> + const struct timespec times[2], int flags)
> +{
> + g_autofree char *full_file_name = NULL;
> + HANDLE hFile = INVALID_HANDLE_VALUE;
> + DWORD attribute;
> + struct utimbuf tm;
> + int err = 0;
> + int ret = -1;
> +
> + full_file_name = get_full_path_win32(dirfd, pathname);
> + if (full_file_name == NULL) {
> + return ret;
> + }
> +
> + /* open file to lock it */
> + hFile = CreateFile(full_file_name, GENERIC_READ,
> + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
> + NULL,
> + OPEN_EXISTING,
> + FILE_FLAG_BACKUP_SEMANTICS
> + | FILE_FLAG_OPEN_REPARSE_POINT,
> + NULL);
> +
> + if (hFile == INVALID_HANDLE_VALUE) {
> + err = EACCES;
> + goto out;
> + }
> +
> + attribute = GetFileAttributes(full_file_name);
> +
> + /* check if it is a symbolic link */
> + if ((attribute == INVALID_FILE_ATTRIBUTES)
> + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> + errno = EACCES;
> + goto out;
> + }
> +
> + tm.actime = times[0].tv_sec;
> + tm.modtime = times[1].tv_sec;
> +
> + ret = utime(full_file_name, &tm);
> +
> +out:
> + if (hFile != INVALID_HANDLE_VALUE) {
> + CloseHandle(hFile);
> + }
> +
> + if (err != 0) {
> + errno = err;
> + }
> + return ret;
> +}
> +
> +/*
> + * unlinkat_win32 - emulate unlinkat()
> + *
> + * This function emulates unlinkat().
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So unlinkat_win32() has to use a directory handle instead of a directory fd.
> + *
> + * Access to a symbolic link will be denied to prevent security issues.
> + */
> +
> +int unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags)
> +{
> + g_autofree char *full_file_name = NULL;
> + HANDLE hFile;
> + DWORD attribute;
> + int err = 0;
> + int ret = -1;
> +
> + full_file_name = get_full_path_win32(dirfd, pathname);
> + if (full_file_name == NULL) {
> + return ret;
> + }
> +
> + /* open file to prevent other one modify it */
> + hFile = CreateFile(full_file_name, GENERIC_READ,
> + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
> + NULL,
> + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
> +
> + attribute = GetFileAttributes(full_file_name);
> +
> + /* check if it is a symbolic link */
> + if ((attribute == INVALID_FILE_ATTRIBUTES)
> + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> + err = EACCES;
> + goto out;
> + }
> +
> + if (flags == AT_REMOVEDIR) { /* remove directory */
> + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
> + err = ENOTDIR;
> + goto out;
> + }
> + ret = rmdir(full_file_name);
> + } else { /* remove regular file */
> + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) != 0) {
> + err = EISDIR;
> + goto out;
> + }
> + ret = remove(full_file_name);
> + }
> +
> + /* after last handle closed, file will be removed */
> + CloseHandle(hFile);
> +
> +out:
> + if (err != 0) {
> + errno = err;
> + }
> + return ret;
> +}
> +
> +/*
> + * statfs_win32 - statfs() on Windows
> + *
> + * This function emulates statfs() on Windows host.
> + */
> +int statfs_win32(const char *path, struct statfs *stbuf)
> +{
> + char RealPath[4] = { 0 };
> + unsigned long SectorsPerCluster;
> + unsigned long BytesPerSector;
> + unsigned long NumberOfFreeClusters;
> + unsigned long TotalNumberOfClusters;
> +
> + /* only need first 3 bytes, e.g. "C:\ABC", only need "C:\" */
> + memcpy(RealPath, path, 3);
> +
> + if (GetDiskFreeSpace(RealPath, &SectorsPerCluster, &BytesPerSector,
> + &NumberOfFreeClusters, &TotalNumberOfClusters) == 0) {
> + errno = EIO;
> + return -1;
> + }
> +
> + stbuf->f_type = V9FS_MAGIC;
> + stbuf->f_bsize =
> + (__fsword_t)SectorsPerCluster * (__fsword_t)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;
> +}
> +
> +/*
> + * openat_dir - emulate openat_dir()
> + *
> + * This function emulates openat_dir().
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * So openat_dir() has to use a directory handle instead of a directory fd.
> + *
> + * Access to a symbolic link will be denied to prevent security issues.
> + */
> +P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name)
> +{
> + g_autofree char *full_file_name = NULL;
> + HANDLE hSubDir;
> + DWORD attribute;
> +
> + full_file_name = get_full_path_win32(dirfd, name);
> + if (full_file_name == NULL) {
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + attribute = GetFileAttributes(full_file_name);
> + if (attribute == INVALID_FILE_ATTRIBUTES) {
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + /* check if it is a directory */
> + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + /* do not allow opening a symbolic link */
> + if ((attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + /* open it */
> + hSubDir = CreateFile(full_file_name, GENERIC_READ,
> + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
> + NULL,
> + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
> + return hSubDir;
> +}
> +
> +P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
> + mode_t mode)
> +{
> + return openat_win32(dirfd, name, flags | _O_BINARY, mode);
> +}
> +
> +/*
> + * fgetxattrat_nofollow - get extended attribute
> + *
> + * This function gets extended attribute from file <path> in the directory
> + * specified by <dirfd>. The extended atrribute name is specified by <name>
> + * and return value will be put in <value>.
> + *
> + * This function emulates extended attribute by NTFS ADS.
> + */
> +ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> + const char *name, void *value, size_t size)
> +{
> + g_autofree char *full_file_name = NULL;
> + char ads_file_name[NAME_MAX + 1] = { 0 };
> + DWORD dwBytesRead;
> + HANDLE hStream;
> +
> + full_file_name = get_full_path_win32(dirfd, path);
> + if (full_file_name == NULL) {
> + errno = EIO;
> + return -1;
> + }
> +
> + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0) {
> + errno = EIO;
> + return -1;
> + }
> +
> + hStream = CreateFile(ads_file_name, 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;
> +}
> +
> +/*
> + * fsetxattrat_nofollow - set extended attribute
> + *
> + * This function set extended attribute to file <path> in the directory
> + * specified by <dirfd>.
> + *
> + * This function emulates extended attribute by NTFS ADS.
> + */
> +
> +int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *path, const char *name,
> + void *value, size_t size, int flags)
> +{
> + g_autofree char *full_file_name = NULL;
> + char ads_file_name[NAME_MAX + 1] = { 0 };
> + DWORD dwBytesWrite;
> + HANDLE hStream;
> +
> + full_file_name = get_full_path_win32(dirfd, path);
> + if (full_file_name == NULL) {
> + errno = EIO;
> + return -1;
> + }
> +
> + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0) {
> + errno = EIO;
> + return -1;
> + }
> +
> + hStream = CreateFile(ads_file_name, 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;
> +}
> +
> +/*
> + * flistxattrat_nofollow - list extended attribute
> + *
> + * This function gets extended attribute lists from file <filename> in the
> + * directory specified by <dirfd>. Lists returned will be put in <list>.
> + *
> + * This function emulates extended attribute by NTFS ADS.
> + */
> +ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> + char *list, size_t size)
> +{
> + g_autofree char *full_file_name = NULL;
> + WCHAR WideCharStr[NAME_MAX + 1] = { 0 };
> + char full_ads_name[NAME_MAX + 1];
> + WIN32_FIND_STREAM_DATA fsd;
> + BOOL bFindNext;
> + char *list_ptr = list;
> + size_t list_left_size = size;
> + HANDLE hFind;
> + int ret;
> +
> + full_file_name = get_full_path_win32(dirfd, filename);
> + if (full_file_name == NULL) {
> + errno = EIO;
> + return -1;
> + }
> +
> + /*
> + * ADS enumerate function only has WCHAR version, so we need to
> + * covert filename to utf-8 string.
> + */
> + ret = MultiByteToWideChar(CP_UTF8, 0, full_file_name,
> + strlen(full_file_name), WideCharStr, NAME_MAX);
> + if (ret == 0) {
> + errno = EIO;
> + return -1;
> + }
> +
> + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd, 0);
> + if (hFind == INVALID_HANDLE_VALUE) {
> + errno = ENODATA;
> + return -1;
> + }
> +
> + do {
> + memset(full_ads_name, 0, sizeof(full_ads_name));
> +
> + /*
> + * ADS enumerate function only has WCHAR version, so we need to
> + * covert cStreamName to utf-8 string.
> + */
> + ret = WideCharToMultiByte(CP_UTF8, 0,
> + fsd.cStreamName, wcslen(fsd.cStreamName) + 1,
> + full_ads_name, sizeof(full_ads_name) - 1,
> + NULL, NULL);
> + if (ret == 0) {
> + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
> + errno = ERANGE;
> + }
> + CloseHandle(hFind);
> + return -1;
> + }
> +
> + ret = copy_ads_name(list_ptr, list_left_size, full_ads_name);
> + if (ret < 0) {
> + errno = ERANGE;
> + CloseHandle(hFind);
> + return -1;
> + }
> +
> + list_ptr = list_ptr + ret;
> + list_left_size = list_left_size - ret;
> +
> + bFindNext = FindNextStreamW(hFind, &fsd);
> + } while (bFindNext);
> +
> + CloseHandle(hFind);
> +
> + return size - list_left_size;
> +}
> +
> +/*
> + * fremovexattrat_nofollow - remove extended attribute
> + *
> + * This function removes an extended attribute from file <filename> in the
> + * directory specified by <dirfd>.
> + *
> + * This function emulates extended attribute by NTFS ADS.
> + */
> +ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> + const char *name)
> +{
> + g_autofree char *full_file_name = NULL;
> + char ads_file_name[NAME_MAX + 1] = { 0 };
> +
> + full_file_name = get_full_path_win32(dirfd, filename);
> + if (full_file_name == NULL) {
> + errno = EIO;
> + return -1;
> + }
> +
> + if (build_ads_name(ads_file_name, NAME_MAX, filename, name) < 0) {
> + errno = EIO;
> + return -1;
> + }
> +
> + if (DeleteFile(ads_file_name) != 0) {
> + if (GetLastError() == ERROR_FILE_NOT_FOUND) {
> + errno = ENODATA;
> + return -1;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * local_opendir_nofollow - open a Windows directory
> + *
> + * This function returns a Windows file handle of the directory specified by
> + * <dirpath> based on 9pfs mount point.
> + *
> + * Windows POSIX API does not support opening a directory by open(). Only
> + * handle of directory can be opened by CreateFile().
> + *
> + * This function checks the resolved path of <dirpath>. If the resolved
> + * path is not in the scope of root directory (e.g. by symbolic link), then
> + * this function will fail to prevent any security issues.
> + */
> +HANDLE local_opendir_nofollow(FsContext *fs_ctx, const char *dirpath)
> +{
> + g_autofree char *full_file_name = NULL;
> + LocalData *data = fs_ctx->private;
> + HANDLE hDir;
> +
> + hDir = openat_dir(data->mountfd, dirpath);
> + if (hDir == INVALID_HANDLE_VALUE) {
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + full_file_name = get_full_path_win32(hDir, NULL);
> + if (full_file_name == NULL) {
> + CloseHandle(hDir);
> + return INVALID_HANDLE_VALUE;
> + }
> +
> + /*
> + * Check if the resolved path is in the root directory scope:
> + * data->root_path and full_file_name are full path with symbolic
> + * link resolved, so fs_ctx->root_path must be in the head of
> + * full_file_name. If not, that means guest OS tries to open a file not
> + * in the scope of mount point. This operation should be denied.
> + */
> + if (memcmp(full_file_name, data->root_path,
> + strlen(data->root_path)) != 0) {
> + CloseHandle(hDir);
> + hDir = INVALID_HANDLE_VALUE;
> + }
> +
> + return hDir;
> +}
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 08/16] hw/9pfs: Handle current directory offset for Windows
2022-10-24 4:57 ` [PATCH 08/16] hw/9pfs: Handle current directory offset for Windows Bin Meng
@ 2022-11-01 14:41 ` Christian Schoenebeck
0 siblings, 0 replies; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 14:41 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Greg Kurz, Bin Meng
On Monday, October 24, 2022 6:57:51 AM CET Bin Meng wrote:
> From: Guohuai Shi <guohuai.shi@windriver.com>
>
> On Windows 'struct dirent' does not have current directory offset.
> We have to save current directory offset and update offset when
> reading directory.
>
> Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> Signed-off-by: Bin Meng <bin.meng@windriver.com>
> ---
>
> hw/9pfs/9p.c | 16 ++++++++++++++++
> hw/9pfs/codir.c | 15 +++++++++++++++
> 2 files changed, 31 insertions(+)
>
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index aebadeaa03..6c4af86240 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -2319,7 +2319,15 @@ 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
> + /*
> + * Get offset by calling telldir() manually,
> + * as Windows does not have dent->d_off.
> + */
> + saved_dir_pos = v9fs_co_telldir(pdu, fidp);
> +#endif
> }
That's not the way to go. We already had the same discussion with the macOS
patches and why we introduced qemu_dirent_off() for exactly that purpose:
v9fs_co_telldir() would dispatch the coroutine from QEMU main thread to
background worker thread and vice versa. So you would get side effects by
doing this.
Please implement this adequately in qemu_dirent_off() instead of touching the
controller portion here.
>
> v9fs_readdir_unlock(&fidp->fs.dir);
> @@ -2520,7 +2528,15 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
> qid.version = 0;
> }
>
> +#ifndef CONFIG_WIN32
> off = qemu_dirent_off(dent);
> +#else
> + /*
> + * Get offset by calling telldir() manually,
> + * as Windows does not have dent->d_off.
> + */
> + off = v9fs_co_telldir(pdu, fidp);
> +#endif
> v9fs_string_init(&name);
> v9fs_string_sprintf(&name, "%s", dent->d_name);
>
> 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] 40+ messages in thread
* Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-10-24 4:57 ` [PATCH 09/16] hw/9pfs: Disable unsupported flags and features " Bin Meng
@ 2022-11-01 15:04 ` Christian Schoenebeck
2022-11-01 15:34 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 15:04 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Greg Kurz, Bin Meng
On Monday, October 24, 2022 6:57:52 AM CET Bin Meng wrote:
> From: Guohuai Shi <guohuai.shi@windriver.com>
>
> Some flags and features are not supported on Windows, like mknod,
> readlink, file mode, etc. Update the codes for Windows.
>
> Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> Signed-off-by: Bin Meng <bin.meng@windriver.com>
> ---
>
> hw/9pfs/9p-util.h | 6 +++-
> hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-----
> 2 files changed, 86 insertions(+), 10 deletions(-)
>
> diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
> index 82b2d0c3e4..3d154e9103 100644
> --- a/hw/9pfs/9p-util.h
> +++ b/hw/9pfs/9p-util.h
> @@ -53,8 +53,10 @@ static inline uint64_t makedev_dotl(uint32_t dev_major, uint32_t dev_minor)
> */
> static inline uint64_t host_dev_to_dotl_dev(dev_t dev)
> {
> -#ifdef CONFIG_LINUX
> +#if defined(CONFIG_LINUX)
> return dev;
> +#elif defined(CONFIG_WIN32)
> + return 0;
Really?
> #else
> return makedev_dotl(major(dev), minor(dev));
> #endif
> @@ -260,7 +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t mode,
> dev_t dev);
> +#endif
>
> #endif
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index 6c4af86240..771aab34ac 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -39,6 +39,11 @@
> #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;
> static int open_fd_rc;
> @@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
> DotlOpenflagMap dotl_oflag_map[] = {
> { P9_DOTL_CREATE, O_CREAT },
> { P9_DOTL_EXCL, O_EXCL },
> +#ifndef CONFIG_WIN32
> { P9_DOTL_NOCTTY , O_NOCTTY },
> +#endif
> { P9_DOTL_TRUNC, O_TRUNC },
> { P9_DOTL_APPEND, O_APPEND },
> +#ifndef CONFIG_WIN32
> { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
> { P9_DOTL_DSYNC, O_DSYNC },
> { P9_DOTL_FASYNC, FASYNC },
> -#ifndef CONFIG_DARWIN
> +#endif
> +#ifdef CONFIG_LINUX
Better
#if !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32)
Otherwise it might automatically opt-out other future platforms
unintentionally.
> { P9_DOTL_NOATIME, O_NOATIME },
> /*
> * On Darwin, we could map to F_NOCACHE, which is
> @@ -151,8 +160,10 @@ static int dotl_to_open_flags(int flags)
> #endif
> { P9_DOTL_LARGEFILE, O_LARGEFILE },
> { P9_DOTL_DIRECTORY, O_DIRECTORY },
> +#ifndef CONFIG_WIN32
> { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
> { P9_DOTL_SYNC, O_SYNC },
> +#endif
> };
>
> for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
> @@ -179,8 +190,11 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
> * Filter the client open flags
> */
> flags = dotl_to_open_flags(oflags);
> - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
> -#ifndef CONFIG_DARWIN
> + flags &= ~(O_CREAT);
> +#ifndef CONFIG_WIN32
> + flags &= ~(O_NOCTTY | O_ASYNC);
> +#endif
> +#ifdef CONFIG_LINUX
Same as above: better explicitly opt-out than the other way around.
> /*
> * Ignore direct disk access hint until the server supports it.
> */
> @@ -986,9 +1000,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;
> }
> @@ -1097,6 +1113,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;
> }
> @@ -1106,6 +1123,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;
> @@ -1118,6 +1136,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;
> }
> @@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
> if (mode & P9_STAT_MODE_SETVTX) {
> ret |= S_ISVTX;
> }
> +#endif
>
> return ret;
> }
> @@ -1182,6 +1202,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;
> }
> @@ -1193,11 +1214,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;
> }
> @@ -1209,6 +1232,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;
> }
> @@ -1247,9 +1271,17 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path,
> return err;
> }
> } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
> + unsigned maj, min;
> +
> +#ifndef CONFIG_WIN32
> + maj = major(stbuf->st_rdev);
> + min = minor(stbuf->st_rdev);
> +#else
> + maj = min = 0;
> +#endif
Really?
> v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
> S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
> - major(stbuf->st_rdev), minor(stbuf->st_rdev));
> + maj, min);
> } 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);
> @@ -1317,7 +1349,14 @@ 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)
> {
> - return blksize_to_iounit(pdu, stbuf->st_blksize);
> + int32_t blksize;
> +
> +#ifndef CONFIG_WIN32
> + blksize = stbuf->st_blksize);
> +#else
> + blksize = 0;
> +#endif
Really?
> + return blksize_to_iounit(pdu, blksize);
> }
>
> static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
> @@ -1332,7 +1371,11 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
> v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
> v9lstat->st_size = stbuf->st_size;
> v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
> +#ifndef CONFIG_WIN32
> v9lstat->st_blocks = stbuf->st_blocks;
> +#else
> + v9lstat->st_blocks = 0;
> +#endif
Really?
> v9lstat->st_atime_sec = stbuf->st_atime;
> v9lstat->st_mtime_sec = stbuf->st_mtime;
> v9lstat->st_ctime_sec = stbuf->st_ctime;
> @@ -1340,7 +1383,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
> v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
> v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
> v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec;
> -#else
> +#endif
> +#ifdef CONFIG_LINUX
> v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
> v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
> v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
> @@ -2471,6 +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
> struct dirent *dent;
> struct stat *st;
> struct V9fsDirEnt *entries = NULL;
> + unsigned char d_type = 0;
>
> /*
> * inode remapping requires the device id, which in turn might be
> @@ -2540,10 +2585,13 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
> v9fs_string_init(&name);
> v9fs_string_sprintf(&name, "%s", dent->d_name);
>
> +#ifndef CONFIG_WIN32
> + d_type = dent->d_type;
> +#endif
> /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
> len = pdu_marshal(pdu, 11 + count, "Qqbs",
> &qid, off,
> - dent->d_type, &name);
> + d_type, &name);
Are you saying that d_type is not initialized with zero already?
> v9fs_string_free(&name);
>
> @@ -2873,8 +2921,12 @@ 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;
> }
> @@ -2899,8 +2951,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
As with previous patches, I would consider making the user aware to use
mapped(-xattr) with something like error_report_once().
> if (err < 0) {
> goto out;
> }
> @@ -3634,7 +3690,7 @@ out_nofid:
>
> static void coroutine_fn v9fs_mknod(void *opaque)
> {
> -
> +#ifndef CONFIG_WIN32
> int mode;
> gid_t gid;
> int32_t fid;
> @@ -3691,6 +3747,10 @@ out:
> out_nofid:
> pdu_complete(pdu, err);
> v9fs_string_free(&name);
> +#else
> + V9fsPDU *pdu = opaque;
> + pdu_complete(pdu, -1);
> +#endif
> }
>
> /*
> @@ -3963,7 +4023,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.
> @@ -3980,6 +4040,7 @@ out_nofid:
>
> static void coroutine_fn v9fs_xattrcreate(void *opaque)
> {
> +#ifndef CONFIG_WIN32
> int flags, rflags = 0;
> int32_t fid;
> uint64_t size;
> @@ -4041,10 +4102,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;
> @@ -4080,6 +4146,10 @@ out:
> put_fid(pdu, fidp);
> out_nofid:
> pdu_complete(pdu, err);
> +#else
> + V9fsPDU *pdu = opaque;
> + pdu_complete(pdu, -1);
> +#endif
Unnecessary double declaration of pdu.
> }
>
> static CoroutineEntry *pdu_co_handlers[] = {
> @@ -4341,6 +4411,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");
> @@ -4348,4 +4419,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
Really?
> }
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-01 14:27 ` Christian Schoenebeck
@ 2022-11-01 15:13 ` Shi, Guohuai
2022-11-01 15:20 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-01 15:13 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> Sent: Tuesday, November 1, 2022 22:28
> To: qemu-devel@nongnu.org
> Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz <groug@kaod.org>;
> Meng, Bin <Bin.Meng@windriver.com>
> Subject: Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities
> functions for 9pfs
>
> [Please note: This e-mail is from an EXTERNAL e-mail address]
>
> On Monday, October 24, 2022 6:57:50 AM CET Bin Meng wrote:
> > From: Guohuai Shi <guohuai.shi@windriver.com>
> >
> > Windows POSIX API and MinGW library do not provide the NO_FOLLOW flag,
> > and do not allow opening a directory by POSIX open(). This causes all
> > xxx_at() functions cannot work directly. However, we can provide
> > Windows handle based functions to emulate xxx_at() functions (e.g.:
> > openat_win32, utimensat_win32, etc.).
> >
> > Windows does not support extended attributes. 9pfs for Windows uses
> > NTFS ADS (Alternate Data Streams) to emulate extended attributes.
> >
> > Windows does not provide POSIX compatible readlink(), and symbolic
> > link feature in 9pfs will be disabled on Windows.
>
> Wouldn't it be more user friendly if the relevant error locations would use
> something like error_report_once() and suggesting to enable mapped(-xattr) to
> make 9p symlinks on guest working if desired by the user?
>
> Probably this error case would need to wrapped into a dedicated function,
> otherwise I guess error_report_once() would fire several times by different
> callers.
>
Windows (MinGW) does not only support symlink, but also does not have symlink definitions.
Windows does not support symlink flags S_IFLNK.
So even I add symlink support by mapped-xattr, the MinGW library does not have symlink flags and get a build error.
And this flags is defined by Windows header files.
The impact of adding a new flags to an pre-defined structure (struct stat) is unknown.
So I think it is not a good idea to do that.
> > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > ---
> >
> > hw/9pfs/9p-local.h | 7 +
> > hw/9pfs/9p-util.h | 40 +-
> > hw/9pfs/9p-local.c | 4 -
> > hw/9pfs/9p-util-win32.c | 885
> > ++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 931 insertions(+), 5 deletions(-) create mode
> > 100644 hw/9pfs/9p-util-win32.c
> >
> > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h index
> > c8404063e5..02fd894ba3 100644
> > --- a/hw/9pfs/9p-local.h
> > +++ b/hw/9pfs/9p-local.h
> > @@ -15,6 +15,13 @@
> >
> > #include "9p-file-id.h"
> >
> > +typedef struct {
> > + P9_FILE_ID mountfd;
> > +#ifdef CONFIG_WIN32
> > + char *root_path;
> > +#endif
> > +} LocalData;
> > +
> > P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int
> flags,
> > mode_t mode); P9_FILE_ID
> > local_opendir_nofollow(FsContext *fs_ctx, const char *path); diff
> > --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > 1e7dc76345..82b2d0c3e4 100644
> > --- a/hw/9pfs/9p-util.h
> > +++ b/hw/9pfs/9p-util.h
> > @@ -90,26 +90,61 @@ static inline int errno_to_dotl(int err) {
> > return err;
> > }
> >
> > -#ifdef CONFIG_DARWIN
> > +#if defined(CONFIG_DARWIN)
> > #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0)
> > +#elif defined(CONFIG_WIN32)
> > +#define qemu_fgetxattr fgetxattr_win32
> > #else
> > #define qemu_fgetxattr fgetxattr
> > #endif
> >
> > +#ifdef CONFIG_WIN32
> > +#define qemu_openat openat_win32
> > +#define qemu_fstatat fstatat_win32
> > +#define qemu_mkdirat mkdirat_win32
> > +#define qemu_renameat renameat_win32
> > +#define qemu_utimensat utimensat_win32
> > +#define qemu_unlinkat unlinkat_win32
> > +#else
> > #define qemu_openat openat
> > #define qemu_fstatat fstatat
> > #define qemu_mkdirat mkdirat
> > #define qemu_renameat renameat
> > #define qemu_utimensat utimensat
> > #define qemu_unlinkat unlinkat
> > +#endif
> > +
> > +#ifdef CONFIG_WIN32
> > +char *get_full_path_win32(P9_FILE_ID fd, const char *name); ssize_t
> > +fgetxattr_win32(int fd, const char *name, void *value, size_t size);
> > +P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int flags,
> > + mode_t mode); int fstatat_win32(P9_FILE_ID
> > +dirfd, const char *pathname,
> > + struct stat *statbuf, int flags); int
> > +mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t mode);
> > +int renameat_win32(P9_FILE_ID olddirfd, const char *oldpath,
> > + P9_FILE_ID newdirfd, const char *newpath); int
> > +utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
> > + const struct timespec times[2], int flags); int
> > +unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags);
> > +int statfs_win32(const char *root_path, struct statfs *stbuf);
> > +P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name); P9_FILE_ID
> > +openat_file(P9_FILE_ID dirfd, const char *name, int flags,
> > + mode_t mode);
> > +#endif
> >
> > static inline void close_preserve_errno(P9_FILE_ID fd) {
> > int serrno = errno;
> > +#ifndef CONFIG_WIN32
> > close(fd);
> > +#else
> > + CloseHandle(fd);
> > +#endif
> > errno = serrno;
> > }
> >
> > +#ifndef CONFIG_WIN32
> > static inline P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char
> > *name) {
> > return qemu_openat(dirfd, name,
> > @@ -157,6 +192,7 @@ again:
> > errno = serrno;
> > return fd;
> > }
> > +#endif
> >
> > ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> > const char *name, void *value, size_t
> > size); @@ -167,6 +203,7 @@ ssize_t flistxattrat_nofollow(P9_FILE_ID
> > dirfd, const char *filename, ssize_t fremovexattrat_nofollow(P9_FILE_ID
> dirfd, const char *filename,
> > const char *name);
> >
> > +#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,
> > @@ -181,6 +218,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.
> > diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index
> > 7e8d8492ea..d0d6d93549 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 {
> > - P9_FILE_ID mountfd;
> > -} LocalData;
> > -
> > P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int
> flags,
> > mode_t mode) { diff --git
> > a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c new file mode
> > 100644 index 0000000000..953e7da6fa
> > --- /dev/null
> > +++ b/hw/9pfs/9p-util-win32.c
> > @@ -0,0 +1,885 @@
> > +/*
> > + * 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.
> > + */
> > +
> > +/*
> > + * This file contains Windows only functions for 9pfs.
> > + *
> > + * For 9pfs Windows host, the following features are different from Linux
> host:
> > + *
> > + * 1. Windows POSIX API does not provide the NO_FOLLOW flag, that means
> MinGW
> > + * cannot detect if a path is a symbolic link or not. Also Windows do
> not
> > + * provide POSIX compatible readlink(). Supporting symbolic link in
> 9pfs on
> > + * Windows may cause security issues, so symbolic link support is
> disabled
> > + * completely.
> > + *
> > + * 2. Windows file system does not support extended attributes directly.
> 9pfs
> > + * for Windows uses NTFS ADS (Alternate Data Streams) to emulate
> extended
> > + * attributes.
> > + *
> > + * 3. statfs() is not available on Windows. qemu_statfs() is used to
> emulate it.
> > + *
> > + * 4. On Windows trying to open a directory with the open() API will fail.
> > + * This is because Windows does not allow opening directory in normal
> usage.
> > + *
> > + * As a result of this, all xxx_at() functions won't work directly on
> > + * Windows, e.g.: openat(), unlinkat(), etc.
> > + *
> > + * As xxx_at() can prevent parent directory to be modified on Linux
> host,
> > + * to support this and prevent security issue, all xxx_at() APIs are
> replaced
> > + * by xxx_at_win32() and Windows handle is used to replace the
> directory fd.
> > + *
> > + * Windows file system does not allow replacing a file or directory if
> it is
> > + * referenced by a handle. Keep the handle open will lock and protect
> the
> > + * parent directory and make the access to files atomically.
> > + *
> > + * If we don't protect (lock) the parent directory, the parent
> directory may
> > + * be replaced by others (e.g.: a symbolic link) and cause security
> issues.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "qemu/error-report.h"
> > +#include "9p.h"
> > +#include "9p-util.h"
> > +#include "9p-local.h"
> > +
> > +#include <windows.h>
> > +#include <dirent.h>
> > +
> > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */
> > +
> > +/*
> > + * build_ads_name - construct Windows ADS name
> > + *
> > + * This function constructs Windows NTFS ADS (Alternate Data Streams)
> > +name
> > + * to <namebuf>.
> > + */
> > +static int build_ads_name(char *namebuf, size_t namebuf_len,
> > + const char *filename, const char *ads_name)
> > +{
> > + size_t total_size;
> > +
> > + total_size = strlen(filename) + strlen(ads_name) + 2;
> > + if (total_size > namebuf_len) {
> > + return -1;
> > + }
> > +
> > + /*
> > + * NTFS ADS (Alternate Data Streams) name format: filename:ads_name
> > + * e.g.: D:\1.txt:my_ads_name
> > + */
> > +
> > + strcpy(namebuf, filename);
> > + strcat(namebuf, ":");
> > + strcat(namebuf, ads_name);
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * copy_ads_name - copy ADS name from buffer returned by
> > +FindNextStreamW()
> > + *
> > + * This function removes string "$DATA" in ADS name string returned
> > +by
> > + * FindNextStreamW(), and copies the real ADS name to <namebuf>.
> > + */
> > +static ssize_t copy_ads_name(char *namebuf, size_t namebuf_len,
> > + char *full_ads_name) {
> > + char *p1, *p2;
> > +
> > + /*
> > + * NTFS ADS (Alternate Data Streams) name from enumerate data format:
> > + * :ads_name:$DATA, e.g.: :my_ads_name:$DATA
> > + *
> > + * ADS name from FindNextStreamW() always has ":$DATA" string at the
> end.
> > + *
> > + * This function copies ADS name to namebuf.
> > + */
> > +
> > + p1 = strchr(full_ads_name, ':');
> > + 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 > namebuf_len) {
> > + return -1;
> > + }
> > +
> > + memcpy(namebuf, p1 + 1, p2 - p1 - 1);
> > + namebuf[p2 - p1 - 1] = '\0';
> > +
> > + return p2 - p1;
> > +}
> > +
> > +/*
> > + * get_full_path_win32 - get full file name base on a handle
> > + *
> > + * This function gets full file name based on a handle specified by
> > +<fd> to
> > + * a file or directory.
> > + *
> > + * Caller function needs to free the file name string after use.
> > + */
> > +char *get_full_path_win32(P9_FILE_ID fd, const char *name) {
> > + g_autofree char *full_file_name = NULL;
> > + DWORD total_size;
> > + DWORD name_size;
> > +
> > + full_file_name = g_malloc0(NAME_MAX);
> > +
> > + /* get parent directory full file name */
> > + name_size = GetFinalPathNameByHandle(fd, full_file_name,
> > + NAME_MAX - 1,
> FILE_NAME_NORMALIZED);
> > + if (name_size == 0 || name_size > NAME_MAX - 1) {
> > + return NULL;
> > + }
> > +
> > + /* full path returned is the "\\?\" syntax, remove the lead string */
> > + memmove(full_file_name, full_file_name + 4, NAME_MAX - 4);
> > +
> > + if (name != NULL) {
> > + total_size = strlen(full_file_name) + strlen(name) + 2;
> > +
> > + if (total_size > NAME_MAX) {
> > + return NULL;
> > + }
> > +
> > + /* build sub-directory file name */
> > + strcat(full_file_name, "\\");
> > + strcat(full_file_name, name);
> > + }
> > +
> > + return g_steal_pointer(&full_file_name); }
> > +
> > +/*
> > + * fgetxattr_win32 - get extended attribute by fd
> > + *
> > + * This function gets extened attribute by <fd>. <fd> will be
> > +translated to
> > + * Windows handle.
> > + *
> > + * This function emulates extended attribute by NTFS ADS.
> > + */
> > +ssize_t fgetxattr_win32(int fd, const char *name, void *value, size_t
> > +size) {
> > + g_autofree char *full_file_name = NULL;
> > + char ads_file_name[NAME_MAX + 1] = {0};
> > + DWORD dwBytesRead;
> > + HANDLE hStream;
> > + HANDLE hFile;
> > +
> > + hFile = (HANDLE)_get_osfhandle(fd);
> > +
> > + full_file_name = get_full_path_win32(hFile, NULL);
> > + if (full_file_name == NULL) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0)
> {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + hStream = CreateFile(ads_file_name, 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;
> > +}
> > +
> > +/*
> > + * openat_win32 - emulate openat()
> > + *
> > + * This function emulates openat().
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So openat_win32() has to use a directory handle instead of a directory
> fd.
> > + *
> > + * For symbolic access:
> > + * 1. Parent directory handle <dirfd> should not be a symbolic link
> because
> > + * it is opened by openat_dir() which can prevent from opening a link
> to
> > + * a dirctory.
> > + * 2. Link flag in <mode> is not set because Windows does not have this
> flag.
> > + * Create a new symbolic link will be denied.
> > + * 3. This function checks file symbolic link attribute after open.
> > + *
> > + * So symbolic link will not be accessed by 9p client.
> > + */
> > +P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int flags,
> > + mode_t mode)
> > +{
> > + g_autofree char *full_file_name1 = NULL;
> > + g_autofree char *full_file_name2 = NULL;
> > + HANDLE hFile = INVALID_HANDLE_VALUE;
> > + int fd;
> > +
> > + full_file_name1 = get_full_path_win32(dirfd, pathname);
> > + if (full_file_name1 == NULL) {
> > + return hFile;
> > + }
> > +
> > + fd = open(full_file_name1, flags, mode);
> > + if (fd > 0) {
> > + DWORD attribute;
> > + hFile = (HANDLE)_get_osfhandle(fd);
> > +
> > + full_file_name2 = get_full_path_win32(hFile, NULL);
> > + attribute = GetFileAttributes(full_file_name2);
> > +
> > + /* check if it is a symbolic link */
> > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > + errno = EACCES;
> > + hFile = INVALID_HANDLE_VALUE;
> > + close(fd);
> > + }
> > + }
> > +
> > + return hFile;
> > +}
> > +
> > +/*
> > + * fstatat_win32 - emulate fstatat()
> > + *
> > + * This function emulates fstatat().
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So fstatat_win32() has to use a directory handle instead of a directory
> fd.
> > + *
> > + * Access to a symbolic link will be denied to prevent security issues.
> > + */
> > +int fstatat_win32(P9_FILE_ID dirfd, const char *pathname,
> > + struct stat *statbuf, int flags) {
> > + g_autofree char *full_file_name = NULL;
> > + HANDLE hFile = INVALID_HANDLE_VALUE;
> > + DWORD attribute;
> > + int err = 0;
> > + int ret = -1;
> > +
> > + full_file_name = get_full_path_win32(dirfd, pathname);
> > + if (full_file_name == NULL) {
> > + return ret;
> > + }
> > +
> > + /* open file to lock it */
> > + hFile = CreateFile(full_file_name, GENERIC_READ,
> > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> FILE_SHARE_DELETE,
> > + NULL,
> > + OPEN_EXISTING,
> > + FILE_FLAG_BACKUP_SEMANTICS
> > + | FILE_FLAG_OPEN_REPARSE_POINT,
> > + NULL);
> > +
> > + if (hFile == INVALID_HANDLE_VALUE) {
> > + err = EACCES;
> > + goto out;
> > + }
> > +
> > + attribute = GetFileAttributes(full_file_name);
> > +
> > + /* check if it is a symbolic link */
> > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > + errno = EACCES;
> > + goto out;
> > + }
> > +
> > + ret = stat(full_file_name, statbuf);
> > +
> > +out:
> > + if (hFile != INVALID_HANDLE_VALUE) {
> > + CloseHandle(hFile);
> > + }
> > +
> > + if (err != 0) {
> > + errno = err;
> > + }
> > + return ret;
> > +}
> > +
> > +/*
> > + * mkdirat_win32 - emulate mkdirat()
> > + *
> > + * This function emulates mkdirat().
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So mkdirat_win32() has to use a directory handle instead of a directory
> fd.
> > + */
> > +int mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t
> > +mode) {
> > + g_autofree char *full_file_name = NULL;
> > + int ret = -1;
> > +
> > + full_file_name = get_full_path_win32(dirfd, pathname);
> > + if (full_file_name == NULL) {
> > + return ret;
> > + }
> > +
> > + ret = mkdir(full_file_name);
> > +
> > + return ret;
> > +}
> > +
> > +/*
> > + * renameat_win32 - emulate renameat()
> > + *
> > + * This function emulates renameat().
> > + *
> > + * Windows POSIX API does not support openning a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So renameat_win32() has to use a directory handle instead of a
> directory fd.
> > + *
> > + * Access to a symbolic link will be denied to prevent security issues.
> > + */
> > +int renameat_win32(HANDLE olddirfd, const char *oldpath,
> > + HANDLE newdirfd, const char *newpath) {
> > + g_autofree char *full_old_name = NULL;
> > + g_autofree char *full_new_name = NULL;
> > + HANDLE hFile;
> > + DWORD attribute;
> > + int err = 0;
> > + int ret = -1;
> > +
> > + full_old_name = get_full_path_win32(olddirfd, oldpath);
> > + full_new_name = get_full_path_win32(newdirfd, newpath);
> > + if (full_old_name == NULL || full_new_name == NULL) {
> > + return ret;
> > + }
> > +
> > + /* open file to lock it */
> > + hFile = CreateFile(full_old_name, GENERIC_READ,
> > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> FILE_SHARE_DELETE,
> > + NULL,
> > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> > + NULL);
> > +
> > + attribute = GetFileAttributes(full_old_name);
> > +
> > + /* check if it is a symbolic link */
> > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > + err = EACCES;
> > + goto out;
> > + }
> > +
> > + CloseHandle(hFile);
> > +
> > + ret = rename(full_old_name, full_new_name);
> > +out:
> > + if (err != 0) {
> > + errno = err;
> > + }
> > + return ret;
> > +}
> > +
> > +/*
> > + * utimensat_win32 - emulate utimensat()
> > + *
> > + * This function emulates utimensat().
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So utimensat_win32() has to use a directory handle instead of a
> directory fd.
> > + *
> > + * Access to a symbolic link will be denied to prevent security issues.
> > + */
> > +int utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
> > + const struct timespec times[2], int flags) {
> > + g_autofree char *full_file_name = NULL;
> > + HANDLE hFile = INVALID_HANDLE_VALUE;
> > + DWORD attribute;
> > + struct utimbuf tm;
> > + int err = 0;
> > + int ret = -1;
> > +
> > + full_file_name = get_full_path_win32(dirfd, pathname);
> > + if (full_file_name == NULL) {
> > + return ret;
> > + }
> > +
> > + /* open file to lock it */
> > + hFile = CreateFile(full_file_name, GENERIC_READ,
> > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> FILE_SHARE_DELETE,
> > + NULL,
> > + OPEN_EXISTING,
> > + FILE_FLAG_BACKUP_SEMANTICS
> > + | FILE_FLAG_OPEN_REPARSE_POINT,
> > + NULL);
> > +
> > + if (hFile == INVALID_HANDLE_VALUE) {
> > + err = EACCES;
> > + goto out;
> > + }
> > +
> > + attribute = GetFileAttributes(full_file_name);
> > +
> > + /* check if it is a symbolic link */
> > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > + errno = EACCES;
> > + goto out;
> > + }
> > +
> > + tm.actime = times[0].tv_sec;
> > + tm.modtime = times[1].tv_sec;
> > +
> > + ret = utime(full_file_name, &tm);
> > +
> > +out:
> > + if (hFile != INVALID_HANDLE_VALUE) {
> > + CloseHandle(hFile);
> > + }
> > +
> > + if (err != 0) {
> > + errno = err;
> > + }
> > + return ret;
> > +}
> > +
> > +/*
> > + * unlinkat_win32 - emulate unlinkat()
> > + *
> > + * This function emulates unlinkat().
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So unlinkat_win32() has to use a directory handle instead of a
> directory fd.
> > + *
> > + * Access to a symbolic link will be denied to prevent security issues.
> > + */
> > +
> > +int unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags)
> > +{
> > + g_autofree char *full_file_name = NULL;
> > + HANDLE hFile;
> > + DWORD attribute;
> > + int err = 0;
> > + int ret = -1;
> > +
> > + full_file_name = get_full_path_win32(dirfd, pathname);
> > + if (full_file_name == NULL) {
> > + return ret;
> > + }
> > +
> > + /* open file to prevent other one modify it */
> > + hFile = CreateFile(full_file_name, GENERIC_READ,
> > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> FILE_SHARE_DELETE,
> > + NULL,
> > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> > + NULL);
> > +
> > + attribute = GetFileAttributes(full_file_name);
> > +
> > + /* check if it is a symbolic link */
> > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > + err = EACCES;
> > + goto out;
> > + }
> > +
> > + if (flags == AT_REMOVEDIR) { /* remove directory */
> > + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
> > + err = ENOTDIR;
> > + goto out;
> > + }
> > + ret = rmdir(full_file_name);
> > + } else { /* remove regular file */
> > + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) != 0) {
> > + err = EISDIR;
> > + goto out;
> > + }
> > + ret = remove(full_file_name);
> > + }
> > +
> > + /* after last handle closed, file will be removed */
> > + CloseHandle(hFile);
> > +
> > +out:
> > + if (err != 0) {
> > + errno = err;
> > + }
> > + return ret;
> > +}
> > +
> > +/*
> > + * statfs_win32 - statfs() on Windows
> > + *
> > + * This function emulates statfs() on Windows host.
> > + */
> > +int statfs_win32(const char *path, struct statfs *stbuf) {
> > + char RealPath[4] = { 0 };
> > + unsigned long SectorsPerCluster;
> > + unsigned long BytesPerSector;
> > + unsigned long NumberOfFreeClusters;
> > + unsigned long TotalNumberOfClusters;
> > +
> > + /* only need first 3 bytes, e.g. "C:\ABC", only need "C:\" */
> > + memcpy(RealPath, path, 3);
> > +
> > + if (GetDiskFreeSpace(RealPath, &SectorsPerCluster, &BytesPerSector,
> > + &NumberOfFreeClusters, &TotalNumberOfClusters) ==
> 0) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + stbuf->f_type = V9FS_MAGIC;
> > + stbuf->f_bsize =
> > + (__fsword_t)SectorsPerCluster * (__fsword_t)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;
> > +}
> > +
> > +/*
> > + * openat_dir - emulate openat_dir()
> > + *
> > + * This function emulates openat_dir().
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * So openat_dir() has to use a directory handle instead of a directory
> fd.
> > + *
> > + * Access to a symbolic link will be denied to prevent security issues.
> > + */
> > +P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name) {
> > + g_autofree char *full_file_name = NULL;
> > + HANDLE hSubDir;
> > + DWORD attribute;
> > +
> > + full_file_name = get_full_path_win32(dirfd, name);
> > + if (full_file_name == NULL) {
> > + return INVALID_HANDLE_VALUE;
> > + }
> > +
> > + attribute = GetFileAttributes(full_file_name);
> > + if (attribute == INVALID_FILE_ATTRIBUTES) {
> > + return INVALID_HANDLE_VALUE;
> > + }
> > +
> > + /* check if it is a directory */
> > + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
> > + return INVALID_HANDLE_VALUE;
> > + }
> > +
> > + /* do not allow opening a symbolic link */
> > + if ((attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > + return INVALID_HANDLE_VALUE;
> > + }
> > +
> > + /* open it */
> > + hSubDir = CreateFile(full_file_name, GENERIC_READ,
> > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> FILE_SHARE_DELETE,
> > + NULL,
> > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
> > + return hSubDir;
> > +}
> > +
> > +P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
> > + mode_t mode)
> > +{
> > + return openat_win32(dirfd, name, flags | _O_BINARY, mode); }
> > +
> > +/*
> > + * fgetxattrat_nofollow - get extended attribute
> > + *
> > + * This function gets extended attribute from file <path> in the
> > +directory
> > + * specified by <dirfd>. The extended atrribute name is specified by
> > +<name>
> > + * and return value will be put in <value>.
> > + *
> > + * This function emulates extended attribute by NTFS ADS.
> > + */
> > +ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> > + const char *name, void *value, size_t
> > +size) {
> > + g_autofree char *full_file_name = NULL;
> > + char ads_file_name[NAME_MAX + 1] = { 0 };
> > + DWORD dwBytesRead;
> > + HANDLE hStream;
> > +
> > + full_file_name = get_full_path_win32(dirfd, path);
> > + if (full_file_name == NULL) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0)
> {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + hStream = CreateFile(ads_file_name, 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;
> > +}
> > +
> > +/*
> > + * fsetxattrat_nofollow - set extended attribute
> > + *
> > + * This function set extended attribute to file <path> in the
> > +directory
> > + * specified by <dirfd>.
> > + *
> > + * This function emulates extended attribute by NTFS ADS.
> > + */
> > +
> > +int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *path, const char
> *name,
> > + void *value, size_t size, int flags) {
> > + g_autofree char *full_file_name = NULL;
> > + char ads_file_name[NAME_MAX + 1] = { 0 };
> > + DWORD dwBytesWrite;
> > + HANDLE hStream;
> > +
> > + full_file_name = get_full_path_win32(dirfd, path);
> > + if (full_file_name == NULL) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) < 0)
> {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + hStream = CreateFile(ads_file_name, 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;
> > +}
> > +
> > +/*
> > + * flistxattrat_nofollow - list extended attribute
> > + *
> > + * This function gets extended attribute lists from file <filename>
> > +in the
> > + * directory specified by <dirfd>. Lists returned will be put in <list>.
> > + *
> > + * This function emulates extended attribute by NTFS ADS.
> > + */
> > +ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> > + char *list, size_t size) {
> > + g_autofree char *full_file_name = NULL;
> > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 };
> > + char full_ads_name[NAME_MAX + 1];
> > + WIN32_FIND_STREAM_DATA fsd;
> > + BOOL bFindNext;
> > + char *list_ptr = list;
> > + size_t list_left_size = size;
> > + HANDLE hFind;
> > + int ret;
> > +
> > + full_file_name = get_full_path_win32(dirfd, filename);
> > + if (full_file_name == NULL) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + /*
> > + * ADS enumerate function only has WCHAR version, so we need to
> > + * covert filename to utf-8 string.
> > + */
> > + ret = MultiByteToWideChar(CP_UTF8, 0, full_file_name,
> > + strlen(full_file_name), WideCharStr,
> NAME_MAX);
> > + if (ret == 0) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd,
> 0);
> > + if (hFind == INVALID_HANDLE_VALUE) {
> > + errno = ENODATA;
> > + return -1;
> > + }
> > +
> > + do {
> > + memset(full_ads_name, 0, sizeof(full_ads_name));
> > +
> > + /*
> > + * ADS enumerate function only has WCHAR version, so we need to
> > + * covert cStreamName to utf-8 string.
> > + */
> > + ret = WideCharToMultiByte(CP_UTF8, 0,
> > + fsd.cStreamName, wcslen(fsd.cStreamName)
> + 1,
> > + full_ads_name, sizeof(full_ads_name) -
> 1,
> > + NULL, NULL);
> > + if (ret == 0) {
> > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
> > + errno = ERANGE;
> > + }
> > + CloseHandle(hFind);
> > + return -1;
> > + }
> > +
> > + ret = copy_ads_name(list_ptr, list_left_size, full_ads_name);
> > + if (ret < 0) {
> > + errno = ERANGE;
> > + CloseHandle(hFind);
> > + return -1;
> > + }
> > +
> > + list_ptr = list_ptr + ret;
> > + list_left_size = list_left_size - ret;
> > +
> > + bFindNext = FindNextStreamW(hFind, &fsd);
> > + } while (bFindNext);
> > +
> > + CloseHandle(hFind);
> > +
> > + return size - list_left_size;
> > +}
> > +
> > +/*
> > + * fremovexattrat_nofollow - remove extended attribute
> > + *
> > + * This function removes an extended attribute from file <filename>
> > +in the
> > + * directory specified by <dirfd>.
> > + *
> > + * This function emulates extended attribute by NTFS ADS.
> > + */
> > +ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> > + const char *name) {
> > + g_autofree char *full_file_name = NULL;
> > + char ads_file_name[NAME_MAX + 1] = { 0 };
> > +
> > + full_file_name = get_full_path_win32(dirfd, filename);
> > + if (full_file_name == NULL) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + if (build_ads_name(ads_file_name, NAME_MAX, filename, name) < 0) {
> > + errno = EIO;
> > + return -1;
> > + }
> > +
> > + if (DeleteFile(ads_file_name) != 0) {
> > + if (GetLastError() == ERROR_FILE_NOT_FOUND) {
> > + errno = ENODATA;
> > + return -1;
> > + }
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * local_opendir_nofollow - open a Windows directory
> > + *
> > + * This function returns a Windows file handle of the directory
> > +specified by
> > + * <dirpath> based on 9pfs mount point.
> > + *
> > + * Windows POSIX API does not support opening a directory by open().
> > +Only
> > + * handle of directory can be opened by CreateFile().
> > + *
> > + * This function checks the resolved path of <dirpath>. If the
> > +resolved
> > + * path is not in the scope of root directory (e.g. by symbolic
> > +link), then
> > + * this function will fail to prevent any security issues.
> > + */
> > +HANDLE local_opendir_nofollow(FsContext *fs_ctx, const char *dirpath)
> > +{
> > + g_autofree char *full_file_name = NULL;
> > + LocalData *data = fs_ctx->private;
> > + HANDLE hDir;
> > +
> > + hDir = openat_dir(data->mountfd, dirpath);
> > + if (hDir == INVALID_HANDLE_VALUE) {
> > + return INVALID_HANDLE_VALUE;
> > + }
> > +
> > + full_file_name = get_full_path_win32(hDir, NULL);
> > + if (full_file_name == NULL) {
> > + CloseHandle(hDir);
> > + return INVALID_HANDLE_VALUE;
> > + }
> > +
> > + /*
> > + * Check if the resolved path is in the root directory scope:
> > + * data->root_path and full_file_name are full path with symbolic
> > + * link resolved, so fs_ctx->root_path must be in the head of
> > + * full_file_name. If not, that means guest OS tries to open a file
> not
> > + * in the scope of mount point. This operation should be denied.
> > + */
> > + if (memcmp(full_file_name, data->root_path,
> > + strlen(data->root_path)) != 0) {
> > + CloseHandle(hDir);
> > + hDir = INVALID_HANDLE_VALUE;
> > + }
> > +
> > + return hDir;
> > +}
> >
>
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-01 15:13 ` Shi, Guohuai
@ 2022-11-01 15:20 ` Shi, Guohuai
2022-11-01 18:22 ` Christian Schoenebeck
0 siblings, 1 reply; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-01 15:20 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Shi, Guohuai
> Sent: Tuesday, November 1, 2022 23:13
> To: Christian Schoenebeck <qemu_oss@crudebyte.com>; qemu-devel@nongnu.org
> Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>
> Subject: RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities
> functions for 9pfs
>
>
>
> > -----Original Message-----
> > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > Sent: Tuesday, November 1, 2022 22:28
> > To: qemu-devel@nongnu.org
> > Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz
> > <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>
> > Subject: Re: [PATCH 07/16] hw/9pfs: Implement Windows specific
> > utilities functions for 9pfs
> >
> > [Please note: This e-mail is from an EXTERNAL e-mail address]
> >
> > On Monday, October 24, 2022 6:57:50 AM CET Bin Meng wrote:
> > > From: Guohuai Shi <guohuai.shi@windriver.com>
> > >
> > > Windows POSIX API and MinGW library do not provide the NO_FOLLOW
> > > flag, and do not allow opening a directory by POSIX open(). This
> > > causes all
> > > xxx_at() functions cannot work directly. However, we can provide
> > > Windows handle based functions to emulate xxx_at() functions (e.g.:
> > > openat_win32, utimensat_win32, etc.).
> > >
> > > Windows does not support extended attributes. 9pfs for Windows uses
> > > NTFS ADS (Alternate Data Streams) to emulate extended attributes.
> > >
> > > Windows does not provide POSIX compatible readlink(), and symbolic
> > > link feature in 9pfs will be disabled on Windows.
> >
> > Wouldn't it be more user friendly if the relevant error locations
> > would use something like error_report_once() and suggesting to enable
> > mapped(-xattr) to make 9p symlinks on guest working if desired by the user?
> >
> > Probably this error case would need to wrapped into a dedicated
> > function, otherwise I guess error_report_once() would fire several
> > times by different callers.
> >
>
> Windows (MinGW) does not only support symlink, but also does not have symlink
> definitions.
> Windows does not support symlink flags S_IFLNK.
>
> So even I add symlink support by mapped-xattr, the MinGW library does not
> have symlink flags and get a build error.
> And this flags is defined by Windows header files.
> The impact of adding a new flags to an pre-defined structure (struct stat) is
> unknown.
>
> So I think it is not a good idea to do that.
Because Windows does not support symlink, so error_report_once() and report it to user will be OK.
But mapped-xattr could not work.
>
> > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > > ---
> > >
> > > hw/9pfs/9p-local.h | 7 +
> > > hw/9pfs/9p-util.h | 40 +-
> > > hw/9pfs/9p-local.c | 4 -
> > > hw/9pfs/9p-util-win32.c | 885
> > > ++++++++++++++++++++++++++++++++++++++++
> > > 4 files changed, 931 insertions(+), 5 deletions(-) create mode
> > > 100644 hw/9pfs/9p-util-win32.c
> > >
> > > diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h index
> > > c8404063e5..02fd894ba3 100644
> > > --- a/hw/9pfs/9p-local.h
> > > +++ b/hw/9pfs/9p-local.h
> > > @@ -15,6 +15,13 @@
> > >
> > > #include "9p-file-id.h"
> > >
> > > +typedef struct {
> > > + P9_FILE_ID mountfd;
> > > +#ifdef CONFIG_WIN32
> > > + char *root_path;
> > > +#endif
> > > +} LocalData;
> > > +
> > > P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path,
> > > int
> > flags,
> > > mode_t mode); P9_FILE_ID
> > > local_opendir_nofollow(FsContext *fs_ctx, const char *path); diff
> > > --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > > 1e7dc76345..82b2d0c3e4 100644
> > > --- a/hw/9pfs/9p-util.h
> > > +++ b/hw/9pfs/9p-util.h
> > > @@ -90,26 +90,61 @@ static inline int errno_to_dotl(int err) {
> > > return err;
> > > }
> > >
> > > -#ifdef CONFIG_DARWIN
> > > +#if defined(CONFIG_DARWIN)
> > > #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0)
> > > +#elif defined(CONFIG_WIN32)
> > > +#define qemu_fgetxattr fgetxattr_win32
> > > #else
> > > #define qemu_fgetxattr fgetxattr
> > > #endif
> > >
> > > +#ifdef CONFIG_WIN32
> > > +#define qemu_openat openat_win32
> > > +#define qemu_fstatat fstatat_win32
> > > +#define qemu_mkdirat mkdirat_win32
> > > +#define qemu_renameat renameat_win32
> > > +#define qemu_utimensat utimensat_win32
> > > +#define qemu_unlinkat unlinkat_win32
> > > +#else
> > > #define qemu_openat openat
> > > #define qemu_fstatat fstatat
> > > #define qemu_mkdirat mkdirat
> > > #define qemu_renameat renameat
> > > #define qemu_utimensat utimensat
> > > #define qemu_unlinkat unlinkat
> > > +#endif
> > > +
> > > +#ifdef CONFIG_WIN32
> > > +char *get_full_path_win32(P9_FILE_ID fd, const char *name); ssize_t
> > > +fgetxattr_win32(int fd, const char *name, void *value, size_t
> > > +size); P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname,
> int flags,
> > > + mode_t mode); int fstatat_win32(P9_FILE_ID
> > > +dirfd, const char *pathname,
> > > + struct stat *statbuf, int flags); int
> > > +mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t mode);
> > > +int renameat_win32(P9_FILE_ID olddirfd, const char *oldpath,
> > > + P9_FILE_ID newdirfd, const char *newpath); int
> > > +utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
> > > + const struct timespec times[2], int flags); int
> > > +unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags);
> > > +int statfs_win32(const char *root_path, struct statfs *stbuf);
> > > +P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name);
> > > +P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
> > > + mode_t mode); #endif
> > >
> > > static inline void close_preserve_errno(P9_FILE_ID fd) {
> > > int serrno = errno;
> > > +#ifndef CONFIG_WIN32
> > > close(fd);
> > > +#else
> > > + CloseHandle(fd);
> > > +#endif
> > > errno = serrno;
> > > }
> > >
> > > +#ifndef CONFIG_WIN32
> > > static inline P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char
> > > *name) {
> > > return qemu_openat(dirfd, name, @@ -157,6 +192,7 @@ again:
> > > errno = serrno;
> > > return fd;
> > > }
> > > +#endif
> > >
> > > ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> > > const char *name, void *value, size_t
> > > size); @@ -167,6 +203,7 @@ ssize_t flistxattrat_nofollow(P9_FILE_ID
> > > dirfd, const char *filename, ssize_t fremovexattrat_nofollow(P9_FILE_ID
> > dirfd, const char *filename,
> > > const char *name);
> > >
> > > +#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,
> > > @@ -181,6 +218,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.
> > > diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index
> > > 7e8d8492ea..d0d6d93549 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 {
> > > - P9_FILE_ID mountfd;
> > > -} LocalData;
> > > -
> > > P9_FILE_ID local_open_nofollow(FsContext *fs_ctx, const char *path, int
> > flags,
> > > mode_t mode) { diff --git
> > > a/hw/9pfs/9p-util-win32.c b/hw/9pfs/9p-util-win32.c new file mode
> > > 100644 index 0000000000..953e7da6fa
> > > --- /dev/null
> > > +++ b/hw/9pfs/9p-util-win32.c
> > > @@ -0,0 +1,885 @@
> > > +/*
> > > + * 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.
> > > + */
> > > +
> > > +/*
> > > + * This file contains Windows only functions for 9pfs.
> > > + *
> > > + * For 9pfs Windows host, the following features are different from
> Linux
> > host:
> > > + *
> > > + * 1. Windows POSIX API does not provide the NO_FOLLOW flag, that means
> > MinGW
> > > + * cannot detect if a path is a symbolic link or not. Also Windows do
> > not
> > > + * provide POSIX compatible readlink(). Supporting symbolic link in
> > 9pfs on
> > > + * Windows may cause security issues, so symbolic link support is
> > disabled
> > > + * completely.
> > > + *
> > > + * 2. Windows file system does not support extended attributes directly.
> > 9pfs
> > > + * for Windows uses NTFS ADS (Alternate Data Streams) to emulate
> > extended
> > > + * attributes.
> > > + *
> > > + * 3. statfs() is not available on Windows. qemu_statfs() is used to
> > emulate it.
> > > + *
> > > + * 4. On Windows trying to open a directory with the open() API will
> fail.
> > > + * This is because Windows does not allow opening directory in normal
> > usage.
> > > + *
> > > + * As a result of this, all xxx_at() functions won't work directly on
> > > + * Windows, e.g.: openat(), unlinkat(), etc.
> > > + *
> > > + * As xxx_at() can prevent parent directory to be modified on Linux
> > host,
> > > + * to support this and prevent security issue, all xxx_at() APIs are
> > replaced
> > > + * by xxx_at_win32() and Windows handle is used to replace the
> > directory fd.
> > > + *
> > > + * Windows file system does not allow replacing a file or directory
> if
> > it is
> > > + * referenced by a handle. Keep the handle open will lock and protect
> > the
> > > + * parent directory and make the access to files atomically.
> > > + *
> > > + * If we don't protect (lock) the parent directory, the parent
> > directory may
> > > + * be replaced by others (e.g.: a symbolic link) and cause security
> > issues.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +#include "qapi/error.h"
> > > +#include "qemu/error-report.h"
> > > +#include "9p.h"
> > > +#include "9p-util.h"
> > > +#include "9p-local.h"
> > > +
> > > +#include <windows.h>
> > > +#include <dirent.h>
> > > +
> > > +#define V9FS_MAGIC 0x53465039 /* string "9PFS" */
> > > +
> > > +/*
> > > + * build_ads_name - construct Windows ADS name
> > > + *
> > > + * This function constructs Windows NTFS ADS (Alternate Data Streams)
> > > +name
> > > + * to <namebuf>.
> > > + */
> > > +static int build_ads_name(char *namebuf, size_t namebuf_len,
> > > + const char *filename, const char *ads_name)
> > > +{
> > > + size_t total_size;
> > > +
> > > + total_size = strlen(filename) + strlen(ads_name) + 2;
> > > + if (total_size > namebuf_len) {
> > > + return -1;
> > > + }
> > > +
> > > + /*
> > > + * NTFS ADS (Alternate Data Streams) name format: filename:ads_name
> > > + * e.g.: D:\1.txt:my_ads_name
> > > + */
> > > +
> > > + strcpy(namebuf, filename);
> > > + strcat(namebuf, ":");
> > > + strcat(namebuf, ads_name);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/*
> > > + * copy_ads_name - copy ADS name from buffer returned by
> > > +FindNextStreamW()
> > > + *
> > > + * This function removes string "$DATA" in ADS name string returned
> > > +by
> > > + * FindNextStreamW(), and copies the real ADS name to <namebuf>.
> > > + */
> > > +static ssize_t copy_ads_name(char *namebuf, size_t namebuf_len,
> > > + char *full_ads_name) {
> > > + char *p1, *p2;
> > > +
> > > + /*
> > > + * NTFS ADS (Alternate Data Streams) name from enumerate data
> format:
> > > + * :ads_name:$DATA, e.g.: :my_ads_name:$DATA
> > > + *
> > > + * ADS name from FindNextStreamW() always has ":$DATA" string at the
> > end.
> > > + *
> > > + * This function copies ADS name to namebuf.
> > > + */
> > > +
> > > + p1 = strchr(full_ads_name, ':');
> > > + 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 > namebuf_len) {
> > > + return -1;
> > > + }
> > > +
> > > + memcpy(namebuf, p1 + 1, p2 - p1 - 1);
> > > + namebuf[p2 - p1 - 1] = '\0';
> > > +
> > > + return p2 - p1;
> > > +}
> > > +
> > > +/*
> > > + * get_full_path_win32 - get full file name base on a handle
> > > + *
> > > + * This function gets full file name based on a handle specified by
> > > +<fd> to
> > > + * a file or directory.
> > > + *
> > > + * Caller function needs to free the file name string after use.
> > > + */
> > > +char *get_full_path_win32(P9_FILE_ID fd, const char *name) {
> > > + g_autofree char *full_file_name = NULL;
> > > + DWORD total_size;
> > > + DWORD name_size;
> > > +
> > > + full_file_name = g_malloc0(NAME_MAX);
> > > +
> > > + /* get parent directory full file name */
> > > + name_size = GetFinalPathNameByHandle(fd, full_file_name,
> > > + NAME_MAX - 1,
> > FILE_NAME_NORMALIZED);
> > > + if (name_size == 0 || name_size > NAME_MAX - 1) {
> > > + return NULL;
> > > + }
> > > +
> > > + /* full path returned is the "\\?\" syntax, remove the lead string
> */
> > > + memmove(full_file_name, full_file_name + 4, NAME_MAX - 4);
> > > +
> > > + if (name != NULL) {
> > > + total_size = strlen(full_file_name) + strlen(name) + 2;
> > > +
> > > + if (total_size > NAME_MAX) {
> > > + return NULL;
> > > + }
> > > +
> > > + /* build sub-directory file name */
> > > + strcat(full_file_name, "\\");
> > > + strcat(full_file_name, name);
> > > + }
> > > +
> > > + return g_steal_pointer(&full_file_name); }
> > > +
> > > +/*
> > > + * fgetxattr_win32 - get extended attribute by fd
> > > + *
> > > + * This function gets extened attribute by <fd>. <fd> will be
> > > +translated to
> > > + * Windows handle.
> > > + *
> > > + * This function emulates extended attribute by NTFS ADS.
> > > + */
> > > +ssize_t fgetxattr_win32(int fd, const char *name, void *value, size_t
> > > +size) {
> > > + g_autofree char *full_file_name = NULL;
> > > + char ads_file_name[NAME_MAX + 1] = {0};
> > > + DWORD dwBytesRead;
> > > + HANDLE hStream;
> > > + HANDLE hFile;
> > > +
> > > + hFile = (HANDLE)_get_osfhandle(fd);
> > > +
> > > + full_file_name = get_full_path_win32(hFile, NULL);
> > > + if (full_file_name == NULL) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) <
> 0)
> > {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + hStream = CreateFile(ads_file_name, 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;
> > > +}
> > > +
> > > +/*
> > > + * openat_win32 - emulate openat()
> > > + *
> > > + * This function emulates openat().
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So openat_win32() has to use a directory handle instead of a
> directory
> > fd.
> > > + *
> > > + * For symbolic access:
> > > + * 1. Parent directory handle <dirfd> should not be a symbolic link
> > because
> > > + * it is opened by openat_dir() which can prevent from opening a link
> > to
> > > + * a dirctory.
> > > + * 2. Link flag in <mode> is not set because Windows does not have this
> > flag.
> > > + * Create a new symbolic link will be denied.
> > > + * 3. This function checks file symbolic link attribute after open.
> > > + *
> > > + * So symbolic link will not be accessed by 9p client.
> > > + */
> > > +P9_FILE_ID openat_win32(P9_FILE_ID dirfd, const char *pathname, int
> flags,
> > > + mode_t mode)
> > > +{
> > > + g_autofree char *full_file_name1 = NULL;
> > > + g_autofree char *full_file_name2 = NULL;
> > > + HANDLE hFile = INVALID_HANDLE_VALUE;
> > > + int fd;
> > > +
> > > + full_file_name1 = get_full_path_win32(dirfd, pathname);
> > > + if (full_file_name1 == NULL) {
> > > + return hFile;
> > > + }
> > > +
> > > + fd = open(full_file_name1, flags, mode);
> > > + if (fd > 0) {
> > > + DWORD attribute;
> > > + hFile = (HANDLE)_get_osfhandle(fd);
> > > +
> > > + full_file_name2 = get_full_path_win32(hFile, NULL);
> > > + attribute = GetFileAttributes(full_file_name2);
> > > +
> > > + /* check if it is a symbolic link */
> > > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > > + errno = EACCES;
> > > + hFile = INVALID_HANDLE_VALUE;
> > > + close(fd);
> > > + }
> > > + }
> > > +
> > > + return hFile;
> > > +}
> > > +
> > > +/*
> > > + * fstatat_win32 - emulate fstatat()
> > > + *
> > > + * This function emulates fstatat().
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So fstatat_win32() has to use a directory handle instead of a
> directory
> > fd.
> > > + *
> > > + * Access to a symbolic link will be denied to prevent security issues.
> > > + */
> > > +int fstatat_win32(P9_FILE_ID dirfd, const char *pathname,
> > > + struct stat *statbuf, int flags) {
> > > + g_autofree char *full_file_name = NULL;
> > > + HANDLE hFile = INVALID_HANDLE_VALUE;
> > > + DWORD attribute;
> > > + int err = 0;
> > > + int ret = -1;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, pathname);
> > > + if (full_file_name == NULL) {
> > > + return ret;
> > > + }
> > > +
> > > + /* open file to lock it */
> > > + hFile = CreateFile(full_file_name, GENERIC_READ,
> > > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> > FILE_SHARE_DELETE,
> > > + NULL,
> > > + OPEN_EXISTING,
> > > + FILE_FLAG_BACKUP_SEMANTICS
> > > + | FILE_FLAG_OPEN_REPARSE_POINT,
> > > + NULL);
> > > +
> > > + if (hFile == INVALID_HANDLE_VALUE) {
> > > + err = EACCES;
> > > + goto out;
> > > + }
> > > +
> > > + attribute = GetFileAttributes(full_file_name);
> > > +
> > > + /* check if it is a symbolic link */
> > > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > > + errno = EACCES;
> > > + goto out;
> > > + }
> > > +
> > > + ret = stat(full_file_name, statbuf);
> > > +
> > > +out:
> > > + if (hFile != INVALID_HANDLE_VALUE) {
> > > + CloseHandle(hFile);
> > > + }
> > > +
> > > + if (err != 0) {
> > > + errno = err;
> > > + }
> > > + return ret;
> > > +}
> > > +
> > > +/*
> > > + * mkdirat_win32 - emulate mkdirat()
> > > + *
> > > + * This function emulates mkdirat().
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So mkdirat_win32() has to use a directory handle instead of a
> directory
> > fd.
> > > + */
> > > +int mkdirat_win32(P9_FILE_ID dirfd, const char *pathname, mode_t
> > > +mode) {
> > > + g_autofree char *full_file_name = NULL;
> > > + int ret = -1;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, pathname);
> > > + if (full_file_name == NULL) {
> > > + return ret;
> > > + }
> > > +
> > > + ret = mkdir(full_file_name);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +/*
> > > + * renameat_win32 - emulate renameat()
> > > + *
> > > + * This function emulates renameat().
> > > + *
> > > + * Windows POSIX API does not support openning a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So renameat_win32() has to use a directory handle instead of a
> > directory fd.
> > > + *
> > > + * Access to a symbolic link will be denied to prevent security issues.
> > > + */
> > > +int renameat_win32(HANDLE olddirfd, const char *oldpath,
> > > + HANDLE newdirfd, const char *newpath) {
> > > + g_autofree char *full_old_name = NULL;
> > > + g_autofree char *full_new_name = NULL;
> > > + HANDLE hFile;
> > > + DWORD attribute;
> > > + int err = 0;
> > > + int ret = -1;
> > > +
> > > + full_old_name = get_full_path_win32(olddirfd, oldpath);
> > > + full_new_name = get_full_path_win32(newdirfd, newpath);
> > > + if (full_old_name == NULL || full_new_name == NULL) {
> > > + return ret;
> > > + }
> > > +
> > > + /* open file to lock it */
> > > + hFile = CreateFile(full_old_name, GENERIC_READ,
> > > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> > FILE_SHARE_DELETE,
> > > + NULL,
> > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> > > + NULL);
> > > +
> > > + attribute = GetFileAttributes(full_old_name);
> > > +
> > > + /* check if it is a symbolic link */
> > > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > > + err = EACCES;
> > > + goto out;
> > > + }
> > > +
> > > + CloseHandle(hFile);
> > > +
> > > + ret = rename(full_old_name, full_new_name);
> > > +out:
> > > + if (err != 0) {
> > > + errno = err;
> > > + }
> > > + return ret;
> > > +}
> > > +
> > > +/*
> > > + * utimensat_win32 - emulate utimensat()
> > > + *
> > > + * This function emulates utimensat().
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So utimensat_win32() has to use a directory handle instead of a
> > directory fd.
> > > + *
> > > + * Access to a symbolic link will be denied to prevent security issues.
> > > + */
> > > +int utimensat_win32(P9_FILE_ID dirfd, const char *pathname,
> > > + const struct timespec times[2], int flags) {
> > > + g_autofree char *full_file_name = NULL;
> > > + HANDLE hFile = INVALID_HANDLE_VALUE;
> > > + DWORD attribute;
> > > + struct utimbuf tm;
> > > + int err = 0;
> > > + int ret = -1;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, pathname);
> > > + if (full_file_name == NULL) {
> > > + return ret;
> > > + }
> > > +
> > > + /* open file to lock it */
> > > + hFile = CreateFile(full_file_name, GENERIC_READ,
> > > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> > FILE_SHARE_DELETE,
> > > + NULL,
> > > + OPEN_EXISTING,
> > > + FILE_FLAG_BACKUP_SEMANTICS
> > > + | FILE_FLAG_OPEN_REPARSE_POINT,
> > > + NULL);
> > > +
> > > + if (hFile == INVALID_HANDLE_VALUE) {
> > > + err = EACCES;
> > > + goto out;
> > > + }
> > > +
> > > + attribute = GetFileAttributes(full_file_name);
> > > +
> > > + /* check if it is a symbolic link */
> > > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > > + errno = EACCES;
> > > + goto out;
> > > + }
> > > +
> > > + tm.actime = times[0].tv_sec;
> > > + tm.modtime = times[1].tv_sec;
> > > +
> > > + ret = utime(full_file_name, &tm);
> > > +
> > > +out:
> > > + if (hFile != INVALID_HANDLE_VALUE) {
> > > + CloseHandle(hFile);
> > > + }
> > > +
> > > + if (err != 0) {
> > > + errno = err;
> > > + }
> > > + return ret;
> > > +}
> > > +
> > > +/*
> > > + * unlinkat_win32 - emulate unlinkat()
> > > + *
> > > + * This function emulates unlinkat().
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So unlinkat_win32() has to use a directory handle instead of a
> > directory fd.
> > > + *
> > > + * Access to a symbolic link will be denied to prevent security issues.
> > > + */
> > > +
> > > +int unlinkat_win32(P9_FILE_ID dirfd, const char *pathname, int flags)
> > > +{
> > > + g_autofree char *full_file_name = NULL;
> > > + HANDLE hFile;
> > > + DWORD attribute;
> > > + int err = 0;
> > > + int ret = -1;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, pathname);
> > > + if (full_file_name == NULL) {
> > > + return ret;
> > > + }
> > > +
> > > + /* open file to prevent other one modify it */
> > > + hFile = CreateFile(full_file_name, GENERIC_READ,
> > > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> > FILE_SHARE_DELETE,
> > > + NULL,
> > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> > > + NULL);
> > > +
> > > + attribute = GetFileAttributes(full_file_name);
> > > +
> > > + /* check if it is a symbolic link */
> > > + if ((attribute == INVALID_FILE_ATTRIBUTES)
> > > + || (attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > > + err = EACCES;
> > > + goto out;
> > > + }
> > > +
> > > + if (flags == AT_REMOVEDIR) { /* remove directory */
> > > + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
> > > + err = ENOTDIR;
> > > + goto out;
> > > + }
> > > + ret = rmdir(full_file_name);
> > > + } else { /* remove regular file */
> > > + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) != 0) {
> > > + err = EISDIR;
> > > + goto out;
> > > + }
> > > + ret = remove(full_file_name);
> > > + }
> > > +
> > > + /* after last handle closed, file will be removed */
> > > + CloseHandle(hFile);
> > > +
> > > +out:
> > > + if (err != 0) {
> > > + errno = err;
> > > + }
> > > + return ret;
> > > +}
> > > +
> > > +/*
> > > + * statfs_win32 - statfs() on Windows
> > > + *
> > > + * This function emulates statfs() on Windows host.
> > > + */
> > > +int statfs_win32(const char *path, struct statfs *stbuf) {
> > > + char RealPath[4] = { 0 };
> > > + unsigned long SectorsPerCluster;
> > > + unsigned long BytesPerSector;
> > > + unsigned long NumberOfFreeClusters;
> > > + unsigned long TotalNumberOfClusters;
> > > +
> > > + /* only need first 3 bytes, e.g. "C:\ABC", only need "C:\" */
> > > + memcpy(RealPath, path, 3);
> > > +
> > > + if (GetDiskFreeSpace(RealPath, &SectorsPerCluster, &BytesPerSector,
> > > + &NumberOfFreeClusters, &TotalNumberOfClusters)
> ==
> > 0) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + stbuf->f_type = V9FS_MAGIC;
> > > + stbuf->f_bsize =
> > > + (__fsword_t)SectorsPerCluster * (__fsword_t)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;
> > > +}
> > > +
> > > +/*
> > > + * openat_dir - emulate openat_dir()
> > > + *
> > > + * This function emulates openat_dir().
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * So openat_dir() has to use a directory handle instead of a directory
> > fd.
> > > + *
> > > + * Access to a symbolic link will be denied to prevent security issues.
> > > + */
> > > +P9_FILE_ID openat_dir(P9_FILE_ID dirfd, const char *name) {
> > > + g_autofree char *full_file_name = NULL;
> > > + HANDLE hSubDir;
> > > + DWORD attribute;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, name);
> > > + if (full_file_name == NULL) {
> > > + return INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + attribute = GetFileAttributes(full_file_name);
> > > + if (attribute == INVALID_FILE_ATTRIBUTES) {
> > > + return INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + /* check if it is a directory */
> > > + if ((attribute & FILE_ATTRIBUTE_DIRECTORY) == 0) {
> > > + return INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + /* do not allow opening a symbolic link */
> > > + if ((attribute & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
> > > + return INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + /* open it */
> > > + hSubDir = CreateFile(full_file_name, GENERIC_READ,
> > > + FILE_SHARE_READ | FILE_SHARE_WRITE |
> > FILE_SHARE_DELETE,
> > > + NULL,
> > > + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
> NULL);
> > > + return hSubDir;
> > > +}
> > > +
> > > +P9_FILE_ID openat_file(P9_FILE_ID dirfd, const char *name, int flags,
> > > + mode_t mode)
> > > +{
> > > + return openat_win32(dirfd, name, flags | _O_BINARY, mode); }
> > > +
> > > +/*
> > > + * fgetxattrat_nofollow - get extended attribute
> > > + *
> > > + * This function gets extended attribute from file <path> in the
> > > +directory
> > > + * specified by <dirfd>. The extended atrribute name is specified by
> > > +<name>
> > > + * and return value will be put in <value>.
> > > + *
> > > + * This function emulates extended attribute by NTFS ADS.
> > > + */
> > > +ssize_t fgetxattrat_nofollow(P9_FILE_ID dirfd, const char *path,
> > > + const char *name, void *value, size_t
> > > +size) {
> > > + g_autofree char *full_file_name = NULL;
> > > + char ads_file_name[NAME_MAX + 1] = { 0 };
> > > + DWORD dwBytesRead;
> > > + HANDLE hStream;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, path);
> > > + if (full_file_name == NULL) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) <
> 0)
> > {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + hStream = CreateFile(ads_file_name, 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;
> > > +}
> > > +
> > > +/*
> > > + * fsetxattrat_nofollow - set extended attribute
> > > + *
> > > + * This function set extended attribute to file <path> in the
> > > +directory
> > > + * specified by <dirfd>.
> > > + *
> > > + * This function emulates extended attribute by NTFS ADS.
> > > + */
> > > +
> > > +int fsetxattrat_nofollow(P9_FILE_ID dirfd, const char *path, const char
> > *name,
> > > + void *value, size_t size, int flags) {
> > > + g_autofree char *full_file_name = NULL;
> > > + char ads_file_name[NAME_MAX + 1] = { 0 };
> > > + DWORD dwBytesWrite;
> > > + HANDLE hStream;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, path);
> > > + if (full_file_name == NULL) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + if (build_ads_name(ads_file_name, NAME_MAX, full_file_name, name) <
> 0)
> > {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + hStream = CreateFile(ads_file_name, 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;
> > > +}
> > > +
> > > +/*
> > > + * flistxattrat_nofollow - list extended attribute
> > > + *
> > > + * This function gets extended attribute lists from file <filename>
> > > +in the
> > > + * directory specified by <dirfd>. Lists returned will be put in <list>.
> > > + *
> > > + * This function emulates extended attribute by NTFS ADS.
> > > + */
> > > +ssize_t flistxattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> > > + char *list, size_t size) {
> > > + g_autofree char *full_file_name = NULL;
> > > + WCHAR WideCharStr[NAME_MAX + 1] = { 0 };
> > > + char full_ads_name[NAME_MAX + 1];
> > > + WIN32_FIND_STREAM_DATA fsd;
> > > + BOOL bFindNext;
> > > + char *list_ptr = list;
> > > + size_t list_left_size = size;
> > > + HANDLE hFind;
> > > + int ret;
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, filename);
> > > + if (full_file_name == NULL) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + /*
> > > + * ADS enumerate function only has WCHAR version, so we need to
> > > + * covert filename to utf-8 string.
> > > + */
> > > + ret = MultiByteToWideChar(CP_UTF8, 0, full_file_name,
> > > + strlen(full_file_name), WideCharStr,
> > NAME_MAX);
> > > + if (ret == 0) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + hFind = FindFirstStreamW(WideCharStr, FindStreamInfoStandard, &fsd,
> > 0);
> > > + if (hFind == INVALID_HANDLE_VALUE) {
> > > + errno = ENODATA;
> > > + return -1;
> > > + }
> > > +
> > > + do {
> > > + memset(full_ads_name, 0, sizeof(full_ads_name));
> > > +
> > > + /*
> > > + * ADS enumerate function only has WCHAR version, so we need to
> > > + * covert cStreamName to utf-8 string.
> > > + */
> > > + ret = WideCharToMultiByte(CP_UTF8, 0,
> > > + fsd.cStreamName,
> wcslen(fsd.cStreamName)
> > + 1,
> > > + full_ads_name, sizeof(full_ads_name) -
> > 1,
> > > + NULL, NULL);
> > > + if (ret == 0) {
> > > + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
> > > + errno = ERANGE;
> > > + }
> > > + CloseHandle(hFind);
> > > + return -1;
> > > + }
> > > +
> > > + ret = copy_ads_name(list_ptr, list_left_size, full_ads_name);
> > > + if (ret < 0) {
> > > + errno = ERANGE;
> > > + CloseHandle(hFind);
> > > + return -1;
> > > + }
> > > +
> > > + list_ptr = list_ptr + ret;
> > > + list_left_size = list_left_size - ret;
> > > +
> > > + bFindNext = FindNextStreamW(hFind, &fsd);
> > > + } while (bFindNext);
> > > +
> > > + CloseHandle(hFind);
> > > +
> > > + return size - list_left_size;
> > > +}
> > > +
> > > +/*
> > > + * fremovexattrat_nofollow - remove extended attribute
> > > + *
> > > + * This function removes an extended attribute from file <filename>
> > > +in the
> > > + * directory specified by <dirfd>.
> > > + *
> > > + * This function emulates extended attribute by NTFS ADS.
> > > + */
> > > +ssize_t fremovexattrat_nofollow(P9_FILE_ID dirfd, const char *filename,
> > > + const char *name) {
> > > + g_autofree char *full_file_name = NULL;
> > > + char ads_file_name[NAME_MAX + 1] = { 0 };
> > > +
> > > + full_file_name = get_full_path_win32(dirfd, filename);
> > > + if (full_file_name == NULL) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + if (build_ads_name(ads_file_name, NAME_MAX, filename, name) < 0) {
> > > + errno = EIO;
> > > + return -1;
> > > + }
> > > +
> > > + if (DeleteFile(ads_file_name) != 0) {
> > > + if (GetLastError() == ERROR_FILE_NOT_FOUND) {
> > > + errno = ENODATA;
> > > + return -1;
> > > + }
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +/*
> > > + * local_opendir_nofollow - open a Windows directory
> > > + *
> > > + * This function returns a Windows file handle of the directory
> > > +specified by
> > > + * <dirpath> based on 9pfs mount point.
> > > + *
> > > + * Windows POSIX API does not support opening a directory by open().
> > > +Only
> > > + * handle of directory can be opened by CreateFile().
> > > + *
> > > + * This function checks the resolved path of <dirpath>. If the
> > > +resolved
> > > + * path is not in the scope of root directory (e.g. by symbolic
> > > +link), then
> > > + * this function will fail to prevent any security issues.
> > > + */
> > > +HANDLE local_opendir_nofollow(FsContext *fs_ctx, const char *dirpath)
> > > +{
> > > + g_autofree char *full_file_name = NULL;
> > > + LocalData *data = fs_ctx->private;
> > > + HANDLE hDir;
> > > +
> > > + hDir = openat_dir(data->mountfd, dirpath);
> > > + if (hDir == INVALID_HANDLE_VALUE) {
> > > + return INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + full_file_name = get_full_path_win32(hDir, NULL);
> > > + if (full_file_name == NULL) {
> > > + CloseHandle(hDir);
> > > + return INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + /*
> > > + * Check if the resolved path is in the root directory scope:
> > > + * data->root_path and full_file_name are full path with symbolic
> > > + * link resolved, so fs_ctx->root_path must be in the head of
> > > + * full_file_name. If not, that means guest OS tries to open a file
> > not
> > > + * in the scope of mount point. This operation should be denied.
> > > + */
> > > + if (memcmp(full_file_name, data->root_path,
> > > + strlen(data->root_path)) != 0) {
> > > + CloseHandle(hDir);
> > > + hDir = INVALID_HANDLE_VALUE;
> > > + }
> > > +
> > > + return hDir;
> > > +}
> > >
> >
> >
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 11/16] hw/9pfs: Add Linux error number definition
2022-10-24 4:57 ` [PATCH 11/16] hw/9pfs: Add Linux error number definition Bin Meng
@ 2022-11-01 15:21 ` Christian Schoenebeck
0 siblings, 0 replies; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 15:21 UTC (permalink / raw)
To: qemu-devel; +Cc: Guohuai Shi, Greg Kurz, Bin Meng
On Monday, October 24, 2022 6:57:54 AM CET Bin Meng wrote:
> From: Guohuai Shi <guohuai.shi@windriver.com>
>
> When using 9p2000.L protocol, the errno should use the Linux errno.
> Currently magic numbers with comments are used. Replace these with
> macros for future expansion.
>
> 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-util.h | 38 ++++++----
> 2 files changed, 176 insertions(+), 13 deletions(-)
> create mode 100644 hw/9pfs/9p-linux-errno.h
>
> diff --git a/hw/9pfs/9p-linux-errno.h b/hw/9pfs/9p-linux-errno.h
> new file mode 100644
> index 0000000000..56c37fa293
> --- /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 non-Linux hosts) 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.
> + */
Yeah, that's my main concern here. I wonder if there is isn't a better
maintainable solution at least for the list of Linux errors, so that we don't
have to manually update the L_ macros below.
> +
> +#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-util.h b/hw/9pfs/9p-util.h
> index 3d154e9103..281fdcbf8c 100644
> --- a/hw/9pfs/9p-util.h
> +++ b/hw/9pfs/9p-util.h
> @@ -62,8 +62,11 @@ static inline uint64_t host_dev_to_dotl_dev(dev_t dev)
> #endif
> }
>
> +#include "9p-linux-errno.h"
> +
> /* Translates errno from host -> Linux if needed */
> -static inline int errno_to_dotl(int err) {
> +static inline int errno_to_dotl(int err)
> +{
> #if defined(CONFIG_LINUX)
> /* nothing to translate (Linux -> Linux) */
> #elif defined(CONFIG_DARWIN)
> @@ -73,18 +76,27 @@ static inline int errno_to_dotl(int err) {
> * FIXME: Only most important errnos translated here yet, this should be
> * extended to as many errnos being translated as possible in future.
> */
> - if (err == ENAMETOOLONG) {
> - err = 36; /* ==ENAMETOOLONG on Linux */
> - } else if (err == ENOTEMPTY) {
> - err = 39; /* ==ENOTEMPTY on Linux */
> - } else if (err == ELOOP) {
> - err = 40; /* ==ELOOP on Linux */
> - } else if (err == ENOATTR) {
> - err = 61; /* ==ENODATA on Linux */
> - } else if (err == ENOTSUP) {
> - err = 95; /* ==EOPNOTSUPP on Linux */
> - } else if (err == EOPNOTSUPP) {
> - err = 95; /* ==EOPNOTSUPP on Linux */
> + switch (err) {
> + case ENAMETOOLONG:
> + err = L_ENAMETOOLONG;
> + break;
> + case ENOTEMPTY:
> + err = L_ENOTEMPTY;
> + break;
> + case ELOOP:
> + err = L_ELOOP;
> + break;
> + case ENOATTR:
> + err = L_ENODATA;
> + break;
> + case ENOTSUP
> + err = L_EOPNOTSUPP;
> + break;
> + case EOPNOTSUPP:
> + err = L_EOPNOTSUPP;
> + break;
> + default:
> + break;
> }
What about a more compact solution like:
switch (err) {
case ENAMETOOLONG: return L_ENAMETOOLONG;
case ENOTEMPTY: return L_ENOTEMPTY;
...
default: return err;
}
Previously I suggested to use an array-solution with designated initializers
instead, but I guess we don't know how high these error numbers could become
on certain systems.
> #else
> #error Missing errno translation to Linux for this host system
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32
2022-10-24 4:57 ` [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32 Bin Meng
2022-10-25 15:55 ` Thomas Huth
@ 2022-11-01 15:32 ` Christian Schoenebeck
1 sibling, 0 replies; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 15:32 UTC (permalink / raw)
To: qemu-devel
Cc: Guohuai Shi, Xuzhou Cheng, Greg Kurz, Laurent Vivier,
Paolo Bonzini, Thomas Huth, Bin Meng
On Monday, October 24, 2022 6:57:58 AM CET Bin Meng wrote:
> From: Guohuai Shi <guohuai.shi@windriver.com>
>
> Windows does not provide the getuid() API. Let's create a local
> one and return a fixed value 0 as the uid for testing.
>
> Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> Signed-off-by: Xuzhou Cheng <xuzhou.cheng@windriver.com>
> Signed-off-by: Bin Meng <bin.meng@windriver.com>
> ---
>
> tests/qtest/virtio-9p-test.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c
> index 25305a4cf7..e81e3e3709 100644
> --- a/tests/qtest/virtio-9p-test.c
> +++ b/tests/qtest/virtio-9p-test.c
> @@ -34,6 +34,13 @@ static uint32_t genfid(void)
> return fid_generator++;
> }
>
> +#ifdef CONFIG_WIN32
> +static uint32_t getuid(void)
> +{
> + return 0;
> +}
> +#endif
> +
Due to recent 9p tests restructuring changes, same would be needed for new
tests/qtest/libqos/virtio-9p-client.c source file, as it's also calling
getuid().
> /**
> * Splits the @a in string by @a delim into individual (non empty) strings
> * and outputs them to @a out. The output array @a out is NULL terminated.
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-11-01 15:04 ` Christian Schoenebeck
@ 2022-11-01 15:34 ` Shi, Guohuai
2022-11-01 18:59 ` Christian Schoenebeck
0 siblings, 1 reply; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-01 15:34 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> Sent: Tuesday, November 1, 2022 23:04
> To: qemu-devel@nongnu.org
> Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz <groug@kaod.org>;
> Meng, Bin <Bin.Meng@windriver.com>
> Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features
> for Windows
>
> [Please note: This e-mail is from an EXTERNAL e-mail address]
>
> On Monday, October 24, 2022 6:57:52 AM CET Bin Meng wrote:
> > From: Guohuai Shi <guohuai.shi@windriver.com>
> >
> > Some flags and features are not supported on Windows, like mknod,
> > readlink, file mode, etc. Update the codes for Windows.
> >
> > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > ---
> >
> > hw/9pfs/9p-util.h | 6 +++-
> > hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-----
> > 2 files changed, 86 insertions(+), 10 deletions(-)
> >
> > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > 82b2d0c3e4..3d154e9103 100644
> > --- a/hw/9pfs/9p-util.h
> > +++ b/hw/9pfs/9p-util.h
> > @@ -53,8 +53,10 @@ static inline uint64_t makedev_dotl(uint32_t dev_major,
> uint32_t dev_minor)
> > */
> > static inline uint64_t host_dev_to_dotl_dev(dev_t dev) { -#ifdef
> > CONFIG_LINUX
> > +#if defined(CONFIG_LINUX)
> > return dev;
> > +#elif defined(CONFIG_WIN32)
> > + return 0;
>
> Really?
Check MS this document: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fstat-fstat32-fstat64-fstati64-fstat32i64-fstat64i32?view=msvc-170
st_rdev: If a device, fd; otherwise 0.
st_dev: If a device, fd; otherwise 0.
So for any file open, it should be 0.
>
> > #else
> > return makedev_dotl(major(dev), minor(dev)); #endif @@ -260,7
> > +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t mode,
> > dev_t dev);
> > +#endif
> >
> > #endif
> > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 6c4af86240..771aab34ac
> > 100644
> > --- a/hw/9pfs/9p.c
> > +++ b/hw/9pfs/9p.c
> > @@ -39,6 +39,11 @@
> > #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;
> > static int open_fd_rc;
> > @@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
> > DotlOpenflagMap dotl_oflag_map[] = {
> > { P9_DOTL_CREATE, O_CREAT },
> > { P9_DOTL_EXCL, O_EXCL },
> > +#ifndef CONFIG_WIN32
> > { P9_DOTL_NOCTTY , O_NOCTTY },
> > +#endif
> > { P9_DOTL_TRUNC, O_TRUNC },
> > { P9_DOTL_APPEND, O_APPEND },
> > +#ifndef CONFIG_WIN32
> > { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
> > { P9_DOTL_DSYNC, O_DSYNC },
> > { P9_DOTL_FASYNC, FASYNC },
> > -#ifndef CONFIG_DARWIN
> > +#endif
> > +#ifdef CONFIG_LINUX
>
> Better
>
> #if !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32)
>
It is OK.
> Otherwise it might automatically opt-out other future platforms
> unintentionally.
>
> > { P9_DOTL_NOATIME, O_NOATIME },
> > /*
> > * On Darwin, we could map to F_NOCACHE, which is @@ -151,8
> > +160,10 @@ static int dotl_to_open_flags(int flags) #endif
> > { P9_DOTL_LARGEFILE, O_LARGEFILE },
> > { P9_DOTL_DIRECTORY, O_DIRECTORY },
> > +#ifndef CONFIG_WIN32
> > { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
> > { P9_DOTL_SYNC, O_SYNC },
> > +#endif
> > };
> >
> > for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { @@ -179,8
> > +190,11 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
> > * Filter the client open flags
> > */
> > flags = dotl_to_open_flags(oflags);
> > - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
> > -#ifndef CONFIG_DARWIN
> > + flags &= ~(O_CREAT);
> > +#ifndef CONFIG_WIN32
> > + flags &= ~(O_NOCTTY | O_ASYNC);
> > +#endif
> > +#ifdef CONFIG_LINUX
>
> Same as above: better explicitly opt-out than the other way around.
>
It is OK.
> > /*
> > * Ignore direct disk access hint until the server supports it.
> > */
> > @@ -986,9 +1000,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;
> > }
> > @@ -1097,6 +1113,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;
> > }
> > @@ -1106,6 +1123,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;
> > @@ -1118,6 +1136,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;
> > }
> > @@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t mode,
> V9fsString *extension)
> > if (mode & P9_STAT_MODE_SETVTX) {
> > ret |= S_ISVTX;
> > }
> > +#endif
> >
> > return ret;
> > }
> > @@ -1182,6 +1202,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;
> > }
> > @@ -1193,11 +1214,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;
> > }
> > @@ -1209,6 +1232,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;
> > }
> > @@ -1247,9 +1271,17 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu,
> V9fsPath *path,
> > return err;
> > }
> > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
> > + unsigned maj, min;
> > +
> > +#ifndef CONFIG_WIN32
> > + maj = major(stbuf->st_rdev);
> > + min = minor(stbuf->st_rdev);
> > +#else
> > + maj = min = 0;
> > +#endif
>
> Really?
See above link.
>
> > v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
> > S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
> > - major(stbuf->st_rdev), minor(stbuf->st_rdev));
> > + maj, min);
> > } 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); @@
> > -1317,7 +1349,14 @@ 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) {
> > - return blksize_to_iounit(pdu, stbuf->st_blksize);
> > + int32_t blksize;
> > +
> > +#ifndef CONFIG_WIN32
> > + blksize = stbuf->st_blksize);
> > +#else
> > + blksize = 0;
> > +#endif
>
> Really?
Windows struct stat does not have such field. See above link.
>
> > + return blksize_to_iounit(pdu, blksize);
> > }
> >
> > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat
> > *stbuf, @@ -1332,7 +1371,11 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu,
> const struct stat *stbuf,
> > v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
> > v9lstat->st_size = stbuf->st_size;
> > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
> > +#ifndef CONFIG_WIN32
> > v9lstat->st_blocks = stbuf->st_blocks;
> > +#else
> > + v9lstat->st_blocks = 0;
> > +#endif
>
> Really?
Windows struct stat does not have such field. See above link.
>
> > v9lstat->st_atime_sec = stbuf->st_atime;
> > v9lstat->st_mtime_sec = stbuf->st_mtime;
> > v9lstat->st_ctime_sec = stbuf->st_ctime; @@ -1340,7 +1383,8 @@
> > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
> > v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
> > v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
> > v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec; -#else
> > +#endif
> > +#ifdef CONFIG_LINUX
> > v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
> > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
> > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; @@ -2471,6
> > +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu,
> V9fsFidState *fidp,
> > struct dirent *dent;
> > struct stat *st;
> > struct V9fsDirEnt *entries = NULL;
> > + unsigned char d_type = 0;
> >
> > /*
> > * inode remapping requires the device id, which in turn might be
> > @@ -2540,10 +2585,13 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU
> *pdu, V9fsFidState *fidp,
> > v9fs_string_init(&name);
> > v9fs_string_sprintf(&name, "%s", dent->d_name);
> >
> > +#ifndef CONFIG_WIN32
> > + d_type = dent->d_type;
> > +#endif
> > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
> > len = pdu_marshal(pdu, 11 + count, "Qqbs",
> > &qid, off,
> > - dent->d_type, &name);
> > + d_type, &name);
>
> Are you saying that d_type is not initialized with zero already?
struct dirent is defined by MinGW, it does not have d_type member:
https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt/dirent.h
>
> > v9fs_string_free(&name);
> >
> > @@ -2873,8 +2921,12 @@ 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;
> > }
> > @@ -2899,8 +2951,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
>
> As with previous patches, I would consider making the user aware to use
> mapped(-xattr) with something like error_report_once().
OK, got it.
>
> > if (err < 0) {
> > goto out;
> > }
> > @@ -3634,7 +3690,7 @@ out_nofid:
> >
> > static void coroutine_fn v9fs_mknod(void *opaque) {
> > -
> > +#ifndef CONFIG_WIN32
> > int mode;
> > gid_t gid;
> > int32_t fid;
> > @@ -3691,6 +3747,10 @@ out:
> > out_nofid:
> > pdu_complete(pdu, err);
> > v9fs_string_free(&name);
> > +#else
> > + V9fsPDU *pdu = opaque;
> > + pdu_complete(pdu, -1);
> > +#endif
> > }
> >
> > /*
> > @@ -3963,7 +4023,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.
> > @@ -3980,6 +4040,7 @@ out_nofid:
> >
> > static void coroutine_fn v9fs_xattrcreate(void *opaque) {
> > +#ifndef CONFIG_WIN32
> > int flags, rflags = 0;
> > int32_t fid;
> > uint64_t size;
> > @@ -4041,10 +4102,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;
> > @@ -4080,6 +4146,10 @@ out:
> > put_fid(pdu, fidp);
> > out_nofid:
> > pdu_complete(pdu, err);
> > +#else
> > + V9fsPDU *pdu = opaque;
> > + pdu_complete(pdu, -1);
> > +#endif
>
> Unnecessary double declaration of pdu.
>
OK, got it.
> > }
> >
> > static CoroutineEntry *pdu_co_handlers[] = { @@ -4341,6 +4411,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"); @@ -4348,4
> > +4419,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
>
> Really?
Windows does not provide getrlimit()
>
> > }
> >
>
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-01 15:20 ` Shi, Guohuai
@ 2022-11-01 18:22 ` Christian Schoenebeck
2022-11-02 3:07 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 18:22 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Meng, Bin, Shi, Guohuai
On Tuesday, November 1, 2022 4:20:53 PM CET Shi, Guohuai wrote:
>
[...]
> > > > Windows POSIX API and MinGW library do not provide the NO_FOLLOW
> > > > flag, and do not allow opening a directory by POSIX open(). This
> > > > causes all
> > > > xxx_at() functions cannot work directly. However, we can provide
> > > > Windows handle based functions to emulate xxx_at() functions (e.g.:
> > > > openat_win32, utimensat_win32, etc.).
> > > >
> > > > Windows does not support extended attributes. 9pfs for Windows uses
> > > > NTFS ADS (Alternate Data Streams) to emulate extended attributes.
> > > >
> > > > Windows does not provide POSIX compatible readlink(), and symbolic
> > > > link feature in 9pfs will be disabled on Windows.
> > >
> > > Wouldn't it be more user friendly if the relevant error locations
> > > would use something like error_report_once() and suggesting to enable
> > > mapped(-xattr) to make 9p symlinks on guest working if desired by the user?
> > >
> > > Probably this error case would need to wrapped into a dedicated
> > > function, otherwise I guess error_report_once() would fire several
> > > times by different callers.
> > >
> >
> > Windows (MinGW) does not only support symlink, but also does not have symlink
> > definitions.
> > Windows does not support symlink flags S_IFLNK.
> >
> > So even I add symlink support by mapped-xattr, the MinGW library does not
> > have symlink flags and get a build error.
> > And this flags is defined by Windows header files.
> > The impact of adding a new flags to an pre-defined structure (struct stat) is
> > unknown.
> >
> > So I think it is not a good idea to do that.
>
> Because Windows does not support symlink, so error_report_once() and report it to user will be OK.
> But mapped-xattr could not work.
Showing an error makes sense for "passthrough" security model, but not for the
"mapped" one.
Just to avoid misapprehensions: are you aware that there is already a system-
agnostic implementation for symlinks if "mapped" is used?
When mapped security model is enabled, then creating symlinks on guest will
simply create a corresponding *regular* file on host and the content of that
regular file on host is the pointing path as raw string. Additionally the
symlink flag is added to "virtfs.mode" xattr to mark that regular file as a
symlink, a virtual one that is. So this does not require any support for
symlinks by either the underlying host file system, nor by host OS.
Likewise interpreting and walking those virtual symlinks in "mapped" mode is
also implemented in the local fs driver already.
Best regards,
Christian Schoenebeck
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-11-01 15:34 ` Shi, Guohuai
@ 2022-11-01 18:59 ` Christian Schoenebeck
2022-11-02 3:44 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-01 18:59 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Meng, Bin, Shi, Guohuai
On Tuesday, November 1, 2022 4:34:54 PM CET Shi, Guohuai wrote:
>
> > -----Original Message-----
> > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > Sent: Tuesday, November 1, 2022 23:04
> > To: qemu-devel@nongnu.org
> > Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz <groug@kaod.org>;
> > Meng, Bin <Bin.Meng@windriver.com>
> > Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features
> > for Windows
> >
> > [Please note: This e-mail is from an EXTERNAL e-mail address]
> >
> > On Monday, October 24, 2022 6:57:52 AM CET Bin Meng wrote:
> > > From: Guohuai Shi <guohuai.shi@windriver.com>
> > >
> > > Some flags and features are not supported on Windows, like mknod,
> > > readlink, file mode, etc. Update the codes for Windows.
> > >
> > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > > ---
> > >
> > > hw/9pfs/9p-util.h | 6 +++-
> > > hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-----
> > > 2 files changed, 86 insertions(+), 10 deletions(-)
> > >
> > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > > 82b2d0c3e4..3d154e9103 100644
> > > --- a/hw/9pfs/9p-util.h
> > > +++ b/hw/9pfs/9p-util.h
> > > @@ -53,8 +53,10 @@ static inline uint64_t makedev_dotl(uint32_t dev_major,
> > uint32_t dev_minor)
> > > */
> > > static inline uint64_t host_dev_to_dotl_dev(dev_t dev) { -#ifdef
> > > CONFIG_LINUX
> > > +#if defined(CONFIG_LINUX)
> > > return dev;
> > > +#elif defined(CONFIG_WIN32)
> > > + return 0;
> >
> > Really?
>
> Check MS this document: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fstat-fstat32-fstat64-fstati64-fstat32i64-fstat64i32?view=msvc-170
> st_rdev: If a device, fd; otherwise 0.
> st_dev: If a device, fd; otherwise 0.
>
> So for any file open, it should be 0.
Yeah, but that function translates a corresponding device ID for *Linux* guest
side. And the intention is to avoid e.g. file ID collisions on guest side.
Because for a Linux guest, the two-tuple (device number, inode number) makes a
system-wide unique file ID.
If you just return zero here, that might be OK if only one 9p directory is
exported to guest, but say you have "C:\foo\" exported and "D:\bar\" exported
and mounted via 9p to guest, then guest would assume every file with the same
inode number on those two to be the same files. But they are not. They are on
two different drives actually.
>
> >
> > > #else
> > > return makedev_dotl(major(dev), minor(dev)); #endif @@ -260,7
> > > +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t mode,
> > > dev_t dev);
> > > +#endif
> > >
> > > #endif
> > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 6c4af86240..771aab34ac
> > > 100644
> > > --- a/hw/9pfs/9p.c
> > > +++ b/hw/9pfs/9p.c
> > > @@ -39,6 +39,11 @@
> > > #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;
> > > static int open_fd_rc;
> > > @@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
> > > DotlOpenflagMap dotl_oflag_map[] = {
> > > { P9_DOTL_CREATE, O_CREAT },
> > > { P9_DOTL_EXCL, O_EXCL },
> > > +#ifndef CONFIG_WIN32
> > > { P9_DOTL_NOCTTY , O_NOCTTY },
> > > +#endif
> > > { P9_DOTL_TRUNC, O_TRUNC },
> > > { P9_DOTL_APPEND, O_APPEND },
> > > +#ifndef CONFIG_WIN32
> > > { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
> > > { P9_DOTL_DSYNC, O_DSYNC },
> > > { P9_DOTL_FASYNC, FASYNC },
> > > -#ifndef CONFIG_DARWIN
> > > +#endif
> > > +#ifdef CONFIG_LINUX
> >
> > Better
> >
> > #if !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32)
> >
>
> It is OK.
You got my point, hopefully:
> > Otherwise it might automatically opt-out other future platforms
> > unintentionally.
> >
> > > { P9_DOTL_NOATIME, O_NOATIME },
> > > /*
> > > * On Darwin, we could map to F_NOCACHE, which is @@ -151,8
> > > +160,10 @@ static int dotl_to_open_flags(int flags) #endif
> > > { P9_DOTL_LARGEFILE, O_LARGEFILE },
> > > { P9_DOTL_DIRECTORY, O_DIRECTORY },
> > > +#ifndef CONFIG_WIN32
> > > { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
> > > { P9_DOTL_SYNC, O_SYNC },
> > > +#endif
> > > };
> > >
> > > for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { @@ -179,8
> > > +190,11 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
> > > * Filter the client open flags
> > > */
> > > flags = dotl_to_open_flags(oflags);
> > > - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
> > > -#ifndef CONFIG_DARWIN
> > > + flags &= ~(O_CREAT);
> > > +#ifndef CONFIG_WIN32
> > > + flags &= ~(O_NOCTTY | O_ASYNC);
> > > +#endif
> > > +#ifdef CONFIG_LINUX
> >
> > Same as above: better explicitly opt-out than the other way around.
> >
>
> It is OK.
>
> > > /*
> > > * Ignore direct disk access hint until the server supports it.
> > > */
> > > @@ -986,9 +1000,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;
> > > }
> > > @@ -1097,6 +1113,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;
> > > }
> > > @@ -1106,6 +1123,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;
> > > @@ -1118,6 +1136,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;
> > > }
> > > @@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t mode,
> > V9fsString *extension)
> > > if (mode & P9_STAT_MODE_SETVTX) {
> > > ret |= S_ISVTX;
> > > }
> > > +#endif
> > >
> > > return ret;
> > > }
> > > @@ -1182,6 +1202,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;
> > > }
> > > @@ -1193,11 +1214,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;
> > > }
> > > @@ -1209,6 +1232,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;
> > > }
> > > @@ -1247,9 +1271,17 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu,
> > V9fsPath *path,
> > > return err;
> > > }
> > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
> > > + unsigned maj, min;
> > > +
> > > +#ifndef CONFIG_WIN32
> > > + maj = major(stbuf->st_rdev);
> > > + min = minor(stbuf->st_rdev);
> > > +#else
> > > + maj = min = 0;
> > > +#endif
> >
> > Really?
>
> See above link.
>
> >
> > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
> > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
> > > - major(stbuf->st_rdev), minor(stbuf->st_rdev));
> > > + maj, min);
> > > } 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); @@
> > > -1317,7 +1349,14 @@ 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) {
> > > - return blksize_to_iounit(pdu, stbuf->st_blksize);
> > > + int32_t blksize;
> > > +
> > > +#ifndef CONFIG_WIN32
> > > + blksize = stbuf->st_blksize);
> > > +#else
> > > + blksize = 0;
> > > +#endif
> >
> > Really?
>
> Windows struct stat does not have such field. See above link.
Yeah, but you cannot simply return zero here, because that information is
interpreted on guest side as the optimum chunk size for I/O. So some apps
might misbehave e.g. by trying allocate buffers with zero size, throwing
division by zero exceptions, or trying to read() / write() with zero chunk
size.
I'm pretty sure there is some kind of API to get the block size of the
underlying drive on Windows. And if not, then something like 4k or 8k is still
better than zero.
> >
> > > + return blksize_to_iounit(pdu, blksize);
> > > }
> > >
> > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat
> > > *stbuf, @@ -1332,7 +1371,11 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu,
> > const struct stat *stbuf,
> > > v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
> > > v9lstat->st_size = stbuf->st_size;
> > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
> > > +#ifndef CONFIG_WIN32
> > > v9lstat->st_blocks = stbuf->st_blocks;
> > > +#else
> > > + v9lstat->st_blocks = 0;
> > > +#endif
> >
> > Really?
>
> Windows struct stat does not have such field. See above link.
Then it probably has to be calculated by file size / block size.
> >
> > > v9lstat->st_atime_sec = stbuf->st_atime;
> > > v9lstat->st_mtime_sec = stbuf->st_mtime;
> > > v9lstat->st_ctime_sec = stbuf->st_ctime; @@ -1340,7 +1383,8 @@
> > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
> > > v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
> > > v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
> > > v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec; -#else
> > > +#endif
> > > +#ifdef CONFIG_LINUX
> > > v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
> > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
> > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; @@ -2471,6
> > > +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu,
> > V9fsFidState *fidp,
> > > struct dirent *dent;
> > > struct stat *st;
> > > struct V9fsDirEnt *entries = NULL;
> > > + unsigned char d_type = 0;
> > >
> > > /*
> > > * inode remapping requires the device id, which in turn might be
> > > @@ -2540,10 +2585,13 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU
> > *pdu, V9fsFidState *fidp,
> > > v9fs_string_init(&name);
> > > v9fs_string_sprintf(&name, "%s", dent->d_name);
> > >
> > > +#ifndef CONFIG_WIN32
> > > + d_type = dent->d_type;
> > > +#endif
> > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
> > > len = pdu_marshal(pdu, 11 + count, "Qqbs",
> > > &qid, off,
> > > - dent->d_type, &name);
> > > + d_type, &name);
> >
> > Are you saying that d_type is not initialized with zero already?
>
> struct dirent is defined by MinGW, it does not have d_type member:
>
> https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt/dirent.h
My bad, I misread your code. That's fine.
>
> >
> > > v9fs_string_free(&name);
> > >
> > > @@ -2873,8 +2921,12 @@ 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;
> > > }
> > > @@ -2899,8 +2951,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
> >
> > As with previous patches, I would consider making the user aware to use
> > mapped(-xattr) with something like error_report_once().
>
> OK, got it.
>
> >
> > > if (err < 0) {
> > > goto out;
> > > }
> > > @@ -3634,7 +3690,7 @@ out_nofid:
> > >
> > > static void coroutine_fn v9fs_mknod(void *opaque) {
> > > -
> > > +#ifndef CONFIG_WIN32
> > > int mode;
> > > gid_t gid;
> > > int32_t fid;
> > > @@ -3691,6 +3747,10 @@ out:
> > > out_nofid:
> > > pdu_complete(pdu, err);
> > > v9fs_string_free(&name);
> > > +#else
> > > + V9fsPDU *pdu = opaque;
> > > + pdu_complete(pdu, -1);
> > > +#endif
> > > }
> > >
> > > /*
> > > @@ -3963,7 +4023,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.
> > > @@ -3980,6 +4040,7 @@ out_nofid:
> > >
> > > static void coroutine_fn v9fs_xattrcreate(void *opaque) {
> > > +#ifndef CONFIG_WIN32
> > > int flags, rflags = 0;
> > > int32_t fid;
> > > uint64_t size;
> > > @@ -4041,10 +4102,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;
> > > @@ -4080,6 +4146,10 @@ out:
> > > put_fid(pdu, fidp);
> > > out_nofid:
> > > pdu_complete(pdu, err);
> > > +#else
> > > + V9fsPDU *pdu = opaque;
> > > + pdu_complete(pdu, -1);
> > > +#endif
> >
> > Unnecessary double declaration of pdu.
> >
>
> OK, got it.
>
> > > }
> > >
> > > static CoroutineEntry *pdu_co_handlers[] = { @@ -4341,6 +4411,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"); @@ -4348,4
> > > +4419,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
> >
> > Really?
>
> Windows does not provide getrlimit()
But it has _getmaxstdio() and _setmaxstdio().
And even if Windows had no replacement functions, you need to initialize these
two global variables with some meaningful value. Otherwise they would be zero,
and that would mean 9p server would assume max. 0 files could be open at the
same time, so it would constantly close and re-open every single file
descriptor on every minor micro-transaction for no reason.
Best regards,
Christian Schoenebeck
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-01 18:22 ` Christian Schoenebeck
@ 2022-11-02 3:07 ` Shi, Guohuai
2022-11-02 11:05 ` Christian Schoenebeck
0 siblings, 1 reply; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-02 3:07 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> Sent: Wednesday, November 2, 2022 02:22
> To: qemu-devel@nongnu.org
> Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi,
> Guohuai <Guohuai.Shi@windriver.com>
> Subject: Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities
> functions for 9pfs
>
> [Please note: This e-mail is from an EXTERNAL e-mail address]
>
> On Tuesday, November 1, 2022 4:20:53 PM CET Shi, Guohuai wrote:
> >
> [...]
> > > > > Windows POSIX API and MinGW library do not provide the NO_FOLLOW
> > > > > flag, and do not allow opening a directory by POSIX open(). This
> > > > > causes all
> > > > > xxx_at() functions cannot work directly. However, we can provide
> > > > > Windows handle based functions to emulate xxx_at() functions (e.g.:
> > > > > openat_win32, utimensat_win32, etc.).
> > > > >
> > > > > Windows does not support extended attributes. 9pfs for Windows
> > > > > uses NTFS ADS (Alternate Data Streams) to emulate extended
> attributes.
> > > > >
> > > > > Windows does not provide POSIX compatible readlink(), and
> > > > > symbolic link feature in 9pfs will be disabled on Windows.
> > > >
> > > > Wouldn't it be more user friendly if the relevant error locations
> > > > would use something like error_report_once() and suggesting to
> > > > enable
> > > > mapped(-xattr) to make 9p symlinks on guest working if desired by the
> user?
> > > >
> > > > Probably this error case would need to wrapped into a dedicated
> > > > function, otherwise I guess error_report_once() would fire several
> > > > times by different callers.
> > > >
> > >
> > > Windows (MinGW) does not only support symlink, but also does not
> > > have symlink definitions.
> > > Windows does not support symlink flags S_IFLNK.
> > >
> > > So even I add symlink support by mapped-xattr, the MinGW library
> > > does not have symlink flags and get a build error.
> > > And this flags is defined by Windows header files.
> > > The impact of adding a new flags to an pre-defined structure (struct
> > > stat) is unknown.
> > >
> > > So I think it is not a good idea to do that.
> >
> > Because Windows does not support symlink, so error_report_once() and report
> it to user will be OK.
> > But mapped-xattr could not work.
>
> Showing an error makes sense for "passthrough" security model, but not for
> the "mapped" one.
>
> Just to avoid misapprehensions: are you aware that there is already a system-
> agnostic implementation for symlinks if "mapped" is used?
>
> When mapped security model is enabled, then creating symlinks on guest will
> simply create a corresponding *regular* file on host and the content of that
> regular file on host is the pointing path as raw string. Additionally the
> symlink flag is added to "virtfs.mode" xattr to mark that regular file as a
> symlink, a virtual one that is. So this does not require any support for
> symlinks by either the underlying host file system, nor by host OS.
>
> Likewise interpreting and walking those virtual symlinks in "mapped" mode is
> also implemented in the local fs driver already.
Yes, symlink can be supported by "mapped" mode.
I mean that MinGW does not provide symlink mode flags "S_IFLNK" and some other related functions and defines.
You can check with "9p.c": S_ISLNK, S_ISSOCK and S_ISFIFO are not valid on Windows (MinGW) host.
So even I enabled symlink supported by "mapped" mode on local-agent code, "9p.c" can not be built.
So I disabled symlink totally, because MinGW interface does not support it.
To resolve this issue, MinGW should add the missing defines at first.
>
> Best regards,
> Christian Schoenebeck
>
Thanks
Guohuai
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-11-01 18:59 ` Christian Schoenebeck
@ 2022-11-02 3:44 ` Shi, Guohuai
2022-11-02 11:34 ` Christian Schoenebeck
0 siblings, 1 reply; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-02 3:44 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> Sent: Wednesday, November 2, 2022 02:59
> To: qemu-devel@nongnu.org
> Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi,
> Guohuai <Guohuai.Shi@windriver.com>
> Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features
> for Windows
>
> [Please note: This e-mail is from an EXTERNAL e-mail address]
>
> On Tuesday, November 1, 2022 4:34:54 PM CET Shi, Guohuai wrote:
> >
> > > -----Original Message-----
> > > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > > Sent: Tuesday, November 1, 2022 23:04
> > > To: qemu-devel@nongnu.org
> > > Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz
> > > <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>
> > > Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and
> > > features for Windows
> > >
> > > [Please note: This e-mail is from an EXTERNAL e-mail address]
> > >
> > > On Monday, October 24, 2022 6:57:52 AM CET Bin Meng wrote:
> > > > From: Guohuai Shi <guohuai.shi@windriver.com>
> > > >
> > > > Some flags and features are not supported on Windows, like mknod,
> > > > readlink, file mode, etc. Update the codes for Windows.
> > > >
> > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > > > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > > > ---
> > > >
> > > > hw/9pfs/9p-util.h | 6 +++-
> > > > hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-----
> > > > 2 files changed, 86 insertions(+), 10 deletions(-)
> > > >
> > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > > > 82b2d0c3e4..3d154e9103 100644
> > > > --- a/hw/9pfs/9p-util.h
> > > > +++ b/hw/9pfs/9p-util.h
> > > > @@ -53,8 +53,10 @@ static inline uint64_t makedev_dotl(uint32_t
> > > > dev_major,
> > > uint32_t dev_minor)
> > > > */
> > > > static inline uint64_t host_dev_to_dotl_dev(dev_t dev) { -#ifdef
> > > > CONFIG_LINUX
> > > > +#if defined(CONFIG_LINUX)
> > > > return dev;
> > > > +#elif defined(CONFIG_WIN32)
> > > > + return 0;
> > >
> > > Really?
> >
> > Check MS this document:
> > https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fsta
> > t-fstat32-fstat64-fstati64-fstat32i64-fstat64i32?view=msvc-170
> > st_rdev: If a device, fd; otherwise 0.
> > st_dev: If a device, fd; otherwise 0.
> >
> > So for any file open, it should be 0.
>
> Yeah, but that function translates a corresponding device ID for *Linux*
> guest side. And the intention is to avoid e.g. file ID collisions on guest
> side.
> Because for a Linux guest, the two-tuple (device number, inode number) makes
> a system-wide unique file ID.
>
> If you just return zero here, that might be OK if only one 9p directory is
> exported to guest, but say you have "C:\foo\" exported and "D:\bar\" exported
> and mounted via 9p to guest, then guest would assume every file with the same
> inode number on those two to be the same files. But they are not. They are on
> two different drives actually.
>
Got it.
Windows does not provide any numerical type ID for device,
I think the solution could be using driver letter ASC code "C:", "D:", etc.
> >
> > >
> > > > #else
> > > > return makedev_dotl(major(dev), minor(dev)); #endif @@
> > > > -260,7
> > > > +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t mode,
> > > > dev_t dev);
> > > > +#endif
> > > >
> > > > #endif
> > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index
> > > > 6c4af86240..771aab34ac
> > > > 100644
> > > > --- a/hw/9pfs/9p.c
> > > > +++ b/hw/9pfs/9p.c
> > > > @@ -39,6 +39,11 @@
> > > > #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;
> > > > static int open_fd_rc;
> > > > @@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
> > > > DotlOpenflagMap dotl_oflag_map[] = {
> > > > { P9_DOTL_CREATE, O_CREAT },
> > > > { P9_DOTL_EXCL, O_EXCL },
> > > > +#ifndef CONFIG_WIN32
> > > > { P9_DOTL_NOCTTY , O_NOCTTY },
> > > > +#endif
> > > > { P9_DOTL_TRUNC, O_TRUNC },
> > > > { P9_DOTL_APPEND, O_APPEND },
> > > > +#ifndef CONFIG_WIN32
> > > > { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
> > > > { P9_DOTL_DSYNC, O_DSYNC },
> > > > { P9_DOTL_FASYNC, FASYNC }, -#ifndef CONFIG_DARWIN
> > > > +#endif
> > > > +#ifdef CONFIG_LINUX
> > >
> > > Better
> > >
> > > #if !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32)
> > >
> >
> > It is OK.
>
> You got my point, hopefully:
>
> > > Otherwise it might automatically opt-out other future platforms
> > > unintentionally.
> > >
> > > > { P9_DOTL_NOATIME, O_NOATIME },
> > > > /*
> > > > * On Darwin, we could map to F_NOCACHE, which is @@
> > > > -151,8
> > > > +160,10 @@ static int dotl_to_open_flags(int flags) #endif
> > > > { P9_DOTL_LARGEFILE, O_LARGEFILE },
> > > > { P9_DOTL_DIRECTORY, O_DIRECTORY },
> > > > +#ifndef CONFIG_WIN32
> > > > { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
> > > > { P9_DOTL_SYNC, O_SYNC },
> > > > +#endif
> > > > };
> > > >
> > > > for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { @@ -179,8
> > > > +190,11 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
> > > > * Filter the client open flags
> > > > */
> > > > flags = dotl_to_open_flags(oflags);
> > > > - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
> > > > -#ifndef CONFIG_DARWIN
> > > > + flags &= ~(O_CREAT);
> > > > +#ifndef CONFIG_WIN32
> > > > + flags &= ~(O_NOCTTY | O_ASYNC); #endif #ifdef CONFIG_LINUX
> > >
> > > Same as above: better explicitly opt-out than the other way around.
> > >
> >
> > It is OK.
> >
> > > > /*
> > > > * Ignore direct disk access hint until the server supports it.
> > > > */
> > > > @@ -986,9 +1000,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;
> > > > }
> > > > @@ -1097,6 +1113,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;
> > > > }
> > > > @@ -1106,6 +1123,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;
> > > > @@ -1118,6 +1136,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;
> > > > }
> > > > @@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t mode,
> > > V9fsString *extension)
> > > > if (mode & P9_STAT_MODE_SETVTX) {
> > > > ret |= S_ISVTX;
> > > > }
> > > > +#endif
> > > >
> > > > return ret;
> > > > }
> > > > @@ -1182,6 +1202,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;
> > > > }
> > > > @@ -1193,11 +1214,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;
> > > > }
> > > > @@ -1209,6 +1232,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;
> > > > }
> > > > @@ -1247,9 +1271,17 @@ static int coroutine_fn
> > > > stat_to_v9stat(V9fsPDU *pdu,
> > > V9fsPath *path,
> > > > return err;
> > > > }
> > > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
> > > > + unsigned maj, min;
> > > > +
> > > > +#ifndef CONFIG_WIN32
> > > > + maj = major(stbuf->st_rdev);
> > > > + min = minor(stbuf->st_rdev); #else
> > > > + maj = min = 0;
> > > > +#endif
> > >
> > > Really?
> >
> > See above link.
> >
> > >
> > > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
> > > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
> > > > - major(stbuf->st_rdev), minor(stbuf->st_rdev));
> > > > + maj, min);
> > > > } 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);
> > > > @@
> > > > -1317,7 +1349,14 @@ 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) {
> > > > - return blksize_to_iounit(pdu, stbuf->st_blksize);
> > > > + int32_t blksize;
> > > > +
> > > > +#ifndef CONFIG_WIN32
> > > > + blksize = stbuf->st_blksize); #else
> > > > + blksize = 0;
> > > > +#endif
> > >
> > > Really?
> >
> > Windows struct stat does not have such field. See above link.
>
> Yeah, but you cannot simply return zero here, because that information is
> interpreted on guest side as the optimum chunk size for I/O. So some apps
> might misbehave e.g. by trying allocate buffers with zero size, throwing
> division by zero exceptions, or trying to read() / write() with zero chunk
> size.
>
> I'm pretty sure there is some kind of API to get the block size of the
> underlying drive on Windows. And if not, then something like 4k or 8k is
> still better than zero.
>
The possible solution could be put a hard-code (e.g. 4096, 512) here.
This function does not have any context parameter for input.
To get block size, need a file handle or path. But this function does not have it.
> > >
> > > > + return blksize_to_iounit(pdu, blksize);
> > > > }
> > > >
> > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat
> > > > *stbuf, @@ -1332,7 +1371,11 @@ static int
> > > > stat_to_v9stat_dotl(V9fsPDU *pdu,
> > > const struct stat *stbuf,
> > > > v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
> > > > v9lstat->st_size = stbuf->st_size;
> > > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
> > > > +#ifndef CONFIG_WIN32
> > > > v9lstat->st_blocks = stbuf->st_blocks;
> > > > +#else
> > > > + v9lstat->st_blocks = 0;
> > > > +#endif
> > >
> > > Really?
> >
> > Windows struct stat does not have such field. See above link.
>
> Then it probably has to be calculated by file size / block size.
Got it.
>
> > >
> > > > v9lstat->st_atime_sec = stbuf->st_atime;
> > > > v9lstat->st_mtime_sec = stbuf->st_mtime;
> > > > v9lstat->st_ctime_sec = stbuf->st_ctime; @@ -1340,7 +1383,8
> > > > @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat
> *stbuf,
> > > > v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
> > > > v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
> > > > v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec; -#else
> > > > +#endif
> > > > +#ifdef CONFIG_LINUX
> > > > v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
> > > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
> > > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; @@ -2471,6
> > > > +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu,
> > > V9fsFidState *fidp,
> > > > struct dirent *dent;
> > > > struct stat *st;
> > > > struct V9fsDirEnt *entries = NULL;
> > > > + unsigned char d_type = 0;
> > > >
> > > > /*
> > > > * inode remapping requires the device id, which in turn
> > > > might be @@ -2540,10 +2585,13 @@ static int coroutine_fn
> > > > v9fs_do_readdir(V9fsPDU
> > > *pdu, V9fsFidState *fidp,
> > > > v9fs_string_init(&name);
> > > > v9fs_string_sprintf(&name, "%s", dent->d_name);
> > > >
> > > > +#ifndef CONFIG_WIN32
> > > > + d_type = dent->d_type;
> > > > +#endif
> > > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count)
> */
> > > > len = pdu_marshal(pdu, 11 + count, "Qqbs",
> > > > &qid, off,
> > > > - dent->d_type, &name);
> > > > + d_type, &name);
> > >
> > > Are you saying that d_type is not initialized with zero already?
> >
> > struct dirent is defined by MinGW, it does not have d_type member:
> >
> > https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt
> > /dirent.h
>
> My bad, I misread your code. That's fine.
>
> >
> > >
> > > > v9fs_string_free(&name);
> > > >
> > > > @@ -2873,8 +2921,12 @@ 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;
> > > > }
> > > > @@ -2899,8 +2951,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
> > >
> > > As with previous patches, I would consider making the user aware to
> > > use
> > > mapped(-xattr) with something like error_report_once().
> >
> > OK, got it.
> >
> > >
> > > > if (err < 0) {
> > > > goto out;
> > > > }
> > > > @@ -3634,7 +3690,7 @@ out_nofid:
> > > >
> > > > static void coroutine_fn v9fs_mknod(void *opaque) {
> > > > -
> > > > +#ifndef CONFIG_WIN32
> > > > int mode;
> > > > gid_t gid;
> > > > int32_t fid;
> > > > @@ -3691,6 +3747,10 @@ out:
> > > > out_nofid:
> > > > pdu_complete(pdu, err);
> > > > v9fs_string_free(&name);
> > > > +#else
> > > > + V9fsPDU *pdu = opaque;
> > > > + pdu_complete(pdu, -1);
> > > > +#endif
> > > > }
> > > >
> > > > /*
> > > > @@ -3963,7 +4023,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.
> > > > @@ -3980,6 +4040,7 @@ out_nofid:
> > > >
> > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) {
> > > > +#ifndef CONFIG_WIN32
> > > > int flags, rflags = 0;
> > > > int32_t fid;
> > > > uint64_t size;
> > > > @@ -4041,10 +4102,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;
> > > > @@ -4080,6 +4146,10 @@ out:
> > > > put_fid(pdu, fidp);
> > > > out_nofid:
> > > > pdu_complete(pdu, err);
> > > > +#else
> > > > + V9fsPDU *pdu = opaque;
> > > > + pdu_complete(pdu, -1);
> > > > +#endif
> > >
> > > Unnecessary double declaration of pdu.
> > >
> >
> > OK, got it.
> >
> > > > }
> > > >
> > > > static CoroutineEntry *pdu_co_handlers[] = { @@ -4341,6 +4411,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"); @@
> > > > -4348,4
> > > > +4419,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
> > >
> > > Really?
> >
> > Windows does not provide getrlimit()
>
> But it has _getmaxstdio() and _setmaxstdio().
>
> And even if Windows had no replacement functions, you need to initialize
> these two global variables with some meaningful value. Otherwise they would
> be zero, and that would mean 9p server would assume max. 0 files could be
> open at the same time, so it would constantly close and re-open every single
> file descriptor on every minor micro-transaction for no reason.
I could implement this function by _getmaxstdio() and _setmaxstdio().
But these two functions are used for struct FILE, but not file descriptor (FD).
I wrote a basic test for this function:
_getmaxstdio() returns 512, but I can use "open" to open at least 600 files without error.
Windows does not provide any document about the limititaion.
I think we can put a hard code here: 512, 1024, 2048, etc.
Thanks
Guohuai
>
> Best regards,
> Christian Schoenebeck
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-02 3:07 ` Shi, Guohuai
@ 2022-11-02 11:05 ` Christian Schoenebeck
2022-11-02 11:28 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-02 11:05 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Meng, Bin, Shi, Guohuai
On Wednesday, November 2, 2022 4:07:35 AM CET Shi, Guohuai wrote:
>
> > -----Original Message-----
> > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > Sent: Wednesday, November 2, 2022 02:22
> > To: qemu-devel@nongnu.org
> > Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi,
> > Guohuai <Guohuai.Shi@windriver.com>
> > Subject: Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities
> > functions for 9pfs
> >
> > [Please note: This e-mail is from an EXTERNAL e-mail address]
> >
> > On Tuesday, November 1, 2022 4:20:53 PM CET Shi, Guohuai wrote:
> > >
> > [...]
> > > > > > Windows POSIX API and MinGW library do not provide the NO_FOLLOW
> > > > > > flag, and do not allow opening a directory by POSIX open(). This
> > > > > > causes all
> > > > > > xxx_at() functions cannot work directly. However, we can provide
> > > > > > Windows handle based functions to emulate xxx_at() functions (e.g.:
> > > > > > openat_win32, utimensat_win32, etc.).
> > > > > >
> > > > > > Windows does not support extended attributes. 9pfs for Windows
> > > > > > uses NTFS ADS (Alternate Data Streams) to emulate extended
> > attributes.
> > > > > >
> > > > > > Windows does not provide POSIX compatible readlink(), and
> > > > > > symbolic link feature in 9pfs will be disabled on Windows.
> > > > >
> > > > > Wouldn't it be more user friendly if the relevant error locations
> > > > > would use something like error_report_once() and suggesting to
> > > > > enable
> > > > > mapped(-xattr) to make 9p symlinks on guest working if desired by the
> > user?
> > > > >
> > > > > Probably this error case would need to wrapped into a dedicated
> > > > > function, otherwise I guess error_report_once() would fire several
> > > > > times by different callers.
> > > > >
> > > >
> > > > Windows (MinGW) does not only support symlink, but also does not
> > > > have symlink definitions.
> > > > Windows does not support symlink flags S_IFLNK.
> > > >
> > > > So even I add symlink support by mapped-xattr, the MinGW library
> > > > does not have symlink flags and get a build error.
> > > > And this flags is defined by Windows header files.
> > > > The impact of adding a new flags to an pre-defined structure (struct
> > > > stat) is unknown.
> > > >
> > > > So I think it is not a good idea to do that.
> > >
> > > Because Windows does not support symlink, so error_report_once() and report
> > it to user will be OK.
> > > But mapped-xattr could not work.
> >
> > Showing an error makes sense for "passthrough" security model, but not for
> > the "mapped" one.
> >
> > Just to avoid misapprehensions: are you aware that there is already a system-
> > agnostic implementation for symlinks if "mapped" is used?
> >
> > When mapped security model is enabled, then creating symlinks on guest will
> > simply create a corresponding *regular* file on host and the content of that
> > regular file on host is the pointing path as raw string. Additionally the
> > symlink flag is added to "virtfs.mode" xattr to mark that regular file as a
> > symlink, a virtual one that is. So this does not require any support for
> > symlinks by either the underlying host file system, nor by host OS.
> >
> > Likewise interpreting and walking those virtual symlinks in "mapped" mode is
> > also implemented in the local fs driver already.
>
> Yes, symlink can be supported by "mapped" mode.
> I mean that MinGW does not provide symlink mode flags "S_IFLNK" and some other related functions and defines.
> You can check with "9p.c": S_ISLNK, S_ISSOCK and S_ISFIFO are not valid on Windows (MinGW) host.
> So even I enabled symlink supported by "mapped" mode on local-agent code, "9p.c" can not be built.
>
> So I disabled symlink totally, because MinGW interface does not support it.
>
> To resolve this issue, MinGW should add the missing defines at first.
And what's wrong with something like the following?
#ifdef CONFIG_WIN32
...
#ifndef S_ISLNK
#define S_ISLNK(x) ...
#endif
...
#endif
Best regards,
Christian Schoenebeck
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-02 11:05 ` Christian Schoenebeck
@ 2022-11-02 11:28 ` Shi, Guohuai
2022-11-02 11:51 ` Christian Schoenebeck
0 siblings, 1 reply; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-02 11:28 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> Sent: Wednesday, November 2, 2022 19:06
> To: qemu-devel@nongnu.org
> Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi,
> Guohuai <Guohuai.Shi@windriver.com>
> Subject: Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities
> functions for 9pfs
>
> CAUTION: This email comes from a non Wind River email account!
> Do not click links or open attachments unless you recognize the sender and
> know the content is safe.
>
> On Wednesday, November 2, 2022 4:07:35 AM CET Shi, Guohuai wrote:
> >
> > > -----Original Message-----
> > > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > > Sent: Wednesday, November 2, 2022 02:22
> > > To: qemu-devel@nongnu.org
> > > Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>;
> > > Shi, Guohuai <Guohuai.Shi@windriver.com>
> > > Subject: Re: [PATCH 07/16] hw/9pfs: Implement Windows specific
> > > utilities functions for 9pfs
> > >
> > > [Please note: This e-mail is from an EXTERNAL e-mail address]
> > >
> > > On Tuesday, November 1, 2022 4:20:53 PM CET Shi, Guohuai wrote:
> > > >
> > > [...]
> > > > > > > Windows POSIX API and MinGW library do not provide the
> > > > > > > NO_FOLLOW flag, and do not allow opening a directory by
> > > > > > > POSIX open(). This causes all
> > > > > > > xxx_at() functions cannot work directly. However, we can
> > > > > > > provide Windows handle based functions to emulate xxx_at()
> functions (e.g.:
> > > > > > > openat_win32, utimensat_win32, etc.).
> > > > > > >
> > > > > > > Windows does not support extended attributes. 9pfs for
> > > > > > > Windows uses NTFS ADS (Alternate Data Streams) to emulate
> > > > > > > extended
> > > attributes.
> > > > > > >
> > > > > > > Windows does not provide POSIX compatible readlink(), and
> > > > > > > symbolic link feature in 9pfs will be disabled on Windows.
> > > > > >
> > > > > > Wouldn't it be more user friendly if the relevant error
> > > > > > locations would use something like error_report_once() and
> > > > > > suggesting to enable
> > > > > > mapped(-xattr) to make 9p symlinks on guest working if desired
> > > > > > by the
> > > user?
> > > > > >
> > > > > > Probably this error case would need to wrapped into a
> > > > > > dedicated function, otherwise I guess error_report_once()
> > > > > > would fire several times by different callers.
> > > > > >
> > > > >
> > > > > Windows (MinGW) does not only support symlink, but also does not
> > > > > have symlink definitions.
> > > > > Windows does not support symlink flags S_IFLNK.
> > > > >
> > > > > So even I add symlink support by mapped-xattr, the MinGW library
> > > > > does not have symlink flags and get a build error.
> > > > > And this flags is defined by Windows header files.
> > > > > The impact of adding a new flags to an pre-defined structure
> > > > > (struct
> > > > > stat) is unknown.
> > > > >
> > > > > So I think it is not a good idea to do that.
> > > >
> > > > Because Windows does not support symlink, so error_report_once()
> > > > and report
> > > it to user will be OK.
> > > > But mapped-xattr could not work.
> > >
> > > Showing an error makes sense for "passthrough" security model, but
> > > not for the "mapped" one.
> > >
> > > Just to avoid misapprehensions: are you aware that there is already
> > > a system- agnostic implementation for symlinks if "mapped" is used?
> > >
> > > When mapped security model is enabled, then creating symlinks on
> > > guest will simply create a corresponding *regular* file on host and
> > > the content of that regular file on host is the pointing path as raw
> > > string. Additionally the symlink flag is added to "virtfs.mode"
> > > xattr to mark that regular file as a symlink, a virtual one that is.
> > > So this does not require any support for symlinks by either the
> underlying host file system, nor by host OS.
> > >
> > > Likewise interpreting and walking those virtual symlinks in "mapped"
> > > mode is also implemented in the local fs driver already.
> >
> > Yes, symlink can be supported by "mapped" mode.
> > I mean that MinGW does not provide symlink mode flags "S_IFLNK" and some
> other related functions and defines.
> > You can check with "9p.c": S_ISLNK, S_ISSOCK and S_ISFIFO are not valid on
> Windows (MinGW) host.
> > So even I enabled symlink supported by "mapped" mode on local-agent code,
> "9p.c" can not be built.
> >
> > So I disabled symlink totally, because MinGW interface does not support it.
> >
> > To resolve this issue, MinGW should add the missing defines at first.
>
> And what's wrong with something like the following?
>
> #ifdef CONFIG_WIN32
> ...
> #ifndef S_ISLNK
> #define S_ISLNK(x) ...
> #endif
> ...
> #endif
>
It is OK to add this just for current solution.
My concern is:
mode_t is a 16-bit value which store permission value in lower 12-bit and file type in higher 4-bit.
Windows does not document the other value for file type defines:
// from MS SDK header file:
#define _S_IFMT 0xF000 // File type mask
#define _S_IFDIR 0x4000 // Directory
#define _S_IFCHR 0x2000 // Character special
#define _S_IFIFO 0x1000 // Pipe
#define _S_IFREG 0x8000 // Regular
If we add a new type, is it a risk that the un-document value may be used by Windows internally and cause some compatible issue?
Or if Windows use this some values in the future cause conflict?
Thanks
Guohuai
> Best regards,
> Christian Schoenebeck
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-11-02 3:44 ` Shi, Guohuai
@ 2022-11-02 11:34 ` Christian Schoenebeck
2022-11-02 12:19 ` Shi, Guohuai
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-02 11:34 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Meng, Bin, Shi, Guohuai
On Wednesday, November 2, 2022 4:44:14 AM CET Shi, Guohuai wrote:
>
> > -----Original Message-----
> > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > Sent: Wednesday, November 2, 2022 02:59
> > To: qemu-devel@nongnu.org
> > Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi,
> > Guohuai <Guohuai.Shi@windriver.com>
> > Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features
> > for Windows
> >
> > [Please note: This e-mail is from an EXTERNAL e-mail address]
> >
> > On Tuesday, November 1, 2022 4:34:54 PM CET Shi, Guohuai wrote:
> > >
> > > > -----Original Message-----
> > > > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > > > Sent: Tuesday, November 1, 2022 23:04
> > > > To: qemu-devel@nongnu.org
> > > > Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz
> > > > <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>
> > > > Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and
> > > > features for Windows
> > > >
> > > > [Please note: This e-mail is from an EXTERNAL e-mail address]
> > > >
> > > > On Monday, October 24, 2022 6:57:52 AM CET Bin Meng wrote:
> > > > > From: Guohuai Shi <guohuai.shi@windriver.com>
> > > > >
> > > > > Some flags and features are not supported on Windows, like mknod,
> > > > > readlink, file mode, etc. Update the codes for Windows.
> > > > >
> > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > > > > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > > > > ---
> > > > >
> > > > > hw/9pfs/9p-util.h | 6 +++-
> > > > > hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-----
> > > > > 2 files changed, 86 insertions(+), 10 deletions(-)
> > > > >
> > > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > > > > 82b2d0c3e4..3d154e9103 100644
> > > > > --- a/hw/9pfs/9p-util.h
> > > > > +++ b/hw/9pfs/9p-util.h
> > > > > @@ -53,8 +53,10 @@ static inline uint64_t makedev_dotl(uint32_t
> > > > > dev_major,
> > > > uint32_t dev_minor)
> > > > > */
> > > > > static inline uint64_t host_dev_to_dotl_dev(dev_t dev) { -#ifdef
> > > > > CONFIG_LINUX
> > > > > +#if defined(CONFIG_LINUX)
> > > > > return dev;
> > > > > +#elif defined(CONFIG_WIN32)
> > > > > + return 0;
> > > >
> > > > Really?
> > >
> > > Check MS this document:
> > > https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fsta
> > > t-fstat32-fstat64-fstati64-fstat32i64-fstat64i32?view=msvc-170
> > > st_rdev: If a device, fd; otherwise 0.
> > > st_dev: If a device, fd; otherwise 0.
> > >
> > > So for any file open, it should be 0.
> >
> > Yeah, but that function translates a corresponding device ID for *Linux*
> > guest side. And the intention is to avoid e.g. file ID collisions on guest
> > side.
> > Because for a Linux guest, the two-tuple (device number, inode number) makes
> > a system-wide unique file ID.
> >
> > If you just return zero here, that might be OK if only one 9p directory is
> > exported to guest, but say you have "C:\foo\" exported and "D:\bar\" exported
> > and mounted via 9p to guest, then guest would assume every file with the same
> > inode number on those two to be the same files. But they are not. They are on
> > two different drives actually.
> >
>
> Got it.
> Windows does not provide any numerical type ID for device,
> I think the solution could be using driver letter ASC code "C:", "D:", etc.
>
> > >
> > > >
> > > > > #else
> > > > > return makedev_dotl(major(dev), minor(dev)); #endif @@
> > > > > -260,7
> > > > > +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t mode,
> > > > > dev_t dev);
> > > > > +#endif
> > > > >
> > > > > #endif
> > > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index
> > > > > 6c4af86240..771aab34ac
> > > > > 100644
> > > > > --- a/hw/9pfs/9p.c
> > > > > +++ b/hw/9pfs/9p.c
> > > > > @@ -39,6 +39,11 @@
> > > > > #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;
> > > > > static int open_fd_rc;
> > > > > @@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
> > > > > DotlOpenflagMap dotl_oflag_map[] = {
> > > > > { P9_DOTL_CREATE, O_CREAT },
> > > > > { P9_DOTL_EXCL, O_EXCL },
> > > > > +#ifndef CONFIG_WIN32
> > > > > { P9_DOTL_NOCTTY , O_NOCTTY },
> > > > > +#endif
> > > > > { P9_DOTL_TRUNC, O_TRUNC },
> > > > > { P9_DOTL_APPEND, O_APPEND },
> > > > > +#ifndef CONFIG_WIN32
> > > > > { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
> > > > > { P9_DOTL_DSYNC, O_DSYNC },
> > > > > { P9_DOTL_FASYNC, FASYNC }, -#ifndef CONFIG_DARWIN
> > > > > +#endif
> > > > > +#ifdef CONFIG_LINUX
> > > >
> > > > Better
> > > >
> > > > #if !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32)
> > > >
> > >
> > > It is OK.
> >
> > You got my point, hopefully:
> >
> > > > Otherwise it might automatically opt-out other future platforms
> > > > unintentionally.
> > > >
> > > > > { P9_DOTL_NOATIME, O_NOATIME },
> > > > > /*
> > > > > * On Darwin, we could map to F_NOCACHE, which is @@
> > > > > -151,8
> > > > > +160,10 @@ static int dotl_to_open_flags(int flags) #endif
> > > > > { P9_DOTL_LARGEFILE, O_LARGEFILE },
> > > > > { P9_DOTL_DIRECTORY, O_DIRECTORY },
> > > > > +#ifndef CONFIG_WIN32
> > > > > { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
> > > > > { P9_DOTL_SYNC, O_SYNC },
> > > > > +#endif
> > > > > };
> > > > >
> > > > > for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { @@ -179,8
> > > > > +190,11 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
> > > > > * Filter the client open flags
> > > > > */
> > > > > flags = dotl_to_open_flags(oflags);
> > > > > - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
> > > > > -#ifndef CONFIG_DARWIN
> > > > > + flags &= ~(O_CREAT);
> > > > > +#ifndef CONFIG_WIN32
> > > > > + flags &= ~(O_NOCTTY | O_ASYNC); #endif #ifdef CONFIG_LINUX
> > > >
> > > > Same as above: better explicitly opt-out than the other way around.
> > > >
> > >
> > > It is OK.
> > >
> > > > > /*
> > > > > * Ignore direct disk access hint until the server supports it.
> > > > > */
> > > > > @@ -986,9 +1000,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;
> > > > > }
> > > > > @@ -1097,6 +1113,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;
> > > > > }
> > > > > @@ -1106,6 +1123,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;
> > > > > @@ -1118,6 +1136,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;
> > > > > }
> > > > > @@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t mode,
> > > > V9fsString *extension)
> > > > > if (mode & P9_STAT_MODE_SETVTX) {
> > > > > ret |= S_ISVTX;
> > > > > }
> > > > > +#endif
> > > > >
> > > > > return ret;
> > > > > }
> > > > > @@ -1182,6 +1202,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;
> > > > > }
> > > > > @@ -1193,11 +1214,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;
> > > > > }
> > > > > @@ -1209,6 +1232,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;
> > > > > }
> > > > > @@ -1247,9 +1271,17 @@ static int coroutine_fn
> > > > > stat_to_v9stat(V9fsPDU *pdu,
> > > > V9fsPath *path,
> > > > > return err;
> > > > > }
> > > > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
> > > > > + unsigned maj, min;
> > > > > +
> > > > > +#ifndef CONFIG_WIN32
> > > > > + maj = major(stbuf->st_rdev);
> > > > > + min = minor(stbuf->st_rdev); #else
> > > > > + maj = min = 0;
> > > > > +#endif
> > > >
> > > > Really?
> > >
> > > See above link.
> > >
> > > >
> > > > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
> > > > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
> > > > > - major(stbuf->st_rdev), minor(stbuf->st_rdev));
> > > > > + maj, min);
> > > > > } 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);
> > > > > @@
> > > > > -1317,7 +1349,14 @@ 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) {
> > > > > - return blksize_to_iounit(pdu, stbuf->st_blksize);
> > > > > + int32_t blksize;
> > > > > +
> > > > > +#ifndef CONFIG_WIN32
> > > > > + blksize = stbuf->st_blksize); #else
> > > > > + blksize = 0;
> > > > > +#endif
> > > >
> > > > Really?
> > >
> > > Windows struct stat does not have such field. See above link.
> >
> > Yeah, but you cannot simply return zero here, because that information is
> > interpreted on guest side as the optimum chunk size for I/O. So some apps
> > might misbehave e.g. by trying allocate buffers with zero size, throwing
> > division by zero exceptions, or trying to read() / write() with zero chunk
> > size.
> >
> > I'm pretty sure there is some kind of API to get the block size of the
> > underlying drive on Windows. And if not, then something like 4k or 8k is
> > still better than zero.
> >
>
> The possible solution could be put a hard-code (e.g. 4096, 512) here.
> This function does not have any context parameter for input.
> To get block size, need a file handle or path. But this function does not have it.
Another major difference on Windows compared to other systems is that you
don't have to worry about potential submounts in the exported directory tree.
Because Windows does not have that concept. So on Windows you can assume that
all files of the exported file tree are on the same storage device, right?
So another solution would be to query the block size with a regular Windows
API only on startup, simply by using the export root, and then storing that
block size e.g. as a new member variable to struct V9fsState.
In the end you could actually also use some meaningful hard coded value for
now, as it is not such of a big deal whether that's 512, 4k or 8k. But zero is
clearly a no go, for the reasons previously described.
> > > >
> > > > > + return blksize_to_iounit(pdu, blksize);
> > > > > }
> > > > >
> > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat
> > > > > *stbuf, @@ -1332,7 +1371,11 @@ static int
> > > > > stat_to_v9stat_dotl(V9fsPDU *pdu,
> > > > const struct stat *stbuf,
> > > > > v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
> > > > > v9lstat->st_size = stbuf->st_size;
> > > > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
> > > > > +#ifndef CONFIG_WIN32
> > > > > v9lstat->st_blocks = stbuf->st_blocks;
> > > > > +#else
> > > > > + v9lstat->st_blocks = 0;
> > > > > +#endif
> > > >
> > > > Really?
> > >
> > > Windows struct stat does not have such field. See above link.
> >
> > Then it probably has to be calculated by file size / block size.
>
> Got it.
>
> >
> > > >
> > > > > v9lstat->st_atime_sec = stbuf->st_atime;
> > > > > v9lstat->st_mtime_sec = stbuf->st_mtime;
> > > > > v9lstat->st_ctime_sec = stbuf->st_ctime; @@ -1340,7 +1383,8
> > > > > @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat
> > *stbuf,
> > > > > v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
> > > > > v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
> > > > > v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec; -#else
> > > > > +#endif
> > > > > +#ifdef CONFIG_LINUX
> > > > > v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
> > > > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
> > > > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; @@ -2471,6
> > > > > +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu,
> > > > V9fsFidState *fidp,
> > > > > struct dirent *dent;
> > > > > struct stat *st;
> > > > > struct V9fsDirEnt *entries = NULL;
> > > > > + unsigned char d_type = 0;
> > > > >
> > > > > /*
> > > > > * inode remapping requires the device id, which in turn
> > > > > might be @@ -2540,10 +2585,13 @@ static int coroutine_fn
> > > > > v9fs_do_readdir(V9fsPDU
> > > > *pdu, V9fsFidState *fidp,
> > > > > v9fs_string_init(&name);
> > > > > v9fs_string_sprintf(&name, "%s", dent->d_name);
> > > > >
> > > > > +#ifndef CONFIG_WIN32
> > > > > + d_type = dent->d_type;
> > > > > +#endif
> > > > > /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count)
> > */
> > > > > len = pdu_marshal(pdu, 11 + count, "Qqbs",
> > > > > &qid, off,
> > > > > - dent->d_type, &name);
> > > > > + d_type, &name);
> > > >
> > > > Are you saying that d_type is not initialized with zero already?
> > >
> > > struct dirent is defined by MinGW, it does not have d_type member:
> > >
> > > https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt
> > > /dirent.h
> >
> > My bad, I misread your code. That's fine.
> >
> > >
> > > >
> > > > > v9fs_string_free(&name);
> > > > >
> > > > > @@ -2873,8 +2921,12 @@ 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;
> > > > > }
> > > > > @@ -2899,8 +2951,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
> > > >
> > > > As with previous patches, I would consider making the user aware to
> > > > use
> > > > mapped(-xattr) with something like error_report_once().
> > >
> > > OK, got it.
> > >
> > > >
> > > > > if (err < 0) {
> > > > > goto out;
> > > > > }
> > > > > @@ -3634,7 +3690,7 @@ out_nofid:
> > > > >
> > > > > static void coroutine_fn v9fs_mknod(void *opaque) {
> > > > > -
> > > > > +#ifndef CONFIG_WIN32
> > > > > int mode;
> > > > > gid_t gid;
> > > > > int32_t fid;
> > > > > @@ -3691,6 +3747,10 @@ out:
> > > > > out_nofid:
> > > > > pdu_complete(pdu, err);
> > > > > v9fs_string_free(&name);
> > > > > +#else
> > > > > + V9fsPDU *pdu = opaque;
> > > > > + pdu_complete(pdu, -1);
> > > > > +#endif
> > > > > }
> > > > >
> > > > > /*
> > > > > @@ -3963,7 +4023,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.
> > > > > @@ -3980,6 +4040,7 @@ out_nofid:
> > > > >
> > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) {
> > > > > +#ifndef CONFIG_WIN32
> > > > > int flags, rflags = 0;
> > > > > int32_t fid;
> > > > > uint64_t size;
> > > > > @@ -4041,10 +4102,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;
> > > > > @@ -4080,6 +4146,10 @@ out:
> > > > > put_fid(pdu, fidp);
> > > > > out_nofid:
> > > > > pdu_complete(pdu, err);
> > > > > +#else
> > > > > + V9fsPDU *pdu = opaque;
> > > > > + pdu_complete(pdu, -1);
> > > > > +#endif
> > > >
> > > > Unnecessary double declaration of pdu.
> > > >
> > >
> > > OK, got it.
> > >
> > > > > }
> > > > >
> > > > > static CoroutineEntry *pdu_co_handlers[] = { @@ -4341,6 +4411,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"); @@
> > > > > -4348,4
> > > > > +4419,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
> > > >
> > > > Really?
> > >
> > > Windows does not provide getrlimit()
> >
> > But it has _getmaxstdio() and _setmaxstdio().
> >
> > And even if Windows had no replacement functions, you need to initialize
> > these two global variables with some meaningful value. Otherwise they would
> > be zero, and that would mean 9p server would assume max. 0 files could be
> > open at the same time, so it would constantly close and re-open every single
> > file descriptor on every minor micro-transaction for no reason.
>
> I could implement this function by _getmaxstdio() and _setmaxstdio().
> But these two functions are used for struct FILE, but not file descriptor (FD).
> I wrote a basic test for this function:
> _getmaxstdio() returns 512, but I can use "open" to open at least 600 files without error.
>
> Windows does not provide any document about the limititaion.
>
> I think we can put a hard code here: 512, 1024, 2048, etc.
Yeah, that's pretty badly documented, I agree. The fact that it's prefixed
with an underscore suggests that they were probably not too convinced about
their API either. But it is as it is.
The fact that you can open a bit more files than what this function advertises
is not a huge surprise or speaking against using it though. Because it is not
untypical for systems to allow overallocation of system resources to a certain
extent.
At least from what I can see right now, this function is commonly used on
Windows to get exactly that metric. So I would rather use that than using hard
coded values, because from what I can see on the web, the numbers people got
experimentally are varying quite a bit.
Best regards,
Christian Schoenebeck
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-02 11:28 ` Shi, Guohuai
@ 2022-11-02 11:51 ` Christian Schoenebeck
2022-11-02 12:06 ` Christian Schoenebeck
0 siblings, 1 reply; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-02 11:51 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Meng, Bin, Shi, Guohuai
On Wednesday, November 2, 2022 12:28:08 PM CET Shi, Guohuai wrote:
>
[...]
> > >
> > > Yes, symlink can be supported by "mapped" mode.
> > > I mean that MinGW does not provide symlink mode flags "S_IFLNK" and some
> > other related functions and defines.
> > > You can check with "9p.c": S_ISLNK, S_ISSOCK and S_ISFIFO are not valid on
> > Windows (MinGW) host.
> > > So even I enabled symlink supported by "mapped" mode on local-agent code,
> > "9p.c" can not be built.
> > >
> > > So I disabled symlink totally, because MinGW interface does not support it.
> > >
> > > To resolve this issue, MinGW should add the missing defines at first.
> >
> > And what's wrong with something like the following?
> >
> > #ifdef CONFIG_WIN32
> > ...
> > #ifndef S_ISLNK
> > #define S_ISLNK(x) ...
> > #endif
> > ...
> > #endif
> >
>
> It is OK to add this just for current solution.
> My concern is:
> mode_t is a 16-bit value which store permission value in lower 12-bit and file type in higher 4-bit.
> Windows does not document the other value for file type defines:
>
> // from MS SDK header file:
>
> #define _S_IFMT 0xF000 // File type mask
> #define _S_IFDIR 0x4000 // Directory
> #define _S_IFCHR 0x2000 // Character special
> #define _S_IFIFO 0x1000 // Pipe
> #define _S_IFREG 0x8000 // Regular
>
> If we add a new type, is it a risk that the un-document value may be used by Windows internally and cause some compatible issue?
> Or if Windows use this some values in the future cause conflict?
We don't have a crystal ball, but there are ways to handle this responsibly:
At compile-time you could error out badly if there is anything we don't
expect, like all of a sudden some of the macros we explicitly define for
Windows are unexpectly there, and telling developers, hey somebody should look
at this.
And at runtime you could add an assert() if some these values are all of a
sudden filled. Because that's what we currently don't expect, as we are
occupying them.
Best regards,
Christian Schoenebeck
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
2022-11-02 11:51 ` Christian Schoenebeck
@ 2022-11-02 12:06 ` Christian Schoenebeck
0 siblings, 0 replies; 40+ messages in thread
From: Christian Schoenebeck @ 2022-11-02 12:06 UTC (permalink / raw)
To: qemu-devel; +Cc: Greg Kurz, Meng, Bin, Shi, Guohuai
On Wednesday, November 2, 2022 12:51:23 PM CET Christian Schoenebeck wrote:
> On Wednesday, November 2, 2022 12:28:08 PM CET Shi, Guohuai wrote:
> >
> [...]
> > > >
> > > > Yes, symlink can be supported by "mapped" mode.
> > > > I mean that MinGW does not provide symlink mode flags "S_IFLNK" and some
> > > other related functions and defines.
> > > > You can check with "9p.c": S_ISLNK, S_ISSOCK and S_ISFIFO are not valid on
> > > Windows (MinGW) host.
> > > > So even I enabled symlink supported by "mapped" mode on local-agent code,
> > > "9p.c" can not be built.
> > > >
> > > > So I disabled symlink totally, because MinGW interface does not support it.
> > > >
> > > > To resolve this issue, MinGW should add the missing defines at first.
> > >
> > > And what's wrong with something like the following?
> > >
> > > #ifdef CONFIG_WIN32
> > > ...
> > > #ifndef S_ISLNK
> > > #define S_ISLNK(x) ...
> > > #endif
> > > ...
> > > #endif
> > >
> >
> > It is OK to add this just for current solution.
> > My concern is:
> > mode_t is a 16-bit value which store permission value in lower 12-bit and file type in higher 4-bit.
> > Windows does not document the other value for file type defines:
> >
> > // from MS SDK header file:
> >
> > #define _S_IFMT 0xF000 // File type mask
> > #define _S_IFDIR 0x4000 // Directory
> > #define _S_IFCHR 0x2000 // Character special
> > #define _S_IFIFO 0x1000 // Pipe
> > #define _S_IFREG 0x8000 // Regular
> >
> > If we add a new type, is it a risk that the un-document value may be used by Windows internally and cause some compatible issue?
> > Or if Windows use this some values in the future cause conflict?
>
> We don't have a crystal ball, but there are ways to handle this responsibly:
> At compile-time you could error out badly if there is anything we don't
> expect, like all of a sudden some of the macros we explicitly define for
> Windows are unexpectly there, and telling developers, hey somebody should look
> at this.
>
> And at runtime you could add an assert() if some these values are all of a
> sudden filled. Because that's what we currently don't expect, as we are
> occupying them.
On a second thought, as far as the runtime aspect is concerned: killing QEMU
with assert() is probably overkill and unnecessarily too harsh. What can
happen if Windows starts to use the new bits for some purpose? As long as you
explicitly clear those bits out after retrieval from Windows, we can still use
them for our purposes. And instead you could simply log one less drastical
warning once to the user.
Does that make sense?
Best regards,
Christian Schoenebeck
^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features for Windows
2022-11-02 11:34 ` Christian Schoenebeck
@ 2022-11-02 12:19 ` Shi, Guohuai
0 siblings, 0 replies; 40+ messages in thread
From: Shi, Guohuai @ 2022-11-02 12:19 UTC (permalink / raw)
To: Christian Schoenebeck, qemu-devel; +Cc: Greg Kurz, Meng, Bin
> -----Original Message-----
> From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> Sent: Wednesday, November 2, 2022 19:34
> To: qemu-devel@nongnu.org
> Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>; Shi,
> Guohuai <Guohuai.Shi@windriver.com>
> Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and features
> for Windows
>
> CAUTION: This email comes from a non Wind River email account!
> Do not click links or open attachments unless you recognize the sender and
> know the content is safe.
>
> On Wednesday, November 2, 2022 4:44:14 AM CET Shi, Guohuai wrote:
> >
> > > -----Original Message-----
> > > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > > Sent: Wednesday, November 2, 2022 02:59
> > > To: qemu-devel@nongnu.org
> > > Cc: Greg Kurz <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>;
> > > Shi, Guohuai <Guohuai.Shi@windriver.com>
> > > Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags and
> > > features for Windows
> > >
> > > [Please note: This e-mail is from an EXTERNAL e-mail address]
> > >
> > > On Tuesday, November 1, 2022 4:34:54 PM CET Shi, Guohuai wrote:
> > > >
> > > > > -----Original Message-----
> > > > > From: Christian Schoenebeck <qemu_oss@crudebyte.com>
> > > > > Sent: Tuesday, November 1, 2022 23:04
> > > > > To: qemu-devel@nongnu.org
> > > > > Cc: Shi, Guohuai <Guohuai.Shi@windriver.com>; Greg Kurz
> > > > > <groug@kaod.org>; Meng, Bin <Bin.Meng@windriver.com>
> > > > > Subject: Re: [PATCH 09/16] hw/9pfs: Disable unsupported flags
> > > > > and features for Windows
> > > > >
> > > > > [Please note: This e-mail is from an EXTERNAL e-mail address]
> > > > >
> > > > > On Monday, October 24, 2022 6:57:52 AM CET Bin Meng wrote:
> > > > > > From: Guohuai Shi <guohuai.shi@windriver.com>
> > > > > >
> > > > > > Some flags and features are not supported on Windows, like
> > > > > > mknod, readlink, file mode, etc. Update the codes for Windows.
> > > > > >
> > > > > > Signed-off-by: Guohuai Shi <guohuai.shi@windriver.com>
> > > > > > Signed-off-by: Bin Meng <bin.meng@windriver.com>
> > > > > > ---
> > > > > >
> > > > > > hw/9pfs/9p-util.h | 6 +++-
> > > > > > hw/9pfs/9p.c | 90 ++++++++++++++++++++++++++++++++++++++++++-
> ----
> > > > > > 2 files changed, 86 insertions(+), 10 deletions(-)
> > > > > >
> > > > > > diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index
> > > > > > 82b2d0c3e4..3d154e9103 100644
> > > > > > --- a/hw/9pfs/9p-util.h
> > > > > > +++ b/hw/9pfs/9p-util.h
> > > > > > @@ -53,8 +53,10 @@ static inline uint64_t
> > > > > > makedev_dotl(uint32_t dev_major,
> > > > > uint32_t dev_minor)
> > > > > > */
> > > > > > static inline uint64_t host_dev_to_dotl_dev(dev_t dev) {
> > > > > > -#ifdef CONFIG_LINUX
> > > > > > +#if defined(CONFIG_LINUX)
> > > > > > return dev;
> > > > > > +#elif defined(CONFIG_WIN32)
> > > > > > + return 0;
> > > > >
> > > > > Really?
> > > >
> > > > Check MS this document:
> > > > https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/
> > > > fsta
> > > > t-fstat32-fstat64-fstati64-fstat32i64-fstat64i32?view=msvc-170
> > > > st_rdev: If a device, fd; otherwise 0.
> > > > st_dev: If a device, fd; otherwise 0.
> > > >
> > > > So for any file open, it should be 0.
> > >
> > > Yeah, but that function translates a corresponding device ID for
> > > *Linux* guest side. And the intention is to avoid e.g. file ID
> > > collisions on guest side.
> > > Because for a Linux guest, the two-tuple (device number, inode
> > > number) makes a system-wide unique file ID.
> > >
> > > If you just return zero here, that might be OK if only one 9p
> > > directory is exported to guest, but say you have "C:\foo\" exported
> > > and "D:\bar\" exported and mounted via 9p to guest, then guest would
> > > assume every file with the same inode number on those two to be the
> > > same files. But they are not. They are on two different drives actually.
> > >
> >
> > Got it.
> > Windows does not provide any numerical type ID for device, I think the
> > solution could be using driver letter ASC code "C:", "D:", etc.
> >
> > > >
> > > > >
> > > > > > #else
> > > > > > return makedev_dotl(major(dev), minor(dev)); #endif @@
> > > > > > -260,7
> > > > > > +262,9 @@ 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(P9_FILE_ID dirfd, const char *filename, mode_t
> mode,
> > > > > > dev_t dev);
> > > > > > +#endif
> > > > > >
> > > > > > #endif
> > > > > > diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index
> > > > > > 6c4af86240..771aab34ac
> > > > > > 100644
> > > > > > --- a/hw/9pfs/9p.c
> > > > > > +++ b/hw/9pfs/9p.c
> > > > > > @@ -39,6 +39,11 @@
> > > > > > #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;
> > > > > > static int open_fd_rc;
> > > > > > @@ -132,13 +137,17 @@ static int dotl_to_open_flags(int flags)
> > > > > > DotlOpenflagMap dotl_oflag_map[] = {
> > > > > > { P9_DOTL_CREATE, O_CREAT },
> > > > > > { P9_DOTL_EXCL, O_EXCL },
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > { P9_DOTL_NOCTTY , O_NOCTTY },
> > > > > > +#endif
> > > > > > { P9_DOTL_TRUNC, O_TRUNC },
> > > > > > { P9_DOTL_APPEND, O_APPEND },
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
> > > > > > { P9_DOTL_DSYNC, O_DSYNC },
> > > > > > { P9_DOTL_FASYNC, FASYNC }, -#ifndef CONFIG_DARWIN
> > > > > > +#endif
> > > > > > +#ifdef CONFIG_LINUX
> > > > >
> > > > > Better
> > > > >
> > > > > #if !defined(CONFIG_DARWIN) && !defined(CONFIG_WIN32)
> > > > >
> > > >
> > > > It is OK.
> > >
> > > You got my point, hopefully:
> > >
> > > > > Otherwise it might automatically opt-out other future platforms
> > > > > unintentionally.
> > > > >
> > > > > > { P9_DOTL_NOATIME, O_NOATIME },
> > > > > > /*
> > > > > > * On Darwin, we could map to F_NOCACHE, which is @@
> > > > > > -151,8
> > > > > > +160,10 @@ static int dotl_to_open_flags(int flags) #endif
> > > > > > { P9_DOTL_LARGEFILE, O_LARGEFILE },
> > > > > > { P9_DOTL_DIRECTORY, O_DIRECTORY },
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
> > > > > > { P9_DOTL_SYNC, O_SYNC },
> > > > > > +#endif
> > > > > > };
> > > > > >
> > > > > > for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) { @@
> > > > > > -179,8
> > > > > > +190,11 @@ static int get_dotl_openflags(V9fsState *s, int
> > > > > > +oflags)
> > > > > > * Filter the client open flags
> > > > > > */
> > > > > > flags = dotl_to_open_flags(oflags);
> > > > > > - flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
> > > > > > -#ifndef CONFIG_DARWIN
> > > > > > + flags &= ~(O_CREAT);
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > + flags &= ~(O_NOCTTY | O_ASYNC); #endif #ifdef
> > > > > > +CONFIG_LINUX
> > > > >
> > > > > Same as above: better explicitly opt-out than the other way around.
> > > > >
> > > >
> > > > It is OK.
> > > >
> > > > > > /*
> > > > > > * Ignore direct disk access hint until the server supports it.
> > > > > > */
> > > > > > @@ -986,9 +1000,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;
> > > > > > }
> > > > > > @@ -1097,6 +1113,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;
> > > > > > }
> > > > > > @@ -1106,6 +1123,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;
> > > > > > @@ -1118,6 +1136,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;
> > > > > > }
> > > > > > @@ -1127,6 +1146,7 @@ static mode_t v9mode_to_mode(uint32_t
> > > > > > mode,
> > > > > V9fsString *extension)
> > > > > > if (mode & P9_STAT_MODE_SETVTX) {
> > > > > > ret |= S_ISVTX;
> > > > > > }
> > > > > > +#endif
> > > > > >
> > > > > > return ret;
> > > > > > }
> > > > > > @@ -1182,6 +1202,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;
> > > > > > }
> > > > > > @@ -1193,11 +1214,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;
> > > > > > }
> > > > > > @@ -1209,6 +1232,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;
> > > > > > }
> > > > > > @@ -1247,9 +1271,17 @@ static int coroutine_fn
> > > > > > stat_to_v9stat(V9fsPDU *pdu,
> > > > > V9fsPath *path,
> > > > > > return err;
> > > > > > }
> > > > > > } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
> > > > > > + unsigned maj, min;
> > > > > > +
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > + maj = major(stbuf->st_rdev);
> > > > > > + min = minor(stbuf->st_rdev); #else
> > > > > > + maj = min = 0;
> > > > > > +#endif
> > > > >
> > > > > Really?
> > > >
> > > > See above link.
> > > >
> > > > >
> > > > > > v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
> > > > > > S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
> > > > > > - major(stbuf->st_rdev), minor(stbuf->st_rdev));
> > > > > > + maj, min);
> > > > > > } 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); @@
> > > > > > -1317,7 +1349,14 @@ 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) {
> > > > > > - return blksize_to_iounit(pdu, stbuf->st_blksize);
> > > > > > + int32_t blksize;
> > > > > > +
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > + blksize = stbuf->st_blksize); #else
> > > > > > + blksize = 0;
> > > > > > +#endif
> > > > >
> > > > > Really?
> > > >
> > > > Windows struct stat does not have such field. See above link.
> > >
> > > Yeah, but you cannot simply return zero here, because that
> > > information is interpreted on guest side as the optimum chunk size
> > > for I/O. So some apps might misbehave e.g. by trying allocate
> > > buffers with zero size, throwing division by zero exceptions, or
> > > trying to read() / write() with zero chunk size.
> > >
> > > I'm pretty sure there is some kind of API to get the block size of
> > > the underlying drive on Windows. And if not, then something like 4k
> > > or 8k is still better than zero.
> > >
> >
> > The possible solution could be put a hard-code (e.g. 4096, 512) here.
> > This function does not have any context parameter for input.
> > To get block size, need a file handle or path. But this function does not
> have it.
>
> Another major difference on Windows compared to other systems is that you
> don't have to worry about potential submounts in the exported directory tree.
> Because Windows does not have that concept. So on Windows you can assume that
> all files of the exported file tree are on the same storage device, right?
>
> So another solution would be to query the block size with a regular Windows
> API only on startup, simply by using the export root, and then storing that
> block size e.g. as a new member variable to struct V9fsState.
>
> In the end you could actually also use some meaningful hard coded value for
> now, as it is not such of a big deal whether that's 512, 4k or 8k. But zero
> is clearly a no go, for the reasons previously described.
>
Got it, thanks!
> > > > >
> > > > > > + return blksize_to_iounit(pdu, blksize);
> > > > > > }
> > > > > >
> > > > > > static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct
> > > > > > stat *stbuf, @@ -1332,7 +1371,11 @@ static int
> > > > > > stat_to_v9stat_dotl(V9fsPDU *pdu,
> > > > > const struct stat *stbuf,
> > > > > > v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev);
> > > > > > v9lstat->st_size = stbuf->st_size;
> > > > > > v9lstat->st_blksize = stat_to_iounit(pdu, stbuf);
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > v9lstat->st_blocks = stbuf->st_blocks;
> > > > > > +#else
> > > > > > + v9lstat->st_blocks = 0;
> > > > > > +#endif
> > > > >
> > > > > Really?
> > > >
> > > > Windows struct stat does not have such field. See above link.
> > >
> > > Then it probably has to be calculated by file size / block size.
> >
> > Got it.
> >
> > >
> > > > >
> > > > > > v9lstat->st_atime_sec = stbuf->st_atime;
> > > > > > v9lstat->st_mtime_sec = stbuf->st_mtime;
> > > > > > v9lstat->st_ctime_sec = stbuf->st_ctime; @@ -1340,7
> > > > > > +1383,8 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const
> > > > > > struct stat
> > > *stbuf,
> > > > > > v9lstat->st_atime_nsec = stbuf->st_atimespec.tv_nsec;
> > > > > > v9lstat->st_mtime_nsec = stbuf->st_mtimespec.tv_nsec;
> > > > > > v9lstat->st_ctime_nsec = stbuf->st_ctimespec.tv_nsec;
> > > > > > -#else
> > > > > > +#endif
> > > > > > +#ifdef CONFIG_LINUX
> > > > > > v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
> > > > > > v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
> > > > > > v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; @@
> > > > > > -2471,6
> > > > > > +2515,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU
> > > > > > +*pdu,
> > > > > V9fsFidState *fidp,
> > > > > > struct dirent *dent;
> > > > > > struct stat *st;
> > > > > > struct V9fsDirEnt *entries = NULL;
> > > > > > + unsigned char d_type = 0;
> > > > > >
> > > > > > /*
> > > > > > * inode remapping requires the device id, which in turn
> > > > > > might be @@ -2540,10 +2585,13 @@ static int coroutine_fn
> > > > > > v9fs_do_readdir(V9fsPDU
> > > > > *pdu, V9fsFidState *fidp,
> > > > > > v9fs_string_init(&name);
> > > > > > v9fs_string_sprintf(&name, "%s", dent->d_name);
> > > > > >
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > + d_type = dent->d_type; #endif
> > > > > > /* 11 = 7 + 4 (7 = start offset, 4 = space for
> > > > > > storing count)
> > > */
> > > > > > len = pdu_marshal(pdu, 11 + count, "Qqbs",
> > > > > > &qid, off,
> > > > > > - dent->d_type, &name);
> > > > > > + d_type, &name);
> > > > >
> > > > > Are you saying that d_type is not initialized with zero already?
> > > >
> > > > struct dirent is defined by MinGW, it does not have d_type member:
> > > >
> > > > https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers
> > > > /crt
> > > > /dirent.h
> > >
> > > My bad, I misread your code. That's fine.
> > >
> > > >
> > > > >
> > > > > > v9fs_string_free(&name);
> > > > > >
> > > > > > @@ -2873,8 +2921,12 @@ 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;
> > > > > > }
> > > > > > @@ -2899,8 +2951,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
> > > > >
> > > > > As with previous patches, I would consider making the user aware
> > > > > to use
> > > > > mapped(-xattr) with something like error_report_once().
> > > >
> > > > OK, got it.
> > > >
> > > > >
> > > > > > if (err < 0) {
> > > > > > goto out;
> > > > > > }
> > > > > > @@ -3634,7 +3690,7 @@ out_nofid:
> > > > > >
> > > > > > static void coroutine_fn v9fs_mknod(void *opaque) {
> > > > > > -
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > int mode;
> > > > > > gid_t gid;
> > > > > > int32_t fid;
> > > > > > @@ -3691,6 +3747,10 @@ out:
> > > > > > out_nofid:
> > > > > > pdu_complete(pdu, err);
> > > > > > v9fs_string_free(&name);
> > > > > > +#else
> > > > > > + V9fsPDU *pdu = opaque;
> > > > > > + pdu_complete(pdu, -1);
> > > > > > +#endif
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > @@ -3963,7 +4023,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.
> > > > > > @@ -3980,6 +4040,7 @@ out_nofid:
> > > > > >
> > > > > > static void coroutine_fn v9fs_xattrcreate(void *opaque) {
> > > > > > +#ifndef CONFIG_WIN32
> > > > > > int flags, rflags = 0;
> > > > > > int32_t fid;
> > > > > > uint64_t size;
> > > > > > @@ -4041,10 +4102,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;
> > > > > > @@ -4080,6 +4146,10 @@ out:
> > > > > > put_fid(pdu, fidp);
> > > > > > out_nofid:
> > > > > > pdu_complete(pdu, err);
> > > > > > +#else
> > > > > > + V9fsPDU *pdu = opaque;
> > > > > > + pdu_complete(pdu, -1);
> > > > > > +#endif
> > > > >
> > > > > Unnecessary double declaration of pdu.
> > > > >
> > > >
> > > > OK, got it.
> > > >
> > > > > > }
> > > > > >
> > > > > > static CoroutineEntry *pdu_co_handlers[] = { @@ -4341,6
> > > > > > +4411,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"); @@
> > > > > > -4348,4
> > > > > > +4419,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
> > > > >
> > > > > Really?
> > > >
> > > > Windows does not provide getrlimit()
> > >
> > > But it has _getmaxstdio() and _setmaxstdio().
> > >
> > > And even if Windows had no replacement functions, you need to
> > > initialize these two global variables with some meaningful value.
> > > Otherwise they would be zero, and that would mean 9p server would
> > > assume max. 0 files could be open at the same time, so it would
> > > constantly close and re-open every single file descriptor on every minor
> micro-transaction for no reason.
> >
> > I could implement this function by _getmaxstdio() and _setmaxstdio().
> > But these two functions are used for struct FILE, but not file descriptor
> (FD).
> > I wrote a basic test for this function:
> > _getmaxstdio() returns 512, but I can use "open" to open at least 600
> files without error.
> >
> > Windows does not provide any document about the limititaion.
> >
> > I think we can put a hard code here: 512, 1024, 2048, etc.
>
> Yeah, that's pretty badly documented, I agree. The fact that it's prefixed
> with an underscore suggests that they were probably not too convinced about
> their API either. But it is as it is.
>
> The fact that you can open a bit more files than what this function
> advertises is not a huge surprise or speaking against using it though.
> Because it is not untypical for systems to allow overallocation of system
> resources to a certain extent.
>
> At least from what I can see right now, this function is commonly used on
> Windows to get exactly that metric. So I would rather use that than using
> hard coded values, because from what I can see on the web, the numbers people
> got experimentally are varying quite a bit.
>
Got it.
Thanks
Guohuai
> Best regards,
> Christian Schoenebeck
>
^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2022-11-02 12:21 UTC | newest]
Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-24 4:57 [PATCH 00/16] hw/9pfs: Add 9pfs support for Windows Bin Meng
2022-10-24 4:57 ` [PATCH 01/16] qemu/xattr.h: Exclude <sys/xattr.h> " Bin Meng
2022-10-24 4:57 ` [PATCH 02/16] hw/9pfs: Drop unnecessary *xattr wrapper API declarations Bin Meng
2022-10-24 4:57 ` [PATCH 03/16] hw/9pfs: Replace the direct call to xxxat() APIs with a wrapper Bin Meng
2022-10-24 4:57 ` [PATCH 04/16] hw/9pfs: Introduce an opaque type 9P_FILE_ID Bin Meng
2022-11-01 13:55 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 05/16] hw/9pfs: Update P9_FILE_ID to support Windows Bin Meng
2022-10-24 4:57 ` [PATCH 06/16] hw/9pfs: Add missing definitions for Windows Bin Meng
2022-10-24 4:57 ` [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs Bin Meng
2022-11-01 14:27 ` Christian Schoenebeck
2022-11-01 15:13 ` Shi, Guohuai
2022-11-01 15:20 ` Shi, Guohuai
2022-11-01 18:22 ` Christian Schoenebeck
2022-11-02 3:07 ` Shi, Guohuai
2022-11-02 11:05 ` Christian Schoenebeck
2022-11-02 11:28 ` Shi, Guohuai
2022-11-02 11:51 ` Christian Schoenebeck
2022-11-02 12:06 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 08/16] hw/9pfs: Handle current directory offset for Windows Bin Meng
2022-11-01 14:41 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 09/16] hw/9pfs: Disable unsupported flags and features " Bin Meng
2022-11-01 15:04 ` Christian Schoenebeck
2022-11-01 15:34 ` Shi, Guohuai
2022-11-01 18:59 ` Christian Schoenebeck
2022-11-02 3:44 ` Shi, Guohuai
2022-11-02 11:34 ` Christian Schoenebeck
2022-11-02 12:19 ` Shi, Guohuai
2022-10-24 4:57 ` [PATCH 10/16] hw/9pfs: Update the local fs driver to support Windows Bin Meng
2022-10-24 4:57 ` [PATCH 11/16] hw/9pfs: Add Linux error number definition Bin Meng
2022-11-01 15:21 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 12/16] hw/9pfs: Translate Windows errno to Linux value Bin Meng
2022-10-24 4:57 ` [PATCH 13/16] fsdev: Disable proxy fs driver on Windows Bin Meng
2022-10-24 4:57 ` [PATCH 14/16] hw/9pfs: Update synth fs driver for Windows Bin Meng
2022-10-24 4:57 ` [PATCH 15/16] tests/qtest: virtio-9p-test: Adapt the case for win32 Bin Meng
2022-10-25 15:55 ` Thomas Huth
2022-11-01 15:32 ` Christian Schoenebeck
2022-10-24 4:57 ` [PATCH 16/16] meson.build: Turn on virtfs for Windows Bin Meng
2022-10-27 16:19 ` [PATCH 00/16] hw/9pfs: Add 9pfs support " Bin Meng
2022-10-27 16:30 ` Christian Schoenebeck
2022-10-28 2:25 ` Bin Meng
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.