Serenity Operating System
at hosted 235 lines 6.6 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@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/NonnullRefPtrVector.h> 28#include <AK/StringBuilder.h> 29#include <AK/StringView.h> 30#include <Kernel/FileSystem/Custody.h> 31#include <Kernel/FileSystem/Inode.h> 32#include <Kernel/FileSystem/InodeWatcher.h> 33#include <Kernel/FileSystem/VirtualFileSystem.h> 34#include <Kernel/Net/LocalSocket.h> 35#include <Kernel/VM/SharedInodeVMObject.h> 36 37namespace Kernel { 38 39InlineLinkedList<Inode>& all_inodes() 40{ 41 static InlineLinkedList<Inode>* list; 42 if (!list) 43 list = new InlineLinkedList<Inode>; 44 return *list; 45} 46 47void Inode::sync() 48{ 49 NonnullRefPtrVector<Inode, 32> inodes; 50 { 51 InterruptDisabler disabler; 52 for (auto& inode : all_inodes()) { 53 if (inode.is_metadata_dirty()) 54 inodes.append(inode); 55 } 56 } 57 58 for (auto& inode : inodes) { 59 ASSERT(inode.is_metadata_dirty()); 60 inode.flush_metadata(); 61 } 62} 63 64ByteBuffer Inode::read_entire(FileDescription* descriptor) const 65{ 66 size_t initial_size = metadata().size ? metadata().size : 4096; 67 StringBuilder builder(initial_size); 68 69 ssize_t nread; 70 u8 buffer[4096]; 71 off_t offset = 0; 72 for (;;) { 73 nread = read_bytes(offset, sizeof(buffer), buffer, descriptor); 74 ASSERT(nread <= (ssize_t)sizeof(buffer)); 75 if (nread <= 0) 76 break; 77 builder.append((const char*)buffer, nread); 78 offset += nread; 79 if (nread < (ssize_t)sizeof(buffer)) 80 break; 81 } 82 if (nread < 0) { 83 klog() << "Inode::read_entire: ERROR: " << nread; 84 return nullptr; 85 } 86 87 return builder.to_byte_buffer(); 88} 89 90KResultOr<NonnullRefPtr<Custody>> Inode::resolve_as_link(Custody& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) const 91{ 92 // The default implementation simply treats the stored 93 // contents as a path and resolves that. That is, it 94 // behaves exactly how you would expect a symlink to work. 95 auto contents = read_entire(); 96 97 if (!contents) { 98 if (out_parent) 99 *out_parent = nullptr; 100 return KResult(-ENOENT); 101 } 102 103 auto path = StringView(contents.data(), contents.size()); 104 return VFS::the().resolve_path(path, base, out_parent, options, symlink_recursion_level); 105} 106 107unsigned Inode::fsid() const 108{ 109 return m_fs.fsid(); 110} 111 112Inode::Inode(FS& fs, unsigned index) 113 : m_fs(fs) 114 , m_index(index) 115{ 116 all_inodes().append(this); 117} 118 119Inode::~Inode() 120{ 121 all_inodes().remove(this); 122} 123 124void Inode::will_be_destroyed() 125{ 126 if (m_metadata_dirty) 127 flush_metadata(); 128} 129 130void Inode::inode_contents_changed(off_t offset, ssize_t size, const u8* data) 131{ 132 if (m_shared_vmobject) 133 m_shared_vmobject->inode_contents_changed({}, offset, size, data); 134} 135 136void Inode::inode_size_changed(size_t old_size, size_t new_size) 137{ 138 if (m_shared_vmobject) 139 m_shared_vmobject->inode_size_changed({}, old_size, new_size); 140} 141 142int Inode::set_atime(time_t) 143{ 144 return -ENOTIMPL; 145} 146 147int Inode::set_ctime(time_t) 148{ 149 return -ENOTIMPL; 150} 151 152int Inode::set_mtime(time_t) 153{ 154 return -ENOTIMPL; 155} 156 157KResult Inode::increment_link_count() 158{ 159 return KResult(-ENOTIMPL); 160} 161 162KResult Inode::decrement_link_count() 163{ 164 return KResult(-ENOTIMPL); 165} 166 167void Inode::set_shared_vmobject(SharedInodeVMObject& vmobject) 168{ 169 m_shared_vmobject = vmobject.make_weak_ptr(); 170} 171 172bool Inode::bind_socket(LocalSocket& socket) 173{ 174 LOCKER(m_lock); 175 if (m_socket) 176 return false; 177 m_socket = socket; 178 return true; 179} 180 181bool Inode::unbind_socket() 182{ 183 LOCKER(m_lock); 184 if (!m_socket) 185 return false; 186 m_socket = nullptr; 187 return true; 188} 189 190void Inode::register_watcher(Badge<InodeWatcher>, InodeWatcher& watcher) 191{ 192 LOCKER(m_lock); 193 ASSERT(!m_watchers.contains(&watcher)); 194 m_watchers.set(&watcher); 195} 196 197void Inode::unregister_watcher(Badge<InodeWatcher>, InodeWatcher& watcher) 198{ 199 LOCKER(m_lock); 200 ASSERT(m_watchers.contains(&watcher)); 201 m_watchers.remove(&watcher); 202} 203 204void Inode::set_metadata_dirty(bool metadata_dirty) 205{ 206 if (m_metadata_dirty == metadata_dirty) 207 return; 208 209 m_metadata_dirty = metadata_dirty; 210 if (m_metadata_dirty) { 211 // FIXME: Maybe we should hook into modification events somewhere else, I'm not sure where. 212 // We don't always end up on this particular code path, for instance when writing to an ext2fs file. 213 LOCKER(m_lock); 214 for (auto& watcher : m_watchers) { 215 watcher->notify_inode_event({}, InodeWatcher::Event::Type::Modified); 216 } 217 } 218} 219 220KResult Inode::prepare_to_write_data() 221{ 222 // FIXME: It's a poor design that filesystems are expected to call this before writing out data. 223 // We should funnel everything through an interface at the VFS layer so this can happen from a single place. 224 LOCKER(m_lock); 225 if (fs().is_readonly()) 226 return KResult(-EROFS); 227 auto metadata = this->metadata(); 228 if (metadata.is_setuid() || metadata.is_setgid()) { 229 dbg() << "Inode::prepare_to_write_data(): Stripping SUID/SGID bits from " << identifier(); 230 return chmod(metadata.mode & ~(04000 | 02000)); 231 } 232 return KSuccess; 233} 234 235}