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