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 |
13 | static hashmap_t vfs_mountpoint_map = { 0 }; // dentry_t -> mount_t |
14 | |
15 | static 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 | } |
19 | MOS_INIT(PRE_VFS, mountpoint_map_init); |
20 | list_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 | */ |
28 | dentry_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 | |
56 | mount_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 | |
76 | bool 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 |
107 | dentry_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 | |