| /* |
| * 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 "qapi/error.h" |
| #include "qemu/error-report.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; |
| } |
| |
| /* |
| * As long as mknodat is not available on macOS, this workaround |
| * using pthread_fchdir_np is needed. |
| * |
| * Radar filed with Apple for implementing mknodat: |
| * rdar://FB9862426 (https://openradar.appspot.com/FB9862426) |
| */ |
| #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"); |
| errno = ENOTSUP; |
| return -1; |
| } |
| if (pthread_fchdir_np(dirfd) < 0) { |
| return -1; |
| } |
| 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); |
| if (err < 0) { |
| errno = preserved_errno; |
| } |
| return err; |
| } |
| |
| #endif |