PatchworkOS
Loading...
Searching...
No Matches
path.c
Go to the documentation of this file.
1#include <kernel/fs/path.h>
2
3#include <kernel/fs/dentry.h>
5#include <kernel/fs/vfs.h>
6#include <kernel/log/log.h>
7#include <kernel/log/panic.h>
8#include <kernel/sync/mutex.h>
9
10#include <errno.h>
11#include <string.h>
12
15
16typedef struct path_flag_entry
17{
21 const char* name;
23
25 {.flag = PATH_NONBLOCK, .name = "nonblock"},
26 {.flag = PATH_APPEND, .name = "append"},
27 {.flag = PATH_CREATE, .name = "create"},
28 {.flag = PATH_EXCLUSIVE, .name = "excl"},
29 {.flag = PATH_TRUNCATE, .name = "trunc"},
30 {.flag = PATH_DIRECTORY, .name = "dir"},
31 {.flag = PATH_RECURSIVE, .name = "recur"},
32};
33
34static path_flag_entry_t* path_flags_get(const char* flag, uint64_t length)
35{
36 if (flag == NULL || length == 0)
37 {
38 return NULL;
39 }
40
41 assert(sizeof(flagEntries) / sizeof(flagEntries[0]) == PATH_FLAGS_AMOUNT);
42
43 map_key_t key = map_key_buffer(flag, length);
44 if (length == 1)
45 {
47 if (entry == NULL)
48 {
50 return NULL;
51 }
52
53 return entry;
54 }
55
57 if (entry == NULL)
58 {
60 return NULL;
61 }
62
63 return entry;
64}
65
67{
70
71 for (uint64_t i = 0; i < PATH_FLAGS_AMOUNT; i++)
72 {
73 map_entry_init(&flagEntries[i].entry);
74 map_entry_init(&flagEntries[i].shortEntry);
75
76#ifndef NDEBUG
77 for (uint64_t j = 0; j < PATH_FLAGS_AMOUNT; j++)
78 {
79 if (i != j && flagEntries[i].name[0] == flagEntries[j].name[0])
80 {
81 panic(NULL, "Flag name collision");
82 }
83 }
84#endif
85
87 if (map_insert(&flagMap, &key, &flagEntries[i].entry) == ERR)
88 {
89 panic(NULL, "Failed to init flag map");
90 }
91
92 map_key_t shortKey = map_key_buffer(flagEntries[i].name, 1);
93 if (map_insert(&flagShortMap, &shortKey, &flagEntries[i].shortEntry) == ERR)
94 {
95 panic(NULL, "Failed to init short flag map");
96 }
97 }
98}
99
100uint64_t pathname_init(pathname_t* pathname, const char* string)
101{
102 if (pathname == NULL)
103 {
104 errno = EINVAL;
105 return ERR;
106 }
107
108 memset(pathname->string, 0, MAX_PATH);
109 pathname->flags = PATH_NONE;
110 pathname->isValid = false;
111
112 if (string == NULL)
113 {
114 errno = EINVAL;
115 return ERR;
116 }
117
118 uint64_t length = strnlen_s(string, MAX_PATH);
119 if (length >= MAX_PATH)
120 {
122 return ERR;
123 }
124
125 uint64_t index = 0;
126 uint64_t currentNameLength = 0;
127 while (string[index] != '\0' && string[index] != ':')
128 {
129 if (string[index] == '/')
130 {
131 currentNameLength = 0;
132 }
133 else
134 {
135 if (!PATH_VALID_CHAR(string[index]))
136 {
137 errno = EINVAL;
138 return ERR;
139 }
140 currentNameLength++;
141 if (currentNameLength >= MAX_NAME)
142 {
144 return ERR;
145 }
146 }
147
148 pathname->string[index] = string[index];
149 index++;
150 }
151
152 pathname->string[index] = '\0';
153
154 if (string[index] != ':')
155 {
156 pathname->isValid = true;
157 return 0;
158 }
159
160 index++; // Skip ':'.
161 const char* flags = &string[index];
162
163 while (true)
164 {
165 while (string[index] == ':')
166 {
167 index++;
168 }
169
170 if (string[index] == '\0')
171 {
172 break;
173 }
174
175 const char* token = &string[index];
176 while (string[index] != '\0' && string[index] != ':')
177 {
178 if (!isalnum(string[index]))
179 {
180 errno = EBADFLAG;
181 return ERR;
182 }
183 index++;
184 }
185
186 uint64_t tokenLength = &string[index] - token;
187 if (tokenLength >= MAX_NAME)
188 {
190 return ERR;
191 }
192
193 path_flag_entry_t* flag = path_flags_get(token, tokenLength);
194 if (flag == NULL)
195 {
196 errno = EBADFLAG;
197 return ERR;
198 }
199
200 pathname->flags |= flag->flag;
201 }
202
203 pathname->isValid = true;
204 return 0;
205}
206
207void path_set(path_t* path, mount_t* mount, dentry_t* dentry)
208{
209 if (path->dentry != NULL)
210 {
211 DEREF(path->dentry);
212 path->dentry = NULL;
213 }
214
215 if (path->mount != NULL)
216 {
217 DEREF(path->mount);
218 path->mount = NULL;
219 }
220
221 if (dentry != NULL)
222 {
223 path->dentry = REF(dentry);
224 }
225
226 if (mount != NULL)
227 {
228 path->mount = REF(mount);
229 }
230}
231
232void path_copy(path_t* dest, const path_t* src)
233{
234 if (dest->dentry != NULL)
235 {
236 DEREF(dest->dentry);
237 dest->dentry = NULL;
238 }
239
240 if (dest->mount != NULL)
241 {
242 DEREF(dest->mount);
243 dest->mount = NULL;
244 }
245
246 if (src->dentry != NULL)
247 {
248 dest->dentry = REF(src->dentry);
249 }
250
251 if (src->mount != NULL)
252 {
253 dest->mount = REF(src->mount);
254 }
255}
256
257void path_put(path_t* path)
258{
259 if (path->dentry != NULL)
260 {
261 DEREF(path->dentry);
262 path->dentry = NULL;
263 }
264
265 if (path->mount != NULL)
266 {
267 DEREF(path->mount);
268 path->mount = NULL;
269 }
270}
271
273{
274 if (current->dentry == current->mount->root)
275 {
276 uint64_t iter = 0;
277
278 while (current->dentry == current->mount->root && iter < PATH_HANDLE_DOTDOT_MAX_ITER)
279 {
280 if (current->mount->parent == NULL || current->mount->mountpoint == NULL)
281 {
282 return 0;
283 }
284
285 mount_t* newMount = REF(current->mount->parent);
286 dentry_t* newDentry = REF(current->mount->mountpoint);
287
288 DEREF(current->mount);
289 current->mount = newMount;
290 DEREF(current->dentry);
291 current->dentry = newDentry;
292
293 iter++;
294 }
295
296 if (iter >= PATH_HANDLE_DOTDOT_MAX_ITER)
297 {
298 errno = ELOOP;
299 return ERR;
300 }
301
302 if (current->dentry != current->mount->root)
303 {
304 dentry_t* parent = current->dentry->parent;
305 if (parent == NULL || parent == current->dentry)
306 {
307 errno = ENOENT;
308 return ERR;
309 }
310
311 dentry_t* new_parent = REF(parent);
312 DEREF(current->dentry);
313 current->dentry = new_parent;
314 }
315
316 return 0;
317 }
318 else
319 {
320 assert(current->dentry->parent != NULL); // This can only happen if the filesystem is corrupt.
321 dentry_t* parent = REF(current->dentry->parent);
322 DEREF(current->dentry);
323 current->dentry = parent;
324
325 return 0;
326 }
327}
328
329uint64_t path_walk_single_step(path_t* outPath, const path_t* parent, const char* component, walk_flags_t flags,
330 namespace_t* ns)
331{
332 if (!vfs_is_name_valid(component))
333 {
334 errno = EINVAL;
335 return ERR;
336 }
337
338 path_t current = PATH_EMPTY;
339 path_copy(&current, parent);
340 PATH_DEFER(&current);
341
342 if (atomic_load(&current.dentry->mountCount) > 0)
343 {
344 path_t nextRoot = PATH_EMPTY;
345 if (namespace_traverse_mount(ns, &current, &nextRoot) == ERR)
346 {
347 return ERR;
348 }
349 PATH_DEFER(&nextRoot);
350
351 path_copy(&current, &nextRoot);
352 }
353
354 dentry_t* next = vfs_get_or_lookup_dentry(&current, component);
355 if (next == NULL)
356 {
357 return ERR;
358 }
360
361 if (atomic_load(&next->flags) & DENTRY_NEGATIVE)
362 {
363 if (flags & WALK_NEGATIVE_IS_OK)
364 {
365 path_set(outPath, current.mount, next);
366 return 0;
367 }
368 errno = ENOENT;
369 return ERR;
370 }
371
372 path_set(outPath, current.mount, next);
373 return 0;
374}
375
376uint64_t path_walk(path_t* outPath, const pathname_t* pathname, const path_t* start, walk_flags_t flags,
377 namespace_t* ns)
378{
379 if (!PATHNAME_IS_VALID(pathname))
380 {
381 errno = EINVAL;
382 return ERR;
383 }
384
385 if (pathname->string[0] == '\0')
386 {
387 errno = EINVAL;
388 return ERR;
389 }
390
391 path_t current = PATH_EMPTY;
392 const char* p = pathname->string;
393 if (pathname->string[0] == '/')
394 {
395 if (namespace_get_root_path(ns, &current) == ERR)
396 {
397 return ERR;
398 }
399 p++;
400 }
401 else
402 {
403 if (start == NULL || start->dentry == NULL || start->mount == NULL)
404 {
405 errno = EINVAL;
406 return ERR;
407 }
408 path_copy(&current, start);
409 }
410 PATH_DEFER(&current);
411
412 assert(current.dentry != NULL && current.mount != NULL);
413
414 if (*p == '\0')
415 {
416 path_copy(outPath, &current);
417 return 0;
418 }
419
420 char component[MAX_NAME];
421 while (*p != '\0')
422 {
423 while (*p == '/')
424 {
425 p++;
426 }
427
428 if (*p == '\0')
429 {
430 break;
431 }
432
433 if (*p == ':')
434 {
435 errno = EBADFLAG;
436 return ERR;
437 }
438
439 const char* componentStart = p;
440 while (*p != '\0' && *p != '/')
441 {
442 if (!PATH_VALID_CHAR(*p))
443 {
444 errno = EINVAL;
445 return ERR;
446 }
447 p++;
448 }
449
450 uint64_t len = p - componentStart;
451 if (len >= MAX_NAME)
452 {
454 return ERR;
455 }
456
457 memcpy(component, componentStart, len);
458 component[len] = '\0';
459
460 if (strcmp(component, ".") == 0)
461 {
462 continue;
463 }
464
465 if (strcmp(component, "..") == 0)
466 {
467 if (path_handle_dotdot(&current) == ERR)
468 {
469 return ERR;
470 }
471 continue;
472 }
473
475 if (path_walk_single_step(&next, &current, component, flags, ns) == ERR)
476 {
477 return ERR;
478 }
479
480 path_copy(&current, &next);
481 path_put(&next);
482
483 if (atomic_load(&current.dentry->flags) & DENTRY_NEGATIVE)
484 {
485 if (flags & WALK_NEGATIVE_IS_OK)
486 {
487 break;
488 }
489
490 errno = ENOENT;
491 return ERR;
492 }
493 }
494
495 if (atomic_load(&current.dentry->mountCount) > 0)
496 {
497 path_t root = PATH_EMPTY;
498 if (namespace_traverse_mount(ns, &current, &root) == ERR)
499 {
500 return ERR;
501 }
502
503 path_copy(&current, &root);
504 path_put(&root);
505 }
506
507 path_copy(outPath, &current);
508 return 0;
509}
510
511uint64_t path_walk_parent(path_t* outPath, const pathname_t* pathname, const path_t* start, char* outLastName,
512 walk_flags_t flags, namespace_t* ns)
513{
514 if (!PATHNAME_IS_VALID(pathname) || outPath == NULL || outLastName == NULL)
515 {
516 errno = EINVAL;
517 return ERR;
518 }
519
520 if (pathname->string[0] == '\0')
521 {
522 errno = EINVAL;
523 return ERR;
524 }
525
526 memset(outLastName, 0, MAX_NAME);
527
528 if (strcmp(pathname->string, "/") == 0)
529 {
530 errno = ENOENT;
531 return ERR;
532 }
533
534 char string[MAX_PATH];
535 strncpy(string, pathname->string, MAX_PATH - 1);
536 string[MAX_PATH - 1] = '\0';
537
538 uint64_t len = strnlen_s(string, MAX_PATH);
539 while (len > 1 && string[len - 1] == '/')
540 {
541 string[len - 1] = '\0';
542 len--;
543 }
544
545 char* lastSlash = strrchr(string, '/');
546 if (lastSlash == NULL)
547 {
548 if (start == NULL)
549 {
550 errno = EINVAL;
551 return ERR;
552 }
553
554 strncpy(outLastName, string, MAX_NAME - 1);
555 outLastName[MAX_NAME - 1] = '\0';
556
557 path_copy(outPath, start);
558 return 0;
559 }
560
561 char* lastComponent = lastSlash + 1;
562 strncpy(outLastName, lastComponent, MAX_NAME - 1);
563 outLastName[MAX_NAME - 1] = '\0';
564
565 if (lastSlash == string)
566 {
567 string[1] = '\0';
568 }
569 else
570 {
571 *lastSlash = '\0';
572 }
573
574 pathname_t parentPathname;
575 if (pathname_init(&parentPathname, string) == ERR)
576 {
577 return ERR;
578 }
579
580 return path_walk(outPath, &parentPathname, start, flags, ns);
581}
582
583uint64_t path_to_name(const path_t* path, pathname_t* pathname)
584{
585 if (path == NULL || path->dentry == NULL || path->mount == NULL || pathname == NULL)
586 {
587 errno = EINVAL;
588 return ERR;
589 }
590
591 memset(pathname->string, 0, MAX_PATH);
592 pathname->flags = PATH_NONE;
593 pathname->isValid = false;
594
595 path_t current = PATH_EMPTY;
596 path_copy(&current, path);
597 PATH_DEFER(&current);
598
599 uint64_t index = MAX_PATH - 1;
600 pathname->string[index] = '\0';
601
602 while (true)
603 {
604 if (DENTRY_IS_ROOT(current.dentry))
605 {
606 if (current.mount->parent == NULL)
607 {
608 pathname->string[index] = '/';
609 break;
610 }
611 path_set(&current, current.mount->parent, current.mount->mountpoint);
612 continue;
613 }
614
615 uint64_t nameLength = strnlen_s(current.dentry->name, MAX_NAME);
616
617 if (index < nameLength + 1)
618 {
620 return ERR;
621 }
622
623 if (nameLength != 0)
624 {
625 index -= nameLength;
626 memcpy(pathname->string + index, current.dentry->name, nameLength);
627
628 index--;
629 pathname->string[index] = '/';
630 }
631
632 path_set(&current, current.mount, current.dentry->parent);
633 }
634
635 uint64_t length = MAX_PATH - index;
636 memmove(pathname->string, pathname->string + index, length);
637 pathname->string[length] = '\0';
638 pathname->isValid = true;
639 return 0;
640}
#define MAX_NAME
Maximum length of names.
Definition MAX_NAME.h:11
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
static mount_t * mount
Definition acpi.c:15
#define assert(expression)
Definition assert.h:29
_PUBLIC int isalnum(int c)
Definition isalnum.c:5
#define DENTRY_IS_ROOT(dentry)
Macro to check if a dentry is the root entry in its filesystem.
Definition dentry.h:43
@ DENTRY_NEGATIVE
Definition dentry.h:59
uint64_t namespace_get_root_path(namespace_t *ns, path_t *outPath)
Get the root path of a namespace.
Definition namespace.c:274
uint64_t namespace_traverse_mount(namespace_t *ns, const path_t *mountpoint, path_t *outRoot)
Traverse a mountpoint path to the root of the mounted filesystem.
Definition namespace.c:73
uint64_t path_walk_parent(path_t *outPath, const pathname_t *pathname, const path_t *start, char *outLastName, walk_flags_t flags, namespace_t *ns)
Traverse a pathname to its parent and get the last component name.
Definition path.c:511
void path_put(path_t *path)
Put a path.
Definition path.c:257
#define PATH_CREATE(inMount, inDentry)
Helper to create a path.
Definition path.h:195
uint64_t path_walk(path_t *outPath, const pathname_t *pathname, const path_t *start, walk_flags_t flags, namespace_t *ns)
Traverse a pathname from a specified starting path.
Definition path.c:376
#define PATH_DEFER(path)
Defer path put.
Definition path.h:53
#define PATH_HANDLE_DOTDOT_MAX_ITER
Maximum iterations to handle .. in a path.
Definition path.h:74
uint64_t path_walk_single_step(path_t *outPath, const path_t *parent, const char *component, walk_flags_t flags, namespace_t *ns)
Traverse a single component from a parent path.
Definition path.c:329
path_flags_t
Path flags.
Definition path.h:83
walk_flags_t
Flags for walking a path.
Definition path.h:100
void path_set(path_t *path, mount_t *mount, dentry_t *dentry)
Set a path.
Definition path.c:207
#define PATH_VALID_CHAR(ch)
Check if a char is valid.
Definition path.h:66
#define PATHNAME_IS_VALID(pathname)
Check if a pathname is valid.
Definition path.h:143
void path_flags_init(void)
Initialize path flags resolution.
Definition path.c:66
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:100
void path_copy(path_t *dest, const path_t *src)
Copy a path.
Definition path.c:232
uint64_t path_to_name(const path_t *path, pathname_t *pathname)
Convert a path to a pathname.
Definition path.c:583
#define PATH_EMPTY
Helper to create an empty path.
Definition path.h:182
@ PATH_NONBLOCK
Definition path.h:85
@ PATH_EXCLUSIVE
Definition path.h:88
@ PATH_NONE
Definition path.h:84
@ PATH_TRUNCATE
Definition path.h:89
@ PATH_DIRECTORY
Definition path.h:90
@ PATH_RECURSIVE
Definition path.h:91
@ PATH_APPEND
Definition path.h:86
@ PATH_FLAGS_AMOUNT
Definition path.h:92
@ WALK_NEGATIVE_IS_OK
If a negative dentry is ok, if not specified then it is considered an error.
Definition path.h:102
bool vfs_is_name_valid(const char *name)
Check if a name is valid.
Definition vfs.c:401
dentry_t * vfs_get_or_lookup_dentry(const path_t *parent, const char *name)
Get or lookup a dentry for the given name. Will NOT traverse mountpoints.
Definition vfs.c:227
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:362
static map_key_t map_key_buffer(const void *buffer, uint64_t length)
Create a map key from a buffer.
Definition map.h:99
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:191
uint64_t map_init(map_t *map)
Initialize a map.
Definition map.c:172
map_entry_t * map_get(map_t *map, const map_key_t *key)
Get a value from the map by key.
Definition map.c:236
static map_key_t map_key_string(const char *str)
Create a map key from a string.
Definition map.h:130
#define DEREF_DEFER(ptr)
RAII-style cleanup for scoped references.
Definition ref.h:54
#define REF(ptr)
Increment reference count.
Definition ref.h:65
#define DEREF(ptr)
Decrement reference count.
Definition ref.h:80
#define ENOENT
No such file or directory.
Definition errno.h:42
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ELOOP
Too many symbolic links encountered.
Definition errno.h:232
#define ENAMETOOLONG
File name too long.
Definition errno.h:212
#define EBADFLAG
Invalid path flag.
Definition errno.h:707
#define errno
Error number variable.
Definition errno.h:27
#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.
static uint64_t path_handle_dotdot(path_t *current)
Definition path.c:272
static map_t flagMap
Definition path.c:13
static path_flag_entry_t flagEntries[]
Definition path.c:24
static path_flag_entry_t * path_flags_get(const char *flag, uint64_t length)
Definition path.c:34
static map_t flagShortMap
Definition path.c:14
static void start()
Definition main.c:542
static atomic_long next
Definition main.c:10
#define atomic_load(object)
Definition stdatomic.h:288
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC void * memmove(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
_PUBLIC char * strncpy(char *_RESTRICT s1, const char *_RESTRICT s2, size_t n)
Definition strncpy.c:3
_PUBLIC void * memcpy(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
Definition memcpy.c:4
_PUBLIC char * strrchr(const char *s, int c)
Definition strrchr.c:3
_PUBLIC int strcmp(const char *s1, const char *s2)
Definition strcmp.c:3
_PUBLIC void * memset(void *s, int c, size_t n)
Definition memset.c:4
size_t strnlen_s(const char *s, size_t maxsize)
Definition strnlen_s.c:4
Directory entry structure.
Definition dentry.h:83
atomic_uint64_t mountCount
Definition dentry.h:114
char name[MAX_NAME]
Definition dentry.h:86
dentry_t * parent
Definition dentry.h:88
Map entry structure.
Definition map.h:57
Map key stucture.
Definition map.h:45
Hash map structure.
Definition map.h:76
Mount structure.
Definition mount.h:36
dentry_t * mountpoint
The dentry that this filesystem is mounted on, can be NULL for the root filesystem.
Definition mount.h:41
dentry_t * root
The root dentry of the mounted filesystem.
Definition mount.h:42
mount_t * parent
The parent mount, can be NULL for the root filesystem.
Definition mount.h:43
Definition path.c:17
path_flags_t flag
Definition path.c:20
map_entry_t entry
Definition path.c:18
const char * name
Definition path.c:21
map_entry_t shortEntry
Definition path.c:19
Path structure.
Definition path.h:110
mount_t * mount
Definition path.h:111
dentry_t * dentry
Definition path.h:112
Pathname structure.
Definition path.h:122
char string[MAX_PATH]
Definition path.h:123
path_flags_t flags
Definition path.h:124
bool isValid
Definition path.h:125