Serenity Operating System
at hosted 407 lines 11 kB view raw
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}