Serenity Operating System
at portability 403 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 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}