aboutsummaryrefslogtreecommitdiff
path: root/hw/9pfs/9p-util-darwin.c
blob: 95146e73546a99431151abf8faec4944f483258c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * 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