Kernel: Make OpenFileDescriptorSet thread safe

Also this allows concurrent calling of read/write/send/recv
This commit is contained in:
Bananymous 2025-04-22 04:31:18 +03:00
parent d54489bbcb
commit c790bad667
2 changed files with 146 additions and 46 deletions

View File

@ -51,7 +51,7 @@ namespace Kernel
BAN::ErrorOr<size_t> sendto(int fd, BAN::ConstByteSpan buffer, const sockaddr* address, socklen_t address_len); BAN::ErrorOr<size_t> sendto(int fd, BAN::ConstByteSpan buffer, const sockaddr* address, socklen_t address_len);
BAN::ErrorOr<VirtualFileSystem::File> file_of(int) const; BAN::ErrorOr<VirtualFileSystem::File> file_of(int) const;
BAN::ErrorOr<BAN::StringView> path_of(int) const; BAN::ErrorOr<BAN::String> path_of(int) const;
BAN::ErrorOr<BAN::RefPtr<Inode>> inode_of(int); BAN::ErrorOr<BAN::RefPtr<Inode>> inode_of(int);
BAN::ErrorOr<int> status_flags_of(int) const; BAN::ErrorOr<int> status_flags_of(int) const;
@ -98,6 +98,7 @@ namespace Kernel
private: private:
const Credentials& m_credentials; const Credentials& m_credentials;
mutable Mutex m_mutex;
BAN::Array<OpenFile, OPEN_MAX> m_open_files; BAN::Array<OpenFile, OPEN_MAX> m_open_files;
}; };

View File

@ -23,6 +23,7 @@ namespace Kernel
OpenFileDescriptorSet& OpenFileDescriptorSet::operator=(OpenFileDescriptorSet&& other) OpenFileDescriptorSet& OpenFileDescriptorSet::operator=(OpenFileDescriptorSet&& other)
{ {
LockGuard _(m_mutex);
for (size_t i = 0; i < m_open_files.size(); i++) for (size_t i = 0; i < m_open_files.size(); i++)
m_open_files[i] = BAN::move(other.m_open_files[i]); m_open_files[i] = BAN::move(other.m_open_files[i]);
return *this; return *this;
@ -30,6 +31,8 @@ namespace Kernel
BAN::ErrorOr<void> OpenFileDescriptorSet::clone_from(const OpenFileDescriptorSet& other) BAN::ErrorOr<void> OpenFileDescriptorSet::clone_from(const OpenFileDescriptorSet& other)
{ {
LockGuard _(m_mutex);
close_all(); close_all();
for (int fd = 0; fd < (int)other.m_open_files.size(); fd++) for (int fd = 0; fd < (int)other.m_open_files.size(); fd++)
@ -67,11 +70,11 @@ namespace Kernel
if ((flags & O_TRUNC) && (flags & O_WRONLY) && file.inode->mode().ifreg()) if ((flags & O_TRUNC) && (flags & O_WRONLY) && file.inode->mode().ifreg())
TRY(file.inode->truncate(0)); TRY(file.inode->truncate(0));
LockGuard _(m_mutex);
constexpr int status_mask = O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC | O_ACCMODE; constexpr int status_mask = O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC | O_ACCMODE;
int fd = TRY(get_free_fd()); int fd = TRY(get_free_fd());
m_open_files[fd].description = TRY(BAN::RefPtr<OpenFileDescription>::create(BAN::move(file), 0, flags & status_mask)); m_open_files[fd].description = TRY(BAN::RefPtr<OpenFileDescription>::create(BAN::move(file), 0, flags & status_mask));
m_open_files[fd].descriptor_flags = flags & O_CLOEXEC; m_open_files[fd].descriptor_flags = flags & O_CLOEXEC;
return fd; return fd;
} }
@ -135,6 +138,7 @@ namespace Kernel
auto socket = TRY(NetworkManager::get().create_socket(sock_domain, sock_type, 0777, m_credentials.euid(), m_credentials.egid())); auto socket = TRY(NetworkManager::get().create_socket(sock_domain, sock_type, 0777, m_credentials.euid(), m_credentials.egid()));
LockGuard _(m_mutex);
int fd = TRY(get_free_fd()); int fd = TRY(get_free_fd());
m_open_files[fd].description = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(socket, "<socket>"_sv), 0, O_RDWR | status_flags)); m_open_files[fd].description = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(socket, "<socket>"_sv), 0, O_RDWR | status_flags));
m_open_files[fd].descriptor_flags = descriptor_flags; m_open_files[fd].descriptor_flags = descriptor_flags;
@ -143,6 +147,8 @@ namespace Kernel
BAN::ErrorOr<void> OpenFileDescriptorSet::pipe(int fds[2]) BAN::ErrorOr<void> OpenFileDescriptorSet::pipe(int fds[2])
{ {
LockGuard _(m_mutex);
TRY(get_free_fd_pair(fds)); TRY(get_free_fd_pair(fds));
auto pipe = TRY(Pipe::create(m_credentials)); auto pipe = TRY(Pipe::create(m_credentials));
@ -159,6 +165,8 @@ namespace Kernel
if (fildes2 < 0 || fildes2 >= (int)m_open_files.size()) if (fildes2 < 0 || fildes2 >= (int)m_open_files.size())
return BAN::Error::from_errno(EBADF); return BAN::Error::from_errno(EBADF);
LockGuard _(m_mutex);
TRY(validate_fd(fildes)); TRY(validate_fd(fildes));
if (fildes == fildes2) if (fildes == fildes2)
return fildes; return fildes;
@ -179,6 +187,8 @@ namespace Kernel
BAN::ErrorOr<int> OpenFileDescriptorSet::fcntl(int fd, int cmd, int extra) BAN::ErrorOr<int> OpenFileDescriptorSet::fcntl(int fd, int cmd, int extra)
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
switch (cmd) switch (cmd)
@ -223,6 +233,8 @@ namespace Kernel
BAN::ErrorOr<off_t> OpenFileDescriptorSet::seek(int fd, off_t offset, int whence) BAN::ErrorOr<off_t> OpenFileDescriptorSet::seek(int fd, off_t offset, int whence)
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
off_t base_offset; off_t base_offset;
@ -252,18 +264,22 @@ namespace Kernel
BAN::ErrorOr<off_t> OpenFileDescriptorSet::tell(int fd) const BAN::ErrorOr<off_t> OpenFileDescriptorSet::tell(int fd) const
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
return m_open_files[fd].offset(); return m_open_files[fd].offset();
} }
BAN::ErrorOr<void> OpenFileDescriptorSet::truncate(int fd, off_t length) BAN::ErrorOr<void> OpenFileDescriptorSet::truncate(int fd, off_t length)
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
return m_open_files[fd].inode()->truncate(length); return m_open_files[fd].inode()->truncate(length);
} }
BAN::ErrorOr<void> OpenFileDescriptorSet::close(int fd) BAN::ErrorOr<void> OpenFileDescriptorSet::close(int fd)
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
if (m_open_files[fd].path() == "<pipe wr>"_sv) if (m_open_files[fd].path() == "<pipe wr>"_sv)
@ -280,12 +296,14 @@ namespace Kernel
void OpenFileDescriptorSet::close_all() void OpenFileDescriptorSet::close_all()
{ {
LockGuard _(m_mutex);
for (int fd = 0; fd < (int)m_open_files.size(); fd++) for (int fd = 0; fd < (int)m_open_files.size(); fd++)
(void)close(fd); (void)close(fd);
} }
void OpenFileDescriptorSet::close_cloexec() void OpenFileDescriptorSet::close_cloexec()
{ {
LockGuard _(m_mutex);
for (int fd = 0; fd < (int)m_open_files.size(); fd++) for (int fd = 0; fd < (int)m_open_files.size(); fd++)
{ {
if (validate_fd(fd).is_error()) if (validate_fd(fd).is_error())
@ -297,83 +315,155 @@ namespace Kernel
BAN::ErrorOr<size_t> OpenFileDescriptorSet::read(int fd, BAN::ByteSpan buffer) BAN::ErrorOr<size_t> OpenFileDescriptorSet::read(int fd, BAN::ByteSpan buffer)
{ {
BAN::RefPtr<Inode> inode;
bool is_nonblock;
off_t offset;
{
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
auto& open_file = m_open_files[fd]; auto& open_file = m_open_files[fd];
if (open_file.inode()->mode().ifsock()) if (open_file.inode()->mode().ifsock())
return recvfrom(fd, buffer, nullptr, nullptr); return recvfrom(fd, buffer, nullptr, nullptr);
if (!(open_file.status_flags() & O_RDONLY)) if (!(open_file.status_flags() & O_RDONLY))
return BAN::Error::from_errno(EBADF); return BAN::Error::from_errno(EBADF);
if ((open_file.status_flags() & O_NONBLOCK) && !open_file.inode()->can_read()) inode = open_file.inode();
is_nonblock = !!(open_file.status_flags() & O_NONBLOCK);
offset = open_file.offset();
}
if (inode->mode().ifsock())
return recvfrom(fd, buffer, nullptr, nullptr);
size_t nread;
{
LockGuard _(inode->m_mutex);
if (is_nonblock && !inode->can_read())
return 0; return 0;
const size_t nread = TRY(open_file.inode()->read(open_file.offset(), buffer)); nread = TRY(inode->read(offset, buffer));
open_file.offset() += nread; }
LockGuard _(m_mutex);
// NOTE: race condition with offset, its UB per POSIX
if (!validate_fd(fd).is_error())
m_open_files[fd].offset() = offset + nread;
return nread; return nread;
} }
BAN::ErrorOr<size_t> OpenFileDescriptorSet::write(int fd, BAN::ConstByteSpan buffer) BAN::ErrorOr<size_t> OpenFileDescriptorSet::write(int fd, BAN::ConstByteSpan buffer)
{ {
BAN::RefPtr<Inode> inode;
bool is_nonblock;
off_t offset;
{
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
auto& open_file = m_open_files[fd]; auto& open_file = m_open_files[fd];
if (open_file.inode()->mode().ifsock())
return sendto(fd, buffer, nullptr, 0);
if (!(open_file.status_flags() & O_WRONLY)) if (!(open_file.status_flags() & O_WRONLY))
return BAN::Error::from_errno(EBADF); return BAN::Error::from_errno(EBADF);
if ((open_file.status_flags() & O_NONBLOCK) && !open_file.inode()->can_write()) inode = open_file.inode();
is_nonblock = !!(open_file.status_flags() & O_NONBLOCK);
offset = (open_file.status_flags() & O_APPEND) ? inode->size() : open_file.offset();
}
if (inode->mode().ifsock())
return sendto(fd, buffer, nullptr, 0);
size_t nwrite;
{
LockGuard _(inode->m_mutex);
if (is_nonblock && !inode->can_write())
return BAN::Error::from_errno(EWOULDBLOCK); return BAN::Error::from_errno(EWOULDBLOCK);
if (open_file.status_flags() & O_APPEND) nwrite = TRY(inode->write(offset, buffer));
open_file.offset() = open_file.inode()->size(); }
const size_t nwrite = TRY(open_file.inode()->write(open_file.offset(), buffer));
open_file.offset() += nwrite; LockGuard _(m_mutex);
// NOTE: race condition with offset, its UB per POSIX
if (!validate_fd(fd).is_error())
m_open_files[fd].offset() = offset + nwrite;
return nwrite; return nwrite;
} }
BAN::ErrorOr<size_t> OpenFileDescriptorSet::read_dir_entries(int fd, struct dirent* list, size_t list_len) BAN::ErrorOr<size_t> OpenFileDescriptorSet::read_dir_entries(int fd, struct dirent* list, size_t list_len)
{ {
BAN::RefPtr<Inode> inode;
off_t offset;
{
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
auto& open_file = m_open_files[fd]; auto& open_file = m_open_files[fd];
if (!(open_file.status_flags() & O_RDONLY)) if (!(open_file.status_flags() & O_RDONLY))
return BAN::Error::from_errno(EACCES); return BAN::Error::from_errno(EACCES);
inode = open_file.inode();
offset = open_file.offset();
}
for (;;) for (;;)
{ {
auto ret = open_file.inode()->list_next_inodes(open_file.offset(), list, list_len); auto ret = inode->list_next_inodes(offset, list, list_len);
if (ret.is_error() && ret.error().get_error_code() != ENODATA) if (ret.is_error() && ret.error().get_error_code() != ENODATA)
return ret; return ret;
open_file.offset()++; offset++;
if (ret.is_error()) if (ret.is_error())
continue; continue;
LockGuard _(m_mutex);
// NOTE: race condition with offset, its UB per POSIX
if (!validate_fd(fd).is_error())
m_open_files[fd].offset() = offset;
return ret; return ret;
} }
} }
BAN::ErrorOr<size_t> OpenFileDescriptorSet::recvfrom(int fd, BAN::ByteSpan buffer, sockaddr* address, socklen_t* address_len) BAN::ErrorOr<size_t> OpenFileDescriptorSet::recvfrom(int fd, BAN::ByteSpan buffer, sockaddr* address, socklen_t* address_len)
{ {
BAN::RefPtr<Inode> inode;
bool is_nonblock;
{
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
auto& open_file = m_open_files[fd]; auto& open_file = m_open_files[fd];
if (!open_file.inode()->mode().ifsock()) if (!open_file.inode()->mode().ifsock())
return BAN::Error::from_errno(ENOTSOCK); return BAN::Error::from_errno(ENOTSOCK);
LockGuard _(open_file.inode()->m_mutex); inode = open_file.inode();
if ((open_file.status_flags() & O_NONBLOCK) && !open_file.inode()->can_read()) is_nonblock = !!(open_file.status_flags() & O_NONBLOCK);
}
LockGuard _(inode->m_mutex);
if (is_nonblock && !inode->can_read())
return BAN::Error::from_errno(EWOULDBLOCK); return BAN::Error::from_errno(EWOULDBLOCK);
return open_file.inode()->recvfrom(buffer, address, address_len); return inode->recvfrom(buffer, address, address_len);
} }
BAN::ErrorOr<size_t> OpenFileDescriptorSet::sendto(int fd, BAN::ConstByteSpan buffer, const sockaddr* address, socklen_t address_len) BAN::ErrorOr<size_t> OpenFileDescriptorSet::sendto(int fd, BAN::ConstByteSpan buffer, const sockaddr* address, socklen_t address_len)
{ {
BAN::RefPtr<Inode> inode;
bool is_nonblock;
{
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
auto& open_file = m_open_files[fd]; auto& open_file = m_open_files[fd];
if (!open_file.inode()->mode().ifsock()) if (!open_file.inode()->mode().ifsock())
return BAN::Error::from_errno(ENOTSOCK); return BAN::Error::from_errno(ENOTSOCK);
if ((open_file.status_flags() & O_NONBLOCK) && !open_file.inode()->can_write()) inode = open_file.inode();
return BAN::Error::from_errno(EWOULDBLOCK); is_nonblock = !!(open_file.status_flags() & O_NONBLOCK);
}
LockGuard _(open_file.inode()->m_mutex); LockGuard _(inode->m_mutex);
if (is_nonblock && !inode->can_write())
return BAN::Error::from_errno(EWOULDBLOCK);
size_t total_sent = 0; size_t total_sent = 0;
while (total_sent < buffer.size()) while (total_sent < buffer.size())
{ {
if ((open_file.status_flags() & O_NONBLOCK) && !open_file.inode()->can_write()) if (is_nonblock && !inode->can_write())
return total_sent; return total_sent;
const size_t nsend = TRY(open_file.inode()->sendto(buffer.slice(total_sent), address, address_len)); const size_t nsend = TRY(inode->sendto(buffer.slice(total_sent), address, address_len));
if (nsend == 0) if (nsend == 0)
return 0; return 0;
total_sent += nsend; total_sent += nsend;
@ -384,30 +474,37 @@ namespace Kernel
BAN::ErrorOr<VirtualFileSystem::File> OpenFileDescriptorSet::file_of(int fd) const BAN::ErrorOr<VirtualFileSystem::File> OpenFileDescriptorSet::file_of(int fd) const
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
return TRY(m_open_files[fd].description->file.clone()); return TRY(m_open_files[fd].description->file.clone());
} }
BAN::ErrorOr<BAN::StringView> OpenFileDescriptorSet::path_of(int fd) const BAN::ErrorOr<BAN::String> OpenFileDescriptorSet::path_of(int fd) const
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
return m_open_files[fd].path(); BAN::String path;
TRY(path.append(m_open_files[fd].path()));
return path;
} }
BAN::ErrorOr<BAN::RefPtr<Inode>> OpenFileDescriptorSet::inode_of(int fd) BAN::ErrorOr<BAN::RefPtr<Inode>> OpenFileDescriptorSet::inode_of(int fd)
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
return m_open_files[fd].inode(); return m_open_files[fd].inode();
} }
BAN::ErrorOr<int> OpenFileDescriptorSet::status_flags_of(int fd) const BAN::ErrorOr<int> OpenFileDescriptorSet::status_flags_of(int fd) const
{ {
LockGuard _(m_mutex);
TRY(validate_fd(fd)); TRY(validate_fd(fd));
return m_open_files[fd].status_flags(); return m_open_files[fd].status_flags();
} }
BAN::ErrorOr<void> OpenFileDescriptorSet::validate_fd(int fd) const BAN::ErrorOr<void> OpenFileDescriptorSet::validate_fd(int fd) const
{ {
LockGuard _(m_mutex);
if (fd < 0 || fd >= (int)m_open_files.size()) if (fd < 0 || fd >= (int)m_open_files.size())
return BAN::Error::from_errno(EBADF); return BAN::Error::from_errno(EBADF);
if (!m_open_files[fd].description) if (!m_open_files[fd].description)
@ -417,6 +514,7 @@ namespace Kernel
BAN::ErrorOr<int> OpenFileDescriptorSet::get_free_fd() const BAN::ErrorOr<int> OpenFileDescriptorSet::get_free_fd() const
{ {
LockGuard _(m_mutex);
for (int fd = 0; fd < (int)m_open_files.size(); fd++) for (int fd = 0; fd < (int)m_open_files.size(); fd++)
if (!m_open_files[fd].description) if (!m_open_files[fd].description)
return fd; return fd;
@ -425,6 +523,7 @@ namespace Kernel
BAN::ErrorOr<void> OpenFileDescriptorSet::get_free_fd_pair(int fds[2]) const BAN::ErrorOr<void> OpenFileDescriptorSet::get_free_fd_pair(int fds[2]) const
{ {
LockGuard _(m_mutex);
size_t found = 0; size_t found = 0;
for (int fd = 0; fd < (int)m_open_files.size(); fd++) for (int fd = 0; fd < (int)m_open_files.size(); fd++)
{ {