/* * This function forces out the primary superblock. We need to only * write out those fields which we have changed, since if the * filesystem is mounted, it may have changed some of the other * fields. * * It takes as input a superblock which has already been byte swapped * (if necessary). * */ static errcode_t write_primary_superblock(ext2_filsys fs, struct ext2_super_block *super) { __u16 *old_super, *new_super; int check_idx, write_idx, size; errcode_t retval; if (!fs->io->manager->write_byte || !fs->orig_super) { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, super); io_channel_set_blksize(fs->io, fs->blocksize); return retval; } old_super = (__u16 *) fs->orig_super; new_super = (__u16 *) super; for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { if (old_super[check_idx] == new_super[check_idx]) continue; write_idx = check_idx; for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) if (old_super[check_idx] == new_super[check_idx]) break; size = 2 * (check_idx - write_idx); retval = io_channel_write_byte(fs->io, SUPERBLOCK_OFFSET + (2 * write_idx), size, new_super + write_idx); if (retval) return retval; } memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); return 0; }
static errcode_t write_file_system_identity(io_channel undo_channel, TDB_CONTEXT *tdb) { errcode_t retval; struct ext2_super_block super; TDB_DATA tdb_key, tdb_data; struct undo_private_data *data; io_channel channel; int block_size ; data = (struct undo_private_data *) undo_channel->private_data; channel = data->real; block_size = channel->block_size; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; /* Write to tdb file in the file system byte order */ tdb_key.dptr = mtime_key; tdb_key.dsize = sizeof(mtime_key); tdb_data.dptr = (unsigned char *) &(super.s_mtime); tdb_data.dsize = sizeof(super.s_mtime); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); goto err_out; } tdb_key.dptr = uuid_key; tdb_key.dsize = sizeof(uuid_key); tdb_data.dptr = (unsigned char *)&(super.s_uuid); tdb_data.dsize = sizeof(super.s_uuid); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); } err_out: io_channel_set_blksize(channel, block_size); return retval; }
static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel) { __u32 s_mtime; __u8 s_uuid[16]; errcode_t retval; TDB_DATA tdb_key, tdb_data; struct ext2_super_block super; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { com_err(prg_name, retval, _("Failed to read the file system data \n")); return retval; } tdb_key.dptr = mtime_key; tdb_key.dsize = sizeof(mtime_key); tdb_data = tdb_fetch(tdb, tdb_key); if (!tdb_data.dptr) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); com_err(prg_name, retval, _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb)); return retval; } s_mtime = *(__u32 *)tdb_data.dptr; if (super.s_mtime != s_mtime) { com_err(prg_name, 0, _("The file system Mount time didn't match %u\n"), s_mtime); return -1; } tdb_key.dptr = uuid_key; tdb_key.dsize = sizeof(uuid_key); tdb_data = tdb_fetch(tdb, tdb_key); if (!tdb_data.dptr) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); com_err(prg_name, retval, _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb)); return retval; } memcpy(s_uuid, tdb_data.dptr, sizeof(s_uuid)); if (memcmp(s_uuid, super.s_uuid, sizeof(s_uuid))) { com_err(prg_name, 0, _("The file system UUID didn't match \n")); return -1; } return 0; }
/* * This function forces out the primary superblock. We need to only * write out those fields which we have changed, since if the * filesystem is mounted, it may have changed some of the other * fields. * * It takes as input a superblock which has already been byte swapped * (if necessary). * */ static errcode_t write_primary_superblock(ext2_filsys fs, struct ext2_super_block *super) { __u16 *old_super, *new_super; int check_idx, write_idx, size; errcode_t retval; if (!fs->io->manager->write_byte || !fs->orig_super) { fallback: io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); retval = io_channel_write_blk64(fs->io, 1, -SUPERBLOCK_SIZE, super); io_channel_set_blksize(fs->io, fs->blocksize); return retval; } old_super = (__u16 *) fs->orig_super; new_super = (__u16 *) super; for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { if (old_super[check_idx] == new_super[check_idx]) continue; write_idx = check_idx; for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) if (old_super[check_idx] == new_super[check_idx]) break; size = 2 * (check_idx - write_idx); #if 0 printf("Writing %d bytes starting at %d\n", size, write_idx*2); #endif retval = io_channel_write_byte(fs->io, SUPERBLOCK_OFFSET + (2 * write_idx), size, new_super + write_idx); if (retval == EXT2_ET_UNIMPLEMENTED) goto fallback; if (retval) return retval; } memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); return 0; }
static errcode_t undo_setup_tdb(struct undo_private_data *data) { int i; errcode_t retval; if (data->tdb_written == 1) return 0; data->tdb_written = 1; /* Make a bitmap to track what we've written */ memset(&data->fake_fs, 0, sizeof(data->fake_fs)); data->fake_fs.blocksize = data->tdb_data_size; retval = ext2fs_alloc_generic_bmap(&data->fake_fs, EXT2_ET_MAGIC_BLOCK_BITMAP64, EXT2FS_BMAP64_RBTREE, 0, ~1ULL, ~1ULL, "undo block map", &data->written_block_map); if (retval) return retval; /* Allocate key block */ retval = ext2fs_get_mem(data->tdb_data_size, &data->keyb); if (retval) return retval; data->key_blk_num = data->first_key_blk; /* Record block size */ dbg_printf("Undo block size %llu\n", data->tdb_data_size); dbg_printf("Keys per block %llu\n", KEYS_PER_BLOCK(data)); data->hdr.block_size = ext2fs_cpu_to_le32(data->tdb_data_size); io_channel_set_blksize(data->undo_file, data->tdb_data_size); /* Ensure that we have space for header blocks */ for (i = 0; i <= 2; i++) { retval = io_channel_read_blk64(data->undo_file, i, 1, data->keyb); if (retval) memset(data->keyb, 0, data->tdb_data_size); retval = io_channel_write_blk64(data->undo_file, i, 1, data->keyb); if (retval) return retval; retval = io_channel_flush(data->undo_file); if (retval) return retval; } memset(data->keyb, 0, data->tdb_data_size); return 0; }
static void zap_sector(ext2_filsys fs, int sect, int nsect) { char *buf; int retval; unsigned int *magic; buf = malloc(512*nsect); if (!buf) { printf(_("Out of memory erasing sectors %d-%d\n"), sect, sect + nsect - 1); exit(1); } if (sect == 0) { /* Check for a BSD disklabel, and don't erase it if so */ retval = io_channel_read_blk(fs->io, 0, -512, buf); if (retval) fprintf(stderr, _("Warning: could not read block 0: %s\n"), error_message(retval)); else { magic = (unsigned int *) (buf + BSD_LABEL_OFFSET); if ((*magic == BSD_DISKMAGIC) || (*magic == BSD_MAGICDISK)) return; } } memset(buf, 0, 512*nsect); io_channel_set_blksize(fs->io, 512); retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf); io_channel_set_blksize(fs->io, fs->blocksize); free(buf); if (retval) fprintf(stderr, _("Warning: could not erase sector %d: %s\n"), sect, error_message(retval)); }
static int check_filesystem(struct undo_context *ctx, io_channel channel) { struct ext2_super_block super, *sb; char *buf; __u32 sb_crc; errcode_t retval; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { com_err(prg_name, retval, "%s", _("while reading filesystem superblock.")); return retval; } /* * Compare the FS and the undo file superblock so that we can't apply * e2undo "patches" out of order. */ retval = ext2fs_get_mem(ctx->blocksize, &buf); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); return retval; } retval = io_channel_read_blk64(ctx->undo_file, ctx->super_block, -SUPERBLOCK_SIZE, buf); if (retval) { com_err(prg_name, retval, "%s", _("while fetching superblock")); goto out; } sb = (struct ext2_super_block *)buf; sb->s_magic = ~sb->s_magic; if (memcmp(&super, buf, sizeof(super))) { print_undo_mismatch(&super, (struct ext2_super_block *)buf); retval = -1; goto out; } sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf, SUPERBLOCK_SIZE); if (ext2fs_le32_to_cpu(ctx->hdr.sb_crc) != sb_crc) { fprintf(stderr, _("Undo file superblock checksum doesn't match.\n")); retval = -1; goto out; } out: ext2fs_free_mem(&buf); return retval; }
static errcode_t test_set_blksize(io_channel channel, int blksize) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_set_blksize(data->real, blksize); if (data->set_blksize) data->set_blksize(blksize, retval); else printf("Test_io: set_blksize(%d) returned %s\n", blksize, retval ? error_message(retval) : "OK"); return retval; }
static errcode_t test_set_blksize(io_channel channel, int blksize) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_set_blksize(data->real, blksize); if (data->set_blksize) data->set_blksize(blksize, retval); if (data->flags & TEST_FLAG_SET_BLKSIZE) fprintf(data->outfile, "Test_io: set_blksize(%d) returned %s\n", blksize, retval ? error_message(retval) : "OK"); channel->block_size = blksize; return retval; }
errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) { errcode_t err; if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; err = io_channel_set_blksize(new_io, fs->blocksize); if (err) return err; if ((new_io == fs->image_io) || (new_io == fs->io)) return 0; if ((fs->image_io != fs->io) && fs->image_io) io_channel_close(fs->image_io); if (fs->io) io_channel_close(fs->io); fs->io = fs->image_io = new_io; fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; fs->flags &= ~EXT2_FLAG_IMAGE_FILE; return 0; }
static int set_blk_size(TDB_CONTEXT *tdb, io_channel channel) { int block_size; errcode_t retval; TDB_DATA tdb_key, tdb_data; tdb_key.dptr = blksize_key; tdb_key.dsize = sizeof(blksize_key); tdb_data = tdb_fetch(tdb, tdb_key); if (!tdb_data.dptr) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); com_err(prg_name, retval, _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb)); return retval; } block_size = *(int *)tdb_data.dptr; #ifdef DEBUG printf("Block size %d\n", block_size); #endif io_channel_set_blksize(channel, block_size); return 0; }
/* * 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; }
static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) { struct ext2_super_block *sb = ctx->fs->super; struct ext2_super_block jsuper; struct problem_context pctx; struct buffer_head *bh; struct inode *j_inode = NULL; struct kdev_s *dev_fs = NULL, *dev_journal; const char *journal_name = 0; journal_t *journal = NULL; errcode_t retval = 0; io_manager io_ptr = 0; unsigned long start = 0; blk_t blk; int ext_journal = 0; int tried_backup_jnl = 0; int i; clear_problem_context(&pctx); journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); if (!journal) { return EXT2_ET_NO_MEMORY; } dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); if (!dev_fs) { retval = EXT2_ET_NO_MEMORY; goto errout; } dev_journal = dev_fs+1; dev_fs->k_ctx = dev_journal->k_ctx = ctx; dev_fs->k_dev = K_DEV_FS; dev_journal->k_dev = K_DEV_JOURNAL; journal->j_dev = dev_journal; journal->j_fs_dev = dev_fs; journal->j_inode = NULL; journal->j_blocksize = ctx->fs->blocksize; if (uuid_is_null(sb->s_journal_uuid)) { if (!sb->s_journal_inum) return EXT2_ET_BAD_INODE_NUM; j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), "journal inode"); if (!j_inode) { retval = EXT2_ET_NO_MEMORY; goto errout; } j_inode->i_ctx = ctx; j_inode->i_ino = sb->s_journal_inum; if ((retval = ext2fs_read_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2))) { try_backup_journal: if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || tried_backup_jnl) goto errout; memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, EXT2_N_BLOCKS*4); j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; j_inode->i_ext2.i_links_count = 1; j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; tried_backup_jnl++; } if (!j_inode->i_ext2.i_links_count || !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { retval = EXT2_ET_NO_JOURNAL; goto try_backup_journal; } if (j_inode->i_ext2.i_size / journal->j_blocksize < JFS_MIN_JOURNAL_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } for (i=0; i < EXT2_N_BLOCKS; i++) { blk = j_inode->i_ext2.i_block[i]; if (!blk) { if (i < EXT2_NDIR_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } continue; } if (blk < sb->s_first_data_block || blk >= sb->s_blocks_count) { retval = EXT2_ET_BAD_BLOCK_NUM; goto try_backup_journal; } } journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize; #ifdef USE_INODE_IO retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2, &journal_name); if (retval) goto errout; io_ptr = inode_io_manager; #else journal->j_inode = j_inode; ctx->journal_io = ctx->fs->io; if ((retval = journal_bmap(journal, 0, &start)) != 0) goto errout; #endif } else { ext_journal = 1; if (!ctx->journal_name) { char uuid[37]; uuid_unparse(sb->s_journal_uuid, uuid); ctx->journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); if (!ctx->journal_name) ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); } journal_name = ctx->journal_name; if (!journal_name) { fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); return EXT2_ET_LOAD_EXT_JOURNAL; } jfs_debug(1, "Using journal file %s\n", journal_name); io_ptr = unix_io_manager; } #if 0 test_io_backing_manager = io_ptr; io_ptr = test_io_manager; #endif #ifndef USE_INODE_IO if (ext_journal) #endif retval = io_ptr->open(journal_name, IO_FLAG_RW, &ctx->journal_io); if (retval) goto errout; io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); if (ext_journal) { if (ctx->fs->blocksize == 1024) start = 1; bh = getblk(dev_journal, start, ctx->fs->blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } ll_rw_block(READ, 1, &bh); if ((retval = bh->b_err) != 0) { brelse(bh); goto errout; } memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024, sizeof(jsuper)); brelse(bh); #ifdef EXT2FS_ENABLE_SWAPFS if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); #endif if (jsuper.s_magic != EXT2_SUPER_MAGIC || !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } /* Make sure the journal UUID is correct */ if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, sizeof(jsuper.s_uuid))) { fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } journal->j_maxlen = jsuper.s_blocks_count; start++; } if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { retval = EXT2_ET_NO_MEMORY; goto errout; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; #ifdef USE_INODE_IO if (j_inode) ext2fs_free_mem(&j_inode); #endif *ret_journal = journal; return 0; errout: if (dev_fs) ext2fs_free_mem(&dev_fs); if (j_inode) ext2fs_free_mem(&j_inode); if (journal) ext2fs_free_mem(&journal); return retval; }
/* * 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; int groups_per_block, blocks_per_group; blk_t group_block, blk; char *dest, *cp; #ifdef EXT2FS_ENABLE_SWAPFS int j; struct ext2_group_desc *gdp; #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; 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; } retval = manager->open(fs->device_name, (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, &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 EXT2FS_ENABLE_SWAPFS if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) || (fs->flags & EXT2_FLAG_SWAP_BYTES)) { fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); } #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)) { if (fs->super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if ((flags & EXT2_FLAG_RW) && (fs->super->s_feature_ro_compat & ~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; } } fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (fs->blocksize == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = EXT2_FRAG_SIZE(fs->super); fs->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)); 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; } /* * 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)) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = (fs->super->s_blocks_count - fs->super->s_first_data_block + blocks_per_group - 1) / blocks_per_group; fs->desc_blocks = (fs->group_desc_count + EXT2_DESC_PER_BLOCK(fs->super) - 1) / EXT2_DESC_PER_BLOCK(fs->super); retval = ext2fs_get_mem(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 = fs->blocksize / sizeof(struct ext2_group_desc); for (i=0 ; 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 EXT2FS_ENABLE_SWAPFS if (fs->flags & EXT2_FLAG_SWAP_BYTES) { gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block; j++) ext2fs_swap_group_desc(gdp++); } #endif dest += fs->blocksize; } *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; }
static errcode_t ext2fs_get_journal(ext2_filsys fs, journal_t **ret_journal) { struct process_block_struct pb; struct ext2_super_block *sb = fs->super; struct ext2_super_block jsuper; struct buffer_head *bh; struct inode *j_inode = NULL; struct kdev_s *dev_fs = NULL, *dev_journal; const char *journal_name = 0; journal_t *journal = NULL; errcode_t retval = 0; io_manager io_ptr = 0; unsigned long long start = 0; int ext_journal = 0; int tried_backup_jnl = 0; retval = ext2fs_get_memzero(sizeof(journal_t), &journal); if (retval) return retval; retval = ext2fs_get_memzero(2 * sizeof(struct kdev_s), &dev_fs); if (retval) goto errout; dev_journal = dev_fs+1; dev_fs->k_fs = dev_journal->k_fs = fs; dev_fs->k_dev = K_DEV_FS; dev_journal->k_dev = K_DEV_JOURNAL; journal->j_dev = dev_journal; journal->j_fs_dev = dev_fs; journal->j_inode = NULL; journal->j_blocksize = fs->blocksize; if (uuid_is_null(sb->s_journal_uuid)) { if (!sb->s_journal_inum) { retval = EXT2_ET_BAD_INODE_NUM; goto errout; } retval = ext2fs_get_memzero(sizeof(*j_inode), &j_inode); if (retval) goto errout; j_inode->i_fs = fs; j_inode->i_ino = sb->s_journal_inum; retval = ext2fs_read_inode(fs, sb->s_journal_inum, &j_inode->i_ext2); if (retval) { try_backup_journal: if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || tried_backup_jnl) goto errout; memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, EXT2_N_BLOCKS*4); j_inode->i_ext2.i_size_high = sb->s_jnl_blocks[15]; j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; j_inode->i_ext2.i_links_count = 1; j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; tried_backup_jnl++; } if (!j_inode->i_ext2.i_links_count || !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { retval = EXT2_ET_NO_JOURNAL; goto try_backup_journal; } if (EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize < JFS_MIN_JOURNAL_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } pb.last_block = -1; retval = ext2fs_block_iterate3(fs, j_inode->i_ino, BLOCK_FLAG_HOLE, 0, process_journal_block, &pb); if ((pb.last_block + 1) * fs->blocksize < (int) EXT2_I_SIZE(&j_inode->i_ext2)) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } if (tried_backup_jnl && (fs->flags & EXT2_FLAG_RW)) { retval = ext2fs_write_inode(fs, sb->s_journal_inum, &j_inode->i_ext2); if (retval) goto errout; } journal->j_maxlen = EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize; #ifdef USE_INODE_IO retval = ext2fs_inode_io_intern2(fs, sb->s_journal_inum, &j_inode->i_ext2, &journal_name); if (retval) goto errout; io_ptr = inode_io_manager; #else journal->j_inode = j_inode; fs->journal_io = fs->io; retval = (errcode_t)journal_bmap(journal, 0, &start); if (retval) goto errout; #endif } else { ext_journal = 1; if (!fs->journal_name) { char uuid[37]; blkid_cache blkid; blkid_get_cache(&blkid, NULL); uuid_unparse(sb->s_journal_uuid, uuid); fs->journal_name = blkid_get_devname(blkid, "UUID", uuid); if (!fs->journal_name) fs->journal_name = blkid_devno_to_devname(sb->s_journal_dev); blkid_put_cache(blkid); } journal_name = fs->journal_name; if (!journal_name) { retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } jfs_debug(1, "Using journal file %s\n", journal_name); io_ptr = unix_io_manager; } #if 0 test_io_backing_manager = io_ptr; io_ptr = test_io_manager; #endif #ifndef USE_INODE_IO if (ext_journal) #endif { retval = io_ptr->open(journal_name, fs->flags & EXT2_FLAG_RW, &fs->journal_io); } if (retval) goto errout; io_channel_set_blksize(fs->journal_io, fs->blocksize); if (ext_journal) { blk64_t maxlen; start = ext2fs_journal_sb_start(fs->blocksize) - 1; bh = getblk(dev_journal, start, fs->blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } ll_rw_block(READ, 1, &bh); retval = bh->b_err; if (retval) { brelse(bh); goto errout; } memcpy(&jsuper, start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET, sizeof(jsuper)); #ifdef WORDS_BIGENDIAN if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); #endif if (jsuper.s_magic != EXT2_SUPER_MAGIC || !ext2fs_has_feature_journal_dev(&jsuper)) { retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } /* Make sure the journal UUID is correct */ if (memcmp(jsuper.s_uuid, fs->super->s_journal_uuid, sizeof(jsuper.s_uuid))) { retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } /* Check the superblock checksum */ if (ext2fs_has_feature_metadata_csum(&jsuper)) { struct struct_ext2_filsys fsx; struct ext2_super_block superx; void *p; p = start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET; memcpy(&fsx, fs, sizeof(fsx)); memcpy(&superx, fs->super, sizeof(superx)); fsx.super = &superx; ext2fs_set_feature_metadata_csum(fsx.super); if (!ext2fs_superblock_csum_verify(&fsx, p)) { retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } } brelse(bh); maxlen = ext2fs_blocks_count(&jsuper); journal->j_maxlen = (maxlen < 1ULL << 32) ? maxlen : (1ULL << 32) - 1; start++; } bh = getblk(dev_journal, start, journal->j_blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; #ifdef USE_INODE_IO if (j_inode) ext2fs_free_mem(&j_inode); #endif *ret_journal = journal; return 0; errout: if (dev_fs) ext2fs_free_mem(&dev_fs); if (j_inode) ext2fs_free_mem(&j_inode); if (journal) ext2fs_free_mem(&journal); return retval; }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; int frags_per_block; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk_t numblocks; int rsv_gdt; int io_flags; char *buf; char c; if (!param || !param->s_blocks_count) return EXT2_ET_INVALID_ARGUMENT; 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 | EXT2_FLAG_RW; fs->umask = 022; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif io_flags = IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; retval = manager->open(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_first_meta_bg, 0); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); } set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->blocksize = EXT2_BLOCK_SIZE(super); fs->fragsize = EXT2_FRAG_SIZE(super); frags_per_block = fs->blocksize / fs->fragsize; /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; super->s_blocks_count = param->s_blocks_count; super->s_r_blocks_count = param->s_r_blocks_count; if (super->s_r_blocks_count >= param->s_blocks_count) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = ext2fs_div_ceil(super->s_blocks_count - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; set_field(s_inodes_count, super->s_blocks_count / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; super->s_blocks_count = param->s_blocks_count; super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; goto retry; } else return EXT2_ET_TOO_MANY_INODES; } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Overhead is the number of bookkeeping blocks per group. It * includes the superblock backup, the group descriptor * backups, the inode bitmap, the block bitmap, and the inode * table. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) return EXT2_ET_TOO_MANY_INODES; /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. */ rem = ((super->s_blocks_count - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) return EXT2_ET_TOOSMALL; if (rem && (rem < overhead+50)) { super->s_blocks_count -= rem; goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; sprintf(buf, "block bitmap for %s", fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; sprintf(buf, "inode bitmap for %s", fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. */ super->s_free_blocks_count = 0; for (i = 0; i < fs->group_desc_count; i++) { numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); super->s_free_blocks_count += numblocks; fs->group_desc[i].bg_free_blocks_count = numblocks; fs->group_desc[i].bg_free_inodes_count = fs->super->s_inodes_per_group; fs->group_desc[i].bg_used_dirs_count = 0; } c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: ext2fs_free(fs); return retval; }
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); }
/* * 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 errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal) { struct process_block_struct pb; struct ext2_super_block *sb = ctx->fs->super; struct ext2_super_block jsuper; struct problem_context pctx; struct buffer_head *bh; struct inode *j_inode = NULL; struct kdev_s *dev_fs = NULL, *dev_journal; const char *journal_name = 0; journal_t *journal = NULL; errcode_t retval = 0; io_manager io_ptr = 0; unsigned long long start = 0; int ext_journal = 0; int tried_backup_jnl = 0; clear_problem_context(&pctx); journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal"); if (!journal) { return EXT2_ET_NO_MEMORY; } dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev"); if (!dev_fs) { retval = EXT2_ET_NO_MEMORY; goto errout; } dev_journal = dev_fs+1; dev_fs->k_ctx = dev_journal->k_ctx = ctx; dev_fs->k_dev = K_DEV_FS; dev_journal->k_dev = K_DEV_JOURNAL; journal->j_dev = dev_journal; journal->j_fs_dev = dev_fs; journal->j_inode = NULL; journal->j_blocksize = ctx->fs->blocksize; if (uuid_is_null(sb->s_journal_uuid)) { if (!sb->s_journal_inum) { retval = EXT2_ET_BAD_INODE_NUM; goto errout; } j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode), "journal inode"); if (!j_inode) { retval = EXT2_ET_NO_MEMORY; goto errout; } j_inode->i_ctx = ctx; j_inode->i_ino = sb->s_journal_inum; if ((retval = ext2fs_read_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2))) { try_backup_journal: if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS || tried_backup_jnl) goto errout; memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode)); memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks, EXT2_N_BLOCKS*4); j_inode->i_ext2.i_size_high = sb->s_jnl_blocks[15]; j_inode->i_ext2.i_size = sb->s_jnl_blocks[16]; j_inode->i_ext2.i_links_count = 1; j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600; e2fsck_use_inode_shortcuts(ctx, 1); ctx->stashed_ino = j_inode->i_ino; ctx->stashed_inode = &j_inode->i_ext2; tried_backup_jnl++; } if (!j_inode->i_ext2.i_links_count || !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) { retval = EXT2_ET_NO_JOURNAL; goto try_backup_journal; } if (EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize < JFS_MIN_JOURNAL_BLOCKS) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } pb.last_block = -1; retval = ext2fs_block_iterate3(ctx->fs, j_inode->i_ino, BLOCK_FLAG_HOLE, 0, process_journal_block, &pb); if ((pb.last_block + 1) * ctx->fs->blocksize < (int) EXT2_I_SIZE(&j_inode->i_ext2)) { retval = EXT2_ET_JOURNAL_TOO_SMALL; goto try_backup_journal; } if (tried_backup_jnl && !(ctx->options & E2F_OPT_READONLY)) { retval = ext2fs_write_inode(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2); if (retval) goto errout; } journal->j_maxlen = EXT2_I_SIZE(&j_inode->i_ext2) / journal->j_blocksize; #ifdef USE_INODE_IO retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum, &j_inode->i_ext2, &journal_name); if (retval) goto errout; io_ptr = inode_io_manager; #else journal->j_inode = j_inode; ctx->journal_io = ctx->fs->io; if ((retval = (errcode_t) journal_bmap(journal, 0, &start)) != 0) goto errout; #endif } else { ext_journal = 1; if (!ctx->journal_name) { char uuid[37]; uuid_unparse(sb->s_journal_uuid, uuid); ctx->journal_name = blkid_get_devname(ctx->blkid, "UUID", uuid); if (!ctx->journal_name) ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev); } journal_name = ctx->journal_name; if (!journal_name) { fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; goto errout; } jfs_debug(1, "Using journal file %s\n", journal_name); io_ptr = unix_io_manager; } #if 0 test_io_backing_manager = io_ptr; io_ptr = test_io_manager; #endif #ifndef USE_INODE_IO if (ext_journal) #endif { int flags = IO_FLAG_RW; if (!(ctx->mount_flags & EXT2_MF_ISROOT && ctx->mount_flags & EXT2_MF_READONLY)) flags |= IO_FLAG_EXCLUSIVE; if ((ctx->mount_flags & EXT2_MF_READONLY) && (ctx->options & E2F_OPT_FORCE)) flags &= ~IO_FLAG_EXCLUSIVE; retval = io_ptr->open(journal_name, flags, &ctx->journal_io); } if (retval) goto errout; io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize); if (ext_journal) { blk64_t maxlen; start = ext2fs_journal_sb_start(ctx->fs->blocksize) - 1; bh = getblk(dev_journal, start, ctx->fs->blocksize); if (!bh) { retval = EXT2_ET_NO_MEMORY; goto errout; } ll_rw_block(READ, 1, &bh); if ((retval = bh->b_err) != 0) { brelse(bh); goto errout; } memcpy(&jsuper, start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET, sizeof(jsuper)); #ifdef WORDS_BIGENDIAN if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(&jsuper); #endif if (jsuper.s_magic != EXT2_SUPER_MAGIC || !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } /* Make sure the journal UUID is correct */ if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid, sizeof(jsuper.s_uuid))) { fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx); retval = EXT2_ET_LOAD_EXT_JOURNAL; brelse(bh); goto errout; } /* Check the superblock checksum */ if (jsuper.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) { struct struct_ext2_filsys fsx; struct ext2_super_block superx; void *p; p = start ? bh->b_data : bh->b_data + SUPERBLOCK_OFFSET; memcpy(&fsx, ctx->fs, sizeof(fsx)); memcpy(&superx, ctx->fs->super, sizeof(superx)); fsx.super = &superx; fsx.super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_METADATA_CSUM; if (!ext2fs_superblock_csum_verify(&fsx, p) && fix_problem(ctx, PR_0_EXT_JOURNAL_SUPER_CSUM_INVALID, &pctx)) { ext2fs_superblock_csum_set(&fsx, p); mark_buffer_dirty(bh); } } brelse(bh); maxlen = ext2fs_blocks_count(&jsuper); journal->j_maxlen = (maxlen < 1ULL << 32) ? maxlen : (1ULL << 32) - 1; start++; } if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) { retval = EXT2_ET_NO_MEMORY; goto errout; } journal->j_sb_buffer = bh; journal->j_superblock = (journal_superblock_t *)bh->b_data; #ifdef USE_INODE_IO if (j_inode) ext2fs_free_mem(&j_inode); #endif *ret_journal = journal; e2fsck_use_inode_shortcuts(ctx, 0); return 0; errout: e2fsck_use_inode_shortcuts(ctx, 0); if (dev_fs) ext2fs_free_mem(&dev_fs); if (j_inode) ext2fs_free_mem(&j_inode); if (journal) ext2fs_free_mem(&journal); return retval; }
errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk64_t free_blocks; blk_t numblocks; int rsv_gdt; int csum_flag; int bigalloc_flag; int io_flags; unsigned reserved_inos; char *buf = 0; char c; double reserved_ratio; if (!param || !ext2fs_blocks_count(param)) return EXT2_ET_INVALID_ARGUMENT; 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 | EXT2_FLAG_RW; fs->umask = 022; fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif 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(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) #define assign_field(field) (super->field = param->field) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; bigalloc_flag = EXT2_HAS_RO_COMPAT_FEATURE(param, EXT4_FEATURE_RO_COMPAT_BIGALLOC); assign_field(s_log_block_size); if (bigalloc_flag) { set_field(s_log_cluster_size, super->s_log_block_size+4); if (super->s_log_block_size > super->s_log_cluster_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } } else super->s_log_cluster_size = super->s_log_block_size; set_field(s_first_data_block, super->s_log_cluster_size ? 0 : 1); set_field(s_max_mnt_count, 0); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_default_mount_opts, 0); set_field(s_first_meta_bg, 0); set_field(s_raid_stride, 0); /* default stride size: 0 */ set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ set_field(s_log_groups_per_flex, 0); set_field(s_flags, 0); assign_field(s_backup_bgs[0]); assign_field(s_backup_bgs[1]); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { int extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; set_field(s_min_extra_isize, extra_isize); set_field(s_want_extra_isize, extra_isize); } } else { super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; } set_field(s_checkinterval, 0); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(super); fs->cluster_ratio_bits = super->s_log_cluster_size - super->s_log_block_size; if (bigalloc_flag) { unsigned long long bpg; if (param->s_blocks_per_group && param->s_clusters_per_group && ((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) != param->s_blocks_per_group)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } if (param->s_clusters_per_group) assign_field(s_clusters_per_group); else if (param->s_blocks_per_group) super->s_clusters_per_group = param->s_blocks_per_group / EXT2FS_CLUSTER_RATIO(fs); else if (super->s_log_cluster_size + 15 < 32) super->s_clusters_per_group = fs->blocksize * 8; else super->s_clusters_per_group = (fs->blocksize - 1) * 8; if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super)) super->s_clusters_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super); bpg = EXT2FS_C2B(fs, (unsigned long long) super->s_clusters_per_group); if (bpg >= (((unsigned long long) 1) << 32)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } super->s_blocks_per_group = bpg; } else { set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_clusters_per_group = super->s_blocks_per_group; } ext2fs_blocks_count_set(super, ext2fs_blocks_count(param) & ~((blk64_t) EXT2FS_CLUSTER_MASK(fs))); ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param)); if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } set_field(s_mmp_update_interval, 0); /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = (dgrp_t) ext2fs_div64_ceil( ext2fs_blocks_count(super) - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } set_field(s_desc_size, super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT ? EXT2_MIN_DESC_SIZE_64BIT : 0); fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; if (super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT && (ext2fs_blocks_count(super) / i) > (1ULL << 32)) set_field(s_inodes_count, ~0U); else set_field(s_inodes_count, ext2fs_blocks_count(super) / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (!bigalloc_flag && super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; ext2fs_blocks_count_set(super, ext2fs_blocks_count(param)); super->s_clusters_per_group = super->s_blocks_per_group; goto retry; } else { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ if (super->s_inodes_per_group < 8) super->s_inodes_per_group = 8; super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Calculate the maximum number of bookkeeping blocks per * group. It includes the superblock, the block group * descriptors, the block bitmap, the inode bitmap, the inode * table, and the reserved gdt blocks. */ overhead = (int) (3 + fs->inode_blocks_per_group + fs->desc_blocks + super->s_reserved_gdt_blocks); /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. We need to recalculate the overhead for the last block * group, since it might or might not have a superblock * backup. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; rem = ((ext2fs_blocks_count(super) - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { retval = EXT2_ET_TOOSMALL; goto cleanup; } if (rem && (rem < overhead+50)) { ext2fs_blocks_count_set(super, ext2fs_blocks_count(super) - rem); /* * If blocks count is changed, we need to recalculate * reserved blocks count not to exceed 50%. */ reserved_ratio = 100.0 * ext2fs_r_blocks_count(param) / ext2fs_blocks_count(param); ext2fs_r_blocks_count_set(super, reserved_ratio * ext2fs_blocks_count(super) / 100.0); goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ /* Set up the locations of the backup superblocks */ if (super->s_feature_compat & EXT4_FEATURE_COMPAT_SPARSE_SUPER2) { if (super->s_backup_bgs[0] >= fs->group_desc_count) super->s_backup_bgs[0] = fs->group_desc_count - 1; if (super->s_backup_bgs[1] >= fs->group_desc_count) super->s_backup_bgs[1] = fs->group_desc_count - 1; if (super->s_backup_bgs[0] == super->s_backup_bgs[1]) super->s_backup_bgs[1] = 0; if (super->s_backup_bgs[0] > super->s_backup_bgs[1]) { __u32 t = super->s_backup_bgs[0]; super->s_backup_bgs[0] = super->s_backup_bgs[1]; super->s_backup_bgs[1] = t; } } retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_subcluster_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. * * If FLEX_BG meta-data grouping is used, only account for the * superblock and group descriptors (the inode tables and * bitmaps will be accounted for when allocated). */ free_blocks = 0; csum_flag = ext2fs_has_group_desc_csum(fs); reserved_inos = super->s_first_ino; for (i = 0; i < fs->group_desc_count; i++) { /* * Don't set the BLOCK_UNINIT group for the last group * because the block bitmap needs to be padded. */ if (csum_flag) { if (i != fs->group_desc_count - 1) ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT); ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); numblocks = super->s_inodes_per_group; if (reserved_inos) { if (numblocks > reserved_inos) { numblocks -= reserved_inos; reserved_inos = 0; } else { reserved_inos -= numblocks; numblocks = 0; } } ext2fs_bg_itable_unused_set(fs, i, numblocks); } numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); if (fs->super->s_log_groups_per_flex) numblocks += 2 + fs->inode_blocks_per_group; free_blocks += numblocks; ext2fs_bg_free_blocks_count_set(fs, i, numblocks); ext2fs_bg_free_inodes_count_set(fs, i, fs->super->s_inodes_per_group); ext2fs_bg_used_dirs_count_set(fs, i, 0); ext2fs_group_desc_csum_set(fs, i); } free_blocks &= ~EXT2FS_CLUSTER_MASK(fs); ext2fs_free_blocks_count_set(super, free_blocks); c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: free(buf); ext2fs_free(fs); return retval; }
int main(int argc, char *argv[]) { int c, force = 0, dry_run = 0, verbose = 0, dump = 0; io_channel channel; errcode_t retval; int mount_flags, csum_error = 0, io_error = 0; size_t i, keys_per_block; char *device_name, *tdb_file; io_manager manager = unix_io_manager; struct undo_context undo_ctx; char *buf; struct undo_key_block *keyb; struct undo_key *dkey; struct undo_key_info *ikey; __u32 key_crc, blk_crc, hdr_crc; blk64_t lblk; ext2_filsys fs; __u64 offset = 0; char opt_offset_string[40] = { 0 }; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); setlocale(LC_CTYPE, ""); bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); set_com_err_gettext(gettext); #endif add_error_table(&et_ext2_error_table); prg_name = argv[0]; while ((c = getopt(argc, argv, "fhno:vz:")) != EOF) { switch (c) { case 'f': force = 1; break; case 'h': dump = 1; break; case 'n': dry_run = 1; break; case 'o': offset = strtoull(optarg, &buf, 0); if (*buf) { com_err(prg_name, 0, _("illegal offset - %s"), optarg); exit(1); } /* used to indicate that an offset was specified */ opt_offset_string[0] = 1; break; case 'v': verbose = 1; break; case 'z': undo_file = optarg; break; default: usage(); } } if (argc != optind + 2) usage(); tdb_file = argv[optind]; device_name = argv[optind+1]; if (undo_file && strcmp(tdb_file, undo_file) == 0) { printf(_("Will not write to an undo file while replaying it.\n")); exit(1); } /* Interpret the undo file */ retval = manager->open(tdb_file, IO_FLAG_EXCLUSIVE, &undo_ctx.undo_file); if (retval) { com_err(prg_name, errno, _("while opening undo file `%s'\n"), tdb_file); exit(1); } retval = io_channel_read_blk64(undo_ctx.undo_file, 0, -(int)sizeof(undo_ctx.hdr), &undo_ctx.hdr); if (retval) { com_err(prg_name, retval, _("while reading undo file")); exit(1); } if (memcmp(undo_ctx.hdr.magic, E2UNDO_MAGIC, sizeof(undo_ctx.hdr.magic))) { fprintf(stderr, _("%s: Not an undo file.\n"), tdb_file); exit(1); } if (dump) { dump_header(&undo_ctx.hdr); exit(1); } hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&undo_ctx.hdr, sizeof(struct undo_header) - sizeof(__u32)); if (!force && ext2fs_le32_to_cpu(undo_ctx.hdr.header_crc) != hdr_crc) { fprintf(stderr, _("%s: Header checksum doesn't match.\n"), tdb_file); exit(1); } undo_ctx.blocksize = ext2fs_le32_to_cpu(undo_ctx.hdr.block_size); undo_ctx.fs_blocksize = ext2fs_le32_to_cpu(undo_ctx.hdr.fs_block_size); if (undo_ctx.blocksize == 0 || undo_ctx.fs_blocksize == 0) { fprintf(stderr, _("%s: Corrupt undo file header.\n"), tdb_file); exit(1); } if (!force && undo_ctx.blocksize > E2UNDO_MAX_BLOCK_SIZE) { fprintf(stderr, _("%s: Undo block size too large.\n"), tdb_file); exit(1); } if (!force && undo_ctx.blocksize < E2UNDO_MIN_BLOCK_SIZE) { fprintf(stderr, _("%s: Undo block size too small.\n"), tdb_file); exit(1); } undo_ctx.super_block = ext2fs_le64_to_cpu(undo_ctx.hdr.super_offset); undo_ctx.num_keys = ext2fs_le64_to_cpu(undo_ctx.hdr.num_keys); io_channel_set_blksize(undo_ctx.undo_file, undo_ctx.blocksize); /* * Do not compare undo_ctx.hdr.f_compat with the available compatible * features set, because a "missing" compatible feature should * not cause any problems. */ if (!force && (undo_ctx.hdr.f_incompat || undo_ctx.hdr.f_rocompat)) { fprintf(stderr, _("%s: Unknown undo file feature set.\n"), tdb_file); exit(1); } /* open the fs */ retval = ext2fs_check_if_mounted(device_name, &mount_flags); if (retval) { com_err(prg_name, retval, _("Error while determining whether " "%s is mounted."), device_name); exit(1); } if (mount_flags & EXT2_MF_MOUNTED) { com_err(prg_name, retval, "%s", _("e2undo should only be run " "on unmounted filesystems")); exit(1); } if (undo_file) { retval = e2undo_setup_tdb(device_name, &manager); if (retval) exit(1); } retval = manager->open(device_name, IO_FLAG_EXCLUSIVE | (dry_run ? 0 : IO_FLAG_RW), &channel); if (retval) { com_err(prg_name, retval, _("while opening `%s'"), device_name); exit(1); } if (*opt_offset_string || e2undo_has_feature_fs_offset(&undo_ctx.hdr)) { if (!*opt_offset_string) offset = ext2fs_le64_to_cpu(undo_ctx.hdr.fs_offset); retval = snprintf(opt_offset_string, sizeof(opt_offset_string), "offset=%llu", offset); if ((size_t) retval >= sizeof(opt_offset_string)) { /* should not happen... */ com_err(prg_name, 0, _("specified offset is too large")); exit(1); } io_channel_set_options(channel, opt_offset_string); } if (!force && check_filesystem(&undo_ctx, channel)) exit(1); /* prepare to read keys */ retval = ext2fs_get_mem(sizeof(struct undo_key_info) * undo_ctx.num_keys, &undo_ctx.keys); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); exit(1); } ikey = undo_ctx.keys; retval = ext2fs_get_mem(undo_ctx.blocksize, &keyb); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); exit(1); } retval = ext2fs_get_mem(E2UNDO_MAX_EXTENT_BLOCKS * undo_ctx.blocksize, &buf); if (retval) { com_err(prg_name, retval, "%s", _("while allocating memory")); exit(1); } /* load keys */ keys_per_block = KEYS_PER_BLOCK(&undo_ctx); lblk = ext2fs_le64_to_cpu(undo_ctx.hdr.key_offset); dbg_printf("nr_keys=%lu, kpb=%zu, blksz=%u\n", undo_ctx.num_keys, keys_per_block, undo_ctx.blocksize); for (i = 0; i < undo_ctx.num_keys; i += keys_per_block) { size_t j, max_j; __le32 crc; retval = io_channel_read_blk64(undo_ctx.undo_file, lblk, 1, keyb); if (retval) { com_err(prg_name, retval, "%s", _("while reading keys")); if (force) { io_error = 1; undo_ctx.num_keys = i - 1; break; } exit(1); } /* check keys */ if (!force && ext2fs_le32_to_cpu(keyb->magic) != KEYBLOCK_MAGIC) { fprintf(stderr, _("%s: wrong key magic at %llu\n"), tdb_file, lblk); exit(1); } crc = keyb->crc; keyb->crc = 0; key_crc = ext2fs_crc32c_le(~0, (unsigned char *)keyb, undo_ctx.blocksize); if (!force && ext2fs_le32_to_cpu(crc) != key_crc) { fprintf(stderr, _("%s: key block checksum error at %llu.\n"), tdb_file, lblk); exit(1); } /* load keys from key block */ lblk++; max_j = undo_ctx.num_keys - i; if (max_j > keys_per_block) max_j = keys_per_block; for (j = 0, dkey = keyb->keys; j < max_j; j++, ikey++, dkey++) { ikey->fsblk = ext2fs_le64_to_cpu(dkey->fsblk); ikey->fileblk = lblk; ikey->blk_crc = ext2fs_le32_to_cpu(dkey->blk_crc); ikey->size = ext2fs_le32_to_cpu(dkey->size); lblk += (ikey->size + undo_ctx.blocksize - 1) / undo_ctx.blocksize; if (E2UNDO_MAX_EXTENT_BLOCKS * undo_ctx.blocksize < ikey->size) { com_err(prg_name, retval, _("%s: block %llu is too long."), tdb_file, ikey->fsblk); exit(1); } /* check each block's crc */ retval = io_channel_read_blk64(undo_ctx.undo_file, ikey->fileblk, -(int)ikey->size, buf); if (retval) { com_err(prg_name, retval, _("while fetching block %llu."), ikey->fileblk); if (!force) exit(1); io_error = 1; continue; } blk_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf, ikey->size); if (blk_crc != ikey->blk_crc) { fprintf(stderr, _("checksum error in filesystem block " "%llu (undo blk %llu)\n"), ikey->fsblk, ikey->fileblk); if (!force) exit(1); csum_error = 1; } } } ext2fs_free_mem(&keyb); /* sort keys in fs block order */ qsort(undo_ctx.keys, undo_ctx.num_keys, sizeof(struct undo_key_info), key_compare); /* replay */ io_channel_set_blksize(channel, undo_ctx.fs_blocksize); for (i = 0, ikey = undo_ctx.keys; i < undo_ctx.num_keys; i++, ikey++) { retval = io_channel_read_blk64(undo_ctx.undo_file, ikey->fileblk, -(int)ikey->size, buf); if (retval) { com_err(prg_name, retval, _("while fetching block %llu."), ikey->fileblk); io_error = 1; continue; } if (verbose) printf("Replayed block of size %u from %llu to %llu\n", ikey->size, ikey->fileblk, ikey->fsblk); if (dry_run) continue; retval = io_channel_write_blk64(channel, ikey->fsblk, -(int)ikey->size, buf); if (retval) { com_err(prg_name, retval, _("while writing block %llu."), ikey->fsblk); io_error = 1; } } if (csum_error) fprintf(stderr, _("Undo file corruption; run e2fsck NOW!\n")); if (io_error) fprintf(stderr, _("IO error during replay; run e2fsck NOW!\n")); if (!(ext2fs_le32_to_cpu(undo_ctx.hdr.state) & E2UNDO_STATE_FINISHED)) { force = 1; fprintf(stderr, _("Incomplete undo record; run e2fsck.\n")); } ext2fs_free_mem(&buf); ext2fs_free_mem(&undo_ctx.keys); io_channel_close(channel); /* If there were problems, try to force a fsck */ if (!dry_run && (force || csum_error || io_error)) { retval = ext2fs_open2(device_name, NULL, EXT2_FLAG_RW | EXT2_FLAG_64BITS, 0, 0, manager, &fs); if (retval) goto out; fs->super->s_state &= ~EXT2_VALID_FS; if (csum_error || io_error) fs->super->s_state |= EXT2_ERROR_FS; ext2fs_mark_super_dirty(fs); ext2fs_close_free(&fs); } out: io_channel_close(undo_ctx.undo_file); return csum_error; }
static errcode_t write_undo_indexes(struct undo_private_data *data, int flush) { errcode_t retval; struct ext2_super_block super; io_channel channel; int block_size; __u32 sb_crc, hdr_crc; /* Spit out a key block, if there's any data */ if (data->keys_in_block) { data->keyb->magic = ext2fs_cpu_to_le32(KEYBLOCK_MAGIC); data->keyb->crc = 0; data->keyb->crc = ext2fs_cpu_to_le32( ext2fs_crc32c_le(~0, (unsigned char *)data->keyb, data->tdb_data_size)); dbg_printf("Writing keyblock to blk %llu\n", data->key_blk_num); retval = io_channel_write_blk64(data->undo_file, data->key_blk_num, 1, data->keyb); if (retval) return retval; /* Move on to the next key block if it's full. */ if (data->keys_in_block == KEYS_PER_BLOCK(data)) { memset(data->keyb, 0, data->tdb_data_size); data->keys_in_block = 0; data->key_blk_num = data->undo_blk_num; data->undo_blk_num++; } } /* Prepare superblock for write */ channel = data->real; block_size = channel->block_size; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)&super, SUPERBLOCK_SIZE); super.s_magic = ~super.s_magic; /* Write the undo header to disk. */ memcpy(data->hdr.magic, E2UNDO_MAGIC, sizeof(data->hdr.magic)); data->hdr.num_keys = ext2fs_cpu_to_le64(data->num_keys); data->hdr.super_offset = ext2fs_cpu_to_le64(data->super_blk_num); data->hdr.key_offset = ext2fs_cpu_to_le64(data->first_key_blk); data->hdr.fs_block_size = ext2fs_cpu_to_le32(block_size); data->hdr.sb_crc = ext2fs_cpu_to_le32(sb_crc); hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&data->hdr, sizeof(data->hdr) - sizeof(data->hdr.header_crc)); data->hdr.header_crc = ext2fs_cpu_to_le32(hdr_crc); retval = io_channel_write_blk64(data->undo_file, 0, -(int)sizeof(data->hdr), &data->hdr); if (retval) goto err_out; /* * Record the entire superblock (in FS byte order) so that we can't * apply e2undo files to the wrong FS or out of order. */ dbg_printf("Writing superblock to block %llu\n", data->super_blk_num); retval = io_channel_write_blk64(data->undo_file, data->super_blk_num, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; if (flush) retval = io_channel_flush(data->undo_file); err_out: io_channel_set_blksize(channel, block_size); return retval; }