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