qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities function


From: Shi, Guohuai
Subject: RE: [PATCH 07/16] hw/9pfs: Implement Windows specific utilities functions for 9pfs
Date: Tue, 1 Nov 2022 15:13:09 +0000


> -----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;
> > +}
> >
> 
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]