summaryrefslogtreecommitdiff
path: root/Kernel/Syscalls/unveil.cpp
blob: 9e4257964c9dd9711c3650940db191f9072a0daf (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
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/LexicalPath.h>
#include <AK/StringView.h>
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/Process.h>

namespace Kernel {

KResultOr<int> Process::sys$unveil(Userspace<const Syscall::SC_unveil_params*> user_params)
{
    Syscall::SC_unveil_params params;
    if (!copy_from_user(&params, user_params))
        return EFAULT;

    if (!params.path.characters && !params.permissions.characters) {
        m_veil_state = VeilState::Locked;
        return 0;
    }

    if (m_veil_state == VeilState::Locked)
        return EPERM;

    if (!params.path.characters || !params.permissions.characters)
        return EINVAL;

    if (params.permissions.length > 5)
        return EINVAL;

    auto path_or_error = get_syscall_path_argument(params.path);
    if (path_or_error.is_error())
        return path_or_error.error();
    auto& path = *path_or_error.value();

    if (path.is_empty() || !path.view().starts_with('/'))
        return EINVAL;

    auto permissions = copy_string_from_user(params.permissions);
    if (permissions.is_null())
        return EFAULT;

    // Let's work out permissions first...
    unsigned new_permissions = 0;
    for (const char permission : permissions) {
        switch (permission) {
        case 'r':
            new_permissions |= UnveilAccess::Read;
            break;
        case 'w':
            new_permissions |= UnveilAccess::Write;
            break;
        case 'x':
            new_permissions |= UnveilAccess::Execute;
            break;
        case 'c':
            new_permissions |= UnveilAccess::CreateOrRemove;
            break;
        case 'b':
            new_permissions |= UnveilAccess::Browse;
            break;
        default:
            return EINVAL;
        }
    }

    // Now, let's try and resolve the path and obtain custody of the inode on the disk, and if not, bail out with
    // the error from resolve_path_without_veil()
    // However, if the user specified unveil() with "c" permissions, we don't set errno if ENOENT is encountered,
    // because they most likely intend the program to create the file for them later on.
    // If this case is encountered, the parent node of the path is returned and the custody of that inode is used instead.
    RefPtr<Custody> parent_custody; // Parent inode in case of ENOENT
    String new_unveiled_path;
    auto custody_or_error = VFS::the().resolve_path_without_veil(path.view(), root_directory(), &parent_custody);
    if (!custody_or_error.is_error()) {
        new_unveiled_path = custody_or_error.value()->absolute_path();
    } else if (custody_or_error.error() == -ENOENT && parent_custody && (new_permissions & UnveilAccess::CreateOrRemove)) {
        String basename = LexicalPath(path.view()).basename();
        new_unveiled_path = String::formatted("{}/{}", parent_custody->absolute_path(), basename);
    } else {
        // FIXME Should this be EINVAL?
        return custody_or_error.error();
    }

    LexicalPath lexical_path(new_unveiled_path);
    auto it = lexical_path.parts().begin();
    auto& matching_node = m_unveiled_paths.traverse_until_last_accessible_node(it, lexical_path.parts().end());
    if (it.is_end()) {
        auto old_permissions = matching_node.permissions();
        // Allow "elevating" the permissions when the permissions are inherited from root (/),
        // as that would be the first time this path is unveiled.
        if (old_permissions != UnveilAccess::None || !matching_node.permissions_inherited_from_root()) {
            if (new_permissions & ~old_permissions)
                return EPERM;
        }
        matching_node.set_metadata({ matching_node.path(), (UnveilAccess)new_permissions, true, false });
        return 0;
    }

    matching_node.insert(
        it,
        lexical_path.parts().end(),
        { new_unveiled_path, (UnveilAccess)new_permissions, true },
        [](auto& parent, auto& it) -> Optional<UnveilMetadata> { return UnveilMetadata { String::formatted("{}/{}", parent.path(), *it), parent.permissions(), false, parent.permissions_inherited_from_root() }; });
    VERIFY(m_veil_state != VeilState::Locked);
    m_veil_state = VeilState::Dropped;
    return 0;
}

}