summaryrefslogtreecommitdiff
path: root/src/fs/path.c
blob: d0e31af07f256f475a66b36dbc4513b8d09ea225 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include <fs/path.h>
#include <toxic/string.h>
#include <toxic/config.h>
#include <mm/kernel_heap.h>
#include <toxic/errno.h>

static int
is_valid(const char *path)
{
	int len = strnlen(path, PATH_MAX);

	/* Only allow for absolute paths for now */
	if (len < PATH_MAX && path[len] == '\0' &&
	    strncmp(path, "/", 1) == 0)
		return true;

	return false;
}

/*
 * Name must be dynamically allocated and not NULL
 */
static struct path_part *
path_alloc(const char *name)
{
	struct path_part *p = kzalloc(sizeof(struct path_part));
	p->name = name;

	return p;
}

/*
 * This is the main parser routine.
 * It is designed to be called in a loop until it returns NULL,
 * indicating the end of the path.
 *
 * It will return a copy of the current part of the path.
 *
 * So for a path like: /home/foo/bar, each call to this routine
 * will return: "home", "foo", "bar" and NULL (signaling the end
 * of the path). It uses the double pointer argument as its own
 * cursor.
 *
 * - This makes no distinction if what it receives is an absolute
 *   or relative path. It just extracts the components.
 *   - The caller is responsible for checking if the path is
 *   absolute or relative.
 *
 * - It makes no distinction between files or directories, this only
 *   parses a path.
 *
 * - Double slashes like /home//foo are ignored and the entries returned
 *   will be "home" and "foo".
 */
static char *
get_path_part(char **path)
{
	char *name;
	char *cur = *path;
	int i = 0;
	size_t pathlen = strlen(cur);

	/* We reached the end of the full path */
	if (!pathlen)
		return NULL;

	/* Ignore any leading slashes */
	while (cur[i] == '/' && i < pathlen)
		i++;

	/* There are no more components, just leading slash(es) */
	if (i >= pathlen)
		return NULL;


	/* Advance cursor to the first non-slash character */
	*path = cur += i;

	/*
	 * Reset the index and advance it accordingly to the
	 * new cursor position.
	 */
	for (i = 0; cur[i] != '/' && cur[i] != '\0'; i++);

	name = kzalloc(i + 1);	/* Size of the current component + null */
	strncpy(name, cur, i);

	/* kzalloc'ed, null terminator already there, but just in case...*/
	name[i] = '\0';

	/*
	 * Advance path to the next component
	 *
	 * The last for loop incremented 'i' to either the next '/' in the path
	 * or to the null terminator of the full path.
	 *
	 * Just point the caller's cursor to that location. Next call here will
	 * either return NULL or begin with a '/'
	 */
	*path += (i + 1);	/* Move the path cursor to the next component */

	return name;
}

/*
 * If last is not NULL, we are parsing a relative path
 * so we should just extend the current path.
 *
 * Relative paths not implemented yet... last should ALWAYS
 * be NULL
 */
static struct path_part *
construct_path(
	struct path_part *last,
	char *p)
{
	char *cur = p; /* Used to walk through the path */
	char *name = kzalloc(2); /* Alloc space for '/' */

	name[0] = '/';
	name[1] = '\0';

	/* Root path component has NULL as its name */
	struct path_part *root = path_alloc(name);
	struct path_part *ptr_next = root;


	/* Reset the name pointer before starting to parse the path */
	name = NULL;

	/* check (last == NULL); */

	while ((name = get_path_part(&cur))) {
		ptr_next->next = path_alloc(name);

		/* Gotta check if path_aloc() failed*/

		ptr_next = ptr_next->next;
	}

	/* Ensure we null terminate the component's linked list */
	ptr_next->next = NULL;
	return root;

err_out:
	return NULL;
}

static struct path_root *
root_init(void)
{
	struct path_root *r = kzalloc(sizeof(struct path_root));

	/*
	 * We just have support for one disk by now, and the root
	 * shouldn't be tied to a specific disk. I want a Unix-like
	 * namespace
	 */
	r->disk_id = 0;
	r->head = NULL;
	return r;
}

void
free_root(struct path_root *r)
{
	if (r)
		kfree(r);
}

/*
 * This should account for a relative path.
 * By now it only deals with absolute paths (see is_valid())
 */

struct path_root *
parse_path(char *path)
{
	struct path_root *root;

	if (!is_valid(path))
		return NULL;

	root = root_init();
	root->head = construct_path(NULL, path);

	if (!root->head) {
		free_root(root);
		return NULL;
	}

	return root;
}