246e90327cc3219af3f03d418c0fba366dc4aea6
[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 <usr/lunaix/fcntl_defs.h>
7
8 #include <klibc/string.h>
9
10 #define VFS_SYMLINK_DEPTH 16
11 #define VFS_SYMLINK_MAXLEN 512
12
13 extern struct lru_zone *dnode_lru, *inode_lru;
14
15 int
16 __vfs_walk(struct v_dnode* start,
17            const char* path,
18            struct v_dnode** dentry,
19            struct hstr* component,
20            int walk_options,
21            size_t depth,
22            char* fname_buffer)
23 {
24     int errno = 0;
25     int i = 0, j = 0;
26
27     if (depth >= VFS_SYMLINK_DEPTH) {
28         return ELOOP;
29     }
30
31     if (path[0] == VFS_PATH_DELIM || !start) {
32         if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
33             start = start->super_block->root;
34         } 
35         else if (unlikely(!__current)) {
36             start = vfs_sysroot;
37         }
38         else {
39             start = __current->root ?: vfs_sysroot;
40         }
41
42         if (unlikely(!start || !start->mnt)) {
43             fail("vfs: no root");
44         }
45
46         if (path[0] == VFS_PATH_DELIM) {
47             i++;
48         }
49     }
50
51     assert(start);
52
53     struct v_dnode* dnode;
54     struct v_dnode* current_level = start;
55     struct v_inode* current_inode = current_level->inode;
56
57     struct hstr name = HSTR(fname_buffer, 0);
58
59     char current = path[i++], lookahead;
60     while (current) 
61     {
62         lookahead = path[i++];
63
64         if (current != VFS_PATH_DELIM) 
65         {
66             if (j >= VFS_NAME_MAXLEN - 1) {
67                 return ENAMETOOLONG;
68             }
69             if (!VFS_VALID_CHAR(current)) {
70                 return EINVAL;
71             }
72             fname_buffer[j++] = current;
73             if (lookahead) {
74                 goto cont;
75             }
76         }
77
78         // handling cases like /^.*(\/+).*$/
79         if (lookahead == VFS_PATH_DELIM) {
80             goto cont;
81         }
82
83         fname_buffer[j] = 0;
84         name.len = j;
85         hstr_rehash(&name, HSTR_FULL_HASH);
86
87         if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
88             if (component) {
89                 hstrcpy(component, &name);
90             }
91             break;
92         }
93
94         lock_dnode(current_level);
95
96         if (!check_allow_execute(current_inode)) {
97             errno = EACCESS;
98             unlock_dnode(current_level);
99             goto error;
100         }
101
102         dnode = vfs_dcache_lookup(current_level, &name);
103
104         if (!dnode) 
105         {
106             dnode = vfs_d_alloc(current_level, &name);
107
108             if (!dnode) {
109                 errno = ENOMEM;
110                 unlock_dnode(current_level);
111                 goto error;
112             }
113
114             lock_inode(current_inode);
115
116             errno = current_inode->ops->dir_lookup(current_inode, dnode);
117
118             if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
119                 if (!current_inode->ops->mkdir) {
120                     errno = ENOTSUP;
121                 } else {
122                     errno = current_inode->ops->mkdir(current_inode, dnode);
123                 }
124             }
125
126             vfs_dcache_add(current_level, dnode);
127             unlock_inode(current_inode);
128
129             if (errno) {
130                 unlock_dnode(current_level);
131                 goto cleanup;
132             }
133         }
134
135         unlock_dnode(current_level);
136
137         j = 0;
138         current_level = dnode;
139         current_inode = current_level->inode;
140
141         assert(current_inode);
142         
143         if (check_symlink_node(current_inode) &&
144             !(walk_options & VFS_WALK_NOFOLLOW)) 
145         {
146             const char* link;
147             struct v_inode_ops* iops;
148
149             iops = current_inode->ops;
150
151             if (!iops->read_symlink) {
152                 errno = ENOTSUP;
153                 goto error;
154             }
155
156             lock_inode(current_inode);
157
158             errno = iops->read_symlink(current_inode, &link);
159             if ((errno < 0)) {
160                 unlock_inode(current_inode);
161                 goto error;
162             }
163
164             unlock_inode(current_inode);
165
166             errno = __vfs_walk(current_level->parent,
167                                link,
168                                &dnode,
169                                NULL,
170                                0,
171                                depth + 1,
172                                fname_buffer + name.len + 1);
173
174             if (errno) {
175                 goto error;
176             }
177
178             current_level = dnode;
179             current_inode = dnode->inode;
180         }
181
182     cont:
183         current = lookahead;
184     };
185
186     *dentry = current_level;
187     return 0;
188
189 cleanup:
190     vfs_d_free(dnode);
191
192 error:
193     *dentry = NULL;
194     return errno;
195 }
196
197 int
198 vfs_walk(struct v_dnode* start,
199          const char* path,
200          struct v_dnode** dentry,
201          struct hstr* component,
202          int options)
203 {
204     if (!path) {
205         *dentry = NULL;
206         return 0;
207     }
208
209     // allocate a file name stack for path walking and recursion to resolve
210     // symlink
211     char* name_buffer = valloc(2048);
212
213     int errno =
214       __vfs_walk(start, path, dentry, component, options, 0, name_buffer);
215
216     vfree(name_buffer);
217     return errno;
218 }
219
220 int
221 vfs_walk_proc(const char* path,
222               struct v_dnode** dentry,
223               struct hstr* component,
224               int options)
225 {
226     return vfs_walk(__current->cwd, path, dentry, component, options);
227 }
228
229 int
230 vfs_walkat(int fd, const char* path, int at_opts, struct v_dnode** dnode_out)
231 {
232     int errno, options = 0;
233     struct v_dnode *root_dnode;
234     struct v_fd* _fd;
235
236     if ((at_opts & AT_FDCWD)) {
237         root_dnode = __current->cwd;
238     }
239     else 
240     {
241         errno = vfs_getfd(fd, &_fd);
242         if (errno) {
243             return errno;
244         }
245
246         root_dnode = _fd->file->dnode;
247     }
248
249     if ((at_opts & AT_SYMLINK_NOFOLLOW)) {
250         options |= VFS_WALK_NOFOLLOW;
251     }
252
253     errno = vfs_walk(root_dnode, path, dnode_out, NULL, options);
254     if (errno) {
255         return errno;
256     }
257
258     return 0;
259 }