1 | #include <dprintf.h> |
---|
2 | #include <stdio.h> |
---|
3 | #include <string.h> |
---|
4 | #include <sys/dirent.h> |
---|
5 | #include <minmax.h> |
---|
6 | #include "cache.h" |
---|
7 | #include "core.h" |
---|
8 | #include "disk.h" |
---|
9 | #include "fs.h" |
---|
10 | #include "ext2_fs.h" |
---|
11 | |
---|
12 | /* |
---|
13 | * Convert an ext2 file type to the global values |
---|
14 | */ |
---|
15 | static enum dirent_type ext2_cvt_type(unsigned int d_file_type) |
---|
16 | { |
---|
17 | static const enum dirent_type inode_type[] = { |
---|
18 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, |
---|
19 | DT_BLK, DT_FIFO, DT_SOCK, DT_LNK, |
---|
20 | }; |
---|
21 | |
---|
22 | if (d_file_type > sizeof inode_type / sizeof *inode_type) |
---|
23 | return DT_UNKNOWN; |
---|
24 | else |
---|
25 | return inode_type[d_file_type]; |
---|
26 | } |
---|
27 | |
---|
28 | /* |
---|
29 | * get the group's descriptor of group_num |
---|
30 | */ |
---|
31 | static const struct ext2_group_desc * |
---|
32 | ext2_get_group_desc(struct fs_info *fs, uint32_t group_num) |
---|
33 | { |
---|
34 | struct ext2_sb_info *sbi = EXT2_SB(fs); |
---|
35 | uint32_t desc_block, desc_index; |
---|
36 | const struct ext2_group_desc *desc_data_block; |
---|
37 | |
---|
38 | if (group_num >= sbi->s_groups_count) { |
---|
39 | printf ("ext2_get_group_desc" |
---|
40 | "block_group >= groups_count - " |
---|
41 | "block_group = %d, groups_count = %d", |
---|
42 | group_num, sbi->s_groups_count); |
---|
43 | |
---|
44 | return NULL; |
---|
45 | } |
---|
46 | |
---|
47 | desc_block = group_num / sbi->s_desc_per_block; |
---|
48 | desc_index = group_num % sbi->s_desc_per_block; |
---|
49 | |
---|
50 | desc_block += sbi->s_first_data_block + 1; |
---|
51 | |
---|
52 | desc_data_block = get_cache(fs->fs_dev, desc_block); |
---|
53 | return &desc_data_block[desc_index]; |
---|
54 | } |
---|
55 | |
---|
56 | /* |
---|
57 | * Unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure. |
---|
58 | */ |
---|
59 | static inline bool ext2_match_entry(const char *name, size_t len, |
---|
60 | const struct ext2_dir_entry * de) |
---|
61 | { |
---|
62 | if (!de->d_inode) |
---|
63 | return false; |
---|
64 | if (len != de->d_name_len) |
---|
65 | return false; |
---|
66 | return !memcmp(name, de->d_name, len); |
---|
67 | } |
---|
68 | |
---|
69 | |
---|
70 | /* |
---|
71 | * p is at least 6 bytes before the end of page |
---|
72 | */ |
---|
73 | static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p) |
---|
74 | { |
---|
75 | return (struct ext2_dir_entry *)((char*)p + p->d_rec_len); |
---|
76 | } |
---|
77 | |
---|
78 | /* |
---|
79 | * Map a logical sector and load it into the cache |
---|
80 | */ |
---|
81 | static const void * |
---|
82 | ext2_get_cache(struct inode *inode, block_t lblock) |
---|
83 | { |
---|
84 | block_t pblock = ext2_bmap(inode, lblock, NULL); |
---|
85 | return get_cache(inode->fs->fs_dev, pblock); |
---|
86 | } |
---|
87 | |
---|
88 | /* |
---|
89 | * find a dir entry, return it if found, or return NULL. |
---|
90 | */ |
---|
91 | static const struct ext2_dir_entry * |
---|
92 | ext2_find_entry(struct fs_info *fs, struct inode *inode, const char *dname) |
---|
93 | { |
---|
94 | block_t index = 0; |
---|
95 | uint32_t i = 0, offset, maxoffset; |
---|
96 | const struct ext2_dir_entry *de; |
---|
97 | const char *data; |
---|
98 | size_t dname_len = strlen(dname); |
---|
99 | |
---|
100 | while (i < inode->size) { |
---|
101 | data = ext2_get_cache(inode, index++); |
---|
102 | offset = 0; |
---|
103 | maxoffset = min(BLOCK_SIZE(fs), i-inode->size); |
---|
104 | |
---|
105 | /* The smallest possible size is 9 bytes */ |
---|
106 | while (offset < maxoffset-8) { |
---|
107 | de = (const struct ext2_dir_entry *)(data + offset); |
---|
108 | if (de->d_rec_len > maxoffset - offset) |
---|
109 | break; |
---|
110 | |
---|
111 | if (ext2_match_entry(dname, dname_len, de)) |
---|
112 | return de; |
---|
113 | |
---|
114 | offset += de->d_rec_len; |
---|
115 | } |
---|
116 | i += BLOCK_SIZE(fs); |
---|
117 | } |
---|
118 | |
---|
119 | return NULL; |
---|
120 | } |
---|
121 | |
---|
122 | static const struct ext2_inode * |
---|
123 | ext2_get_inode(struct fs_info *fs, int inr) |
---|
124 | { |
---|
125 | const struct ext2_group_desc *desc; |
---|
126 | const char *data; |
---|
127 | uint32_t inode_group, inode_offset; |
---|
128 | uint32_t block_num, block_off; |
---|
129 | |
---|
130 | inr--; |
---|
131 | inode_group = inr / EXT2_INODES_PER_GROUP(fs); |
---|
132 | inode_offset = inr % EXT2_INODES_PER_GROUP(fs); |
---|
133 | desc = ext2_get_group_desc(fs, inode_group); |
---|
134 | if (!desc) |
---|
135 | return NULL; |
---|
136 | |
---|
137 | block_num = desc->bg_inode_table + |
---|
138 | inode_offset / EXT2_INODES_PER_BLOCK(fs); |
---|
139 | block_off = inode_offset % EXT2_INODES_PER_BLOCK(fs); |
---|
140 | |
---|
141 | data = get_cache(fs->fs_dev, block_num); |
---|
142 | |
---|
143 | return (const struct ext2_inode *) |
---|
144 | (data + block_off * EXT2_SB(fs)->s_inode_size); |
---|
145 | } |
---|
146 | |
---|
147 | static void fill_inode(struct inode *inode, const struct ext2_inode *e_inode) |
---|
148 | { |
---|
149 | inode->mode = IFTODT(e_inode->i_mode); |
---|
150 | inode->size = e_inode->i_size; |
---|
151 | inode->atime = e_inode->i_atime; |
---|
152 | inode->ctime = e_inode->i_ctime; |
---|
153 | inode->mtime = e_inode->i_mtime; |
---|
154 | inode->dtime = e_inode->i_dtime; |
---|
155 | inode->blocks = e_inode->i_blocks; |
---|
156 | inode->flags = e_inode->i_flags; |
---|
157 | inode->file_acl = e_inode->i_file_acl; |
---|
158 | memcpy(PVT(inode)->i_block, e_inode->i_block, sizeof PVT(inode)->i_block); |
---|
159 | } |
---|
160 | |
---|
161 | static struct inode *ext2_iget_by_inr(struct fs_info *fs, uint32_t inr) |
---|
162 | { |
---|
163 | const struct ext2_inode *e_inode; |
---|
164 | struct inode *inode; |
---|
165 | |
---|
166 | e_inode = ext2_get_inode(fs, inr); |
---|
167 | if (!e_inode) |
---|
168 | return NULL; |
---|
169 | |
---|
170 | if (!(inode = alloc_inode(fs, inr, sizeof(struct ext2_pvt_inode)))) |
---|
171 | return NULL; |
---|
172 | fill_inode(inode, e_inode); |
---|
173 | |
---|
174 | return inode; |
---|
175 | } |
---|
176 | |
---|
177 | static struct inode *ext2_iget_root(struct fs_info *fs) |
---|
178 | { |
---|
179 | return ext2_iget_by_inr(fs, EXT2_ROOT_INO); |
---|
180 | } |
---|
181 | |
---|
182 | static struct inode *ext2_iget(const char *dname, struct inode *parent) |
---|
183 | { |
---|
184 | const struct ext2_dir_entry *de; |
---|
185 | struct fs_info *fs = parent->fs; |
---|
186 | |
---|
187 | de = ext2_find_entry(fs, parent, dname); |
---|
188 | if (!de) |
---|
189 | return NULL; |
---|
190 | |
---|
191 | return ext2_iget_by_inr(fs, de->d_inode); |
---|
192 | } |
---|
193 | |
---|
194 | /* |
---|
195 | * Read the entire contents of an inode into a memory buffer |
---|
196 | */ |
---|
197 | static int cache_get_file(struct inode *inode, void *buf, size_t bytes) |
---|
198 | { |
---|
199 | struct fs_info *fs = inode->fs; |
---|
200 | size_t block_size = BLOCK_SIZE(fs); |
---|
201 | uint32_t index = 0; /* Logical block number */ |
---|
202 | size_t chunk; |
---|
203 | const char *data; |
---|
204 | char *p = buf; |
---|
205 | |
---|
206 | if (inode->size > bytes) |
---|
207 | bytes = inode->size; |
---|
208 | |
---|
209 | while (bytes) { |
---|
210 | chunk = min(bytes, block_size); |
---|
211 | data = ext2_get_cache(inode, index++); |
---|
212 | memcpy(p, data, chunk); |
---|
213 | |
---|
214 | bytes -= chunk; |
---|
215 | p += chunk; |
---|
216 | } |
---|
217 | |
---|
218 | return 0; |
---|
219 | } |
---|
220 | |
---|
221 | static int ext2_readlink(struct inode *inode, char *buf) |
---|
222 | { |
---|
223 | struct fs_info *fs = inode->fs; |
---|
224 | int sec_per_block = 1 << (fs->block_shift - fs->sector_shift); |
---|
225 | bool fast_symlink; |
---|
226 | |
---|
227 | if (inode->size > BLOCK_SIZE(fs)) |
---|
228 | return -1; /* Error! */ |
---|
229 | |
---|
230 | fast_symlink = (inode->file_acl ? sec_per_block : 0) == inode->blocks; |
---|
231 | if (fast_symlink) |
---|
232 | memcpy(buf, PVT(inode)->i_block, inode->size); |
---|
233 | else |
---|
234 | cache_get_file(inode, buf, inode->size); |
---|
235 | |
---|
236 | return inode->size; |
---|
237 | } |
---|
238 | |
---|
239 | /* |
---|
240 | * Read one directory entry at a time |
---|
241 | */ |
---|
242 | static int ext2_readdir(struct file *file, struct dirent *dirent) |
---|
243 | { |
---|
244 | struct fs_info *fs = file->fs; |
---|
245 | struct inode *inode = file->inode; |
---|
246 | const struct ext2_dir_entry *de; |
---|
247 | const char *data; |
---|
248 | block_t index = file->offset >> fs->block_shift; |
---|
249 | |
---|
250 | if (file->offset >= inode->size) |
---|
251 | return -1; /* End of file */ |
---|
252 | |
---|
253 | data = ext2_get_cache(inode, index); |
---|
254 | de = (const struct ext2_dir_entry *) |
---|
255 | (data + (file->offset & (BLOCK_SIZE(fs) - 1))); |
---|
256 | |
---|
257 | dirent->d_ino = de->d_inode; |
---|
258 | dirent->d_off = file->offset; |
---|
259 | dirent->d_reclen = offsetof(struct dirent, d_name) + de->d_name_len + 1; |
---|
260 | dirent->d_type = ext2_cvt_type(de->d_file_type); |
---|
261 | memcpy(dirent->d_name, de->d_name, de->d_name_len); |
---|
262 | dirent->d_name[de->d_name_len] = '\0'; |
---|
263 | |
---|
264 | file->offset += de->d_rec_len; /* Update for next reading */ |
---|
265 | |
---|
266 | return 0; |
---|
267 | } |
---|
268 | |
---|
269 | /* |
---|
270 | * init. the fs meta data, return the block size bits. |
---|
271 | */ |
---|
272 | static int ext2_fs_init(struct fs_info *fs) |
---|
273 | { |
---|
274 | struct disk *disk = fs->fs_dev->disk; |
---|
275 | struct ext2_sb_info *sbi; |
---|
276 | struct ext2_super_block sb; |
---|
277 | struct cache *cs; |
---|
278 | |
---|
279 | /* read the super block */ |
---|
280 | disk->rdwr_sectors(disk, &sb, 2, 2, 0); |
---|
281 | |
---|
282 | /* check if it is ext2, since we also support btrfs now */ |
---|
283 | if (sb.s_magic != EXT2_SUPER_MAGIC) |
---|
284 | return -1; |
---|
285 | |
---|
286 | sbi = malloc(sizeof(*sbi)); |
---|
287 | if (!sbi) { |
---|
288 | malloc_error("ext2_sb_info structure"); |
---|
289 | return -1; |
---|
290 | } |
---|
291 | fs->fs_info = sbi; |
---|
292 | |
---|
293 | if (sb.s_magic != EXT2_SUPER_MAGIC) { |
---|
294 | printf("ext2 mount error: it's not a EXT2/3/4 file system!\n"); |
---|
295 | return 0; |
---|
296 | } |
---|
297 | |
---|
298 | fs->sector_shift = disk->sector_shift; |
---|
299 | fs->block_shift = sb.s_log_block_size + 10; |
---|
300 | fs->sector_size = 1 << fs->sector_shift; |
---|
301 | fs->block_size = 1 << fs->block_shift; |
---|
302 | |
---|
303 | sbi->s_inodes_per_group = sb.s_inodes_per_group; |
---|
304 | sbi->s_blocks_per_group = sb.s_blocks_per_group; |
---|
305 | sbi->s_inodes_per_block = BLOCK_SIZE(fs) / sb.s_inode_size; |
---|
306 | if (sb.s_desc_size < sizeof(struct ext2_group_desc)) |
---|
307 | sb.s_desc_size = sizeof(struct ext2_group_desc); |
---|
308 | sbi->s_desc_per_block = BLOCK_SIZE(fs) / sb.s_desc_size; |
---|
309 | sbi->s_groups_count = (sb.s_blocks_count - sb.s_first_data_block |
---|
310 | + EXT2_BLOCKS_PER_GROUP(fs) - 1) |
---|
311 | / EXT2_BLOCKS_PER_GROUP(fs); |
---|
312 | sbi->s_first_data_block = sb.s_first_data_block; |
---|
313 | sbi->s_inode_size = sb.s_inode_size; |
---|
314 | |
---|
315 | /* Volume UUID */ |
---|
316 | memcpy(sbi->s_uuid, sb.s_uuid, sizeof(sbi->s_uuid)); |
---|
317 | |
---|
318 | /* Initialize the cache, and force block zero to all zero */ |
---|
319 | cache_init(fs->fs_dev, fs->block_shift); |
---|
320 | cs = _get_cache_block(fs->fs_dev, 0); |
---|
321 | memset(cs->data, 0, fs->block_size); |
---|
322 | cache_lock_block(cs); |
---|
323 | |
---|
324 | return fs->block_shift; |
---|
325 | } |
---|
326 | |
---|
327 | #define EXT2_UUID_LEN (4 + 4 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 4 + 4 + 4 + 1) |
---|
328 | static char *ext2_fs_uuid(struct fs_info *fs) |
---|
329 | { |
---|
330 | char *uuid = NULL; |
---|
331 | |
---|
332 | uuid = malloc(EXT2_UUID_LEN); |
---|
333 | if (!uuid) |
---|
334 | return NULL; |
---|
335 | |
---|
336 | if (snprintf(uuid, EXT2_UUID_LEN, |
---|
337 | "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
---|
338 | EXT2_SB(fs)->s_uuid[0], |
---|
339 | EXT2_SB(fs)->s_uuid[1], |
---|
340 | EXT2_SB(fs)->s_uuid[2], |
---|
341 | EXT2_SB(fs)->s_uuid[3], |
---|
342 | EXT2_SB(fs)->s_uuid[4], |
---|
343 | EXT2_SB(fs)->s_uuid[5], |
---|
344 | EXT2_SB(fs)->s_uuid[6], |
---|
345 | EXT2_SB(fs)->s_uuid[7], |
---|
346 | EXT2_SB(fs)->s_uuid[8], |
---|
347 | EXT2_SB(fs)->s_uuid[9], |
---|
348 | EXT2_SB(fs)->s_uuid[10], |
---|
349 | EXT2_SB(fs)->s_uuid[11], |
---|
350 | EXT2_SB(fs)->s_uuid[12], |
---|
351 | EXT2_SB(fs)->s_uuid[13], |
---|
352 | EXT2_SB(fs)->s_uuid[14], |
---|
353 | EXT2_SB(fs)->s_uuid[15] |
---|
354 | ) < 0) { |
---|
355 | free(uuid); |
---|
356 | return NULL; |
---|
357 | } |
---|
358 | |
---|
359 | return uuid; |
---|
360 | } |
---|
361 | |
---|
362 | const struct fs_ops ext2_fs_ops = { |
---|
363 | .fs_name = "ext2", |
---|
364 | .fs_flags = FS_THISIND | FS_USEMEM, |
---|
365 | .fs_init = ext2_fs_init, |
---|
366 | .searchdir = NULL, |
---|
367 | .getfssec = generic_getfssec, |
---|
368 | .close_file = generic_close_file, |
---|
369 | .mangle_name = generic_mangle_name, |
---|
370 | .chdir_start = generic_chdir_start, |
---|
371 | .open_config = generic_open_config, |
---|
372 | .iget_root = ext2_iget_root, |
---|
373 | .iget = ext2_iget, |
---|
374 | .readlink = ext2_readlink, |
---|
375 | .readdir = ext2_readdir, |
---|
376 | .next_extent = ext2_next_extent, |
---|
377 | .fs_uuid = ext2_fs_uuid, |
---|
378 | }; |
---|