From 18348cebf1f2954741790d4c2bc2f8ff1658f8a3 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 10 Nov 2019 13:47:02 +0100 Subject: [PATCH] Kernel+LibC: Implement the openat() syscall POSIX's openat() is very similar to open(), except you also provide a file descriptor referring to a directory from which relative paths should be resolved. Passing it the magical fd number AT_FDCWD means "resolve from current directory" (which is indeed also what open() normally does.) This fixes libarchive's bsdtar, since it was trying to do something extremely wrong in the absence of openat() support. The issue has recently been fixed upstream in libarchive: https://github.com/libarchive/libarchive/issues/1239 However, we should have openat() support anyway, so I went ahead and implemented it. :^) Fixes #748. --- Kernel/Process.cpp | 49 ++++++++++++++++++++++++++++++++++----- Kernel/Process.h | 1 + Kernel/Syscall.h | 11 ++++++++- Kernel/UnixTypes.h | 3 +++ Libraries/LibC/unistd.cpp | 20 ++++++++++++++++ Libraries/LibC/unistd.h | 3 +++ 6 files changed, 80 insertions(+), 7 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 11ae31cf96..b60ac264b3 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -1351,15 +1351,12 @@ int Process::sys$open(const Syscall::SC_open_params* params) { if (!validate_read_typed(params)) return -EFAULT; - auto* path = params->path; - auto path_length = params->path_length; - auto options = params->options; - auto mode = params->mode; + auto [path, path_length, options, mode] = *params; + if (!validate_read(path, path_length)) + return -EFAULT; #ifdef DEBUG_IO dbgprintf("%s(%u) sys$open(\"%s\")\n", name().characters(), pid(), path); #endif - if (!validate_read(path, path_length)) - return -EFAULT; int fd = alloc_fd(); if (fd < 0) return fd; @@ -1375,6 +1372,46 @@ int Process::sys$open(const Syscall::SC_open_params* params) return fd; } +int Process::sys$openat(const Syscall::SC_openat_params* params) +{ + if (!validate_read_typed(params)) + return -EFAULT; + auto [dirfd, path, path_length, options, mode] = *params; + if (!validate_read(path, path_length)) + return -EFAULT; +#ifdef DEBUG_IO + dbgprintf("%s(%u) sys$openat(%d, \"%s\")\n", dirfd, name().characters(), pid(), path); +#endif + int fd = alloc_fd(); + if (fd < 0) + return fd; + + RefPtr base; + if (dirfd == AT_FDCWD) { + base = current_directory(); + } else { + auto* base_description = file_description(dirfd); + if (!base_description) + return -EBADF; + if (!base_description->is_directory()) + return -ENOTDIR; + if (!base_description->custody()) + return -EINVAL; + base = base_description->custody(); + } + + auto result = VFS::the().open(path, options, mode & ~umask(), *base); + if (result.is_error()) + return result.error(); + auto description = result.value(); + if (options & O_DIRECTORY && !description->is_directory()) + return -ENOTDIR; // FIXME: This should be handled by VFS::open. + description->set_file_flags(options); + u32 fd_flags = (options & O_CLOEXEC) ? FD_CLOEXEC : 0; + m_fds[fd].set(move(description), fd_flags); + return fd; +} + int Process::alloc_fd(int first_candidate_fd) { int fd = -EMFILE; diff --git a/Kernel/Process.h b/Kernel/Process.h index bf67fe574b..fb9e8585d9 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -124,6 +124,7 @@ public: pid_t sys$getppid(); mode_t sys$umask(mode_t); int sys$open(const Syscall::SC_open_params*); + int sys$openat(const Syscall::SC_openat_params*); int sys$close(int fd); ssize_t sys$read(int fd, u8*, ssize_t); ssize_t sys$write(int fd, const u8*, ssize_t); diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index 2dde172ba0..be41c92159 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -134,7 +134,8 @@ struct timespec; __ENUMERATE_SYSCALL(fchdir) \ __ENUMERATE_SYSCALL(getrandom) \ __ENUMERATE_SYSCALL(clock_gettime) \ - __ENUMERATE_SYSCALL(clock_nanosleep) + __ENUMERATE_SYSCALL(clock_nanosleep) \ + __ENUMERATE_SYSCALL(openat) namespace Syscall { @@ -179,6 +180,14 @@ struct SC_open_params { u16 mode; }; +struct SC_openat_params { + int dirfd; + const char* path; + int path_length; + int options; + u16 mode; +}; + struct SC_select_params { int nfds; fd_set* readfds; diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 52122f8727..e50bfc0cb6 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -448,3 +448,6 @@ struct ifreq { #define ifr_llprio ifr_ifru.ifru_metric // link layer priority #define ifr_hwaddr ifr_ifru.ifru_hwaddr // MAC address }; + +#define AT_FDCWD -100 + diff --git a/Libraries/LibC/unistd.cpp b/Libraries/LibC/unistd.cpp index d465792cab..b7a846917c 100644 --- a/Libraries/LibC/unistd.cpp +++ b/Libraries/LibC/unistd.cpp @@ -189,6 +189,17 @@ int open_with_path_length(const char* path, size_t path_length, int options, mod __RETURN_WITH_ERRNO(rc, rc, -1); } +int openat_with_path_length(int dirfd, const char* path, size_t path_length, int options, mode_t mode) +{ + if (path_length > INT32_MAX) { + errno = EINVAL; + return -1; + } + Syscall::SC_openat_params params { dirfd, path, (int)path_length, options, mode }; + int rc = syscall(SC_openat, ¶ms); + __RETURN_WITH_ERRNO(rc, rc, -1); +} + int open(const char* path, int options, ...) { va_list ap; @@ -198,6 +209,15 @@ int open(const char* path, int options, ...) return open_with_path_length(path, strlen(path), options, mode); } +int openat(int dirfd, const char* path, int options, ...) +{ + va_list ap; + va_start(ap, options); + auto mode = (mode_t)va_arg(ap, unsigned); + va_end(ap); + return openat_with_path_length(dirfd, path, strlen(path), options, mode); +} + ssize_t read(int fd, void* buf, size_t count) { int rc = syscall(SC_read, fd, buf, count); diff --git a/Libraries/LibC/unistd.h b/Libraries/LibC/unistd.h index 6a879684e4..41f359d53d 100644 --- a/Libraries/LibC/unistd.h +++ b/Libraries/LibC/unistd.h @@ -77,6 +77,9 @@ int creat(const char* path, mode_t); int open(const char* path, int options, ...); int creat_with_path_length(const char* path, size_t path_length, mode_t); int open_with_path_length(const char* path, size_t path_length, int options, mode_t); +#define AT_FDCWD -100 +int openat(int dirfd, const char* path, int options, ...); +int openat_with_path_length(int dirfd, const char* path, size_t path_length, int options, mode_t); ssize_t read(int fd, void* buf, size_t count); ssize_t write(int fd, const void* buf, size_t count); int close(int fd);