@@ -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);
}