mirror of https://github.com/vitalif/e2fsprogs
301 lines
6.3 KiB
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;
|
|
}
|
|
|