1 /*
2 * 9p utilities (Darwin Implementation)
3 *
4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8 #include "qemu/osdep.h"
9 #include "qemu/xattr.h"
10 #include "qapi/error.h"
11 #include "qemu/error-report.h"
12 #include "9p-util.h"
13
fgetxattrat_nofollow(int dirfd,const char * filename,const char * name,void * value,size_t size)14 ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
15 void *value, size_t size)
16 {
17 int ret;
18 int fd = openat_file(dirfd, filename,
19 O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
20 if (fd == -1) {
21 return -1;
22 }
23 ret = fgetxattr(fd, name, value, size, 0, 0);
24 close_preserve_errno(fd);
25 return ret;
26 }
27
flistxattrat_nofollow(int dirfd,const char * filename,char * list,size_t size)28 ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
29 char *list, size_t size)
30 {
31 int ret;
32 int fd = openat_file(dirfd, filename,
33 O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
34 if (fd == -1) {
35 return -1;
36 }
37 ret = flistxattr(fd, list, size, 0);
38 close_preserve_errno(fd);
39 return ret;
40 }
41
fremovexattrat_nofollow(int dirfd,const char * filename,const char * name)42 ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
43 const char *name)
44 {
45 int ret;
46 int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
47 if (fd == -1) {
48 return -1;
49 }
50 ret = fremovexattr(fd, name, 0);
51 close_preserve_errno(fd);
52 return ret;
53 }
54
fsetxattrat_nofollow(int dirfd,const char * filename,const char * name,void * value,size_t size,int flags)55 int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
56 void *value, size_t size, int flags)
57 {
58 int ret;
59 int fd = openat_file(dirfd, filename, O_PATH_9P_UTIL | O_NOFOLLOW, 0);
60 if (fd == -1) {
61 return -1;
62 }
63 ret = fsetxattr(fd, name, value, size, 0, flags);
64 close_preserve_errno(fd);
65 return ret;
66 }
67
68 /*
69 * As long as mknodat is not available on macOS, this workaround
70 * using pthread_fchdir_np is needed.
71 *
72 * Radar filed with Apple for implementing mknodat:
73 * rdar://FB9862426 (https://openradar.appspot.com/FB9862426)
74 */
75 #if defined CONFIG_PTHREAD_FCHDIR_NP
76
create_socket_file_at_cwd(const char * filename,mode_t mode)77 static int create_socket_file_at_cwd(const char *filename, mode_t mode) {
78 int fd, err;
79 struct sockaddr_un addr = {
80 .sun_family = AF_UNIX
81 };
82
83 err = snprintf(addr.sun_path, sizeof(addr.sun_path), "./%s", filename);
84 if (err < 0 || err >= sizeof(addr.sun_path)) {
85 errno = ENAMETOOLONG;
86 return -1;
87 }
88 fd = socket(PF_UNIX, SOCK_DGRAM, 0);
89 if (fd == -1) {
90 return fd;
91 }
92 err = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
93 if (err == -1) {
94 goto out;
95 }
96 /*
97 * FIXME: Should rather be using descriptor-based fchmod() on the
98 * socket file descriptor above (preferably before bind() call),
99 * instead of path-based fchmodat(), to prevent concurrent transient
100 * state issues between creating the named FIFO file at bind() and
101 * delayed adjustment of permissions at fchmodat(). However currently
102 * macOS (12.x) does not support such operations on socket file
103 * descriptors yet.
104 *
105 * Filed report with Apple: FB9997731
106 */
107 err = fchmodat(AT_FDCWD, filename, mode, AT_SYMLINK_NOFOLLOW);
108 out:
109 close_preserve_errno(fd);
110 return err;
111 }
112
qemu_mknodat(int dirfd,const char * filename,mode_t mode,dev_t dev)113 int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev)
114 {
115 int preserved_errno, err;
116
117 if (S_ISREG(mode) || !(mode & S_IFMT)) {
118 int fd = openat_file(dirfd, filename, O_CREAT, mode);
119 if (fd == -1) {
120 return fd;
121 }
122 close(fd);
123 return 0;
124 }
125 if (!pthread_fchdir_np) {
126 error_report_once("pthread_fchdir_np() not available on this version of macOS");
127 errno = ENOTSUP;
128 return -1;
129 }
130 if (pthread_fchdir_np(dirfd) < 0) {
131 return -1;
132 }
133 if (S_ISSOCK(mode)) {
134 err = create_socket_file_at_cwd(filename, mode);
135 } else {
136 err = mknod(filename, mode, dev);
137 }
138 preserved_errno = errno;
139 /* Stop using the thread-local cwd */
140 pthread_fchdir_np(-1);
141 if (err < 0) {
142 errno = preserved_errno;
143 }
144 return err;
145 }
146
147 #endif
148