#include #include #include #include namespace Kernel { OpenFileDescriptorSet::OpenFileDescriptorSet(const Credentials& credentials) : m_credentials(credentials) { } OpenFileDescriptorSet::~OpenFileDescriptorSet() { close_all(); } OpenFileDescriptorSet& OpenFileDescriptorSet::operator=(OpenFileDescriptorSet&& other) { for (size_t i = 0; i < m_open_files.size(); i++) m_open_files[i] = BAN::move(other.m_open_files[i]); return *this; } BAN::ErrorOr OpenFileDescriptorSet::clone_from(const OpenFileDescriptorSet& other) { close_all(); for (int fd = 0; fd < (int)other.m_open_files.size(); fd++) { if (other.validate_fd(fd).is_error()) continue; auto& open_file = other.m_open_files[fd]; auto result = BAN::RefPtr::create(open_file->inode, open_file->path, open_file->offset, open_file->flags); if (result.is_error()) { close_all(); return result.error(); } m_open_files[fd] = result.release_value(); if (m_open_files[fd]->flags & O_WRONLY && m_open_files[fd]->inode->is_pipe()) ((Pipe*)m_open_files[fd]->inode.ptr())->clone_writing(); } return {}; } BAN::ErrorOr 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)) 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); auto file = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, flags)); if (flags & O_TRUNC) TRY(file.inode->truncate(0)); int fd = TRY(get_free_fd()); m_open_files[fd] = TRY(BAN::RefPtr::create(file.inode, BAN::move(file.canonical_path), 0, flags)); return fd; } BAN::ErrorOr OpenFileDescriptorSet::pipe(int fds[2]) { TRY(get_free_fd_pair(fds)); auto pipe = TRY(Pipe::create(m_credentials)); m_open_files[fds[0]] = TRY(BAN::RefPtr::create(pipe, ""sv, 0, O_RDONLY)); m_open_files[fds[1]] = TRY(BAN::RefPtr::create(pipe, ""sv, 0, O_WRONLY)); return {}; } BAN::ErrorOr OpenFileDescriptorSet::dup(int fildes) { TRY(validate_fd(fildes)); int result = TRY(get_free_fd()); m_open_files[result] = m_open_files[fildes]; if (m_open_files[result]->flags & O_WRONLY && m_open_files[result]->inode->is_pipe()) ((Pipe*)m_open_files[result]->inode.ptr())->clone_writing(); return result; } BAN::ErrorOr OpenFileDescriptorSet::dup2(int fildes, int fildes2) { if (fildes2 < 0 || fildes2 >= (int)m_open_files.size()) return BAN::Error::from_errno(EBADF); TRY(validate_fd(fildes)); if (fildes == fildes2) return fildes; (void)close(fildes2); m_open_files[fildes2] = m_open_files[fildes]; m_open_files[fildes2]->flags &= ~O_CLOEXEC; if (m_open_files[fildes]->flags & O_WRONLY && m_open_files[fildes]->inode->is_pipe()) ((Pipe*)m_open_files[fildes]->inode.ptr())->clone_writing(); return fildes; } BAN::ErrorOr OpenFileDescriptorSet::fcntl(int fd, int cmd, int extra) { TRY(validate_fd(fd)); constexpr int creation_flags = O_CLOEXEC | O_CREAT | O_DIRECTORY | O_EXCL | O_NOCTTY | O_NOFOLLOW | O_TRUNC | O_TTY_INIT; switch (cmd) { case F_GETFD: return m_open_files[fd]->flags; case F_SETFD: // FIXME: validate permissions to set access flags m_open_files[fd]->flags = extra; return 0; case F_GETFL: return m_open_files[fd]->flags & ~creation_flags; case F_SETFL: m_open_files[fd]->flags |= extra & ~(O_ACCMODE | creation_flags); return 0; default: break; } return BAN::Error::from_errno(ENOTSUP); } BAN::ErrorOr OpenFileDescriptorSet::seek(int fd, off_t offset, int whence) { TRY(validate_fd(fd)); off_t new_offset = 0; switch (whence) { case SEEK_CUR: new_offset = m_open_files[fd]->offset + offset; break; case SEEK_END: new_offset = m_open_files[fd]->inode->size() - offset; break; case SEEK_SET: new_offset = offset; break; default: return BAN::Error::from_errno(EINVAL); } if (new_offset < 0) return BAN::Error::from_errno(EINVAL); m_open_files[fd]->offset = new_offset; return {}; } BAN::ErrorOr OpenFileDescriptorSet::tell(int fd) const { TRY(validate_fd(fd)); return m_open_files[fd]->offset; } BAN::ErrorOr OpenFileDescriptorSet::fstat(int fd, struct stat* out) const { TRY(validate_fd(fd)); auto inode = m_open_files[fd]->inode; out->st_dev = inode->dev(); out->st_ino = inode->ino(); out->st_mode = inode->mode().mode; out->st_nlink = inode->nlink(); out->st_uid = inode->uid(); out->st_gid = inode->gid(); out->st_rdev = inode->rdev(); out->st_size = inode->size(); out->st_atim = inode->atime(); out->st_mtim = inode->mtime(); out->st_ctim = inode->ctime(); out->st_blksize = inode->blksize(); out->st_blocks = inode->blocks(); return {}; } BAN::ErrorOr OpenFileDescriptorSet::close(int fd) { TRY(validate_fd(fd)); if (m_open_files[fd]->flags & O_WRONLY && m_open_files[fd]->inode->is_pipe()) ((Pipe*)m_open_files[fd]->inode.ptr())->close_writing(); m_open_files[fd].clear(); return {}; } void OpenFileDescriptorSet::close_all() { for (int fd = 0; fd < (int)m_open_files.size(); fd++) (void)close(fd); } void OpenFileDescriptorSet::close_cloexec() { for (int fd = 0; fd < (int)m_open_files.size(); fd++) { if (validate_fd(fd).is_error()) continue; if (m_open_files[fd]->flags & O_CLOEXEC) (void)close(fd); } } BAN::ErrorOr OpenFileDescriptorSet::read(int fd, void* buffer, size_t count) { TRY(validate_fd(fd)); auto& open_file = m_open_files[fd]; if ((open_file->flags & O_NONBLOCK) && !open_file->inode->has_data()) return 0; size_t nread = TRY(open_file->inode->read(open_file->offset, buffer, count)); open_file->offset += nread; return nread; } BAN::ErrorOr OpenFileDescriptorSet::write(int fd, const void* buffer, size_t count) { TRY(validate_fd(fd)); auto& open_file = m_open_files[fd]; if (open_file->flags & O_APPEND) open_file->offset = open_file->inode->size(); size_t nwrite = TRY(open_file->inode->write(open_file->offset, buffer, count)); open_file->offset += nwrite; return nwrite; } BAN::ErrorOr OpenFileDescriptorSet::read_dir_entries(int fd, DirectoryEntryList* list, size_t list_size) { TRY(validate_fd(fd)); auto& open_file = m_open_files[fd]; TRY(open_file->inode->directory_read_next_entries(open_file->offset, list, list_size)); open_file->offset++; return {}; } BAN::ErrorOr OpenFileDescriptorSet::path_of(int fd) const { TRY(validate_fd(fd)); return m_open_files[fd]->path.sv(); } BAN::ErrorOr> OpenFileDescriptorSet::inode_of(int fd) { TRY(validate_fd(fd)); return m_open_files[fd]->inode; } BAN::ErrorOr OpenFileDescriptorSet::validate_fd(int fd) const { if (fd < 0 || fd >= (int)m_open_files.size()) return BAN::Error::from_errno(EBADF); if (!m_open_files[fd]) return BAN::Error::from_errno(EBADF); return {}; } BAN::ErrorOr OpenFileDescriptorSet::get_free_fd() const { for (int fd = 0; fd < (int)m_open_files.size(); fd++) if (!m_open_files[fd]) return fd; return BAN::Error::from_errno(EMFILE); } BAN::ErrorOr OpenFileDescriptorSet::get_free_fd_pair(int fds[2]) const { size_t found = 0; for (int fd = 0; fd < (int)m_open_files.size(); fd++) { if (!m_open_files[fd]) fds[found++] = fd; if (found == 2) return {}; } return BAN::Error::from_errno(EMFILE); } }