5-malloc.md (#25)
[lunaix-os.git] / lunaix-os / kernel / fs / path_walk.c
1 #include <lunaix/fs.h>
2 #include <lunaix/mm/valloc.h>
3 #include <lunaix/process.h>
4 #include <lunaix/spike.h>
5
6 #include <klibc/string.h>
7
8 #define VFS_SYMLINK_DEPTH 16
9
10 extern struct lru_zone *dnode_lru, *inode_lru;
11
12 int
13 __vfs_walk(struct v_dnode* start,
14            const char* path,
15            struct v_dnode** dentry,
16            struct hstr* component,
17            int walk_options,
18            size_t depth,
19            char* fname_buffer)
20 {
21     int errno = 0;
22     int i = 0, j = 0;
23
24     if (depth >= VFS_SYMLINK_DEPTH) {
25         return ELOOP;
26     }
27
28     if (path[0] == VFS_PATH_DELIM || !start) {
29         if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
30             start = start->super_block->root;
31         } else {
32             start = vfs_sysroot;
33             if (!vfs_sysroot->mnt) {
34                 panick("vfs: no root");
35             }
36         }
37
38         if (path[0] == VFS_PATH_DELIM) {
39             i++;
40         }
41     }
42
43     assert(start);
44
45     struct v_dnode* dnode;
46     struct v_dnode* current_level = start;
47     struct v_inode* current_inode = current_level->inode;
48
49     struct hstr name = HSTR(fname_buffer, 0);
50
51     char current = path[i++], lookahead;
52     while (current) {
53         lookahead = path[i++];
54         if (current != VFS_PATH_DELIM) {
55             if (j >= VFS_NAME_MAXLEN - 1) {
56                 return ENAMETOOLONG;
57             }
58             if (!VFS_VALID_CHAR(current)) {
59                 return EINVAL;
60             }
61             fname_buffer[j++] = current;
62             if (lookahead) {
63                 goto cont;
64             }
65         }
66
67         // handling cases like /^.*(\/+).*$/
68         if (lookahead == VFS_PATH_DELIM) {
69             goto cont;
70         }
71
72         fname_buffer[j] = 0;
73         name.len = j;
74         hstr_rehash(&name, HSTR_FULL_HASH);
75
76         if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
77             if (component) {
78                 hstrcpy(component, &name);
79             }
80             break;
81         }
82
83         lock_dnode(current_level);
84
85         dnode = vfs_dcache_lookup(current_level, &name);
86
87         if (!dnode) {
88             dnode = vfs_d_alloc(current_level, &name);
89
90             if (!dnode) {
91                 errno = ENOMEM;
92                 goto error;
93             }
94
95             lock_inode(current_inode);
96
97             errno = current_inode->ops->dir_lookup(current_inode, dnode);
98
99             if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
100                 if (!current_inode->ops->mkdir) {
101                     errno = ENOTSUP;
102                 } else {
103                     errno = current_inode->ops->mkdir(current_inode, dnode);
104                 }
105             }
106
107             vfs_dcache_add(current_level, dnode);
108             unlock_inode(current_inode);
109
110             if (errno) {
111                 unlock_dnode(current_level);
112                 goto cleanup;
113             }
114         }
115
116         unlock_dnode(current_level);
117
118         j = 0;
119         current_level = dnode;
120         current_inode = current_level->inode;
121
122         if ((current_inode->itype & F_MSLNK) &&
123             !(walk_options & VFS_WALK_NOFOLLOW)) {
124             const char* link;
125
126             if (!current_inode->ops->read_symlink) {
127                 errno = ENOTSUP;
128                 goto error;
129             }
130
131             lock_inode(current_inode);
132             if ((errno =
133                    current_inode->ops->read_symlink(current_inode, &link))) {
134                 unlock_inode(current_inode);
135                 goto error;
136             }
137             unlock_inode(current_inode);
138
139             errno = __vfs_walk(current_level->parent,
140                                link,
141                                &dnode,
142                                NULL,
143                                0,
144                                depth + 1,
145                                fname_buffer + name.len + 1);
146
147             if (errno) {
148                 goto error;
149             }
150
151             current_level = dnode;
152             current_inode = dnode->inode;
153         }
154
155     cont:
156         current = lookahead;
157     };
158
159     *dentry = current_level;
160     return 0;
161
162 cleanup:
163     vfs_d_free(dnode);
164 error:
165     *dentry = NULL;
166     return errno;
167 }
168
169 int
170 vfs_walk(struct v_dnode* start,
171          const char* path,
172          struct v_dnode** dentry,
173          struct hstr* component,
174          int options)
175 {
176     if (!path) {
177         *dentry = NULL;
178         return 0;
179     }
180
181     // allocate a file name stack for path walking and recursion to resolve
182     // symlink
183     char* name_buffer = valloc(2048);
184
185     int errno =
186       __vfs_walk(start, path, dentry, component, options, 0, name_buffer);
187
188     vfree(name_buffer);
189     return errno;
190 }
191
192 int
193 vfs_walk_proc(const char* path,
194               struct v_dnode** dentry,
195               struct hstr* component,
196               int options)
197 {
198     return vfs_walk(__current->cwd, path, dentry, component, options);
199 }