fix: remove unnecessary backtracing in mnt_mkbusy/chillax
[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                 hstrcpy(component, &name);
78             }
79             break;
80         }
81
82         current_inode = current_level->inode;
83
84         if ((current_inode->itype & VFS_IFSYMLINK) &&
85             !(walk_options & VFS_WALK_NOFOLLOW)) {
86             const char* link;
87
88             lock_inode(current_inode);
89             if ((errno =
90                    current_inode->ops->read_symlink(current_inode, &link))) {
91                 unlock_inode(current_inode);
92                 goto error;
93             }
94             unlock_inode(current_inode);
95
96             errno = __vfs_walk(current_level->parent,
97                                link,
98                                &dnode,
99                                NULL,
100                                0,
101                                depth + 1,
102                                fname_buffer + name.len + 1);
103
104             if (errno) {
105                 goto error;
106             }
107
108             // reposition the resolved subtree pointed by symlink
109             // vfs_dcache_rehash(current_level->parent, dnode);
110             current_level = dnode;
111             current_inode = dnode->inode;
112         }
113
114         lock_dnode(current_level);
115
116         dnode = vfs_dcache_lookup(current_level, &name);
117
118         if (!dnode) {
119             dnode = vfs_d_alloc(current_level, &name);
120
121             if (!dnode) {
122                 errno = ENOMEM;
123                 goto error;
124             }
125
126             lock_inode(current_inode);
127
128             errno = current_inode->ops->dir_lookup(current_inode, dnode);
129
130             if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
131                 if (!current_inode->ops->mkdir) {
132                     errno = ENOTSUP;
133                 } else {
134                     errno = current_inode->ops->mkdir(current_inode, dnode);
135                 }
136             }
137
138             vfs_dcache_add(current_level, dnode);
139             unlock_inode(current_inode);
140
141             if (errno) {
142                 unlock_dnode(current_level);
143                 goto cleanup;
144             }
145         }
146
147         unlock_dnode(current_level);
148
149         j = 0;
150         current_level = dnode;
151     cont:
152         current = lookahead;
153     };
154
155     *dentry = current_level;
156     return 0;
157
158 cleanup:
159     vfs_d_free(dnode);
160 error:
161     *dentry = NULL;
162     return errno;
163 }
164
165 int
166 vfs_walk(struct v_dnode* start,
167          const char* path,
168          struct v_dnode** dentry,
169          struct hstr* component,
170          int options)
171 {
172     // allocate a file name stack for path walking and recursion to resolve
173     // symlink
174     char* name_buffer = valloc(2048);
175
176     int errno =
177       __vfs_walk(start, path, dentry, component, options, 0, name_buffer);
178
179     vfree(name_buffer);
180     return errno;
181 }
182
183 int
184 vfs_walk_proc(const char* path,
185               struct v_dnode** dentry,
186               struct hstr* component,
187               int options)
188 {
189     return vfs_walk(__current->cwd, path, dentry, component, options);
190 }