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);