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