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