1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/filesystem/mount.hpp"
4
5#include "mos/filesystem/dentry.hpp"
6#include "mos/filesystem/vfs.hpp"
7
8#include <mos/hashmap.hpp>
9#include <mos/shared_ptr.hpp>
10#include <mos_stdlib.hpp>
11
12#define VFS_MOUNTPOINT_MAP_SIZE 256
13static mos::HashMap<const dentry_t *, ptr<mount_t>> vfs_mountpoint_map; // dentry_t -> mount_t
14list_head vfs_mountpoint_list;
15
16/**
17 * @brief Given a mounted root dentry, return the mountpoint dentry that points to it
18 *
19 * @param dentry The mounted root dentry
20 * @return dentry_t* The mountpoint dentry
21 */
22dentry_t *dentry_root_get_mountpoint(dentry_t *dentry)
23{
24 MOS_ASSERT(dentry);
25 MOS_ASSERT_X(dentry->name.empty(), "mounted root should not have a name");
26
27 if (dentry == root_dentry)
28 return dentry; // the root dentry is its own mountpoint
29
30 dentry_t *parent = dentry_parent(dentry: *dentry);
31 if (parent == NULL)
32 {
33 // root for some other fs trees
34 return NULL;
35 }
36
37 tree_foreach_child(dentry_t, child, parent)
38 {
39 if (child->is_mountpoint)
40 {
41 const auto mount = dentry_get_mount(dentry: child);
42 if (mount->root == dentry)
43 return child;
44 }
45 }
46
47 return NULL; // not found, possibly just have been unmounted
48}
49
50ptr<mount_t> dentry_get_mount(const dentry_t *dentry)
51{
52 if (!dentry->is_mountpoint)
53 {
54 mos_warn("dentry is not a mountpoint");
55 return NULL;
56 }
57
58 const auto pmount = vfs_mountpoint_map.get(key: dentry);
59 if (!pmount)
60 {
61 mos_warn("mountpoint not found");
62 return NULL;
63 }
64
65 // otherwise the mountpoint must match the dentry
66 MOS_ASSERT((*pmount)->mountpoint == dentry);
67 return *pmount;
68}
69
70bool dentry_mount(dentry_t *mountpoint, dentry_t *root, filesystem_t *fs)
71{
72 MOS_ASSERT_X(root->name.empty(), "mountpoint already has a name");
73 MOS_ASSERT_X(dentry_parent(*root) == NULL, "mountpoint already has a parent");
74
75 dentry_ref(dentry: root);
76
77 tree_node(root)->parent = NULL;
78 if (dentry_parent(dentry: *mountpoint))
79 tree_node(root)->parent = tree_node(dentry_parent(*mountpoint));
80
81 mountpoint->is_mountpoint = true;
82
83 auto mount = mos::make_shared<mount_t>();
84 linked_list_init(list_node(mount));
85 list_node_append(head: &vfs_mountpoint_list, list_node(mount));
86 mount->root = root;
87 mount->superblock = root->inode->superblock;
88 mount->mountpoint = mountpoint;
89 mount->fs = fs;
90 vfs_mountpoint_map.insert(key: mountpoint, value: std::move(t&: mount));
91 return true;
92}
93
94// remove root from the mount tree
95dentry_t *dentry_unmount(dentry_t *root)
96{
97 const auto mount = dentry_get_mount(dentry: dentry_root_get_mountpoint(dentry: root));
98 if (!mount)
99 return NULL;
100
101 dentry_t *mountpoint = mount->mountpoint;
102
103 vfs_mountpoint_map.remove(key: mount->mountpoint);
104 list_node_remove(list_node(mount));
105 // delete mount; // TODO: verify if mount is correctly deleted
106 MOS_ASSERT(mount.use_count() == 1);
107 mountpoint->is_mountpoint = false;
108 return mountpoint;
109}
110