PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
dentry.c
Go to the documentation of this file.
1#include <kernel/fs/dentry.h>
2
3#include <kernel/sync/rcu.h>
5#include <stdio.h>
6
7#include <kernel/fs/vfs.h>
8#include <kernel/fs/vnode.h>
9#include <kernel/log/log.h>
10#include <kernel/log/panic.h>
11#include <kernel/mem/cache.h>
12#include <kernel/sched/thread.h>
13#include <kernel/sync/lock.h>
14#include <kernel/sync/mutex.h>
15#include <kernel/sync/seqlock.h>
16
17#include <stdlib.h>
18#include <sys/list.h>
19
20#define DENTRY_MAP_SIZE 4096
23
24static uint64_t dentry_hash(dentry_id_t parentId, const char* name, size_t length)
25{
26 uint64_t hash = hash_object(name, length);
27 hash ^= parentId;
28 return hash % DENTRY_MAP_SIZE;
29}
30
32{
33 size_t length = strlen(dentry->name);
34 uint64_t hash = dentry_hash(dentry->parent->id, dentry->name, length);
35
37 for (dentry_t* iter = map[hash]; iter != NULL; iter = iter->next)
38 {
39 if (iter->parent == dentry->parent && iter->name[length] == '\0' &&
40 memcmp(iter->name, dentry->name, length) == 0)
41 {
42 if (REF_COUNT(iter) > 0)
43 {
45 errno = EEXIST;
46 return ERR;
47 }
48 }
49 }
50 dentry->next = map[hash];
51 map[hash] = dentry;
53
54 return 0;
55}
56
57static void dentry_map_remove(dentry_t* dentry)
58{
59 uint64_t hash = dentry_hash(dentry->parent->id, dentry->name, strlen(dentry->name));
60
62 dentry_t** curr = &map[hash];
63 while (*curr != NULL)
64 {
65 if (*curr == dentry)
66 {
67 *curr = dentry->next;
68 dentry->next = NULL;
69 break;
70 }
71 curr = &(*curr)->next;
72 }
74}
75
76static void dentry_free(dentry_t* dentry)
77{
78 dentry_map_remove(dentry);
79
80 if (!DENTRY_IS_ROOT(dentry))
81 {
82 assert(dentry->parent != NULL);
83 assert(dentry->parent->vnode != NULL);
84
85 mutex_acquire(&dentry->parent->vnode->mutex);
86 list_remove(&dentry->siblingEntry);
87 mutex_release(&dentry->parent->vnode->mutex);
88
89 UNREF(dentry->parent);
90 dentry->parent = NULL;
91 }
92
93 if (dentry->ops != NULL && dentry->ops->cleanup != NULL)
94 {
95 dentry->ops->cleanup(dentry);
96 }
97 dentry->data = NULL;
98
99 if (dentry->vnode != NULL)
100 {
101 atomic_fetch_sub_explicit(&dentry->vnode->dentryCount, 1, memory_order_relaxed);
102 UNREF(dentry->vnode);
103 dentry->vnode = NULL;
104 }
105
106 UNREF(dentry->superblock);
107 dentry->superblock = NULL;
108
109 rcu_call(&dentry->rcu, rcu_call_cache_free, dentry);
110}
111
112static void dentry_ctor(void* ptr)
113{
114 dentry_t* dentry = (dentry_t*)ptr;
115
116 dentry->ref = (ref_t){0};
117 dentry->id = vfs_id_get();
118 dentry->name[0] = '\0';
119 dentry->vnode = NULL;
120 dentry->parent = NULL;
122 list_init(&dentry->children);
123 dentry->superblock = NULL;
124 dentry->ops = NULL;
125 dentry->data = NULL;
126 dentry->next = NULL;
127 atomic_init(&dentry->mountCount, 0);
128 dentry->rcu = (rcu_entry_t){0};
129 list_entry_init(&dentry->otherEntry);
130}
131
133
134dentry_t* dentry_new(superblock_t* superblock, dentry_t* parent, const char* name)
135{
136 if (superblock == NULL)
137 {
138 errno = EINVAL;
139 return NULL;
140 }
141
142 if ((parent == NULL || name == NULL) && ((void*)parent != (void*)name))
143 {
144 errno = EINVAL;
145 return NULL;
146 }
147
148 if (name == NULL)
149 {
150 name = "";
151 }
152
153 assert(parent == NULL || superblock == parent->superblock);
154
155 dentry_t* dentry = cache_alloc(&cache);
156 if (dentry == NULL)
157 {
158 errno = ENOMEM;
159 return NULL;
160 }
161
162 ref_init(&dentry->ref, dentry_free);
163 dentry->superblock = REF(superblock);
164 dentry->ops = superblock->dentryOps;
165 strncpy(dentry->name, name, MAX_NAME);
166 dentry->name[MAX_NAME - 1] = '\0';
167 dentry->parent = parent != NULL ? REF(parent) : dentry;
168
169 if (dentry_map_add(dentry) == ERR)
170 {
171 UNREF(dentry);
172 return NULL;
173 }
174
175 return dentry;
176}
177
179{
180 if (dentry == NULL)
181 {
182 return;
183 }
184
185 dentry_map_remove(dentry);
186}
187
188dentry_t* dentry_rcu_get(const dentry_t* parent, const char* name, size_t length)
189{
190 if (parent == NULL || name == NULL || length == 0)
191 {
192 return NULL;
193 }
194
195 uint64_t hash = dentry_hash(parent->id, name, length);
196 dentry_t* dentry = NULL;
197
198 uint64_t seq;
199 do
200 {
201 seq = seqlock_read_begin(&lock);
202 for (dentry = map[hash]; dentry != NULL; dentry = dentry->next)
203 {
204 if (dentry->parent == parent && dentry->name[length] == '\0' && memcmp(dentry->name, name, length) == 0)
205 {
206 break;
207 }
208 }
209 } while (seqlock_read_retry(&lock, seq));
210
211 if (dentry != NULL && dentry->ops != NULL && dentry->ops->revalidate != NULL)
212 {
213 if (dentry->ops->revalidate(dentry) == ERR)
214 {
215 return NULL;
216 }
217 }
218
219 return dentry;
220}
221
222static dentry_t* dentry_get(const dentry_t* parent, const char* name, size_t length)
223{
225
226 return REF_TRY(dentry_rcu_get(parent, name, length));
227}
228
229dentry_t* dentry_lookup(dentry_t* parent, const char* name, size_t length)
230{
231 if (parent == NULL || name == NULL || length == 0)
232 {
233 errno = EINVAL;
234 return NULL;
235 }
236
237 dentry_t* dentry = dentry_get(parent, name, length);
238 if (dentry != NULL)
239 {
240 return dentry;
241 }
242
243 if (!DENTRY_IS_DIR(parent))
244 {
245 errno = ENOENT;
246 return NULL;
247 }
248
249 char buffer[MAX_NAME];
250 strncpy(buffer, name, length);
251 buffer[length] = '\0';
252
253 dentry = dentry_new(parent->superblock, parent, buffer);
254 if (dentry == NULL)
255 {
256 if (errno == EEXIST)
257 {
258 return dentry_get(parent, name, length);
259 }
260 return NULL;
261 }
262
264
265 vnode_t* dir = parent->vnode;
266 if (dir->ops == NULL || dir->ops->lookup == NULL)
267 {
268 return dentry; // Leave it as negative.
269 }
270
271 if (dir->ops->lookup(dir, dentry) == ERR)
272 {
273 UNREF(dentry);
274 return NULL;
275 }
276
277 if (dentry->ops != NULL && dentry->ops->revalidate != NULL)
278 {
279 if (dentry->ops->revalidate(dentry) == ERR)
280 {
281 UNREF(dentry);
282 return NULL;
283 }
284 }
285
286 return dentry;
287}
288
290{
291 if (dentry == NULL || vnode == NULL)
292 {
293 return;
294 }
295
296 atomic_fetch_add_explicit(&vnode->dentryCount, 1, memory_order_relaxed);
297 dentry->vnode = REF(vnode);
298 if (!DENTRY_IS_ROOT(dentry))
299 {
300 list_push_back(&dentry->parent->children, &dentry->siblingEntry);
301 }
302}
303
305{
306 if (ctx->index++ >= ctx->pos)
307 {
308 if (!ctx->emit(ctx, ".", dentry->vnode->type))
309 {
310 return false;
311 }
312 }
313
314 if (ctx->index++ >= ctx->pos)
315 {
316 if (!ctx->emit(ctx, "..", dentry->parent->vnode->type))
317 {
318 return false;
319 }
320 }
321
322 return true;
323}
324
326{
327 if (!dentry_iterate_dots(dentry, ctx))
328 {
329 return 0;
330 }
331
332 dentry_t* child;
333 LIST_FOR_EACH(child, &dentry->children, siblingEntry)
334 {
335 if (ctx->index++ >= ctx->pos)
336 {
338 if (!ctx->emit(ctx, child->name, child->vnode->type))
339 {
340 return 0;
341 }
342 }
343 }
344
345 return 0;
346}
#define MAX_NAME
Maximum length of names.
Definition MAX_NAME.h:11
#define assert(expression)
Definition assert.h:29
EFI_PHYSICAL_ADDRESS buffer
Definition main.c:237
static seqlock_t lock
Definition dentry.c:22
static dentry_t * map[DENTRY_MAP_SIZE]
Definition dentry.c:21
static dentry_t * dentry_get(const dentry_t *parent, const char *name, size_t length)
Definition dentry.c:222
#define DENTRY_MAP_SIZE
Definition dentry.c:20
static void dentry_map_remove(dentry_t *dentry)
Definition dentry.c:57
static cache_t cache
Definition dentry.c:132
static void dentry_ctor(void *ptr)
Definition dentry.c:112
static uint64_t dentry_hash(dentry_id_t parentId, const char *name, size_t length)
Definition dentry.c:24
static uint64_t dentry_map_add(dentry_t *dentry)
Definition dentry.c:31
static void dentry_free(dentry_t *dentry)
Definition dentry.c:76
static dentry_t * dir
Definition fb.c:16
#define DENTRY_IS_DIR(dentry)
Check if the vnode associated with a dentry is a directory.
Definition dentry.h:83
dentry_t * dentry_new(superblock_t *superblock, dentry_t *parent, const char *name)
Create a new dentry.
Definition dentry.c:134
uint64_t dentry_generic_iterate(dentry_t *dentry, dir_ctx_t *ctx)
Helper function for a basic iterate.
Definition dentry.c:325
uint64_t dentry_id_t
Dentry ID type.
Definition dentry.h:49
dentry_t * dentry_lookup(dentry_t *parent, const char *name, size_t length)
Lookup a dentry for the given name without traversing mountpoints.
Definition dentry.c:229
void dentry_make_positive(dentry_t *dentry, vnode_t *vnode)
Make a dentry positive by associating it with an vnode.
Definition dentry.c:289
void dentry_remove(dentry_t *dentry)
Remove a dentry from the dentry cache.
Definition dentry.c:178
bool dentry_iterate_dots(dentry_t *dentry, dir_ctx_t *ctx)
Helper function to iterate over the special entries "." and "..".
Definition dentry.c:304
#define DENTRY_IS_POSITIVE(dentry)
Check if a dentry is positive.
Definition dentry.h:67
#define DENTRY_IS_ROOT(dentry)
Macro to check if a dentry is the root entry in its filesystem.
Definition dentry.h:59
dentry_t * dentry_rcu_get(const dentry_t *parent, const char *name, size_t length)
Get a dentry from the dentry cache in an RCU read-side critical section without traversing mountpoint...
Definition dentry.c:188
#define CACHE_LINE
Cache line size in bytes.
Definition cache.h:75
void * cache_alloc(cache_t *cache)
Allocate an object from the cache.
Definition cache.c:109
#define CACHE_CREATE(_cache, _name, _size, _alignment, _ctor, _dtor)
Macro to create a cache initializer.
Definition cache.h:148
void mutex_release(mutex_t *mtx)
Releases a mutex.
Definition mutex.c:105
void mutex_acquire(mutex_t *mtx)
Acquires a mutex, blocking until it is available.
Definition mutex.c:28
#define RCU_READ_SCOPE()
RCU read-side critical section for the current scope.
Definition rcu.h:94
void rcu_call_cache_free(void *arg)
Helper callback to free a cache object.
Definition rcu.c:193
void rcu_call(rcu_entry_t *entry, rcu_callback_t func, void *arg)
Add a callback to be executed after a grace period.
Definition rcu.c:72
static uint64_t seqlock_read_begin(seqlock_t *seqlock)
Begins a read operation on a sequence lock.
Definition seqlock.h:98
static void seqlock_write_release(seqlock_t *seqlock)
Releases the write lock of a sequence lock.
Definition seqlock.h:75
static bool seqlock_read_retry(seqlock_t *seqlock, uint64_t seq)
Checks if a read operation on a sequence lock needs to be retried.
Definition seqlock.h:110
#define SEQLOCK_CREATE()
Create a sequence lock initializer.
Definition seqlock.h:40
static void seqlock_write_acquire(seqlock_t *seqlock)
Acquires the write lock of a sequence lock.
Definition seqlock.h:64
uint64_t hash_object(const void *object, uint64_t length)
Hash a object.
Definition map.c:8
#define REF_TRY(ptr)
Increment reference count, but only if the current count is not zero.
Definition ref.h:95
#define REF_COUNT(ptr)
Get current reference count.
Definition ref.h:71
#define REF(ptr)
Increment reference count.
Definition ref.h:82
static void ref_init(ref_t *ref, void *callback)
Initialize a reference counter.
Definition ref.h:130
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:109
uint64_t vfs_id_get(void)
Generates a new unique ID, to be used for any VFS object.
Definition vfs.c:1236
#define ENOENT
No such file or directory.
Definition errno.h:42
#define EEXIST
File exists.
Definition errno.h:117
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ENOMEM
Out of memory.
Definition errno.h:92
#define errno
Error number variable.
Definition errno.h:27
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
Definition list.h:58
static void list_remove(list_entry_t *entry)
Removes a list entry from its current list.
Definition list.h:290
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:322
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:173
static void list_init(list_t *list)
Initializes a list.
Definition list.h:185
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:34
static uint64_t rflags_read(void)
Definition regs.h:80
@ memory_order_relaxed
Definition stdatomic.h:116
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#define atomic_init(obj, value)
Definition stdatomic.h:75
#define atomic_fetch_sub_explicit(object, operand, order)
Definition stdatomic.h:262
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC char * strncpy(char *_RESTRICT s1, const char *_RESTRICT s2, size_t n)
Definition strncpy.c:3
_PUBLIC int memcmp(const void *s1, const void *s2, size_t n)
Definition memcmp.c:3
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
Cache structure.
Definition cache.h:123
uint64_t(* revalidate)(dentry_t *dentry)
Called when the dentry is looked up or retrieved from cache.
Definition dentry.h:130
void(* cleanup)(dentry_t *dentry)
Called when the dentry is being freed.
Definition dentry.h:144
Directory entry structure.
Definition dentry.h:155
list_t children
Definition dentry.h:162
rcu_entry_t rcu
RCU entry for deferred cleanup.
Definition dentry.h:168
char name[MAX_NAME]
The name of the dentry, immutable after creation.
Definition dentry.h:158
ref_t ref
Definition dentry.h:156
dentry_id_t id
Definition dentry.h:157
const dentry_ops_t * ops
Definition dentry.h:164
list_entry_t otherEntry
Made available for use by any other subsystems for convenience.
Definition dentry.h:169
dentry_t * parent
The parent dentry, will be itself if this is the root dentry, immutable after creation.
Definition dentry.h:160
void * data
Definition dentry.h:165
struct dentry * next
Next dentry in the dentry cache hash bucket.
Definition dentry.h:166
vnode_t * vnode
Will be NULL if the dentry is negative, once positive it will never be modified.
Definition dentry.h:159
superblock_t * superblock
Definition dentry.h:163
list_entry_t siblingEntry
Definition dentry.h:161
Directory context used to iterate over directory entries.
Definition dentry.h:97
size_t index
An index that the filesystem can use for its own purposes.
Definition dentry.h:114
size_t pos
The current position in the directory, can be used to skip entries.
Definition dentry.h:112
bool(* emit)(dir_ctx_t *ctx, const char *name, vtype_t type)
Emit function.
Definition dentry.h:111
Intrusive RCU head structure.
Definition rcu.h:65
Reference counting structure.
Definition ref.h:52
Sequence lock structure.
Definition seqlock.h:30
Superblock structure.
Definition superblock.h:33
const dentry_ops_t * dentryOps
Definition superblock.h:42
vnode structure.
Definition vnode.h:48
mutex_t mutex
Definition vnode.h:59
vtype_t type
Definition vnode.h:50