Add bmove.c as a basis for block moving during inode table grow

master
Vitaliy Filippov 2014-01-04 20:29:32 +00:00
parent 7ba9912c52
commit 9f543c9ca1
4 changed files with 397 additions and 12 deletions

View File

@ -1,3 +1,3 @@
all: realloc-inodes
realloc-inodes: realloc-inodes.c
gcc -Wall -o realloc-inodes -lcom_err -lext2fs realloc-inodes.c
realloc-inodes: realloc-inodes.c bmove.c ext2fsP.h
gcc -Wall -o realloc-inodes -lcom_err -lext2fs realloc-inodes.c bmove.c

166
bmove.c Normal file
View File

@ -0,0 +1,166 @@
/*
* bmove.c --- Move blocks around to make way for a particular
* filesystem structure.
*
* Copyright (C) 1997 Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Library
* General Public License, version 2.
* %End-Header%
*/
#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <ext2fs/ext2_fs.h>
#include "ext2fsP.h"
struct process_block_struct {
ext2_ino_t ino;
struct ext2_inode * inode;
ext2fs_block_bitmap reserve;
ext2fs_block_bitmap alloc_map;
errcode_t error;
char *buf;
int add_dir;
int flags;
};
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;
}
errcode_t ext2fs_move_blocks(ext2_filsys fs,
ext2fs_block_bitmap reserve,
ext2fs_block_bitmap alloc_map,
int flags)
{
ext2_ino_t ino;
struct ext2_inode inode;
errcode_t retval;
struct process_block_struct pb;
ext2_inode_scan scan;
char *block_buf;
retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval)
return retval;
pb.reserve = reserve;
pb.error = 0;
pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
pb.flags = flags;
retval = ext2fs_get_array(4, fs->blocksize, &block_buf);
if (retval)
return retval;
pb.buf = block_buf + fs->blocksize * 3;
/*
* If GET_DBLIST is set in the flags field, then we should
* gather directory block information while we're doing the
* block move.
*/
if (flags & EXT2_BMOVE_GET_DBLIST) {
if (fs->dblist) {
ext2fs_free_dblist(fs->dblist);
fs->dblist = NULL;
}
retval = ext2fs_init_dblist(fs, 0);
if (retval)
return retval;
}
retval = ext2fs_get_next_inode(scan, &ino, &inode);
if (retval)
return retval;
while (ino) {
if ((inode.i_links_count == 0) ||
!ext2fs_inode_has_valid_blocks2(fs, &inode))
goto next;
pb.ino = ino;
pb.inode = &inode;
pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
flags & EXT2_BMOVE_GET_DBLIST);
retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
process_block, &pb);
if (retval)
return retval;
if (pb.error)
return pb.error;
next:
retval = ext2fs_get_next_inode(scan, &ino, &inode);
if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
goto next;
}
return 0;
}

146
ext2fsP.h Normal file
View File

@ -0,0 +1,146 @@
/*
* ext2fsP.h --- private header file for ext2 library
*
* Copyright (C) 1997 Theodore Ts'o.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Library
* General Public License, version 2.
* %End-Header%
*/
#include <ext2fs/ext2fs.h>
#define EXT2FS_MAX_NESTED_LINKS 8
/*
* Badblocks list
*/
struct ext2_struct_u32_list {
int magic;
int num;
int size;
__u32 *list;
int badblocks_flags;
};
struct ext2_struct_u32_iterate {
int magic;
ext2_u32_list bb;
int ptr;
};
/*
* Directory block iterator definition
*/
struct ext2_struct_dblist {
int magic;
ext2_filsys fs;
unsigned long long size;
unsigned long long count;
int sorted;
struct ext2_db_entry2 * list;
};
/*
* For directory iterators
*/
struct dir_context {
ext2_ino_t dir;
int flags;
char *buf;
int (*func)(ext2_ino_t dir,
int entry,
struct ext2_dir_entry *dirent,
int offset,
int blocksize,
char *buf,
void *priv_data);
void *priv_data;
errcode_t errcode;
};
/*
* Inode cache structure
*/
struct ext2_inode_cache {
void * buffer;
blk_t buffer_blk;
int cache_last;
int cache_size;
int refcount;
struct ext2_inode_cache_ent *cache;
};
struct ext2_inode_cache_ent {
ext2_ino_t ino;
struct ext2_inode inode;
};
/* Function prototypes */
extern int ext2fs_process_dir_block(ext2_filsys fs,
blk64_t *blocknr,
e2_blkcnt_t blockcnt,
blk64_t ref_block,
int ref_offset,
void *priv_data);
/* Generic numeric progress meter */
struct ext2fs_numeric_progress_struct {
__u64 max;
int log_max;
int skip_progress;
};
extern void ext2fs_numeric_progress_init(ext2_filsys fs,
struct ext2fs_numeric_progress_struct * progress,
const char *label, __u64 max);
extern void ext2fs_numeric_progress_update(ext2_filsys fs,
struct ext2fs_numeric_progress_struct * progress,
__u64 val);
extern void ext2fs_numeric_progress_close(ext2_filsys fs,
struct ext2fs_numeric_progress_struct * progress,
const char *message);
/*
* 64-bit bitmap support
*/
extern errcode_t ext2fs_alloc_generic_bmap(ext2_filsys fs, errcode_t magic,
int type, __u64 start, __u64 end,
__u64 real_end,
const char * description,
ext2fs_generic_bitmap *bmap);
extern void ext2fs_free_generic_bmap(ext2fs_generic_bitmap bmap);
extern errcode_t ext2fs_copy_generic_bmap(ext2fs_generic_bitmap src,
ext2fs_generic_bitmap *dest);
extern errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap bmap,
__u64 new_end,
__u64 new_real_end);
extern errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap bitmap,
errcode_t neq,
__u64 end, __u64 *oend);
extern int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 arg);
extern int ext2fs_unmark_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 arg);
extern int ext2fs_test_generic_bmap(ext2fs_generic_bitmap bitmap,
__u64 arg);
extern errcode_t ext2fs_set_generic_bmap_range(ext2fs_generic_bitmap bitmap,
__u64 start, unsigned int num,
void *in);
extern errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap bitmap,
__u64 start, unsigned int num,
void *out);
extern void ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap,const char *func);
extern int ext2fs_mem_is_zero(const char *mem, size_t len);
#define EXT2_BMOVE_GET_DBLIST 0x0001
#define EXT2_BMOVE_DEBUG 0x0002

View File

@ -8,15 +8,13 @@
* 1) If shrinking - move inodes away from the end of each block group inode table
* 1.1) move each inode to the new place, mark new place as occupied, unmark old one
* 1.2) remember the old->new inode number mapping
* 2) If growing - move data away from extra blocks needed by growing inode tables
* 2.1) Iterate through all block groups and remember extra blocks needed for
* inode tables that are occupied.
* 2) If growing - move data away from extra blocks needed by growing inode tables:
* 2.1) Create a map of blocks that we want to free
* 2.2) Iterate through all inodes and move remembered blocks.
* It involves overwriting the whole file extent tree or block mapping...
* If some of these blocks are in the bad block inode, we should either
* abort the reallocation process, or move inode tables to another location
* in a block group, possibly first defragmenting it... :-(
* 2.3) While copying, remember block number mapping and mark/unmark new/old blocks.
* 3) Change all inode numbers in directory entries according to mappings from (1.2),
* and then using a formula: new_num = 1 + ((old_num-1)/old_i_per_g)*new_i_per_g + ((old_num-1) % old_i_per_g)
* 4) Move parts of inode tables so they are consecutive again if flex_bg feature is active
@ -45,6 +43,11 @@
#define _(a) (a)
errcode_t ext2fs_move_blocks(ext2_filsys fs,
ext2fs_block_bitmap reserve,
ext2fs_block_bitmap alloc_map,
int flags);
// "local data" for the inode reallocation process
typedef struct
{
@ -280,9 +283,75 @@ out:
*/
int extend_move_blocks(realloc_data *rd)
{
// we'll probably need ext2fs_block_alloc_stats2(fs, new_blk, +1);
return ENOSYS;
ext2fs_block_bitmap reserve_map;
blk64_t it_start, blk_diff, b_per_g;
dgrp_t grp, n_flex, n_grp, flex_count;
int retval, flexbg_size;
if (rd->new_inode_blocks_per_group == rd->fs->inode_blocks_per_group)
{
return 0;
}
blk_diff = rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group;
b_per_g = EXT2_BLOCKS_PER_GROUP(rd->fs->super);
retval = ext2fs_allocate_block_bitmap(rd->fs, "reserved block map", &reserve_map);
if (retval)
{
return retval;
}
if (!rd->fs->block_map)
{
ext2fs_read_block_bitmap(rd->fs);
}
// Mark reserved blocks (those we want to free)
if (EXT2_HAS_INCOMPAT_FEATURE(rd->fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG)
&& rd->fs->super->s_log_groups_per_flex)
{
// flex_bg
flexbg_size = 1 << rd->fs->super->s_log_groups_per_flex;
flex_count = (rd->fs->group_desc_count + flexbg_size - 1) / flexbg_size;
for (n_flex = 0; n_flex < flex_count; n_flex++)
{
n_grp = flexbg_size;
if (n_flex*flexbg_size+n_grp > rd->fs->group_desc_count)
{
n_grp = rd->fs->group_desc_count-n_flex*flexbg_size;
}
it_start = ext2fs_inode_table_loc(rd->fs, n_flex*flexbg_size);
// Check group boundaries
if ((it_start + rd->new_inode_blocks_per_group*n_grp - 1) / b_per_g
!= (it_start + rd->fs->inode_blocks_per_group*n_grp - 1) / b_per_g)
{
retval = ENOSPC;
goto out;
}
it_start += rd->fs->inode_blocks_per_group*n_grp;
ext2fs_mark_block_bitmap_range2(reserve_map, it_start, blk_diff*n_grp);
}
}
else
{
// No flex_bg
for (grp = 0; grp < rd->fs->group_desc_count; grp++)
{
it_start = ext2fs_inode_table_loc(rd->fs, grp);
// Check group boundaries
if ((it_start + rd->new_inode_blocks_per_group - 1) / b_per_g
!= (it_start + rd->fs->inode_blocks_per_group - 1) / b_per_g)
{
retval = ENOSPC;
goto out;
}
it_start += rd->fs->inode_blocks_per_group;
ext2fs_mark_block_bitmap_range2(reserve_map, it_start, rd->new_inode_blocks_per_group-rd->fs->inode_blocks_per_group);
}
}
// TODO Check the bad block inode
retval = ext2fs_move_blocks(rd->fs, reserve_map, rd->fs->block_map, 2);
ext2fs_mark_bb_dirty(rd->fs);
ext2fs_flush(rd->fs);
out:
ext2fs_free_block_bitmap(reserve_map);
return retval;
}
static int change_inode_numbers_callback(ext2_ino_t dir, int entry,
@ -327,13 +396,16 @@ int change_inode_numbers(realloc_data *rd)
int change_super_and_bgd(realloc_data *rd)
{
blk64_t it_start, blk;
dgrp_t grp, n_flex, n_grp;
dgrp_t grp, n_flex, n_grp, flex_count;
__u32 unus;
__u32 i_per_g_diff = rd->new_inodes_per_group - EXT2_INODES_PER_GROUP(rd->fs->super);
int flexbg_size = 0, i, retval = 0;
void *buf = NULL;
ext2fs_flush(rd->fs);
ext2fs_read_block_bitmap(rd->fs);
if (!rd->fs->block_map)
{
ext2fs_read_block_bitmap(rd->fs);
}
if (rd->new_inode_blocks_per_group != rd->fs->inode_blocks_per_group)
{
// Move inode tables if flex_bg is active
@ -341,12 +413,13 @@ int change_super_and_bgd(realloc_data *rd)
&& rd->fs->super->s_log_groups_per_flex)
{
flexbg_size = 1 << rd->fs->super->s_log_groups_per_flex;
flex_count = (rd->fs->group_desc_count + flexbg_size - 1) / flexbg_size;
retval = ext2fs_get_mem(EXT2_BLOCK_SIZE(rd->fs->super) * rd->new_inode_blocks_per_group * flexbg_size, &buf);
if (retval)
{
goto out;
}
for (n_flex = 0; n_flex < rd->fs->group_desc_count/flexbg_size; n_flex++)
for (n_flex = 0; n_flex < flex_count; n_flex++)
{
n_grp = flexbg_size;
if (n_flex*flexbg_size+n_grp > rd->fs->group_desc_count)