xref: /haiku/src/build/libroot/fs_darwin.cpp (revision fdc59e4e7e24bf7a54d086faeabed505c8334604)
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  */
6 
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>
17 
18 
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);
22 
23 
24 static int
get_path(int fd,const char * path,char fullPath[])25 get_path(int fd, const char* path, char fullPath[])
26 {
27 	struct stat dirst;
28 	if (fstat(fd, &dirst) < 0) {
29 		// failed to grab stat information, fstat() sets errno
30 		return -1;
31 	}
32 
33 	if (!S_ISDIR(dirst.st_mode)) {
34 		// fd does not point to a directory
35 		errno = ENOTDIR;
36 		return -1;
37 	}
38 
39 	if (fcntl(fd, F_GETPATH, fullPath) < 0) {
40 		// failed to get the path of fd, fcntl() sets errno
41 		return -1;
42 	}
43 
44 	if (strlcat(fullPath, "/", MAXPATHLEN) > MAXPATHLEN
45 		|| strlcat(fullPath, path, MAXPATHLEN) > MAXPATHLEN) {
46 		// full path is too long
47 		errno = ENAMETOOLONG;
48 		return -1;
49 	}
50 
51 	return 0;
52 }
53 
54 
55 static int
eaccess(const char * path,int accessMode)56 eaccess(const char* path, int accessMode)
57 {
58 	uid_t uid = geteuid();
59 	int fileMode = 0;
60 
61 	struct stat st;
62 	if (stat(path, &st) < 0) {
63 		// failed to get stat information on path, stat() sets errno
64 		return -1;
65 	}
66 
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;
98 	}
99 
100 	if ((accessMode & ~fileMode) != 0) {
101 		errno = EACCES;
102 		return -1;
103 	}
104 
105 	return 0;
106 }
107 
108 
109 int
faccessat(int fd,const char * path,int accessMode,int flag)110 faccessat(int fd, const char* path, int accessMode, int flag)
111 {
112 	if (flag != AT_EACCESS && flag != 0) {
113 		// invalid flag
114 		errno = EINVAL;
115 		return -1;
116 	}
117 
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);
122 	}
123 
124 	if (fd < 0) {
125 		// Invalid file descriptor
126 		errno = EBADF;
127 		return -1;
128 	}
129 
130 	char fullPath[MAXPATHLEN];
131 	if (get_path(fd, path, fullPath) < 0)
132 		return -1;
133 
134 	return (flag & AT_EACCESS) != 0 ? eaccess(fullPath, accessMode)
135 		: access(fullPath, accessMode);
136 }
137 
138 
139 int
fchmodat(int fd,const char * path,mode_t mode,int flag)140 fchmodat(int fd, const char* path, mode_t mode, int flag)
141 {
142 	if ((flag & AT_SYMLINK_NOFOLLOW) == 0 && flag != 0) {
143 		// invalid flag
144 		errno = EINVAL;
145 		return -1;
146 	}
147 
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);
158 	}
159 
160 	if (fd < 0) {
161 		// Invalid file descriptor
162 		errno = EBADF;
163 		return -1;
164 	}
165 
166 	char fullPath[MAXPATHLEN];
167 	if (get_path(fd, path, fullPath) < 0)
168 		return -1;
169 
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);
178 
179 	return status;
180 }
181 
182 
183 int
fchownat(int fd,const char * path,uid_t owner,gid_t group,int flag)184 fchownat(int fd, const char* path, uid_t owner, gid_t group, int flag)
185 {
186 	if (flag != AT_SYMLINK_NOFOLLOW && flag != 0) {
187 		// invalid flag
188 		errno = EINVAL;
189 		return -1;
190 	}
191 
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);
196 	}
197 
198 	if (fd < 0) {
199 		// Invalid file descriptor
200 		errno = EBADF;
201 		return -1;
202 	}
203 
204 	char fullPath[MAXPATHLEN];
205 	if (get_path(fd, path, fullPath) < 0)
206 		return -1;
207 
208 	return (flag & AT_SYMLINK_NOFOLLOW) != 0 ? lchown(fullPath, owner, group)
209 		: chown(fullPath, owner, group);
210 }
211 
212 
213 DIR*
fdopendir(int fd)214 fdopendir(int fd)
215 {
216 	struct stat st;
217 	if (fstat(fd, &st)) {
218 		// failed to get the stat info for fd, fstat() sets errno
219 		return NULL;
220 	}
221 
222 	if (!S_ISDIR(st.st_mode)) {
223 		errno = ENOTDIR;
224 		return NULL;
225 	}
226 
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;
231 	}
232 
233 	DIR* dir = opendir(path);
234 	if (dir != NULL)
235 		close(fd);
236 
237 	return dir;
238 }
239 
240 
241 int
fstatat(int fd,const char * path,struct stat * st,int flag)242 fstatat(int fd, const char *path, struct stat *st, int flag)
243 {
244 	if (flag != AT_SYMLINK_NOFOLLOW && flag != 0) {
245 		// invalid flag
246 		errno = EINVAL;
247 		return -1;
248 	}
249 
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);
254 	}
255 
256 	if (fd < 0) {
257 		// Invalid file descriptor
258 		errno = EBADF;
259 		return -1;
260 	}
261 
262 	char fullPath[MAXPATHLEN];
263 	if (get_path(fd, path, fullPath) < 0)
264 		return -1;
265 
266 	return (flag & AT_SYMLINK_NOFOLLOW) != 0 ? lstat(fullPath, st)
267 		: stat(fullPath, st);
268 }
269 
270 
271 int
mkdirat(int fd,const char * path,mode_t mode)272 mkdirat(int fd, const char *path, mode_t mode)
273 {
274 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
275 		// call mkdir() ignoring fd
276 		return mkdir(path, mode);
277 	}
278 
279 	if (fd < 0) {
280 		// Invalid file descriptor
281 		errno = EBADF;
282 		return -1;
283 	}
284 
285 	char fullPath[MAXPATHLEN];
286 	if (get_path(fd, path, fullPath) < 0)
287 		return -1;
288 
289 	return mkdir(fullPath, mode);
290 }
291 
292 
293 int
mkfifoat(int fd,const char * path,mode_t mode)294 mkfifoat(int fd, const char *path, mode_t mode)
295 {
296 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
297 		// call mkfifo() ignoring fd
298 		return mkfifo(path, mode);
299 	}
300 
301 	if (fd < 0) {
302 		// Invalid file descriptor
303 		errno = EBADF;
304 		return -1;
305 	}
306 
307 	char fullPath[MAXPATHLEN];
308 	if (get_path(fd, path, fullPath) < 0)
309 		return -1;
310 
311 	return mkfifo(fullPath, mode);
312 }
313 
314 
315 int
mknodat(int fd,const char * path,mode_t mode,dev_t dev)316 mknodat(int fd, const char *path, mode_t mode, dev_t dev)
317 {
318 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
319 		// call mknod() ignoring fd
320 		return mknod(path, mode, dev);
321 	}
322 
323 	if (fd < 0) {
324 		// Invalid file descriptor
325 		errno = EBADF;
326 		return -1;
327 	}
328 
329 	char fullPath[MAXPATHLEN];
330 	if (get_path(fd, path, fullPath) < 0)
331 		return -1;
332 
333 	return mknod(fullPath, mode, dev);
334 }
335 
336 
337 int
renameat(int oldFD,const char * oldPath,int newFD,const char * newPath)338 renameat(int oldFD, const char* oldPath, int newFD, const char* newPath)
339 {
340 	bool ignoreOldFD = false;
341 	bool ignoreNewFD = false;
342 
343 	if (oldFD == AT_FDCWD || (oldPath != NULL && oldPath[0] == '/'))
344 		ignoreOldFD = true;
345 
346 	if (newFD == AT_FDCWD || (newPath != NULL && newPath[0] == '/'))
347 		ignoreNewFD = true;
348 
349 	if (ignoreOldFD && ignoreNewFD) {
350 		// call rename() ignoring the fd's
351 		return rename(oldPath, newPath);
352 	}
353 
354 	char oldFullPath[MAXPATHLEN];
355 	if (!ignoreOldFD) {
356 		if (oldFD < 0) {
357 			// Invalid file descriptor
358 			errno = EBADF;
359 			return -1;
360 		}
361 
362 		if (get_path(oldFD, oldPath, oldFullPath) < 0)
363 			return -1;
364 	}
365 
366 	char newFullPath[MAXPATHLEN];
367 	if (!ignoreNewFD) {
368 		if (newFD < 0) {
369 			// Invalid file descriptor
370 			errno = EBADF;
371 			return -1;
372 		}
373 
374 		if (get_path(newFD, newPath, newFullPath) < 0)
375 			return -1;
376 	}
377 
378 	return rename(ignoreOldFD ? oldPath : oldFullPath,
379 		ignoreNewFD ? newPath : newFullPath);
380 }
381 
382 
383 ssize_t
readlinkat(int fd,const char * path,char * buffer,size_t bufferSize)384 readlinkat(int fd, const char *path, char *buffer, size_t bufferSize)
385 {
386 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
387 		// call readlink() ignoring fd
388 		return readlink(path, buffer, bufferSize);
389 	}
390 
391 	if (fd < 0) {
392 		// Invalid file descriptor
393 		errno = EBADF;
394 		return -1;
395 	}
396 
397 	char fullPath[MAXPATHLEN];
398 	if (get_path(fd, path, fullPath) < 0)
399 		return -1;
400 
401 	return readlink(fullPath, buffer, bufferSize);
402 }
403 
404 
405 int
symlinkat(const char * oldPath,int fd,const char * newPath)406 symlinkat(const char *oldPath, int fd, const char *newPath)
407 {
408 	if (fd == AT_FDCWD || (newPath != NULL && newPath[0] == '/')) {
409 		// call symlink() ignoring fd
410 		return symlink(oldPath, newPath);
411 	}
412 
413 	if (fd < 0) {
414 		// Invalid file descriptor
415 		errno = EBADF;
416 		return -1;
417 	}
418 
419 	// newPath is relative to the fd
420 	char newFullPath[MAXPATHLEN];
421 	if (get_path(fd, newPath, newFullPath) < 0)
422 		return -1;
423 
424 	return symlink(oldPath, newFullPath);
425 }
426 
427 
428 int
unlinkat(int fd,const char * path,int flag)429 unlinkat(int fd, const char *path, int flag)
430 {
431 	if (flag != AT_REMOVEDIR && flag != 0) {
432 		// invalid flag
433 		errno = EINVAL;
434 		return -1;
435 	}
436 
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);
440 	}
441 
442 	if (fd < 0) {
443 		// Invalid file descriptor
444 		errno = EBADF;
445 		return -1;
446 	}
447 
448 	char fullPath[MAXPATHLEN];
449 	if (get_path(fd, path, fullPath) < 0)
450 		return -1;
451 
452 	return (flag & AT_REMOVEDIR) != 0 ? rmdir(fullPath)
453 		: unlink(fullPath);
454 }
455 
456 
457 int
linkat(int oldFD,const char * oldPath,int newFD,const char * newPath,int flag)458 linkat(int oldFD, const char *oldPath, int newFD, const char *newPath,
459 	   int flag)
460 {
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;
469 	}
470 
471 	bool ignoreOldFD = false;
472 	bool ignoreNewFD = false;
473 
474 	if (oldFD == AT_FDCWD || (oldPath != NULL && oldPath[0] == '/'))
475 		ignoreOldFD = true;
476 
477 	if (newFD == AT_FDCWD || (newPath != NULL && newPath[0] == '/'))
478 		ignoreNewFD = true;
479 
480 	if (ignoreOldFD && ignoreNewFD) {
481 		// call link() ignoring the fd's
482 		return link(oldPath, newPath);
483 	}
484 
485 	char oldFullPath[MAXPATHLEN];
486 	if (!ignoreOldFD) {
487 		if (oldFD < 0) {
488 			// Invalid file descriptor
489 			errno = EBADF;
490 			return -1;
491 		}
492 
493 		if (get_path(oldFD, oldPath, oldFullPath) < 0)
494 			return -1;
495 	}
496 
497 	char newFullPath[MAXPATHLEN];
498 	if (!ignoreNewFD) {
499 		if (newFD < 0) {
500 			// Invalid file descriptor
501 			errno = EBADF;
502 			return -1;
503 		}
504 
505 		if (get_path(newFD, newPath, newFullPath) < 0)
506 			return -1;
507 	}
508 
509 	return link(ignoreOldFD ? oldPath : oldFullPath,
510 		ignoreNewFD ? newPath : newFullPath);
511 }
512 
513 
514 int
futimesat(int fd,const char * path,const struct timeval times[2])515 futimesat(int fd, const char *path, const struct timeval times[2])
516 {
517 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
518 		// call utimes() ignoring fd
519 		return utimes(path, times);
520 	}
521 
522 	if (fd < 0) {
523 		// Invalid file descriptor
524 		errno = EBADF;
525 		return -1;
526 	}
527 
528 	char fullPath[MAXPATHLEN];
529 	if (get_path(fd, path, fullPath) < 0)
530 		return -1;
531 
532 	return utimes(fullPath, times);
533 }
534