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