e2fsprogs/resize/ext2_inode_move.c

301 lines
6.3 KiB
C

/*
* ext2_inode_move.c --- ext2resizer inode mover
*
* Copyright (C) 1997 Theodore Ts'o
*
* %Begin-Header%
* All rights reserved.
* %End-Header%
*/
#include "resize2fs.h"
/*
* Progress callback
*/
struct callback_info {
ext2_sim_progmeter progress;
int offset;
};
static errcode_t progress_callback(ext2_filsys fs, ext2_inode_scan scan,
dgrp_t group, void * private)
{
struct callback_info *cb = private;
if (!cb->progress)
return 0;
ext2fs_progress_update(cb->progress, group - cb->offset + 1);
return 0;
}
struct istruct {
ext2_sim_progmeter progress;
ext2_extent imap;
int flags;
int num;
};
static int check_and_change_inodes(ino_t dir, int entry,
struct ext2_dir_entry *dirent, int offset,
int blocksize, char *buf, void *private)
{
struct istruct *is = private;
ino_t new;
if (is->progress && offset == 0) {
ext2fs_progress_update(is->progress, ++is->num);
}
if (!dirent->inode)
return 0;
new = ext2fs_extent_translate(is->imap, dirent->inode);
if (!new)
return 0;
if (is->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode translate (dir=%ld, name=%.*s, %u->%ld)\n",
dir, dirent->name_len, dirent->name,
dirent->inode, new);
dirent->inode = new;
return DIRENT_CHANGED;
}
/*
* Function to obtain the dblist information (if we didn't get it
* earlier)
*/
struct process_block_struct {
ino_t ino;
struct ext2_inode * inode;
errcode_t error;
};
static int process_block(ext2_filsys fs, blk_t *block_nr,
int blockcnt, blk_t ref_block,
int ref_offset, void *private)
{
struct process_block_struct *pb = private;
errcode_t retval;
int ret = 0;
retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
*block_nr, blockcnt);
if (retval) {
pb->error = retval;
ret |= BLOCK_ABORT;
}
return ret;
}
static errcode_t get_dblist(ext2_filsys fs, int flags)
{
ext2_inode_scan scan = 0;
errcode_t retval;
char *block_buf;
struct process_block_struct pb;
ext2_sim_progmeter progress = 0;
ino_t ino;
struct ext2_inode inode;
retval = ext2fs_open_inode_scan(fs, 0, &scan);
if (retval) goto errout;
pb.error = 0;
block_buf = malloc(fs->blocksize * 3);
if (!block_buf) {
retval = ENOMEM;
goto errout;
}
/*
* We're going to initialize the dblist while we're at it.
*/
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) goto errout;
if (flags & RESIZE_PERCENT_COMPLETE) {
retval = ext2fs_progress_init(&progress,
"Finding directories", 30, 40,
fs->super->s_inodes_count, 0);
if (retval)
return retval;
}
while (ino) {
if ((inode.i_links_count == 0) ||
!ext2fs_inode_has_valid_blocks(&inode) ||
!LINUX_S_ISDIR(inode.i_mode))
goto next;
pb.ino = ino;
pb.inode = &inode;
retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
process_block, &pb);
if (retval)
goto errout;
if (pb.error) {
retval = pb.error;
goto errout;
}
next:
if (progress)
ext2fs_progress_update(progress, ino);
retval = ext2fs_get_next_inode(scan, &ino, &inode);
if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
goto next;
}
retval = 0;
errout:
if (progress)
ext2fs_progress_close(progress);
if (scan)
ext2fs_close_inode_scan(scan);
return retval;
}
errcode_t ext2fs_inode_move(ext2_resize_t rfs)
{
ino_t ino, new;
struct ext2_inode inode;
ext2_inode_scan scan = NULL;
ext2_extent imap;
errcode_t retval;
int group;
struct istruct is;
struct callback_info callback_info;
ext2_sim_progmeter progress = 0;
if (rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count)
return 0;
retval = ext2fs_create_extent_table(&imap, 0);
if (retval)
return retval;
retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
if (retval) goto errout;
retval = ext2fs_inode_scan_goto_blockgroup(scan,
rfs->new_fs->group_desc_count);
if (retval) goto errout;
if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
callback_info.offset = rfs->new_fs->group_desc_count;
group = (rfs->old_fs->group_desc_count -
rfs->new_fs->group_desc_count);
retval = ext2fs_progress_init(&progress,
"Moving inodes", 30, 40, group, 0);
if (retval)
return retval;
ext2fs_set_inode_callback(scan, progress_callback,
&callback_info);
}
callback_info.progress = progress;
new = EXT2_FIRST_INODE(rfs->new_fs->super);
/*
* First, copy all of the inodes that need to be moved
* elsewhere in the inode table
*/
while (1) {
retval = ext2fs_get_next_inode(scan, &ino, &inode);
if (retval) goto errout;
if (!ino)
break;
if (!ext2fs_test_inode_bitmap(rfs->old_fs->inode_map, ino))
continue;
/*
* Find a new inode
*/
while (1) {
if (!ext2fs_test_inode_bitmap(rfs->new_fs->inode_map,
new))
break;
new++;
if (new > rfs->new_fs->super->s_inodes_count) {
retval = ENOSPC;
goto errout;
}
}
ext2fs_mark_inode_bitmap(rfs->new_fs->inode_map, new);
retval = ext2fs_write_inode(rfs->old_fs, new, &inode);
if (retval) goto errout;
group = (new-1) / EXT2_INODES_PER_GROUP(rfs->new_fs->super);
if (LINUX_S_ISDIR(inode.i_mode))
rfs->new_fs->group_desc[group].bg_used_dirs_count++;
if (rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode moved %ld->%ld\n", ino, new);
ext2fs_add_extent_entry(imap, ino, new);
}
io_channel_flush(rfs->new_fs->io);
if (progress) {
ext2fs_progress_close(progress);
progress = 0;
}
/*
* Get the list of directory blocks, if necessary
*/
if (!rfs->old_fs->dblist) {
retval = get_dblist(rfs->old_fs, rfs->flags);
if (retval) goto errout;
}
/*
* Now, we iterate over all of the directories to update the
* inode references
*/
if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
retval = ext2fs_progress_init(&progress,
"Updating inode references", 30, 40,
ext2fs_dblist_count(rfs->old_fs->dblist), 0);
if (retval)
return retval;
}
is.imap = imap;
is.flags = rfs->flags;
is.num = 0;
is.progress = progress;
retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
DIRENT_FLAG_INCLUDE_EMPTY, 0,
check_and_change_inodes, &is);
/* if (retval) goto errout; */
errout:
if (progress)
ext2fs_progress_close(progress);
ext2fs_free_extent_table(imap);
if (scan)
ext2fs_close_inode_scan(scan);
return retval;
}