static quik_err_t ext2fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) { struct ext2_block_group blkgrp; struct ext2_sblock *sblock = &data->sblock; int inodes_per_block; quik_err_t err; unsigned int blkno; unsigned int blkoff; #ifdef DEBUG printk ("ext2fs read inode %d, inode_size %d\n", ino, inode_size); #endif /* It is easier to calculate if the first inode is 0. */ ino--; err = ext2fs_blockgroup(data, ino / __le32_to_cpu (sblock->inodes_per_group), &blkgrp); if (err != ERR_NONE) { return err; } inodes_per_block = EXT2_BLOCK_SIZE(data) / inode_size; blkno = __le32_to_cpu(blkgrp.inode_table_id) + (ino % __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; blkoff = (ino % inodes_per_block) * inode_size; #ifdef DEBUG printk ("ext2fs read inode blkno %d blkoff %d\n", blkno, blkoff); #endif /* Read the inode. */ err = part_read(data->part, blkno << LOG2_EXT2_BLOCK_SIZE (data), blkoff, sizeof(struct ext2_inode), (char *) inode); if (err != ERR_NONE) { return err; } return ERR_NONE; }
static int ext2fs_blockgroup (struct ext2_data *data, int group, struct ext2_block_group *blkgrp) { unsigned int blkno; unsigned int blkoff; unsigned int desc_per_blk; desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group); blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 + group / desc_per_blk; blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group); #ifdef DEBUG printf ("ext2fs read %d group descriptor (blkno %d blkoff %d)\n", group, blkno, blkoff); #endif return (ext2fs_devread (blkno << LOG2_EXT2_BLOCK_SIZE(data), blkoff, sizeof(struct ext2_block_group), (char *)blkgrp)); }
static int ext4fs_blockgroup (struct ext2_data *data, int group, struct ext2_block_group *blkgrp) { long int blkno; unsigned int blkoff, desc_per_blk; desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group); blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 + group / desc_per_blk; blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group); debug("ext4fs read %d group descriptor (blkno %ld blkoff %u)\n", group, blkno, blkoff); return ext4fs_devread(blkno << LOG2_EXT2_BLOCK_SIZE(data), blkoff, sizeof(struct ext2_block_group), (char *)blkgrp); }
int ext4fs_get_indir_block(struct ext2fs_node *node, struct ext4fs_indir_block *indir, int blkno) { struct ext_filesystem *fs = node->data->fs; int blksz; int ret; blksz = EXT2_BLOCK_SIZE(node->data); if (indir->blkno == blkno) return 0; ret = ext4fs_devread(fs, blkno, 0, blksz, (void *)indir->data); if (ret) { dev_err(fs->dev, "** SI ext2fs read block (indir 1)" "failed. **\n"); return ret; } return 0; }
static int ext2fs_read_inode (struct ext2_data *data, int ino, struct ext2_inode *inode) { struct ext2_block_group blkgrp; struct ext2_sblock *sblock = &data->sblock; int inodes_per_block; int status; unsigned int blkno; unsigned int blkoff; /* It is easier to calculate if the first inode is 0. */ ino--; #ifdef DEBUG printf ("ext2fs read inode %d\n", ino); #endif status = ext2fs_blockgroup (data, ino / __le32_to_cpu (sblock->inodes_per_group), &blkgrp); if (status == 0) { return (0); } inodes_per_block = EXT2_BLOCK_SIZE (data) / 128; blkno = (ino % __le32_to_cpu (sblock->inodes_per_group)) / inodes_per_block; blkoff = (ino % __le32_to_cpu (sblock->inodes_per_group)) % inodes_per_block; #ifdef DEBUG printf ("ext2fs read inode blkno %d blkoff %d\n", blkno, blkoff); #endif /* Read the inode. */ status = ext2fs_devread (((__le32_to_cpu (blkgrp.inode_table_id) + blkno) << LOG2_EXT2_BLOCK_SIZE (data)), sizeof (struct ext2_inode) * blkoff, sizeof (struct ext2_inode), (char *) inode); if (status == 0) { return (0); } return (1); }
static struct grub_ext4_extent_header * grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf, struct grub_ext4_extent_header *ext_block, grub_uint32_t fileblock) { struct grub_ext4_extent_idx *index; while (1) { int i; grub_disk_addr_t block; index = (struct grub_ext4_extent_idx *) (ext_block + 1); if (grub_le_to_cpu16(ext_block->magic) != EXT4_EXT_MAGIC) return 0; if (ext_block->depth == 0) return ext_block; for (i = 0; i < grub_le_to_cpu16 (ext_block->entries); i++) { if (fileblock < grub_le_to_cpu32(index[i].block)) break; } if (--i < 0) return 0; block = grub_le_to_cpu16 (index[i].leaf_hi); block = (block << 32) + grub_le_to_cpu32 (index[i].leaf); if (grub_disk_read (data->disk, block << LOG2_EXT2_BLOCK_SIZE (data), 0, EXT2_BLOCK_SIZE(data), buf)) return 0; ext_block = (struct grub_ext4_extent_header *) buf; } }
ext2_VOLUME* ext2_mount(int fd) { ext2_VOLUME *volume; struct ext2_super_block *super; char *buffer; super = (struct ext2_super_block*)malloc(sizeof(struct ext2_super_block)); if (super == NULL) return NULL; ext2_get_super(fd, super); if (super->s_magic != EXT2_SUPER_MAGIC) { free(super); return NULL; } buffer = (char*)malloc(EXT2_BLOCK_SIZE(super)); if (buffer == NULL) { free(super); return NULL; } volume = (ext2_VOLUME*)malloc(sizeof(ext2_VOLUME)); if (volume == NULL) { free(super); free(buffer); return NULL; } volume->buffer = buffer; volume->fd = fd; volume->super = super; volume->current = -1; ext2_read_block(volume, 0); return volume; }
static struct ext4_extent_header *ext4fs_get_extent_block(struct ext2_data *data, char *buf, struct ext4_extent_header *ext_block, uint32_t fileblock, int log2_blksz) { struct ext4_extent_idx *index; unsigned long long block; struct ext_filesystem *fs = data->fs; int blksz = EXT2_BLOCK_SIZE(data); int i, ret; while (1) { index = (struct ext4_extent_idx *)(ext_block + 1); if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC) return 0; if (ext_block->eh_depth == 0) return ext_block; i = -1; do { i++; if (i >= le16_to_cpu(ext_block->eh_entries)) break; } while (fileblock >= le32_to_cpu(index[i].ei_block)); if (--i < 0) return 0; block = le16_to_cpu(index[i].ei_leaf_hi); block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo); ret = ext4fs_devread(fs, block << log2_blksz, 0, blksz, buf); if (ret) return NULL; else ext_block = (struct ext4_extent_header *)buf; } }
static int move_metadata_block(struct defrag_ctx *c, struct inode *inode, blk64_t from, blk64_t to) { struct ext3_extent_header *header = malloc(EXT2_BLOCK_SIZE(&c->sb)); /* Whether it's a leaf or an index doesn't matter for the logical block address */ struct ext3_extent *extents = (void *)(header + 1); __u32 logical_block; int ret; ret = read_block(c, header, from); if (ret) goto out_error; ret = write_block(c, header, to); if (ret) goto out_error; logical_block = extents->ee_block; free(header); return update_metadata_move(c, inode, from, to, logical_block, 0); out_error: free(header); return ret; }
static int ext2fs_read_block(struct ext2fs_node *node, int fileblock) { struct ext2_data *data = node->data; struct ext2_inode *inode = &node->inode; int blknr; int blksz = EXT2_BLOCK_SIZE (data); int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data); int status; /* Direct blocks. */ if (fileblock < INDIRECT_BLOCKS) { blknr = __le32_to_cpu (inode->b.blocks.dir_blocks[fileblock]); } /* Indirect. */ else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) { if (indir1_block == NULL) { indir1_block = (uint32_t *) malloc (blksz); if (indir1_block == NULL) { printf ("** ext2fs read block (indir 1) malloc failed. **\n"); return (-1); } indir1_size = blksz; indir1_blkno = -1; } if (blksz != indir1_size) { free (indir1_block); indir1_block = NULL; indir1_size = 0; indir1_blkno = -1; indir1_block = (uint32_t *) malloc (blksz); if (indir1_block == NULL) { printf ("** ext2fs read block (indir 1) malloc failed. **\n"); return (-1); } indir1_size = blksz; } if ((__le32_to_cpu (inode->b.blocks.indir_block) << log2_blksz) != indir1_blkno) { status = ext2fs_devread (__le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz, 0, blksz, (char *) indir1_block); if (status == 0) { printf ("** ext2fs read block (indir 1) failed. **\n"); return (0); } indir1_blkno = __le32_to_cpu (inode->b.blocks. indir_block) << log2_blksz; } blknr = __le32_to_cpu (indir1_block [fileblock - INDIRECT_BLOCKS]); } /* Double indirect. */ else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 * (blksz / 4 + 1)))) { unsigned int perblock = blksz / 4; unsigned int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4); if (indir1_block == NULL) { indir1_block = (uint32_t *) malloc (blksz); if (indir1_block == NULL) { printf ("** ext2fs read block (indir 2 1) malloc failed. **\n"); return (-1); } indir1_size = blksz; indir1_blkno = -1; } if (blksz != indir1_size) { free (indir1_block); indir1_block = NULL; indir1_size = 0; indir1_blkno = -1; indir1_block = (uint32_t *) malloc (blksz); if (indir1_block == NULL) { printf ("** ext2fs read block (indir 2 1) malloc failed. **\n"); return (-1); } indir1_size = blksz; } if ((__le32_to_cpu (inode->b.blocks.double_indir_block) << log2_blksz) != indir1_blkno) { status = ext2fs_devread (__le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz, 0, blksz, (char *) indir1_block); if (status == 0) { printf ("** ext2fs read block (indir 2 1) failed. **\n"); return (-1); } indir1_blkno = __le32_to_cpu (inode->b.blocks.double_indir_block) << log2_blksz; } if (indir2_block == NULL) { indir2_block = (uint32_t *) malloc (blksz); if (indir2_block == NULL) { printf ("** ext2fs read block (indir 2 2) malloc failed. **\n"); return (-1); } indir2_size = blksz; indir2_blkno = -1; } if (blksz != indir2_size) { free (indir2_block); indir2_block = NULL; indir2_size = 0; indir2_blkno = -1; indir2_block = (uint32_t *) malloc (blksz); if (indir2_block == NULL) { printf ("** ext2fs read block (indir 2 2) malloc failed. **\n"); return (-1); } indir2_size = blksz; } if ((__le32_to_cpu (indir1_block[rblock / perblock]) << log2_blksz) != indir2_blkno) { status = ext2fs_devread (__le32_to_cpu(indir1_block[rblock / perblock]) << log2_blksz, 0, blksz, (char *) indir2_block); if (status == 0) { printf ("** ext2fs read block (indir 2 2) failed. **\n"); return (-1); } indir2_blkno = __le32_to_cpu (indir1_block[rblock / perblock]) << log2_blksz; } blknr = __le32_to_cpu (indir2_block[rblock % perblock]); } /* Tripple indirect. */ else { printf ("** ext2fs doesn't support tripple indirect blocks. **\n"); return (-1); } #ifdef DEBUG printf ("ext2fs_read_block %08x\n", blknr); #endif return (blknr); }
errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { blk64_t block_nr; unsigned long group, block, offset; errcode_t retval = 0; struct ext2_inode_large *w_inode; char *ptr; unsigned i; int clen; int length = EXT2_INODE_SIZE(fs->super); EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user provided an override function */ if (fs->write_inode) { retval = (fs->write_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; /* Prepare our shadow buffer for read/modify/byteswap/write */ retval = ext2fs_get_mem(length, &w_inode); if (retval) return retval; if (bufsize < length) { int old_flags = fs->flags; fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)w_inode, length); fs->flags = (old_flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) | (fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS); if (retval) goto errout; } /* Check to see if the inode cache needs to be updated */ if (fs->icache) { for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { memcpy(fs->icache->cache[i].inode, inode, (bufsize > length) ? length : bufsize); break; } } } else { retval = ext2fs_create_inode_cache(fs, 4); if (retval) goto errout; } memcpy(w_inode, inode, (bufsize > length) ? length : bufsize); if (!(fs->flags & EXT2_FLAG_RW)) { retval = EXT2_ET_RO_FILSYS; goto errout; } #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length); #endif retval = ext2fs_inode_csum_set(fs, ino, w_inode); if (retval) goto errout; group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!ext2fs_inode_table_loc(fs, (unsigned) group)) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block; offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); ptr = (char *) w_inode; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (fs->icache->buffer_blk != block_nr) { retval = io_channel_read_blk64(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; fs->icache->buffer_blk = block_nr; } memcpy((char *) fs->icache->buffer + (unsigned) offset, ptr, clen); retval = io_channel_write_blk64(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; offset = 0; ptr += clen; length -= clen; block_nr++; } fs->flags |= EXT2_FLAG_CHANGED; errout: ext2fs_free_mem(&w_inode); return retval; }
static void list_desc (ext2_filsys fs) { unsigned long i; blk64_t first_block, last_block; blk64_t super_blk, old_desc_blk, new_desc_blk; char *block_bitmap=NULL, *inode_bitmap=NULL; const char *units = _("blocks"); int inode_blocks_per_group, old_desc_blocks, reserved_gdt; int block_nbytes, inode_nbytes; int has_super; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); ext2_ino_t ino_itr = 1; errcode_t retval; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) units = _("clusters"); block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; if (fs->block_map) block_bitmap = malloc(block_nbytes); if (fs->inode_map) inode_bitmap = malloc(inode_nbytes); inode_blocks_per_group = ((fs->super->s_inodes_per_group * EXT2_INODE_SIZE(fs->super)) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super); reserved_gdt = fs->super->s_reserved_gdt_blocks; fputc('\n', stdout); first_block = fs->super->s_first_data_block; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks; for (i = 0; i < fs->group_desc_count; i++) { first_block = ext2fs_group_first_block2(fs, i); last_block = ext2fs_group_last_block2(fs, i); ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, &new_desc_blk, 0); printf (_("Group %lu: (Blocks "), i); print_range(first_block, last_block); fputs(")", stdout); print_bg_opts(fs, i); if (ext2fs_has_group_desc_csum(fs)) { unsigned csum = ext2fs_bg_checksum(fs, i); unsigned exp_csum = ext2fs_group_desc_csum(fs, i); printf(_(" Checksum 0x%04x"), csum); if (csum != exp_csum) printf(_(" (EXPECTED 0x%04x)"), exp_csum); printf(_(", unused inodes %u\n"), ext2fs_bg_itable_unused(fs, i)); } has_super = ((i==0) || super_blk); if (has_super) { printf (_(" %s superblock at "), i == 0 ? _("Primary") : _("Backup")); print_number(super_blk); } if (old_desc_blk) { printf("%s", _(", Group descriptors at ")); print_range(old_desc_blk, old_desc_blk + old_desc_blocks - 1); if (reserved_gdt) { printf("%s", _("\n Reserved GDT blocks at ")); print_range(old_desc_blk + old_desc_blocks, old_desc_blk + old_desc_blocks + reserved_gdt - 1); } } else if (new_desc_blk) { fputc(has_super ? ',' : ' ', stdout); printf("%s", _(" Group descriptor at ")); print_number(new_desc_blk); has_super++; } if (has_super) fputc('\n', stdout); fputs(_(" Block bitmap at "), stdout); print_number(ext2fs_block_bitmap_loc(fs, i)); print_bg_rel_offset(fs, ext2fs_block_bitmap_loc(fs, i), 0, first_block, last_block); if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) printf(_(", csum 0x%08x"), ext2fs_block_bitmap_checksum(fs, i)); fputs(_(", Inode bitmap at "), stdout); print_number(ext2fs_inode_bitmap_loc(fs, i)); print_bg_rel_offset(fs, ext2fs_inode_bitmap_loc(fs, i), 0, first_block, last_block); if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) printf(_(", csum 0x%08x"), ext2fs_inode_bitmap_checksum(fs, i)); fputs(_("\n Inode table at "), stdout); print_range(ext2fs_inode_table_loc(fs, i), ext2fs_inode_table_loc(fs, i) + inode_blocks_per_group - 1); print_bg_rel_offset(fs, ext2fs_inode_table_loc(fs, i), 1, first_block, last_block); printf (_("\n %u free %s, %u free inodes, " "%u directories%s"), ext2fs_bg_free_blocks_count(fs, i), units, ext2fs_bg_free_inodes_count(fs, i), ext2fs_bg_used_dirs_count(fs, i), ext2fs_bg_itable_unused(fs, i) ? "" : "\n"); if (ext2fs_bg_itable_unused(fs, i)) printf (_(", %u unused inodes\n"), ext2fs_bg_itable_unused(fs, i)); if (block_bitmap) { fputs(_(" Free blocks: "), stdout); retval = ext2fs_get_block_bitmap_range2(fs->block_map, blk_itr, block_nbytes << 3, block_bitmap); if (retval) com_err("list_desc", retval, "while reading block bitmap"); else print_free(i, block_bitmap, fs->super->s_clusters_per_group, fs->super->s_first_data_block, EXT2FS_CLUSTER_RATIO(fs)); fputc('\n', stdout); blk_itr += fs->super->s_clusters_per_group; } if (inode_bitmap) { fputs(_(" Free inodes: "), stdout); retval = ext2fs_get_inode_bitmap_range2(fs->inode_map, ino_itr, inode_nbytes << 3, inode_bitmap); if (retval) com_err("list_desc", retval, "while reading inode bitmap"); else print_free(i, inode_bitmap, fs->super->s_inodes_per_group, 1, 1); fputc('\n', stdout); ino_itr += fs->super->s_inodes_per_group; } } if (block_bitmap) free(block_bitmap); if (inode_bitmap) free(inode_bitmap); }
errcode_t e2fsck_get_device_size(e2fsck_t ctx) { return (ext2fs_get_device_size(ctx->filesystem_name, EXT2_BLOCK_SIZE(ctx->fs->super), &ctx->num_blocks)); }
// Return the block size for a filesystem. __u32 get_block_size(void *fs) { return EXT2_BLOCK_SIZE(get_super_block(fs)); }
static grub_disk_addr_t grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_ext2_data *data = node->data; struct grub_ext2_inode *inode = &node->inode; int blknr = -1; unsigned int blksz = EXT2_BLOCK_SIZE (data); int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data); if (grub_le_to_cpu32(inode->flags) & EXT4_EXTENTS_FLAG) { char buf[EXT2_BLOCK_SIZE(data)]; struct grub_ext4_extent_header *leaf; struct grub_ext4_extent *ext; int i; leaf = grub_ext4_find_leaf (data, buf, (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, fileblock); if (! leaf) { grub_error (GRUB_ERR_BAD_FS, "invalid extent"); return -1; } ext = (struct grub_ext4_extent *) (leaf + 1); for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++) { if (fileblock < grub_le_to_cpu32 (ext[i].block)) break; } if (--i >= 0) { fileblock -= grub_le_to_cpu32 (ext[i].block); if (fileblock >= grub_le_to_cpu16 (ext[i].len)) return 0; else { grub_disk_addr_t start; start = grub_le_to_cpu16 (ext[i].start_hi); start = (start << 32) + grub_le_to_cpu32 (ext[i].start); return fileblock + start; } } else { grub_error (GRUB_ERR_BAD_FS, "something wrong with extent"); return -1; } } /* Direct blocks. */ if (fileblock < INDIRECT_BLOCKS) blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]); /* Indirect. */ else if (fileblock < INDIRECT_BLOCKS + blksz / 4) { grub_uint32_t indir[blksz / 4]; if (grub_disk_read (data->disk, ((grub_disk_addr_t) grub_le_to_cpu32 (inode->blocks.indir_block)) << log2_blksz, 0, blksz, indir)) return grub_errno; blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]); } /* Double indirect. */ else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1)) { unsigned int perblock = blksz / 4; unsigned int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4); grub_uint32_t indir[blksz / 4]; if (grub_disk_read (data->disk, ((grub_disk_addr_t) grub_le_to_cpu32 (inode->blocks.double_indir_block)) << log2_blksz, 0, blksz, indir)) return grub_errno; if (grub_disk_read (data->disk, ((grub_disk_addr_t) grub_le_to_cpu32 (indir[rblock / perblock])) << log2_blksz, 0, blksz, indir)) return grub_errno; blknr = grub_le_to_cpu32 (indir[rblock % perblock]); } /* triple indirect. */ else { grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ext2fs doesn't support triple indirect blocks"); } return blknr; }
/* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. * * Valid flags for ext2fs_open() * * EXT2_FLAG_RW - Open the filesystem for read/write. * EXT2_FLAG_FORCE - Open the filesystem even if some of the * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device */ errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; unsigned long i, first_meta_bg; __u32 features; int groups_per_block, blocks_per_group, io_flags; blk_t group_block, blk; char *dest, *cp; #ifdef WORDS_BIGENDIAN struct ext2_group_desc *gdp; int j; #endif EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; /* don't overwrite sb backups unless flag is explicitly cleared */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; fs->umask = 022; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); cp = strchr(fs->device_name, '?'); if (!io_options && cp) { *cp++ = 0; io_options = cp; } io_flags = 0; if (flags & EXT2_FLAG_RW) io_flags |= IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(fs->device_name, io_flags, &fs->io); if (retval) goto cleanup; if (io_options && (retval = io_channel_set_options(fs->io, io_options))) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -(int)sizeof(struct ext2_image_hdr), fs->image_header); if (retval) goto cleanup; if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) return EXT2_ET_MAGIC_E2IMAGE; superblock = 1; block_size = fs->image_header->fs_blocksize; } /* * If the user specifies a specific block # for the * superblock, then he/she must also specify the block size! * Otherwise, read the master superblock located at offset * SUPERBLOCK_OFFSET from the start of the partition. * * Note: we only save a backup copy of the superblock if we * are reading the superblock from the primary superblock location. */ if (superblock) { if (!block_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } io_channel_set_blksize(fs->io, block_size); group_block = superblock; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto cleanup; } retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, fs->super); if (retval) goto cleanup; if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); #else if (fs->flags & EXT2_FLAG_SWAP_BYTES) { retval = EXT2_ET_UNIMPLEMENTED; goto cleanup; } #endif if (fs->super->s_magic != EXT2_SUPER_MAGIC) { retval = EXT2_ET_BAD_MAGIC; goto cleanup; } if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { retval = EXT2_ET_REV_TOO_HIGH; goto cleanup; } /* * Check for feature set incompatibility */ if (!(flags & EXT2_FLAG_FORCE)) { features = fs->super->s_feature_incompat; #ifdef EXT2_LIB_SOFTSUPP_INCOMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= !EXT2_LIB_SOFTSUPP_INCOMPAT; #endif if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } features = fs->super->s_feature_ro_compat; #ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT; #endif if ((flags & EXT2_FLAG_RW) && (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } } if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > EXT2_MAX_BLOCK_LOG_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = EXT2_FRAG_SIZE(fs->super); fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * EXT2_INODE_SIZE(fs->super) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super)); if (block_size) { if (block_size != fs->blocksize) { retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; goto cleanup; } } /* * Set the blocksize to the filesystem's blocksize. */ io_channel_set_blksize(fs->io, fs->blocksize); /* * If this is an external journal device, don't try to read * the group descriptors, because they're not there. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; *ret_fs = fs; return 0; } if (EXT2_INODES_PER_GROUP(fs->super) == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* * Read group descriptors */ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); if (blocks_per_group == 0 || blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || EXT2_DESC_PER_BLOCK(fs->super) == 0 || fs->super->s_first_data_block >= fs->super->s_blocks_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = ext2fs_div_ceil(fs->super->s_blocks_count - fs->super->s_first_data_block, blocks_per_group); if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != fs->super->s_inodes_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(fs->super)); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block; dest = (char *) fs->group_desc; groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) first_meta_bg = fs->super->s_first_meta_bg; else first_meta_bg = fs->desc_blocks; if (first_meta_bg) { retval = io_channel_read_blk(fs->io, group_block+1, first_meta_bg, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block*first_meta_bg; j++) ext2fs_swap_group_desc(gdp++); #endif dest += fs->blocksize*first_meta_bg; } for (i=first_meta_bg ; i < fs->desc_blocks; i++) { blk = ext2fs_descriptor_block_loc(fs, group_block, i); retval = io_channel_read_blk(fs->io, blk, 1, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block; j++) ext2fs_swap_group_desc(gdp++); #endif dest += fs->blocksize; } fs->stride = fs->super->s_raid_stride; /* * If recovery is from backup superblock, Clear _UNININT flags & * reset bg_itable_unused to zero */ if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { struct ext2_group_desc *gd; for (i = 0, gd = fs->group_desc; i < fs->group_desc_count; i++, gd++) { gd->bg_flags &= ~EXT2_BG_BLOCK_UNINIT; gd->bg_flags &= ~EXT2_BG_INODE_UNINIT; gd->bg_itable_unused = 0; } ext2fs_mark_super_dirty(fs); } fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; *ret_fs = fs; return 0; cleanup: if (flags & EXT2_FLAG_NOFREE_ON_ERROR) *ret_fs = fs; else ext2fs_free(fs); return retval; }
int ext2_lblkno( struct ext2_super_block * fs, vm_offset_t offset) { return offset / EXT2_BLOCK_SIZE(fs); }
int ext2_blkoff( struct ext2_super_block * fs, vm_offset_t offset) { return offset % EXT2_BLOCK_SIZE(fs); }
int ext2_fsbtodb( struct ext2_super_block *fs, int b) { return (b * EXT2_BLOCK_SIZE(fs)) / DEV_BSIZE; }
errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { blk64_t block_nr; unsigned long group, block, offset; errcode_t retval = 0; struct ext2_inode_large temp_inode, *w_inode; char *ptr; int clen, i, length; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user provided an override function */ if (fs->write_inode) { retval = (fs->write_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } /* Check to see if the inode cache needs to be updated */ if (fs->icache) { for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { fs->icache->cache[i].inode = *inode; break; } } } else { retval = create_icache(fs); if (retval) return retval; } if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; length = bufsize; if (length < EXT2_INODE_SIZE(fs->super)) length = EXT2_INODE_SIZE(fs->super); if (length > (int) sizeof(struct ext2_inode_large)) { w_inode = malloc(length); if (!w_inode) { retval = ENOMEM; goto errout; } } else w_inode = &temp_inode; memset(w_inode, 0, length); #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, w_inode, (struct ext2_inode_large *) inode, 1, bufsize); #else memcpy(w_inode, inode, bufsize); #endif group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!ext2fs_inode_table_loc(fs, (unsigned) group)) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } block_nr = ext2fs_inode_table_loc(fs, (unsigned) group) + block; offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); length = EXT2_INODE_SIZE(fs->super); if (length > bufsize) length = bufsize; ptr = (char *) w_inode; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (fs->icache->buffer_blk != block_nr) { retval = io_channel_read_blk64(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; fs->icache->buffer_blk = block_nr; } memcpy((char *) fs->icache->buffer + (unsigned) offset, ptr, clen); retval = io_channel_write_blk64(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; offset = 0; ptr += clen; length -= clen; block_nr++; } fs->flags |= EXT2_FLAG_CHANGED; errout: if (w_inode && w_inode != &temp_inode) free(w_inode); return retval; }
int ext2_bmaparray(struct vnode *vp, daddr_t bn, daddr_t *bnp, int *runp, int *runb) { struct inode *ip; struct buf *bp; struct ext2mount *ump; struct mount *mp; struct vnode *devvp; struct indir a[NIADDR+1], *ap; daddr_t daddr; e2fs_lbn_t metalbn; int error, num, maxrun = 0, bsize; int *nump; ap = NULL; ip = VTOI(vp); mp = vp->v_mount; ump = VFSTOEXT2(mp); devvp = ump->um_devvp; bsize = EXT2_BLOCK_SIZE(ump->um_e2fs); if (runp) { maxrun = mp->mnt_iosize_max / bsize - 1; *runp = 0; } if (runb) { *runb = 0; } ap = a; nump = # error = ext2_getlbns(vp, bn, ap, nump); if (error) return (error); num = *nump; if (num == 0) { *bnp = blkptrtodb(ump, ip->i_db[bn]); if (*bnp == 0) { *bnp = -1; } else if (runp) { daddr_t bnb = bn; for (++bn; bn < NDADDR && *runp < maxrun && is_sequential(ump, ip->i_db[bn - 1], ip->i_db[bn]); ++bn, ++*runp); bn = bnb; if (runb && (bn > 0)) { for (--bn; (bn >= 0) && (*runb < maxrun) && is_sequential(ump, ip->i_db[bn], ip->i_db[bn + 1]); --bn, ++*runb); } } return (0); } /* Get disk address out of indirect block array */ daddr = ip->i_ib[ap->in_off]; for (bp = NULL, ++ap; --num; ++ap) { /* * Exit the loop if there is no disk address assigned yet and * the indirect block isn't in the cache, or if we were * looking for an indirect block and we've found it. */ metalbn = ap->in_lbn; if ((daddr == 0 && !incore(&vp->v_bufobj, metalbn)) || metalbn == bn) break; /* * If we get here, we've either got the block in the cache * or we have a disk address for it, go fetch it. */ if (bp) bqrelse(bp); bp = getblk(vp, metalbn, bsize, 0, 0, 0); if ((bp->b_flags & B_CACHE) == 0) { #ifdef INVARIANTS if (!daddr) panic("ext2_bmaparray: indirect block not in cache"); #endif bp->b_blkno = blkptrtodb(ump, daddr); bp->b_iocmd = BIO_READ; bp->b_flags &= ~B_INVAL; bp->b_ioflags &= ~BIO_ERROR; vfs_busy_pages(bp, 0); bp->b_iooffset = dbtob(bp->b_blkno); bstrategy(bp); curthread->td_ru.ru_inblock++; error = bufwait(bp); if (error) { brelse(bp); return (error); } } daddr = ((e2fs_daddr_t *)bp->b_data)[ap->in_off]; if (num == 1 && daddr && runp) { for (bn = ap->in_off + 1; bn < MNINDIR(ump) && *runp < maxrun && is_sequential(ump, ((e2fs_daddr_t *)bp->b_data)[bn - 1], ((e2fs_daddr_t *)bp->b_data)[bn]); ++bn, ++*runp); bn = ap->in_off; if (runb && bn) { for (--bn; bn >= 0 && *runb < maxrun && is_sequential(ump, ((e2fs_daddr_t *)bp->b_data)[bn], ((e2fs_daddr_t *)bp->b_data)[bn + 1]); --bn, ++*runb); } } } if (bp) bqrelse(bp); /* * Since this is FFS independent code, we are out of scope for the * definitions of BLK_NOCOPY and BLK_SNAP, but we do know that they * will fall in the range 1..um_seqinc, so we use that test and * return a request for a zeroed out buffer if attempts are made * to read a BLK_NOCOPY or BLK_SNAP block. */ if ((ip->i_flags & SF_SNAPSHOT) && daddr > 0 && daddr < ump->um_seqinc){ *bnp = -1; return (0); } *bnp = blkptrtodb(ump, daddr); if (*bnp == 0) { *bnp = -1; } return (0); }
long int read_allocated_block(struct ext2fs_node *node, int fileblock) { long int blknr; int blksz; int log2_blksz; long int rblock; long int perblock_parent; long int perblock_child; unsigned long long start; struct ext2_inode *inode = &node->inode; struct ext2_data *data = node->data; int ret; /* get the blocksize of the filesystem */ blksz = EXT2_BLOCK_SIZE(node->data); log2_blksz = LOG2_EXT2_BLOCK_SIZE(node->data); if (le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) { char *buf = zalloc(blksz); struct ext4_extent_header *ext_block; struct ext4_extent *extent; int i = -1; if (!buf) return -ENOMEM; ext_block = ext4fs_get_extent_block(node->data, buf, (struct ext4_extent_header *)inode->b.blocks.dir_blocks, fileblock, log2_blksz); if (!ext_block) { pr_err("invalid extent block\n"); free(buf); return -EINVAL; } extent = (struct ext4_extent *)(ext_block + 1); do { i++; if (i >= le32_to_cpu(ext_block->eh_entries)) break; } while (fileblock >= le32_to_cpu(extent[i].ee_block)); if (--i >= 0) { fileblock -= le32_to_cpu(extent[i].ee_block); if (fileblock >= le32_to_cpu(extent[i].ee_len)) { free(buf); return 0; } start = le32_to_cpu(extent[i].ee_start_hi); start = (start << 32) + le32_to_cpu(extent[i].ee_start_lo); free(buf); return fileblock + start; } free(buf); return -EIO; } if (fileblock < INDIRECT_BLOCKS) { /* Direct blocks. */ blknr = __le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]); } else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) { /* Indirect. */ ret = ext4fs_get_indir_block(node, &data->indir1, __le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz); if (ret) return ret; blknr = __le32_to_cpu(data->indir1.data[fileblock - INDIRECT_BLOCKS]); } else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 * (blksz / 4 + 1)))) { /* Double indirect. */ long int perblock = blksz / 4; long int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4); ret = ext4fs_get_indir_block(node, &data->indir1, __le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz); if (ret) return ret; ret = ext4fs_get_indir_block(node, &data->indir2, __le32_to_cpu(data->indir1.data[rblock / perblock]) << log2_blksz); if (ret) return ret; blknr = __le32_to_cpu(data->indir2.data[rblock % perblock]); } else { /* Triple indirect. */ rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4 + (blksz / 4 * blksz / 4)); perblock_child = blksz / 4; perblock_parent = ((blksz / 4) * (blksz / 4)); ret = ext4fs_get_indir_block(node, &data->indir1, __le32_to_cpu(inode->b.blocks.triple_indir_block) << log2_blksz); if (ret) return ret; ret = ext4fs_get_indir_block(node, &data->indir2, __le32_to_cpu(data->indir1.data[rblock / perblock_parent]) << log2_blksz); if (ret) return ret; ret = ext4fs_get_indir_block(node, &data->indir3, __le32_to_cpu(data->indir2.data[rblock / perblock_child]) << log2_blksz); if (ret) return ret; blknr = __le32_to_cpu(data->indir3.data[rblock % perblock_child]); } return blknr; }
void list_super2(struct ext2_super_block * sb, FILE *f) { int inode_blocks_per_group; char buf[80], *str; time_t tm; inode_blocks_per_group = (((sb->s_inodes_per_group * EXT2_INODE_SIZE(sb)) + EXT2_BLOCK_SIZE(sb) - 1) / EXT2_BLOCK_SIZE(sb)); if (sb->s_volume_name[0]) { memset(buf, 0, sizeof(buf)); strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name)); } else strcpy(buf, "<none>"); fprintf(f, "Filesystem volume name: %s\n", buf); if (sb->s_last_mounted[0]) { memset(buf, 0, sizeof(buf)); strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted)); } else strcpy(buf, "<not available>"); fprintf(f, "Last mounted on: %s\n", buf); fprintf(f, "Filesystem UUID: %s\n", e2p_uuid2str(sb->s_uuid)); fprintf(f, "Filesystem magic number: 0x%04X\n", sb->s_magic); fprintf(f, "Filesystem revision #: %d", sb->s_rev_level); if (sb->s_rev_level == EXT2_GOOD_OLD_REV) { fprintf(f, " (original)\n"); #ifdef EXT2_DYNAMIC_REV } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) { fprintf(f, " (dynamic)\n"); #endif } else fprintf(f, " (unknown)\n"); print_features(sb, f); print_super_flags(sb, f); print_mntopts(sb, f); fprintf(f, "Filesystem state: "); print_fs_state (f, sb->s_state); fprintf(f, "\n"); fprintf(f, "Errors behavior: "); print_fs_errors(f, sb->s_errors); fprintf(f, "\n"); str = e2p_os2string(sb->s_creator_os); fprintf(f, "Filesystem OS type: %s\n", str); free(str); fprintf(f, "Inode count: %u\n", sb->s_inodes_count); fprintf(f, "Block count: %u\n", sb->s_blocks_count); fprintf(f, "Reserved block count: %u\n", sb->s_r_blocks_count); fprintf(f, "Free blocks: %u\n", sb->s_free_blocks_count); fprintf(f, "Free inodes: %u\n", sb->s_free_inodes_count); fprintf(f, "First block: %u\n", sb->s_first_data_block); fprintf(f, "Block size: %u\n", EXT2_BLOCK_SIZE(sb)); fprintf(f, "Fragment size: %u\n", EXT2_FRAG_SIZE(sb)); if (sb->s_reserved_gdt_blocks) fprintf(f, "Reserved GDT blocks: %u\n", sb->s_reserved_gdt_blocks); fprintf(f, "Blocks per group: %u\n", sb->s_blocks_per_group); fprintf(f, "Fragments per group: %u\n", sb->s_frags_per_group); fprintf(f, "Inodes per group: %u\n", sb->s_inodes_per_group); fprintf(f, "Inode blocks per group: %u\n", inode_blocks_per_group); if (sb->s_raid_stride) fprintf(f, "RAID stride: %u\n", sb->s_raid_stride); if (sb->s_raid_stripe_width) fprintf(f, "RAID stripe width: %u\n", sb->s_raid_stripe_width); if (sb->s_first_meta_bg) fprintf(f, "First meta block group: %u\n", sb->s_first_meta_bg); if (sb->s_log_groups_per_flex) fprintf(f, "Flex block group size: %u\n", 1 << sb->s_log_groups_per_flex); if (sb->s_mkfs_time) { tm = sb->s_mkfs_time; fprintf(f, "Filesystem created: %s", ctime(&tm)); } tm = sb->s_mtime; fprintf(f, "Last mount time: %s", sb->s_mtime ? ctime(&tm) : "n/a\n"); tm = sb->s_wtime; fprintf(f, "Last write time: %s", ctime(&tm)); fprintf(f, "Mount count: %u\n", sb->s_mnt_count); fprintf(f, "Maximum mount count: %d\n", sb->s_max_mnt_count); tm = sb->s_lastcheck; fprintf(f, "Last checked: %s", ctime(&tm)); fprintf(f, "Check interval: %u (%s)\n", sb->s_checkinterval, interval_string(sb->s_checkinterval)); if (sb->s_checkinterval) { time_t next; next = sb->s_lastcheck + sb->s_checkinterval; fprintf(f, "Next check after: %s", ctime(&next)); } fprintf(f, "Reserved blocks uid: "); print_user(sb->s_def_resuid, f); fprintf(f, "Reserved blocks gid: "); print_group(sb->s_def_resgid, f); if (sb->s_rev_level >= EXT2_DYNAMIC_REV) { fprintf(f, "First inode: %d\n", sb->s_first_ino); fprintf(f, "Inode size: %d\n", sb->s_inode_size); if (sb->s_min_extra_isize) fprintf(f, "Required extra isize: %d\n", sb->s_min_extra_isize); if (sb->s_want_extra_isize) fprintf(f, "Desired extra isize: %d\n", sb->s_want_extra_isize); } if (!e2p_is_null_uuid(sb->s_journal_uuid)) fprintf(f, "Journal UUID: %s\n", e2p_uuid2str(sb->s_journal_uuid)); if (sb->s_journal_inum) fprintf(f, "Journal inode: %u\n", sb->s_journal_inum); if (sb->s_journal_dev) fprintf(f, "Journal device: 0x%04x\n", sb->s_journal_dev); if (sb->s_last_orphan) fprintf(f, "First orphan inode: %u\n", sb->s_last_orphan); if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) || sb->s_def_hash_version) fprintf(f, "Default directory hash: %s\n", e2p_hash2string(sb->s_def_hash_version)); if (!e2p_is_null_uuid(sb->s_hash_seed)) fprintf(f, "Directory Hash Seed: %s\n", e2p_uuid2str(sb->s_hash_seed)); if (sb->s_jnl_backup_type) { fprintf(f, "Journal backup: "); switch (sb->s_jnl_backup_type) { case 1: fprintf(f, "inode blocks\n"); break; default: fprintf(f, "type %u\n", sb->s_jnl_backup_type); } } }
static void dump_journal(char *cmdname, FILE *out_file, struct journal_source *source) { struct ext2_super_block *sb; char jsb_buffer[1024]; char buf[8192]; journal_superblock_t *jsb; unsigned int blocksize = 1024; unsigned int got; int retval; __u32 magic, sequence, blocktype; journal_header_t *header; tid_t transaction; unsigned int blocknr = 0; /* First, check to see if there's an ext2 superblock header */ retval = read_journal_block(cmdname, source, 0, buf, 2048, &got); if (retval) return; jsb = (journal_superblock_t *) buf; sb = (struct ext2_super_block *) (buf+1024); #ifdef ENABLE_SWAPFS if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(sb); #endif if ((be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) && (sb->s_magic == EXT2_SUPER_MAGIC) && (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { blocksize = EXT2_BLOCK_SIZE(sb); blocknr = (blocksize == 1024) ? 2 : 1; uuid_unparse(sb->s_uuid, jsb_buffer); fprintf(out_file, "Ext2 superblock header found.\n"); if (dump_all) { fprintf(out_file, "\tuuid=%s\n", jsb_buffer); fprintf(out_file, "\tblocksize=%d\n", blocksize); fprintf(out_file, "\tjournal data size %ld\n", (long) sb->s_blocks_count); } } /* Next, read the journal superblock */ retval = read_journal_block(cmdname, source, blocknr*blocksize, jsb_buffer, 1024, &got); if (retval) return; jsb = (journal_superblock_t *) jsb_buffer; if (be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) { fprintf(out_file, "Journal superblock magic number invalid!\n"); return; } blocksize = be32_to_cpu(jsb->s_blocksize); transaction = be32_to_cpu(jsb->s_sequence); blocknr = be32_to_cpu(jsb->s_start); fprintf(out_file, "Journal starts at block %u, transaction %u\n", blocknr, transaction); if (!blocknr) /* Empty journal, nothing to do. */ return; while (1) { retval = read_journal_block(cmdname, source, blocknr*blocksize, buf, blocksize, &got); if (retval || got != blocksize) return; header = (journal_header_t *) buf; magic = be32_to_cpu(header->h_magic); sequence = be32_to_cpu(header->h_sequence); blocktype = be32_to_cpu(header->h_blocktype); if (magic != JFS_MAGIC_NUMBER) { fprintf (out_file, "No magic number at block %u: " "end of journal.\n", blocknr); return; } if (sequence != transaction) { fprintf (out_file, "Found sequence %u (not %u) at " "block %u: end of journal.\n", sequence, transaction, blocknr); return; } if (dump_descriptors) { fprintf (out_file, "Found expected sequence %u, " "type %u (%s) at block %u\n", sequence, blocktype, type_to_name(blocktype), blocknr); } switch (blocktype) { case JFS_DESCRIPTOR_BLOCK: dump_descriptor_block(out_file, source, buf, jsb, &blocknr, blocksize, transaction); continue; case JFS_COMMIT_BLOCK: transaction++; blocknr++; WRAP(jsb, blocknr); continue; case JFS_REVOKE_BLOCK: dump_revoke_block(out_file, buf, jsb, blocknr, blocksize, transaction); blocknr++; WRAP(jsb, blocknr); continue; default: fprintf (out_file, "Unexpected block type %u at " "block %u.\n", blocktype, blocknr); return; } } }
int main (int argc, char *argv[]) { errcode_t retval = 0, retval2 = 0, orig_retval = 0; int exit_value = FSCK_OK; ext2_filsys fs = 0; io_manager io_ptr; struct ext2_super_block *sb; const char *lib_ver_date; int my_ver, lib_ver; e2fsck_t ctx; blk_t orig_superblock; struct problem_context pctx; int flags, run_result; int journal_size; int sysval, sys_page_size = 4096; __u32 features[3]; char *cp; klog_init(); E2F_DBG_INFO("e2fsck start..."); #ifdef E2FSCK_PIPE_DEBUG_ memset(pipe_msg_temp, 0, MSG_LEN); memcpy(pipe_msg_temp, argv[2], strlen(argv[2])); pipe_open(); pipe_write(C_IN_START); #endif clear_problem_context(&pctx); #ifdef MTRACE mtrace(); #endif #ifdef MCHECK mcheck(0); #endif #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); setlocale(LC_CTYPE, ""); bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); #endif my_ver = ext2fs_parse_version_string(my_ver_string); lib_ver = ext2fs_get_library_version(0, &lib_ver_date); if (my_ver > lib_ver) { fprintf( stderr, _("Error: ext2fs library version " "out of date!\n")); show_version_only++; } retval = PRS(argc, argv, &ctx); if (retval) { com_err("e2fsck", retval, _("while trying to initialize program")); exit(FSCK_ERROR); } reserve_stdio_fds(); init_resource_track(&ctx->global_rtrack, NULL); if (!(ctx->options & E2F_OPT_PREEN) || show_version_only) fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string, my_ver_date); if (show_version_only) { fprintf(stderr, _("\tUsing %s, %s\n"), error_message(EXT2_ET_BASE), lib_ver_date); exit(FSCK_OK); } check_mount(ctx); if (!(ctx->options & E2F_OPT_PREEN) && !(ctx->options & E2F_OPT_NO) && !(ctx->options & E2F_OPT_YES)) { if (!ctx->interactive) fatal_error(ctx, _("need terminal for interactive repairs")); } ctx->superblock = ctx->use_superblock; restart: #ifdef CONFIG_TESTIO_DEBUG if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { io_ptr = test_io_manager; test_io_backing_manager = unix_io_manager; } else #endif io_ptr = unix_io_manager; flags = EXT2_FLAG_NOFREE_ON_ERROR; if ((ctx->options & E2F_OPT_READONLY) == 0) flags |= EXT2_FLAG_RW; if ((ctx->mount_flags & EXT2_MF_MOUNTED) == 0) flags |= EXT2_FLAG_EXCLUSIVE; retval = try_open_fs(ctx, flags, io_ptr, &fs); if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && !(ctx->flags & E2F_FLAG_SB_SPECIFIED) && ((retval == EXT2_ET_BAD_MAGIC) || (retval == EXT2_ET_CORRUPT_SUPERBLOCK) || ((retval == 0) && (retval2 = ext2fs_check_desc(fs))))) { if (retval2 == ENOMEM) { retval = retval2; goto failure; } if (fs->flags & EXT2_FLAG_NOFREE_ON_ERROR) { ext2fs_free(fs); fs = NULL; } if (!fs || (fs->group_desc_count > 1)) { printf(_("%s: %s trying backup blocks...\n"), ctx->program_name, retval ? _("Superblock invalid,") : _("Group descriptors look bad...")); orig_superblock = ctx->superblock; get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr); if (fs) ext2fs_close(fs); orig_retval = retval; retval = try_open_fs(ctx, flags, io_ptr, &fs); if ((orig_retval == 0) && retval != 0) { if (fs) ext2fs_close(fs); com_err(ctx->program_name, retval, "when using the backup blocks"); printf(_("%s: going back to original " "superblock\n"), ctx->program_name); ctx->superblock = orig_superblock; retval = try_open_fs(ctx, flags, io_ptr, &fs); } } } if (((retval == EXT2_ET_UNSUPP_FEATURE) || (retval == EXT2_ET_RO_UNSUPP_FEATURE)) && fs && fs->super) { sb = fs->super; features[0] = (sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP); features[1] = (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP); features[2] = (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP); if (features[0] || features[1] || features[2]) goto print_unsupp_features; } failure: if (retval) { if (orig_retval) retval = orig_retval; com_err(ctx->program_name, retval, _("while trying to open %s"), ctx->filesystem_name); if (retval == EXT2_ET_REV_TOO_HIGH) { printf(_("The filesystem revision is apparently " "too high for this version of e2fsck.\n" "(Or the filesystem superblock " "is corrupt)\n\n")); fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); } else if (retval == EXT2_ET_SHORT_READ) printf(_("Could this be a zero-length partition?\n")); else if ((retval == EPERM) || (retval == EACCES)) printf(_("You must have %s access to the " "filesystem or be root\n"), (ctx->options & E2F_OPT_READONLY) ? "r/o" : "r/w"); else if (retval == ENXIO) printf(_("Possibly non-existent or swap device?\n")); else if (retval == EBUSY) printf(_("Filesystem mounted or opened exclusively " "by another program?\n")); else if (retval == ENOENT) printf(_("Possibly non-existent device?\n")); #ifdef EROFS else if (retval == EROFS) printf(_("Disk write-protected; use the -n option " "to do a read-only\n" "check of the device.\n")); #endif else fix_problem(ctx, PR_0_SB_CORRUPT, &pctx); fatal_error(ctx, 0); } /* * We only update the master superblock because (a) paranoia; * we don't want to corrupt the backup superblocks, and (b) we * don't need to update the mount count and last checked * fields in the backup superblock (the kernel doesn't update * the backup superblocks anyway). With newer versions of the * library this flag is set by ext2fs_open2(), but we set this * here just to be sure. (No, we don't support e2fsck running * with some other libext2fs than the one that it was shipped * with, but just in case....) */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; if (!(ctx->flags & E2F_FLAG_GOT_DEVSIZE)) { __u32 blocksize = EXT2_BLOCK_SIZE(fs->super); int need_restart = 0; pctx.errcode = ext2fs_get_device_size2(ctx->filesystem_name, blocksize, &ctx->num_blocks); /* * The floppy driver refuses to allow anyone else to * open the device if has been opened with O_EXCL; * this is unlike other block device drivers in Linux. * To handle this, we close the filesystem and then * reopen the filesystem after we get the device size. */ if (pctx.errcode == EBUSY) { ext2fs_close(fs); need_restart++; pctx.errcode = ext2fs_get_device_size2(ctx->filesystem_name, blocksize, &ctx->num_blocks); } if (pctx.errcode == EXT2_ET_UNIMPLEMENTED) ctx->num_blocks = 0; else if (pctx.errcode) { fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx); ctx->flags |= E2F_FLAG_ABORT; fatal_error(ctx, 0); } ctx->flags |= E2F_FLAG_GOT_DEVSIZE; if (need_restart) goto restart; } ctx->fs = fs; fs->priv_data = ctx; fs->now = ctx->now; sb = fs->super; if (sb->s_rev_level > E2FSCK_CURRENT_REV) { com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH, _("while trying to open %s"), ctx->filesystem_name); get_newer: fatal_error(ctx, _("Get a newer version of e2fsck!")); } /* * Set the device name, which is used whenever we print error * or informational messages to the user. */ if (ctx->device_name == 0 && (sb->s_volume_name[0] != 0)) { ctx->device_name = string_copy(ctx, sb->s_volume_name, sizeof(sb->s_volume_name)); } if (ctx->device_name == 0) ctx->device_name = string_copy(ctx, ctx->filesystem_name, 0); for (cp = ctx->device_name; *cp; cp++) if (isspace(*cp) || *cp == ':') *cp = '_'; ehandler_init(fs->io); if ((ctx->mount_flags & EXT2_MF_MOUNTED) && !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) goto skip_journal; /* * Make sure the ext3 superblock fields are consistent. */ retval = e2fsck_check_ext3_journal(ctx); if (retval) { com_err(ctx->program_name, retval, _("while checking ext3 journal for %s"), ctx->device_name); fatal_error(ctx, 0); } /* * Check to see if we need to do ext3-style recovery. If so, * do it, and then restart the fsck. */ if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) { if (ctx->options & E2F_OPT_READONLY) { printf(_("Warning: skipping journal recovery " "because doing a read-only filesystem " "check.\n")); io_channel_flush(ctx->fs->io); } else { if (ctx->flags & E2F_FLAG_RESTARTED) { /* * Whoops, we attempted to run the * journal twice. This should never * happen, unless the hardware or * device driver is being bogus. */ com_err(ctx->program_name, 0, _("unable to set superblock flags on %s\n"), ctx->device_name); fatal_error(ctx, 0); } retval = e2fsck_run_ext3_journal(ctx); if (retval) { com_err(ctx->program_name, retval, _("while recovering ext3 journal of %s"), ctx->device_name); fatal_error(ctx, 0); } ext2fs_close(ctx->fs); ctx->fs = 0; ctx->flags |= E2F_FLAG_RESTARTED; goto restart; } } skip_journal: /* * Check for compatibility with the feature sets. We need to * be more stringent than ext2fs_open(). */ features[0] = sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP; features[1] = sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP; features[2] = (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP); print_unsupp_features: if (features[0] || features[1] || features[2]) { int i, j; __u32 *mask = features, m; fprintf(stderr, _("%s has unsupported feature(s):"), ctx->filesystem_name); for (i=0; i <3; i++,mask++) { for (j=0,m=1; j < 32; j++, m<<=1) { if (*mask & m) fprintf(stderr, " %s", e2p_feature2string(i, m)); } } putc('\n', stderr); goto get_newer; } #ifdef ENABLE_COMPRESSION if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) com_err(ctx->program_name, 0, _("Warning: compression support is experimental.\n")); #endif #ifndef ENABLE_HTREE if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) { com_err(ctx->program_name, 0, _("E2fsck not compiled with HTREE support,\n\t" "but filesystem %s has HTREE directories.\n"), ctx->device_name); goto get_newer; } #endif /* * If the user specified a specific superblock, presumably the * master superblock has been trashed. So we mark the * superblock as dirty, so it can be written out. */ if (ctx->superblock && !(ctx->options & E2F_OPT_READONLY)) ext2fs_mark_super_dirty(fs); /* * Calculate the number of filesystem blocks per pagesize. If * fs->blocksize > page_size, set the number of blocks per * pagesize to 1 to avoid division by zero errors. */ #ifdef _SC_PAGESIZE sysval = sysconf(_SC_PAGESIZE); if (sysval > 0) sys_page_size = sysval; #endif /* _SC_PAGESIZE */ ctx->blocks_per_page = sys_page_size / fs->blocksize; if (ctx->blocks_per_page == 0) ctx->blocks_per_page = 1; if (ctx->superblock) set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0); ext2fs_mark_valid(fs); check_super_block(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) fatal_error(ctx, 0); check_if_skip(ctx); check_resize_inode(ctx); if (bad_blocks_file) read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks); else if (cflag) read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */ if (ctx->flags & E2F_FLAG_SIGNAL_MASK) fatal_error(ctx, 0); /* * Mark the system as valid, 'til proven otherwise */ ext2fs_mark_valid(fs); retval = ext2fs_read_bb_inode(fs, &fs->badblocks); if (retval) { com_err(ctx->program_name, retval, _("while reading bad blocks inode")); preenhalt(ctx); printf(_("This doesn't bode well," " but we'll try to go on...\n")); } /* * Save the journal size in megabytes. * Try and use the journal size from the backup else let e2fsck * find the default journal size. */ if (sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) journal_size = sb->s_jnl_blocks[16] >> 20; else
/* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. * * Valid flags for ext2fs_open() * * EXT2_FLAG_RW - Open the filesystem for read/write. * EXT2_FLAG_FORCE - Open the filesystem even if some of the * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device * EXT2_FLAG_SKIP_MMP - Open without multi-mount protection check. * EXT2_FLAG_64BITS - Allow 64-bit bitfields (needed for large * filesystems) */ errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; unsigned long i, first_meta_bg; __u32 features; unsigned int blocks_per_group, io_flags; blk64_t group_block, blk; char *dest, *cp; int group_zero_adjust = 0; #ifdef WORDS_BIGENDIAN unsigned int groups_per_block; struct ext2_group_desc *gdp; int j; #endif char *time_env; EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; /* don't overwrite sb backups unless flag is explicitly cleared */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; fs->umask = 022; time_env = getenv("E2FSPROGS_FAKE_TIME"); if (time_env) fs->now = strtoul(time_env, NULL, 0); retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); cp = strchr(fs->device_name, '?'); if (!io_options && cp) { *cp++ = 0; io_options = cp; } io_flags = 0; if (flags & EXT2_FLAG_RW) io_flags |= IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(fs->device_name, io_flags, &fs->io); if (retval) goto cleanup; if (io_options && (retval = io_channel_set_options(fs->io, io_options))) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = io_channel_alloc_buf(fs->io, -SUPERBLOCK_SIZE, &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -(int)sizeof(struct ext2_image_hdr), fs->image_header); if (retval) goto cleanup; if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) return EXT2_ET_MAGIC_E2IMAGE; superblock = 1; block_size = fs->image_header->fs_blocksize; } /* * If the user specifies a specific block # for the * superblock, then he/she must also specify the block size! * Otherwise, read the master superblock located at offset * SUPERBLOCK_OFFSET from the start of the partition. * * Note: we only save a backup copy of the superblock if we * are reading the superblock from the primary superblock location. */ if (superblock) { if (!block_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } io_channel_set_blksize(fs->io, block_size); group_block = superblock; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto cleanup; } retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, fs->super); if (retval) goto cleanup; if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) { retval = 0; if (!ext2fs_verify_csum_type(fs, fs->super)) retval = EXT2_ET_UNKNOWN_CSUM; if (!ext2fs_superblock_csum_verify(fs, fs->super)) retval = EXT2_ET_SB_CSUM_INVALID; } #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); #else if (fs->flags & EXT2_FLAG_SWAP_BYTES) { retval = EXT2_ET_UNIMPLEMENTED; goto cleanup; } #endif if (fs->super->s_magic != EXT2_SUPER_MAGIC) retval = EXT2_ET_BAD_MAGIC; if (retval) goto cleanup; if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { retval = EXT2_ET_REV_TOO_HIGH; goto cleanup; } /* * Check for feature set incompatibility */ if (!(flags & EXT2_FLAG_FORCE)) { features = fs->super->s_feature_incompat; #ifdef EXT2_LIB_SOFTSUPP_INCOMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= ~EXT2_LIB_SOFTSUPP_INCOMPAT; #endif if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } features = fs->super->s_feature_ro_compat; #ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= ~EXT2_LIB_SOFTSUPP_RO_COMPAT; #endif if ((flags & EXT2_FLAG_RW) && (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && ext2fs_has_feature_journal_dev(fs->super)) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } } if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > EXT2_MAX_BLOCK_LOG_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* * bigalloc requires cluster-aware bitfield operations, which at the * moment means we need EXT2_FLAG_64BITS. */ if (ext2fs_has_feature_bigalloc(fs->super) && !(flags & EXT2_FLAG_64BITS)) { retval = EXT2_ET_CANT_USE_LEGACY_BITMAPS; goto cleanup; } if (!ext2fs_has_feature_bigalloc(fs->super) && (fs->super->s_log_block_size != fs->super->s_log_cluster_size)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* Enforce the block group descriptor size */ if (ext2fs_has_feature_64bit(fs->super)) { if (fs->super->s_desc_size < EXT2_MIN_DESC_SIZE_64BIT) { retval = EXT2_ET_BAD_DESC_SIZE; goto cleanup; } } else { if (fs->super->s_desc_size && fs->super->s_desc_size != EXT2_MIN_DESC_SIZE) { retval = EXT2_ET_BAD_DESC_SIZE; goto cleanup; } } fs->cluster_ratio_bits = fs->super->s_log_cluster_size - fs->super->s_log_block_size; if (EXT2_BLOCKS_PER_GROUP(fs->super) != EXT2_CLUSTERS_PER_GROUP(fs->super) << fs->cluster_ratio_bits) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * EXT2_INODE_SIZE(fs->super) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super)); if (block_size) { if (block_size != fs->blocksize) { retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; goto cleanup; } } /* * Set the blocksize to the filesystem's blocksize. */ io_channel_set_blksize(fs->io, fs->blocksize); /* * If this is an external journal device, don't try to read * the group descriptors, because they're not there. */ if (ext2fs_has_feature_journal_dev(fs->super)) { fs->group_desc_count = 0; *ret_fs = fs; return 0; } if (EXT2_INODES_PER_GROUP(fs->super) == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* Precompute the FS UUID to seed other checksums */ ext2fs_init_csum_seed(fs); /* * Read group descriptors */ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); if (blocks_per_group == 0 || blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || EXT2_DESC_PER_BLOCK(fs->super) == 0 || fs->super->s_first_data_block >= ext2fs_blocks_count(fs->super)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) - fs->super->s_first_data_block, blocks_per_group); if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != fs->super->s_inodes_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(fs->super)); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block; /* * On a FS with a 1K blocksize, block 0 is reserved for bootloaders * so we must increment block numbers to any group 0 items. * * However, we cannot touch group_block directly because in the meta_bg * case, the ext2fs_descriptor_block_loc2() function will interpret * group_block != s_first_data_block to mean that we want to access the * backup group descriptors. This is not what we want if the caller * set superblock == 0 (i.e. auto-detect the superblock), which is * what's going on here. */ if (group_block == 0 && fs->blocksize == 1024) group_zero_adjust = 1; dest = (char *) fs->group_desc; #ifdef WORDS_BIGENDIAN groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); #endif if (ext2fs_has_feature_meta_bg(fs->super)) { first_meta_bg = fs->super->s_first_meta_bg; if (first_meta_bg > fs->desc_blocks) first_meta_bg = fs->desc_blocks; } else first_meta_bg = fs->desc_blocks; if (first_meta_bg) { retval = io_channel_read_blk(fs->io, group_block + group_zero_adjust + 1, first_meta_bg, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block*first_meta_bg; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, j); ext2fs_swap_group_desc2(fs, gdp); } #endif dest += fs->blocksize*first_meta_bg; } for (i=first_meta_bg ; i < fs->desc_blocks; i++) { blk = ext2fs_descriptor_block_loc2(fs, group_block, i); retval = io_channel_read_blk64(fs->io, blk, 1, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN for (j=0; j < groups_per_block; j++) { gdp = ext2fs_group_desc(fs, fs->group_desc, i * groups_per_block + j); ext2fs_swap_group_desc2(fs, gdp); } #endif dest += fs->blocksize; } fs->stride = fs->super->s_raid_stride; /* * If recovery is from backup superblock, Clear _UNININT flags & * reset bg_itable_unused to zero */ if (superblock > 1 && ext2fs_has_group_desc_csum(fs)) { dgrp_t group; for (group = 0; group < fs->group_desc_count; group++) { ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT); ext2fs_bg_itable_unused_set(fs, group, 0); /* The checksum will be reset later, but fix it here * anyway to avoid printing a lot of spurious errors. */ ext2fs_group_desc_csum_set(fs, group); } if (fs->flags & EXT2_FLAG_RW) ext2fs_mark_super_dirty(fs); } if (ext2fs_has_feature_mmp(fs->super) && !(flags & EXT2_FLAG_SKIP_MMP) && (flags & (EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE))) { retval = ext2fs_mmp_start(fs); if (retval) { fs->flags |= EXT2_FLAG_SKIP_MMP; /* just do cleanup */ ext2fs_mmp_stop(fs); goto cleanup; } } if (fs->flags & EXT2_FLAG_SHARE_DUP) { fs->block_sha_map = ext2fs_hashmap_create(ext2fs_djb2_hash, block_sha_map_free_entry, 4096); if (!fs->block_sha_map) { retval = EXT2_ET_NO_MEMORY; goto cleanup; } ext2fs_set_feature_shared_blocks(fs->super); } fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; *ret_fs = fs; return 0; cleanup: if (!(flags & EXT2_FLAG_NOFREE_ON_ERROR)) { ext2fs_free(fs); fs = NULL; } *ret_fs = fs; return retval; }
static void parse_extended_opts(struct ext2_super_block *param, const char *opts) { char *buf, *token, *next, *p, *arg; int len; int r_usage = 0; len = strlen(opts); buf = malloc(len+1); if (!buf) { fprintf(stderr, _("Couldn't allocate memory to parse options!\n")); exit(1); } strcpy(buf, opts); for (token = buf; token && *token; token = next) { p = strchr(token, ','); next = 0; if (p) { *p = 0; next = p+1; } arg = strchr(token, '='); if (arg) { *arg = 0; arg++; } if (strcmp(token, "stride") == 0) { if (!arg) { r_usage++; continue; } fs_stride = strtoul(arg, &p, 0); if (*p || (fs_stride == 0)) { fprintf(stderr, _("Invalid stride parameter: %s\n"), arg); r_usage++; continue; } } else if (!strcmp(token, "resize")) { unsigned long resize, bpg, rsv_groups; unsigned long group_desc_count, desc_blocks; unsigned int gdpb, blocksize; int rsv_gdb; if (!arg) { r_usage++; continue; } resize = parse_num_blocks(arg, param->s_log_block_size); if (resize == 0) { fprintf(stderr, _("Invalid resize parameter: %s\n"), arg); r_usage++; continue; } if (resize <= param->s_blocks_count) { fprintf(stderr, _("The resize maximum must be greater " "than the filesystem size.\n")); r_usage++; continue; } blocksize = EXT2_BLOCK_SIZE(param); bpg = param->s_blocks_per_group; if (!bpg) bpg = blocksize * 8; gdpb = blocksize / sizeof(struct ext2_group_desc); group_desc_count = (param->s_blocks_count + bpg - 1) / bpg; desc_blocks = (group_desc_count + gdpb - 1) / gdpb; rsv_groups = (resize + bpg - 1) / bpg; rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - desc_blocks; if (rsv_gdb > (int) EXT2_ADDR_PER_BLOCK(param)) rsv_gdb = EXT2_ADDR_PER_BLOCK(param); if (rsv_gdb > 0) { param->s_feature_compat |= EXT2_FEATURE_COMPAT_RESIZE_INODE; param->s_reserved_gdt_blocks = rsv_gdb; } } else r_usage++; } if (r_usage) { fprintf(stderr, _("\nBad options specified.\n\n" "Extended options are separated by commas, " "and may take an argument which\n" "\tis set off by an equals ('=') sign.\n\n" "Valid extended options are:\n" "\tstride=<stride length in blocks>\n" "\tresize=<resize maximum size in blocks>\n\n")); exit(1); } }
blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name, io_manager manager) { struct ext2_super_block *sb; io_channel io = NULL; void *buf = NULL; int blocksize; blk64_t superblock, ret_sb = 8193; if (fs && fs->super) { ret_sb = (fs->super->s_blocks_per_group + fs->super->s_first_data_block); if (ctx) { ctx->superblock = ret_sb; ctx->blocksize = fs->blocksize; } return ret_sb; } if (ctx) { if (ctx->blocksize) { ret_sb = ctx->blocksize * 8; if (ctx->blocksize == 1024) ret_sb++; ctx->superblock = ret_sb; return ret_sb; } ctx->superblock = ret_sb; ctx->blocksize = 1024; } if (!name || !manager) goto cleanup; if (manager->open(name, 0, &io) != 0) goto cleanup; if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf)) goto cleanup; sb = (struct ext2_super_block *) buf; for (blocksize = EXT2_MIN_BLOCK_SIZE; blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) { superblock = blocksize*8; if (blocksize == 1024) superblock++; io_channel_set_blksize(io, blocksize); if (io_channel_read_blk64(io, superblock, -SUPERBLOCK_SIZE, buf)) continue; #ifdef WORDS_BIGENDIAN if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(sb); #endif if ((sb->s_magic == EXT2_SUPER_MAGIC) && (EXT2_BLOCK_SIZE(sb) == blocksize)) { ret_sb = superblock; if (ctx) { ctx->superblock = superblock; ctx->blocksize = blocksize; } break; } } cleanup: if (io) io_channel_close(io); if (buf) ext2fs_free_mem(&buf); return (ret_sb); }
int ext2_mount(bdev_t *dev, fscookie *cookie) { int err; LTRACEF("dev %p\n", dev); ext2_t *ext2 = malloc(sizeof(ext2_t)); ext2->dev = dev; err = bio_read(dev, &ext2->sb, 1024, sizeof(struct ext2_super_block)); if (err < 0) goto err; endian_swap_superblock(&ext2->sb); /* see if the superblock is good */ if (ext2->sb.s_magic != EXT2_SUPER_MAGIC) { err = -1; return err; } /* calculate group count, rounded up */ ext2->s_group_count = (ext2->sb.s_blocks_count + ext2->sb.s_blocks_per_group - 1) / ext2->sb.s_blocks_per_group; /* print some info */ LTRACEF("rev level %d\n", ext2->sb.s_rev_level); LTRACEF("compat features 0x%x\n", ext2->sb.s_feature_compat); LTRACEF("incompat features 0x%x\n", ext2->sb.s_feature_incompat); LTRACEF("ro compat features 0x%x\n", ext2->sb.s_feature_ro_compat); LTRACEF("block size %d\n", EXT2_BLOCK_SIZE(ext2->sb)); LTRACEF("inode size %d\n", EXT2_INODE_SIZE(ext2->sb)); LTRACEF("block count %d\n", ext2->sb.s_blocks_count); LTRACEF("blocks per group %d\n", ext2->sb.s_blocks_per_group); LTRACEF("group count %d\n", ext2->s_group_count); LTRACEF("inodes per group %d\n", ext2->sb.s_inodes_per_group); /* we only support dynamic revs */ if (ext2->sb.s_rev_level > EXT2_DYNAMIC_REV) { err = -2; return err; } /* make sure it doesn't have any ro features we don't support */ if (ext2->sb.s_feature_ro_compat & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) { err = -3; return err; } /* read in all the group descriptors */ ext2->gd = malloc(sizeof(struct ext2_group_desc) * ext2->s_group_count); err = bio_read(ext2->dev, (void *)ext2->gd, (EXT2_BLOCK_SIZE(ext2->sb) == 4096) ? 4096 : 2048, sizeof(struct ext2_group_desc) * ext2->s_group_count); if (err < 0) { err = -4; return err; } int i; for (i=0; i < ext2->s_group_count; i++) { endian_swap_group_desc(&ext2->gd[i]); LTRACEF("group %d:\n", i); LTRACEF("\tblock bitmap %d\n", ext2->gd[i].bg_block_bitmap); LTRACEF("\tinode bitmap %d\n", ext2->gd[i].bg_inode_bitmap); LTRACEF("\tinode table %d\n", ext2->gd[i].bg_inode_table); LTRACEF("\tfree blocks %d\n", ext2->gd[i].bg_free_blocks_count); LTRACEF("\tfree inodes %d\n", ext2->gd[i].bg_free_inodes_count); LTRACEF("\tused dirs %d\n", ext2->gd[i].bg_used_dirs_count); } /* initialize the block cache */ ext2->cache = bcache_create(ext2->dev, EXT2_BLOCK_SIZE(ext2->sb), 4); /* load the first inode */ err = ext2_load_inode(ext2, EXT2_ROOT_INO, &ext2->root_inode); if (err < 0) goto err; // TRACE("successfully mounted volume\n"); *cookie = ext2; return 0; err: LTRACEF("exiting with err code %d\n", err); free(ext2); return err; }
/* * Functions to read and write a single inode. */ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { blk64_t block_nr; unsigned long group, block, offset; char *ptr; errcode_t retval; unsigned i; int clen, inodes_per_block; io_channel io; int length = EXT2_INODE_SIZE(fs->super); struct ext2_inode_large *iptr; int cache_slot, fail_csum; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user has an override function */ if (fs->read_inode && ((bufsize == sizeof(struct ext2_inode)) || (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_inode)))) { retval = (fs->read_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; /* Create inode cache if not present */ if (!fs->icache) { retval = ext2fs_create_inode_cache(fs, 4); if (retval) return retval; } /* Check to see if it's in the inode cache */ for (i = 0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { memcpy(inode, fs->icache->cache[i].inode, (bufsize > length) ? length : bufsize); return 0; } } if (fs->flags & EXT2_FLAG_IMAGE_FILE) { inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); block_nr = fs->image_header->offset_inode / fs->blocksize; block_nr += (ino - 1) / inodes_per_block; offset = ((ino - 1) % inodes_per_block) * EXT2_INODE_SIZE(fs->super); io = fs->image_io; } else { group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); if (group > fs->group_desc_count) return EXT2_ET_BAD_INODE_NUM; offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!ext2fs_inode_table_loc(fs, (unsigned) group)) return EXT2_ET_MISSING_INODE_TABLE; block_nr = ext2fs_inode_table_loc(fs, group) + block; io = fs->io; } offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size; iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode; ptr = (char *) iptr; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (block_nr != fs->icache->buffer_blk) { retval = io_channel_read_blk64(io, block_nr, 1, fs->icache->buffer); if (retval) return retval; fs->icache->buffer_blk = block_nr; } memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, clen); offset = 0; length -= clen; ptr += clen; block_nr++; } length = EXT2_INODE_SIZE(fs->super); /* Verify the inode checksum. */ fail_csum = !ext2fs_inode_csum_verify(fs, ino, iptr); #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr, (struct ext2_inode_large *) iptr, 0, length); #endif /* Update the inode cache bookkeeping */ if (!fail_csum) { fs->icache->cache_last = cache_slot; fs->icache->cache[cache_slot].ino = ino; } memcpy(inode, iptr, (bufsize > length) ? length : bufsize); if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) && fail_csum) return EXT2_ET_INODE_CSUM_INVALID; return 0; }