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