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 DIR* dir = opendir(path); 234 if (dir != NULL) 235 close(fd); 236 237 return dir; 238 } 239 240 241 int 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 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 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 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 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 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 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 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 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 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