diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index 57a36cd0e39..2d4e839d354 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -422,6 +422,7 @@ struct SC_waitid_params { }; struct SC_stat_params { + int dirfd; StringArgument path; struct stat* statbuf; int follow_symlinks; diff --git a/Kernel/Syscalls/stat.cpp b/Kernel/Syscalls/stat.cpp index 379e762b54e..d75f6a677fb 100644 --- a/Kernel/Syscalls/stat.cpp +++ b/Kernel/Syscalls/stat.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -33,7 +34,20 @@ KResultOr Process::sys$stat(Userspace user_ auto path = get_syscall_path_argument(params.path); if (path.is_error()) return path.error(); - auto metadata_or_error = VFS::the().lookup_metadata(path.value(), current_directory(), params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR); + RefPtr base; + if (params.dirfd == AT_FDCWD) { + base = current_directory(); + } else { + auto base_description = file_description(params.dirfd); + if (!base_description) + return EBADF; + if (!base_description->is_directory()) + return ENOTDIR; + if (!base_description->custody()) + return EINVAL; + base = base_description->custody(); + } + auto metadata_or_error = VFS::the().lookup_metadata(path.value(), *base, params.follow_symlinks ? 0 : O_NOFOLLOW_NOERROR); if (metadata_or_error.is_error()) return metadata_or_error.error(); stat statbuf; diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 0e3ea314ed4..dce27f9070c 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -680,6 +680,7 @@ struct rtentry { #define RTF_GATEWAY 0x2 /* the route is a gateway and not an end host */ #define AT_FDCWD -100 +#define AT_SYMLINK_NOFOLLOW 0x100 #define PURGE_ALL_VOLATILE 0x1 #define PURGE_ALL_CLEAN_INODE 0x2 diff --git a/Userland/Libraries/LibC/fcntl.h b/Userland/Libraries/LibC/fcntl.h index b23df06e38a..7e61ec33247 100644 --- a/Userland/Libraries/LibC/fcntl.h +++ b/Userland/Libraries/LibC/fcntl.h @@ -40,6 +40,7 @@ __BEGIN_DECLS int creat(const char* path, mode_t); int open(const char* path, int options, ...); #define AT_FDCWD -100 +#define AT_SYMLINK_NOFOLLOW 0x100 int openat(int dirfd, const char* path, int options, ...); int fcntl(int fd, int cmd, ...); diff --git a/Userland/Libraries/LibC/stat.cpp b/Userland/Libraries/LibC/stat.cpp index b10acf02258..e17e30eb65b 100644 --- a/Userland/Libraries/LibC/stat.cpp +++ b/Userland/Libraries/LibC/stat.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -50,25 +51,25 @@ int mkfifo(const char* pathname, mode_t mode) return mknod(pathname, mode | S_IFIFO, 0); } -static int do_stat(const char* path, struct stat* statbuf, bool follow_symlinks) +static int do_stat(int dirfd, const char* path, struct stat* statbuf, bool follow_symlinks) { if (!path) { errno = EFAULT; return -1; } - Syscall::SC_stat_params params { { path, strlen(path) }, statbuf, follow_symlinks }; + Syscall::SC_stat_params params { dirfd, { path, strlen(path) }, statbuf, follow_symlinks }; int rc = syscall(SC_stat, ¶ms); __RETURN_WITH_ERRNO(rc, rc, -1); } int lstat(const char* path, struct stat* statbuf) { - return do_stat(path, statbuf, false); + return do_stat(AT_FDCWD, path, statbuf, false); } int stat(const char* path, struct stat* statbuf) { - return do_stat(path, statbuf, true); + return do_stat(AT_FDCWD, path, statbuf, true); } int fstat(int fd, struct stat* statbuf) @@ -76,4 +77,9 @@ int fstat(int fd, struct stat* statbuf) int rc = syscall(SC_fstat, fd, statbuf); __RETURN_WITH_ERRNO(rc, rc, -1); } + +int fstatat(int fd, const char* path, struct stat* statbuf, int flags) +{ + return do_stat(fd, path, statbuf, !(flags & AT_SYMLINK_NOFOLLOW)); +} } diff --git a/Userland/Libraries/LibC/sys/stat.h b/Userland/Libraries/LibC/sys/stat.h index 46e9f48f0eb..579559d7968 100644 --- a/Userland/Libraries/LibC/sys/stat.h +++ b/Userland/Libraries/LibC/sys/stat.h @@ -77,5 +77,6 @@ int mkfifo(const char* pathname, mode_t); int fstat(int fd, struct stat* statbuf); int lstat(const char* path, struct stat* statbuf); int stat(const char* path, struct stat* statbuf); +int fstatat(int fd, const char* path, struct stat* statbuf, int flags); __END_DECLS