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 }; 379