Serenity Operating System
1/*
2 * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/StringView.h>
8#include <Kernel/API/Ioctl.h>
9#include <Kernel/API/POSIX/errno.h>
10#include <Kernel/FileSystem/Inode.h>
11#include <Kernel/FileSystem/InodeFile.h>
12#include <Kernel/FileSystem/OpenFileDescription.h>
13#include <Kernel/FileSystem/VirtualFileSystem.h>
14#include <Kernel/Memory/PrivateInodeVMObject.h>
15#include <Kernel/Memory/SharedInodeVMObject.h>
16#include <Kernel/Process.h>
17
18namespace Kernel {
19
20InodeFile::InodeFile(NonnullRefPtr<Inode> inode)
21 : m_inode(move(inode))
22{
23}
24
25InodeFile::~InodeFile() = default;
26
27ErrorOr<size_t> InodeFile::read(OpenFileDescription& description, u64 offset, UserOrKernelBuffer& buffer, size_t count)
28{
29 if (Checked<off_t>::addition_would_overflow(offset, count))
30 return EOVERFLOW;
31
32 auto nread = TRY(m_inode->read_bytes(offset, count, buffer, &description));
33 if (nread > 0) {
34 Thread::current()->did_file_read(nread);
35 evaluate_block_conditions();
36 }
37 return nread;
38}
39
40ErrorOr<size_t> InodeFile::write(OpenFileDescription& description, u64 offset, UserOrKernelBuffer const& data, size_t count)
41{
42 if (Checked<off_t>::addition_would_overflow(offset, count))
43 return EOVERFLOW;
44
45 size_t nwritten = TRY(m_inode->write_bytes(offset, count, data, &description));
46 if (nwritten > 0) {
47 auto mtime_result = m_inode->update_timestamps({}, {}, kgettimeofday());
48 Thread::current()->did_file_write(nwritten);
49 evaluate_block_conditions();
50 if (mtime_result.is_error())
51 return mtime_result.release_error();
52 }
53 return nwritten;
54}
55
56ErrorOr<void> InodeFile::ioctl(OpenFileDescription& description, unsigned request, Userspace<void*> arg)
57{
58 switch (request) {
59 case FIBMAP: {
60 auto current_process_credentials = Process::current().credentials();
61 if (!current_process_credentials->is_superuser())
62 return EPERM;
63
64 auto user_block_number = static_ptr_cast<int*>(arg);
65 int block_number = 0;
66 TRY(copy_from_user(&block_number, user_block_number));
67
68 if (block_number < 0)
69 return EINVAL;
70
71 auto block_address = TRY(inode().get_block_address(block_number));
72 return copy_to_user(user_block_number, &block_address);
73 }
74 case FIONREAD: {
75 int remaining_bytes = inode().size() - description.offset();
76 return copy_to_user(static_ptr_cast<int*>(arg), &remaining_bytes);
77 }
78 default:
79 return EINVAL;
80 }
81}
82
83ErrorOr<NonnullLockRefPtr<Memory::VMObject>> InodeFile::vmobject_for_mmap(Process&, Memory::VirtualRange const& range, u64& offset, bool shared)
84{
85 if (shared)
86 return TRY(Memory::SharedInodeVMObject::try_create_with_inode_and_range(inode(), offset, range.size()));
87 return TRY(Memory::PrivateInodeVMObject::try_create_with_inode_and_range(inode(), offset, range.size()));
88}
89
90ErrorOr<NonnullOwnPtr<KString>> InodeFile::pseudo_path(OpenFileDescription const&) const
91{
92 // If it has an inode, then it has a path, and therefore the caller should have been able to get a custody at some point.
93 VERIFY_NOT_REACHED();
94}
95
96ErrorOr<void> InodeFile::truncate(u64 size)
97{
98 TRY(m_inode->truncate(size));
99 TRY(m_inode->update_timestamps({}, {}, kgettimeofday()));
100 return {};
101}
102
103ErrorOr<void> InodeFile::sync()
104{
105 m_inode->sync();
106 return {};
107}
108
109ErrorOr<void> InodeFile::chown(Credentials const& credentials, OpenFileDescription& description, UserID uid, GroupID gid)
110{
111 VERIFY(description.inode() == m_inode);
112 VERIFY(description.custody());
113 return VirtualFileSystem::the().chown(credentials, *description.custody(), uid, gid);
114}
115
116ErrorOr<void> InodeFile::chmod(Credentials const& credentials, OpenFileDescription& description, mode_t mode)
117{
118 VERIFY(description.inode() == m_inode);
119 VERIFY(description.custody());
120 return VirtualFileSystem::the().chmod(credentials, *description.custody(), mode);
121}
122
123bool InodeFile::is_regular_file() const
124{
125 return inode().metadata().is_regular_file();
126}
127
128}