PatchworkOS  966e257
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
4#include <stdio.h>
5
6#include <kernel/fs/inode.h>
7#include <kernel/fs/vfs.h>
8#include <kernel/log/log.h>
9#include <kernel/log/panic.h>
10#include <kernel/sched/thread.h>
11#include <kernel/sync/lock.h>
12#include <kernel/sync/mutex.h>
13
14#include <stdlib.h>
15
18
19static map_key_t dentry_cache_key(dentry_id_t parentId, const char* name)
20{
21 struct
22 {
23 dentry_id_t parentId;
24 char name[MAX_NAME];
25 } buffer;
26 buffer.parentId = parentId;
27 memset(buffer.name, 0, sizeof(buffer.name));
28 strncpy_s(buffer.name, sizeof(buffer.name), name, MAX_NAME - 1);
29
30 return map_key_buffer(&buffer, sizeof(buffer));
31}
32
34{
35 map_key_t key = dentry_cache_key(dentry->parent->id, dentry->name);
36
38 if (map_insert(&dentryCache, &key, &dentry->mapEntry) == ERR)
39 {
40 return ERR;
41 }
42
43 return 0;
44}
45
51
53{
55
56 dentry_t* dentry = CONTAINER_OF_SAFE(map_get(&dentryCache, key), dentry_t, mapEntry);
57 if (dentry == NULL)
58 {
59 errno = ENOENT;
60 return NULL;
61 }
62
63 if (atomic_load(&dentry->ref.count) == 0) // Is currently being removed
64 {
65 errno = ENOENT;
66 return NULL;
67 }
68
69 return REF(dentry);
70}
71
72static void dentry_free(dentry_t* dentry)
73{
74 if (dentry == NULL)
75 {
76 return;
77 }
78
79 assert(dentry->parent != NULL);
80
81 dentry_cache_remove(dentry);
82
83 if (!DENTRY_IS_ROOT(dentry))
84 {
85 assert(dentry->parent != NULL);
86 assert(dentry->parent->inode != NULL);
87
88 mutex_acquire(&dentry->parent->inode->mutex);
89 list_remove(&dentry->parent->children, &dentry->siblingEntry);
90 mutex_release(&dentry->parent->inode->mutex);
91
92 UNREF(dentry->parent);
93 dentry->parent = NULL;
94 }
95
96 if (dentry->ops != NULL && dentry->ops->cleanup != NULL)
97 {
98 dentry->ops->cleanup(dentry);
99 }
100
101 if (dentry->inode != NULL)
102 {
103 atomic_fetch_sub_explicit(&dentry->inode->dentryCount, 1, memory_order_relaxed);
104 UNREF(dentry->inode);
105 dentry->inode = NULL;
106 }
107
108 UNREF(dentry->superblock);
109 dentry->superblock = NULL;
110
111 free(dentry);
112}
113
114dentry_t* dentry_new(superblock_t* superblock, dentry_t* parent, const char* name)
115{
116 if (name == NULL || superblock == NULL)
117 {
118 errno = EINVAL;
119 return NULL;
120 }
121
122 size_t nameLen = strnlen_s(name, MAX_NAME);
123 if (nameLen >= MAX_NAME || nameLen == 0)
124 {
125 errno = EINVAL;
126 return NULL;
127 }
128
129 assert(parent == NULL || superblock == parent->superblock);
130
131 dentry_t* dentry = malloc(sizeof(dentry_t));
132 if (dentry == NULL)
133 {
134 return NULL;
135 }
136
137 ref_init(&dentry->ref, dentry_free);
138 map_entry_init(&dentry->mapEntry);
139 dentry->id = vfs_id_get();
140 strncpy(dentry->name, name, MAX_NAME - 1);
141 dentry->name[MAX_NAME - 1] = '\0';
142 dentry->inode = NULL;
143 atomic_init(&dentry->flags, DENTRY_NEGATIVE);
144 dentry->parent = parent != NULL
145 ? REF(parent)
146 : dentry; // We set its parent now but its only added to its list when it is made positive.
148 list_init(&dentry->children);
149 dentry->superblock = REF(superblock);
150 dentry->ops = dentry->superblock != NULL ? dentry->superblock->dentryOps : NULL;
151 dentry->private = NULL;
152 atomic_init(&dentry->mountCount, 0);
153 list_entry_init(&dentry->otherEntry);
154
155 if (dentry_cache_add(dentry) == ERR)
156 {
157 UNREF(dentry);
158 return NULL;
159 }
160
161 return dentry;
162}
163
164dentry_t* dentry_get(const dentry_t* parent, const char* name)
165{
166 if (parent == NULL || name == NULL)
167 {
168 errno = EINVAL;
169 return NULL;
170 }
171
172 map_key_t key = dentry_cache_key(parent->id, name);
173 return dentry_cache_get(&key);
174}
175
176dentry_t* dentry_lookup(const path_t* parent, const char* name)
177{
178 if (parent == NULL || parent->dentry == NULL || parent->mount == NULL || name == NULL)
179 {
180 errno = EINVAL;
181 return NULL;
182 }
183
184 map_key_t key = dentry_cache_key(parent->dentry->id, name);
185 dentry_t* dentry = dentry_cache_get(&key);
186 if (dentry != NULL)
187 {
188 return dentry;
189 }
190
191 if (!dentry_is_dir(parent->dentry))
192 {
193 errno = ENOENT;
194 return NULL;
195 }
196
197 dentry = dentry_new(parent->dentry->superblock, parent->dentry, name);
198 if (dentry == NULL)
199 {
200 // This logic is a bit complex, but im pretty confident its correct.
201 if (errno == EEXIST) // Dentry was created after we called dentry_cache_get but before dentry_new
202 {
203 // If this fails then the dentry was deleted in between dentry_new and here, which should be fine?
204 return dentry_cache_get(&key);
205 }
206 return NULL;
207 }
208
210
211 inode_t* dir = parent->dentry->inode;
212 if (dir->ops == NULL || dir->ops->lookup == NULL)
213 {
214 return dentry; // Leave it negative
215 }
216
217 if (dir->ops->lookup(dir, dentry) == ERR)
218 {
219 UNREF(dentry);
220 return NULL;
221 }
222
223 return dentry;
224}
225
227{
228 if (dentry == NULL || inode == NULL)
229 {
230 return;
231 }
232
233 atomic_fetch_add_explicit(&inode->dentryCount, 1, memory_order_relaxed);
234 dentry->inode = REF(inode);
235 if (!DENTRY_IS_ROOT(dentry))
236 {
237 list_push_back(&dentry->parent->children, &dentry->siblingEntry);
238 }
239
240 atomic_fetch_and(&dentry->flags, ~DENTRY_NEGATIVE);
241}
242
244{
245 if (dentry == NULL)
246 {
247 return false;
248 }
249
250 return !(atomic_load(&dentry->flags) & DENTRY_NEGATIVE);
251}
252
254{
255 if (dentry == NULL)
256 {
257 return false;
258 }
259
260 if (!dentry_is_positive(dentry))
261 {
262 return false;
263 }
264
265 return dentry->inode->type == INODE_FILE;
266}
267
269{
270 if (dentry == NULL)
271 {
272 return false;
273 }
274
275 if (!dentry_is_positive(dentry))
276 {
277 return false;
278 }
279
280 return dentry->inode->type == INODE_DIR;
281}
282
292
293static void getdents_write(getdents_ctx_t* ctx, inode_number_t number, inode_type_t type, const char* name)
294{
295 uint64_t start = *ctx->offset / sizeof(dirent_t);
296 uint64_t amount = ctx->count / sizeof(dirent_t);
297
298 if (ctx->index >= start && ctx->index < start + amount)
299 {
300 dirent_t* dirent = &ctx->buffer[ctx->index - start];
301 dirent->number = number;
302 dirent->type = type;
303 strncpy(dirent->path, name, MAX_PATH - 1);
304 dirent->path[MAX_PATH - 1] = '\0';
305 }
306
307 ctx->index++;
308}
309
311{
312 if (!dentry_is_positive(dentry))
313 {
314 return;
315 }
316
317 getdents_write(ctx, dentry->inode->number, dentry->inode->type, ctx->basePath);
318
319 dentry_t* child;
320 LIST_FOR_EACH(child, &dentry->children, siblingEntry)
321 {
323
324 if (ctx->mode & MODE_RECURSIVE && child->inode->type == INODE_DIR)
325 {
326 char originalBasePath[MAX_PATH];
327 strncpy(originalBasePath, ctx->basePath, MAX_PATH - 1);
328 originalBasePath[MAX_PATH - 1] = '\0';
329
330 snprintf(ctx->basePath, MAX_PATH, "%s/%s", originalBasePath, child->name);
332
333 strncpy(ctx->basePath, originalBasePath, MAX_PATH - 1);
334 ctx->basePath[MAX_PATH - 1] = '\0';
335 }
336 else
337 {
338 char fullPath[MAX_PATH];
339 snprintf(fullPath, MAX_PATH, "%s/%s", ctx->basePath, child->name);
340 getdents_write(ctx, child->inode->number, child->inode->type, fullPath);
341 }
342 }
343}
344
346{
347 getdents_ctx_t ctx = {
348 .index = 0,
349 .buffer = buffer,
350 .count = count,
351 .offset = offset,
352 .mode = mode,
353 .basePath = "",
354 };
355
356 if (!dentry_is_positive(dentry))
357 {
358 errno = ENOENT;
359 return ERR;
360 }
361
362 getdents_write(&ctx, dentry->inode->number, dentry->inode->type, ".");
363 getdents_write(&ctx, dentry->parent->inode->number, dentry->parent->inode->type, "..");
364
365 if (mode & MODE_RECURSIVE)
366 {
367 dentry_t* child;
368 LIST_FOR_EACH(child, &dentry->children, siblingEntry)
369 {
371
372 if (child->inode->type == INODE_DIR)
373 {
374 snprintf(ctx.basePath, MAX_PATH, "%s", child->name);
375 getdents_recursive_traversal(&ctx, child);
376 ctx.basePath[0] = '\0';
377 }
378 else
379 {
380 getdents_write(&ctx, child->inode->number, child->inode->type, child->name);
381 }
382 }
383 }
384 else
385 {
386 dentry_t* child;
387 LIST_FOR_EACH(child, &dentry->children, siblingEntry)
388 {
390
391 getdents_write(&ctx, child->inode->number, child->inode->type, child->name);
392 }
393 }
394
395 uint64_t start = *offset / sizeof(dirent_t);
396 if (start >= ctx.index)
397 {
398 return 0;
399 }
400
401 uint64_t entriesWritten = MIN(ctx.index - start, count / sizeof(dirent_t));
402 uint64_t bytesWritten = entriesWritten * sizeof(dirent_t);
403 *offset += bytesWritten;
404 return bytesWritten;
405}
#define MAX_NAME
Maximum length of names.
Definition MAX_NAME.h:11
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
#define assert(expression)
Definition assert.h:29
static dentry_t * dentry_cache_get(map_key_t *key)
Definition dentry.c:52
static rwlock_t dentryCacheLock
Definition dentry.c:17
static void dentry_cache_remove(dentry_t *dentry)
Definition dentry.c:46
static map_key_t dentry_cache_key(dentry_id_t parentId, const char *name)
Definition dentry.c:19
static void getdents_write(getdents_ctx_t *ctx, inode_number_t number, inode_type_t type, const char *name)
Definition dentry.c:293
static void dentry_free(dentry_t *dentry)
Definition dentry.c:72
static map_t dentryCache
Definition dentry.c:16
static void getdents_recursive_traversal(getdents_ctx_t *ctx, dentry_t *dentry)
Definition dentry.c:310
static uint64_t dentry_cache_add(dentry_t *dentry)
Definition dentry.c:33
void dentry_make_positive(dentry_t *dentry, inode_t *inode)
Make a dentry positive by associating it with an inode.
Definition dentry.c:226
dentry_t * dentry_new(superblock_t *superblock, dentry_t *parent, const char *name)
Create a new dentry.
Definition dentry.c:114
uint64_t dentry_id_t
Dentry ID type.
Definition dentry.h:55
dentry_t * dentry_lookup(const path_t *parent, const char *name)
Lookup a dentry for the given name. Will NOT traverse mountpoints.
Definition dentry.c:176
bool dentry_is_positive(dentry_t *dentry)
Check if a dentry is positive.
Definition dentry.c:243
uint64_t dentry_generic_getdents(dentry_t *dentry, dirent_t *buffer, uint64_t count, uint64_t *offset, mode_t mode)
Helper function for a basic getdents.
Definition dentry.c:345
bool dentry_is_dir(dentry_t *dentry)
Check if the inode associated with a dentry is a directory.
Definition dentry.c:268
#define DENTRY_IS_ROOT(dentry)
Macro to check if a dentry is the root entry in its filesystem.
Definition dentry.h:65
dentry_t * dentry_get(const dentry_t *parent, const char *name)
Get a dentry for the given name. Will NOT traverse mountpoints.
Definition dentry.c:164
bool dentry_is_file(dentry_t *dentry)
Check if the inode associated with a dentry is a file.
Definition dentry.c:253
@ DENTRY_NEGATIVE
Dentry is negative (no associated inode).
Definition dentry.h:49
mode_t
Path flags and permissions.
Definition path.h:74
@ MODE_RECURSIVE
Definition path.h:85
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 RWLOCK_READ_SCOPE(lock)
Acquires a rwlock for reading for the reminder of the current scope.
Definition rwlock.h:29
#define RWLOCK_CREATE()
Create a rwlock initializer.
Definition rwlock.h:47
#define RWLOCK_WRITE_SCOPE(lock)
Acquires a rwlock for writing for the reminder of the current scope.
Definition rwlock.h:38
static map_key_t map_key_buffer(const void *buffer, uint64_t length)
Create a map key from a buffer.
Definition map.h:112
void map_entry_init(map_entry_t *entry)
Initialize a map entry.
Definition map.c:71
uint64_t map_insert(map_t *map, const map_key_t *key, map_entry_t *value)
Insert a key-value pair into the map.
Definition map.c:204
void map_remove(map_t *map, map_entry_t *entry)
Remove a entry from the map.
Definition map.c:353
map_entry_t * map_get(map_t *map, const map_key_t *key)
Get a value from the map by key.
Definition map.c:287
#define MAP_CREATE()
Create a map initializer.
Definition map.h:160
static void ref_init(ref_t *ref, void *free)
Initialize a reference counter.
Definition ref.h:92
#define REF(ptr)
Increment reference count.
Definition ref.h:65
#define UNREF(ptr)
Decrement reference count.
Definition ref.h:80
uint64_t vfs_id_get(void)
Generates a new unique ID, to be used for any VFS object.
Definition vfs.c:765
#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 errno
Error number variable.
Definition errno.h:27
inode_type_t
Inode type enum.
Definition io.h:313
uint64_t inode_number_t
Inode number enum.
Definition io.h:322
@ INODE_FILE
Is a file.
Definition io.h:314
@ INODE_DIR
Is a directory.
Definition io.h:315
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
Definition list.h:63
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:343
static void list_remove(list_t *list, list_entry_t *entry)
Removes a list entry from its current list.
Definition list.h:315
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:182
static void list_init(list_t *list)
Initializes a list.
Definition list.h:196
#define MIN(x, y)
Definition math.h:16
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
#define CONTAINER_OF_SAFE(ptr, type, member)
Safe container of macro.
EFI_PHYSICAL_ADDRESS buffer
Definition mem.c:15
static void start()
Definition main.c:542
static atomic_long count
Definition main.c:10
#define RFLAGS_INTERRUPT_ENABLE
Definition regs.h:32
static uint64_t rflags_read()
Definition regs.h:78
@ memory_order_relaxed
Definition stdatomic.h:116
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:259
#define atomic_load(object)
Definition stdatomic.h:288
#define atomic_fetch_and(object, operand)
Definition stdatomic.h:284
#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 int snprintf(char *_RESTRICT s, size_t n, const char *_RESTRICT format,...)
Definition snprintf.c:3
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC char * strncpy(char *_RESTRICT s1, const char *_RESTRICT s2, size_t n)
Definition strncpy.c:3
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
errno_t strncpy_s(char *_RESTRICT s1, rsize_t s1max, const char *_RESTRICT s2, rsize_t n)
Definition strncpy_s.c:9
size_t strnlen_s(const char *s, size_t maxsize)
Definition strnlen_s.c:4
void(* cleanup)(dentry_t *entry)
Called when the dentry is being freed.
Definition dentry.h:74
Directory entry structure.
Definition dentry.h:84
list_t children
Definition dentry.h:92
inode_t * inode
Will be NULL if the dentry is negative, once positive it will never be NULL.
Definition dentry.h:88
char name[MAX_NAME]
Constant after creation.
Definition dentry.h:87
ref_t ref
Definition dentry.h:85
dentry_id_t id
Definition dentry.h:86
const dentry_ops_t * ops
Definition dentry.h:94
list_entry_t otherEntry
Made available for use by any other subsystems for convenience.
Definition dentry.h:98
dentry_t * parent
Definition dentry.h:90
void * private
Definition dentry.h:95
superblock_t * superblock
Definition dentry.h:93
list_entry_t siblingEntry
Definition dentry.h:91
map_entry_t mapEntry
Definition dentry.h:96
Directory entry struct.
Definition io.h:393
char path[MAX_PATH]
The relative path of the directory.
Definition io.h:396
inode_type_t type
The type of the entries inode.
Definition io.h:395
inode_number_t number
The number of the entries inode.
Definition io.h:394
uint64_t count
Definition dentry.c:287
char basePath[MAX_PATH]
Definition dentry.c:290
uint64_t index
Definition dentry.c:285
uint64_t * offset
Definition dentry.c:288
mode_t mode
Definition dentry.c:289
dirent_t * buffer
Definition dentry.c:286
uint64_t(* lookup)(inode_t *dir, dentry_t *target)
Look up a dentry in a directory inode.
Definition inode.h:93
Inode structure.
Definition inode.h:56
mutex_t mutex
Definition inode.h:72
inode_type_t type
Constant after creation.
Definition inode.h:59
inode_number_t number
Constant after creation.
Definition inode.h:58
const inode_ops_t * ops
Constant after creation.
Definition inode.h:70
Map key stucture.
Definition map.h:56
Hash map structure.
Definition map.h:89
Path structure.
Definition path.h:125
mount_t * mount
Definition path.h:126
dentry_t * dentry
Definition path.h:127
atomic_uint32_t count
Atomic reference counter.
Definition ref.h:40
Read-Write Ticket Lock structure.
Definition rwlock.h:61
Superblock structure.
Definition superblock.h:44
const dentry_ops_t * dentryOps
Definition superblock.h:53