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(const 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 const_cast<dentry_t *>(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
74 if (dentry_parent(dentry: *root))
75 {
76 mWarn << "dentry mount root already has a parent";
77 return false;
78 }
79
80 dentry_ref(dentry: root);
81
82 tree_node(root)->parent = NULL;
83 if (dentry_parent(dentry: *mountpoint))
84 tree_node(root)->parent = tree_node(dentry_parent(*mountpoint));
85
86 mountpoint->is_mountpoint = true;
87
88 auto mount = mos::make_shared<mount_t>();
89 linked_list_init(list_node(mount));
90 list_node_append(head: &vfs_mountpoint_list, list_node(mount));
91 mount->root = root;
92 mount->superblock = root->inode->superblock;
93 mount->mountpoint = mountpoint;
94 mount->fs = fs;
95 vfs_mountpoint_map.insert(key: mountpoint, value: std::move(t&: mount));
96 return true;
97}
98
99// remove root from the mount tree
100dentry_t *dentry_unmount(dentry_t *root)
101{
102 const auto mount = dentry_get_mount(dentry: dentry_root_get_mountpoint(dentry: root));
103 if (!mount)
104 return NULL;
105
106 dentry_t *mountpoint = mount->mountpoint;
107
108 vfs_mountpoint_map.remove(key: mount->mountpoint);
109 list_node_remove(list_node(mount));
110 // delete mount; // TODO: verify if mount is correctly deleted
111 MOS_ASSERT(mount.use_count() == 1);
112 mountpoint->is_mountpoint = false;
113 return mountpoint;
114}
115