Serenity Operating System
at hosted 872 lines 31 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/FileSystemPath.h> 28#include <AK/StringBuilder.h> 29#include <Kernel/Devices/BlockDevice.h> 30#include <Kernel/FileSystem/Custody.h> 31#include <Kernel/FileSystem/FileBackedFileSystem.h> 32#include <Kernel/FileSystem/FileDescription.h> 33#include <Kernel/FileSystem/FileSystem.h> 34#include <Kernel/FileSystem/VirtualFileSystem.h> 35#include <Kernel/KSyms.h> 36#include <Kernel/Process.h> 37#include <LibC/errno_numbers.h> 38 39//#define VFS_DEBUG 40 41namespace Kernel { 42 43static VFS* s_the; 44static constexpr int symlink_recursion_limit { 5 }; // FIXME: increase? 45 46VFS& VFS::the() 47{ 48 ASSERT(s_the); 49 return *s_the; 50} 51 52VFS::VFS() 53{ 54#ifdef VFS_DEBUG 55 klog() << "VFS: Constructing VFS"; 56#endif 57 s_the = this; 58} 59 60VFS::~VFS() 61{ 62} 63 64InodeIdentifier VFS::root_inode_id() const 65{ 66 ASSERT(m_root_inode); 67 return m_root_inode->identifier(); 68} 69 70KResult VFS::mount(FS& file_system, Custody& mount_point, int flags) 71{ 72 auto& inode = mount_point.inode(); 73 dbg() << "VFS: Mounting " << file_system.class_name() << " at " << mount_point.absolute_path() << " (inode: " << inode.identifier() << ") with flags " << flags; 74 // FIXME: check that this is not already a mount point 75 Mount mount { file_system, &mount_point, flags }; 76 m_mounts.append(move(mount)); 77 return KSuccess; 78} 79 80KResult VFS::bind_mount(Custody& source, Custody& mount_point, int flags) 81{ 82 dbg() << "VFS: Bind-mounting " << source.absolute_path() << " at " << mount_point.absolute_path(); 83 // FIXME: check that this is not already a mount point 84 Mount mount { source.inode(), mount_point, flags }; 85 m_mounts.append(move(mount)); 86 return KSuccess; 87} 88 89KResult VFS::unmount(InodeIdentifier guest_inode_id) 90{ 91 LOCKER(m_lock); 92 dbg() << "VFS: unmount called with inode " << guest_inode_id; 93 94 for (size_t i = 0; i < m_mounts.size(); ++i) { 95 auto& mount = m_mounts.at(i); 96 if (mount.guest() == guest_inode_id) { 97 auto result = mount.guest_fs().prepare_to_unmount(); 98 if (result.is_error()) { 99 dbg() << "VFS: Failed to unmount!"; 100 return result; 101 } 102 dbg() << "VFS: found fs " << mount.guest_fs().fsid() << " at mount index " << i << "! Unmounting..."; 103 m_mounts.unstable_remove(i); 104 return KSuccess; 105 } 106 } 107 108 dbg() << "VFS: Nothing mounted on inode " << guest_inode_id; 109 return KResult(-ENODEV); 110} 111 112bool VFS::mount_root(FS& file_system) 113{ 114 if (m_root_inode) { 115 klog() << "VFS: mount_root can't mount another root"; 116 return false; 117 } 118 119 Mount mount { file_system, nullptr, MS_NODEV | MS_NOSUID }; 120 121 auto root_inode_id = mount.guest().fs()->root_inode(); 122 auto root_inode = mount.guest().fs()->get_inode(root_inode_id); 123 if (!root_inode->is_directory()) { 124 klog() << "VFS: root inode (" << String::format("%02u", root_inode_id.fsid()) << ":" << String::format("%08u", root_inode_id.index()) << ") for / is not a directory :("; 125 return false; 126 } 127 128 m_root_inode = move(root_inode); 129 klog() << "VFS: mounted root from " << m_root_inode->fs().class_name() << " (" << static_cast<FileBackedFS&>(m_root_inode->fs()).file_description().absolute_path() << ")"; 130 131 m_mounts.append(move(mount)); 132 return true; 133} 134 135auto VFS::find_mount_for_host(InodeIdentifier inode) -> Mount* 136{ 137 for (auto& mount : m_mounts) { 138 if (mount.host() == inode) 139 return &mount; 140 } 141 return nullptr; 142} 143 144auto VFS::find_mount_for_guest(InodeIdentifier inode) -> Mount* 145{ 146 for (auto& mount : m_mounts) { 147 if (mount.guest() == inode) 148 return &mount; 149 } 150 return nullptr; 151} 152 153bool VFS::is_vfs_root(InodeIdentifier inode) const 154{ 155 return inode == root_inode_id(); 156} 157 158void VFS::traverse_directory_inode(Inode& dir_inode, Function<bool(const FS::DirectoryEntry&)> callback) 159{ 160 dir_inode.traverse_as_directory([&](const FS::DirectoryEntry& entry) { 161 InodeIdentifier resolved_inode; 162 if (auto mount = find_mount_for_host(entry.inode)) 163 resolved_inode = mount->guest(); 164 else 165 resolved_inode = entry.inode; 166 167 // FIXME: This is now broken considering chroot and bind mounts. 168 if (dir_inode.identifier().is_root_inode() && !is_vfs_root(dir_inode.identifier()) && !strcmp(entry.name, "..")) { 169 auto mount = find_mount_for_guest(entry.inode); 170 ASSERT(mount); 171 resolved_inode = mount->host(); 172 } 173 callback(FS::DirectoryEntry(entry.name, entry.name_length, resolved_inode, entry.file_type)); 174 return true; 175 }); 176} 177 178KResult VFS::utime(StringView path, Custody& base, time_t atime, time_t mtime) 179{ 180 auto descriptor_or_error = VFS::the().open(move(path), 0, 0, base); 181 if (descriptor_or_error.is_error()) 182 return descriptor_or_error.error(); 183 auto& inode = *descriptor_or_error.value()->inode(); 184 if (inode.fs().is_readonly()) 185 return KResult(-EROFS); 186 if (!Process::current->is_superuser() && inode.metadata().uid != Process::current->euid()) 187 return KResult(-EACCES); 188 189 int error = inode.set_atime(atime); 190 if (error) 191 return KResult(error); 192 error = inode.set_mtime(mtime); 193 if (error) 194 return KResult(error); 195 return KSuccess; 196} 197 198KResultOr<InodeMetadata> VFS::lookup_metadata(StringView path, Custody& base, int options) 199{ 200 auto custody_or_error = resolve_path(path, base, nullptr, options); 201 if (custody_or_error.is_error()) 202 return custody_or_error.error(); 203 return custody_or_error.value()->inode().metadata(); 204} 205 206KResultOr<NonnullRefPtr<FileDescription>> VFS::open(StringView path, int options, mode_t mode, Custody& base, Optional<UidAndGid> owner) 207{ 208 if ((options & O_CREAT) && (options & O_DIRECTORY)) 209 return KResult(-EINVAL); 210 211 RefPtr<Custody> parent_custody; 212 auto custody_or_error = resolve_path(path, base, &parent_custody, options); 213 if (options & O_CREAT) { 214 if (!parent_custody) 215 return KResult(-ENOENT); 216 if (custody_or_error.is_error()) { 217 if (custody_or_error.error() != -ENOENT) 218 return custody_or_error.error(); 219 return create(path, options, mode, *parent_custody, move(owner)); 220 } 221 if (options & O_EXCL) 222 return KResult(-EEXIST); 223 } 224 if (custody_or_error.is_error()) 225 return custody_or_error.error(); 226 227 auto& custody = *custody_or_error.value(); 228 auto& inode = custody.inode(); 229 auto metadata = inode.metadata(); 230 231 if ((options & O_DIRECTORY) && !metadata.is_directory()) 232 return KResult(-ENOTDIR); 233 234 bool should_truncate_file = false; 235 236 if ((options & O_RDONLY) && !metadata.may_read(*Process::current)) 237 return KResult(-EACCES); 238 239 if (options & O_WRONLY) { 240 if (!metadata.may_write(*Process::current)) 241 return KResult(-EACCES); 242 if (metadata.is_directory()) 243 return KResult(-EISDIR); 244 should_truncate_file = options & O_TRUNC; 245 } 246 if (options & O_EXEC) { 247 if (!metadata.may_execute(*Process::current) || (custody.mount_flags() & MS_NOEXEC)) 248 return KResult(-EACCES); 249 } 250 251 if (auto preopen_fd = inode.preopen_fd()) 252 return *preopen_fd; 253 254 if (metadata.is_device()) { 255 if (custody.mount_flags() & MS_NODEV) 256 return KResult(-EACCES); 257 auto device = Device::get_device(metadata.major_device, metadata.minor_device); 258 if (device == nullptr) { 259 return KResult(-ENODEV); 260 } 261 auto descriptor_or_error = device->open(options); 262 if (descriptor_or_error.is_error()) 263 return descriptor_or_error.error(); 264 descriptor_or_error.value()->set_original_inode({}, inode); 265 return descriptor_or_error; 266 } 267 if (should_truncate_file) { 268 inode.truncate(0); 269 inode.set_mtime(kgettimeofday().tv_sec); 270 } 271 auto description = FileDescription::create(custody); 272 description->set_rw_mode(options); 273 description->set_file_flags(options); 274 return description; 275} 276 277KResult VFS::mknod(StringView path, mode_t mode, dev_t dev, Custody& base) 278{ 279 if (!is_regular_file(mode) && !is_block_device(mode) && !is_character_device(mode) && !is_fifo(mode) && !is_socket(mode)) 280 return KResult(-EINVAL); 281 282 RefPtr<Custody> parent_custody; 283 auto existing_file_or_error = resolve_path(path, base, &parent_custody); 284 if (!existing_file_or_error.is_error()) 285 return KResult(-EEXIST); 286 if (!parent_custody) 287 return KResult(-ENOENT); 288 if (existing_file_or_error.error() != -ENOENT) 289 return existing_file_or_error.error(); 290 auto& parent_inode = parent_custody->inode(); 291 if (!parent_inode.metadata().may_write(*Process::current)) 292 return KResult(-EACCES); 293 294 FileSystemPath p(path); 295 dbg() << "VFS::mknod: '" << p.basename() << "' mode=" << mode << " dev=" << dev << " in " << parent_inode.identifier(); 296 return parent_inode.fs().create_inode(parent_inode.identifier(), p.basename(), mode, 0, dev, Process::current->uid(), Process::current->gid()).result(); 297} 298 299KResultOr<NonnullRefPtr<FileDescription>> VFS::create(StringView path, int options, mode_t mode, Custody& parent_custody, Optional<UidAndGid> owner) 300{ 301 auto result = validate_path_against_process_veil(path, options); 302 if (result.is_error()) 303 return result; 304 305 if (!is_socket(mode) && !is_fifo(mode) && !is_block_device(mode) && !is_character_device(mode)) { 306 // Turn it into a regular file. (This feels rather hackish.) 307 mode |= 0100000; 308 } 309 310 auto& parent_inode = parent_custody.inode(); 311 if (!parent_inode.metadata().may_write(*Process::current)) 312 return KResult(-EACCES); 313 FileSystemPath p(path); 314#ifdef VFS_DEBUG 315 dbg() << "VFS::create: '" << p.basename() << "' in " << parent_inode.identifier(); 316#endif 317 uid_t uid = owner.has_value() ? owner.value().uid : Process::current->uid(); 318 gid_t gid = owner.has_value() ? owner.value().gid : Process::current->gid(); 319 auto inode_or_error = parent_inode.fs().create_inode(parent_inode.identifier(), p.basename(), mode, 0, 0, uid, gid); 320 if (inode_or_error.is_error()) 321 return inode_or_error.error(); 322 323 auto new_custody = Custody::create(&parent_custody, p.basename(), inode_or_error.value(), parent_custody.mount_flags()); 324 auto description = FileDescription::create(*new_custody); 325 description->set_rw_mode(options); 326 description->set_file_flags(options); 327 return description; 328} 329 330KResult VFS::mkdir(StringView path, mode_t mode, Custody& base) 331{ 332 // Unlike in basically every other case, where it's only the last 333 // path component (the one being created) that is allowed not to 334 // exist, POSIX allows mkdir'ed path to have trailing slashes. 335 // Let's handle that case by trimming any trailing slashes. 336 while (path.length() > 1 && path.ends_with("/")) 337 path = path.substring_view(0, path.length() - 1); 338 339 RefPtr<Custody> parent_custody; 340 auto result = resolve_path(path, base, &parent_custody); 341 if (!result.is_error()) 342 return KResult(-EEXIST); 343 if (!parent_custody) 344 return KResult(-ENOENT); 345 if (result.error() != -ENOENT) 346 return result.error(); 347 348 auto& parent_inode = parent_custody->inode(); 349 if (!parent_inode.metadata().may_write(*Process::current)) 350 return KResult(-EACCES); 351 352 FileSystemPath p(path); 353#ifdef VFS_DEBUG 354 dbg() << "VFS::mkdir: '" << p.basename() << "' in " << parent_inode.identifier(); 355#endif 356 return parent_inode.fs().create_directory(parent_inode.identifier(), p.basename(), mode, Process::current->uid(), Process::current->gid()); 357} 358 359KResult VFS::access(StringView path, int mode, Custody& base) 360{ 361 auto custody_or_error = resolve_path(path, base); 362 if (custody_or_error.is_error()) 363 return custody_or_error.error(); 364 auto& custody = *custody_or_error.value(); 365 auto& inode = custody.inode(); 366 auto metadata = inode.metadata(); 367 if (mode & R_OK) { 368 if (!metadata.may_read(*Process::current)) 369 return KResult(-EACCES); 370 } 371 if (mode & W_OK) { 372 if (!metadata.may_write(*Process::current)) 373 return KResult(-EACCES); 374 } 375 if (mode & X_OK) { 376 if (!metadata.may_execute(*Process::current)) 377 return KResult(-EACCES); 378 } 379 return KSuccess; 380} 381 382KResultOr<NonnullRefPtr<Custody>> VFS::open_directory(StringView path, Custody& base) 383{ 384 auto inode_or_error = resolve_path(path, base); 385 if (inode_or_error.is_error()) 386 return inode_or_error.error(); 387 auto& custody = *inode_or_error.value(); 388 auto& inode = custody.inode(); 389 if (!inode.is_directory()) 390 return KResult(-ENOTDIR); 391 if (!inode.metadata().may_execute(*Process::current)) 392 return KResult(-EACCES); 393 return custody; 394} 395 396KResult VFS::chmod(Inode& inode, mode_t mode) 397{ 398 if (inode.fs().is_readonly()) 399 return KResult(-EROFS); 400 401 if (Process::current->euid() != inode.metadata().uid && !Process::current->is_superuser()) 402 return KResult(-EPERM); 403 404 // Only change the permission bits. 405 mode = (inode.mode() & ~04777u) | (mode & 04777u); 406 return inode.chmod(mode); 407} 408 409KResult VFS::chmod(StringView path, mode_t mode, Custody& base) 410{ 411 auto custody_or_error = resolve_path(path, base); 412 if (custody_or_error.is_error()) 413 return custody_or_error.error(); 414 auto& custody = *custody_or_error.value(); 415 auto& inode = custody.inode(); 416 return chmod(inode, mode); 417} 418 419KResult VFS::rename(StringView old_path, StringView new_path, Custody& base) 420{ 421 RefPtr<Custody> old_parent_custody; 422 auto old_custody_or_error = resolve_path(old_path, base, &old_parent_custody); 423 if (old_custody_or_error.is_error()) 424 return old_custody_or_error.error(); 425 auto& old_custody = *old_custody_or_error.value(); 426 auto& old_inode = old_custody.inode(); 427 428 RefPtr<Custody> new_parent_custody; 429 auto new_custody_or_error = resolve_path(new_path, base, &new_parent_custody); 430 if (new_custody_or_error.is_error()) { 431 if (new_custody_or_error.error() != -ENOENT || !new_parent_custody) 432 return new_custody_or_error.error(); 433 } 434 435 auto& old_parent_inode = old_parent_custody->inode(); 436 auto& new_parent_inode = new_parent_custody->inode(); 437 438 if (&old_parent_inode.fs() != &new_parent_inode.fs()) 439 return KResult(-EXDEV); 440 441 if (!new_parent_inode.metadata().may_write(*Process::current)) 442 return KResult(-EACCES); 443 444 if (!old_parent_inode.metadata().may_write(*Process::current)) 445 return KResult(-EACCES); 446 447 if (old_parent_inode.metadata().is_sticky()) { 448 if (!Process::current->is_superuser() && old_inode.metadata().uid != Process::current->euid()) 449 return KResult(-EACCES); 450 } 451 452 auto new_basename = FileSystemPath(new_path).basename(); 453 454 if (!new_custody_or_error.is_error()) { 455 auto& new_custody = *new_custody_or_error.value(); 456 auto& new_inode = new_custody.inode(); 457 // FIXME: Is this really correct? Check what other systems do. 458 if (&new_inode == &old_inode) 459 return KSuccess; 460 if (new_parent_inode.metadata().is_sticky()) { 461 if (!Process::current->is_superuser() && new_inode.metadata().uid != Process::current->euid()) 462 return KResult(-EACCES); 463 } 464 if (new_inode.is_directory() && !old_inode.is_directory()) 465 return KResult(-EISDIR); 466 auto result = new_parent_inode.remove_child(new_basename); 467 if (result.is_error()) 468 return result; 469 } 470 471 auto result = new_parent_inode.add_child(old_inode.identifier(), new_basename, old_inode.mode()); 472 if (result.is_error()) 473 return result; 474 475 result = old_parent_inode.remove_child(FileSystemPath(old_path).basename()); 476 if (result.is_error()) 477 return result; 478 479 return KSuccess; 480} 481 482KResult VFS::chown(Inode& inode, uid_t a_uid, gid_t a_gid) 483{ 484 if (inode.fs().is_readonly()) 485 return KResult(-EROFS); 486 487 auto metadata = inode.metadata(); 488 489 if (Process::current->euid() != metadata.uid && !Process::current->is_superuser()) 490 return KResult(-EPERM); 491 492 uid_t new_uid = metadata.uid; 493 gid_t new_gid = metadata.gid; 494 495 if (a_uid != (uid_t)-1) { 496 if (Process::current->euid() != a_uid && !Process::current->is_superuser()) 497 return KResult(-EPERM); 498 new_uid = a_uid; 499 } 500 if (a_gid != (gid_t)-1) { 501 if (!Process::current->in_group(a_gid) && !Process::current->is_superuser()) 502 return KResult(-EPERM); 503 new_gid = a_gid; 504 } 505 506 dbg() << "VFS::chown(): inode " << inode.identifier() << " <- uid:" << new_uid << " gid:" << new_gid; 507 508 if (metadata.is_setuid() || metadata.is_setgid()) { 509 dbg() << "VFS::chown(): Stripping SUID/SGID bits from " << inode.identifier(); 510 auto result = inode.chmod(metadata.mode & ~(04000 | 02000)); 511 if (result.is_error()) 512 return result; 513 } 514 515 return inode.chown(new_uid, new_gid); 516} 517 518KResult VFS::chown(StringView path, uid_t a_uid, gid_t a_gid, Custody& base) 519{ 520 auto custody_or_error = resolve_path(path, base); 521 if (custody_or_error.is_error()) 522 return custody_or_error.error(); 523 auto& custody = *custody_or_error.value(); 524 auto& inode = custody.inode(); 525 return chown(inode, a_uid, a_gid); 526} 527 528KResult VFS::link(StringView old_path, StringView new_path, Custody& base) 529{ 530 auto old_custody_or_error = resolve_path(old_path, base); 531 if (old_custody_or_error.is_error()) 532 return old_custody_or_error.error(); 533 auto& old_custody = *old_custody_or_error.value(); 534 auto& old_inode = old_custody.inode(); 535 536 RefPtr<Custody> parent_custody; 537 auto new_custody_or_error = resolve_path(new_path, base, &parent_custody); 538 if (!new_custody_or_error.is_error()) 539 return KResult(-EEXIST); 540 541 if (!parent_custody) 542 return KResult(-ENOENT); 543 544 auto& parent_inode = parent_custody->inode(); 545 546 if (parent_inode.fsid() != old_inode.fsid()) 547 return KResult(-EXDEV); 548 549 if (parent_inode.fs().is_readonly()) 550 return KResult(-EROFS); 551 552 if (!parent_inode.metadata().may_write(*Process::current)) 553 return KResult(-EACCES); 554 555 if (old_inode.is_directory()) 556 return KResult(-EPERM); 557 558 return parent_inode.add_child(old_inode.identifier(), FileSystemPath(new_path).basename(), old_inode.mode()); 559} 560 561KResult VFS::unlink(StringView path, Custody& base) 562{ 563 RefPtr<Custody> parent_custody; 564 auto custody_or_error = resolve_path(path, base, &parent_custody, O_NOFOLLOW_NOERROR | O_UNLINK_INTERNAL); 565 if (custody_or_error.is_error()) 566 return custody_or_error.error(); 567 auto& custody = *custody_or_error.value(); 568 auto& inode = custody.inode(); 569 570 if (inode.is_directory()) 571 return KResult(-EISDIR); 572 573 auto& parent_inode = parent_custody->inode(); 574 if (!parent_inode.metadata().may_write(*Process::current)) 575 return KResult(-EACCES); 576 577 if (parent_inode.metadata().is_sticky()) { 578 if (!Process::current->is_superuser() && inode.metadata().uid != Process::current->euid()) 579 return KResult(-EACCES); 580 } 581 582 auto result = parent_inode.remove_child(FileSystemPath(path).basename()); 583 if (result.is_error()) 584 return result; 585 586 return KSuccess; 587} 588 589KResult VFS::symlink(StringView target, StringView linkpath, Custody& base) 590{ 591 RefPtr<Custody> parent_custody; 592 auto existing_custody_or_error = resolve_path(linkpath, base, &parent_custody); 593 if (!existing_custody_or_error.is_error()) 594 return KResult(-EEXIST); 595 if (!parent_custody) 596 return KResult(-ENOENT); 597 if (existing_custody_or_error.error() != -ENOENT) 598 return existing_custody_or_error.error(); 599 auto& parent_inode = parent_custody->inode(); 600 if (!parent_inode.metadata().may_write(*Process::current)) 601 return KResult(-EACCES); 602 603 FileSystemPath p(linkpath); 604 dbg() << "VFS::symlink: '" << p.basename() << "' (-> '" << target << "') in " << parent_inode.identifier(); 605 auto inode_or_error = parent_inode.fs().create_inode(parent_inode.identifier(), p.basename(), 0120644, 0, 0, Process::current->uid(), Process::current->gid()); 606 if (inode_or_error.is_error()) 607 return inode_or_error.error(); 608 auto& inode = inode_or_error.value(); 609 ssize_t nwritten = inode->write_bytes(0, target.length(), (const u8*)target.characters_without_null_termination(), nullptr); 610 if (nwritten < 0) 611 return KResult(nwritten); 612 return KSuccess; 613} 614 615KResult VFS::rmdir(StringView path, Custody& base) 616{ 617 RefPtr<Custody> parent_custody; 618 auto custody_or_error = resolve_path(path, base, &parent_custody); 619 if (custody_or_error.is_error()) 620 return KResult(custody_or_error.error()); 621 622 auto& custody = *custody_or_error.value(); 623 auto& inode = custody.inode(); 624 if (inode.fs().is_readonly()) 625 return KResult(-EROFS); 626 627 // FIXME: We should return EINVAL if the last component of the path is "." 628 // FIXME: We should return ENOTEMPTY if the last component of the path is ".." 629 630 if (!inode.is_directory()) 631 return KResult(-ENOTDIR); 632 633 auto& parent_inode = parent_custody->inode(); 634 635 if (!parent_inode.metadata().may_write(*Process::current)) 636 return KResult(-EACCES); 637 638 if (inode.directory_entry_count() != 2) 639 return KResult(-ENOTEMPTY); 640 641 auto result = inode.remove_child("."); 642 if (result.is_error()) 643 return result; 644 645 result = inode.remove_child(".."); 646 if (result.is_error()) 647 return result; 648 649 return parent_inode.remove_child(FileSystemPath(path).basename()); 650} 651 652RefPtr<Inode> VFS::get_inode(InodeIdentifier inode_id) 653{ 654 if (!inode_id.is_valid()) 655 return nullptr; 656 return inode_id.fs()->get_inode(inode_id); 657} 658 659VFS::Mount::Mount(FS& guest_fs, Custody* host_custody, int flags) 660 : m_guest(guest_fs.root_inode()) 661 , m_guest_fs(guest_fs) 662 , m_host_custody(host_custody) 663 , m_flags(flags) 664{ 665} 666 667VFS::Mount::Mount(Inode& source, Custody& host_custody, int flags) 668 : m_guest(source.identifier()) 669 , m_guest_fs(source.fs()) 670 , m_host_custody(host_custody) 671 , m_flags(flags) 672{ 673} 674 675String VFS::Mount::absolute_path() const 676{ 677 if (!m_host_custody) 678 return "/"; 679 return m_host_custody->absolute_path(); 680} 681 682InodeIdentifier VFS::Mount::host() const 683{ 684 if (!m_host_custody) 685 return {}; 686 return m_host_custody->inode().identifier(); 687} 688 689void VFS::for_each_mount(Function<void(const Mount&)> callback) const 690{ 691 for (auto& mount : m_mounts) { 692 callback(mount); 693 } 694} 695 696void VFS::sync() 697{ 698 FS::sync(); 699} 700 701Custody& VFS::root_custody() 702{ 703 if (!m_root_custody) 704 m_root_custody = Custody::create(nullptr, "", *m_root_inode, MS_NODEV | MS_NOSUID); 705 return *m_root_custody; 706} 707 708const UnveiledPath* VFS::find_matching_unveiled_path(StringView path) 709{ 710 for (auto& unveiled_path : Process::current->unveiled_paths()) { 711 if (path == unveiled_path.path) 712 return &unveiled_path; 713 if (path.starts_with(unveiled_path.path) && path.length() > unveiled_path.path.length() && path[unveiled_path.path.length()] == '/') 714 return &unveiled_path; 715 } 716 return nullptr; 717} 718 719KResult VFS::validate_path_against_process_veil(StringView path, int options) 720{ 721 if (Process::current->veil_state() == VeilState::None) 722 return KSuccess; 723 724 // FIXME: Figure out a nicer way to do this. 725 if (String(path).contains("/..")) 726 return KResult(-EINVAL); 727 728 auto* unveiled_path = find_matching_unveiled_path(path); 729 if (!unveiled_path) { 730 dbg() << "Rejecting path '" << path << "' since it hasn't been unveiled."; 731 dump_backtrace(); 732 return KResult(-ENOENT); 733 } 734 735 if (options & O_CREAT) { 736 if (!(unveiled_path->permissions & UnveiledPath::Access::CreateOrRemove)) { 737 dbg() << "Rejecting path '" << path << "' since it hasn't been unveiled with 'c' permission."; 738 dump_backtrace(); 739 return KResult(-EACCES); 740 } 741 } 742 if (options & O_UNLINK_INTERNAL) { 743 if (!(unveiled_path->permissions & UnveiledPath::Access::CreateOrRemove)) { 744 dbg() << "Rejecting path '" << path << "' for unlink since it hasn't been unveiled with 'c' permission."; 745 dump_backtrace(); 746 return KResult(-EACCES); 747 } 748 return KSuccess; 749 } 750 if (options & O_RDONLY) { 751 if (!(unveiled_path->permissions & UnveiledPath::Access::Read)) { 752 dbg() << "Rejecting path '" << path << "' since it hasn't been unveiled with 'r' permission."; 753 dump_backtrace(); 754 return KResult(-EACCES); 755 } 756 } 757 if (options & O_WRONLY) { 758 if (!(unveiled_path->permissions & UnveiledPath::Access::Write)) { 759 dbg() << "Rejecting path '" << path << "' since it hasn't been unveiled with 'w' permission."; 760 dump_backtrace(); 761 return KResult(-EACCES); 762 } 763 } 764 if (options & O_EXEC) { 765 if (!(unveiled_path->permissions & UnveiledPath::Access::Execute)) { 766 dbg() << "Rejecting path '" << path << "' since it hasn't been unveiled with 'x' permission."; 767 dump_backtrace(); 768 return KResult(-EACCES); 769 } 770 } 771 return KSuccess; 772} 773 774KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) 775{ 776 auto custody_or_error = resolve_path_without_veil(path, base, out_parent, options, symlink_recursion_level); 777 if (custody_or_error.is_error()) 778 return custody_or_error.error(); 779 780 auto& custody = custody_or_error.value(); 781 auto result = validate_path_against_process_veil(custody->absolute_path(), options); 782 if (result.is_error()) 783 return result; 784 785 return custody; 786} 787 788KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path_without_veil(StringView path, Custody& base, RefPtr<Custody>* out_parent, int options, int symlink_recursion_level) 789{ 790 if (symlink_recursion_level >= symlink_recursion_limit) 791 return KResult(-ELOOP); 792 793 if (path.is_empty()) 794 return KResult(-EINVAL); 795 796 auto parts = path.split_view('/', true); 797 auto& current_root = Process::current->root_directory(); 798 799 NonnullRefPtr<Custody> custody = path[0] == '/' ? current_root : base; 800 801 for (size_t i = 0; i < parts.size(); ++i) { 802 Custody& parent = custody; 803 auto parent_metadata = parent.inode().metadata(); 804 if (!parent_metadata.is_directory()) 805 return KResult(-ENOTDIR); 806 // Ensure the current user is allowed to resolve paths inside this directory. 807 if (!parent_metadata.may_execute(*Process::current)) 808 return KResult(-EACCES); 809 810 auto& part = parts[i]; 811 bool have_more_parts = i + 1 < parts.size(); 812 813 if (part == "..") { 814 // If we encounter a "..", take a step back, but don't go beyond the root. 815 if (custody->parent()) 816 custody = *custody->parent(); 817 continue; 818 } else if (part == "." || part.is_empty()) { 819 continue; 820 } 821 822 // Okay, let's look up this part. 823 auto child_inode = parent.inode().lookup(part); 824 if (!child_inode) { 825 if (out_parent) { 826 // ENOENT with a non-null parent custody signals to caller that 827 // we found the immediate parent of the file, but the file itself 828 // does not exist yet. 829 *out_parent = have_more_parts ? nullptr : &parent; 830 } 831 return KResult(-ENOENT); 832 } 833 834 int mount_flags_for_child = parent.mount_flags(); 835 836 // See if there's something mounted on the child; in that case 837 // we would need to return the guest inode, not the host inode. 838 if (auto mount = find_mount_for_host(child_inode->identifier())) { 839 child_inode = get_inode(mount->guest()); 840 mount_flags_for_child = mount->flags(); 841 } 842 843 custody = Custody::create(&parent, part, *child_inode, mount_flags_for_child); 844 845 if (child_inode->metadata().is_symlink()) { 846 if (!have_more_parts) { 847 if (options & O_NOFOLLOW) 848 return KResult(-ELOOP); 849 if (options & O_NOFOLLOW_NOERROR) 850 break; 851 } 852 auto symlink_target = child_inode->resolve_as_link(parent, out_parent, options, symlink_recursion_level + 1); 853 if (symlink_target.is_error() || !have_more_parts) 854 return symlink_target; 855 856 // Now, resolve the remaining path relative to the symlink target. 857 // We prepend a "." to it to ensure that it's not empty and that 858 // any initial slashes it might have get interpreted properly. 859 StringBuilder remaining_path; 860 remaining_path.append('.'); 861 remaining_path.append(path.substring_view_starting_after_substring(part)); 862 863 return resolve_path_without_veil(remaining_path.to_string(), *symlink_target.value(), out_parent, options, symlink_recursion_level + 1); 864 } 865 } 866 867 if (out_parent) 868 *out_parent = custody->parent(); 869 return custody; 870} 871 872}