Luna/kernel/src/sys/open.cpp
apio dc766e1da7
Some checks failed
Build and test / build (push) Has been cancelled
kernel: Rework VFS access checking + add processes
VFS functions now accept a single Process* pointer instead of credentials and groups.
There is now a distinction between processes and threads
Now to fix all the bugs... waitpid crashes the process with an NX error...
2024-12-06 21:35:59 +01:00

124 lines
3.8 KiB
C++

#include "Log.h"
#include "Pledge.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/modes.h>
#include <bits/open-flags.h>
// These flags are needed after open(), the rest only affect open().
constexpr int FLAGS_TO_KEEP = O_RDWR | O_APPEND | O_NONBLOCK;
Result<u64> sys_openat(Registers*, SyscallArgs args)
{
int dirfd = (int)args[0];
auto path = TRY(MemoryManager::strdup_from_user(args[1]));
int flags = (int)args[2];
mode_t mode = (mode_t)args[3];
Process* current = Process::current();
SharedPtr<VFS::Inode> inode;
// Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR
if ((flags & O_RDWR) == 0) { return err(EINVAL); }
if (flags & O_RDONLY) TRY(check_pledge(current, Promise::p_rpath));
else if (flags & O_WRONLY)
TRY(check_pledge(current, Promise::p_wpath));
if (flags & O_CREAT) TRY(check_pledge(current, Promise::p_cpath));
if (flags & O_TMPFILE)
{
if (!(flags & O_WRONLY)) return err(EINVAL);
if (flags & O_CREAT) return err(EINVAL);
}
int error;
SharedPtr<VFS::Inode> parent_inode;
bool ok = current->resolve_atfile(dirfd, path, false, !(flags & O_NOFOLLOW), &parent_inode)
.try_set_value_or_error(inode, error);
if (!ok)
{
if (error == ENOENT && (flags & O_CREAT) && !path.is_empty())
{
auto auth = current->credentials();
inode = TRY(VFS::create_file(path.chars(), mode & ~current->umask, current, parent_inode));
// FIXME: Pass these in create_file().
auto metadata = inode->metadata();
metadata.uid = auth.euid;
metadata.gid = auth.egid;
TRY(inode->set_metadata(metadata));
}
else
return err(error);
}
else if (flags & O_EXCL)
return err(EEXIST);
else
{
if ((flags & O_RDONLY) && !VFS::can_read(inode, current)) return err(EACCES);
if ((flags & O_WRONLY) && !VFS::can_write(inode, current)) return err(EACCES);
}
inode = TRY(inode->open());
// This should only be possible if O_NOFOLLOW was in flags.
if (inode->type() == VFS::InodeType::Symlink) return err(ELOOP);
if (inode->type() == VFS::InodeType::Socket) return err(ENXIO);
if (flags & O_TMPFILE)
{
auto auth = current->credentials();
if (inode->type() != VFS::InodeType::Directory) return err(EINVAL);
inode = TRY(inode->fs()->create_file_inode(mode & current->umask));
auto metadata = inode->metadata();
metadata.uid = auth.euid;
metadata.gid = auth.egid;
TRY(inode->set_metadata(metadata));
}
if ((flags & O_WRONLY) && inode->fs() && inode->fs()->is_readonly()) return err(EROFS);
if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR);
if (inode->type() == VFS::InodeType::Directory)
{
if ((flags & O_WRONLY) || (flags & O_CREAT)) return err(EISDIR);
}
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
auto descriptor =
FileDescriptor { TRY(make_shared<OpenFileDescription>(inode, flags & FLAGS_TO_KEEP)), 0, flags & O_CLOEXEC };
int fd = TRY(current->allocate_fd(0, descriptor));
#ifdef OPEN_DEBUG
kdbgln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd);
#endif
return (u64)fd;
}
Result<u64> sys_close(Registers*, SyscallArgs args)
{
int fd = (int)args[0];
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
Process* current = Process::current();
TRY(check_pledge(current, Promise::p_stdio));
auto table = current->fd_table.lock();
Option<FileDescriptor>& descriptor = (*table)[fd];
if (!descriptor.has_value()) return err(EBADF);
descriptor = {};
return 0;
}