Serenity Operating System
1/*
2 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/StringBuilder.h>
28#include <Kernel/FileSystem/DevPtsFS.h>
29#include <Kernel/FileSystem/VirtualFileSystem.h>
30#include <Kernel/TTY/SlavePTY.h>
31
32namespace Kernel {
33
34NonnullRefPtr<DevPtsFS> DevPtsFS::create()
35{
36 return adopt(*new DevPtsFS);
37}
38
39DevPtsFS::DevPtsFS()
40{
41}
42
43DevPtsFS::~DevPtsFS()
44{
45}
46
47static HashTable<unsigned>* ptys;
48
49bool DevPtsFS::initialize()
50{
51 if (ptys == nullptr) {
52 ptys = new HashTable<unsigned>();
53 }
54
55 m_root_inode = adopt(*new DevPtsFSInode(*this, 1));
56 m_root_inode->m_metadata.inode = { fsid(), 1 };
57 m_root_inode->m_metadata.mode = 0040555;
58 m_root_inode->m_metadata.uid = 0;
59 m_root_inode->m_metadata.gid = 0;
60 m_root_inode->m_metadata.size = 0;
61 m_root_inode->m_metadata.mtime = mepoch;
62
63 return true;
64}
65
66static unsigned inode_index_to_pty_index(unsigned inode_index)
67{
68 ASSERT(inode_index > 1);
69 return inode_index - 2;
70}
71
72static unsigned pty_index_to_inode_index(unsigned pty_index)
73{
74 return pty_index + 2;
75}
76
77InodeIdentifier DevPtsFS::root_inode() const
78{
79 return { fsid(), 1 };
80}
81
82KResultOr<NonnullRefPtr<Inode>> DevPtsFS::create_inode(InodeIdentifier, const String&, mode_t, off_t, dev_t, uid_t, gid_t)
83{
84 return KResult(-EROFS);
85}
86
87KResult DevPtsFS::create_directory(InodeIdentifier, const String&, mode_t, uid_t, gid_t)
88{
89 return KResult(-EROFS);
90}
91
92RefPtr<Inode> DevPtsFS::get_inode(InodeIdentifier inode_id) const
93{
94 if (inode_id.index() == 1)
95 return m_root_inode;
96
97 unsigned pty_index = inode_index_to_pty_index(inode_id.index());
98 auto* device = Device::get_device(201, pty_index);
99 ASSERT(device);
100
101 auto inode = adopt(*new DevPtsFSInode(const_cast<DevPtsFS&>(*this), inode_id.index()));
102 inode->m_metadata.inode = inode_id;
103 inode->m_metadata.size = 0;
104 inode->m_metadata.uid = device->uid();
105 inode->m_metadata.gid = device->gid();
106 inode->m_metadata.mode = 0020600;
107 inode->m_metadata.major_device = device->major();
108 inode->m_metadata.minor_device = device->minor();
109 inode->m_metadata.mtime = mepoch;
110
111 return inode;
112}
113
114void DevPtsFS::register_slave_pty(SlavePTY& slave_pty)
115{
116 ptys->set(slave_pty.index());
117}
118
119void DevPtsFS::unregister_slave_pty(SlavePTY& slave_pty)
120{
121 ptys->remove(slave_pty.index());
122}
123
124DevPtsFSInode::DevPtsFSInode(DevPtsFS& fs, unsigned index)
125 : Inode(fs, index)
126{
127}
128
129DevPtsFSInode::~DevPtsFSInode()
130{
131}
132
133ssize_t DevPtsFSInode::read_bytes(off_t, ssize_t, u8*, FileDescription*) const
134{
135 ASSERT_NOT_REACHED();
136}
137
138ssize_t DevPtsFSInode::write_bytes(off_t, ssize_t, const u8*, FileDescription*)
139{
140 ASSERT_NOT_REACHED();
141}
142
143InodeMetadata DevPtsFSInode::metadata() const
144{
145 return m_metadata;
146}
147
148bool DevPtsFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) const
149{
150 if (identifier().index() > 1)
151 return false;
152
153 callback({ ".", 1, identifier(), 0 });
154 callback({ "..", 2, identifier(), 0 });
155
156 for (unsigned pty_index : *ptys) {
157 String name = String::number(pty_index);
158 InodeIdentifier identifier = { fsid(), pty_index_to_inode_index(pty_index) };
159 callback({ name.characters(), name.length(), identifier, 0 });
160 }
161
162 return true;
163}
164
165size_t DevPtsFSInode::directory_entry_count() const
166{
167 ASSERT(identifier().index() == 1);
168
169 return 2 + ptys->size();
170}
171
172RefPtr<Inode> DevPtsFSInode::lookup(StringView name)
173{
174 ASSERT(identifier().index() == 1);
175
176 if (name == "." || name == "..")
177 return fs().get_inode(identifier());
178
179 bool ok;
180 unsigned pty_index = name.to_uint(ok);
181 if (ok && ptys->contains(pty_index)) {
182 return fs().get_inode({ fsid(), pty_index_to_inode_index(pty_index) });
183 }
184
185 return {};
186}
187
188void DevPtsFSInode::flush_metadata()
189{
190}
191
192KResult DevPtsFSInode::add_child(InodeIdentifier, const StringView&, mode_t)
193{
194 return KResult(-EROFS);
195}
196
197KResult DevPtsFSInode::remove_child(const StringView&)
198{
199 return KResult(-EROFS);
200}
201
202KResult DevPtsFSInode::chmod(mode_t)
203{
204 return KResult(-EPERM);
205}
206
207KResult DevPtsFSInode::chown(uid_t, gid_t)
208{
209 return KResult(-EPERM);
210}
211
212}