Serenity Operating System
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/DiskBackedFileSystem.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 kprintf("VFS: Constructing VFS\n");
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 mount_point.did_mount_on({});
78 return KSuccess;
79}
80
81KResult VFS::bind_mount(Custody& source, Custody& mount_point, int flags)
82{
83 dbg() << "VFS: Bind-mounting " << source.absolute_path() << " at " << mount_point.absolute_path();
84 // FIXME: check that this is not already a mount point
85 Mount mount { source.inode(), mount_point, flags };
86 m_mounts.append(move(mount));
87 mount_point.did_mount_on({});
88 return KSuccess;
89}
90
91KResult VFS::unmount(InodeIdentifier guest_inode_id)
92{
93 LOCKER(m_lock);
94 dbg() << "VFS: unmount called with inode " << guest_inode_id;
95
96 for (size_t i = 0; i < m_mounts.size(); ++i) {
97 auto& mount = m_mounts.at(i);
98 if (mount.guest() == guest_inode_id) {
99 auto result = mount.guest_fs().prepare_to_unmount();
100 if (result.is_error()) {
101 dbg() << "VFS: Failed to unmount!";
102 return result;
103 }
104 dbg() << "VFS: found fs " << mount.guest_fs().fsid() << " at mount index " << i << "! Unmounting...";
105 m_mounts.unstable_remove(i);
106 return KSuccess;
107 }
108 }
109
110 dbg() << "VFS: Nothing mounted on inode " << guest_inode_id;
111 return KResult(-ENODEV);
112}
113
114bool VFS::mount_root(FS& file_system)
115{
116 if (m_root_inode) {
117 kprintf("VFS: mount_root can't mount another root\n");
118 return false;
119 }
120
121 Mount mount { file_system, nullptr, MS_NODEV | MS_NOSUID };
122
123 auto root_inode_id = mount.guest().fs()->root_inode();
124 auto root_inode = mount.guest().fs()->get_inode(root_inode_id);
125 if (!root_inode->is_directory()) {
126 kprintf("VFS: root inode (%02u:%08u) for / is not a directory :(\n", root_inode_id.fsid(), root_inode_id.index());
127 return false;
128 }
129
130 m_root_inode = move(root_inode);
131 char device_name[32];
132 if (m_root_inode->fs().is_disk_backed()) {
133 auto& device = static_cast<DiskBackedFS&>(m_root_inode->fs()).device();
134 sprintf(device_name, "%d,%d", device.major(), device.minor());
135 } else {
136 sprintf(device_name, "not-a-disk");
137 }
138 kprintf("VFS: mounted root on %s (%s)\n", m_root_inode->fs().class_name(), device_name);
139
140 m_mounts.append(move(mount));
141 return true;
142}
143
144auto VFS::find_mount_for_host(InodeIdentifier inode) -> Mount*
145{
146 for (auto& mount : m_mounts) {
147 if (mount.host() == inode)
148 return &mount;
149 }
150 return nullptr;
151}
152
153auto VFS::find_mount_for_guest(InodeIdentifier inode) -> Mount*
154{
155 for (auto& mount : m_mounts) {
156 if (mount.guest() == inode)
157 return &mount;
158 }
159 return nullptr;
160}
161
162bool VFS::is_vfs_root(InodeIdentifier inode) const
163{
164 return inode == root_inode_id();
165}
166
167void VFS::traverse_directory_inode(Inode& dir_inode, Function<bool(const FS::DirectoryEntry&)> callback)
168{
169 dir_inode.traverse_as_directory([&](const FS::DirectoryEntry& entry) {
170 InodeIdentifier resolved_inode;
171 if (auto mount = find_mount_for_host(entry.inode))
172 resolved_inode = mount->guest();
173 else
174 resolved_inode = entry.inode;
175
176 // FIXME: This is now broken considering chroot and bind mounts.
177 if (dir_inode.identifier().is_root_inode() && !is_vfs_root(dir_inode.identifier()) && !strcmp(entry.name, "..")) {
178 auto mount = find_mount_for_guest(entry.inode);
179 ASSERT(mount);
180 resolved_inode = mount->host();
181 }
182 callback(FS::DirectoryEntry(entry.name, entry.name_length, resolved_inode, entry.file_type));
183 return true;
184 });
185}
186
187KResult VFS::utime(StringView path, Custody& base, time_t atime, time_t mtime)
188{
189 auto descriptor_or_error = VFS::the().open(move(path), 0, 0, base);
190 if (descriptor_or_error.is_error())
191 return descriptor_or_error.error();
192 auto& inode = *descriptor_or_error.value()->inode();
193 if (inode.fs().is_readonly())
194 return KResult(-EROFS);
195 if (!Process::current->is_superuser() && inode.metadata().uid != Process::current->euid())
196 return KResult(-EACCES);
197
198 int error = inode.set_atime(atime);
199 if (error)
200 return KResult(error);
201 error = inode.set_mtime(mtime);
202 if (error)
203 return KResult(error);
204 return KSuccess;
205}
206
207KResultOr<InodeMetadata> VFS::lookup_metadata(StringView path, Custody& base, int options)
208{
209 auto custody_or_error = resolve_path(path, base, nullptr, options);
210 if (custody_or_error.is_error())
211 return custody_or_error.error();
212 return custody_or_error.value()->inode().metadata();
213}
214
215KResultOr<NonnullRefPtr<FileDescription>> VFS::open(StringView path, int options, mode_t mode, Custody& base, Optional<UidAndGid> owner)
216{
217 if ((options & O_CREAT) && (options & O_DIRECTORY))
218 return KResult(-EINVAL);
219
220 RefPtr<Custody> parent_custody;
221 auto custody_or_error = resolve_path(path, base, &parent_custody, options);
222 if (options & O_CREAT) {
223 if (!parent_custody)
224 return KResult(-ENOENT);
225 if (custody_or_error.is_error()) {
226 if (custody_or_error.error() != -ENOENT)
227 return custody_or_error.error();
228 return create(path, options, mode, *parent_custody, move(owner));
229 }
230 if (options & O_EXCL)
231 return KResult(-EEXIST);
232 }
233 if (custody_or_error.is_error())
234 return custody_or_error.error();
235
236 auto& custody = *custody_or_error.value();
237 auto& inode = custody.inode();
238 auto metadata = inode.metadata();
239
240 if ((options & O_DIRECTORY) && !metadata.is_directory())
241 return KResult(-ENOTDIR);
242
243 bool should_truncate_file = false;
244
245 if ((options & O_RDONLY) && !metadata.may_read(*Process::current))
246 return KResult(-EACCES);
247
248 if (options & O_WRONLY) {
249 if (!metadata.may_write(*Process::current))
250 return KResult(-EACCES);
251 if (metadata.is_directory())
252 return KResult(-EISDIR);
253 should_truncate_file = options & O_TRUNC;
254 }
255 if (options & O_EXEC) {
256 if (!metadata.may_execute(*Process::current) || (custody.mount_flags() & MS_NOEXEC))
257 return KResult(-EACCES);
258 }
259
260 if (auto preopen_fd = inode.preopen_fd())
261 return *preopen_fd;
262
263 if (metadata.is_device()) {
264 if (custody.mount_flags() & MS_NODEV)
265 return KResult(-EACCES);
266 auto device = Device::get_device(metadata.major_device, metadata.minor_device);
267 if (device == nullptr) {
268 return KResult(-ENODEV);
269 }
270 auto descriptor_or_error = device->open(options);
271 if (descriptor_or_error.is_error())
272 return descriptor_or_error.error();
273 descriptor_or_error.value()->set_original_inode({}, inode);
274 return descriptor_or_error;
275 }
276 if (should_truncate_file) {
277 inode.truncate(0);
278 inode.set_mtime(kgettimeofday().tv_sec);
279 }
280 auto description = FileDescription::create(custody);
281 description->set_rw_mode(options);
282 description->set_file_flags(options);
283 return description;
284}
285
286KResult VFS::mknod(StringView path, mode_t mode, dev_t dev, Custody& base)
287{
288 if (!is_regular_file(mode) && !is_block_device(mode) && !is_character_device(mode) && !is_fifo(mode) && !is_socket(mode))
289 return KResult(-EINVAL);
290
291 RefPtr<Custody> parent_custody;
292 auto existing_file_or_error = resolve_path(path, base, &parent_custody);
293 if (!existing_file_or_error.is_error())
294 return KResult(-EEXIST);
295 if (!parent_custody)
296 return KResult(-ENOENT);
297 if (existing_file_or_error.error() != -ENOENT)
298 return existing_file_or_error.error();
299 auto& parent_inode = parent_custody->inode();
300 if (!parent_inode.metadata().may_write(*Process::current))
301 return KResult(-EACCES);
302
303 FileSystemPath p(path);
304 dbg() << "VFS::mknod: '" << p.basename() << "' mode=" << mode << " dev=" << dev << " in " << parent_inode.identifier();
305 return parent_inode.fs().create_inode(parent_inode.identifier(), p.basename(), mode, 0, dev, Process::current->uid(), Process::current->gid()).result();
306}
307
308KResultOr<NonnullRefPtr<FileDescription>> VFS::create(StringView path, int options, mode_t mode, Custody& parent_custody, Optional<UidAndGid> owner)
309{
310 if (!is_socket(mode) && !is_fifo(mode) && !is_block_device(mode) && !is_character_device(mode)) {
311 // Turn it into a regular file. (This feels rather hackish.)
312 mode |= 0100000;
313 }
314
315 auto& parent_inode = parent_custody.inode();
316 if (!parent_inode.metadata().may_write(*Process::current))
317 return KResult(-EACCES);
318 FileSystemPath p(path);
319#ifdef VFS_DEBUG
320 dbg() << "VFS::create: '" << p.basename() << "' in " << parent_inode.identifier();
321#endif
322 uid_t uid = owner.has_value() ? owner.value().uid : Process::current->uid();
323 gid_t gid = owner.has_value() ? owner.value().gid : Process::current->gid();
324 auto inode_or_error = parent_inode.fs().create_inode(parent_inode.identifier(), p.basename(), mode, 0, 0, uid, gid);
325 if (inode_or_error.is_error())
326 return inode_or_error.error();
327
328 auto new_custody = Custody::create(&parent_custody, p.basename(), inode_or_error.value(), parent_custody.mount_flags());
329 auto description = FileDescription::create(*new_custody);
330 description->set_rw_mode(options);
331 description->set_file_flags(options);
332 return description;
333}
334
335KResult VFS::mkdir(StringView path, mode_t mode, Custody& base)
336{
337 // Unlike in basically every other case, where it's only the last
338 // path component (the one being created) that is allowed not to
339 // exist, POSIX allows mkdir'ed path to have trailing slashes.
340 // Let's handle that case by trimming any trailing slashes.
341 while (path.length() > 1 && path.ends_with("/"))
342 path = path.substring_view(0, path.length() - 1);
343
344 RefPtr<Custody> parent_custody;
345 auto result = resolve_path(path, base, &parent_custody);
346 if (!result.is_error())
347 return KResult(-EEXIST);
348 if (!parent_custody)
349 return KResult(-ENOENT);
350 if (result.error() != -ENOENT)
351 return result.error();
352
353 auto& parent_inode = parent_custody->inode();
354 if (!parent_inode.metadata().may_write(*Process::current))
355 return KResult(-EACCES);
356
357 FileSystemPath p(path);
358#ifdef VFS_DEBUG
359 dbg() << "VFS::mkdir: '" << p.basename() << "' in " << parent_inode.identifier();
360#endif
361 return parent_inode.fs().create_directory(parent_inode.identifier(), p.basename(), mode, Process::current->uid(), Process::current->gid());
362}
363
364KResult VFS::access(StringView path, int mode, Custody& base)
365{
366 auto custody_or_error = resolve_path(path, base);
367 if (custody_or_error.is_error())
368 return custody_or_error.error();
369 auto& custody = *custody_or_error.value();
370 auto& inode = custody.inode();
371 auto metadata = inode.metadata();
372 if (mode & R_OK) {
373 if (!metadata.may_read(*Process::current))
374 return KResult(-EACCES);
375 }
376 if (mode & W_OK) {
377 if (!metadata.may_write(*Process::current))
378 return KResult(-EACCES);
379 }
380 if (mode & X_OK) {
381 if (!metadata.may_execute(*Process::current))
382 return KResult(-EACCES);
383 }
384 return KSuccess;
385}
386
387KResultOr<NonnullRefPtr<Custody>> VFS::open_directory(StringView path, Custody& base)
388{
389 auto inode_or_error = resolve_path(path, base);
390 if (inode_or_error.is_error())
391 return inode_or_error.error();
392 auto& custody = *inode_or_error.value();
393 auto& inode = custody.inode();
394 if (!inode.is_directory())
395 return KResult(-ENOTDIR);
396 if (!inode.metadata().may_execute(*Process::current))
397 return KResult(-EACCES);
398 return custody;
399}
400
401KResult VFS::chmod(Inode& inode, mode_t mode)
402{
403 if (inode.fs().is_readonly())
404 return KResult(-EROFS);
405
406 if (Process::current->euid() != inode.metadata().uid && !Process::current->is_superuser())
407 return KResult(-EPERM);
408
409 // Only change the permission bits.
410 mode = (inode.mode() & ~04777u) | (mode & 04777u);
411 return inode.chmod(mode);
412}
413
414KResult VFS::chmod(StringView path, mode_t mode, Custody& base)
415{
416 auto custody_or_error = resolve_path(path, base);
417 if (custody_or_error.is_error())
418 return custody_or_error.error();
419 auto& custody = *custody_or_error.value();
420 auto& inode = custody.inode();
421 return chmod(inode, mode);
422}
423
424KResult VFS::rename(StringView old_path, StringView new_path, Custody& base)
425{
426 RefPtr<Custody> old_parent_custody;
427 auto old_custody_or_error = resolve_path(old_path, base, &old_parent_custody);
428 if (old_custody_or_error.is_error())
429 return old_custody_or_error.error();
430 auto& old_custody = *old_custody_or_error.value();
431 auto& old_inode = old_custody.inode();
432
433 RefPtr<Custody> new_parent_custody;
434 auto new_custody_or_error = resolve_path(new_path, base, &new_parent_custody);
435 if (new_custody_or_error.is_error()) {
436 if (new_custody_or_error.error() != -ENOENT || !new_parent_custody)
437 return new_custody_or_error.error();
438 }
439
440 auto& old_parent_inode = old_parent_custody->inode();
441 auto& new_parent_inode = new_parent_custody->inode();
442
443 if (&old_parent_inode.fs() != &new_parent_inode.fs())
444 return KResult(-EXDEV);
445
446 if (!new_parent_inode.metadata().may_write(*Process::current))
447 return KResult(-EACCES);
448
449 if (!old_parent_inode.metadata().may_write(*Process::current))
450 return KResult(-EACCES);
451
452 if (old_parent_inode.metadata().is_sticky()) {
453 if (!Process::current->is_superuser() && old_inode.metadata().uid != Process::current->euid())
454 return KResult(-EACCES);
455 }
456
457 auto new_basename = FileSystemPath(new_path).basename();
458
459 if (!new_custody_or_error.is_error()) {
460 auto& new_custody = *new_custody_or_error.value();
461 auto& new_inode = new_custody.inode();
462 // FIXME: Is this really correct? Check what other systems do.
463 if (&new_inode == &old_inode)
464 return KSuccess;
465 if (new_parent_inode.metadata().is_sticky()) {
466 if (!Process::current->is_superuser() && new_inode.metadata().uid != Process::current->euid())
467 return KResult(-EACCES);
468 }
469 if (new_inode.is_directory() && !old_inode.is_directory())
470 return KResult(-EISDIR);
471 auto result = new_parent_inode.remove_child(new_basename);
472 if (result.is_error())
473 return result;
474 new_custody.did_delete({});
475 }
476
477 auto result = new_parent_inode.add_child(old_inode.identifier(), new_basename, old_inode.mode());
478 if (result.is_error())
479 return result;
480
481 result = old_parent_inode.remove_child(FileSystemPath(old_path).basename());
482 if (result.is_error())
483 return result;
484 old_custody.did_rename({}, new_basename);
485
486 return KSuccess;
487}
488
489KResult VFS::chown(Inode& inode, uid_t a_uid, gid_t a_gid)
490{
491 if (inode.fs().is_readonly())
492 return KResult(-EROFS);
493
494 auto metadata = inode.metadata();
495
496 if (Process::current->euid() != metadata.uid && !Process::current->is_superuser())
497 return KResult(-EPERM);
498
499 uid_t new_uid = metadata.uid;
500 gid_t new_gid = metadata.gid;
501
502 if (a_uid != (uid_t)-1) {
503 if (Process::current->euid() != a_uid && !Process::current->is_superuser())
504 return KResult(-EPERM);
505 new_uid = a_uid;
506 }
507 if (a_gid != (gid_t)-1) {
508 if (!Process::current->in_group(a_gid) && !Process::current->is_superuser())
509 return KResult(-EPERM);
510 new_gid = a_gid;
511 }
512
513 dbg() << "VFS::chown(): inode " << inode.identifier() << " <- uid:" << new_uid << " gid:" << new_gid;
514 return inode.chown(new_uid, new_gid);
515}
516
517KResult VFS::chown(StringView path, uid_t a_uid, gid_t a_gid, Custody& base)
518{
519 auto custody_or_error = resolve_path(path, base);
520 if (custody_or_error.is_error())
521 return custody_or_error.error();
522 auto& custody = *custody_or_error.value();
523 auto& inode = custody.inode();
524 return chown(inode, a_uid, a_gid);
525}
526
527KResult VFS::link(StringView old_path, StringView new_path, Custody& base)
528{
529 auto old_custody_or_error = resolve_path(old_path, base);
530 if (old_custody_or_error.is_error())
531 return old_custody_or_error.error();
532 auto& old_custody = *old_custody_or_error.value();
533 auto& old_inode = old_custody.inode();
534
535 RefPtr<Custody> parent_custody;
536 auto new_custody_or_error = resolve_path(new_path, base, &parent_custody);
537 if (!new_custody_or_error.is_error())
538 return KResult(-EEXIST);
539
540 if (!parent_custody)
541 return KResult(-ENOENT);
542
543 auto& parent_inode = parent_custody->inode();
544
545 if (parent_inode.fsid() != old_inode.fsid())
546 return KResult(-EXDEV);
547
548 if (parent_inode.fs().is_readonly())
549 return KResult(-EROFS);
550
551 if (!parent_inode.metadata().may_write(*Process::current))
552 return KResult(-EACCES);
553
554 if (old_inode.is_directory())
555 return KResult(-EPERM);
556
557 return parent_inode.add_child(old_inode.identifier(), FileSystemPath(new_path).basename(), old_inode.mode());
558}
559
560KResult VFS::unlink(StringView path, Custody& base)
561{
562 RefPtr<Custody> parent_custody;
563 auto custody_or_error = resolve_path(path, base, &parent_custody, O_NOFOLLOW_NOERROR | O_UNLINK_INTERNAL);
564 if (custody_or_error.is_error())
565 return custody_or_error.error();
566 auto& custody = *custody_or_error.value();
567 auto& inode = custody.inode();
568
569 if (inode.is_directory())
570 return KResult(-EISDIR);
571
572 auto& parent_inode = parent_custody->inode();
573 if (!parent_inode.metadata().may_write(*Process::current))
574 return KResult(-EACCES);
575
576 if (parent_inode.metadata().is_sticky()) {
577 if (!Process::current->is_superuser() && inode.metadata().uid != Process::current->euid())
578 return KResult(-EACCES);
579 }
580
581 auto result = parent_inode.remove_child(FileSystemPath(path).basename());
582 if (result.is_error())
583 return result;
584
585 custody.did_delete({});
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 result = validate_path_against_process_veil(path, options);
777 if (result.is_error())
778 return result;
779
780 if (symlink_recursion_level >= symlink_recursion_limit)
781 return KResult(-ELOOP);
782
783 if (path.is_empty())
784 return KResult(-EINVAL);
785
786 auto parts = path.split_view('/', true);
787 auto& current_root = Process::current->root_directory();
788
789 NonnullRefPtr<Custody> custody = path[0] == '/' ? current_root : base;
790
791 for (size_t i = 0; i < parts.size(); ++i) {
792 Custody& parent = custody;
793 auto parent_metadata = parent.inode().metadata();
794 if (!parent_metadata.is_directory())
795 return KResult(-ENOTDIR);
796 // Ensure the current user is allowed to resolve paths inside this directory.
797 if (!parent_metadata.may_execute(*Process::current))
798 return KResult(-EACCES);
799
800 auto& part = parts[i];
801 bool have_more_parts = i + 1 < parts.size();
802
803 if (part == "..") {
804 // If we encounter a "..", take a step back, but don't go beyond the root.
805 if (custody->parent())
806 custody = *custody->parent();
807 continue;
808 } else if (part == "." || part.is_empty()) {
809 continue;
810 }
811
812 // Okay, let's look up this part.
813 auto child_inode = parent.inode().lookup(part);
814 if (!child_inode) {
815 if (out_parent) {
816 // ENOENT with a non-null parent custody signals to caller that
817 // we found the immediate parent of the file, but the file itself
818 // does not exist yet.
819 *out_parent = have_more_parts ? nullptr : &parent;
820 }
821 return KResult(-ENOENT);
822 }
823
824 int mount_flags_for_child = parent.mount_flags();
825
826 // See if there's something mounted on the child; in that case
827 // we would need to return the guest inode, not the host inode.
828 if (auto mount = find_mount_for_host(child_inode->identifier())) {
829 child_inode = get_inode(mount->guest());
830 mount_flags_for_child = mount->flags();
831 }
832
833 custody = Custody::create(&parent, part, *child_inode, mount_flags_for_child);
834
835 if (child_inode->metadata().is_symlink()) {
836 if (!have_more_parts) {
837 if (options & O_NOFOLLOW)
838 return KResult(-ELOOP);
839 if (options & O_NOFOLLOW_NOERROR)
840 break;
841 }
842 auto symlink_target = child_inode->resolve_as_link(parent, out_parent, options, symlink_recursion_level + 1);
843 if (symlink_target.is_error() || !have_more_parts)
844 return symlink_target;
845
846 // Now, resolve the remaining path relative to the symlink target.
847 // We prepend a "." to it to ensure that it's not empty and that
848 // any initial slashes it might have get interpreted properly.
849 StringBuilder remaining_path;
850 remaining_path.append('.');
851 remaining_path.append(path.substring_view_starting_after_substring(part));
852
853 return resolve_path(remaining_path.to_string(), *symlink_target.value(), out_parent, options, symlink_recursion_level + 1);
854 }
855 }
856
857 if (out_parent)
858 *out_parent = custody->parent();
859 return custody;
860}
861
862}