2 * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2012, John Scipione, jscipione@gmail.com.
4 * Distributed under the terms of the MIT License.
13 #include <sys/param.h>
19 // Private helper functions
20 static int get_path(int fd
, const char* path
, char fullPath
[]);
21 static int eaccess(const char* path
, int accessMode
);
25 get_path(int fd
, const char* path
, char fullPath
[])
28 if (fstat(fd
, &dirst
) < 0) {
29 // failed to grab stat information, fstat() sets errno
33 if (!S_ISDIR(dirst
.st_mode
)) {
34 // fd does not point to a directory
39 if (fcntl(fd
, F_GETPATH
, fullPath
) < 0) {
40 // failed to get the path of fd, fcntl() sets errno
44 if (strlcat(fullPath
, "/", MAXPATHLEN
) > MAXPATHLEN
45 || strlcat(fullPath
, path
, MAXPATHLEN
) > MAXPATHLEN
) {
46 // full path is too long
56 eaccess(const char* path
, int accessMode
)
58 uid_t uid
= geteuid();
62 if (stat(path
, &st
) < 0) {
63 // failed to get stat information on path, stat() sets errno
69 // root has always read/write permission, but at least one of the
70 // X bits must be set for execute permission
71 fileMode
= R_OK
| W_OK
;
72 if ((st
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)) != 0)
74 } else if (st
.st_uid
== uid
) {
76 if ((st
.st_mode
& S_IRUSR
) != 0)
78 if ((st
.st_mode
& S_IWUSR
) != 0)
80 if ((st
.st_mode
& S_IXUSR
) != 0)
82 } else if (st
.st_gid
== getegid()) {
83 // user is in owning group
84 if ((st
.st_mode
& S_IRGRP
) != 0)
86 if ((st
.st_mode
& S_IWGRP
) != 0)
88 if ((st
.st_mode
& S_IXGRP
) != 0)
91 // user is one of the others
92 if ((st
.st_mode
& S_IROTH
) != 0)
94 if ((st
.st_mode
& S_IWOTH
) != 0)
96 if ((st
.st_mode
& S_IXOTH
) != 0)
100 if ((accessMode
& ~fileMode
) != 0) {
110 faccessat(int fd
, const char* path
, int accessMode
, int flag
)
112 if (flag
!= AT_EACCESS
&& flag
!= 0) {
118 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
119 // call access() ignoring fd
120 return (flag
& AT_EACCESS
) != 0 ? eaccess(path
, accessMode
)
121 : access(path
, accessMode
);
125 // Invalid file descriptor
130 char fullPath
[MAXPATHLEN
];
131 if (get_path(fd
, path
, fullPath
) < 0)
134 return (flag
& AT_EACCESS
) != 0 ? eaccess(fullPath
, accessMode
)
135 : access(fullPath
, accessMode
);
140 fchmodat(int fd
, const char* path
, mode_t mode
, int flag
)
142 if ((flag
& AT_SYMLINK_NOFOLLOW
) == 0 && flag
!= 0) {
148 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
149 // call chmod() ignoring fd
150 if ((flag
& AT_SYMLINK_NOFOLLOW
) != 0) {
151 // fake lchmod() with open() and fchmod()
152 int symlinkfd
= open(path
, O_RDONLY
| O_SYMLINK
);
153 int status
= fchmod(symlinkfd
, mode
);
157 return chmod(path
, mode
);
161 // Invalid file descriptor
166 char fullPath
[MAXPATHLEN
];
167 if (get_path(fd
, path
, fullPath
) < 0)
171 if ((flag
& AT_SYMLINK_NOFOLLOW
) != 0) {
172 // fake lchmod() with open() and fchmod()
173 int fullfd
= open(fullPath
, O_RDONLY
| O_SYMLINK
);
174 status
= fchmod(fullfd
, mode
);
177 status
= chmod(fullPath
, mode
);
184 fchownat(int fd
, const char* path
, uid_t owner
, gid_t group
, int flag
)
186 if (flag
!= AT_SYMLINK_NOFOLLOW
&& flag
!= 0) {
192 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
193 // call chown() ignoring fd
194 return (flag
& AT_SYMLINK_NOFOLLOW
) != 0 ? lchown(path
, owner
, group
)
195 : chown(path
, owner
, group
);
199 // Invalid file descriptor
204 char fullPath
[MAXPATHLEN
];
205 if (get_path(fd
, path
, fullPath
) < 0)
208 return (flag
& AT_SYMLINK_NOFOLLOW
) != 0 ? lchown(fullPath
, owner
, group
)
209 : chown(fullPath
, owner
, group
);
217 if (fstat(fd
, &st
)) {
218 // failed to get the stat info for fd, fstat() sets errno
222 if (!S_ISDIR(st
.st_mode
)) {
227 char path
[MAXPATHLEN
];
228 if (fcntl(fd
, F_GETPATH
, path
) < 0) {
229 // failed to get the path of fd, fcntl() sets errno
233 DIR* dir
= opendir(path
);
242 fstatat(int fd
, const char *path
, struct stat
*st
, int flag
)
244 if (flag
!= AT_SYMLINK_NOFOLLOW
&& flag
!= 0) {
250 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
251 // call stat() or lstat() ignoring fd
252 return (flag
& AT_SYMLINK_NOFOLLOW
) != 0 ? lstat(path
, st
)
257 // Invalid file descriptor
262 char fullPath
[MAXPATHLEN
];
263 if (get_path(fd
, path
, fullPath
) < 0)
266 return (flag
& AT_SYMLINK_NOFOLLOW
) != 0 ? lstat(fullPath
, st
)
267 : stat(fullPath
, st
);
272 mkdirat(int fd
, const char *path
, mode_t mode
)
274 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
275 // call mkdir() ignoring fd
276 return mkdir(path
, mode
);
280 // Invalid file descriptor
285 char fullPath
[MAXPATHLEN
];
286 if (get_path(fd
, path
, fullPath
) < 0)
289 return mkdir(fullPath
, mode
);
294 mkfifoat(int fd
, const char *path
, mode_t mode
)
296 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
297 // call mkfifo() ignoring fd
298 return mkfifo(path
, mode
);
302 // Invalid file descriptor
307 char fullPath
[MAXPATHLEN
];
308 if (get_path(fd
, path
, fullPath
) < 0)
311 return mkfifo(fullPath
, mode
);
316 mknodat(int fd
, const char *path
, mode_t mode
, dev_t dev
)
318 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
319 // call mknod() ignoring fd
320 return mknod(path
, mode
, dev
);
324 // Invalid file descriptor
329 char fullPath
[MAXPATHLEN
];
330 if (get_path(fd
, path
, fullPath
) < 0)
333 return mknod(fullPath
, mode
, dev
);
338 renameat(int oldFD
, const char* oldPath
, int newFD
, const char* newPath
)
340 bool ignoreOldFD
= false;
341 bool ignoreNewFD
= false;
343 if (oldFD
== AT_FDCWD
|| (oldPath
!= NULL
&& oldPath
[0] == '/'))
346 if (newFD
== AT_FDCWD
|| (newPath
!= NULL
&& newPath
[0] == '/'))
349 if (ignoreOldFD
&& ignoreNewFD
) {
350 // call rename() ignoring the fd's
351 return rename(oldPath
, newPath
);
354 char oldFullPath
[MAXPATHLEN
];
357 // Invalid file descriptor
362 if (get_path(oldFD
, oldPath
, oldFullPath
) < 0)
366 char newFullPath
[MAXPATHLEN
];
369 // Invalid file descriptor
374 if (get_path(newFD
, newPath
, newFullPath
) < 0)
378 return rename(ignoreOldFD
? oldPath
: oldFullPath
,
379 ignoreNewFD
? newPath
: newFullPath
);
384 readlinkat(int fd
, const char *path
, char *buffer
, size_t bufferSize
)
386 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
387 // call readlink() ignoring fd
388 return readlink(path
, buffer
, bufferSize
);
392 // Invalid file descriptor
397 char fullPath
[MAXPATHLEN
];
398 if (get_path(fd
, path
, fullPath
) < 0)
401 return readlink(fullPath
, buffer
, bufferSize
);
406 symlinkat(const char *oldPath
, int fd
, const char *newPath
)
408 if (fd
== AT_FDCWD
|| (newPath
!= NULL
&& newPath
[0] == '/')) {
409 // call symlink() ignoring fd
410 return symlink(oldPath
, newPath
);
414 // Invalid file descriptor
419 // newPath is relative to the fd
420 char newFullPath
[MAXPATHLEN
];
421 if (get_path(fd
, newPath
, newFullPath
) < 0)
424 return symlink(oldPath
, newFullPath
);
429 unlinkat(int fd
, const char *path
, int flag
)
431 if (flag
!= AT_REMOVEDIR
&& flag
!= 0) {
437 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
438 // call rmdir() or unlink() ignoring fd
439 return (flag
& AT_REMOVEDIR
) != 0 ? rmdir(path
) : unlink(path
);
443 // Invalid file descriptor
448 char fullPath
[MAXPATHLEN
];
449 if (get_path(fd
, path
, fullPath
) < 0)
452 return (flag
& AT_REMOVEDIR
) != 0 ? rmdir(fullPath
)
458 linkat(int oldFD
, const char *oldPath
, int newFD
, const char *newPath
,
461 if ((flag
& AT_SYMLINK_FOLLOW
) != 0) {
462 // Dereference oldPath
463 // CURRENTLY UNSUPPORTED
466 } else if (flag
!= 0) {
471 bool ignoreOldFD
= false;
472 bool ignoreNewFD
= false;
474 if (oldFD
== AT_FDCWD
|| oldPath
!= NULL
&& oldPath
[0] == '/')
477 if (newFD
== AT_FDCWD
|| newPath
!= NULL
&& newPath
[0] == '/')
480 if (ignoreOldFD
&& ignoreNewFD
) {
481 // call link() ignoring the fd's
482 return link(oldPath
, newPath
);
485 char oldFullPath
[MAXPATHLEN
];
488 // Invalid file descriptor
493 if (get_path(oldFD
, oldPath
, oldFullPath
) < 0)
497 char newFullPath
[MAXPATHLEN
];
500 // Invalid file descriptor
505 if (get_path(newFD
, newPath
, newFullPath
) < 0)
509 return link(ignoreOldFD
? oldPath
: oldFullPath
,
510 ignoreNewFD
? newPath
: newFullPath
);
515 futimesat(int fd
, const char *path
, const struct timeval times
[2])
517 if (fd
== AT_FDCWD
|| (path
!= NULL
&& path
[0] == '/')) {
518 // call utimes() ignoring fd
519 return utimes(path
, times
);
523 // Invalid file descriptor
528 char fullPath
[MAXPATHLEN
];
529 if (get_path(fd
, path
, fullPath
) < 0)
532 return utimes(fullPath
, times
);