xref: /haiku/src/build/libroot/fs_darwin.cpp (revision 776c58b2b56d8bcf33638a2ecb6c697f95a1cbf3)
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
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
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
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
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
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*
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 	return opendir(path);
234 }
235 
236 
237 int
238 fstatat(int fd, const char *path, struct stat *st, int flag)
239 {
240 	if (flag != AT_SYMLINK_NOFOLLOW && flag != 0) {
241 		// invalid flag
242 		errno = EINVAL;
243 		return -1;
244 	}
245 
246 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
247 		// call stat() or lstat() ignoring fd
248 		return (flag & AT_SYMLINK_NOFOLLOW) != 0 ? lstat(path, st)
249 			: stat(path, st);
250 	}
251 
252 	if (fd < 0) {
253 		// Invalid file descriptor
254 		errno = EBADF;
255 		return -1;
256 	}
257 
258 	char fullPath[MAXPATHLEN];
259 	if (get_path(fd, path, fullPath) < 0)
260 		return -1;
261 
262 	return (flag & AT_SYMLINK_NOFOLLOW) != 0 ? lstat(fullPath, st)
263 		: stat(fullPath, st);
264 }
265 
266 
267 int
268 mkdirat(int fd, const char *path, mode_t mode)
269 {
270 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
271 		// call mkdir() ignoring fd
272 		return mkdir(path, mode);
273 	}
274 
275 	if (fd < 0) {
276 		// Invalid file descriptor
277 		errno = EBADF;
278 		return -1;
279 	}
280 
281 	char fullPath[MAXPATHLEN];
282 	if (get_path(fd, path, fullPath) < 0)
283 		return -1;
284 
285 	return mkdir(fullPath, mode);
286 }
287 
288 
289 int
290 mkfifoat(int fd, const char *path, mode_t mode)
291 {
292 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
293 		// call mkfifo() ignoring fd
294 		return mkfifo(path, mode);
295 	}
296 
297 	if (fd < 0) {
298 		// Invalid file descriptor
299 		errno = EBADF;
300 		return -1;
301 	}
302 
303 	char fullPath[MAXPATHLEN];
304 	if (get_path(fd, path, fullPath) < 0)
305 		return -1;
306 
307 	return mkfifo(fullPath, mode);
308 }
309 
310 
311 int
312 mknodat(int fd, const char *path, mode_t mode, dev_t dev)
313 {
314 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
315 		// call mknod() ignoring fd
316 		return mknod(path, mode, dev);
317 	}
318 
319 	if (fd < 0) {
320 		// Invalid file descriptor
321 		errno = EBADF;
322 		return -1;
323 	}
324 
325 	char fullPath[MAXPATHLEN];
326 	if (get_path(fd, path, fullPath) < 0)
327 		return -1;
328 
329 	return mknod(fullPath, mode, dev);
330 }
331 
332 
333 int
334 renameat(int oldFD, const char* oldPath, int newFD, const char* newPath)
335 {
336 	bool ignoreOldFD = false;
337 	bool ignoreNewFD = false;
338 
339 	if (oldFD == AT_FDCWD || (oldPath != NULL && oldPath[0] == '/'))
340 		ignoreOldFD = true;
341 
342 	if (newFD == AT_FDCWD || (newPath != NULL && newPath[0] == '/'))
343 		ignoreNewFD = true;
344 
345 	if (ignoreOldFD && ignoreNewFD) {
346 		// call rename() ignoring the fd's
347 		return rename(oldPath, newPath);
348 	}
349 
350 	char oldFullPath[MAXPATHLEN];
351 	if (!ignoreOldFD) {
352 		if (oldFD < 0) {
353 			// Invalid file descriptor
354 			errno = EBADF;
355 			return -1;
356 		}
357 
358 		if (get_path(oldFD, oldPath, oldFullPath) < 0)
359 			return -1;
360 	}
361 
362 	char newFullPath[MAXPATHLEN];
363 	if (!ignoreNewFD) {
364 		if (newFD < 0) {
365 			// Invalid file descriptor
366 			errno = EBADF;
367 			return -1;
368 		}
369 
370 		if (get_path(newFD, newPath, newFullPath) < 0)
371 			return -1;
372 	}
373 
374 	return rename(ignoreOldFD ? oldPath : oldFullPath,
375 		ignoreNewFD ? newPath : newFullPath);
376 }
377 
378 
379 ssize_t
380 readlinkat(int fd, const char *path, char *buffer, size_t bufferSize)
381 {
382 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
383 		// call readlink() ignoring fd
384 		return readlink(path, buffer, bufferSize);
385 	}
386 
387 	if (fd < 0) {
388 		// Invalid file descriptor
389 		errno = EBADF;
390 		return -1;
391 	}
392 
393 	char fullPath[MAXPATHLEN];
394 	if (get_path(fd, path, fullPath) < 0)
395 		return -1;
396 
397 	return readlink(fullPath, buffer, bufferSize);
398 }
399 
400 
401 int
402 symlinkat(const char *oldPath, int fd, const char *newPath)
403 {
404 	if (fd == AT_FDCWD || (newPath != NULL && newPath[0] == '/')) {
405 		// call symlink() ignoring fd
406 		return symlink(oldPath, newPath);
407 	}
408 
409 	if (fd < 0) {
410 		// Invalid file descriptor
411 		errno = EBADF;
412 		return -1;
413 	}
414 
415 	// newPath is relative to the fd
416 	char newFullPath[MAXPATHLEN];
417 	if (get_path(fd, newPath, newFullPath) < 0)
418 		return -1;
419 
420 	return symlink(oldPath, newFullPath);
421 }
422 
423 
424 int
425 unlinkat(int fd, const char *path, int flag)
426 {
427 	if (flag != AT_REMOVEDIR && flag != 0) {
428 		// invalid flag
429 		errno = EINVAL;
430 		return -1;
431 	}
432 
433 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
434 		// call rmdir() or unlink() ignoring fd
435 		return (flag & AT_REMOVEDIR) != 0 ? rmdir(path) : unlink(path);
436 	}
437 
438 	if (fd < 0) {
439 		// Invalid file descriptor
440 		errno = EBADF;
441 		return -1;
442 	}
443 
444 	char fullPath[MAXPATHLEN];
445 	if (get_path(fd, path, fullPath) < 0)
446 		return -1;
447 
448 	return (flag & AT_REMOVEDIR) != 0 ? rmdir(fullPath)
449 		: unlink(fullPath);
450 }
451 
452 
453 int
454 linkat(int oldFD, const char *oldPath, int newFD, const char *newPath,
455 	   int flag)
456 {
457 	if ((flag & AT_SYMLINK_FOLLOW) != 0) {
458 		// Dereference oldPath
459 		// CURRENTLY UNSUPPORTED
460 		errno = ENOTSUP;
461 		return -1;
462 	} else if (flag != 0) {
463 		errno = EINVAL;
464 		return -1;
465 	}
466 
467 	bool ignoreOldFD = false;
468 	bool ignoreNewFD = false;
469 
470 	if (oldFD == AT_FDCWD || oldPath != NULL && oldPath[0] == '/')
471 		ignoreOldFD = true;
472 
473 	if (newFD == AT_FDCWD || newPath != NULL && newPath[0] == '/')
474 		ignoreNewFD = true;
475 
476 	if (ignoreOldFD && ignoreNewFD) {
477 		// call link() ignoring the fd's
478 		return link(oldPath, newPath);
479 	}
480 
481 	char oldFullPath[MAXPATHLEN];
482 	if (!ignoreOldFD) {
483 		if (oldFD < 0) {
484 			// Invalid file descriptor
485 			errno = EBADF;
486 			return -1;
487 		}
488 
489 		if (get_path(oldFD, oldPath, oldFullPath) < 0)
490 			return -1;
491 	}
492 
493 	char newFullPath[MAXPATHLEN];
494 	if (!ignoreNewFD) {
495 		if (newFD < 0) {
496 			// Invalid file descriptor
497 			errno = EBADF;
498 			return -1;
499 		}
500 
501 		if (get_path(newFD, newPath, newFullPath) < 0)
502 			return -1;
503 	}
504 
505 	return link(ignoreOldFD ? oldPath : oldFullPath,
506 		ignoreNewFD ? newPath : newFullPath);
507 }
508 
509 
510 int
511 futimesat(int fd, const char *path, const struct timeval times[2])
512 {
513 	if (fd == AT_FDCWD || (path != NULL && path[0] == '/')) {
514 		// call utimes() ignoring fd
515 		return utimes(path, times);
516 	}
517 
518 	if (fd < 0) {
519 		// Invalid file descriptor
520 		errno = EBADF;
521 		return -1;
522 	}
523 
524 	char fullPath[MAXPATHLEN];
525 	if (get_path(fd, path, fullPath) < 0)
526 		return -1;
527 
528 	return utimes(fullPath, times);
529 }
530