summaryrefslogtreecommitdiff
path: root/Kernel/Syscalls/mount.cpp
blob: 87d7e60ce18cf1256d4246d66a52587461a95536 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/DevPtsFS.h>
#include <Kernel/FileSystem/Ext2FileSystem.h>
#include <Kernel/FileSystem/Plan9FileSystem.h>
#include <Kernel/FileSystem/ProcFS.h>
#include <Kernel/FileSystem/TmpFS.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Process.h>

namespace Kernel {

int Process::sys$mount(const Syscall::SC_mount_params* user_params)
{
    if (!is_superuser())
        return -EPERM;

    REQUIRE_NO_PROMISES;

    Syscall::SC_mount_params params;
    if (!validate_read_and_copy_typed(&params, user_params))
        return -EFAULT;

    auto source_fd = params.source_fd;
    auto target = validate_and_copy_string_from_user(params.target);
    auto fs_type = validate_and_copy_string_from_user(params.fs_type);

    if (target.is_null())
        return -EFAULT;

    auto description = file_description(source_fd);
    if (!description.is_null())
        dbg() << "mount " << fs_type << ": source fd " << source_fd << " @ " << target;
    else
        dbg() << "mount " << fs_type << " @ " << target;

    auto custody_or_error = VFS::the().resolve_path(target, current_directory());
    if (custody_or_error.is_error())
        return custody_or_error.error();

    auto& target_custody = custody_or_error.value();

    if (params.flags & MS_REMOUNT) {
        // We're not creating a new mount, we're updating an existing one!
        return VFS::the().remount(target_custody, params.flags & ~MS_REMOUNT);
    }

    if (params.flags & MS_BIND) {
        // We're doing a bind mount.
        if (description.is_null())
            return -EBADF;
        if (!description->custody()) {
            // We only support bind-mounting inodes, not arbitrary files.
            return -ENODEV;
        }
        return VFS::the().bind_mount(*description->custody(), target_custody, params.flags);
    }

    RefPtr<FS> fs;

    if (fs_type == "ext2" || fs_type == "Ext2FS") {
        if (description.is_null())
            return -EBADF;
        if (!description->file().is_seekable()) {
            dbg() << "mount: this is not a seekable file";
            return -ENODEV;
        }

        dbg() << "mount: attempting to mount " << description->absolute_path() << " on " << target;

        fs = Ext2FS::create(*description);
    } else if (fs_type == "9p" || fs_type == "Plan9FS") {
        if (description.is_null())
            return -EBADF;

        fs = Plan9FS::create(*description);
    } else if (fs_type == "proc" || fs_type == "ProcFS") {
        fs = ProcFS::create();
    } else if (fs_type == "devpts" || fs_type == "DevPtsFS") {
        fs = DevPtsFS::create();
    } else if (fs_type == "tmp" || fs_type == "TmpFS") {
        fs = TmpFS::create();
    } else {
        return -ENODEV;
    }

    if (!fs->initialize()) {
        dbg() << "mount: failed to initialize " << fs_type << " filesystem, fd - " << source_fd;
        return -ENODEV;
    }

    auto result = VFS::the().mount(fs.release_nonnull(), target_custody, params.flags);
    if (!description.is_null())
        dbg() << "mount: successfully mounted " << description->absolute_path() << " on " << target;
    else
        dbg() << "mount: successfully mounted " << target;
    return result;
}

int Process::sys$umount(const char* user_mountpoint, size_t mountpoint_length)
{
    if (!is_superuser())
        return -EPERM;

    REQUIRE_NO_PROMISES;

    if (!validate_read(user_mountpoint, mountpoint_length))
        return -EFAULT;

    auto mountpoint = get_syscall_path_argument(user_mountpoint, mountpoint_length);
    if (mountpoint.is_error())
        return mountpoint.error();

    auto custody_or_error = VFS::the().resolve_path(mountpoint.value(), current_directory());
    if (custody_or_error.is_error())
        return custody_or_error.error();

    auto& guest_inode = custody_or_error.value()->inode();
    return VFS::the().unmount(guest_inode);
}

}