xref: /qemu/hw/9pfs/9p-util-darwin.c (revision a976a99a)
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 
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 
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 
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 
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 
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 
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