1 /*
2 * Copyright (c) 2007-2015 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
9 #include <string.h>
10 #include <stdlib.h>
11 #include <lk/debug.h>
12 #include <lk/err.h>
13 #include <lk/trace.h>
14 #include <lk/init.h>
15 #include <lib/fs.h>
16 #include "ext2_priv.h"
17
18 #define LOCAL_TRACE 0
19
endian_swap_superblock(struct ext2_super_block * sb)20 static void endian_swap_superblock(struct ext2_super_block *sb) {
21 LE32SWAP(sb->s_inodes_count);
22 LE32SWAP(sb->s_blocks_count);
23 LE32SWAP(sb->s_r_blocks_count);
24 LE32SWAP(sb->s_free_blocks_count);
25 LE32SWAP(sb->s_free_inodes_count);
26 LE32SWAP(sb->s_first_data_block);
27 LE32SWAP(sb->s_log_block_size);
28 LE32SWAP(sb->s_log_frag_size);
29 LE32SWAP(sb->s_blocks_per_group);
30 LE32SWAP(sb->s_frags_per_group);
31 LE32SWAP(sb->s_inodes_per_group);
32 LE32SWAP(sb->s_mtime);
33 LE32SWAP(sb->s_wtime);
34 LE16SWAP(sb->s_mnt_count);
35 LE16SWAP(sb->s_max_mnt_count);
36 LE16SWAP(sb->s_magic);
37 LE16SWAP(sb->s_state);
38 LE16SWAP(sb->s_errors);
39 LE16SWAP(sb->s_minor_rev_level);
40 LE32SWAP(sb->s_lastcheck);
41 LE32SWAP(sb->s_checkinterval);
42 LE32SWAP(sb->s_creator_os);
43 LE32SWAP(sb->s_rev_level);
44 LE16SWAP(sb->s_def_resuid);
45 LE16SWAP(sb->s_def_resgid);
46 LE32SWAP(sb->s_first_ino);
47 LE16SWAP(sb->s_inode_size);
48 LE16SWAP(sb->s_block_group_nr);
49 LE32SWAP(sb->s_feature_compat);
50 LE32SWAP(sb->s_feature_incompat);
51 LE32SWAP(sb->s_feature_ro_compat);
52 LE32SWAP(sb->s_algorithm_usage_bitmap);
53
54 /* ext3 journal stuff */
55 LE32SWAP(sb->s_journal_inum);
56 LE32SWAP(sb->s_journal_dev);
57 LE32SWAP(sb->s_last_orphan);
58 LE32SWAP(sb->s_default_mount_opts);
59 LE32SWAP(sb->s_first_meta_bg);
60 }
61
endian_swap_inode(struct ext2_inode * inode)62 static void endian_swap_inode(struct ext2_inode *inode) {
63 LE16SWAP(inode->i_mode);
64 LE16SWAP(inode->i_uid_low);
65 LE32SWAP(inode->i_size);
66 LE32SWAP(inode->i_atime);
67 LE32SWAP(inode->i_ctime);
68 LE32SWAP(inode->i_mtime);
69 LE32SWAP(inode->i_dtime);
70 LE16SWAP(inode->i_gid_low);
71 LE16SWAP(inode->i_links_count);
72 LE32SWAP(inode->i_blocks);
73 LE32SWAP(inode->i_flags);
74
75 // leave block pointers/symlink data alone
76
77 LE32SWAP(inode->i_generation);
78 LE32SWAP(inode->i_file_acl);
79 LE32SWAP(inode->i_dir_acl);
80 LE32SWAP(inode->i_faddr);
81
82 LE16SWAP(inode->i_uid_high);
83 LE16SWAP(inode->i_gid_high);
84 }
85
endian_swap_group_desc(struct ext2_group_desc * gd)86 static void endian_swap_group_desc(struct ext2_group_desc *gd) {
87 LE32SWAP(gd->bg_block_bitmap);
88 LE32SWAP(gd->bg_inode_bitmap);
89 LE32SWAP(gd->bg_inode_table);
90 LE16SWAP(gd->bg_free_blocks_count);
91 LE16SWAP(gd->bg_free_inodes_count);
92 LE16SWAP(gd->bg_used_dirs_count);
93 }
94
ext2_mount(bdev_t * dev,fscookie ** cookie)95 status_t ext2_mount(bdev_t *dev, fscookie **cookie) {
96 int err;
97
98 LTRACEF("dev %p\n", dev);
99
100 if (!dev)
101 return ERR_NOT_FOUND;
102
103 ext2_t *ext2 = malloc(sizeof(ext2_t));
104 ext2->dev = dev;
105
106 err = bio_read(dev, &ext2->sb, 1024, sizeof(struct ext2_super_block));
107 if (err < 0)
108 goto err;
109
110 endian_swap_superblock(&ext2->sb);
111
112 /* see if the superblock is good */
113 if (ext2->sb.s_magic != EXT2_SUPER_MAGIC) {
114 err = -1;
115 return err;
116 }
117
118 /* calculate group count, rounded up */
119 ext2->s_group_count = (ext2->sb.s_blocks_count + ext2->sb.s_blocks_per_group - 1) / ext2->sb.s_blocks_per_group;
120
121 /* print some info */
122 LTRACEF("rev level %d\n", ext2->sb.s_rev_level);
123 LTRACEF("compat features 0x%x\n", ext2->sb.s_feature_compat);
124 LTRACEF("incompat features 0x%x\n", ext2->sb.s_feature_incompat);
125 LTRACEF("ro compat features 0x%x\n", ext2->sb.s_feature_ro_compat);
126 LTRACEF("block size %d\n", EXT2_BLOCK_SIZE(ext2->sb));
127 LTRACEF("inode size %d\n", EXT2_INODE_SIZE(ext2->sb));
128 LTRACEF("block count %d\n", ext2->sb.s_blocks_count);
129 LTRACEF("blocks per group %d\n", ext2->sb.s_blocks_per_group);
130 LTRACEF("group count %d\n", ext2->s_group_count);
131 LTRACEF("inodes per group %d\n", ext2->sb.s_inodes_per_group);
132
133 /* we only support dynamic revs */
134 if (ext2->sb.s_rev_level > EXT2_DYNAMIC_REV) {
135 err = -2;
136 return err;
137 }
138
139 /* make sure it doesn't have any ro features we don't support */
140 if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
141 err = -3;
142 return err;
143 }
144
145 /* read in all the group descriptors */
146 ext2->gd = malloc(sizeof(struct ext2_group_desc) * ext2->s_group_count);
147 err = bio_read(ext2->dev, (void *)ext2->gd,
148 (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048,
149 sizeof(struct ext2_group_desc) * ext2->s_group_count);
150 if (err < 0) {
151 err = -4;
152 return err;
153 }
154
155 int i;
156 for (i=0; i < ext2->s_group_count; i++) {
157 endian_swap_group_desc(&ext2->gd[i]);
158 LTRACEF("group %d:\n", i);
159 LTRACEF("\tblock bitmap %d\n", ext2->gd[i].bg_block_bitmap);
160 LTRACEF("\tinode bitmap %d\n", ext2->gd[i].bg_inode_bitmap);
161 LTRACEF("\tinode table %d\n", ext2->gd[i].bg_inode_table);
162 LTRACEF("\tfree blocks %d\n", ext2->gd[i].bg_free_blocks_count);
163 LTRACEF("\tfree inodes %d\n", ext2->gd[i].bg_free_inodes_count);
164 LTRACEF("\tused dirs %d\n", ext2->gd[i].bg_used_dirs_count);
165 }
166
167 /* initialize the block cache */
168 ext2->cache = bcache_create(ext2->dev, EXT2_BLOCK_SIZE(ext2->sb), 4);
169
170 /* load the first inode */
171 err = ext2_load_inode(ext2, EXT2_ROOT_INO, &ext2->root_inode);
172 if (err < 0)
173 goto err;
174
175 // TRACE("successfully mounted volume\n");
176
177 *cookie = (fscookie *)ext2;
178
179 return 0;
180
181 err:
182 LTRACEF("exiting with err code %d\n", err);
183
184 free(ext2);
185 return err;
186 }
187
ext2_unmount(fscookie * cookie)188 status_t ext2_unmount(fscookie *cookie) {
189 // free it up
190 ext2_t *ext2 = (ext2_t *)cookie;
191
192 bcache_destroy(ext2->cache);
193 free(ext2->gd);
194 free(ext2);
195
196 return 0;
197 }
198
get_inode_addr(ext2_t * ext2,inodenum_t num,blocknum_t * block,size_t * block_offset)199 static void get_inode_addr(ext2_t *ext2, inodenum_t num, blocknum_t *block, size_t *block_offset) {
200 num--;
201
202 uint32_t group = num / ext2->sb.s_inodes_per_group;
203
204 // calculate the start of the inode table for the group it's in
205 *block = ext2->gd[group].bg_inode_table;
206
207 // add the offset of the inode within the group
208 size_t offset = (num % EXT2_INODES_PER_GROUP(ext2->sb)) * EXT2_INODE_SIZE(ext2->sb);
209 *block_offset = offset % EXT2_BLOCK_SIZE(ext2->sb);
210 *block += offset / EXT2_BLOCK_SIZE(ext2->sb);
211 }
212
ext2_load_inode(ext2_t * ext2,inodenum_t num,struct ext2_inode * inode)213 int ext2_load_inode(ext2_t *ext2, inodenum_t num, struct ext2_inode *inode) {
214 int err;
215
216 LTRACEF("num %d, inode %p\n", num, inode);
217
218 blocknum_t bnum;
219 size_t block_offset;
220 get_inode_addr(ext2, num, &bnum, &block_offset);
221
222 LTRACEF("bnum %u, offset %zd\n", bnum, block_offset);
223
224 /* get a pointer to the cache block */
225 void *cache_ptr;
226 err = bcache_get_block(ext2->cache, &cache_ptr, bnum);
227 if (err < 0)
228 return err;
229
230 /* copy the inode out */
231 memcpy(inode, (uint8_t *)cache_ptr + block_offset, sizeof(struct ext2_inode));
232
233 /* put the cache block */
234 bcache_put_block(ext2->cache, bnum);
235
236 /* endian swap it */
237 endian_swap_inode(inode);
238
239 LTRACEF("read inode: mode 0x%x, size %d\n", inode->i_mode, inode->i_size);
240
241 return 0;
242 }
243
244 static const struct fs_api ext2_api = {
245 .mount = ext2_mount,
246 .unmount = ext2_unmount,
247 .open = ext2_open_file,
248 .stat = ext2_stat_file,
249 .read = ext2_read_file,
250 .close = ext2_close_file,
251 };
252
253 STATIC_FS_IMPL(ext2, &ext2_api);
254