/* * Stupid algorithm --- we now just search forward starting from the * goal. Should put in a smarter one someday.... */ errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, ext2fs_block_bitmap map, blk64_t *ret) { errcode_t retval; blk64_t b = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!goal || (goal >= ext2fs_blocks_count(fs->super))) goal = fs->super->s_first_data_block; goal &= ~EXT2FS_CLUSTER_MASK(fs); retval = ext2fs_find_first_zero_block_bitmap2(map, goal, ext2fs_blocks_count(fs->super) - 1, &b); if ((retval == ENOENT) && (goal != fs->super->s_first_data_block)) retval = ext2fs_find_first_zero_block_bitmap2(map, fs->super->s_first_data_block, goal - 1, &b); if (retval == ENOENT) return EXT2_ET_BLOCK_ALLOC_FAIL; if (retval) return retval; clear_block_uninit(fs, ext2fs_group_of_blk2(fs, b)); *ret = b; return 0; }
/* * Calculate the number of GDT blocks to reserve for online filesystem growth. * The absolute maximum number of GDT blocks we can reserve is determined by * the number of block pointers that can fit into a single block. */ static unsigned int calc_reserved_gdt_blocks(ext2_filsys fs) { struct ext2_super_block *sb = fs->super; unsigned long bpg = sb->s_blocks_per_group; unsigned int gdpb = EXT2_DESC_PER_BLOCK(sb); unsigned long max_blocks = 0xffffffff; unsigned long rsv_groups; unsigned int rsv_gdb; /* We set it at 1024x the current filesystem size, or * the upper block count limit (2^32), whichever is lower. */ if (ext2fs_blocks_count(sb) < max_blocks / 1024) max_blocks = ext2fs_blocks_count(sb) * 1024; /* * ext2fs_div64_ceil() is unnecessary because max_blocks is * max _GDT_ blocks, which is limited to 32 bits. */ rsv_groups = ext2fs_div_ceil(max_blocks - sb->s_first_data_block, bpg); rsv_gdb = ext2fs_div_ceil(rsv_groups, gdpb) - fs->desc_blocks; if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); #ifdef RES_GDT_DEBUG printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n", max_blocks, rsv_groups, rsv_gdb); #endif return rsv_gdb; }
errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, blk64_t finish, int num, ext2fs_block_bitmap map, blk64_t *ret) { blk64_t b = start; int c_ratio; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!b) b = fs->super->s_first_data_block; if (!finish) finish = start; if (!num) num = 1; c_ratio = 1 << ext2fs_get_bitmap_granularity(map); b &= ~(c_ratio - 1); finish &= ~(c_ratio -1); do { if (b+num-1 > ext2fs_blocks_count(fs->super)) b = fs->super->s_first_data_block; if (ext2fs_fast_test_block_bitmap_range2(map, b, num)) { *ret = b; return 0; } b += c_ratio; } while (b != finish); return EXT2_ET_BLOCK_ALLOC_FAIL; }
/* * Determine the number of journal blocks to use, either via * user-specified # of megabytes, or via some intelligently selected * defaults. * * Find a reasonable journal file size (in blocks) given the number of blocks * in the filesystem. For very small filesystems, it is not reasonable to * have a journal that fills more than half of the filesystem. */ unsigned int figure_journal_size(int size, ext2_filsys fs) { int j_blocks; j_blocks = ext2fs_default_journal_size(ext2fs_blocks_count(fs->super)); if (j_blocks < 0) { fputs(_("\nFilesystem too small for a journal\n"), stderr); return 0; } if (size > 0) { j_blocks = size * 1024 / (fs->blocksize / 1024); if (j_blocks < 1024 || j_blocks > 10240000) { fprintf(stderr, _("\nThe requested journal " "size is %d blocks; it must be\n" "between 1024 and 10240000 blocks. " "Aborting.\n"), j_blocks); exit(1); } if ((unsigned) j_blocks > ext2fs_free_blocks_count(fs->super) / 2) { fputs(_("\nJournal size too big for filesystem.\n"), stderr); exit(1); } } return j_blocks; }
/* * Find the place where we should start allocating blocks for the huge * files. Leave <slack> free blocks at the beginning of the file * system for things like metadata blocks. */ static blk64_t get_start_block(ext2_filsys fs, blk64_t slack) { errcode_t retval; blk64_t blk = fs->super->s_first_data_block, next; blk64_t last_blk = ext2fs_blocks_count(fs->super) - 1; while (slack) { retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map, blk, last_blk, &blk); if (retval) break; retval = ext2fs_find_first_set_block_bitmap2(fs->block_map, blk, last_blk, &next); if (retval) next = last_blk; if (next - blk > slack) { blk += slack; break; } slack -= (next - blk); blk = next; } return blk; }
static int block_map_looks_insane(ext2_filsys fs, struct ext2_inode_large *inode) { unsigned int i, bad; /* We're only interested in block mapped files, dirs, and symlinks */ if ((inode->i_flags & EXT4_INLINE_DATA_FL) || (inode->i_flags & EXT4_EXTENTS_FL)) return 0; if (!LINUX_S_ISREG(inode->i_mode) && !LINUX_S_ISLNK(inode->i_mode) && !LINUX_S_ISDIR(inode->i_mode)) return 0; if (LINUX_S_ISLNK(inode->i_mode) && EXT2_I_SIZE(inode) <= sizeof(inode->i_block)) return 0; /* Unused inodes probably aren't insane */ if (inode->i_links_count == 0) return 0; /* See if more than half the block maps are insane */ for (i = 0, bad = 0; i < EXT2_N_BLOCKS; i++) if (inode->i_block[i] != 0 && (inode->i_block[i] < fs->super->s_first_data_block || inode->i_block[i] >= ext2fs_blocks_count(fs->super))) bad++; return bad > EXT2_N_BLOCKS / 2; }
errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf) { struct mmp_struct *mmp_s = buf; struct timeval tv; errcode_t retval = 0; gettimeofday(&tv, 0); mmp_s->mmp_time = tv.tv_sec; fs->mmp_last_written = tv.tv_sec; if (fs->super->s_mmp_block < fs->super->s_first_data_block || fs->super->s_mmp_block > ext2fs_blocks_count(fs->super)) return EXT2_ET_MMP_BAD_BLOCK; #ifdef WORDS_BIGENDIAN ext2fs_swap_mmp(mmp_s); #endif /* I was tempted to make this use O_DIRECT and the mmp_fd, but * this caused no end of grief, while leaving it as-is works. */ retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf); #ifdef WORDS_BIGENDIAN ext2fs_swap_mmp(mmp_s); #endif /* Make sure the block gets to disk quickly */ io_channel_flush(fs->io); return retval; }
void do_dump_unused(int argc EXT2FS_ATTR((unused)), char **argv) { blk64_t blk; unsigned char buf[EXT2_MAX_BLOCK_SIZE]; unsigned int i; errcode_t retval; if (common_args_process(argc, argv, 1, 1, "dump_unused", "", 0)) return; for (blk=current_fs->super->s_first_data_block; blk < ext2fs_blocks_count(current_fs->super); blk++) { if (ext2fs_test_block_bitmap2(current_fs->block_map,blk)) continue; retval = io_channel_read_blk64(current_fs->io, blk, 1, buf); if (retval) { com_err(argv[0], retval, "While reading block\n"); return; } for (i=0; i < current_fs->blocksize; i++) if (buf[i]) break; if (i >= current_fs->blocksize) continue; printf("\nUnused block %llu contains non-zero data:\n\n", blk); for (i=0; i < current_fs->blocksize; i++) fputc(buf[i], stdout); } }
/* * This function is called by ext2fs_get_next_inode when it needs to * get ready to read in a new blockgroup. */ static errcode_t get_next_blockgroup(ext2_inode_scan scan) { ext2_filsys fs = scan->fs; scan->current_group++; scan->groups_left--; scan->current_block = ext2fs_inode_table_loc(scan->fs, scan->current_group); scan->current_inode = scan->current_group * EXT2_INODES_PER_GROUP(fs->super); scan->bytes_left = 0; scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super); scan->blocks_left = fs->inode_blocks_per_group; if (ext2fs_has_group_desc_csum(fs)) { __u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group); if (scan->inodes_left > unused) scan->inodes_left -= unused; else scan->inodes_left = 0; scan->blocks_left = (scan->inodes_left + (fs->blocksize / scan->inode_size - 1)) * scan->inode_size / fs->blocksize; } if (scan->current_block && ((scan->current_block < fs->super->s_first_data_block) || (scan->current_block + fs->inode_blocks_per_group - 1 >= ext2fs_blocks_count(fs->super)))) return EXT2_ET_GDESC_BAD_INODE_TABLE; return 0; }
errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret) { __u64 start, end, real_end; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; start = fs->super->s_first_data_block; end = ext2fs_blocks_count(fs->super)-1; real_end = ((__u64) EXT2_BLOCKS_PER_GROUP(fs->super) * (__u64) fs->group_desc_count)-1 + start; if (fs->flags & EXT2_FLAG_64BITS) return (ext2fs_alloc_generic_bmap(fs, EXT2_ET_MAGIC_BLOCK_BITMAP64, EXT2FS_BMAP64_BITARRAY, start, end, real_end, descr, ret)); if ((end > ~0U) || (real_end > ~0U)) return EXT2_ET_CANT_USE_LEGACY_BITMAPS; return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, fs, start, end, real_end, descr, 0, (ext2fs_generic_bitmap *) ret)); }
/* * ext2fs_allocate_block_bitmap() really allocates a per-cluster * bitmap for backwards compatibility. This function allocates a * block bitmap which is truly per-block, even if clusters/bigalloc * are enabled. mke2fs and e2fsck need this for tracking the * allocation of the file system metadata blocks. */ errcode_t ext2fs_allocate_subcluster_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret) { __u64 start, end, real_end; ext2fs_generic_bitmap bmap; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; if (!fs->cluster_ratio_bits) return ext2fs_allocate_block_bitmap(fs, descr, ret); if ((fs->flags & EXT2_FLAG_64BITS) == 0) return EXT2_ET_CANT_USE_LEGACY_BITMAPS; start = fs->super->s_first_data_block; end = ext2fs_blocks_count(fs->super)-1; real_end = ((__u64) EXT2_BLOCKS_PER_GROUP(fs->super) * (__u64) fs->group_desc_count)-1 + start; retval = ext2fs_alloc_generic_bmap(fs, EXT2_ET_MAGIC_BLOCK_BITMAP64, fs->default_bitmap_type, start, end, real_end, descr, &bmap); if (retval) return retval; bmap->cluster_bits = 0; *ret = bmap; return 0; }
blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block, dgrp_t i) { int bg; int has_super = 0; blk64_t ret_blk; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (i < fs->super->s_first_meta_bg)) return (group_block + i + 1); bg = EXT2_DESC_PER_BLOCK(fs->super) * i; if (ext2fs_bg_has_super(fs, bg)) has_super = 1; ret_blk = ext2fs_group_first_block2(fs, bg) + has_super; /* * If group_block is not the normal value, we're trying to use * the backup group descriptors and superblock --- so use the * alternate location of the second block group in the * metablock group. Ideally we should be testing each bg * descriptor block individually for correctness, but we don't * have the infrastructure in place to do that. */ if (group_block != fs->super->s_first_data_block && ((ret_blk + fs->super->s_blocks_per_group) < ext2fs_blocks_count(fs->super))) ret_blk += fs->super->s_blocks_per_group; return ret_blk; }
/* * Calculate the initial goal block to be roughly at the middle of the * filesystem. Pick a group that has the largest number of free * blocks. */ static blk64_t get_midpoint_journal_block(ext2_filsys fs) { dgrp_t group, start, end, i, log_flex; group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) - fs->super->s_first_data_block) / 2); log_flex = 1 << fs->super->s_log_groups_per_flex; if (fs->super->s_log_groups_per_flex && (group > log_flex)) { group = group & ~(log_flex - 1); while ((group < fs->group_desc_count) && ext2fs_bg_free_blocks_count(fs, group) == 0) group++; if (group == fs->group_desc_count) group = 0; start = group; } else start = (group > 0) ? group-1 : group; end = ((group+1) < fs->group_desc_count) ? group+1 : group; group = start; for (i = start + 1; i <= end; i++) if (ext2fs_bg_free_blocks_count(fs, i) > ext2fs_bg_free_blocks_count(fs, group)) group = i; return ext2fs_group_first_block2(fs, group); }
static int process_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_block, int ref_offset, void *priv_data) { struct process_block_struct *pb; errcode_t retval; int ret; blk64_t block, orig; pb = (struct process_block_struct *) priv_data; block = orig = *block_nr; ret = 0; /* * Let's see if this is one which we need to relocate */ if (ext2fs_test_block_bitmap2(pb->reserve, block)) { do { if (++block >= ext2fs_blocks_count(fs->super)) block = fs->super->s_first_data_block; if (block == orig) { pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; return BLOCK_ABORT; } } while (ext2fs_test_block_bitmap2(pb->reserve, block) || ext2fs_test_block_bitmap2(pb->alloc_map, block)); retval = io_channel_read_blk64(fs->io, orig, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } retval = io_channel_write_blk64(fs->io, block, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } *block_nr = block; ext2fs_mark_block_bitmap2(pb->alloc_map, block); ret = BLOCK_CHANGED; if (pb->flags & EXT2_BMOVE_DEBUG) printf("ino=%u, blockcnt=%lld, %llu->%llu\n", (unsigned) pb->ino, blockcnt, (unsigned long long) orig, (unsigned long long) block); } if (pb->add_dir) { retval = ext2fs_add_dir_block2(fs->dblist, pb->ino, block, blockcnt); if (retval) { pb->error = retval; ret |= BLOCK_ABORT; } } return ret; }
/* * Stupid algorithm --- we now just search forward starting from the * goal. Should put in a smarter one someday.... */ errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal, ext2fs_block_bitmap map, blk64_t *ret) { blk64_t i; int c_ratio; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!goal || (goal >= ext2fs_blocks_count(fs->super))) goal = fs->super->s_first_data_block; i = goal; c_ratio = 1 << ext2fs_get_bitmap_granularity(map); if (c_ratio > 1) goal &= ~EXT2FS_CLUSTER_MASK(fs); check_block_uninit(fs, map, (i - fs->super->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(fs->super)); do { if (((i - fs->super->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(fs->super)) == 0) check_block_uninit(fs, map, (i - fs->super->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(fs->super)); if (!ext2fs_fast_test_block_bitmap2(map, i)) { *ret = i; return 0; } i = (i + c_ratio) & ~(c_ratio - 1); if (i >= ext2fs_blocks_count(fs->super)) i = fs->super->s_first_data_block; } while (i != goal); return EXT2_ET_BLOCK_ALLOC_FAIL; }
static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; unsigned long old_size; if (!*block_nr) return 0; /* * If the block number is outrageous, clear it and ignore it. */ if (*block_nr >= ext2fs_blocks_count(fs->super) || *block_nr < fs->super->s_first_data_block) { *block_nr = 0; return BLOCK_CHANGED; } if (blockcnt < 0) { if (rec->ind_blocks_size >= rec->max_ind_blocks) { old_size = rec->max_ind_blocks * sizeof(blk_t); rec->max_ind_blocks += 10; retval = ext2fs_resize_mem(old_size, rec->max_ind_blocks * sizeof(blk_t), &rec->ind_blocks); if (retval) { rec->max_ind_blocks -= 10; rec->err = retval; return BLOCK_ABORT; } } rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; } /* * Mark the block as unused, and update accounting information */ ext2fs_block_alloc_stats2(fs, *block_nr, -1); *block_nr = 0; return BLOCK_CHANGED; }
blk64_t ext2fs_descriptor_block_loc2(ext2_filsys fs, blk64_t group_block, dgrp_t i) { int bg; int has_super = 0, group_zero_adjust = 0; blk64_t ret_blk; /* * On a bigalloc FS with 1K blocks, block 0 is reserved for non-ext4 * stuff, so adjust for that if we're being asked for group 0. */ if (i == 0 && fs->blocksize == 1024 && EXT2FS_CLUSTER_RATIO(fs) > 1) group_zero_adjust = 1; if (!ext2fs_has_feature_meta_bg(fs->super) || (i < fs->super->s_first_meta_bg)) return group_block + i + 1 + group_zero_adjust; bg = EXT2_DESC_PER_BLOCK(fs->super) * i; if (ext2fs_bg_has_super(fs, bg)) has_super = 1; ret_blk = ext2fs_group_first_block2(fs, bg); /* * If group_block is not the normal value, we're trying to use * the backup group descriptors and superblock --- so use the * alternate location of the second block group in the * metablock group. Ideally we should be testing each bg * descriptor block individually for correctness, but we don't * have the infrastructure in place to do that. */ if (group_block != fs->super->s_first_data_block && ((ret_blk + has_super + fs->super->s_blocks_per_group) < ext2fs_blocks_count(fs->super))) { ret_blk += fs->super->s_blocks_per_group; /* * If we're going to jump forward a block group, make sure * that we adjust has_super to account for the next group's * backup superblock (or lack thereof). */ if (ext2fs_bg_has_super(fs, bg + 1)) has_super = 1; else has_super = 0; } return ret_blk + has_super + group_zero_adjust; }
/* * This function returns the location of the superblock, block group * descriptors for a given block group. It currently returns the * number of free blocks assuming that inode table and allocation * bitmaps will be in the group. This is not necessarily the case * when the flex_bg feature is enabled, so callers should take care! * It was only really intended for use by mke2fs, and even there it's * not that useful. * * The ext2fs_super_and_bgd_loc2() function is 64-bit block number * capable and returns the number of blocks used by super block and * group descriptors. */ int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, blk_t *ret_old_desc_blk, blk_t *ret_new_desc_blk, int *ret_meta_bg) { blk64_t ret_super_blk2; blk64_t ret_old_desc_blk2; blk64_t ret_new_desc_blk2; blk_t ret_used_blks; blk_t numblocks; unsigned int meta_bg_size; ext2fs_super_and_bgd_loc2(fs, group, &ret_super_blk2, &ret_old_desc_blk2, &ret_new_desc_blk2, &ret_used_blks); if (group == fs->group_desc_count-1) { numblocks = (ext2fs_blocks_count(fs->super) - (blk64_t) fs->super->s_first_data_block) % (blk64_t) fs->super->s_blocks_per_group; if (!numblocks) numblocks = fs->super->s_blocks_per_group; } else numblocks = fs->super->s_blocks_per_group; if (ret_super_blk) *ret_super_blk = (blk_t)ret_super_blk2; if (ret_old_desc_blk) *ret_old_desc_blk = (blk_t)ret_old_desc_blk2; if (ret_new_desc_blk) *ret_new_desc_blk = (blk_t)ret_new_desc_blk2; if (ret_meta_bg) { meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); *ret_meta_bg = group / meta_bg_size; } numblocks -= 2 + fs->inode_blocks_per_group + ret_used_blks; return numblocks; }
/* * This function is called to deallocate a block, and is an interator * functioned called by deallocate inode via ext2fs_iterate_block(). */ static int deallocate_inode_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct del_block *p = priv_data; if (HOLE_BLKADDR(*block_nr)) return 0; if ((*block_nr < fs->super->s_first_data_block) || (*block_nr >= ext2fs_blocks_count(fs->super))) return 0; ext2fs_unmark_block_bitmap2(p->ctx->block_found_map, *block_nr); ext2fs_block_alloc_stats2(fs, *block_nr, -1); p->num++; return 0; }
static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct read_bb_record *rb = (struct read_bb_record *) priv_data; if (blockcnt < 0) return 0; if ((*block_nr < fs->super->s_first_data_block) || (*block_nr >= ext2fs_blocks_count(fs->super))) return 0; /* Ignore illegal blocks */ rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); if (rb->err) return BLOCK_ABORT; return 0; }
static int process_journal_block(ext2_filsys fs, blk64_t *block_nr, e2_blkcnt_t blockcnt, blk64_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct process_block_struct *p; blk64_t blk = *block_nr; p = (struct process_block_struct *) priv_data; if (!blk || blk < fs->super->s_first_data_block || blk >= ext2fs_blocks_count(fs->super)) return BLOCK_ABORT; if (blockcnt >= 0) p->last_block = blockcnt; return 0; }
/* * Reads a list of bad blocks from a FILE * */ errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void *priv_data, void (*invalid)(ext2_filsys fs, blk_t blk, char *badstr, void *priv_data)) { errcode_t retval; blk_t blockno; int count; char buf[128]; if (fs) EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!*bb_list) { retval = ext2fs_badblocks_list_create(bb_list, 10); if (retval) return retval; } while (!feof (f)) { if (fgets(buf, sizeof(buf), f) == NULL) break; count = sscanf(buf, "%u", &blockno); if (count <= 0) continue; if (fs && ((blockno < fs->super->s_first_data_block) || (blockno >= ext2fs_blocks_count(fs->super)))) { if (invalid) (invalid)(fs, blockno, buf, priv_data); continue; } retval = ext2fs_badblocks_list_add(*bb_list, blockno); if (retval) return retval; } return 0; }
/* * This function reserves the superblock and block group descriptors * for a given block group. It currently returns the number of free * blocks assuming that inode table and allocation bitmaps will be in * the group. This is not necessarily the case when the flex_bg * feature is enabled, so callers should take care! It was only * really intended for use by mke2fs, and even there it's not that * useful. In the future, when we redo this function for 64-bit block * numbers, we should probably return the number of blocks used by the * super block and group descriptors instead. * * See also the comment for ext2fs_super_and_bgd_loc() */ int ext2fs_reserve_super_and_bgd(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap) { blk64_t super_blk, old_desc_blk, new_desc_blk; blk_t used_blks; int j, old_desc_blocks, num_blocks; ext2fs_super_and_bgd_loc2(fs, group, &super_blk, &old_desc_blk, &new_desc_blk, &used_blks); 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 + fs->super->s_reserved_gdt_blocks; if (super_blk || (group == 0)) ext2fs_mark_block_bitmap2(bmap, super_blk); if ((group == 0) && (fs->blocksize == 1024) && EXT2FS_CLUSTER_RATIO(fs) > 1) ext2fs_mark_block_bitmap2(bmap, 0); if (old_desc_blk) { if (fs->super->s_reserved_gdt_blocks && fs->block_map == bmap) ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT); for (j=0; j < old_desc_blocks; j++) if (old_desc_blk + j < ext2fs_blocks_count(fs->super)) ext2fs_mark_block_bitmap2(bmap, old_desc_blk + j); } if (new_desc_blk) ext2fs_mark_block_bitmap2(bmap, new_desc_blk); num_blocks = ext2fs_group_blocks_count(fs, group); num_blocks -= 2 + fs->inode_blocks_per_group + used_blks; return num_blocks ; }
static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) { dgrp_t i; unsigned int j; int block_nbytes, inode_nbytes; unsigned int nbits; errcode_t retval; char *block_buf = NULL, *inode_buf = NULL; int csum_flag = 0; blk64_t blk; blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); ext2_ino_t ino_itr = 1; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; inode_nbytes = block_nbytes = 0; if (do_block) { block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; retval = io_channel_alloc_buf(fs->io, 0, &block_buf); if (retval) goto errout; memset(block_buf, 0xff, fs->blocksize); } if (do_inode) { inode_nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); retval = io_channel_alloc_buf(fs->io, 0, &inode_buf); if (retval) goto errout; memset(inode_buf, 0xff, fs->blocksize); } for (i = 0; i < fs->group_desc_count; i++) { if (!do_block) goto skip_block_bitmap; if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) ) goto skip_this_block_bitmap; retval = ext2fs_get_block_bitmap_range2(fs->block_map, blk_itr, block_nbytes << 3, block_buf); if (retval) goto errout; if (i == fs->group_desc_count - 1) { /* Force bitmap padding for the last group */ nbits = EXT2FS_NUM_B2C(fs, ((ext2fs_blocks_count(fs->super) - (__u64) fs->super->s_first_data_block) % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super))); if (nbits) for (j = nbits; j < fs->blocksize * 8; j++) ext2fs_set_bit(j, block_buf); } blk = ext2fs_block_bitmap_loc(fs, i); if (blk) { retval = io_channel_write_blk64(fs->io, blk, 1, block_buf); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_WRITE; goto errout; } } skip_this_block_bitmap: blk_itr += block_nbytes << 3; skip_block_bitmap: if (!do_inode) continue; if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) ) goto skip_this_inode_bitmap; retval = ext2fs_get_inode_bitmap_range2(fs->inode_map, ino_itr, inode_nbytes << 3, inode_buf); if (retval) goto errout; blk = ext2fs_inode_bitmap_loc(fs, i); if (blk) { retval = io_channel_write_blk64(fs->io, blk, 1, inode_buf); if (retval) { retval = EXT2_ET_INODE_BITMAP_WRITE; goto errout; } } skip_this_inode_bitmap: ino_itr += inode_nbytes << 3; } if (do_block) { fs->flags &= ~EXT2_FLAG_BB_DIRTY; ext2fs_free_mem(&block_buf); } if (do_inode) { fs->flags &= ~EXT2_FLAG_IB_DIRTY; ext2fs_free_mem(&inode_buf); } return 0; errout: if (inode_buf) ext2fs_free_mem(&inode_buf); if (block_buf) ext2fs_free_mem(&block_buf); 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 < 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 = 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 retval = io_ptr->open(journal_name, IO_FLAG_RW | IO_FLAG_EXCLUSIVE, &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 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; 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 = ext2fs_blocks_count(&jsuper); 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; }
/* * This function creates a journal using direct I/O routines. */ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, blk_t num_blocks, int flags) { char *buf; dgrp_t group, start, end, i, log_flex; errcode_t retval; struct ext2_inode inode; unsigned long long inode_size; struct mkjournal_struct es; if ((retval = ext2fs_create_journal_superblock(fs, num_blocks, flags, &buf))) return retval; if ((retval = ext2fs_read_bitmaps(fs))) goto out2; if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) goto out2; if (inode.i_blocks > 0) { retval = EEXIST; goto out2; } es.num_blocks = num_blocks; es.newblocks = 0; es.buf = buf; es.err = 0; es.flags = flags; es.zero_count = 0; if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { inode.i_flags |= EXT4_EXTENTS_FL; if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) goto out2; } /* * Set the initial goal block to be roughly at the middle of * the filesystem. Pick a group that has the largest number * of free blocks. */ group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) - fs->super->s_first_data_block) / 2); log_flex = 1 << fs->super->s_log_groups_per_flex; if (fs->super->s_log_groups_per_flex && (group > log_flex)) { group = group & ~(log_flex - 1); while ((group < fs->group_desc_count) && ext2fs_bg_free_blocks_count(fs, group) == 0) group++; if (group == fs->group_desc_count) group = 0; start = group; } else start = (group > 0) ? group-1 : group; end = ((group+1) < fs->group_desc_count) ? group+1 : group; group = start; for (i=start+1; i <= end; i++) if (ext2fs_bg_free_blocks_count(fs, i) > ext2fs_bg_free_blocks_count(fs, group)) group = i; es.goal = ext2fs_group_first_block2(fs, group); retval = ext2fs_block_iterate3(fs, journal_ino, BLOCK_FLAG_APPEND, 0, mkjournal_proc, &es); if (es.err) { retval = es.err; goto errout; } if (es.zero_count) { retval = ext2fs_zero_blocks2(fs, es.blk_to_zero, es.zero_count, 0, 0); if (retval) goto errout; } if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) goto errout; inode_size = (unsigned long long)fs->blocksize * num_blocks; inode.i_size = inode_size & 0xFFFFFFFF; inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; if (ext2fs_needs_large_file_feature(inode_size)) fs->super->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); inode.i_links_count = 1; inode.i_mode = LINUX_S_IFREG | 0600; if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode))) goto errout; retval = 0; memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); fs->super->s_jnl_blocks[15] = inode.i_size_high; fs->super->s_jnl_blocks[16] = inode.i_size; fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; ext2fs_mark_super_dirty(fs); errout: ext2fs_zero_blocks2(0, 0, 0, 0, 0); out2: ext2fs_free_mem(&buf); return retval; }
extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir, ext2_ino_t ino, char *buf) { ext2_filsys fs = ctx->fs; struct ext2_inode inode; int inode_modified = 0; int not_fixed = 0; unsigned char *frag, *fsize; struct problem_context pctx; int problem = 0; e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode"); clear_problem_context(&pctx); pctx.ino = ino; pctx.dir = dir; pctx.inode = &inode; if (ext2fs_file_acl_block(fs, &inode) && !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) { ext2fs_file_acl_block_set(fs, &inode, 0); inode_modified++; } else not_fixed++; } if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) && !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) && !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) && !(LINUX_S_ISSOCK(inode.i_mode))) problem = PR_2_BAD_MODE; else if (LINUX_S_ISCHR(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_CHAR_DEV; else if (LINUX_S_ISBLK(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_BLOCK_DEV; else if (LINUX_S_ISFIFO(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_FIFO; else if (LINUX_S_ISSOCK(inode.i_mode) && !e2fsck_pass1_check_device_inode(fs, &inode)) problem = PR_2_BAD_SOCKET; else if (LINUX_S_ISLNK(inode.i_mode) && !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) { problem = PR_2_INVALID_SYMLINK; } if (problem) { if (fix_problem(ctx, problem, &pctx)) { deallocate_inode(ctx, ino, 0); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return 0; return 1; } else not_fixed++; problem = 0; } if (inode.i_faddr) { if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) { inode.i_faddr = 0; inode_modified++; } else not_fixed++; } switch (fs->super->s_creator_os) { case EXT2_OS_HURD: frag = &inode.osd2.hurd2.h_i_frag; fsize = &inode.osd2.hurd2.h_i_fsize; break; default: frag = fsize = 0; } if (frag && *frag) { pctx.num = *frag; if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) { *frag = 0; inode_modified++; } else not_fixed++; pctx.num = 0; } if (fsize && *fsize) { pctx.num = *fsize; if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) { *fsize = 0; inode_modified++; } else not_fixed++; pctx.num = 0; } if ((fs->super->s_creator_os == EXT2_OS_LINUX) && !(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && (inode.osd2.linux2.l_i_blocks_hi != 0)) { pctx.num = inode.osd2.linux2.l_i_blocks_hi; if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) { inode.osd2.linux2.l_i_blocks_hi = 0; inode_modified++; } } if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) && inode.osd2.linux2.l_i_file_acl_high != 0) { pctx.num = inode.osd2.linux2.l_i_file_acl_high; if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) { inode.osd2.linux2.l_i_file_acl_high = 0; inode_modified++; } else not_fixed++; } if (ext2fs_file_acl_block(fs, &inode) && ((ext2fs_file_acl_block(fs, &inode) < fs->super->s_first_data_block) || (ext2fs_file_acl_block(fs, &inode) >= ext2fs_blocks_count(fs->super)))) { if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) { ext2fs_file_acl_block_set(fs, &inode, 0); inode_modified++; } else not_fixed++; } if (inode.i_dir_acl && LINUX_S_ISDIR(inode.i_mode)) { if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) { inode.i_dir_acl = 0; inode_modified++; } else not_fixed++; } if (inode_modified) e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode"); if (!not_fixed && ctx->inode_bad_map) ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino); 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 * 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; }
int main (int argc, char ** argv) { errcode_t retval; ext2_filsys fs; int c; int flags = 0; int flush = 0; int force = 0; int io_flags = 0; int force_min_size = 0; int print_min_size = 0; int fd, ret; blk64_t new_size = 0; blk64_t max_size = 0; blk64_t min_size = 0; io_manager io_ptr; char *new_size_str = 0; int use_stride = -1; ext2fs_struct_stat st_buf; __s64 new_file_size; unsigned int sys_page_size = 4096; long sysval; int len, mount_flags; char *mtpt; #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); fprintf (stderr, "resize2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); if (argc && *argv) program_name = *argv; while ((c = getopt (argc, argv, "d:fFhMPpS:")) != EOF) { switch (c) { case 'h': usage(program_name); break; case 'f': force = 1; break; case 'F': flush = 1; break; case 'M': force_min_size = 1; break; case 'P': print_min_size = 1; break; case 'd': flags |= atoi(optarg); break; case 'p': flags |= RESIZE_PERCENT_COMPLETE; break; case 'S': use_stride = atoi(optarg); break; default: usage(program_name); } } if (optind == argc) usage(program_name); device_name = argv[optind++]; if (optind < argc) new_size_str = argv[optind++]; if (optind < argc) usage(program_name); io_options = strchr(device_name, '?'); if (io_options) *io_options++ = 0; /* * Figure out whether or not the device is mounted, and if it is * where it is mounted. */ len=80; while (1) { mtpt = malloc(len); if (!mtpt) return ENOMEM; mtpt[len-1] = 0; retval = ext2fs_check_mount_point(device_name, &mount_flags, mtpt, len); if (retval) { com_err("ext2fs_check_mount_point", retval, _("while determining whether %s is mounted."), device_name); exit(1); } if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0)) break; free(mtpt); len = 2 * len; } fd = ext2fs_open_file(device_name, O_RDWR, 0); if (fd < 0) { com_err("open", errno, _("while opening %s"), device_name); exit(1); } ret = ext2fs_fstat(fd, &st_buf); if (ret < 0) { com_err("open", errno, _("while getting stat information for %s"), device_name); exit(1); } if (flush) { retval = ext2fs_sync_device(fd, 1); if (retval) { com_err(argv[0], retval, _("while trying to flush %s"), device_name); exit(1); } } if (!S_ISREG(st_buf.st_mode )) { close(fd); fd = -1; } #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; if (!(mount_flags & EXT2_MF_MOUNTED)) io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; io_flags |= EXT2_FLAG_64BITS; retval = ext2fs_open2(device_name, io_options, io_flags, 0, 0, io_ptr, &fs); if (retval) { com_err (program_name, retval, _("while trying to open %s"), device_name); printf (_("Couldn't find valid filesystem superblock.\n")); exit (1); } /* * Check for compatibility with the feature sets. We need to * be more stringent than ext2fs_open(). */ if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) { com_err(program_name, EXT2_ET_UNSUPP_FEATURE, "(%s)", device_name); exit(1); } min_size = calculate_minimum_resize_size(fs); if (print_min_size) { if (!force && ((fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) { fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), device_name); exit(1); } printf(_("Estimated minimum size of the filesystem: %llu\n"), min_size); exit(0); } /* Determine the system page size if possible */ #ifdef HAVE_SYSCONF #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) #define _SC_PAGESIZE _SC_PAGE_SIZE #endif #ifdef _SC_PAGESIZE sysval = sysconf(_SC_PAGESIZE); if (sysval > 0) sys_page_size = sysval; #endif /* _SC_PAGESIZE */ #endif /* HAVE_SYSCONF */ /* * Get the size of the containing partition, and use this for * defaults and for making sure the new filesystem doesn't * exceed the partition size. */ retval = ext2fs_get_device_size2(device_name, fs->blocksize, &max_size); if (retval) { com_err(program_name, retval, _("while trying to determine filesystem size")); exit(1); } if (force_min_size) new_size = min_size; else if (new_size_str) { new_size = parse_num_blocks2(new_size_str, fs->super->s_log_block_size); if (new_size == 0) { com_err(program_name, 0, _("Invalid new size: %s\n"), new_size_str); exit(1); } } else { new_size = max_size; /* Round down to an even multiple of a pagesize */ if (sys_page_size > fs->blocksize) new_size &= ~((sys_page_size / fs->blocksize)-1); } if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_64BIT)) { /* Take 16T down to 2^32-1 blocks */ if (new_size == (1ULL << 32)) new_size--; else if (new_size > (1ULL << 32)) { com_err(program_name, 0, _("New size too large to be " "expressed in 32 bits\n")); exit(1); } } if (!force && new_size < min_size) { com_err(program_name, 0, _("New size smaller than minimum (%llu)\n"), min_size); exit(1); } if (use_stride >= 0) { if (use_stride >= (int) fs->super->s_blocks_per_group) { com_err(program_name, 0, _("Invalid stride length")); exit(1); } fs->stride = fs->super->s_raid_stride = use_stride; ext2fs_mark_super_dirty(fs); } else determine_fs_stride(fs); /* * If we are resizing a plain file, and it's not big enough, * automatically extend it in a sparse fashion by writing the * last requested block. */ new_file_size = ((__u64) new_size) * fs->blocksize; if ((__u64) new_file_size > (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1) fd = -1; if ((new_file_size > st_buf.st_size) && (fd > 0)) { if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) && (write(fd, "0", 1) == 1)) max_size = new_size; } if (!force && (new_size > max_size)) { fprintf(stderr, _("The containing partition (or device)" " is only %llu (%dk) blocks.\nYou requested a new size" " of %llu blocks.\n\n"), max_size, fs->blocksize / 1024, new_size); exit(1); } if (new_size == ext2fs_blocks_count(fs->super)) { fprintf(stderr, _("The filesystem is already %llu blocks " "long. Nothing to do!\n\n"), new_size); exit(0); } if (mount_flags & EXT2_MF_MOUNTED) { retval = online_resize_fs(fs, mtpt, &new_size, flags); } else { if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) || (fs->super->s_state & EXT2_ERROR_FS) || ((fs->super->s_state & EXT2_VALID_FS) == 0))) { fprintf(stderr, _("Please run 'e2fsck -f %s' first.\n\n"), device_name); exit(1); } /* * XXXX The combination of flex_bg and !resize_inode * causes major problems for resize2fs, since when the * group descriptors grow in size this can potentially * require multiple inode tables to be moved aside to * make room, and resize2fs chokes rather badly in * this scenario. It's a rare combination, except * when a filesystem is expanded more than a certain * size, so for now, we'll just prohibit that * combination. This is something we should fix * eventually, though. */ if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) && !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)) { com_err(program_name, 0, _("%s: The combination of " "flex_bg and\n\t!resize_inode features " "is not supported by resize2fs.\n"), device_name); exit(1); } printf(_("Resizing the filesystem on " "%s to %llu (%dk) blocks.\n"), device_name, new_size, fs->blocksize / 1024); retval = resize_fs(fs, &new_size, flags, ((flags & RESIZE_PERCENT_COMPLETE) ? resize_progress_func : 0)); } free(mtpt); if (retval) { com_err(program_name, retval, _("while trying to resize %s"), device_name); fprintf(stderr, _("Please run 'e2fsck -fy %s' to fix the filesystem\n" "after the aborted resize operation.\n"), device_name); ext2fs_close(fs); exit(1); } printf(_("The filesystem on %s is now %llu blocks long.\n\n"), device_name, new_size); if ((st_buf.st_size > new_file_size) && (fd > 0)) { #ifdef HAVE_FTRUNCATE64 retval = ftruncate64(fd, new_file_size); #else retval = 0; /* Only truncate if new_file_size doesn't overflow off_t */ if (((off_t) new_file_size) == new_file_size) retval = ftruncate(fd, (off_t) new_file_size); #endif if (retval) com_err(program_name, retval, _("while trying to truncate %s"), device_name); } if (fd > 0) close(fd); remove_error_table(&et_ext2_error_table); return (0); }
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; 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); if (retval) return; jsb = (journal_superblock_t *) buf; sb = (struct ext2_super_block *) (buf+1024); #ifdef WORDS_BIGENDIAN 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) && ext2fs_has_feature_journal_dev(sb)) { 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 %lu\n", (unsigned long) ext2fs_blocks_count(sb)); } } /* Next, read the journal superblock */ retval = read_journal_block(cmdname, source, ((ext2_loff_t) blocknr) * blocksize, jsb_buffer, 1024); 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. */ if (!dump_old) return; else blocknr = 1; } while (1) { retval = read_journal_block(cmdname, source, ((ext2_loff_t) blocknr) * blocksize, buf, blocksize); if (retval) 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); if (!dump_old) 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; } } }