1 /*
2 * Copyright (c) 2007 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <string.h>
9 #include <stdlib.h>
10 #include <lk/debug.h>
11 #include <lk/trace.h>
12 #include <lk/err.h>
13 #include "ext2_priv.h"
14
15 #define LOCAL_TRACE 0
16
17 /* read in the dir, look for the entry */
ext2_dir_lookup(ext2_t * ext2,struct ext2_inode * dir_inode,const char * name,inodenum_t * inum)18 static int ext2_dir_lookup(ext2_t *ext2, struct ext2_inode *dir_inode, const char *name, inodenum_t *inum) {
19 uint file_blocknum;
20 int err;
21 uint8_t *buf;
22 size_t namelen = strlen(name);
23
24 if (!S_ISDIR(dir_inode->i_mode))
25 return ERR_NOT_DIR;
26
27 buf = malloc(EXT2_BLOCK_SIZE(ext2->sb));
28
29 file_blocknum = 0;
30 for (;;) {
31 /* read in the offset */
32 err = ext2_read_inode(ext2, dir_inode, buf, file_blocknum * EXT2_BLOCK_SIZE(ext2->sb), EXT2_BLOCK_SIZE(ext2->sb));
33 if (err <= 0) {
34 free(buf);
35 return -1;
36 }
37
38 /* walk through the directory entries, looking for the one that matches */
39 struct ext2_dir_entry_2 *ent;
40 uint pos = 0;
41 while (pos < EXT2_BLOCK_SIZE(ext2->sb)) {
42 ent = (struct ext2_dir_entry_2 *)&buf[pos];
43
44 LTRACEF("ent %d:%d: inode 0x%x, reclen %d, namelen %d\n",
45 file_blocknum, pos, LE32(ent->inode), LE16(ent->rec_len), ent->name_len/* , ent->name*/);
46
47 /* sanity check the record length */
48 if (LE16(ent->rec_len) == 0)
49 break;
50
51 if (ent->name_len == namelen && memcmp(name, ent->name, ent->name_len) == 0) {
52 // match
53 *inum = LE32(ent->inode);
54 LTRACEF("match: inode %d\n", *inum);
55 free(buf);
56 return 1;
57 }
58
59 pos += ROUNDUP(LE16(ent->rec_len), 4);
60 }
61
62 file_blocknum++;
63
64 /* sanity check the directory. 4MB should be enough */
65 if (file_blocknum > 1024) {
66 free(buf);
67 return -1;
68 }
69 }
70 }
71
72 /* note, trashes path */
ext2_walk(ext2_t * ext2,char * path,struct ext2_inode * start_inode,inodenum_t * inum,int recurse)73 static int ext2_walk(ext2_t *ext2, char *path, struct ext2_inode *start_inode, inodenum_t *inum, int recurse) {
74 char *ptr;
75 struct ext2_inode inode;
76 struct ext2_inode dir_inode;
77 int err;
78 bool done;
79
80 LTRACEF("path '%s', start_inode %p, inum %p, recurse %d\n", path, start_inode, inum, recurse);
81
82 if (recurse > 4)
83 return ERR_RECURSE_TOO_DEEP;
84
85 /* chew up leading slashes */
86 ptr = &path[0];
87 while (*ptr == '/')
88 ptr++;
89
90 done = false;
91 memcpy(&dir_inode, start_inode, sizeof(struct ext2_inode));
92 while (!done) {
93 /* process the first component */
94 char *next_sep = strchr(ptr, '/');
95 if (next_sep) {
96 /* terminate the next component, giving us a substring */
97 *next_sep = 0;
98 } else {
99 /* this is the last component */
100 done = true;
101 }
102
103 LTRACEF("component '%s', done %d\n", ptr, done);
104
105 /* do the lookup on this component */
106 err = ext2_dir_lookup(ext2, &dir_inode, ptr, inum);
107 if (err < 0)
108 return err;
109
110 nextcomponent:
111 LTRACEF("inum %u\n", *inum);
112
113 /* load the next inode */
114 err = ext2_load_inode(ext2, *inum, &inode);
115 if (err < 0)
116 return err;
117
118 /* is it a symlink? */
119 if (S_ISLNK(inode.i_mode)) {
120 char link[512];
121
122 LTRACEF("hit symlink\n");
123
124 err = ext2_read_link(ext2, &inode, link, sizeof(link));
125 if (err < 0)
126 return err;
127
128 LTRACEF("symlink read returns %d '%s'\n", err, link);
129
130 /* recurse, parsing the link */
131 if (link[0] == '/') {
132 /* link starts with '/', so start over again at the rootfs */
133 err = ext2_walk(ext2, link, &ext2->root_inode, inum, recurse + 1);
134 } else {
135 err = ext2_walk(ext2, link, &dir_inode, inum, recurse + 1);
136 }
137
138 LTRACEF("recursive walk returns %d\n", err);
139
140 if (err < 0)
141 return err;
142
143 /* if we weren't done with our path parsing, start again with the result of this recurse */
144 if (!done) {
145 goto nextcomponent;
146 }
147 } else if (S_ISDIR(inode.i_mode)) {
148 /* for the next cycle, point the dir inode at our new directory */
149 memcpy(&dir_inode, &inode, sizeof(struct ext2_inode));
150 } else {
151 if (!done) {
152 /* we aren't done and this walked over a nondir, abort */
153 LTRACEF("not finished and component is nondir\n");
154 return ERR_NOT_FOUND;
155 }
156 }
157
158 if (!done) {
159 /* move to the next separator */
160 ptr = next_sep + 1;
161
162 /* consume multiple separators */
163 while (*ptr == '/')
164 ptr++;
165 }
166 }
167
168 return 0;
169 }
170
171 /* do a path parse, looking up each component */
ext2_lookup(ext2_t * ext2,const char * _path,inodenum_t * inum)172 int ext2_lookup(ext2_t *ext2, const char *_path, inodenum_t *inum) {
173 LTRACEF("path '%s', inum %p\n", _path, inum);
174
175 char path[512];
176 strlcpy(path, _path, sizeof(path));
177
178 return ext2_walk(ext2, path, &ext2->root_inode, inum, 1);
179 }
180
181