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 auto result = prepare_to_write_data();
211 if (result.is_error())
212 return result;
213
214 off_t old_size = m_metadata.size;
215 off_t new_size = m_metadata.size;
216 if ((offset + size) > new_size)
217 new_size = offset + size;
218
219 if (new_size > old_size) {
220 if (m_content.has_value() && m_content.value().capacity() >= (size_t)new_size) {
221 m_content.value().set_size(new_size);
222 } else {
223 // Grow the content buffer 2x the new sizeto accomodate repeating write() calls.
224 // Note that we're not actually committing physical memory to the buffer
225 // until it's needed. We only grow VM here.
226
227 // FIXME: Fix this so that no memcpy() is necessary, and we can just grow the
228 // KBuffer and it will add physical pages as needed while keeping the
229 // existing ones.
230 auto tmp = KBuffer::create_with_size(new_size * 2);
231 tmp.set_size(new_size);
232 if (m_content.has_value())
233 memcpy(tmp.data(), m_content.value().data(), old_size);
234 m_content = move(tmp);
235 }
236 m_metadata.size = new_size;
237 set_metadata_dirty(true);
238 set_metadata_dirty(false);
239 inode_size_changed(old_size, new_size);
240 }
241
242 memcpy(m_content.value().data() + offset, buffer, size);
243 inode_contents_changed(offset, size, buffer);
244
245 return size;
246}
247
248RefPtr<Inode> TmpFSInode::lookup(StringView name)
249{
250 LOCKER(m_lock);
251 ASSERT(is_directory());
252
253 if (name == ".")
254 return fs().get_inode(identifier());
255 if (name == "..")
256 return fs().get_inode(m_parent);
257
258 auto it = m_children.find(name);
259 if (it == m_children.end())
260 return {};
261 return fs().get_inode(it->value.entry.inode);
262}
263
264size_t TmpFSInode::directory_entry_count() const
265{
266 LOCKER(m_lock);
267 ASSERT(is_directory());
268 return 2 + m_children.size();
269}
270
271void TmpFSInode::flush_metadata()
272{
273 // We don't really have any metadata that could become dirty.
274 // The only reason we even call set_metadata_dirty() is
275 // to let the watchers know we have updates. Once that is
276 // switched to a different mechanism, we can stop ever marking
277 // our metadata as dirty at all.
278 set_metadata_dirty(false);
279}
280
281KResult TmpFSInode::chmod(mode_t mode)
282{
283 LOCKER(m_lock);
284
285 m_metadata.mode = mode;
286 set_metadata_dirty(true);
287 set_metadata_dirty(false);
288 return KSuccess;
289}
290
291KResult TmpFSInode::chown(uid_t uid, gid_t gid)
292{
293 LOCKER(m_lock);
294
295 m_metadata.uid = uid;
296 m_metadata.gid = gid;
297 set_metadata_dirty(true);
298 set_metadata_dirty(false);
299 return KSuccess;
300}
301
302KResult TmpFSInode::add_child(InodeIdentifier child_id, const StringView& name, mode_t)
303{
304 LOCKER(m_lock);
305 ASSERT(is_directory());
306 ASSERT(child_id.fsid() == fsid());
307
308 String owned_name = name;
309 FS::DirectoryEntry entry = { owned_name.characters(), owned_name.length(), child_id, 0 };
310 auto child_tmp = fs().get_inode(child_id);
311 auto child = static_ptr_cast<TmpFSInode>(child_tmp.release_nonnull());
312
313 m_children.set(owned_name, { entry, move(child) });
314 set_metadata_dirty(true);
315 set_metadata_dirty(false);
316 return KSuccess;
317}
318
319KResult TmpFSInode::remove_child(const StringView& name)
320{
321 LOCKER(m_lock);
322 ASSERT(is_directory());
323
324 if (name == "." || name == "..")
325 return KSuccess;
326
327 auto it = m_children.find(name);
328 if (it == m_children.end())
329 return KResult(-ENOENT);
330 m_children.remove(it);
331 set_metadata_dirty(true);
332 set_metadata_dirty(false);
333 return KSuccess;
334}
335
336KResult TmpFSInode::truncate(u64 size)
337{
338 LOCKER(m_lock);
339 ASSERT(!is_directory());
340
341 if (size == 0)
342 m_content.clear();
343 else if (!m_content.has_value()) {
344 m_content = KBuffer::create_with_size(size);
345 } else if (static_cast<size_t>(size) < m_content.value().capacity()) {
346 size_t prev_size = m_metadata.size;
347 m_content.value().set_size(size);
348 if (prev_size < static_cast<size_t>(size))
349 memset(m_content.value().data() + prev_size, 0, size - prev_size);
350 } else {
351 size_t prev_size = m_metadata.size;
352 KBuffer tmp = KBuffer::create_with_size(size);
353 memcpy(tmp.data(), m_content.value().data(), prev_size);
354 m_content = move(tmp);
355 }
356
357 size_t old_size = m_metadata.size;
358 m_metadata.size = size;
359 set_metadata_dirty(true);
360 set_metadata_dirty(false);
361
362 if (old_size != (size_t)size) {
363 inode_size_changed(old_size, size);
364 if (m_content.has_value())
365 inode_contents_changed(0, size, m_content.value().data());
366 }
367
368 return KSuccess;
369}
370
371int TmpFSInode::set_atime(time_t time)
372{
373 LOCKER(m_lock);
374
375 m_metadata.atime = time;
376 set_metadata_dirty(true);
377 set_metadata_dirty(false);
378 return KSuccess;
379}
380
381int TmpFSInode::set_ctime(time_t time)
382{
383 LOCKER(m_lock);
384
385 m_metadata.ctime = time;
386 set_metadata_dirty(true);
387 set_metadata_dirty(false);
388 return KSuccess;
389}
390
391int TmpFSInode::set_mtime(time_t time)
392{
393 LOCKER(m_lock);
394
395 m_metadata.mtime = time;
396 set_metadata_dirty(true);
397 set_metadata_dirty(false);
398 return KSuccess;
399}
400
401void TmpFSInode::one_ref_left()
402{
403 // Destroy ourselves.
404 fs().unregister_inode(identifier());
405}
406
407}