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