rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 1 | /* |
| 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 | |
| 35 | static 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 | |
| 78 | static 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 | |
| 103 | static 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 | |
| 113 | status_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 | |
| 200 | err: |
| 201 | LTRACEF("exiting with err code %d\n", err); |
| 202 | |
| 203 | free(ext2); |
| 204 | return err; |
| 205 | } |
| 206 | |
| 207 | status_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 | |
| 219 | static 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 | |
| 234 | int 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 | |
| 266 | static 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 | |
| 275 | STATIC_FS_IMPL(ext2, &ext2_api); |