diff --git a/src/build/libroot/fs_darwin.cpp b/src/build/libroot/fs_darwin.cpp index 3cfeb4bfaa..58df04bcc5 100644 --- a/src/build/libroot/fs_darwin.cpp +++ b/src/build/libroot/fs_darwin.cpp @@ -761,10 +761,113 @@ unlinkat(int fd, const char *path, int flag) int -linkat(int toFD, const char *toPath, int linkFD, const char *linkPath, int flag) +linkat(int oldFD, const char *oldPath, int newFD, const char *newPath, + int flag) { - errno = ENOSYS; - return -1; + if ((flag & AT_SYMLINK_FOLLOW) == 0) { + // Dereference oldPath + // CURRENTLY UNSUPPORTED + errno = ENOTSUP; + return -1; + } else if (flag != 0) { + errno = EINVAL; + return -1; + } + + bool oldIsAbsolute = false; + bool newIsAbsolute = false; + + if (oldPath && oldPath[0] == '/') + oldIsAbsolute = true; + + if (newPath && newPath[0] == '/') + newIsAbsolute = true; + + if (oldIsAbsolute && newIsAbsolute) { + // oldPath and newPath are both absolute, call link() ignoring the fd's + return link(oldPath, newPath); + } + + if ((!oldIsAbsolute && !oldFD) || (!newIsAbsolute && !newFD)) { + // oldPath is not an absolute path and oldFD is not a valid file + // descriptor or newPath is not an absolute path and newFD is not a + // valid file descriptor + errno = EBADF; + return -1; + } + + char *oldDirPath; + char *newDirPath; + + if (oldIsAbsolute || oldFD == AT_FDCWD) + oldDirPath = const_cast(oldPath); + else { + oldDirPath = (char *)malloc(MAXPATHLEN); + if (oldDirPath == NULL) { + // ran out of memory allocating oldDirPath + errno = ENOMEM; + return -1; + } + + struct stat dirst; + if (fstat(oldFD, &dirst) < 0) { + // failed to grab stat information, fstat() sets errno + return -1; + } + + if ((dirst.st_mode & S_IFDIR) != 0) { + // fd does not point to a directory + errno = ENOTDIR; + return -1; + } + + if (fcntl(oldFD, F_GETPATH, oldDirPath) < 0) { + // failed to get the path of oldFD, fcntl sets errno + return -1; + } + + if (strlcat(oldDirPath, oldPath, MAXPATHLEN) > MAXPATHLEN) { + // full path is too long, set errno and return + errno = ENAMETOOLONG; + return -1; + } + } + + if (newIsAbsolute || newFD == AT_FDCWD) + newDirPath = const_cast(newPath); + else { + newDirPath = (char *)malloc(MAXPATHLEN); + if (newDirPath == NULL) { + // ran out of memory allocating newDirPath + errno = ENOMEM; + return -1; + } + + struct stat dirst; + if (fstat(newFD, &dirst) < 0) { + // failed to grab stat information, fstat() sets errno + return -1; + } + + if ((dirst.st_mode & S_IFDIR) != 0) { + // fd does not point to a directory + errno = ENOTDIR; + return -1; + } + + if (fcntl(newFD, F_GETPATH, newDirPath) < 0) { + // failed to get the path of newFD, fcntl sets errno + return -1; + } + + if (strlcat(newDirPath, newPath, MAXPATHLEN) > MAXPATHLEN) { + // full path is too long, set errno and return + errno = ENAMETOOLONG; + return -1; + } + } + + return link(oldDirPath, newDirPath); } diff --git a/src/build/libroot/fs_darwin.h b/src/build/libroot/fs_darwin.h index c07fe4041e..7c09eac990 100644 --- a/src/build/libroot/fs_darwin.h +++ b/src/build/libroot/fs_darwin.h @@ -28,8 +28,8 @@ int renameat(int fromFD, const char* from, int toFD, const char* to); ssize_t readlinkat(int fd, const char *path, char *buffer, size_t bufferSize); int symlinkat(const char *toPath, int fd, const char *symlinkPath); int unlinkat(int fd, const char *path, int flag); -int linkat(int toFD, const char *toPath, int linkFD, const char *linkPath, - int flag); +int linkat(int oldFD, const char *oldPath, int newFD, const char *newPath, + int flag); int futimesat(int fd, const char *path, const struct timeval times[2]);