diff options
-rw-r--r-- | hw/9pfs/9p-posix-acl.c | 12 | ||||
-rw-r--r-- | hw/9pfs/9p-synth.c | 4 | ||||
-rw-r--r-- | hw/9pfs/9p-util-darwin.c | 54 | ||||
-rw-r--r-- | hw/9pfs/9p-util.h | 69 | ||||
-rw-r--r-- | hw/9pfs/9p.c | 4 |
5 files changed, 136 insertions, 7 deletions
diff --git a/hw/9pfs/9p-posix-acl.c b/hw/9pfs/9p-posix-acl.c index eadae270dd..4b2cb3c66c 100644 --- a/hw/9pfs/9p-posix-acl.c +++ b/hw/9pfs/9p-posix-acl.c @@ -65,7 +65,11 @@ static int mp_pacl_removexattr(FsContext *ctx, int ret; ret = local_removexattr_nofollow(ctx, path, MAP_ACL_ACCESS); - if (ret == -1 && errno == ENODATA) { + /* + * macOS returns ENOATTR (!=ENODATA on macOS), whereas Linux returns + * ENODATA (==ENOATTR on Linux), so checking for ENOATTR is fine + */ + if (ret == -1 && errno == ENOATTR) { /* * We don't get ENODATA error when trying to remove a * posix acl that is not present. So don't throw the error @@ -115,7 +119,11 @@ static int mp_dacl_removexattr(FsContext *ctx, int ret; ret = local_removexattr_nofollow(ctx, path, MAP_ACL_DEFAULT); - if (ret == -1 && errno == ENODATA) { + /* + * macOS returns ENOATTR (!=ENODATA on macOS), whereas Linux returns + * ENODATA (==ENOATTR on Linux), so checking for ENOATTR is fine + */ + if (ret == -1 && errno == ENOATTR) { /* * We don't get ENODATA error when trying to remove a * posix acl that is not present. So don't throw the error diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index d99d263985..1c5813e4dd 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -92,7 +92,7 @@ int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode, } } /* Add the name */ - node = v9fs_add_dir_node(parent, mode, name, NULL, synth_node_count++); + node = v9fs_add_dir_node(parent, mode, name, NULL, ++synth_node_count); v9fs_add_dir_node(node, parent->attr->mode, "..", parent->attr, parent->attr->inode); v9fs_add_dir_node(node, node->attr->mode, ".", @@ -130,7 +130,7 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, mode = ((mode & 0777) | S_IFREG); node = g_new0(V9fsSynthNode, 1); node->attr = &node->actual_attr; - node->attr->inode = synth_node_count++; + node->attr->inode = ++synth_node_count; node->attr->nlink = 1; node->attr->read = read; node->attr->write = write; diff --git a/hw/9pfs/9p-util-darwin.c b/hw/9pfs/9p-util-darwin.c index bec0253474..95146e7354 100644 --- a/hw/9pfs/9p-util-darwin.c +++ b/hw/9pfs/9p-util-darwin.c @@ -74,17 +74,67 @@ int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, */ #if defined CONFIG_PTHREAD_FCHDIR_NP +static int create_socket_file_at_cwd(const char *filename, mode_t mode) { + int fd, err; + struct sockaddr_un addr = { + .sun_family = AF_UNIX + }; + + err = snprintf(addr.sun_path, sizeof(addr.sun_path), "./%s", filename); + if (err < 0 || err >= sizeof(addr.sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (fd == -1) { + return fd; + } + err = bind(fd, (struct sockaddr *) &addr, sizeof(addr)); + if (err == -1) { + goto out; + } + /* + * FIXME: Should rather be using descriptor-based fchmod() on the + * socket file descriptor above (preferably before bind() call), + * instead of path-based fchmodat(), to prevent concurrent transient + * state issues between creating the named FIFO file at bind() and + * delayed adjustment of permissions at fchmodat(). However currently + * macOS (12.x) does not support such operations on socket file + * descriptors yet. + * + * Filed report with Apple: FB9997731 + */ + err = fchmodat(AT_FDCWD, filename, mode, AT_SYMLINK_NOFOLLOW); +out: + close_preserve_errno(fd); + return err; +} + int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev) { int preserved_errno, err; + + if (S_ISREG(mode) || !(mode & S_IFMT)) { + int fd = openat_file(dirfd, filename, O_CREAT, mode); + if (fd == -1) { + return fd; + } + close(fd); + return 0; + } if (!pthread_fchdir_np) { error_report_once("pthread_fchdir_np() not available on this version of macOS"); - return -ENOTSUP; + errno = ENOTSUP; + return -1; } if (pthread_fchdir_np(dirfd) < 0) { return -1; } - err = mknod(filename, mode, dev); + if (S_ISSOCK(mode)) { + err = create_socket_file_at_cwd(filename, mode); + } else { + err = mknod(filename, mode, dev); + } preserved_errno = errno; /* Stop using the thread-local cwd */ pthread_fchdir_np(-1); diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 97e681e167..c3526144c9 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -19,6 +19,75 @@ #define O_PATH_9P_UTIL 0 #endif +#if !defined(CONFIG_LINUX) + +/* + * Generates a Linux device number (a.k.a. dev_t) for given device major + * and minor numbers. + * + * To be more precise: it generates a device number in glibc's format + * (MMMM_Mmmm_mmmM_MMmm, 64 bits) actually, which is compatible with + * Linux's format (mmmM_MMmm, 32 bits), as described in <bits/sysmacros.h>. + */ +static inline uint64_t makedev_dotl(uint32_t dev_major, uint32_t dev_minor) +{ + uint64_t dev; + + // from glibc sysmacros.h: + dev = (((uint64_t) (dev_major & 0x00000fffu)) << 8); + dev |= (((uint64_t) (dev_major & 0xfffff000u)) << 32); + dev |= (((uint64_t) (dev_minor & 0x000000ffu)) << 0); + dev |= (((uint64_t) (dev_minor & 0xffffff00u)) << 12); + return dev; +} + +#endif + +/* + * Converts given device number from host's device number format to Linux + * device number format. As both the size of type dev_t and encoding of + * dev_t is system dependant, we have to convert them for Linux guests if + * host is not running Linux. + */ +static inline uint64_t host_dev_to_dotl_dev(dev_t dev) +{ +#ifdef CONFIG_LINUX + return dev; +#else + return makedev_dotl(major(dev), minor(dev)); +#endif +} + +/* Translates errno from host -> Linux if needed */ +static inline int errno_to_dotl(int err) { +#if defined(CONFIG_LINUX) + /* nothing to translate (Linux -> Linux) */ +#elif defined(CONFIG_DARWIN) + /* + * translation mandatory for macOS hosts + * + * FIXME: Only most important errnos translated here yet, this should be + * extended to as many errnos being translated as possible in future. + */ + if (err == ENAMETOOLONG) { + err = 36; /* ==ENAMETOOLONG on Linux */ + } else if (err == ENOTEMPTY) { + err = 39; /* ==ENOTEMPTY on Linux */ + } else if (err == ELOOP) { + err = 40; /* ==ELOOP on Linux */ + } else if (err == ENOATTR) { + err = 61; /* ==ENODATA on Linux */ + } else if (err == ENOTSUP) { + err = 95; /* ==EOPNOTSUPP on Linux */ + } else if (err == EOPNOTSUPP) { + err = 95; /* ==EOPNOTSUPP on Linux */ + } +#else +#error Missing errno translation to Linux for this host system +#endif + return err; +} + #ifdef CONFIG_DARWIN #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0) #define qemu_lgetxattr(...) getxattr(__VA_ARGS__, 0, XATTR_NOFOLLOW) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 225f31fc31..0cd0c14c2a 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1054,6 +1054,8 @@ static void coroutine_fn pdu_complete(V9fsPDU *pdu, ssize_t len) } len += ret; id = P9_RERROR; + } else { + err = errno_to_dotl(err); } ret = pdu_marshal(pdu, len, "d", err); @@ -1327,7 +1329,7 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, v9lstat->st_nlink = stbuf->st_nlink; v9lstat->st_uid = stbuf->st_uid; v9lstat->st_gid = stbuf->st_gid; - v9lstat->st_rdev = stbuf->st_rdev; + v9lstat->st_rdev = host_dev_to_dotl_dev(stbuf->st_rdev); v9lstat->st_size = stbuf->st_size; v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); v9lstat->st_blocks = stbuf->st_blocks; |