refactor: add user space printf.
[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         i++;
37     }
38
39     assert(start);
40
41     struct v_dnode* dnode;
42     struct v_inode* current_inode;
43     struct v_dnode* current_level = start;
44
45     struct hstr name = HSTR(fname_buffer, 0);
46
47     char current = path[i++], lookahead;
48     while (current) {
49         lookahead = path[i++];
50         if (current != VFS_PATH_DELIM) {
51             if (j >= VFS_NAME_MAXLEN - 1) {
52                 return ENAMETOOLONG;
53             }
54             if (!VFS_VALID_CHAR(current)) {
55                 return EINVAL;
56             }
57             fname_buffer[j++] = current;
58             if (lookahead) {
59                 goto cont;
60             }
61         }
62
63         // handling cases like /^.*(\/+).*$/
64         if (lookahead == VFS_PATH_DELIM) {
65             goto cont;
66         }
67
68         fname_buffer[j] = 0;
69         name.len = j;
70         hstr_rehash(&name, HSTR_FULL_HASH);
71
72         if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
73             if (component) {
74                 component->hash = name.hash;
75                 component->len = j;
76                 strcpy(component->value, fname_buffer);
77             }
78             break;
79         }
80
81         current_inode = current_level->inode;
82
83         if ((current_inode->itype & VFS_IFSYMLINK) &&
84             !(walk_options & VFS_WALK_NOFOLLOW)) {
85             const char* link;
86
87             lock_inode(current_inode);
88             if ((errno =
89                    current_inode->ops->read_symlink(current_inode, &link))) {
90                 unlock_inode(current_inode);
91                 goto error;
92             }
93             unlock_inode(current_inode);
94
95             errno = __vfs_walk(current_level->parent,
96                                link,
97                                &dnode,
98                                NULL,
99                                0,
100                                depth + 1,
101                                fname_buffer + name.len + 1);
102
103             if (errno) {
104                 goto error;
105             }
106
107             // reposition the resolved subtree pointed by symlink
108             vfs_dcache_rehash(current_level->parent, dnode);
109             current_level = dnode;
110             current_inode = dnode->inode;
111         }
112
113         lock_dnode(current_level);
114
115         dnode = vfs_dcache_lookup(current_level, &name);
116
117         if (!dnode) {
118             dnode = vfs_d_alloc(current_level, &name);
119
120             if (!dnode) {
121                 errno = ENOMEM;
122                 goto error;
123             }
124
125             lock_inode(current_inode);
126
127             errno = current_inode->ops->dir_lookup(current_inode, dnode);
128
129             if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
130                 if (!current_inode->ops->mkdir) {
131                     errno = ENOTSUP;
132                 } else {
133                     errno = current_inode->ops->mkdir(current_inode, dnode);
134                 }
135             }
136
137             vfs_dcache_add(current_level, dnode);
138             unlock_inode(current_inode);
139
140             if (errno) {
141                 unlock_dnode(current_level);
142                 goto cleanup;
143             }
144         }
145
146         unlock_dnode(current_level);
147
148         j = 0;
149         current_level = dnode;
150     cont:
151         current = lookahead;
152     };
153
154     *dentry = current_level;
155     return 0;
156
157 cleanup:
158     vfs_d_free(dnode);
159 error:
160     *dentry = NULL;
161     return errno;
162 }
163
164 int
165 vfs_walk(struct v_dnode* start,
166          const char* path,
167          struct v_dnode** dentry,
168          struct hstr* component,
169          int options)
170 {
171     // allocate a file name stack for path walking and recursion to resolve
172     // symlink
173     char* name_buffer = valloc(2048);
174
175     int errno =
176       __vfs_walk(start, path, dentry, component, options, 0, name_buffer);
177
178     vfree(name_buffer);
179     return errno;
180 }
181
182 int
183 vfs_walk_proc(const char* path,
184               struct v_dnode** dentry,
185               struct hstr* component,
186               int options)
187 {
188     return vfs_walk(__current->cwd, path, dentry, component, options);
189 }