Kernel: Implement more POSIX compliant open() and openat() syscalls

This commit is contained in:
Bananymous 2024-08-01 15:35:02 +03:00
parent 401b460d75
commit 838d31fa41
3 changed files with 56 additions and 44 deletions

View File

@ -2,6 +2,7 @@
#include <BAN/Array.h> #include <BAN/Array.h>
#include <kernel/FS/Inode.h> #include <kernel/FS/Inode.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <limits.h> #include <limits.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -21,7 +22,7 @@ namespace Kernel
BAN::ErrorOr<void> clone_from(const OpenFileDescriptorSet&); BAN::ErrorOr<void> clone_from(const OpenFileDescriptorSet&);
BAN::ErrorOr<int> open(BAN::RefPtr<Inode>, int flags); BAN::ErrorOr<int> open(VirtualFileSystem::File, int flags);
BAN::ErrorOr<int> open(BAN::StringView absolute_path, int flags); BAN::ErrorOr<int> open(BAN::StringView absolute_path, int flags);
BAN::ErrorOr<int> socket(int domain, int type, int protocol); BAN::ErrorOr<int> socket(int domain, int type, int protocol);

View File

@ -55,32 +55,16 @@ namespace Kernel
return {}; return {};
} }
BAN::ErrorOr<int> OpenFileDescriptorSet::open(BAN::RefPtr<Inode> inode, int flags) BAN::ErrorOr<int> OpenFileDescriptorSet::open(VirtualFileSystem::File file, int flags)
{ {
ASSERT(inode); ASSERT(file.inode);
ASSERT(!inode->mode().ifdir());
if (flags & ~(O_RDONLY | O_WRONLY)) if (flags & ~(O_ACCMODE | O_NOFOLLOW | O_APPEND | O_TRUNC | O_CLOEXEC | O_TTY_INIT | O_DIRECTORY | O_CREAT | O_EXCL | O_NONBLOCK))
return BAN::Error::from_errno(ENOTSUP); return BAN::Error::from_errno(ENOTSUP);
int fd = TRY(get_free_fd()); if ((flags & O_ACCMODE) != O_RDWR && __builtin_popcount(flags & O_ACCMODE) != 1)
// FIXME: path?
m_open_files[fd] = TRY(BAN::RefPtr<OpenFileDescription>::create(inode, ""_sv, 0, flags));
return fd;
}
BAN::ErrorOr<int> OpenFileDescriptorSet::open(BAN::StringView absolute_path, int flags)
{
if (flags & ~(O_RDONLY | O_WRONLY | O_NOFOLLOW | O_SEARCH | O_APPEND | O_TRUNC | O_CLOEXEC | O_TTY_INIT | O_DIRECTORY | O_NONBLOCK))
return BAN::Error::from_errno(ENOTSUP);
int access_mask = O_EXEC | O_RDONLY | O_WRONLY | O_SEARCH;
if ((flags & access_mask) != O_RDWR && __builtin_popcount(flags & access_mask) != 1)
return BAN::Error::from_errno(EINVAL); return BAN::Error::from_errno(EINVAL);
auto file = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, flags));
if ((flags & O_DIRECTORY) && !file.inode->mode().ifdir()) if ((flags & O_DIRECTORY) && !file.inode->mode().ifdir())
return BAN::Error::from_errno(ENOTDIR); return BAN::Error::from_errno(ENOTDIR);
@ -93,6 +77,11 @@ namespace Kernel
return fd; return fd;
} }
BAN::ErrorOr<int> OpenFileDescriptorSet::open(BAN::StringView absolute_path, int flags)
{
return open(TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, flags)), flags);
}
BAN::ErrorOr<int> OpenFileDescriptorSet::socket(int domain, int type, int protocol) BAN::ErrorOr<int> OpenFileDescriptorSet::socket(int domain, int type, int protocol)
{ {
bool valid_protocol = true; bool valid_protocol = true;

View File

@ -664,9 +664,9 @@ namespace Kernel
return BAN::Error::from_errno(EEXIST); return BAN::Error::from_errno(EEXIST);
if (Inode::Mode(mode).ifdir()) if (Inode::Mode(mode).ifdir())
TRY(parent_inode->create_directory(file_name, mode, m_credentials.euid(), m_credentials.egid())); TRY(parent_inode->create_directory(file_name, mode, m_credentials.euid(), parent_inode->gid()));
else else
TRY(parent_inode->create_file(file_name, mode, m_credentials.euid(), m_credentials.egid())); TRY(parent_inode->create_file(file_name, mode, m_credentials.euid(), parent_inode->gid()));
return {}; return {};
} }
@ -704,38 +704,51 @@ namespace Kernel
{ {
ASSERT(inode); ASSERT(inode);
LockGuard _(m_process_lock); LockGuard _(m_process_lock);
return TRY(m_open_file_descriptors.open(inode, flags)); return TRY(m_open_file_descriptors.open(VirtualFileSystem::File { .inode = inode, .canonical_path = {} }, flags));
} }
BAN::ErrorOr<long> Process::open_file(BAN::StringView path, int flags, mode_t mode) BAN::ErrorOr<long> Process::open_file(BAN::StringView path, int flags, mode_t mode)
{ {
if ((flags & (O_DIRECTORY | O_CREAT)) == (O_DIRECTORY | O_CREAT))
return BAN::Error::from_errno(EINVAL);
LockGuard _(m_process_lock); LockGuard _(m_process_lock);
BAN::String absolute_path = TRY(absolute_path_of(path)); BAN::String absolute_path = TRY(absolute_path_of(path));
if (flags & O_CREAT) auto file_or_error = VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, flags | O_NOFOLLOW);
VirtualFileSystem::File file;
if (file_or_error.is_error())
{ {
if (flags & O_DIRECTORY) if (!(flags & O_CREAT) || file_or_error.error().get_error_code() != ENOENT)
return BAN::Error::from_errno(ENOTSUP); return file_or_error.release_error();
auto file_or_error = VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, O_WRONLY);
if (!file_or_error.is_error() && (flags & O_EXCL)) // FIXME: There is a race condition between these lines
TRY(create_file_or_dir(absolute_path, (mode & 0777) | Inode::Mode::IFREG));
file = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, flags));
}
else
{
if ((flags & O_CREAT) && (flags & O_EXCL))
return BAN::Error::from_errno(EEXIST); return BAN::Error::from_errno(EEXIST);
if (file_or_error.is_error())
{ file = file_or_error.release_value();
if (file_or_error.error().get_error_code() == ENOENT) if (file.inode->mode().ifdir() && (flags & O_WRONLY))
TRY(create_file_or_dir(path, Inode::Mode::IFREG | mode)); return BAN::Error::from_errno(EISDIR);
else if (!file.inode->mode().ifdir() && (flags & O_DIRECTORY))
return file_or_error.release_error(); return BAN::Error::from_errno(ENOTDIR);
} if (file.inode->mode().iflnk() && !(flags & O_NOFOLLOW))
flags &= ~O_CREAT; file = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, flags));
} }
int fd = TRY(m_open_file_descriptors.open(absolute_path, flags)); ASSERT(file.inode);
auto inode = MUST(m_open_file_descriptors.inode_of(fd));
int fd = TRY(m_open_file_descriptors.open(file, flags));
// Open controlling terminal // Open controlling terminal
if ((flags & O_TTY_INIT) && !(flags & O_NOCTTY) && inode->is_tty() && is_session_leader() && !m_controlling_terminal) if ((flags & O_TTY_INIT) && !(flags & O_NOCTTY) && file.inode->is_tty() && is_session_leader() && !m_controlling_terminal)
m_controlling_terminal = (TTY*)inode.ptr(); m_controlling_terminal = (TTY*)file.inode.ptr();
return fd; return fd;
} }
@ -753,10 +766,19 @@ namespace Kernel
TRY(validate_string_access(path)); TRY(validate_string_access(path));
// FIXME: handle O_SEARCH in fd
BAN::String absolute_path; BAN::String absolute_path;
TRY(absolute_path.append(TRY(m_open_file_descriptors.path_of(fd))));
if (fd == AT_FDCWD)
TRY(absolute_path.append(m_working_directory));
else if (path[0] != '/')
{
int flags = TRY(m_open_file_descriptors.flags_of(fd));
if (!(flags & O_RDONLY) && !(flags & O_SEARCH))
return BAN::Error::from_errno(EBADF);
if (!TRY(m_open_file_descriptors.inode_of(fd))->mode().ifdir())
return BAN::Error::from_errno(ENOTDIR);
TRY(absolute_path.append(TRY(m_open_file_descriptors.path_of(fd))));
}
TRY(absolute_path.push_back('/')); TRY(absolute_path.push_back('/'));
TRY(absolute_path.append(path)); TRY(absolute_path.append(path));