vfs: check userland buffers before reading them.
[haiku.git] / src / build / libroot / fs_darwin.cpp
blobf4d11b690efb78096fd8297002711f9eed53d3e3
1 /*
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.
5 */
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/param.h>
14 #include <sys/stat.h>
15 #include <sys/time.h>
16 #include <unistd.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);
24 static int
25 get_path(int fd, const char* path, char fullPath[])
27 struct stat dirst;
28 if (fstat(fd, &dirst) < 0) {
29 // failed to grab stat information, fstat() sets errno
30 return -1;
33 if (!S_ISDIR(dirst.st_mode)) {
34 // fd does not point to a directory
35 errno = ENOTDIR;
36 return -1;
39 if (fcntl(fd, F_GETPATH, fullPath) < 0) {
40 // failed to get the path of fd, fcntl() sets errno
41 return -1;
44 if (strlcat(fullPath, "/", MAXPATHLEN) > MAXPATHLEN
45 || strlcat(fullPath, path, MAXPATHLEN) > MAXPATHLEN) {
46 // full path is too long
47 errno = ENAMETOOLONG;
48 return -1;
51 return 0;
55 static int
56 eaccess(const char* path, int accessMode)
58 uid_t uid = geteuid();
59 int fileMode = 0;
61 struct stat st;
62 if (stat(path, &st) < 0) {
63 // failed to get stat information on path, stat() sets errno
64 return -1;
67 if (uid == 0) {
68 // user is root
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)
73 fileMode |= X_OK;
74 } else if (st.st_uid == uid) {
75 // user is node owner
76 if ((st.st_mode & S_IRUSR) != 0)
77 fileMode |= R_OK;
78 if ((st.st_mode & S_IWUSR) != 0)
79 fileMode |= W_OK;
80 if ((st.st_mode & S_IXUSR) != 0)
81 fileMode |= X_OK;
82 } else if (st.st_gid == getegid()) {
83 // user is in owning group
84 if ((st.st_mode & S_IRGRP) != 0)
85 fileMode |= R_OK;
86 if ((st.st_mode & S_IWGRP) != 0)
87 fileMode |= W_OK;
88 if ((st.st_mode & S_IXGRP) != 0)
89 fileMode |= X_OK;
90 } else {
91 // user is one of the others
92 if ((st.st_mode & S_IROTH) != 0)
93 fileMode |= R_OK;
94 if ((st.st_mode & S_IWOTH) != 0)
95 fileMode |= W_OK;
96 if ((st.st_mode & S_IXOTH) != 0)
97 fileMode |= X_OK;
100 if ((accessMode & ~fileMode) != 0) {
101 errno = EACCES;
102 return -1;
105 return 0;
110 faccessat(int fd, const char* path, int accessMode, int flag)
112 if (flag != AT_EACCESS && flag != 0) {
113 // invalid flag
114 errno = EINVAL;
115 return -1;
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);
124 if (fd < 0) {
125 // Invalid file descriptor
126 errno = EBADF;
127 return -1;
130 char fullPath[MAXPATHLEN];
131 if (get_path(fd, path, fullPath) < 0)
132 return -1;
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) {
143 // invalid flag
144 errno = EINVAL;
145 return -1;
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);
154 close(symlinkfd);
155 return status;
156 } else
157 return chmod(path, mode);
160 if (fd < 0) {
161 // Invalid file descriptor
162 errno = EBADF;
163 return -1;
166 char fullPath[MAXPATHLEN];
167 if (get_path(fd, path, fullPath) < 0)
168 return -1;
170 int status;
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);
175 close(fullfd);
176 } else
177 status = chmod(fullPath, mode);
179 return status;
184 fchownat(int fd, const char* path, uid_t owner, gid_t group, int flag)
186 if (flag != AT_SYMLINK_NOFOLLOW && flag != 0) {
187 // invalid flag
188 errno = EINVAL;
189 return -1;
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);
198 if (fd < 0) {
199 // Invalid file descriptor
200 errno = EBADF;
201 return -1;
204 char fullPath[MAXPATHLEN];
205 if (get_path(fd, path, fullPath) < 0)
206 return -1;
208 return (flag & AT_SYMLINK_NOFOLLOW) != 0 ? lchown(fullPath, owner, group)
209 : chown(fullPath, owner, group);
213 DIR*
214 fdopendir(int fd)
216 struct stat st;
217 if (fstat(fd, &st)) {
218 // failed to get the stat info for fd, fstat() sets errno
219 return NULL;
222 if (!S_ISDIR(st.st_mode)) {
223 errno = ENOTDIR;
224 return NULL;
227 char path[MAXPATHLEN];
228 if (fcntl(fd, F_GETPATH, path) < 0) {
229 // failed to get the path of fd, fcntl() sets errno
230 return NULL;
233 DIR* dir = opendir(path);
234 if (dir != NULL)
235 close(fd);
237 return dir;
242 fstatat(int fd, const char *path, struct stat *st, int flag)
244 if (flag != AT_SYMLINK_NOFOLLOW && flag != 0) {
245 // invalid flag
246 errno = EINVAL;
247 return -1;
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)
253 : stat(path, st);
256 if (fd < 0) {
257 // Invalid file descriptor
258 errno = EBADF;
259 return -1;
262 char fullPath[MAXPATHLEN];
263 if (get_path(fd, path, fullPath) < 0)
264 return -1;
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);
279 if (fd < 0) {
280 // Invalid file descriptor
281 errno = EBADF;
282 return -1;
285 char fullPath[MAXPATHLEN];
286 if (get_path(fd, path, fullPath) < 0)
287 return -1;
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);
301 if (fd < 0) {
302 // Invalid file descriptor
303 errno = EBADF;
304 return -1;
307 char fullPath[MAXPATHLEN];
308 if (get_path(fd, path, fullPath) < 0)
309 return -1;
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);
323 if (fd < 0) {
324 // Invalid file descriptor
325 errno = EBADF;
326 return -1;
329 char fullPath[MAXPATHLEN];
330 if (get_path(fd, path, fullPath) < 0)
331 return -1;
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] == '/'))
344 ignoreOldFD = true;
346 if (newFD == AT_FDCWD || (newPath != NULL && newPath[0] == '/'))
347 ignoreNewFD = true;
349 if (ignoreOldFD && ignoreNewFD) {
350 // call rename() ignoring the fd's
351 return rename(oldPath, newPath);
354 char oldFullPath[MAXPATHLEN];
355 if (!ignoreOldFD) {
356 if (oldFD < 0) {
357 // Invalid file descriptor
358 errno = EBADF;
359 return -1;
362 if (get_path(oldFD, oldPath, oldFullPath) < 0)
363 return -1;
366 char newFullPath[MAXPATHLEN];
367 if (!ignoreNewFD) {
368 if (newFD < 0) {
369 // Invalid file descriptor
370 errno = EBADF;
371 return -1;
374 if (get_path(newFD, newPath, newFullPath) < 0)
375 return -1;
378 return rename(ignoreOldFD ? oldPath : oldFullPath,
379 ignoreNewFD ? newPath : newFullPath);
383 ssize_t
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);
391 if (fd < 0) {
392 // Invalid file descriptor
393 errno = EBADF;
394 return -1;
397 char fullPath[MAXPATHLEN];
398 if (get_path(fd, path, fullPath) < 0)
399 return -1;
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);
413 if (fd < 0) {
414 // Invalid file descriptor
415 errno = EBADF;
416 return -1;
419 // newPath is relative to the fd
420 char newFullPath[MAXPATHLEN];
421 if (get_path(fd, newPath, newFullPath) < 0)
422 return -1;
424 return symlink(oldPath, newFullPath);
429 unlinkat(int fd, const char *path, int flag)
431 if (flag != AT_REMOVEDIR && flag != 0) {
432 // invalid flag
433 errno = EINVAL;
434 return -1;
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);
442 if (fd < 0) {
443 // Invalid file descriptor
444 errno = EBADF;
445 return -1;
448 char fullPath[MAXPATHLEN];
449 if (get_path(fd, path, fullPath) < 0)
450 return -1;
452 return (flag & AT_REMOVEDIR) != 0 ? rmdir(fullPath)
453 : unlink(fullPath);
458 linkat(int oldFD, const char *oldPath, int newFD, const char *newPath,
459 int flag)
461 if ((flag & AT_SYMLINK_FOLLOW) != 0) {
462 // Dereference oldPath
463 // CURRENTLY UNSUPPORTED
464 errno = ENOTSUP;
465 return -1;
466 } else if (flag != 0) {
467 errno = EINVAL;
468 return -1;
471 bool ignoreOldFD = false;
472 bool ignoreNewFD = false;
474 if (oldFD == AT_FDCWD || oldPath != NULL && oldPath[0] == '/')
475 ignoreOldFD = true;
477 if (newFD == AT_FDCWD || newPath != NULL && newPath[0] == '/')
478 ignoreNewFD = true;
480 if (ignoreOldFD && ignoreNewFD) {
481 // call link() ignoring the fd's
482 return link(oldPath, newPath);
485 char oldFullPath[MAXPATHLEN];
486 if (!ignoreOldFD) {
487 if (oldFD < 0) {
488 // Invalid file descriptor
489 errno = EBADF;
490 return -1;
493 if (get_path(oldFD, oldPath, oldFullPath) < 0)
494 return -1;
497 char newFullPath[MAXPATHLEN];
498 if (!ignoreNewFD) {
499 if (newFD < 0) {
500 // Invalid file descriptor
501 errno = EBADF;
502 return -1;
505 if (get_path(newFD, newPath, newFullPath) < 0)
506 return -1;
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);
522 if (fd < 0) {
523 // Invalid file descriptor
524 errno = EBADF;
525 return -1;
528 char fullPath[MAXPATHLEN];
529 if (get_path(fd, path, fullPath) < 0)
530 return -1;
532 return utimes(fullPath, times);