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 <Kernel/FileSystem/TmpFS.h>
28#include <Kernel/Process.h>
29#include <Kernel/Thread.h>
30
31namespace Kernel {
32
33NonnullRefPtr<TmpFS> TmpFS::create()
34{
35 return adopt(*new TmpFS);
36}
37
38TmpFS::TmpFS()
39{
40}
41
42TmpFS::~TmpFS()
43{
44}
45
46bool TmpFS::initialize()
47{
48 m_root_inode = TmpFSInode::create_root(*this);
49 return true;
50}
51
52InodeIdentifier TmpFS::root_inode() const
53{
54 ASSERT(!m_root_inode.is_null());
55 return m_root_inode->identifier();
56}
57
58void TmpFS::register_inode(TmpFSInode& inode)
59{
60 LOCKER(m_lock);
61 ASSERT(inode.identifier().fsid() == fsid());
62
63 unsigned index = inode.identifier().index();
64 m_inodes.set(index, inode);
65}
66
67void TmpFS::unregister_inode(InodeIdentifier identifier)
68{
69 LOCKER(m_lock);
70 ASSERT(identifier.fsid() == fsid());
71
72 m_inodes.remove(identifier.index());
73}
74
75unsigned TmpFS::next_inode_index()
76{
77 LOCKER(m_lock);
78
79 return m_next_inode_index++;
80}
81
82RefPtr<Inode> TmpFS::get_inode(InodeIdentifier identifier) const
83{
84 LOCKER(m_lock);
85 ASSERT(identifier.fsid() == fsid());
86
87 auto it = m_inodes.find(identifier.index());
88 if (it == m_inodes.end())
89 return nullptr;
90 return it->value;
91}
92
93KResultOr<NonnullRefPtr<Inode>> TmpFS::create_inode(InodeIdentifier parent_id, const String& name, mode_t mode, off_t size, dev_t dev, uid_t uid, gid_t gid)
94{
95 LOCKER(m_lock);
96 ASSERT(parent_id.fsid() == fsid());
97
98 ASSERT(size == 0);
99 ASSERT(dev == 0);
100
101 struct timeval now;
102 kgettimeofday(now);
103
104 InodeMetadata metadata;
105 metadata.mode = mode;
106 metadata.uid = uid;
107 metadata.gid = gid;
108 metadata.atime = now.tv_sec;
109 metadata.ctime = now.tv_sec;
110 metadata.mtime = now.tv_sec;
111
112 auto inode = TmpFSInode::create(*this, metadata, parent_id);
113
114 auto it = m_inodes.find(parent_id.index());
115 ASSERT(it != m_inodes.end());
116 auto parent_inode = it->value;
117
118 auto result = parent_inode->add_child(inode->identifier(), name, mode);
119 if (result.is_error())
120 return result;
121
122 return inode;
123}
124
125KResult TmpFS::create_directory(InodeIdentifier parent_id, const String& name, mode_t mode, uid_t uid, gid_t gid)
126{
127 // Ensure it's a directory.
128 mode &= ~0170000;
129 mode |= 0040000;
130 auto result = create_inode(parent_id, name, mode, 0, 0, uid, gid);
131 if (result.is_error())
132 return result.error();
133 return KSuccess;
134}
135
136TmpFSInode::TmpFSInode(TmpFS& fs, InodeMetadata metadata, InodeIdentifier parent)
137 : Inode(fs, fs.next_inode_index())
138 , m_metadata(metadata)
139 , m_parent(parent)
140{
141 m_metadata.inode = identifier();
142}
143
144TmpFSInode::~TmpFSInode()
145{
146}
147
148NonnullRefPtr<TmpFSInode> TmpFSInode::create(TmpFS& fs, InodeMetadata metadata, InodeIdentifier parent)
149{
150 auto inode = adopt(*new TmpFSInode(fs, metadata, parent));
151 fs.register_inode(inode);
152 return inode;
153}
154
155NonnullRefPtr<TmpFSInode> TmpFSInode::create_root(TmpFS& fs)
156{
157 InodeMetadata metadata;
158 metadata.mode = 0041777;
159 return create(fs, metadata, { fs.fsid(), 1 });
160}
161
162InodeMetadata TmpFSInode::metadata() const
163{
164 LOCKER(m_lock);
165
166 return m_metadata;
167}
168
169bool TmpFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) const
170{
171 LOCKER(m_lock);
172
173 if (!is_directory())
174 return false;
175
176 callback({ ".", identifier(), 0 });
177 callback({ "..", m_parent, 0 });
178
179 for (auto& it : m_children)
180 callback(it.value.entry);
181 return true;
182}
183
184ssize_t TmpFSInode::read_bytes(off_t offset, ssize_t size, u8* buffer, FileDescription*) const
185{
186 LOCKER(m_lock);
187 ASSERT(!is_directory());
188 ASSERT(size >= 0);
189 ASSERT(offset >= 0);
190
191 if (!m_content.has_value())
192 return 0;
193
194 if (offset >= m_metadata.size)
195 return 0;
196
197 if (static_cast<off_t>(size) > m_metadata.size - offset)
198 size = m_metadata.size - offset;
199
200 memcpy(buffer, m_content.value().data() + offset, size);
201 return size;
202}
203
204ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, FileDescription*)
205{
206 LOCKER(m_lock);
207 ASSERT(!is_directory());
208 ASSERT(offset >= 0);
209
210 off_t old_size = m_metadata.size;
211 off_t new_size = m_metadata.size;
212 if ((offset + size) > new_size)
213 new_size = offset + size;
214
215 if (new_size > old_size) {
216 if (m_content.has_value() && m_content.value().capacity() >= (size_t)new_size) {
217 m_content.value().set_size(new_size);
218 } else {
219 // Grow the content buffer 2x the new sizeto accomodate repeating write() calls.
220 // Note that we're not actually committing physical memory to the buffer
221 // until it's needed. We only grow VM here.
222
223 // FIXME: Fix this so that no memcpy() is necessary, and we can just grow the
224 // KBuffer and it will add physical pages as needed while keeping the
225 // existing ones.
226 auto tmp = KBuffer::create_with_size(new_size * 2);
227 tmp.set_size(new_size);
228 if (m_content.has_value())
229 memcpy(tmp.data(), m_content.value().data(), old_size);
230 m_content = move(tmp);
231 }
232 m_metadata.size = new_size;
233 set_metadata_dirty(true);
234 set_metadata_dirty(false);
235 inode_size_changed(old_size, new_size);
236 }
237
238 memcpy(m_content.value().data() + offset, buffer, size);
239 inode_contents_changed(offset, size, buffer);
240
241 return size;
242}
243
244RefPtr<Inode> TmpFSInode::lookup(StringView name)
245{
246 LOCKER(m_lock);
247 ASSERT(is_directory());
248
249 if (name == ".")
250 return fs().get_inode(identifier());
251 if (name == "..")
252 return fs().get_inode(m_parent);
253
254 auto it = m_children.find(name);
255 if (it == m_children.end())
256 return {};
257 return fs().get_inode(it->value.entry.inode);
258}
259
260size_t TmpFSInode::directory_entry_count() const
261{
262 LOCKER(m_lock);
263 ASSERT(is_directory());
264 return 2 + m_children.size();
265}
266
267void TmpFSInode::flush_metadata()
268{
269 // We don't really have any metadata that could become dirty.
270 // The only reason we even call set_metadata_dirty() is
271 // to let the watchers know we have updates. Once that is
272 // switched to a different mechanism, we can stop ever marking
273 // our metadata as dirty at all.
274 set_metadata_dirty(false);
275}
276
277KResult TmpFSInode::chmod(mode_t mode)
278{
279 LOCKER(m_lock);
280
281 m_metadata.mode = mode;
282 set_metadata_dirty(true);
283 set_metadata_dirty(false);
284 return KSuccess;
285}
286
287KResult TmpFSInode::chown(uid_t uid, gid_t gid)
288{
289 LOCKER(m_lock);
290
291 m_metadata.uid = uid;
292 m_metadata.gid = gid;
293 set_metadata_dirty(true);
294 set_metadata_dirty(false);
295 return KSuccess;
296}
297
298KResult TmpFSInode::add_child(InodeIdentifier child_id, const StringView& name, mode_t)
299{
300 LOCKER(m_lock);
301 ASSERT(is_directory());
302 ASSERT(child_id.fsid() == fsid());
303
304 String owned_name = name;
305 FS::DirectoryEntry entry = { owned_name.characters(), owned_name.length(), child_id, 0 };
306 RefPtr<Inode> child_tmp = fs().get_inode(child_id);
307 NonnullRefPtr<TmpFSInode> child = static_cast<NonnullRefPtr<TmpFSInode>>(child_tmp.release_nonnull());
308
309 m_children.set(owned_name, { entry, move(child) });
310 set_metadata_dirty(true);
311 set_metadata_dirty(false);
312 return KSuccess;
313}
314
315KResult TmpFSInode::remove_child(const StringView& name)
316{
317 LOCKER(m_lock);
318 ASSERT(is_directory());
319
320 if (name == "." || name == "..")
321 return KSuccess;
322
323 auto it = m_children.find(name);
324 if (it == m_children.end())
325 return KResult(-ENOENT);
326 m_children.remove(it);
327 set_metadata_dirty(true);
328 set_metadata_dirty(false);
329 return KSuccess;
330}
331
332KResult TmpFSInode::truncate(u64 size)
333{
334 LOCKER(m_lock);
335 ASSERT(!is_directory());
336
337 if (size == 0)
338 m_content.clear();
339 else if (!m_content.has_value()) {
340 m_content = KBuffer::create_with_size(size);
341 } else if (static_cast<size_t>(size) < m_content.value().capacity()) {
342 size_t prev_size = m_metadata.size;
343 m_content.value().set_size(size);
344 if (prev_size < static_cast<size_t>(size))
345 memset(m_content.value().data() + prev_size, 0, size - prev_size);
346 } else {
347 size_t prev_size = m_metadata.size;
348 KBuffer tmp = KBuffer::create_with_size(size);
349 memcpy(tmp.data(), m_content.value().data(), prev_size);
350 m_content = move(tmp);
351 }
352
353 size_t old_size = m_metadata.size;
354 m_metadata.size = size;
355 set_metadata_dirty(true);
356 set_metadata_dirty(false);
357
358 if (old_size != (size_t)size) {
359 inode_size_changed(old_size, size);
360 if (m_content.has_value())
361 inode_contents_changed(0, size, m_content.value().data());
362 }
363
364 return KSuccess;
365}
366
367int TmpFSInode::set_atime(time_t time)
368{
369 LOCKER(m_lock);
370
371 m_metadata.atime = time;
372 set_metadata_dirty(true);
373 set_metadata_dirty(false);
374 return KSuccess;
375}
376
377int TmpFSInode::set_ctime(time_t time)
378{
379 LOCKER(m_lock);
380
381 m_metadata.ctime = time;
382 set_metadata_dirty(true);
383 set_metadata_dirty(false);
384 return KSuccess;
385}
386
387int TmpFSInode::set_mtime(time_t time)
388{
389 LOCKER(m_lock);
390
391 m_metadata.mtime = time;
392 set_metadata_dirty(true);
393 set_metadata_dirty(false);
394 return KSuccess;
395}
396
397void TmpFSInode::one_ref_left()
398{
399 // Destroy ourselves.
400 fs().unregister_inode(identifier());
401}
402
403}