Ticket #54638: atcalls.c

File atcalls.c, 8.5 KB (added by jeremyhu (Jeremy Huddleston Sequoia), 5 years ago)

atcalls.c

Line 
1/*
2 * atcalls.c
3 *
4 * Copyright (c) 2013-2015 Apple Inc. All rights reserved.
5 *
6 * @APPLE_LICENSE_HEADER_START@
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26#include <sys/attr.h>
27#include <sys/errno.h>
28#include <sys/fcntl.h>
29#include <sys/param.h>
30#include <sys/stat.h>
31#include <sys/mount.h>
32#include <sys/ucred.h>
33#include <sys/shm.h>
34
35#include <assert.h>
36#include <dlfcn.h>
37#include <stdarg.h>
38#include <stdbool.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <unistd.h>
43#include <dirent.h>
44
45extern int __pthread_fchdir(int fd);
46
47#define PROTECT_ERRNO(what)  ({ int __err = (errno); what; errno = __err; })
48#define ERR_ON(code, what)   if (what) { errno = (code); return -1; }
49
50#define _ATCALL(fd, p, onerr, what)                             \
51    ({  typeof(what) __result;                                  \
52        int oldCWD = -1;                                        \
53        if (fd != AT_FDCWD && p[0] != '/') {                    \
54            oldCWD = open(".", O_RDONLY);                       \
55            if (__pthread_fchdir(-1) < 0 && oldCWD != -1) {     \
56                close(oldCWD); oldCWD = -1;                     \
57            }                                                   \
58            if (__pthread_fchdir(fd) < 0) {                     \
59                PROTECT_ERRNO(__pthread_fchdir(oldCWD));        \
60                if (oldCWD != -1) PROTECT_ERRNO(close(oldCWD)); \
61                return onerr;                                   \
62            }                                                   \
63        }                                                       \
64        __result = (what);                                      \
65        if (fd != AT_FDCWD && p[0] != '/') {                    \
66            PROTECT_ERRNO(__pthread_fchdir(oldCWD));            \
67            if (oldCWD != -1) PROTECT_ERRNO(close(oldCWD));     \
68        }                                                       \
69        __result;                                               \
70    })
71
72#define ATCALL(fd, p, what)  _ATCALL(fd, p, -1, what)
73
74// buf is a pointer to a buffer of PATH_MAX or larger size
75static int
76_fullpathat(int dirfd, const char *relative, char *buf)
77{
78    int cwd = -1;
79    if (dirfd == AT_FDCWD) {
80        cwd = open(".", O_RDONLY);
81        if (cwd == -1)
82            return -1;
83
84        dirfd = cwd;
85    }
86
87    int ret;
88
89    ret = fcntl(dirfd, F_GETPATH, buf);
90    if (ret == -1)
91        goto fail;
92
93    strlcat(buf, "/", PATH_MAX);
94    strlcat(buf, relative, PATH_MAX);
95
96fail:
97    if (cwd != -1)
98        close(cwd);
99    return ret;
100}
101
102int faccessat(int dirfd, const char *pathname, int mode, int flags)
103{
104    struct attrlist al = {
105        .bitmapcount = ATTR_BIT_MAP_COUNT,
106        .commonattr  = ATTR_CMN_USERACCESS,
107    };
108    struct {
109        uint32_t length;
110        uint32_t access;
111    } __attribute__((aligned(4), packed)) ab;
112    unsigned long opts = 0;
113
114    ERR_ON(EINVAL, flags & ~AT_SYMLINK_NOFOLLOW);
115    if (flags & AT_SYMLINK_NOFOLLOW)
116        opts |= FSOPT_NOFOLLOW;
117    if (getattrlistat(dirfd, pathname, &al, &ab, sizeof(ab), opts) < 0)
118        return -1;
119    ERR_ON(EACCES, (ab.access & mode) != (uint32_t)mode);
120    return 0;
121}
122
123#if 0
124int fchflagsat(int dirfd, const char *path, int flags, int at_flags)
125{
126    ERR_ON(EINVAL, at_flags & ~AT_SYMLINK_NOFOLLOW);
127    if (at_flags & AT_SYMLINK_NOFOLLOW) {
128        return ATCALL(dirfd, path, lchflags(path, flags));
129    } else {
130        return ATCALL(dirfd, path, chflags(path, flags));
131    }
132}
133#endif
134
135int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags)
136{
137    ERR_ON(EINVAL, flags & ~AT_SYMLINK_NOFOLLOW);
138    if (flags & AT_SYMLINK_NOFOLLOW) {
139        return ATCALL(dirfd, pathname, lchmod(pathname, mode));
140    } else {
141        return ATCALL(dirfd, pathname, chmod(pathname, mode));
142    }
143}
144
145int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags)
146{
147    ERR_ON(EINVAL, flags & ~AT_SYMLINK_NOFOLLOW);
148    if (flags & AT_SYMLINK_NOFOLLOW) {
149        return ATCALL(dirfd, pathname, lchown(pathname, owner, group));
150    } else {
151        return ATCALL(dirfd, pathname, chown(pathname, owner, group));
152    }
153}
154
155int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags)
156{
157    ERR_ON(EINVAL, flags & ~AT_SYMLINK_NOFOLLOW);
158    if (flags & AT_SYMLINK_NOFOLLOW) {
159        return ATCALL(dirfd, pathname, lstat(pathname, buf));
160    } else {
161        return ATCALL(dirfd, pathname, stat(pathname, buf));
162    }
163}
164
165int getattrlistat(int dirfd, const char *pathname, struct attrlist *a,
166                                 void *buf, size_t size, unsigned long flags)
167{
168#ifdef __LP64__
169    /* This is fricken stupid */
170    unsigned int _flags = (unsigned int)flags;
171    assert ((unsigned long)_flags == flags);
172
173    return ATCALL(dirfd, pathname, getattrlist(pathname, a, buf, size, _flags));
174#else
175    return ATCALL(dirfd, pathname, getattrlist(pathname, a, buf, size, flags));
176#endif
177}
178
179int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags)
180{
181    ERR_ON(EINVAL, flags);
182    if (oldpath[0] == '/') {
183        return ATCALL(newdirfd, newpath, link(oldpath, newpath));
184    }
185    if (newpath[0] == '/' || olddirfd == newdirfd) {
186        return ATCALL(olddirfd, oldpath, link(oldpath, newpath));
187    }
188
189    // olddirfd != newdirfd and both relative
190    int ret;
191
192    char _oldpath[PATH_MAX];
193    ret = _fullpathat(olddirfd, oldpath, _oldpath);
194    if (ret == -1)
195        return ret;
196
197    char _newpath[PATH_MAX];
198    ret = _fullpathat(newdirfd, newpath, _newpath);
199    if (ret == -1)
200        return ret;
201
202    return link(_oldpath, _newpath);
203}
204
205int mkdirat(int dirfd, const char *pathname, mode_t mode)
206{
207    return ATCALL(dirfd, pathname, mkdir(pathname, mode));
208}
209
210#if 0
211int mkfifoat(int dirfd, const char *pathname, mode_t mode)
212{
213    return ATCALL(dirfd, pathname, mkfifo(pathname, mode));
214}
215
216int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev)
217{
218    return ATCALL(dirfd, pathname, mknod(pathname, mode, dev));
219}
220#endif
221
222int openat(int dirfd, const char *pathname, int flags, ...)
223{
224    mode_t mode = 0;
225    if (flags & O_CREAT) {
226        va_list ap;
227        va_start(ap, flags);
228        mode = (mode_t)va_arg(ap, int);
229        va_end(ap);
230    }
231
232    return ATCALL(dirfd, pathname, open(pathname, flags, mode));
233}
234
235int openat$NOCANCEL(int dirfd, const char *pathname, int flags, ...)
236{
237    mode_t mode = 0;
238    if (flags & O_CREAT) {
239        va_list ap;
240        va_start(ap, flags);
241        mode = (mode_t)va_arg(ap, int);
242        va_end(ap);
243    }
244
245    return ATCALL(dirfd, pathname, open(pathname, flags, mode));
246}
247
248ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz)
249{
250    return ATCALL(dirfd, pathname, readlink(pathname, buf, bufsiz));
251}
252
253int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath)
254{
255    if (oldpath[0] == '/') {
256        return ATCALL(newdirfd, newpath, rename(oldpath, newpath));
257    }
258    if (newpath[0] == '/' || olddirfd == newdirfd) {
259        return ATCALL(olddirfd, oldpath, rename(oldpath, newpath));
260    }
261
262    // olddirfd != newdirfd and both relative
263    int ret;
264
265    char _oldpath[PATH_MAX];
266    ret = _fullpathat(olddirfd, oldpath, _oldpath);
267    if (ret == -1)
268        return ret;
269
270    char _newpath[PATH_MAX];
271    ret = _fullpathat(newdirfd, newpath, _newpath);
272    if (ret == -1)
273        return ret;
274
275    return rename(_oldpath, _newpath);
276}
277
278int symlinkat(const char *oldpath, int newdirfd, const char *newpath)
279{
280    return ATCALL(newdirfd, newpath, symlink(oldpath, newpath));
281}
282
283int unlinkat(int dirfd, const char *pathname, int flags)
284{
285    ERR_ON(EINVAL, flags & ~AT_REMOVEDIR);
286    if (flags & AT_REMOVEDIR) {
287        return ATCALL(dirfd, pathname, rmdir(pathname));
288    } else {
289        return ATCALL(dirfd, pathname, unlink(pathname));
290    }
291}