Skip to content

Commit

Permalink
Kernel+LibC: Implement the openat() syscall
Browse files Browse the repository at this point in the history
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:

libarchive/libarchive#1239

However, we should have openat() support anyway, so I went ahead and
implemented it. :^)

Fixes SerenityOS#748.
  • Loading branch information
awesomekling committed Nov 10, 2019
1 parent 4f27745 commit 18348ce
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 6 deletions.
47 changes: 42 additions & 5 deletions Kernel/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1351,19 +1351,56 @@ 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
int fd = alloc_fd();
if (fd < 0)
return fd;
auto result = VFS::the().open(path, options, mode & ~umask(), current_directory());
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::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;
auto result = VFS::the().open(path, options, mode & ~umask(), current_directory());

RefPtr<Custody> 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();
Expand Down
1 change: 1 addition & 0 deletions Kernel/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class Process : public InlineLinkedListNode<Process>
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);
Expand Down
11 changes: 10 additions & 1 deletion Kernel/Syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions Kernel/UnixTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

20 changes: 20 additions & 0 deletions Libraries/LibC/unistd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, &params);
__RETURN_WITH_ERRNO(rc, rc, -1);
}

int open(const char* path, int options, ...)
{
va_list ap;
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions Libraries/LibC/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 18348ce

Please sign in to comment.