Skip to content

Instantly share code, notes, and snippets.

@woachk
Created December 11, 2020 17:11
Show Gist options
  • Save woachk/3a7c88d8b04acea8633a6297600f2a21 to your computer and use it in GitHub Desktop.
Save woachk/3a7c88d8b04acea8633a6297600f2a21 to your computer and use it in GitHub Desktop.
Totally unsupported 9p on Darwin patch (inspired from the Julia Computing patchset circa 2018)
diff --git a/configure b/configure
index 18c26e0389..ceab414c5b 100755
--- a/configure
+++ b/configure
@@ -5754,22 +5754,26 @@ if [ "$eventfd" = "yes" ]; then
fi
if test "$softmmu" = yes ; then
- if test "$linux" = yes; then
- if test "$virtfs" != no && test "$cap_ng" = yes && test "$attr" = yes ; then
+ if test "$virtfs" != no; then
+ if test "$linux" = yes; then
+ if test "$cap" = yes && test "$attr" = yes ; then
+ virtfs=yes
+ else
+ if test "$virtfs" = yes; then
+ error_exit "VirtFS requires libcap-ng devel and libattr devel"
+ fi
+ virtfs=no
+ fi
+ fi
+ elif test "$darwin" = yes; then
virtfs=yes
else
if test "$virtfs" = yes; then
- error_exit "VirtFS requires libcap-ng devel and libattr devel"
+ error_exit "VirtFS is supported only on Linux and Darwin"
fi
virtfs=no
fi
- else
- if test "$virtfs" = yes; then
- error_exit "VirtFS is supported only on Linux"
- fi
- virtfs=no
fi
-fi
# Probe for guest agent support/options
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index d51cec2f3b..65c78678ab 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -16,7 +16,7 @@
#include <dirent.h>
#include <utime.h>
-#include <sys/vfs.h>
+#include "qemu/statfs.h"
#include "qemu-fsdev-throttle.h"
#define SM_LOCAL_MODE_BITS 0600
diff --git a/fsdev/meson.build b/fsdev/meson.build
index 7dd1cc9bfb..8b7d3754fa 100644
--- a/fsdev/meson.build
+++ b/fsdev/meson.build
@@ -7,7 +7,7 @@ fsdev_ss.add(when: ['CONFIG_FSDEV_9P'], if_true: files(
'qemu-fsdev.c',
), 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)
have_virtfs_proxy_helper = have_tools and libattr.found() and libcap_ng.found() and 'CONFIG_VIRTFS' in config_host
if have_virtfs_proxy_helper
executable('virtfs-proxy-helper',
diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c
index 15c0e79b06..0d2d47e1cb 100644
--- a/fsdev/virtfs-proxy-helper.c
+++ b/fsdev/virtfs-proxy-helper.c
@@ -13,19 +13,21 @@
#include <sys/resource.h>
#include <getopt.h>
#include <syslog.h>
+#ifdef CONFIG_LINUX
#include <sys/fsuid.h>
-#include <sys/vfs.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#ifdef CONFIG_LINUX_MAGIC_H
#include <linux/magic.h>
#endif
+#endif
#include <cap-ng.h>
#include "qemu-common.h"
#include "qemu/sockets.h"
#include "qemu/xattr.h"
#include "9p-iov-marshal.h"
#include "hw/9pfs/9p-proxy.h"
+#include "hw/9pfs/9p-util.h"
#include "fsdev/9p-iov-marshal.h"
#define PROGNAME "virtfs-proxy-helper"
@@ -78,6 +80,85 @@ static void do_perror(const char *string)
fprintf(stderr, "%s:%s\n", string, strerror(errno));
}
}
+#ifdef CONFIG_LINUX
+static int acquire_dac_override(void)
+{
+ cap_value_t cap_list[] = {
+ CAP_DAC_OVERRIDE,
+ };
+ return do_cap_set(cap_list, ARRAY_SIZE(cap_list), 0);
+}
+
+/*
+ * from man 7 capabilities, section
+ * Effect of User ID Changes on Capabilities:
+ * If the effective user ID is changed from nonzero to 0, then the permitted
+ * set is copied to the effective set. If the effective user ID is changed
+ * from 0 to nonzero, then all capabilities are are cleared from the effective
+ * set.
+ *
+ * The setfsuid/setfsgid man pages warn that changing the effective user ID may
+ * expose the program to unwanted signals, but this is not true anymore: for an
+ * unprivileged (without CAP_KILL) program to send a signal, the real or
+ * effective user ID of the sending process must equal the real or saved user
+ * ID of the target process. Even when dropping privileges, it is enough to
+ * keep the saved UID to a "privileged" value and virtfs-proxy-helper won't
+ * be exposed to signals. So just use setresuid/setresgid.
+ */
+static int setugid(int uid, int gid, int *suid, int *sgid)
+{
+ int retval;
+
+ *suid = geteuid();
+ *sgid = getegid();
+
+ if (setresgid(-1, gid, *sgid) == -1) {
+ retval = -errno;
+ goto err_out;
+ }
+
+ if (setresuid(-1, uid, *suid) == -1) {
+ retval = -errno;
+ goto err_sgid;
+ }
+
+ if (uid != 0 || gid != 0) {
+ /*
+ * We still need DAC_OVERRIDE because we don't change
+ * supplementary group ids, and hence may be subjected DAC rules
+ */
+ if (acquire_dac_override() < 0) {
+ retval = -errno;
+ goto err_suid;
+ }
+ }
+ return 0;
+
+err_suid:
+ if (setresuid(-1, *suid, *suid) == -1) {
+ abort();
+ }
+err_sgid:
+ if (setresgid(-1, *sgid, *sgid) == -1) {
+ abort();
+ }
+err_out:
+ return retval;
+}
+
+/*
+ * This is used to reset the ugid back with the saved values
+ * There is nothing much we can do checking error values here.
+ */
+static void resetugid(int suid, int sgid)
+{
+ if (setresgid(-1, sgid, sgid) == -1) {
+ abort();
+ }
+ if (setresuid(-1, suid, suid) == -1) {
+ abort();
+ }
+}
static int init_capabilities(void)
{
@@ -120,6 +201,51 @@ static int init_capabilities(void)
}
return 0;
}
+#else
+static int setugid(int uid, int gid, int *suid, int *sgid)
+{
+ int retval;
+
+ *suid = geteuid();
+ *sgid = getegid();
+
+ if (setegid(gid) == -1) {
+ retval = -errno;
+ goto err_out;
+ }
+
+ if (seteuid(uid) == -1) {
+ retval = -errno;
+ goto err_sgid;
+ }
+
+err_sgid:
+ if (setgid(*sgid) == -1) {
+ abort();
+ }
+err_out:
+ return retval;
+}
+
+/*
+ * This is used to reset the ugid back with the saved values
+ * There is nothing much we can do checking error values here.
+ */
+static void resetugid(int suid, int sgid)
+{
+ if (setegid(sgid) == -1) {
+ abort();
+ }
+ if (seteuid(suid) == -1) {
+ abort();
+ }
+}
+
+static int init_capabilities(void)
+{
+ return 0;
+}
+#endif
static int socket_read(int sockfd, void *buff, ssize_t size)
{
@@ -445,7 +571,7 @@ static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec)
v9fs_string_init(&name);
retval = proxy_unmarshal(iovec, offset, "s", &name);
if (retval > 0) {
- retval = lgetxattr(path.data, name.data, xattr.data, size);
+ retval = qemu_lgetxattr(path.data, name.data, xattr.data, size);
if (retval < 0) {
retval = -errno;
} else {
@@ -455,7 +581,7 @@ static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec)
v9fs_string_free(&name);
break;
case T_LLISTXATTR:
- retval = llistxattr(path.data, xattr.data, size);
+ retval = qemu_llistxattr(path.data, xattr.data, size);
if (retval < 0) {
retval = -errno;
} else {
@@ -934,8 +1060,7 @@ static int process_requests(int sock)
&spec[0].tv_sec, &spec[0].tv_nsec,
&spec[1].tv_sec, &spec[1].tv_nsec);
if (retval > 0) {
- retval = utimensat(AT_FDCWD, path.data, spec,
- AT_SYMLINK_NOFOLLOW);
+ retval = utimensat_nofollow(AT_FDCWD, path.data, spec);
if (retval < 0) {
retval = -errno;
}
@@ -978,7 +1103,7 @@ static int process_requests(int sock)
retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path,
&name, &value, &size, &flags);
if (retval > 0) {
- retval = lsetxattr(path.data,
+ retval = qemu_lsetxattr(path.data,
name.data, value.data, size, flags);
if (retval < 0) {
retval = -errno;
@@ -994,7 +1119,7 @@ static int process_requests(int sock)
retval = proxy_unmarshal(&in_iovec,
PROXY_HDR_SZ, "ss", &path, &name);
if (retval > 0) {
- retval = lremovexattr(path.data, name.data);
+ retval = qemu_lremovexattr(path.data, name.data);
if (retval < 0) {
retval = -errno;
}
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index af52c1daac..c61c2efd34 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -27,10 +27,12 @@
#include "qemu/error-report.h"
#include "qemu/option.h"
#include <libgen.h>
+#ifdef CONFIG_LINUX
#include <linux/fs.h>
#ifdef CONFIG_LINUX_MAGIC_H
#include <linux/magic.h>
#endif
+#endif
#include <sys/ioctl.h>
#ifndef XFS_SUPER_MAGIC
@@ -666,7 +668,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
- err = mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0);
+ err = qemu_mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0);
if (err == -1) {
goto out;
}
@@ -681,7 +683,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
}
} else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
fs_ctx->export_flags & V9FS_SM_NONE) {
- err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
+ err = qemu_mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
if (err == -1) {
goto out;
}
@@ -694,6 +696,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
err_end:
unlinkat_preserve_errno(dirfd, name, 0);
+
out:
close_preserve_errno(dirfd);
return err;
@@ -774,20 +777,24 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
mode_t tmp_mode;
dev_t tmp_dev;
- if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
+ if (qemu_fgetxattr(fd, "user.virtfs.uid",
+ &tmp_uid, sizeof(uid_t)) > 0) {
stbuf->st_uid = le32_to_cpu(tmp_uid);
}
- if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
+ if (qemu_fgetxattr(fd, "user.virtfs.gid",
+ &tmp_gid, sizeof(gid_t)) > 0) {
stbuf->st_gid = le32_to_cpu(tmp_gid);
}
- if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
+ if (qemu_fgetxattr(fd, "user.virtfs.mode",
+ &tmp_mode, sizeof(mode_t)) > 0) {
stbuf->st_mode = le32_to_cpu(tmp_mode);
}
- if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
+ if (qemu_fgetxattr(fd, "user.virtfs.rdev",
+ &tmp_dev, sizeof(dev_t)) > 0) {
stbuf->st_rdev = le64_to_cpu(tmp_dev);
}
} else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
- errno = EOPNOTSUPP;
+ errno = P9_EOPNOTSUPP;
return -1;
}
return err;
@@ -1065,7 +1072,7 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path,
goto out;
}
- ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
+ ret = utimensat_nofollow(dirfd, name, buf);
close_preserve_errno(dirfd);
out:
g_free(dirpath);
diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c
index 6f598a0f11..909bda2a26 100644
--- a/hw/9pfs/9p-proxy.c
+++ b/hw/9pfs/9p-proxy.c
@@ -118,10 +118,15 @@ static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs)
stfs->f_bavail = prstfs->f_bavail;
stfs->f_files = prstfs->f_files;
stfs->f_ffree = prstfs->f_ffree;
+#ifdef CONFIG_LINUX
stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
stfs->f_namelen = prstfs->f_namelen;
stfs->f_frsize = prstfs->f_frsize;
+#else
+ stfs->f_fsid.val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU;
+ stfs->f_fsid.val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU;
+#endif
}
/* Converts proxy_stat structure to VFS stat structure */
@@ -138,12 +143,19 @@ static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat)
stbuf->st_size = prstat->st_size;
stbuf->st_blksize = prstat->st_blksize;
stbuf->st_blocks = prstat->st_blocks;
+ stbuf->st_mtime = prstat->st_mtim_sec;
+ stbuf->st_ctime = prstat->st_ctim_sec;
+#ifdef CONFIG_LINUX
stbuf->st_atim.tv_sec = prstat->st_atim_sec;
stbuf->st_atim.tv_nsec = prstat->st_atim_nsec;
- stbuf->st_mtime = prstat->st_mtim_sec;
stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec;
- stbuf->st_ctime = prstat->st_ctim_sec;
stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec;
+#else
+ stbuf->st_atimespec.tv_sec = prstat->st_atim_sec;
+ stbuf->st_atimespec.tv_nsec = prstat->st_atim_nsec;
+ stbuf->st_mtimespec.tv_nsec = prstat->st_mtim_nsec;
+ stbuf->st_ctimespec.tv_nsec = prstat->st_ctim_nsec;
+#endif
}
/*
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index 7eb210ffa8..3194d1db2d 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -221,7 +221,9 @@ static void synth_direntry(V9fsSynthNode *node,
{
strcpy(entry->d_name, node->name);
entry->d_ino = node->attr->inode;
+#ifndef CONFIG_DARWIN
entry->d_off = off + 1;
+#endif
}
static struct dirent *synth_get_dentry(V9fsSynthNode *dir,
@@ -426,7 +428,9 @@ static int synth_statfs(FsContext *s, V9fsPath *fs_path,
stbuf->f_bsize = 512;
stbuf->f_blocks = 0;
stbuf->f_files = synth_node_count;
+#ifdef CONFIG_LINUX
stbuf->f_namelen = NAME_MAX;
+#endif
return 0;
}
diff --git a/hw/9pfs/9p-util-darwin.c b/hw/9pfs/9p-util-darwin.c
new file mode 100644
index 0000000000..194f068f00
--- /dev/null
+++ b/hw/9pfs/9p-util-darwin.c
@@ -0,0 +1,191 @@
+/*
+ * 9p utilities (Darwin Implementation)
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/xattr.h"
+#include "9p-util.h"
+
+ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
+ void *value, size_t size)
+{
+ int ret;
+ int fd = openat_file(dirfd, filename,
+ O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ ret = fgetxattr(fd, name, value, size, 0, 0);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
+ char *list, size_t size)
+{
+ int ret;
+ int fd = openat_file(dirfd, filename,
+ O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ ret = flistxattr(fd, list, size, 0);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
+ const char *name)
+{
+ int ret;
+ int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+ ret = fremovexattr(fd, name, 0);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+int fsetxattrat_nofollow(int 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);
+ if (fd == -1) {
+ return -1;
+ }
+ ret = fsetxattr(fd, name, value, size, 0, flags);
+ close_preserve_errno(fd);
+ return ret;
+}
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+static int update_times_from_stat(int fd, struct timespec times[2],
+ int update0, int update1)
+{
+ struct stat buf;
+ int ret = fstat(fd, &buf);
+ if (ret == -1) {
+ return ret;
+ }
+ if (update0) {
+ times[0] = buf.st_atimespec;
+ }
+ if (update1) {
+ times[1] = buf.st_mtimespec;
+ }
+ return 0;
+}
+
+int utimensat_nofollow(int dirfd, const char *filename,
+ const struct timespec times_in[2])
+{
+ int ret, fd;
+ int special0, special1;
+ struct timeval futimes_buf[2];
+ struct timespec times[2];
+ memcpy(times, times_in, 2 * sizeof(struct timespec));
+
+/* Check whether we have an SDK version that defines utimensat */
+#if defined(__MAC_10_13)
+# if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_13
+# define UTIMENSAT_AVAILABLE 1
+# elif __has_builtin(__builtin_available)
+# define UTIMENSAT_AVAILABLE __builtin_available(macos 10.13, *)
+# else
+# define UTIMENSAT_AVAILABLE 0
+# endif
+ if (UTIMENSAT_AVAILABLE) {
+ return utimensat(dirfd, filename, times, AT_SYMLINK_NOFOLLOW);
+ }
+#endif
+
+ /* utimensat not available. Use futimes. */
+ fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
+ if (fd == -1) {
+ return -1;
+ }
+
+ special0 = times[0].tv_nsec == UTIME_OMIT;
+ special1 = times[1].tv_nsec == UTIME_OMIT;
+ if (special0 || special1) {
+ /* If both are set, nothing to do */
+ if (special0 && special1) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = update_times_from_stat(fd, times, special0, special1);
+ if (ret < 0) {
+ goto done;
+ }
+ }
+
+ special0 = times[0].tv_nsec == UTIME_NOW;
+ special1 = times[1].tv_nsec == UTIME_NOW;
+ if (special0 || special1) {
+ ret = futimes(fd, NULL);
+ if (ret < 0) {
+ goto done;
+ }
+
+ /* If both are set, we are done */
+ if (special0 && special1) {
+ ret = 0;
+ goto done;
+ }
+
+ ret = update_times_from_stat(fd, times, special0, special1);
+ if (ret < 0) {
+ goto done;
+ }
+ }
+
+ futimes_buf[0].tv_sec = times[0].tv_sec;
+ futimes_buf[0].tv_usec = times[0].tv_nsec / 1000;
+ futimes_buf[1].tv_sec = times[1].tv_sec;
+ futimes_buf[1].tv_usec = times[1].tv_nsec / 1000;
+ ret = futimes(fd, futimes_buf);
+
+done:
+ close_preserve_errno(fd);
+ return ret;
+}
+
+#ifndef SYS___pthread_fchdir
+# define SYS___pthread_fchdir 349
+#endif
+
+// This is an undocumented OS X syscall. It would be best to avoid it,
+// but there doesn't seem to be another safe way to implement mknodat.
+// Dear Apple, please implement mknodat before you remove this syscall.
+static int fchdir_thread_local(int fd)
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ return syscall(SYS___pthread_fchdir, fd);
+#pragma clang diagnostic pop
+}
+
+int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
+{
+ int preserved_errno, err;
+ if (fchdir_thread_local(dirfd) < 0) {
+ return -1;
+ }
+ err = mknod(filename, mode, dev);
+ preserved_errno = errno;
+ /* Stop using the thread-local cwd */
+ fchdir_thread_local(-1);
+ if (err < 0) {
+ errno = preserved_errno;
+ }
+ return err;
+}
diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util-linux.c
similarity index 83%
rename from hw/9pfs/9p-util.c
rename to hw/9pfs/9p-util-linux.c
index 614b7fc34d..17fa6e6bac 100644
--- a/hw/9pfs/9p-util.c
+++ b/hw/9pfs/9p-util-linux.c
@@ -57,3 +57,14 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
g_free(proc_path);
return ret;
}
+
+int utimensat_nofollow(int dirfd, const char *filename,
+ const struct timespec times[2])
+{
+ return utimensat(dirfd, filename, times, AT_SYMLINK_NOFOLLOW);
+}
+
+int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
+{
+ return mknodat(dirfd, filename, mode, dev);
+}
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 546f46dc7d..3262b19346 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -19,6 +19,30 @@
#define O_PATH_9P_UTIL 0
#endif
+#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);
+}
+#define O_NOATIME 0
+#else
+#define qemu_fgetxattr fgetxattr
+#define qemu_lgetxattr lgetxattr
+#define qemu_llistxattr llistxattr
+#define qemu_lremovexattr lremovexattr
+#define qemu_lsetxattr lsetxattr
+#endif
+
+/* Compatibility with old SDK Versions for Darwin */
+#if defined(CONFIG_DARWIN) && !defined(UTIME_NOW)
+#define UTIME_NOW -1
+#define UTIME_OMIT -2
+#endif
+
static inline void close_preserve_errno(int fd)
{
int serrno = errno;
@@ -77,5 +101,9 @@ ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
char *list, size_t size);
ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
const char *name);
+int utimensat_nofollow(int dirfd, const char *filename,
+ const struct timespec times[2]);
+
+int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev);
#endif
diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c
index c696d8f846..9c334d7478 100644
--- a/hw/9pfs/9p-xattr.c
+++ b/hw/9pfs/9p-xattr.c
@@ -38,7 +38,7 @@ ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
if (xops) {
return xops->getxattr(ctx, path, name, value, size);
}
- errno = EOPNOTSUPP;
+ errno = P9_EOPNOTSUPP;
return -1;
}
@@ -141,7 +141,7 @@ int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
if (xops) {
return xops->setxattr(ctx, path, name, value, size, flags);
}
- errno = EOPNOTSUPP;
+ errno = P9_EOPNOTSUPP;
return -1;
}
@@ -153,7 +153,7 @@ int v9fs_remove_xattr(FsContext *ctx,
if (xops) {
return xops->removexattr(ctx, path, name);
}
- errno = EOPNOTSUPP;
+ errno = P9_EOPNOTSUPP;
return -1;
}
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 94df440fc7..4caf619564 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -22,13 +22,16 @@
#include "virtio-9p.h"
#include "fsdev/qemu-fsdev.h"
#include "9p-xattr.h"
+#include "9p-util.h"
#include "coth.h"
#include "trace.h"
#include "migration/blocker.h"
#include "sysemu/qtest.h"
#include "qemu/xxhash.h"
#include <math.h>
+#ifdef CONFIG_LINUX
#include <linux/limits.h>
+#endif
int open_fd_hw;
int total_open_fd;
@@ -127,11 +130,18 @@ static int dotl_to_open_flags(int flags)
{ P9_DOTL_NONBLOCK, O_NONBLOCK } ,
{ P9_DOTL_DSYNC, O_DSYNC },
{ P9_DOTL_FASYNC, FASYNC },
+#ifndef CONFIG_DARWIN
+ { P9_DOTL_NOATIME, O_NOATIME },
+ /* On Darwin, we could map to F_NOCACHE, which is
+ similar, but doesn't quite have the same
+ semantics. However, we don't support O_DIRECT
+ even on linux at the moment, so we just ignore
+ it here. */
{ P9_DOTL_DIRECT, O_DIRECT },
+#endif
{ P9_DOTL_LARGEFILE, O_LARGEFILE },
{ P9_DOTL_DIRECTORY, O_DIRECTORY },
{ P9_DOTL_NOFOLLOW, O_NOFOLLOW },
- { P9_DOTL_NOATIME, O_NOATIME },
{ P9_DOTL_SYNC, O_SYNC },
};
@@ -160,10 +170,12 @@ static int get_dotl_openflags(V9fsState *s, int oflags)
*/
flags = dotl_to_open_flags(oflags);
flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
+#ifndef CONFIG_DARWIN
/*
* Ignore direct disk access hint until the server supports it.
*/
flags &= ~O_DIRECT;
+#endif
return flags;
}
@@ -1277,11 +1289,17 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf,
v9lstat->st_blksize = stbuf->st_blksize;
v9lstat->st_blocks = stbuf->st_blocks;
v9lstat->st_atime_sec = stbuf->st_atime;
- v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
v9lstat->st_mtime_sec = stbuf->st_mtime;
- v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
v9lstat->st_ctime_sec = stbuf->st_ctime;
+#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;
+#else
+ 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;
+#endif
/* Currently we only support BASIC fields in stat */
v9lstat->st_result_mask = P9_STATS_BASIC;
@@ -2138,6 +2156,25 @@ static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
return offset;
}
+/**
+ * Get the seek offset of a dirent. If not available from the structure itself,
+ * obtain it by calling telldir.
+ */
+static int v9fs_dent_telldir(V9fsPDU *pdu, V9fsFidState *fidp,
+ struct dirent *dent)
+{
+#ifdef CONFIG_DARWIN
+ /*
+ * Darwin has d_seekoff, which appears to function similarly to d_off.
+ * However, it does not appear to be supported on all file systems,
+ * so use telldir for correctness.
+ */
+ return v9fs_co_telldir(pdu, fidp);
+#else
+ return dent->d_off;
+#endif
+}
+
static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu,
V9fsFidState *fidp,
uint32_t max_count)
@@ -2146,8 +2183,8 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu,
V9fsStat v9stat;
int len, err = 0;
int32_t count = 0;
- struct stat stbuf;
off_t saved_dir_pos;
+ struct stat stbuf;
struct dirent *dent;
/* save the directory position */
@@ -2201,7 +2238,11 @@ static int coroutine_fn v9fs_do_readdir_with_stat(V9fsPDU *pdu,
count += len;
v9fs_stat_free(&v9stat);
v9fs_path_free(&path);
- saved_dir_pos = dent->d_off;
+ saved_dir_pos = v9fs_dent_telldir(pdu, fidp, dent);
+ if (saved_dir_pos < 0) {
+ err = saved_dir_pos;
+ break;
+ }
}
v9fs_readdir_unlock(&fidp->fs.dir);
@@ -2242,7 +2283,7 @@ static void coroutine_fn v9fs_read(void *opaque)
"9p: bad client: T_read request on directory only expected "
"with 9P2000.u protocol version"
);
- err = -EOPNOTSUPP;
+ err = -P9_EOPNOTSUPP;
goto out;
}
if (off == 0) {
@@ -2343,6 +2384,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
struct dirent *dent;
struct stat *st;
struct V9fsDirEnt *entries = NULL;
+ off_t off;
/*
* inode remapping requires the device id, which in turn might be
@@ -2398,6 +2440,11 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
/* Fill the other fields with dummy values */
qid.type = 0;
qid.version = 0;
+ off = v9fs_dent_telldir(pdu, fidp, dent);
+ if (off < 0) {
+ err = off;
+ break;
+ }
}
v9fs_string_init(&name);
@@ -2405,7 +2452,7 @@ static int coroutine_fn v9fs_do_readdir(V9fsPDU *pdu, V9fsFidState *fidp,
/* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
len = pdu_marshal(pdu, 11 + count, "Qqbs",
- &qid, dent->d_off,
+ &qid, off,
dent->d_type, &name);
v9fs_string_free(&name);
@@ -2467,7 +2514,7 @@ static void coroutine_fn v9fs_readdir(void *opaque)
"9p: bad client: T_readdir request only expected with 9P2000.L "
"protocol version"
);
- retval = -EOPNOTSUPP;
+ retval = -P9_EOPNOTSUPP;
goto out;
}
count = v9fs_do_readdir(pdu, fidp, (off_t) initial_offset, max_count);
@@ -2978,9 +3025,9 @@ static void coroutine_fn v9fs_remove(void *opaque)
err = -EINVAL;
goto out_nofid;
}
- /* if fs driver is not path based, return EOPNOTSUPP */
+ /* if fs driver is not path based, return P9_EOPNOTSUPP */
if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
- err = -EOPNOTSUPP;
+ err = -P9_EOPNOTSUPP;
goto out_err;
}
/*
@@ -3172,9 +3219,9 @@ static void coroutine_fn v9fs_rename(void *opaque)
err = -EINVAL;
goto out;
}
- /* if fs driver is not path based, return EOPNOTSUPP */
+ /* if fs driver is not path based, return P9_EOPNOTSUPP */
if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
- err = -EOPNOTSUPP;
+ err = -P9_EOPNOTSUPP;
goto out;
}
v9fs_path_write_lock(s);
@@ -3445,10 +3492,15 @@ static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf)
f_bavail = stbuf->f_bavail / bsize_factor;
f_files = stbuf->f_files;
f_ffree = stbuf->f_ffree;
+#ifdef CONFIG_LINUX
fsid_val = (unsigned int) stbuf->f_fsid.__val[0] |
(unsigned long long)stbuf->f_fsid.__val[1] << 32;
f_namelen = stbuf->f_namelen;
-
+#else
+ fsid_val = (unsigned int) stbuf->f_fsid.val[0] |
+ (unsigned long long)stbuf->f_fsid.val[1] << 32;
+ f_namelen = 256;
+#endif
return pdu_marshal(pdu, offset, "ddqqqqqqd",
f_type, f_bsize, f_blocks, f_bfree,
f_bavail, f_files, f_ffree,
@@ -3817,6 +3869,13 @@ out_nofid:
v9fs_string_free(&name);
}
+#if defined(CONFIG_DARWIN) && !defined(XATTR_SIZE_MAX)
+/* Darwin doesn't seem to define a maximum xattr size in its user
+ user space header, but looking at the kernel source, HFS supports
+ up to INT32_MAX, so use that as the maximum.
+*/
+#define XATTR_SIZE_MAX INT32_MAX
+#endif
static void coroutine_fn v9fs_xattrcreate(void *opaque)
{
int flags, rflags = 0;
@@ -3961,7 +4020,7 @@ static CoroutineEntry *pdu_co_handlers[] = {
static void coroutine_fn v9fs_op_not_supp(void *opaque)
{
V9fsPDU *pdu = opaque;
- pdu_complete(pdu, -EOPNOTSUPP);
+ pdu_complete(pdu, -P9_EOPNOTSUPP);
}
static void coroutine_fn v9fs_fs_ro(void *opaque)
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 32df81f360..b2c3346211 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -402,6 +402,9 @@ struct V9fsState {
#define P9_LOCK_FLAGS_BLOCK 1
#define P9_LOCK_FLAGS_RECLAIM 2
+/* Error types */
+#define P9_EOPNOTSUPP 95
+
typedef struct V9fsFlock
{
uint8_t type;
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c
index 1f70a58df5..05466fdd54 100644
--- a/hw/9pfs/codir.c
+++ b/hw/9pfs/codir.c
@@ -162,7 +162,12 @@ static int do_readdir_many(V9fsPDU *pdu, V9fsFidState *fidp,
}
size += len;
+#ifdef CONFIG_LINUX
saved_dir_pos = dent->d_off;
+#endif
+#ifdef CONFIG_DARWIN
+ saved_dir_pos = dent->d_seekoff + 1;
+#endif
}
/* restore (last) saved position */
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 83bb6c14e0..84f9820ec3 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -82,7 +82,7 @@ int coroutine_fn v9fs_co_fstat(V9fsPDU *pdu, V9fsFidState *fidp,
* Some FS driver (local:mapped-file) can't support fetching attributes
* using file descriptor. Use Path name in that case.
*/
- if (err == -EOPNOTSUPP) {
+ if (err == -P9_EOPNOTSUPP) {
err = v9fs_co_lstat(pdu, &fidp->path, stbuf);
if (err == -ENOENT) {
/*
diff --git a/hw/9pfs/meson.build b/hw/9pfs/meson.build
index 99be5d9119..357d07e2d2 100644
--- a/hw/9pfs/meson.build
+++ b/hw/9pfs/meson.build
@@ -4,7 +4,6 @@ fs_ss.add(files(
'9p-posix-acl.c',
'9p-proxy.c',
'9p-synth.c',
- '9p-util.c',
'9p-xattr-user.c',
'9p-xattr.c',
'9p.c',
@@ -15,6 +14,8 @@ fs_ss.add(files(
'coxattr.c',
))
fs_ss.add(when: 'CONFIG_XEN', if_true: files('xen-9p-backend.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'))
softmmu_ss.add_all(when: 'CONFIG_FSDEV_9P', if_true: fs_ss)
specific_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-device.c'))
diff --git a/include/qemu/statfs.h b/include/qemu/statfs.h
new file mode 100644
index 0000000000..dde289f9bb
--- /dev/null
+++ b/include/qemu/statfs.h
@@ -0,0 +1,19 @@
+/*
+ * Host statfs header abstraction
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2, or any
+ * later version. See the COPYING file in the top-level directory.
+ *
+ */
+#ifndef QEMU_STATFS_H
+#define QEMU_STATFS_H
+
+#ifdef CONFIG_LINUX
+# include <sys/vfs.h>
+#endif
+#ifdef CONFIG_DARWIN
+# include <sys/param.h>
+# include <sys/mount.h>
+#endif
+
+#endif
diff --git a/include/qemu/xattr.h b/include/qemu/xattr.h
index a83fe8e749..f1d0f7be74 100644
--- a/include/qemu/xattr.h
+++ b/include/qemu/xattr.h
@@ -22,7 +22,9 @@
#ifdef CONFIG_LIBATTR
# include <attr/xattr.h>
#else
-# define ENOATTR ENODATA
+# if !defined(ENOATTR)
+# define ENOATTR ENODATA
+# endif
# include <sys/xattr.h>
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment