PatchworkOS  c9fea19
A non-POSIX operating system.
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 <kernel/utils/map.h>
12#include <stdint.h>
13#include <string.h>
14
17
18typedef struct path_flag_short
19{
22
24 ['r'] = {.mode = MODE_READ},
25 ['w'] = {.mode = MODE_WRITE},
26 ['x'] = {.mode = MODE_EXECUTE},
27 ['n'] = {.mode = MODE_NONBLOCK},
28 ['a'] = {.mode = MODE_APPEND},
29 ['c'] = {.mode = MODE_CREATE},
30 ['e'] = {.mode = MODE_EXCLUSIVE},
31 ['t'] = {.mode = MODE_TRUNCATE},
32 ['d'] = {.mode = MODE_DIRECTORY},
33 ['R'] = {.mode = MODE_RECURSIVE},
34};
35
36typedef struct path_flag
37{
39 const char* name;
41
42static const path_flag_t flags[] = {
43 {.mode = MODE_READ, .name = "read"},
44 {.mode = MODE_WRITE, .name = "write"},
45 {.mode = MODE_EXECUTE, .name = "execute"},
46 {.mode = MODE_NONBLOCK, .name = "nonblock"},
47 {.mode = MODE_APPEND, .name = "append"},
48 {.mode = MODE_CREATE, .name = "create"},
49 {.mode = MODE_EXCLUSIVE, .name = "exclusive"},
50 {.mode = MODE_TRUNCATE, .name = "truncate"},
51 {.mode = MODE_DIRECTORY, .name = "directory"},
52 {.mode = MODE_RECURSIVE, .name = "recursive"},
53};
54
55static mode_t path_flag_to_mode(const char* flag, uint64_t length)
56{
57 if (flag == NULL || length == 0)
58 {
59 return MODE_NONE;
60 }
61
62 for (uint64_t i = 0; i < MODE_AMOUNT; i++)
63 {
64 size_t len = strnlen_s(flags[i].name, MAX_NAME);
65 if (len == length && strncmp(flag, flags[i].name, length) == 0)
66 {
67 return flags[i].mode;
68 }
69 }
70
71 mode_t combinedMode = MODE_NONE;
72 for (uint64_t i = 0; i < length; i++)
73 {
74 if (flag[i] < 0 || (uint8_t)flag[i] >= INT8_MAX)
75 {
76 return MODE_NONE;
77 }
78 mode_t mode = shortFlags[(uint8_t)flag[i]].mode;
79 if (mode == MODE_NONE)
80 {
81 return MODE_NONE;
82 }
83 combinedMode |= mode;
84 }
85
86 return combinedMode;
87}
88
89uint64_t pathname_init(pathname_t* pathname, const char* string)
90{
91 if (pathname == NULL)
92 {
93 errno = EINVAL;
94 return ERR;
95 }
96
97 memset(pathname->string, 0, MAX_PATH);
98 pathname->mode = MODE_NONE;
99 pathname->isValid = false;
100
101 if (string == NULL)
102 {
103 errno = EINVAL;
104 return ERR;
105 }
106
107 uint64_t length = strnlen_s(string, MAX_PATH);
108 if (length >= MAX_PATH)
109 {
111 return ERR;
112 }
113
114 uint64_t index = 0;
115 uint64_t currentNameLength = 0;
116 while (string[index] != '\0' && string[index] != ':')
117 {
118 if (string[index] == '/')
119 {
120 currentNameLength = 0;
121 }
122 else
123 {
124 if (!PATH_VALID_CHAR(string[index]))
125 {
126 errno = EINVAL;
127 return ERR;
128 }
129 currentNameLength++;
130 if (currentNameLength >= MAX_NAME)
131 {
133 return ERR;
134 }
135 }
136
137 pathname->string[index] = string[index];
138 index++;
139 }
140
141 pathname->string[index] = '\0';
142
143 if (string[index] != ':')
144 {
145 pathname->isValid = true;
146 return 0;
147 }
148
149 index++; // Skip ':'.
150 const char* flags = &string[index];
151
152 while (true)
153 {
154 while (string[index] == ':')
155 {
156 index++;
157 }
158
159 if (string[index] == '\0')
160 {
161 break;
162 }
163
164 const char* token = &string[index];
165 while (string[index] != '\0' && string[index] != ':')
166 {
167 if (!isalnum(string[index]))
168 {
169 errno = EINVAL;
170 return ERR;
171 }
172 index++;
173 }
174
175 uint64_t tokenLength = &string[index] - token;
176 if (tokenLength >= MAX_NAME)
177 {
179 return ERR;
180 }
181
182 mode_t mode = path_flag_to_mode(token, tokenLength);
183 if (mode == MODE_NONE)
184 {
185 errno = EINVAL;
186 return ERR;
187 }
188
189 pathname->mode |= mode;
190 }
191
192 pathname->isValid = true;
193 return 0;
194}
195
196void path_set(path_t* path, mount_t* mount, dentry_t* dentry)
197{
198 if (path->dentry != NULL)
199 {
200 UNREF(path->dentry);
201 path->dentry = NULL;
202 }
203
204 if (path->mount != NULL)
205 {
206 UNREF(path->mount);
207 path->mount = NULL;
208 }
209
210 if (dentry != NULL)
211 {
212 path->dentry = REF(dentry);
213 }
214
215 if (mount != NULL)
216 {
217 path->mount = REF(mount);
218 }
219}
220
221void path_copy(path_t* dest, const path_t* src)
222{
223 if (dest->dentry != NULL)
224 {
225 UNREF(dest->dentry);
226 dest->dentry = NULL;
227 }
228
229 if (dest->mount != NULL)
230 {
231 UNREF(dest->mount);
232 dest->mount = NULL;
233 }
234
235 if (src->dentry != NULL)
236 {
237 dest->dentry = REF(src->dentry);
238 }
239
240 if (src->mount != NULL)
241 {
242 dest->mount = REF(src->mount);
243 }
244}
245
246void path_put(path_t* path)
247{
248 if (path->dentry != NULL)
249 {
250 UNREF(path->dentry);
251 path->dentry = NULL;
252 }
253
254 if (path->mount != NULL)
255 {
256 UNREF(path->mount);
257 path->mount = NULL;
258 }
259}
260
261static bool path_is_name_valid(const char* name)
262{
263 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
264 {
265 return false;
266 }
267
268 for (uint64_t i = 0; i < MAX_NAME - 1; i++)
269 {
270 if (name[i] == '\0')
271 {
272 return true;
273 }
274 if (!PATH_VALID_CHAR(name[i]))
275 {
276 return false;
277 }
278 }
279
280 return false;
281}
282
284{
285 if (path->dentry == path->mount->source)
286 {
287 uint64_t iter = 0;
288 while (path->dentry == path->mount->source && iter < PATH_HANDLE_DOTDOT_MAX_ITER)
289 {
290 if (path->mount->parent == NULL || path->mount->target == NULL)
291 {
292 return 0;
293 }
294
295 path_set(path, path->mount->parent, path->mount->target);
296 iter++;
297 }
298
299 if (iter >= PATH_HANDLE_DOTDOT_MAX_ITER)
300 {
301 errno = ELOOP;
302 return ERR;
303 }
304
305 if (path->dentry != path->mount->source)
306 {
307 if (DENTRY_IS_ROOT(path->dentry))
308 {
309 errno = ENOENT;
310 return ERR;
311 }
312
313 path_set(path, path->mount, path->dentry->parent);
314 }
315
316 return 0;
317 }
318
319 path_set(path, path->mount, path->dentry->parent);
320 return 0;
321}
322
323uint64_t path_step(path_t* path, const char* component, namespace_t* ns)
324{
325 if (!path_is_name_valid(component))
326 {
327 errno = EINVAL;
328 return ERR;
329 }
330
331 if (namespace_traverse(ns, path) == ERR)
332 {
333 return ERR;
334 }
335
336 dentry_t* next = dentry_lookup(path, component);
337 if (next == NULL)
338 {
339 return ERR;
340 }
342
343 path_set(path, path->mount, next);
344 return 0;
345}
346
347uint64_t path_walk(path_t* path, const pathname_t* pathname, namespace_t* ns)
348{
349 if (path == NULL || !PATHNAME_IS_VALID(pathname) || ns == NULL)
350 {
351 errno = EINVAL;
352 return ERR;
353 }
354
355 const char* p = pathname->string;
356 if (pathname->string[0] == '/')
357 {
358 if (namespace_get_root_path(ns, path) == ERR)
359 {
360 return ERR;
361 }
362 p++;
363 }
364
365 assert(path->dentry != NULL && path->mount != NULL);
366
367 if (*p == '\0')
368 {
369 return 0;
370 }
371
372 char component[MAX_NAME];
373 while (*p != '\0')
374 {
375 while (*p == '/')
376 {
377 p++;
378 }
379
380 if (*p == '\0')
381 {
382 break;
383 }
384
385 if (*p == ':')
386 {
387 errno = EINVAL;
388 return ERR;
389 }
390
391 const char* componentStart = p;
392 while (*p != '\0' && *p != '/')
393 {
394 if (!PATH_VALID_CHAR(*p))
395 {
396 errno = EINVAL;
397 return ERR;
398 }
399 p++;
400 }
401
402 uint64_t len = p - componentStart;
403 if (len >= MAX_NAME)
404 {
406 return ERR;
407 }
408
409 memcpy(component, componentStart, len);
410 component[len] = '\0';
411
412 if (strcmp(component, ".") == 0)
413 {
414 continue;
415 }
416
417 if (strcmp(component, "..") == 0)
418 {
419 if (path_handle_dotdot(path) == ERR)
420 {
421 return ERR;
422 }
423 continue;
424 }
425
426 if (path_step(path, component, ns) == ERR)
427 {
428 return ERR;
429 }
430 }
431
432 if (namespace_traverse(ns, path) == ERR)
433 {
434 return ERR;
435 }
436
437 return 0;
438}
439
440uint64_t path_walk_parent(path_t* path, const pathname_t* pathname, char* outLastName, namespace_t* ns)
441{
442 if (path == NULL || !PATHNAME_IS_VALID(pathname) || outLastName == NULL || ns == NULL)
443 {
444 errno = EINVAL;
445 return ERR;
446 }
447
448 memset(outLastName, 0, MAX_NAME);
449
450 if (strcmp(pathname->string, "/") == 0)
451 {
452 errno = ENOENT;
453 return ERR;
454 }
455
456 char string[MAX_PATH];
457 strncpy(string, pathname->string, MAX_PATH - 1);
458 string[MAX_PATH - 1] = '\0';
459
460 uint64_t len = strnlen_s(string, MAX_PATH);
461 while (len > 1 && string[len - 1] == '/')
462 {
463 string[len - 1] = '\0';
464 len--;
465 }
466
467 char* lastSlash = strrchr(string, '/');
468 if (lastSlash == NULL)
469 {
470 strncpy(outLastName, string, MAX_NAME - 1);
471 outLastName[MAX_NAME - 1] = '\0';
472 return 0;
473 }
474
475 char* lastComponent = lastSlash + 1;
476 strncpy(outLastName, lastComponent, MAX_NAME - 1);
477 outLastName[MAX_NAME - 1] = '\0';
478
479 if (lastSlash == string)
480 {
481 string[1] = '\0';
482 }
483 else
484 {
485 *lastSlash = '\0';
486 }
487
488 pathname_t parentPathname;
489 if (pathname_init(&parentPathname, string) == ERR)
490 {
491 return ERR;
492 }
493
494 return path_walk(path, &parentPathname, ns);
495}
496
497uint64_t path_walk_parent_and_child(const path_t* from, path_t* outParent, path_t* outChild, const pathname_t* pathname,
498 namespace_t* ns)
499{
500 if (from == NULL || outParent == NULL || outChild == NULL || !PATHNAME_IS_VALID(pathname) || ns == NULL)
501 {
502 errno = EINVAL;
503 return ERR;
504 }
505
506 char lastName[MAX_NAME];
507 path_copy(outParent, from);
508 if (path_walk_parent(outParent, pathname, lastName, ns) == ERR)
509 {
510 return ERR;
511 }
512
513 path_copy(outChild, outParent);
514 if (path_step(outChild, lastName, ns) == ERR)
515 {
516 return ERR;
517 }
518
519 return 0;
520}
521
522uint64_t path_to_name(const path_t* path, pathname_t* pathname)
523{
524 if (path == NULL || path->dentry == NULL || path->mount == NULL || pathname == NULL)
525 {
526 errno = EINVAL;
527 return ERR;
528 }
529
530 memset(pathname->string, 0, MAX_PATH);
531 pathname->mode = MODE_NONE;
532 pathname->isValid = false;
533
534 path_t current = PATH_CREATE(path->mount, path->dentry);
535 PATH_DEFER(&current);
536
537 uint64_t index = MAX_PATH - 1;
538 pathname->string[index] = '\0';
539
540 while (true)
541 {
542 if (DENTRY_IS_ROOT(current.dentry))
543 {
544 if (current.mount->parent == NULL)
545 {
546 pathname->string[index] = '/';
547 break;
548 }
549 path_set(&current, current.mount->parent, current.mount->target);
550 continue;
551 }
552
553 uint64_t nameLength = strnlen_s(current.dentry->name, MAX_NAME);
554
555 if (index < nameLength + 1)
556 {
558 return ERR;
559 }
560
561 if (nameLength != 0)
562 {
563 index -= nameLength;
564 memcpy(pathname->string + index, current.dentry->name, nameLength);
565
566 index--;
567 pathname->string[index] = '/';
568 }
569
570 path_set(&current, current.mount, current.dentry->parent);
571 }
572
573 uint64_t length = MAX_PATH - index;
574 memmove(pathname->string, pathname->string + index, length);
575 pathname->string[length] = '\0';
576 pathname->isValid = true;
577 return 0;
578}
#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
_PUBLIC int isalnum(int c)
Definition isalnum.c:5
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
#define DENTRY_IS_ROOT(dentry)
Macro to check if a dentry is the root entry in its filesystem.
Definition dentry.h:65
uint64_t namespace_get_root_path(namespace_t *ns, path_t *out)
Get the root path of a namespace.
Definition namespace.c:286
uint64_t namespace_traverse(namespace_t *ns, path_t *path)
If the given path is a mountpoint in the namespace, traverse to the mounted filesystem,...
Definition namespace.c:163
mode_t
Path flags and permissions.
Definition path.h:74
void path_put(path_t *path)
Put a path.
Definition path.c:246
#define PATH_CREATE(inMount, inDentry)
Helper to create a path.
Definition path.h:207
#define PATH_DEFER(path)
Defer path put.
Definition path.h:97
uint64_t path_step(path_t *path, const char *component, namespace_t *ns)
Walk a single step in a path.
Definition path.c:323
#define PATH_HANDLE_DOTDOT_MAX_ITER
Maximum iterations to handle .. in a path.
Definition path.h:118
void path_set(path_t *path, mount_t *mount, dentry_t *dentry)
Set a path.
Definition path.c:196
#define PATH_VALID_CHAR(ch)
Check if a char is valid.
Definition path.h:110
#define PATHNAME_IS_VALID(pathname)
Check if a pathname is valid.
Definition path.h:153
uint64_t path_walk_parent(path_t *path, const pathname_t *pathname, char *outLastName, namespace_t *ns)
Walk a pathname to its parent and get the name of the last component.
Definition path.c:440
uint64_t path_walk_parent_and_child(const path_t *from, path_t *outParent, path_t *outChild, const pathname_t *pathname, namespace_t *ns)
Traverse a pathname to its parent and child paths.
Definition path.c:497
uint64_t path_walk(path_t *path, const pathname_t *pathname, namespace_t *ns)
Walk a pathname to a path.
Definition path.c:347
uint64_t pathname_init(pathname_t *pathname, const char *string)
Initialize a pathname.
Definition path.c:89
void path_copy(path_t *dest, const path_t *src)
Copy a path.
Definition path.c:221
uint64_t path_to_name(const path_t *path, pathname_t *pathname)
Convert a path to a pathname.
Definition path.c:522
@ MODE_AMOUNT
Definition path.h:86
@ MODE_CREATE
Definition path.h:81
@ MODE_NONE
Definition path.h:75
@ MODE_APPEND
Definition path.h:80
@ MODE_EXECUTE
Definition path.h:78
@ MODE_NONBLOCK
Definition path.h:79
@ MODE_TRUNCATE
Definition path.h:83
@ MODE_WRITE
Definition path.h:77
@ MODE_RECURSIVE
Definition path.h:85
@ MODE_READ
Definition path.h:76
@ MODE_EXCLUSIVE
Definition path.h:82
@ MODE_DIRECTORY
Definition path.h:84
#define UNREF_DEFER(ptr)
RAII-style cleanup for scoped references.
Definition ref.h:54
#define REF(ptr)
Increment reference count.
Definition ref.h:65
#define UNREF(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 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
static const path_flag_t flags[]
Definition path.c:42
static uint64_t path_handle_dotdot(path_t *path)
Definition path.c:283
static bool path_is_name_valid(const char *name)
Definition path.c:261
static path_flag_short_t shortFlags[UINT8_MAX+1]
Definition path.c:23
static map_t flagMap
Definition path.c:15
static mode_t path_flag_to_mode(const char *flag, uint64_t length)
Definition path.c:55
static map_t flagShortMap
Definition path.c:16
static atomic_long next
Definition main.c:11
static mount_t * mount
Definition ramfs.c:28
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
#define INT8_MAX
Definition stdint.h:61
#define UINT8_MAX
Definition stdint.h:62
_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:61
_PUBLIC int strncmp(const char *s1, const char *s2, size_t n)
Definition strncmp.c:3
_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:84
char name[MAX_NAME]
Constant after creation.
Definition dentry.h:87
dentry_t * parent
Definition dentry.h:90
Hash map structure.
Definition map.h:89
Mount structure.
Definition mount.h:44
dentry_t * target
The dentry which the source is mounted to, can be NULL for the root filesystem.
Definition mount.h:49
mount_t * parent
The parent mount, can be NULL for the root filesystem.
Definition mount.h:51
dentry_t * source
The dentry to appear at target once mounted, usually the root dentry of the mounted filesystem.
Definition mount.h:48
mode_t mode
Definition path.c:20
const char * name
Definition path.c:39
mode_t mode
Definition path.c:38
Path structure.
Definition path.h:125
mount_t * mount
Definition path.h:126
dentry_t * dentry
Definition path.h:127
Pathname structure.
Definition path.h:137
char string[MAX_PATH]
Definition path.h:138
mode_t mode
Definition path.h:139
bool isValid
Definition path.h:140