Created
December 11, 2020 17:11
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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