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