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