| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #pragma once |
| 4 | |
| 5 | #include <mos/filesystem/fs_types.h> |
| 6 | #include <mos/filesystem/vfs_types.hpp> |
| 7 | #include <mos/lib/structures/list.hpp> |
| 8 | #include <mos/mos_global.h> |
| 9 | |
| 10 | /** |
| 11 | * @defgroup dentry Directory Entry |
| 12 | * @brief Directory entry |
| 13 | * @ingroup vfs |
| 14 | * |
| 15 | * @details |
| 16 | * A dentry is a directory entry, it is a reference to an inode. |
| 17 | * |
| 18 | * dentry cache policy: The function who references the dentry should be |
| 19 | * responsible for unrefing it. |
| 20 | * |
| 21 | * All existing files' dentries have a reference count of 0 at the start. |
| 22 | * When a file is opened, the dentry will be referenced, and the reference |
| 23 | * count will be incremented by 1. |
| 24 | * |
| 25 | * For all directories, the initial reference count is also 0, but when |
| 26 | * a directory is opened, the reference count will be incremented by 1. |
| 27 | * |
| 28 | * When mounting a filesystem, the root dentry of the filesystem is |
| 29 | * inserted into the dentry cache, and will have a reference count of 1. |
| 30 | * The mountpoint itself will have its reference count incremented by 1. |
| 31 | * |
| 32 | * For the root dentry ("/"), the reference count is 2, one for the mountpoint, |
| 33 | * and one for the dentry cache. |
| 34 | * @{ |
| 35 | */ |
| 36 | |
| 37 | enum LastSegmentResolveFlag |
| 38 | { |
| 39 | // bit 0, 1: the operation only succeeds if the inode is a... |
| 40 | RESOLVE_EXPECT_FILE = 1 << 0, |
| 41 | RESOLVE_EXPECT_DIR = 1 << 1, |
| 42 | RESOLVE_EXPECT_ANY_TYPE = RESOLVE_EXPECT_FILE | RESOLVE_EXPECT_DIR, |
| 43 | |
| 44 | // bit 2: follow symlinks? |
| 45 | // only for the last segment, (if it is a symlink) |
| 46 | RESOLVE_SYMLINK_NOFOLLOW = 1 << 2, |
| 47 | |
| 48 | // bit 3, 4: the operation only succeeds if... |
| 49 | RESOLVE_EXPECT_EXIST = 1 << 3, |
| 50 | RESOLVE_EXPECT_NONEXIST = 1 << 4, |
| 51 | RESOLVE_EXPECT_ANY_EXIST = RESOLVE_EXPECT_EXIST | RESOLVE_EXPECT_NONEXIST, |
| 52 | }; |
| 53 | |
| 54 | MOS_ENUM_FLAGS(LastSegmentResolveFlag, LastSegmentResolveFlags); |
| 55 | |
| 56 | /** |
| 57 | * @brief Check if a path is absolute |
| 58 | * |
| 59 | * @param path The path to check |
| 60 | * @return true if the path is absolute (starts with a '/'), false otherwise |
| 61 | */ |
| 62 | should_inline bool path_is_absolute(mos::string_view path) |
| 63 | { |
| 64 | return path[0] == '/'; |
| 65 | } |
| 66 | |
| 67 | should_inline dentry_t *dentry_parent(const dentry_t &dentry) |
| 68 | { |
| 69 | return tree_parent(&dentry, dentry_t); |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * @brief Check the reference count of a dentry |
| 74 | * |
| 75 | * @param dentry The dentry to check |
| 76 | */ |
| 77 | void dentry_check_refstat(const dentry_t *dentry); |
| 78 | typedef void(dump_refstat_receiver_t)(int depth, const dentry_t *dentry, bool mountroot, void *data); |
| 79 | void dentry_dump_refstat(const dentry_t *dentry, dump_refstat_receiver_t receiver, void *data); |
| 80 | |
| 81 | /** |
| 82 | * @brief Increment the reference count of a dentry |
| 83 | * |
| 84 | * @param dentry The dentry to increment the reference count of |
| 85 | * @return the dentry itself |
| 86 | */ |
| 87 | dentry_t *dentry_ref(dentry_t *dentry); |
| 88 | |
| 89 | /** |
| 90 | * @brief Increment the reference count of a dentry up to a given dentry |
| 91 | * |
| 92 | * @param dentry The dentry to increment the reference count of |
| 93 | * @param root The dentry to stop at |
| 94 | * @return dentry_t* The dentry itself |
| 95 | */ |
| 96 | dentry_t *dentry_ref_up_to(dentry_t *dentry, dentry_t *root); |
| 97 | |
| 98 | /** |
| 99 | * @brief Decrement the reference count of a dentry |
| 100 | * |
| 101 | * @param dentry The dentry to decrement the reference count of |
| 102 | */ |
| 103 | void dentry_unref(dentry_t *dentry); |
| 104 | __nodiscard bool dentry_unref_one_norelease(dentry_t *dentry); |
| 105 | void dentry_try_release(dentry_t *dentry); |
| 106 | |
| 107 | /** |
| 108 | * @brief Attach an inode to a dentry |
| 109 | * |
| 110 | * @param d The dentry to attach the inode to |
| 111 | * @param inode The inode to attach |
| 112 | */ |
| 113 | void dentry_attach(dentry_t *d, inode_t *inode); |
| 114 | |
| 115 | /** |
| 116 | * @brief Detach the inode from a dentry |
| 117 | * |
| 118 | * @param dentry The dentry to detach the inode from |
| 119 | */ |
| 120 | void dentry_detach(dentry_t *dentry); |
| 121 | |
| 122 | /** |
| 123 | * @brief Get the dentry from a file descriptor |
| 124 | * |
| 125 | * @param fd The file descriptor, there's a special case for AT_FDCWD, which corresponds to the process's current working directory |
| 126 | * |
| 127 | * @return The dentry associated with the file descriptor, or NULL if the file descriptor is invalid |
| 128 | */ |
| 129 | PtrResult<dentry_t> dentry_from_fd(fd_t fd); |
| 130 | |
| 131 | /** |
| 132 | * @brief Get a child dentry from a parent dentry |
| 133 | * |
| 134 | * @param parent The parent dentry |
| 135 | * @param name The name of the child dentry |
| 136 | * |
| 137 | * @return The child dentry, always non-NULL, even if the child dentry does not exist in the filesystem |
| 138 | * @note The returned dentry will have its reference count incremented, even if it does not exist. |
| 139 | */ |
| 140 | PtrResult<dentry_t> dentry_lookup_child(dentry_t *parent, mos::string_view name); |
| 141 | |
| 142 | /** |
| 143 | * @brief Lookup a path in the filesystem |
| 144 | * |
| 145 | * @details If the path is absolute, the base is ignored and the path starts from the root_dir |
| 146 | * If the path is relative, the base is used as the starting points |
| 147 | * |
| 148 | * @param starting_dir The starting directory when resolving a [relative] path |
| 149 | * @param root_dir the root directory when resolving the path, the resolved path will not go above this directory |
| 150 | * @param path The path to resolve, can be absolute or relative |
| 151 | * @param flags Flags to control the behavior of the path resolution, see \ref lastseg_resolve_flags_t |
| 152 | * |
| 153 | * @return The parent directory (if such a parent exists) containing (or to contain) the last segment of the path, or |
| 154 | * NULL if any intermediate directory in the path does not exist. |
| 155 | * |
| 156 | */ |
| 157 | PtrResult<dentry_t> dentry_resolve(dentry_t *starting_dir, dentry_t *root_dir, mos::string_view path, LastSegmentResolveFlags flags); |
| 158 | |
| 159 | /** |
| 160 | * @brief Mount a filesystem at a mountpoint |
| 161 | * |
| 162 | * @param mountpoint The mountpoint |
| 163 | * @param root The root directory of the filesystem |
| 164 | * @param fs The filesystem to mount |
| 165 | * |
| 166 | * @return true if the filesystem was mounted successfully, false otherwise |
| 167 | */ |
| 168 | __nodiscard bool dentry_mount(dentry_t *mountpoint, dentry_t *root, filesystem_t *fs); |
| 169 | |
| 170 | /** |
| 171 | * @brief Unmount a filesystem at the mountpoint |
| 172 | * |
| 173 | * @return __nodiscard |
| 174 | */ |
| 175 | __nodiscard dentry_t *dentry_unmount(dentry_t *root); |
| 176 | |
| 177 | /** |
| 178 | * @brief List the contents of a directory |
| 179 | * |
| 180 | * @param dir The directory to list |
| 181 | * @param state The state of the directory iterator |
| 182 | */ |
| 183 | void vfs_populate_listdir_buf(dentry_t *dir, vfs_listdir_state_t *state); |
| 184 | |
| 185 | /** |
| 186 | * @brief Get the path of a dentry |
| 187 | * |
| 188 | * @param dentry The dentry to get the path of |
| 189 | * @param root The root directory, the path will not go above this directory |
| 190 | * @return mos::string String representation of the path |
| 191 | */ |
| 192 | std::optional<mos::string> dentry_path(const dentry_t *dentry, dentry_t *root); |
| 193 | |
| 194 | /**@}*/ |
| 195 | |