blob: 5c0261b2d397a8695a219d3291f6b1e16fd10907 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2007-2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <string.h>
25#include <stdlib.h>
26#include <debug.h>
27#include <err.h>
28#include <trace.h>
29#include <lk/init.h>
30#include <lib/fs.h>
31#include "ext2_priv.h"
32
33#define LOCAL_TRACE 0
34
35static void endian_swap_superblock(struct ext2_super_block *sb)
36{
37 LE32SWAP(sb->s_inodes_count);
38 LE32SWAP(sb->s_blocks_count);
39 LE32SWAP(sb->s_r_blocks_count);
40 LE32SWAP(sb->s_free_blocks_count);
41 LE32SWAP(sb->s_free_inodes_count);
42 LE32SWAP(sb->s_first_data_block);
43 LE32SWAP(sb->s_log_block_size);
44 LE32SWAP(sb->s_log_frag_size);
45 LE32SWAP(sb->s_blocks_per_group);
46 LE32SWAP(sb->s_frags_per_group);
47 LE32SWAP(sb->s_inodes_per_group);
48 LE32SWAP(sb->s_mtime);
49 LE32SWAP(sb->s_wtime);
50 LE16SWAP(sb->s_mnt_count);
51 LE16SWAP(sb->s_max_mnt_count);
52 LE16SWAP(sb->s_magic);
53 LE16SWAP(sb->s_state);
54 LE16SWAP(sb->s_errors);
55 LE16SWAP(sb->s_minor_rev_level);
56 LE32SWAP(sb->s_lastcheck);
57 LE32SWAP(sb->s_checkinterval);
58 LE32SWAP(sb->s_creator_os);
59 LE32SWAP(sb->s_rev_level);
60 LE16SWAP(sb->s_def_resuid);
61 LE16SWAP(sb->s_def_resgid);
62 LE32SWAP(sb->s_first_ino);
63 LE16SWAP(sb->s_inode_size);
64 LE16SWAP(sb->s_block_group_nr);
65 LE32SWAP(sb->s_feature_compat);
66 LE32SWAP(sb->s_feature_incompat);
67 LE32SWAP(sb->s_feature_ro_compat);
68 LE32SWAP(sb->s_algorithm_usage_bitmap);
69
70 /* ext3 journal stuff */
71 LE32SWAP(sb->s_journal_inum);
72 LE32SWAP(sb->s_journal_dev);
73 LE32SWAP(sb->s_last_orphan);
74 LE32SWAP(sb->s_default_mount_opts);
75 LE32SWAP(sb->s_first_meta_bg);
76}
77
78static void endian_swap_inode(struct ext2_inode *inode)
79{
80 LE16SWAP(inode->i_mode);
81 LE16SWAP(inode->i_uid_low);
82 LE32SWAP(inode->i_size);
83 LE32SWAP(inode->i_atime);
84 LE32SWAP(inode->i_ctime);
85 LE32SWAP(inode->i_mtime);
86 LE32SWAP(inode->i_dtime);
87 LE16SWAP(inode->i_gid_low);
88 LE16SWAP(inode->i_links_count);
89 LE32SWAP(inode->i_blocks);
90 LE32SWAP(inode->i_flags);
91
92 // leave block pointers/symlink data alone
93
94 LE32SWAP(inode->i_generation);
95 LE32SWAP(inode->i_file_acl);
96 LE32SWAP(inode->i_dir_acl);
97 LE32SWAP(inode->i_faddr);
98
99 LE16SWAP(inode->i_uid_high);
100 LE16SWAP(inode->i_gid_high);
101}
102
103static void endian_swap_group_desc(struct ext2_group_desc *gd)
104{
105 LE32SWAP(gd->bg_block_bitmap);
106 LE32SWAP(gd->bg_inode_bitmap);
107 LE32SWAP(gd->bg_inode_table);
108 LE16SWAP(gd->bg_free_blocks_count);
109 LE16SWAP(gd->bg_free_inodes_count);
110 LE16SWAP(gd->bg_used_dirs_count);
111}
112
113status_t ext2_mount(bdev_t *dev, fscookie **cookie)
114{
115 int err;
116
117 LTRACEF("dev %p\n", dev);
118
119 if (!dev)
120 return ERR_NOT_FOUND;
121
122 ext2_t *ext2 = malloc(sizeof(ext2_t));
123 ext2->dev = dev;
124
125 err = bio_read(dev, &ext2->sb, 1024, sizeof(struct ext2_super_block));
126 if (err < 0)
127 goto err;
128
129 endian_swap_superblock(&ext2->sb);
130
131 /* see if the superblock is good */
132 if (ext2->sb.s_magic != EXT2_SUPER_MAGIC) {
133 err = -1;
134 return err;
135 }
136
137 /* calculate group count, rounded up */
138 ext2->s_group_count = (ext2->sb.s_blocks_count + ext2->sb.s_blocks_per_group - 1) / ext2->sb.s_blocks_per_group;
139
140 /* print some info */
141 LTRACEF("rev level %d\n", ext2->sb.s_rev_level);
142 LTRACEF("compat features 0x%x\n", ext2->sb.s_feature_compat);
143 LTRACEF("incompat features 0x%x\n", ext2->sb.s_feature_incompat);
144 LTRACEF("ro compat features 0x%x\n", ext2->sb.s_feature_ro_compat);
145 LTRACEF("block size %d\n", EXT2_BLOCK_SIZE(ext2->sb));
146 LTRACEF("inode size %d\n", EXT2_INODE_SIZE(ext2->sb));
147 LTRACEF("block count %d\n", ext2->sb.s_blocks_count);
148 LTRACEF("blocks per group %d\n", ext2->sb.s_blocks_per_group);
149 LTRACEF("group count %d\n", ext2->s_group_count);
150 LTRACEF("inodes per group %d\n", ext2->sb.s_inodes_per_group);
151
152 /* we only support dynamic revs */
153 if (ext2->sb.s_rev_level > EXT2_DYNAMIC_REV) {
154 err = -2;
155 return err;
156 }
157
158 /* make sure it doesn't have any ro features we don't support */
159 if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
160 err = -3;
161 return err;
162 }
163
164 /* read in all the group descriptors */
165 ext2->gd = malloc(sizeof(struct ext2_group_desc) * ext2->s_group_count);
166 err = bio_read(ext2->dev, (void *)ext2->gd,
167 (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048,
168 sizeof(struct ext2_group_desc) * ext2->s_group_count);
169 if (err < 0) {
170 err = -4;
171 return err;
172 }
173
174 int i;
175 for (i=0; i < ext2->s_group_count; i++) {
176 endian_swap_group_desc(&ext2->gd[i]);
177 LTRACEF("group %d:\n", i);
178 LTRACEF("\tblock bitmap %d\n", ext2->gd[i].bg_block_bitmap);
179 LTRACEF("\tinode bitmap %d\n", ext2->gd[i].bg_inode_bitmap);
180 LTRACEF("\tinode table %d\n", ext2->gd[i].bg_inode_table);
181 LTRACEF("\tfree blocks %d\n", ext2->gd[i].bg_free_blocks_count);
182 LTRACEF("\tfree inodes %d\n", ext2->gd[i].bg_free_inodes_count);
183 LTRACEF("\tused dirs %d\n", ext2->gd[i].bg_used_dirs_count);
184 }
185
186 /* initialize the block cache */
187 ext2->cache = bcache_create(ext2->dev, EXT2_BLOCK_SIZE(ext2->sb), 4);
188
189 /* load the first inode */
190 err = ext2_load_inode(ext2, EXT2_ROOT_INO, &ext2->root_inode);
191 if (err < 0)
192 goto err;
193
194// TRACE("successfully mounted volume\n");
195
196 *cookie = (fscookie *)ext2;
197
198 return 0;
199
200err:
201 LTRACEF("exiting with err code %d\n", err);
202
203 free(ext2);
204 return err;
205}
206
207status_t ext2_unmount(fscookie *cookie)
208{
209 // free it up
210 ext2_t *ext2 = (ext2_t *)cookie;
211
212 bcache_destroy(ext2->cache);
213 free(ext2->gd);
214 free(ext2);
215
216 return 0;
217}
218
219static void get_inode_addr(ext2_t *ext2, inodenum_t num, blocknum_t *block, size_t *block_offset)
220{
221 num--;
222
223 uint32_t group = num / ext2->sb.s_inodes_per_group;
224
225 // calculate the start of the inode table for the group it's in
226 *block = ext2->gd[group].bg_inode_table;
227
228 // add the offset of the inode within the group
229 size_t offset = (num % EXT2_INODES_PER_GROUP(ext2->sb)) * EXT2_INODE_SIZE(ext2->sb);
230 *block_offset = offset % EXT2_BLOCK_SIZE(ext2->sb);
231 *block += offset / EXT2_BLOCK_SIZE(ext2->sb);
232}
233
234int ext2_load_inode(ext2_t *ext2, inodenum_t num, struct ext2_inode *inode)
235{
236 int err;
237
238 LTRACEF("num %d, inode %p\n", num, inode);
239
240 blocknum_t bnum;
241 size_t block_offset;
242 get_inode_addr(ext2, num, &bnum, &block_offset);
243
244 LTRACEF("bnum %u, offset %zd\n", bnum, block_offset);
245
246 /* get a pointer to the cache block */
247 void *cache_ptr;
248 err = bcache_get_block(ext2->cache, &cache_ptr, bnum);
249 if (err < 0)
250 return err;
251
252 /* copy the inode out */
253 memcpy(inode, (uint8_t *)cache_ptr + block_offset, sizeof(struct ext2_inode));
254
255 /* put the cache block */
256 bcache_put_block(ext2->cache, bnum);
257
258 /* endian swap it */
259 endian_swap_inode(inode);
260
261 LTRACEF("read inode: mode 0x%x, size %d\n", inode->i_mode, inode->i_size);
262
263 return 0;
264}
265
266static const struct fs_api ext2_api = {
267 .mount = ext2_mount,
268 .unmount = ext2_unmount,
269 .open = ext2_open_file,
270 .stat = ext2_stat_file,
271 .read = ext2_read_file,
272 .close = ext2_close_file,
273};
274
275STATIC_FS_IMPL(ext2, &ext2_api);