Compare commits

...

13 Commits

Author SHA1 Message Date
Vitaliy Filippov 6e4fa6e281 Fix ea remapping 2016-09-29 01:51:04 +03:00
Vitaliy Filippov b331d80fba Fix itable_unused adjustment - fixes shrink FS + shrink inode tables and some other cases 2016-09-27 23:53:58 +03:00
Vitaliy Filippov f99c7a2e12 One way to fix non-contiguous inode table allocations... 2016-09-26 00:02:00 +03:00
Vitaliy Filippov b9b963d427 Fix the rest of block bitmap differences 2016-09-26 00:02:00 +03:00
Vitaliy Filippov f58c260dc4 Remap blocks before moving inode tables (fixes big flex_bg extend),
check blocks to move even when shrinking inode tables (fixes big flex_bg shrink),
mark inode tables during allocation (fixes bigalloc shrink)
2016-09-25 23:48:01 +03:00
Vitaliy Filippov 152890aa45 Split inode_scan_and_fix into two parts.
- Basic extend and shrink works
- Extend on bigalloc fs works
- Shrink on bigalloc fs gives errors
- Extend on big flex_bg gives errors
2016-09-25 23:48:01 +03:00
Vitaliy Filippov 63039f4bac Implement inode table resizing (not done yet, should split inode_scan_and_fix()) 2016-09-25 23:47:54 +03:00
Vitaliy Filippov 7e0136332c Add patch_io manager support to resize2fs and e2fsck 2016-09-25 23:47:26 +03:00
Vitaliy Filippov b3782b2e5b Add e2patch utility 2016-09-16 11:54:35 +03:00
Vitaliy Filippov 460c0af190 Add patch_io manager 2016-09-16 11:38:14 +03:00
Vitaliy Filippov 2051d63a83 misc: Return error if file is too big for FIBMAP 2016-09-16 11:02:45 +03:00
Theodore Ts'o e703ba4b42 Fix reversed FORCE_NATIVE_MAKE test in acinclude.m4
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2016-09-11 23:12:07 -04:00
Theodore Ts'o d6cad379eb libext2fs: fix unaligned, multiblock writes in the unix_io handler
The read-modify-write code for the unaligned fallback code wasn't
working for multi-block writes.  This was unmasked by FreeBSD 11-rc2,
since its malloc() is returning unaligned memory regions for large
memory regions.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2016-09-11 00:25:48 -04:00
23 changed files with 1496 additions and 143 deletions

2
.gitignore vendored
View File

@ -161,6 +161,8 @@ misc/e2initrd_helper
misc/e2label.8
misc/e2undo
misc/e2undo.8
misc/e2patch
misc/e2patch.8
misc/e4defrag
misc/e4defrag.8
misc/ext4.5

View File

@ -108,7 +108,7 @@ AC_DEFUN(
[CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command,
_cv_gnu_make_command='' ;
dnl Search all the common names for GNU make
if test -z "$FORCE_NATIVE_MAKE" ; then
if test -n "$FORCE_NATIVE_MAKE" ; then
MAKES="make"
else
MAKES="make gmake gnumake"

2
configure vendored
View File

@ -11515,7 +11515,7 @@ if ${_cv_gnu_make_command+:} false; then :
$as_echo_n "(cached) " >&6
else
_cv_gnu_make_command='' ;
if test -z "$FORCE_NATIVE_MAKE" ; then
if test -n "$FORCE_NATIVE_MAKE" ; then
MAKES="make"
else
MAKES="make gmake gnumake"

View File

@ -345,6 +345,12 @@ or
.B \-p
options.
.TP
.BI \-T " patch_file"
Do not apply changes to the real filesystem; write updates into a sparse file
named 'patch_file' to safely apply them later with e2patch(8) utility.
You'll also be able to create a backup before applying patch and safely
restore it in case of power outage or system crash.
.TP
.BI \-z " undo_file"
Before overwriting a file system block, write the old contents of the block to
an undo file. This undo file can be used with e2undo(8) to restore the old

View File

@ -391,6 +391,9 @@ struct e2fsck_struct {
*/
ext2fs_inode_bitmap inodes_to_rebuild;
/* Patch file */
char *patch_file;
/* Undo file */
char *undo_file;
};

View File

@ -76,7 +76,7 @@ static void usage(e2fsck_t ctx)
fprintf(stderr,
_("Usage: %s [-panyrcdfktvDFV] [-b superblock] [-B blocksize]\n"
"\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n"
"\t\t[-E extended-options] [-z undo_file] device\n"),
"\t\t[-E extended-options] [-T patch_file] [-z undo_file] device\n"),
ctx->program_name);
fprintf(stderr, "%s", _("\nEmergency help:\n"
@ -92,6 +92,7 @@ static void usage(e2fsck_t ctx)
" -j external_journal Set location of the external journal\n"
" -l bad_blocks_file Add to badblocks list\n"
" -L bad_blocks_file Set badblocks list\n"
" -T patch_file Create a patch file instead of applying changes to real FS\n"
" -z undo_file Create an undo file\n"
));
@ -804,7 +805,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
phys_mem_kb = get_memory_size() / 1024;
ctx->readahead_kb = ~0ULL;
while ((c = getopt(argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkz:")) != EOF)
while ((c = getopt(argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDkT:z:")) != EOF)
switch (c) {
case 'C':
ctx->progress = e2fsck_update_progress;
@ -936,6 +937,9 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
case 'k':
keep_bad_blocks++;
break;
case 'T':
ctx->patch_file = optarg;
break;
case 'z':
ctx->undo_file = optarg;
break;
@ -1233,6 +1237,19 @@ check_error:
return retval;
}
static int e2fsck_setup_patch(e2fsck_t ctx, io_manager *io_ptr)
{
set_patch_io_backing_manager(*io_ptr);
set_patch_io_patch_file(ctx->patch_file);
*io_ptr = patch_io_manager;
printf(_("To make backup before applying changes run:\n"
" e2patch backup %s %s %s.backup\n"
"Then, to apply the operation to the real filesystem run:\n"
" e2patch apply %s %s\n"),
ctx->device_name, ctx->patch_file, ctx->patch_file, ctx->device_name, ctx->patch_file);
return 0;
}
static int e2fsck_setup_tdb(e2fsck_t ctx, io_manager *io_ptr)
{
errcode_t retval = ENOMEM;
@ -1430,7 +1447,11 @@ restart:
flags &= ~EXT2_FLAG_EXCLUSIVE;
}
if (ctx->undo_file) {
if (ctx->patch_file) {
retval = e2fsck_setup_patch(ctx, &io_ptr);
if (retval)
exit(FSCK_ERROR);
} else if (ctx->undo_file) {
retval = e2fsck_setup_tdb(ctx, &io_ptr);
if (retval)
exit(FSCK_ERROR);

View File

@ -117,6 +117,7 @@ exit 0
%{_root_sbindir}/e2image
%{_root_sbindir}/e2label
%{_root_sbindir}/e2undo
%{_root_sbindir}/e2patch
%{_root_sbindir}/findfs
%{_root_sbindir}/fsck
%{_root_sbindir}/fsck.ext2
@ -168,6 +169,7 @@ exit 0
%{_mandir}/man8/e2image.8*
%{_mandir}/man8/e2label.8*
%{_mandir}/man8/e2undo.8*
%{_mandir}/man8/e2patch.8*
%{_mandir}/man8/fsck.8*
%{_mandir}/man8/logsave.8*
%{_mandir}/man8/mke2fs.8*

View File

@ -75,6 +75,8 @@ libext2fs_src_files := \
swapfs.c \
symlink.c \
undo_io.c \
patch.c \
patch_io.c \
unix_io.c \
unlink.c \
valid_blk.c \

View File

@ -124,6 +124,8 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
symlink.o \
$(TDB_OBJ) \
undo_io.o \
patch.o \
patch_io.o \
unix_io.o \
unlink.o \
valid_blk.o \
@ -211,6 +213,8 @@ SRCS= ext2_err.c \
$(srcdir)/tst_getsize.c \
$(srcdir)/tst_iscan.c \
$(srcdir)/undo_io.c \
$(srcdir)/patch.c \
$(srcdir)/patch_io.c \
$(srcdir)/unix_io.c \
$(srcdir)/unlink.c \
$(srcdir)/valid_blk.c \
@ -1099,6 +1103,18 @@ undo_io.o: $(srcdir)/undo_io.c $(top_builddir)/lib/config.h \
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
patch.o: $(srcdir)/patch.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
patch_io.o: $(srcdir)/patch_io.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
unix_io.o: $(srcdir)/unix_io.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \

View File

@ -145,6 +145,11 @@ extern io_manager undo_io_manager;
extern errcode_t set_undo_io_backing_manager(io_manager manager);
extern errcode_t set_undo_io_backup_file(char *file_name);
/* patch_io.c */
extern io_manager patch_io_manager;
extern errcode_t set_patch_io_backing_manager(io_manager manager);
extern errcode_t set_patch_io_patch_file(char *file_name);
/* test_io.c */
extern io_manager test_io_manager, test_io_backing_manager;
extern void (*test_io_cb_read_blk)

228
lib/ext2fs/patch.c Normal file
View File

@ -0,0 +1,228 @@
/**
* patch.c --- Common "patch" file functions
*
* Copyright (c) Vitaliy Filippov <vitalif@mail.ru> 2014
* License: GNU GPLv2 or later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include "patch.h"
errcode_t ext2fs_patch_retry_read(int fd, ssize_t size, void *buf)
{
ssize_t r, done = 0;
while (done < size)
{
r = read(fd, buf+done, size-done);
if (!r || (r < 0 && errno != EAGAIN))
break;
done += r;
}
if (done < size)
return errno;
return 0;
}
errcode_t ext2fs_patch_retry_write(int fd, ssize_t size, const void *buf)
{
ssize_t r, done = 0;
while (done < size)
{
r = write(fd, buf+done, size-done);
if (r <= 0 && errno != EAGAIN)
break;
done += r;
}
if (done < size)
return errno;
return 0;
}
errcode_t ext2fs_patch_retry_read_at(int fd, unsigned long long offset, ssize_t size, void *buf)
{
if ((unsigned long long)ext2fs_llseek(fd, offset, SEEK_SET) != offset)
return errno ? errno : EXT2_ET_LLSEEK_FAILED;
return ext2fs_patch_retry_read(fd, size, buf);
}
errcode_t ext2fs_patch_retry_write_at(int fd, unsigned long long offset, ssize_t size, const void *buf)
{
if ((unsigned long long)ext2fs_llseek(fd, offset, SEEK_SET) != offset)
return errno ? errno : EXT2_ET_LLSEEK_FAILED;
return ext2fs_patch_retry_write(fd, size, buf);
}
errcode_t ext2fs_patch_read_bmap(struct ext2fs_patch_file *data)
{
errcode_t retval = 0;
int bufsize = 65536;
blk64_t i, r;
void *buf = malloc(bufsize);
if (!buf)
return ENOMEM;
ext2fs_llseek(data->patch_fd, data->block_size, SEEK_SET);
for (i = 0; i < data->size/8; )
{
r = bufsize;
if (data->size/8 - i < r)
r = data->size/8 - i;
retval = ext2fs_patch_retry_read(data->patch_fd, r, buf);
if (retval)
goto out;
ext2fs_set_generic_bmap_range(data->bmap, i*8, r*8, buf);
i += r;
}
out:
free(buf);
return retval;
}
errcode_t ext2fs_patch_write_bmap(struct ext2fs_patch_file *data)
{
errcode_t retval = 0;
int bufsize = 65536;
blk64_t i, r;
struct patchbd_super s;
void *buf = malloc(bufsize);
if (!buf)
return ENOMEM;
ext2fs_llseek(data->patch_fd, data->block_size, SEEK_SET);
for (i = 0; i < data->size/8; )
{
r = bufsize;
if (data->size/8 - i < r)
r = data->size/8 - i;
ext2fs_get_generic_bmap_range(data->bmap, i*8, r*8, buf);
retval = ext2fs_patch_retry_write(data->patch_fd, r, buf);
if (retval)
goto out;
i += r;
}
ext2fs_llseek(data->patch_fd, 0, SEEK_SET);
s.magic = PATCHBD_MAGIC;
s.patch_block = data->block_size;
s.patch_size = data->size;
write(data->patch_fd, &s, sizeof(struct patchbd_super));
out:
free(buf);
return 0;
}
errcode_t ext2fs_patch_open(struct ext2fs_patch_file *data, char *patch_file, int flags)
{
errcode_t retval = 0;
ext2_loff_t size;
struct patchbd_super s;
data->block_size = 0;
data->size = 0;
data->offset = 0;
data->bmap = NULL;
data->patch_file = strdup(patch_file);
data->patch_fd = open(data->patch_file, flags|O_RDWR, 0666);
if (data->patch_fd < 0)
return errno;
size = ext2fs_llseek(data->patch_fd, 0, SEEK_END);
if (size < 0)
return errno;
if (size > 0)
{
size = ext2fs_llseek(data->patch_fd, 0, SEEK_SET);
read(data->patch_fd, &s, sizeof(struct patchbd_super));
if (s.magic != PATCHBD_MAGIC)
return 0;
data->block_size = s.patch_block;
// if (data->block_size != 4096)
// return EINVAL;
data->size = s.patch_size;
retval = ext2fs_patch_init_bmap(data, NULL);
if (retval)
return retval;
retval = ext2fs_patch_read_bmap(data);
}
return 0;
}
errcode_t ext2fs_patch_close(struct ext2fs_patch_file *data)
{
if (data)
{
if (data->bmap)
{
if (data->patch_fd >= 0)
ext2fs_patch_write_bmap(data);
ext2fs_free_generic_bmap(data->bmap);
data->bmap = NULL;
}
if (data->patch_fd >= 0)
{
close(data->patch_fd);
data->patch_fd = -1;
}
if (data->patch_file)
{
free(data->patch_file);
data->patch_file = NULL;
}
}
return 0;
}
errcode_t ext2fs_patch_init_bmap(struct ext2fs_patch_file *data, io_channel channel)
{
errcode_t retval = 0;
if (!data->bmap)
{
if (channel)
{
// channel is optional parameter, if passed, means 'take size from channel'
data->block_size = channel->block_size;
// if (data->block_size != 4096)
// return EINVAL;
retval = ext2fs_get_device_size2(channel->name, data->block_size, &data->size);
if (retval)
return retval;
}
else if (!data->block_size || !data->size)
return EINVAL;
retval = ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, NULL,
0, data->size, data->size, "overwritten blocks", 0, &data->bmap);
data->offset = data->block_size + ((((data->size+7)>>3)+(data->block_size-1))&~(data->block_size-1));
}
return retval;
}
errcode_t ext2fs_patch_write_blk64(struct ext2fs_patch_file *data, unsigned long long block, int count, const void *buf)
{
ssize_t size;
if (!data->bmap)
return EINVAL;
if (count < 0)
{
if ((unsigned)-count > data->block_size)
return EINVAL;
size = -count;
count = 1;
}
else
size = count*data->block_size;
ext2fs_mark_block_bitmap_range2(data->bmap, block, count);
return ext2fs_patch_retry_write_at(data->patch_fd, data->offset + block*data->block_size, size, buf);
}

64
lib/ext2fs/patch.h Normal file
View File

@ -0,0 +1,64 @@
/**
* patch.h --- Common "patch" file functions
*
* Patch file format:
* 1) sparse data blocks - same size as the patched filesystem, but only changed blocks are written
* 2) updated block bitmap - fs_size/block_size/8 bytes
* 3) 4 byte FS block size
* 4) 8 byte FS size in blocks
*
* Copyright (c) Vitaliy Filippov <vitalif@mail.ru> 2014
* License: GNU GPLv2 or later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef E2_PATCH_H
#define E2_PATCH_H
#include "ext2_fs.h"
#include "ext2fs.h"
#define PATCHBD_MAGIC 0x44623950 // P9bD
struct ext2fs_patch_file
{
char *patch_file;
int patch_fd;
__u32 block_size;
blk64_t size;
ext2_loff_t offset;
ext2fs_generic_bitmap bmap;
};
struct patchbd_super
{
__u32 magic;
__u32 patch_block;
__u64 patch_size;
};
errcode_t ext2fs_patch_retry_read(int fd, ssize_t size, void *buf);
errcode_t ext2fs_patch_retry_write(int fd, ssize_t size, const void *buf);
errcode_t ext2fs_patch_retry_read_at(int fd, unsigned long long offset, ssize_t size, void *buf);
errcode_t ext2fs_patch_retry_write_at(int fd, unsigned long long offset, ssize_t size, const void *buf);
errcode_t ext2fs_patch_read_bmap(struct ext2fs_patch_file *data);
errcode_t ext2fs_patch_write_bmap(struct ext2fs_patch_file *data);
errcode_t ext2fs_patch_open(struct ext2fs_patch_file *data, char *patch_file, int flags);
errcode_t ext2fs_patch_close(struct ext2fs_patch_file *data);
errcode_t ext2fs_patch_init_bmap(struct ext2fs_patch_file *data, io_channel channel);
errcode_t ext2fs_patch_write_blk64(struct ext2fs_patch_file *data, unsigned long long block, int count, const void *buf);
#endif

375
lib/ext2fs/patch_io.c Normal file
View File

@ -0,0 +1,375 @@
/*
* patch_io.c --- This is the "patch" io manager that writes the new data into
* a separate sparse file to apply it later.
*
* Copyright (c) Vitaliy Filippov <vitalif@mail.ru> 2014
* License: GNU GPLv2 or later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include "ext2_fs.h"
#include "ext2fs.h"
#include "patch.h"
#ifdef __GNUC__
#define ATTR(x) __attribute__(x)
#else
#define ATTR(x)
#endif
#define EXT2_CHECK_MAGIC(struct, code) if ((struct)->magic != (code)) return (code)
struct patch_private_data
{
int magic;
struct ext2fs_patch_file patch;
/* The backing io channel */
io_channel real;
/* to support offset in unix I/O manager */
ext2_loff_t offset;
};
static errcode_t patch_open(const char *name, int flags, io_channel *channel);
static errcode_t patch_close(io_channel channel);
static errcode_t patch_set_blksize(io_channel channel, int blksize);
static errcode_t patch_read_blk64(io_channel channel, unsigned long long block, int count, void *data);
static errcode_t patch_write_blk64(io_channel channel, unsigned long long block, int count, const void *data);
static errcode_t patch_read_blk(io_channel channel, unsigned long block, int count, void *data);
static errcode_t patch_write_blk(io_channel channel, unsigned long block, int count, const void *data);
static errcode_t patch_flush(io_channel channel);
static errcode_t patch_write_byte(io_channel channel, unsigned long offset, int size, const void *data);
static errcode_t patch_set_option(io_channel channel, const char *option, const char *arg);
static errcode_t patch_get_stats(io_channel channel, io_stats *stats);
static struct struct_io_manager struct_patch_manager = {
EXT2_ET_MAGIC_IO_MANAGER,
"Patch I/O Manager",
patch_open,
patch_close,
patch_set_blksize,
patch_read_blk,
patch_write_blk,
patch_flush,
patch_write_byte,
patch_set_option,
patch_get_stats,
patch_read_blk64,
patch_write_blk64,
};
io_manager patch_io_manager = &struct_patch_manager;
static char *patch_file;
static io_manager patch_io_backing_manager;
errcode_t set_patch_io_backing_manager(io_manager manager)
{
patch_io_backing_manager = manager;
return 0;
}
errcode_t set_patch_io_patch_file(char *file)
{
patch_file = file;
return 0;
}
static errcode_t patch_open(const char *name, int flags, io_channel *channel)
{
io_channel io = NULL;
struct patch_private_data *data = NULL;
errcode_t retval;
if (name == 0)
return EXT2_ET_BAD_DEVICE_NAME;
retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
if (retval)
goto cleanup;
memset(io, 0, sizeof(struct struct_io_channel));
io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
retval = ext2fs_get_mem(sizeof(struct patch_private_data), &data);
if (retval)
goto cleanup;
io->manager = patch_io_manager;
retval = ext2fs_get_mem(strlen(name)+1, &io->name);
if (retval)
goto cleanup;
strcpy(io->name, name);
io->private_data = data;
io->block_size = 1024;
io->read_error = 0;
io->write_error = 0;
io->refcount = 1;
memset(data, 0, sizeof(struct patch_private_data));
data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
if (patch_io_backing_manager)
{
retval = patch_io_backing_manager->open(name, flags & ~IO_FLAG_RW, &data->real);
if (retval)
goto cleanup;
}
if (patch_file)
{
retval = ext2fs_patch_open(&data->patch, patch_file, O_CREAT);
if (retval)
goto cleanup;
if (data->patch.block_size)
{
retval = io_channel_set_blksize(data->real, data->patch.block_size);
if (retval)
goto cleanup;
}
}
*channel = io;
return 0;
cleanup:
if (data)
{
ext2fs_patch_close(&data->patch);
if (data->real)
io_channel_close(data->real);
ext2fs_free_mem(&data);
}
if (io)
{
if (io->name)
ext2fs_free_mem(&io->name);
ext2fs_free_mem(&io);
}
return retval;
}
static errcode_t patch_close(io_channel channel)
{
struct patch_private_data *data;
errcode_t retval = 0;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
if (--channel->refcount > 0)
return 0;
ext2fs_patch_close(&data->patch);
if (data->real)
retval = io_channel_close(data->real);
ext2fs_free_mem(&channel->private_data);
if (channel->name)
ext2fs_free_mem(&channel->name);
ext2fs_free_mem(&channel);
return retval;
}
static errcode_t patch_set_blksize(io_channel channel, int blksize)
{
struct patch_private_data *data;
errcode_t retval = 0;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
channel->block_size = (unsigned)blksize;
if (data->patch.block_size && data->patch.block_size != (unsigned)blksize)
return EINVAL;
if (data->real)
retval = io_channel_set_blksize(data->real, blksize);
return retval;
}
static errcode_t patch_read_blk64(io_channel channel, unsigned long long block, int count, void *buf)
{
errcode_t retval = 0;
struct patch_private_data *data;
int b, n;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
if (count < 0)
{
if (-count <= channel->block_size)
{
if (data->patch.bmap && ext2fs_test_generic_bitmap(data->patch.bmap, block))
retval = ext2fs_patch_retry_read_at(data->patch.patch_fd, data->patch.offset + block*channel->block_size, -count, buf);
else
retval = io_channel_read_blk64(data->real, block, count, buf);
return retval;
}
else
return EINVAL;
}
for (b = 0; b < count; )
{
for (n = 0; (b+n < count) && data->patch.bmap && ext2fs_test_generic_bitmap(data->patch.bmap, block+b+n); n++) {}
if (n > 0)
{
retval = ext2fs_patch_retry_read_at(data->patch.patch_fd, data->patch.offset + (block+b)*channel->block_size, n*channel->block_size, buf+b*channel->block_size);
if (retval)
break;
b += n;
}
for (n = 0; (b+n < count) && (!data->patch.bmap || !ext2fs_test_generic_bitmap(data->patch.bmap, block+b+n)); n++) {}
if (n > 0)
{
retval = io_channel_read_blk64(data->real, block+b, n, buf+b*channel->block_size);
if (retval)
break;
b += n;
}
}
return retval;
}
static errcode_t patch_read_blk(io_channel channel, unsigned long block, int count, void *buf)
{
return patch_read_blk64(channel, block, count, buf);
}
static errcode_t patch_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf)
{
struct patch_private_data *data;
errcode_t retval = 0;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
retval = ext2fs_patch_init_bmap(&data->patch, channel);
if (retval)
return retval;
// libext2fs changes block size to 1024 in order to write the superblock, so we must support it...
if ((__u32)channel->block_size < data->patch.block_size)
{
void *buf2 = NULL;
unsigned long long block_real = block / (data->patch.block_size / channel->block_size);
int count_real = ( (block % (data->patch.block_size / channel->block_size))
+ (count > 0 ? count*channel->block_size : -count)
+ data->patch.block_size - 1 ) / data->patch.block_size;
retval = ext2fs_get_mem(count_real * data->patch.block_size, &buf2);
if (retval)
goto out;
retval = patch_read_blk64(channel, block_real, count_real, buf2);
if (retval)
goto out;
memcpy(buf2 + (block % (data->patch.block_size / channel->block_size)) * channel->block_size,
buf, (count > 0 ? count*channel->block_size : -count));
retval = ext2fs_patch_write_blk64(&data->patch, block_real, count_real, buf2);
out:
if (buf2)
ext2fs_free_mem(&buf2);
return retval;
}
else if ((__u32)channel->block_size > data->patch.block_size)
{
return EXT2_ET_UNIMPLEMENTED;
}
return ext2fs_patch_write_blk64(&data->patch, block, count, buf);
}
static errcode_t patch_write_blk(io_channel channel, unsigned long block, int count, const void *buf)
{
return patch_write_blk64(channel, block, count, buf);
}
static errcode_t patch_write_byte(io_channel channel, unsigned long offset, int size, const void *buf)
{
return EXT2_ET_UNIMPLEMENTED;
}
/*
* Flush data buffers to disk.
*/
static errcode_t patch_flush(io_channel channel)
{
errcode_t retval = 0;
struct patch_private_data *data;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
if (data->real)
retval = io_channel_flush(data->real);
if (data->patch.patch_fd)
fsync(data->patch.patch_fd);
return retval;
}
static errcode_t patch_set_option(io_channel channel, const char *option, const char *arg)
{
errcode_t retval = 0;
struct patch_private_data *data;
unsigned long tmp;
char *end;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
/*
* Need to support offset option to work with
* Unix I/O manager
*/
if (data->real && data->real->manager->set_option)
retval = data->real->manager->set_option(data->real, option, arg);
if (!retval && !strcmp(option, "offset"))
{
if (!arg)
return EXT2_ET_INVALID_ARGUMENT;
tmp = strtoul(arg, &end, 0);
if (*end)
return EXT2_ET_INVALID_ARGUMENT;
data->offset = tmp;
}
return retval;
}
static errcode_t patch_get_stats(io_channel channel, io_stats *stats)
{
errcode_t retval = 0;
struct patch_private_data *data;
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
data = (struct patch_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
if (data->real)
retval = (data->real->manager->get_stats)(data->real, stats);
return retval;
}

View File

@ -300,6 +300,7 @@ static errcode_t raw_write_blk(io_channel channel,
goto short_write;
size -= actual;
buf += actual;
location += actual;
}
return 0;

View File

@ -33,12 +33,12 @@ INSTALL = @INSTALL@
@FUSE_CMT@FUSE_PROG= fuse2fs
SPROGS= mke2fs badblocks tune2fs dumpe2fs $(BLKID_PROG) logsave \
$(E2IMAGE_PROG) @FSCK_PROG@ e2undo
$(E2IMAGE_PROG) @FSCK_PROG@ e2undo e2patch
USPROGS= mklost+found filefrag e2freefrag $(UUIDD_PROG) \
$(E4DEFRAG_PROG) $(E4CRYPT_PROG) $(FUSE_PROG)
SMANPAGES= tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
e2label.8 $(FINDFS_MAN) $(BLKID_MAN) $(E2IMAGE_MAN) \
logsave.8 filefrag.8 e2freefrag.8 e2undo.8 \
logsave.8 filefrag.8 e2freefrag.8 e2undo.8 e2patch.8 \
$(UUIDD_MAN) $(E4DEFRAG_MAN) $(E4CRYPT_MAN) @FSCK_MAN@
FMANPAGES= mke2fs.conf.5 ext4.5
@ -63,6 +63,7 @@ FSCK_OBJS= fsck.o base_device.o ismounted.o
BLKID_OBJS= blkid.o
FILEFRAG_OBJS= filefrag.o
E2UNDO_OBJS= e2undo.o
E2PATCH_OBJS= e2patch.o
E4DEFRAG_OBJS= e4defrag.o
E4CRYPT_OBJS= e4crypt.o
E2FREEFRAG_OBJS= e2freefrag.o
@ -88,6 +89,7 @@ PROFILED_BLKID_OBJS= profiled/blkid.o
PROFILED_FILEFRAG_OBJS= profiled/filefrag.o
PROFILED_E2FREEFRAG_OBJS= profiled/e2freefrag.o
PROFILED_E2UNDO_OBJS= profiled/e2undo.o
PROFILED_E2PATCH_OBJS= profiled/e2patch.o
PROFILED_E4DEFRAG_OBJS= profiled/e4defrag.o
PROFILED_E4CRYPT_OBJS= profiled/e4crypt.o
PROFILED_FUSE2FS_OJBS= profiled/fuse2fs.o profiled/journal.o \
@ -98,7 +100,7 @@ SRCS= $(srcdir)/tune2fs.c $(srcdir)/mklost+found.c $(srcdir)/mke2fs.c $(srcdir)/
$(srcdir)/badblocks.c $(srcdir)/fsck.c $(srcdir)/util.c \
$(srcdir)/uuidgen.c $(srcdir)/blkid.c $(srcdir)/logsave.c \
$(srcdir)/filefrag.c $(srcdir)/base_device.c \
$(srcdir)/ismounted.c $(srcdir)/e2undo.c \
$(srcdir)/ismounted.c $(srcdir)/e2undo.c $(srcdir)/e2patch.c \
$(srcdir)/e2freefrag.c $(srcdir)/create_inode.c \
$(srcdir)/fuse2fs.c \
$(srcdir)/../debugfs/journal.c $(srcdir)/../e2fsck/revoke.c \
@ -133,7 +135,7 @@ all:: profiled $(SPROGS) $(UPROGS) $(USPROGS) $(SMANPAGES) $(UMANPAGES) \
$(FMANPAGES) $(LPROGS) $(E4DEFRAG_PROG) $(E4CRYPT_PROGS) e2fuzz
@PROFILE_CMT@all:: tune2fs.profiled blkid.profiled e2image.profiled \
e2undo.profiled mke2fs.profiled dumpe2fs.profiled fsck.profiled \
e2undo.profiled e2patch.profiled mke2fs.profiled dumpe2fs.profiled fsck.profiled \
logsave.profiled filefrag.profiled uuidgen.profiled $(UUIDD_PROFILED) \
e2image.profiled e4defrag.profiled e4crypt.profiled \
e2freefrag.profiled
@ -226,6 +228,16 @@ e2undo.profiled: $(E2UNDO_OBJS) $(PROFILED_DEPLIBS)
$(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e2undo.profiled \
$(PROFILED_E2UNDO_OBJS) $(PROFILED_LIBS) $(LIBINTL) $(SYSLIBS)
e2patch: $(E2PATCH_OBJS) $(DEPLIBS)
$(E) " LD $@"
$(Q) $(CC) $(ALL_LDFLAGS) -o e2patch $(E2PATCH_OBJS) $(LIBS) \
$(LIBINTL) $(SYSLIBS)
e2patch.profiled: $(E2PATCH_OBJS) $(PROFILED_DEPLIBS)
$(E) " LD $@"
$(Q) $(CC) $(ALL_LDFLAGS) -g -pg -o e2patch.profiled \
$(PROFILED_E2PATCH_OBJS) $(PROFILED_LIBS) $(LIBINTL) $(SYSLIBS)
e4defrag: $(E4DEFRAG_OBJS) $(DEPLIBS)
$(E) " LD $@"
$(Q) $(CC) $(ALL_LDFLAGS) -o e4defrag $(E4DEFRAG_OBJS) $(LIBS) \
@ -439,6 +451,10 @@ e2undo.8: $(DEP_SUBSTITUTE) $(srcdir)/e2undo.8.in
$(E) " SUBST $@"
$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2undo.8.in e2undo.8
e2patch.8: $(DEP_SUBSTITUTE) $(srcdir)/e2patch.8.in
$(E) " SUBST $@"
$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2patch.8.in e2patch.8
findfs.8: $(DEP_SUBSTITUTE) $(srcdir)/findfs.8.in
$(E) " SUBST $@"
$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/findfs.8.in findfs.8
@ -669,7 +685,7 @@ clean::
e2initrd_helper partinfo prof_err.[ch] default_profile.c \
uuidd e2image tune2fs.static tst_ismounted fsck.profiled \
blkid.profiled tune2fs.profiled e2image.profiled \
e2undo.profiled mke2fs.profiled dumpe2fs.profiled \
e2undo.profiled e2patch.profiled mke2fs.profiled dumpe2fs.profiled \
logsave.profiled filefrag.profiled uuidgen.profiled \
uuidd.profiled e2image.profiled e2fuzz mke2fs.conf \
profiled/*.o \#* *.s *.o *.a *~ core gmon.out
@ -792,6 +808,12 @@ e2undo.o: $(srcdir)/e2undo.c $(top_builddir)/lib/config.h \
$(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h \
$(top_srcdir)/lib/support/nls-enable.h
e2patch.o: $(srcdir)/e2patch.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
$(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/bitops.h
e2freefrag.o: $(srcdir)/e2freefrag.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \

31
misc/e2patch.8.in Normal file
View File

@ -0,0 +1,31 @@
.TH E2PATCH 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
.SH NAME
e2patch \- patch tool for safely applying libext2fs patch files to block devices
.SH SYNOPSIS
.B e2patch backup \fI<device>\fR \fI<patch_file>\fR \fI<backup_file>\fR
.br
.B e2patch apply \fI<device>\fR \fI<patch_file>\fR
.SH DESCRIPTION
.B e2patch
applies and restores "patches" produced by e2fsprogs with patch file option
(\fBresize2fs -T <patch_file>\fR) to block devices.
.PP
"Patch files" are sparse files containing overwritten device blocks and
a block bitmap.
.PP
"Patch files" are faster, safer and simpler to use than "undo files"
(\fBe2undo\fR and \fBresize2fs -z <undo_file>\fR), even though their original
design goal is similar.
.SH COMMANDS
.TP
.B e2patch backup \fI<device>\fR \fI<patch_file>\fR \fI<backup_file>\fR
Creates a backup in <backup_file> for restoring after bad patch <patch_file>.
Backup can then also be applied with \fBe2patch apply\fR
.TP
.B e2patch apply \fI<device>\fR \fI<patch_file>\fR
Apply <patch_file> to <device>.
.SH AUTHOR
Written by Vitaliy Filippov <vitalif@mail.ru>
.SH SEE ALSO
.BR resize2fs (8),
.BR e2undo (8).

166
misc/e2patch.c Normal file
View File

@ -0,0 +1,166 @@
/**
* e2patch.c --- Utility to apply/restore patches created by patch_io_manager.
*
* Copyright (c) Vitaliy Filippov <vitalif@mail.ru> 2014
* License: GNU GPLv2 or later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include "ext2fs/ext2_fs.h"
#include "ext2fs/ext2fs.h"
#include "ext2fs/patch.h"
#define BUF 0x10000
#define _(a) (a)
errcode_t make_backup_patch(char *device, char *io_options, char *patch_file, char *backup_file)
{
io_manager mgr = unix_io_manager;
io_channel io;
errcode_t retval;
blk64_t blk, start, buf_blocks;
int eq;
void *buf = NULL;
struct ext2fs_patch_file patch = { 0 }, backup = { 0 };
retval = mgr->open(device, IO_FLAG_EXCLUSIVE, &io);
if (retval) goto out;
if (io_options &&
(retval = io_channel_set_options(io, io_options)))
goto out;
retval = ext2fs_patch_open(&patch, patch_file, 0);
if (retval) goto out;
retval = ext2fs_patch_open(&backup, backup_file, O_CREAT);
if (retval) goto out;
backup.block_size = patch.block_size;
backup.size = patch.size;
ext2fs_patch_init_bmap(&backup, NULL);
buf_blocks = BUF/patch.block_size;
retval = mgr->set_blksize(io, patch.block_size);
if (retval) goto out;
buf = malloc(BUF);
for (start = 0, blk = 0; blk <= patch.size; blk++)
{
if ((eq = !ext2fs_test_generic_bitmap(patch.bmap, blk)) || blk >= patch.size || blk-start >= buf_blocks)
{
if (start != blk)
{
retval = io_channel_read_blk64(io, start, blk-start, buf);
if (retval) goto out;
retval = ext2fs_patch_write_blk64(&backup, start, blk-start, buf);
if (retval) goto out;
}
start = blk+eq;
}
}
out:
if (buf)
free(buf);
ext2fs_patch_close(&backup);
ext2fs_patch_close(&patch);
mgr->close(io);
return retval;
}
errcode_t apply_patch(char *device, char *io_options, char *patch_file)
{
io_manager mgr = unix_io_manager;
io_channel io;
errcode_t retval;
blk64_t blk, start, buf_blocks;
int eq;
void *buf = NULL;
struct ext2fs_patch_file patch = { 0 };
retval = mgr->open(device, IO_FLAG_EXCLUSIVE|IO_FLAG_RW, &io);
if (retval) goto out;
if (io_options &&
(retval = io_channel_set_options(io, io_options)))
goto out;
retval = ext2fs_patch_open(&patch, patch_file, 0);
if (retval) goto out;
buf_blocks = BUF/patch.block_size;
retval = mgr->set_blksize(io, patch.block_size);
if (retval) goto out;
buf = malloc(BUF);
for (start = 0, blk = 0; blk <= patch.size; blk++)
{
if ((eq = blk < patch.size && !ext2fs_test_generic_bitmap(patch.bmap, blk)) || blk >= patch.size || blk-start >= buf_blocks)
{
if (start != blk)
{
retval = ext2fs_patch_retry_read_at(patch.patch_fd, patch.offset + start*patch.block_size, (blk-start)*patch.block_size, buf);
if (retval) goto out;
retval = io_channel_write_blk64(io, start, blk-start, buf);
if (retval) goto out;
}
start = blk+eq;
}
}
out:
if (buf)
free(buf);
ext2fs_patch_close(&patch);
mgr->close(io);
return retval;
}
int main(int narg, char **args)
{
errcode_t retval;
char *io_options = NULL;
if (narg >= 5 && !strcmp(args[1], "backup"))
{
io_options = strchr(args[2], '?');
if (io_options)
*io_options++ = 0;
retval = make_backup_patch(args[2], io_options, args[3], args[4]);
}
else if (narg >= 4 && !strcmp(args[1], "apply"))
{
io_options = strchr(args[2], '?');
if (io_options)
*io_options++ = 0;
retval = apply_patch(args[2], io_options, args[3]);
}
else
{
printf(
"Patch tool for safely applying changes to block devices\n"
"License: GNU GPLv2 or later\n"
"Copyright (c) Vitaliy Filippov, 2014\n\n"
"To create a backup for restoring after bad patch:\n"
" e2patch backup <filesystem> <patch_file> <backup_file>\n"
"To apply a patch:\n"
" e2patch apply <filesystem> <patch_file>\n"
);
return 0;
}
if (retval)
{
com_err("e2patch", retval, _("while trying to %s"), args[1]);
}
return 0;
}

View File

@ -474,6 +474,10 @@ static int frag_report(const char *filename)
}
if (force_bmap || rc < 0) { /* FIEMAP failed, try FIBMAP instead */
if (numblocks > (unsigned long)-1L) {
fprintf(stderr, "%s: File too big to use FIBMAP\n", filename);
goto out_close;
}
expected = filefrag_fibmap(fd, blk_shift, &num_extents,
&st, numblocks, is_ext2);
if (expected < 0) {

View File

@ -43,6 +43,7 @@ misc/e2image.c
misc/e2initrd_helper.c
misc/e2label.c
misc/e2undo.c
misc/e2patch.c
misc/e4crypt.c
misc/e4defrag.c
misc/filefrag.c

View File

@ -46,8 +46,8 @@ static char *device_name, *io_options;
static void usage (char *prog)
{
fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-P] "
"[-p] device [-b|-s|new_size] [-z undo_file]\n\n"),
fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-N new_inodes] [-P] "
"[-p] device [-b|-s|new_size] [-T patch_file] [-z undo_file]\n\n"),
prog);
exit (1);
@ -167,6 +167,20 @@ static void bigalloc_check(ext2_filsys fs, int force)
}
}
static int resize2fs_setup_patch(const char *device, char *patch_file,
io_manager *io_ptr)
{
set_patch_io_backing_manager(*io_ptr);
set_patch_io_patch_file(patch_file);
*io_ptr = patch_io_manager;
printf(_("To make backup before applying changes run:\n"
" e2patch backup %s %s %s.backup\n"
"Then, to apply the operation to the real filesystem run:\n"
" e2patch apply %s %s\n"),
device, patch_file, patch_file, device, patch_file);
return 0;
}
static int resize2fs_setup_tdb(const char *device, char *undo_file,
io_manager *io_ptr)
{
@ -258,6 +272,7 @@ int main (int argc, char ** argv)
blk64_t new_size = 0;
blk64_t max_size = 0;
blk64_t min_size = 0;
__u32 new_inodes = 0;
io_manager io_ptr;
char *new_size_str = 0;
int use_stride = -1;
@ -267,7 +282,7 @@ int main (int argc, char ** argv)
unsigned int blocksize;
long sysval;
int len, mount_flags;
char *mtpt, *undo_file = NULL;
char *mtpt, *undo_file = NULL, *patch_file = NULL;
#ifdef ENABLE_NLS
setlocale(LC_MESSAGES, "");
@ -284,7 +299,7 @@ int main (int argc, char ** argv)
if (argc && *argv)
program_name = *argv;
while ((c = getopt(argc, argv, "d:fFhMPpS:bsz:")) != EOF) {
while ((c = getopt(argc, argv, "d:fFhMPN:pS:bsT:z:")) != EOF) {
switch (c) {
case 'h':
usage(program_name);
@ -301,6 +316,9 @@ int main (int argc, char ** argv)
case 'P':
print_min_size = 1;
break;
case 'N':
new_inodes = strtoul(optarg, 0, 0);
break;
case 'd':
flags |= atoi(optarg);
break;
@ -316,6 +334,9 @@ int main (int argc, char ** argv)
case 's':
flags |= RESIZE_DISABLE_64BIT;
break;
case 'T':
patch_file = optarg;
break;
case 'z':
undo_file = optarg;
break;
@ -402,7 +423,11 @@ int main (int argc, char ** argv)
io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE;
io_flags |= EXT2_FLAG_64BITS;
if (undo_file) {
if (patch_file) {
retval = resize2fs_setup_patch(device_name, patch_file, &io_ptr);
if (retval)
exit(1);
} else if (undo_file) {
retval = resize2fs_setup_tdb(device_name, undo_file, &io_ptr);
if (retval)
exit(1);
@ -583,7 +608,7 @@ int main (int argc, char ** argv)
"feature.\n"));
exit(1);
}
} else if (new_size == ext2fs_blocks_count(fs->super)) {
} else if (new_size == ext2fs_blocks_count(fs->super) && !new_inodes) {
fprintf(stderr, _("The filesystem is already %llu (%dk) "
"blocks long. Nothing to do!\n\n"), new_size,
blocksize / 1024);
@ -612,7 +637,7 @@ int main (int argc, char ** argv)
printf(_("Resizing the filesystem on "
"%s to %llu (%dk) blocks.\n"),
device_name, new_size, blocksize / 1024);
retval = resize_fs(fs, &new_size, flags,
retval = resize_fs(fs, &new_size, new_inodes, flags,
((flags & RESIZE_PERCENT_COMPLETE) ?
resize_progress_func : 0));
}

View File

@ -159,6 +159,12 @@ program will heuristically determine the RAID stride that was specified
when the filesystem was created. This option allows the user to
explicitly specify a RAID stride setting to be used by resize2fs instead.
.TP
.BI \-T " patch_file"
Do not apply changes to the real filesystem; write updates into a sparse file
named 'patch_file' to safely apply them later with e2patch(8) utility.
You'll also be able to create a backup before applying patch and safely
restore it in case of power outage or system crash.
.TP
.BI \-z " undo_file"
Before overwriting a file system block, write the old contents of the block to
an undo file. This undo file can be used with e2undo(8) to restore the old

View File

@ -45,19 +45,21 @@ static void fix_uninit_block_bitmaps(ext2_filsys fs);
static errcode_t adjust_superblock(ext2_resize_t rfs, blk64_t new_size);
static errcode_t blocks_to_move(ext2_resize_t rfs);
static errcode_t block_mover(ext2_resize_t rfs);
static errcode_t inodes_to_move(ext2_resize_t rfs);
static errcode_t inode_scan_and_fix(ext2_resize_t rfs);
static errcode_t inode_ref_fix(ext2_resize_t rfs);
static errcode_t move_itables(ext2_resize_t rfs);
static errcode_t move_inode_tables(ext2_resize_t rfs);
static errcode_t fix_resize_inode(ext2_filsys fs);
static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs);
static errcode_t fix_sb_journal_backup(ext2_filsys fs);
static errcode_t mark_table_blocks(ext2_filsys fs,
ext2fs_block_bitmap bmap);
ext2fs_block_bitmap bmap, int skip_inode_tables);
static errcode_t clear_sparse_super2_last_group(ext2_resize_t rfs);
static errcode_t reserve_sparse_super2_last_group(ext2_resize_t rfs,
ext2fs_block_bitmap meta_bmap);
static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size);
static errcode_t move_bg_metadata(ext2_resize_t rfs);
static errcode_t move_bg_metadata(ext2_resize_t rfs, __u32 new_inodes);
static errcode_t zero_high_bits_in_inodes(ext2_resize_t rfs);
/*
@ -94,7 +96,7 @@ static int lazy_itable_init;
/*
* This is the top-level routine which does the dirty deed....
*/
errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, __u32 new_inodes, int flags,
errcode_t (*progress)(ext2_resize_t rfs, int pass,
unsigned long cur,
unsigned long max_val))
@ -142,7 +144,7 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
print_resource_track(rfs, &rtrack, fs->io);
init_resource_track(&rtrack, "move_bg_metadata", fs->io);
retval = move_bg_metadata(rfs);
retval = move_bg_metadata(rfs, new_inodes);
if (retval)
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
@ -188,6 +190,18 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
init_resource_track(&rtrack, "inodes_to_move", fs->io);
retval = inodes_to_move(rfs);
if (retval)
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
init_resource_track(&rtrack, "move_inode_tables", fs->io);
retval = move_inode_tables(rfs);
if (retval)
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
init_resource_track(&rtrack, "inode_scan_and_fix", fs->io);
retval = inode_scan_and_fix(rfs);
if (retval)
@ -200,11 +214,11 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
init_resource_track(&rtrack, "move_itables", fs->io);
/* init_resource_track(&rtrack, "move_itables", fs->io);
retval = move_itables(rfs);
if (retval)
goto errout;
print_resource_track(rfs, &rtrack, fs->io);
print_resource_track(rfs, &rtrack, fs->io);*/
retval = clear_sparse_super2_last_group(rfs);
if (retval)
@ -344,8 +358,39 @@ static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size)
return 0;
}
/**
* Inode table resizing:
* 1) move_bg_metadata(), calls set_inode_count(): set new inode count
* 2) adjust_superblock(): extend bitmaps, write group descriptors
* 3) blocks_to_move(): allocate new inode tables and, when extending inode tables,
* mark extra blocks needed for new inode tables as 'blocks to move'
* 4) block_mover(): move blocks out of the way as usual
* 5) inodes_to_move(): when shrinking inode tables, move inodes from the end
* of each group's inode table (still operating on old fs inode tables).
* it would be possible to rewrite blocks on step 7, but we need to read
* extents, and they may be overwritten by inode tables on step 6.
* so, rewrite blocks used by inodes here!
* 6) move_inode_tables(): move inode tables
* 7) inode_scan_and_fix(): rewrite all inode, extent, extattr and ACL checksums
* 8) inode_ref_fix(): translate inode numbers (operating on new fs inode tables)
*/
static int set_inode_count(ext2_filsys fs, __u32 new_inodes)
{
__u32 old_ipg = fs->super->s_inodes_per_group;
__u32 new_ipg = ((new_inodes + fs->group_desc_count - 1) / fs->group_desc_count);
new_ipg = (((new_ipg * EXT2_INODE_SIZE(fs->super)) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super))
* EXT2_BLOCK_SIZE(fs->super) / EXT2_INODE_SIZE(fs->super);
if (new_ipg == old_ipg)
return -1;
fs->inode_blocks_per_group = new_ipg * EXT2_INODE_SIZE(fs->super) / EXT2_BLOCK_SIZE(fs->super);
fs->super->s_inodes_per_group = new_ipg;
fs->super->s_inodes_count = fs->group_desc_count * fs->super->s_inodes_per_group;
fs->super->s_free_inodes_count += fs->group_desc_count * (new_ipg - old_ipg);
return 0;
}
/* Move bitmaps/inode tables out of the way. */
static errcode_t move_bg_metadata(ext2_resize_t rfs)
static errcode_t move_bg_metadata(ext2_resize_t rfs, __u32 new_inodes)
{
dgrp_t i;
blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j;
@ -354,7 +399,12 @@ static errcode_t move_bg_metadata(ext2_resize_t rfs)
errcode_t retval;
int cluster_ratio;
if (!(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
if (new_inodes) {
if (set_inode_count(rfs->new_fs, new_inodes) != 0)
new_inodes = 0;
}
if (!new_inodes && !(rfs->flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)))
return 0;
retval = ext2fs_allocate_block_bitmap(rfs->old_fs, "oldfs", &old_map);
@ -644,7 +694,7 @@ static errcode_t free_gdp_blocks(ext2_filsys fs,
if (retval)
goto out;
retval = mark_table_blocks(fs, bg_map);
retval = mark_table_blocks(fs, bg_map, 0);
if (retval)
goto out;
}
@ -1011,7 +1061,7 @@ retry:
* number of the block group descriptors.
*/
if (reserve_blocks)
mark_table_blocks(fs, reserve_blocks);
mark_table_blocks(fs, reserve_blocks, 0);
errout:
return (retval);
@ -1145,7 +1195,7 @@ errout:
* filesystem meta-data blocks.
*/
static errcode_t mark_table_blocks(ext2_filsys fs,
ext2fs_block_bitmap bmap)
ext2fs_block_bitmap bmap, int skip_inode_tables)
{
dgrp_t i;
blk64_t blk;
@ -1156,10 +1206,12 @@ static errcode_t mark_table_blocks(ext2_filsys fs,
/*
* Mark the blocks used for the inode table
*/
blk = ext2fs_inode_table_loc(fs, i);
if (blk)
ext2fs_mark_block_bitmap_range2(bmap, blk,
fs->inode_blocks_per_group);
if (!skip_inode_tables) {
blk = ext2fs_inode_table_loc(fs, i);
if (blk)
ext2fs_mark_block_bitmap_range2(bmap, blk,
fs->inode_blocks_per_group);
}
/*
* Mark block used for the block bitmap
@ -1283,12 +1335,69 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
if (retval)
return retval;
retval = mark_table_blocks(old_fs, meta_bmap);
retval = mark_table_blocks(old_fs, meta_bmap, 0);
if (retval)
return retval;
fs = rfs->new_fs;
/**
* If we're resizing inode tables, we need to reallocate all of them
*/
if (rfs->old_fs->inode_blocks_per_group != fs->inode_blocks_per_group) {
dgrp_t flexbg_size, flex_count, grp_in_flex;
ext2fs_block_bitmap empty_bmap;
/* // this is how mke2fs allocates inode tables...
retval = ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, fs,
fs->super->s_first_data_block,
ext2fs_blocks_count(fs->super)-1,
ext2fs_blocks_count(fs->super)-1, "subcluster inode table bitmap", 0, &empty_bmap);*/
retval = ext2fs_allocate_block_bitmap(fs, _("metadata without inode tables"), &empty_bmap);
if (retval) {
return retval;
}
retval = mark_table_blocks(fs, empty_bmap, 1);
if (retval) {
return retval;
}
if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG)
&& fs->super->s_log_groups_per_flex) {
flexbg_size = 1 << fs->super->s_log_groups_per_flex;
} else {
flexbg_size = 1;
}
flex_count = (fs->group_desc_count + flexbg_size - 1) / flexbg_size;
for (g = 0; g < fs->group_desc_count; g++) {
ext2fs_inode_table_loc_set(fs, g, 0);
retval = ext2fs_allocate_group_table(fs, g, empty_bmap);
if (retval) {
return retval;
}
/* ext2fs_mark_block_bitmap_range2(empty_bmap,
ext2fs_inode_table_loc(fs, g), fs->inode_blocks_per_group);*/
group_blk = ext2fs_inode_table_loc(fs, g);
if (g > 0 && group_blk < ext2fs_inode_table_loc(fs, g-1)+fs->inode_blocks_per_group) {
ext2fs_unmark_block_bitmap_range2(fs->block_map, group_blk, fs->inode_blocks_per_group);
group_blk = ext2fs_inode_table_loc(fs, g-1)+fs->inode_blocks_per_group;
ext2fs_inode_table_loc_set(fs, g, group_blk);
ext2fs_group_desc_csum_set(fs, g);
ext2fs_mark_block_bitmap_range2(fs->block_map, group_blk, fs->inode_blocks_per_group);
}
group_blk += fs->inode_blocks_per_group;
// We may need to move some blocks away (mainly if extending inode tables)
for (blk = ext2fs_inode_table_loc(fs, g); blk < group_blk; blk++) {
if (blk < ext2fs_blocks_count(old_fs->super) &&
ext2fs_test_block_bitmap2(old_fs->block_map, blk) &&
!ext2fs_test_block_bitmap2(meta_bmap, blk)) {
ext2fs_mark_block_bitmap2(rfs->move_blocks, blk);
rfs->needed_blocks++;
}
ext2fs_mark_block_bitmap2(rfs->reserve_blocks, blk);
}
}
ext2fs_free_block_bitmap(empty_bmap);
}
/*
* If we're shrinking the filesystem, we need to move any
* group's metadata blocks (either allocation bitmaps or the
@ -1387,7 +1496,7 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
if (retval)
goto errout;
retval = mark_table_blocks(fs, new_meta_bmap);
retval = mark_table_blocks(fs, new_meta_bmap, 0);
if (retval)
goto errout;
}
@ -1848,7 +1957,7 @@ static int process_block(ext2_filsys fs, blk64_t *block_nr,
}
}
if (pb->is_dir) {
if (pb->is_dir && fs->dblist) {
retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
block, (int) blockcnt);
if (retval) {
@ -1895,27 +2004,27 @@ static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
errcode_t err = 0;
/* No EA block or no remapping? Quit early. */
if (ext2fs_file_acl_block(rfs->old_fs, inode) == 0 || !rfs->bmap)
if (ext2fs_file_acl_block(rfs->new_fs, inode) == 0 || !rfs->bmap)
return 0;
new_block = extent_translate(rfs->old_fs, rfs->bmap,
ext2fs_file_acl_block(rfs->old_fs, inode));
new_block = extent_translate(rfs->new_fs, rfs->bmap,
ext2fs_file_acl_block(rfs->new_fs, inode));
if (new_block == 0)
return 0;
/* Set the new ACL block */
ext2fs_file_acl_block_set(rfs->old_fs, inode, new_block);
ext2fs_file_acl_block_set(rfs->new_fs, inode, new_block);
/* Update checksum */
if (ext2fs_has_feature_metadata_csum(rfs->new_fs->super)) {
err = ext2fs_get_mem(rfs->old_fs->blocksize, &buf);
err = ext2fs_get_mem(rfs->new_fs->blocksize, &buf);
if (err)
return err;
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
err = ext2fs_read_ext_attr3(rfs->old_fs, new_block, buf, ino);
rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
err = ext2fs_read_ext_attr3(rfs->new_fs, new_block, buf, ino);
rfs->new_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (err)
goto out;
err = ext2fs_write_ext_attr3(rfs->old_fs, new_block, buf, ino);
err = ext2fs_write_ext_attr3(rfs->new_fs, new_block, buf, ino);
if (err)
goto out;
}
@ -1986,52 +2095,62 @@ static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)),
{
}
static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
#define TRANSLATE_IPG(ino,old_fs,new_fs) (1 + (((ino)-1) % (old_fs)->super->s_inodes_per_group) + \
((ino)-1) / (old_fs)->super->s_inodes_per_group * (new_fs)->super->s_inodes_per_group)
/**
* Move inodes and rewrite references to blocks moved in blocks_to_move()
*/
static errcode_t inodes_to_move(ext2_resize_t rfs)
{
struct process_block_struct pb;
ext2_ino_t ino, new_inode;
ext2_ino_t ino, new_inode, tr_ino;
struct ext2_inode *inode = NULL;
ext2_inode_scan scan = NULL;
errcode_t retval;
dgrp_t g, old_g;
char *block_buf = 0;
ext2_ino_t start_to_move;
int inode_size;
int shrink_inodes = rfs->old_fs->inode_blocks_per_group > rfs->new_fs->inode_blocks_per_group;
int change_inodes = rfs->old_fs->inode_blocks_per_group != rfs->new_fs->inode_blocks_per_group;
if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) &&
!rfs->bmap)
!rfs->bmap &&
!change_inodes)
return 0;
set_com_err_hook(quiet_com_err_proc);
retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
if (retval) goto errout;
retval = ext2fs_init_dblist(rfs->old_fs, 0);
if (retval) goto errout;
retval = ext2fs_get_array(rfs->old_fs->blocksize, 3, &block_buf);
if (retval) goto errout;
start_to_move = (rfs->new_fs->group_desc_count *
rfs->new_fs->super->s_inodes_per_group);
if (rfs->progress) {
retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
0, rfs->old_fs->group_desc_count);
if (retval)
goto errout;
rfs->old_fs->super->s_inodes_per_group);
for (ino = start_to_move+1; ino <= rfs->old_fs->group_desc_count * rfs->old_fs->super->s_inodes_per_group; ino++) {
ext2fs_mark_inode_bitmap2(rfs->old_fs->inode_map, ino);
}
ext2fs_set_inode_callback(scan, progress_callback, (void *) rfs);
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
new_inode = EXT2_FIRST_INODE(rfs->new_fs->super);
if (shrink_inodes) {
for (g = 0; g < rfs->old_fs->group_desc_count; g++) {
ext2_ino_t first_ino = 1+rfs->old_fs->super->s_inodes_per_group*g;
for (ino = first_ino+rfs->new_fs->super->s_inodes_per_group; ino < first_ino+rfs->old_fs->super->s_inodes_per_group; ino++) {
ext2fs_mark_inode_bitmap2(rfs->old_fs->inode_map, ino);
}
}
}
retval = ext2fs_get_array(rfs->new_fs->blocksize, 3, &block_buf);
if (retval) goto errout;
inode_size = EXT2_INODE_SIZE(rfs->new_fs->super);
inode = malloc(inode_size);
if (!inode) {
retval = ENOMEM;
goto errout;
}
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
/*
* First, copy all of the inodes that need to be moved
* elsewhere in the inode table
@ -2045,78 +2164,68 @@ static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
continue; /* inode not in use */
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
pb.changed = 0;
/* Remap EA block */
retval = migrate_ea_block(rfs, ino, inode, &pb.changed);
if (retval)
goto errout;
new_inode = ino;
if (ino <= start_to_move)
goto remap_blocks; /* Don't need to move inode. */
if (ino > start_to_move ||
(shrink_inodes && (ino-1) % rfs->old_fs->super->s_inodes_per_group >= rfs->new_fs->super->s_inodes_per_group))
{
/*
* Find a new inode. Now that extents and directory blocks
* are tied to the inode number through the checksum, we must
* set up the new inode before we start rewriting blocks.
*/
retval = ext2fs_new_inode(rfs->old_fs, 0, 0, 0, &new_inode);
if (retval)
goto errout;
/*
* Find a new inode. Now that extents and directory blocks
* are tied to the inode number through the checksum, we must
* set up the new inode before we start rewriting blocks.
*/
retval = ext2fs_new_inode(rfs->new_fs, 0, 0, 0, &new_inode);
if (retval)
goto errout;
/* Translate inode number according to new inodes_per_group */
tr_ino = TRANSLATE_IPG(new_inode, rfs->old_fs, rfs->new_fs);
ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
pb.is_dir);
inode->i_ctime = time(0);
retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
inode, inode_size);
if (retval)
goto errout;
pb.changed = 0;
old_g = ext2fs_group_of_ino(rfs->old_fs, ino);
ext2fs_inode_alloc_stats2(rfs->old_fs, new_inode, +1, LINUX_S_ISDIR(inode->i_mode));
if (old_g < rfs->new_fs->group_desc_count)
{
/*
* Don't bother to adjust free_inodes_count for group because freed
* inodes will be accounted as part of inodes_per_group change
*/
if (LINUX_S_ISDIR(inode->i_mode))
ext2fs_bg_used_dirs_count_set(rfs->new_fs, old_g, ext2fs_bg_used_dirs_count(rfs->new_fs, old_g) - 1);
ext2fs_group_desc_csum_set(rfs->new_fs, old_g);
}
ext2fs_inode_alloc_stats2(rfs->new_fs, tr_ino, +1, LINUX_S_ISDIR(inode->i_mode));
inode->i_ctime = time(0);
retval = ext2fs_write_inode_full(rfs->old_fs, new_inode, inode, inode_size);
if (retval)
goto errout;
#ifdef RESIZE2FS_DEBUG
if (rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode moved %u->%u\n", ino, new_inode);
if (rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode moved %u->%u\n", ino, new_inode);
#endif
if (!rfs->imap) {
retval = ext2fs_create_extent_table(&rfs->imap, 0);
if (retval)
goto errout;
}
ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
remap_blocks:
if (pb.changed)
retval = ext2fs_write_inode_full(rfs->old_fs,
new_inode,
inode, inode_size);
if (retval)
goto errout;
if (!rfs->imap) {
retval = ext2fs_create_extent_table(&rfs->imap, 0);
if (retval)
goto errout;
}
ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
/* Rewrite extent block checksums with new inode number */
if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) &&
(inode->i_flags & EXT4_EXTENTS_FL)) {
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = rewrite_extents(rfs->old_fs, new_inode);
rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval)
goto errout;
// continue with rewritten inode number
ino = new_inode;
}
/*
* Update inodes to point to new blocks; schedule directory
* blocks for inode remapping. Need to write out dir blocks
* with new inode numbers if we have metadata_csum enabled.
* Update inodes to point to new blocks.
*/
if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) &&
(rfs->bmap || pb.is_dir)) {
pb.ino = new_inode;
pb.old_ino = ino;
if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) && rfs->bmap) {
pb.changed = 0;
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
pb.ino = ino;
pb.old_ino = ino; // FIXME debug output will show translated inodes
pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_block_iterate3(rfs->old_fs,
new_inode, 0, block_buf,
ino, 0, block_buf,
process_block, &pb);
rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval)
@ -2125,16 +2234,134 @@ remap_blocks:
retval = pb.error;
goto errout;
}
} else if ((inode->i_flags & EXT4_INLINE_DATA_FL) &&
(rfs->bmap || pb.is_dir)) {
}
}
errout:
if (scan)
ext2fs_close_inode_scan(scan);
if (block_buf)
ext2fs_free_mem(&block_buf);
free(inode);
return retval;
}
/**
* Remap extattr blocks, rewrite inode and extattr checksums
* and schedule directory blocks for inode remapping
*/
static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
{
struct process_block_struct pb;
ext2_ino_t ino, tr_ino;
struct ext2_inode *inode = NULL;
ext2_inode_scan scan = NULL;
errcode_t retval;
dgrp_t g;
char *block_buf = 0;
int inode_size;
int change_inodes = rfs->old_fs->inode_blocks_per_group != rfs->new_fs->inode_blocks_per_group;
if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) &&
!rfs->bmap &&
!change_inodes)
return 0;
set_com_err_hook(quiet_com_err_proc);
retval = ext2fs_open_inode_scan(rfs->new_fs, 0, &scan);
if (retval) goto errout;
retval = ext2fs_init_dblist(rfs->new_fs, 0);
if (retval) goto errout;
retval = ext2fs_get_array(rfs->new_fs->blocksize, 3, &block_buf);
if (retval) goto errout;
if (rfs->progress) {
retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
0, rfs->new_fs->group_desc_count);
if (retval)
goto errout;
}
ext2fs_set_inode_callback(scan, progress_callback, (void *) rfs);
inode_size = EXT2_INODE_SIZE(rfs->new_fs->super);
inode = malloc(inode_size);
if (!inode) {
retval = ENOMEM;
goto errout;
}
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
/*
* First, copy all of the inodes that need to be moved
* elsewhere in the inode table
*/
while (1) {
rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_get_next_inode_full(scan, &ino, inode, inode_size);
rfs->new_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval) goto errout;
if (!ino)
break;
if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
continue; /* inode not in use */
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
pb.changed = change_inodes;
/* Remap EA block */
retval = migrate_ea_block(rfs, ino, inode, &pb.changed);
if (retval)
goto errout;
if (pb.changed)
retval = ext2fs_write_inode_full(rfs->new_fs,
ino,
inode, inode_size);
if (retval)
goto errout;
/* Rewrite extent block checksums with new inode number */
if (ext2fs_has_feature_metadata_csum(rfs->new_fs->super) &&
(inode->i_flags & EXT4_EXTENTS_FL)) {
rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = rewrite_extents(rfs->new_fs, ino);
rfs->new_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval)
goto errout;
}
/*
* Schedule directory blocks for inode remapping. Need to write out dir blocks
* with new inode numbers if we have metadata_csum enabled.
*/
if (ext2fs_inode_has_valid_blocks2(rfs->new_fs, inode) && pb.is_dir) {
pb.ino = ino;
pb.old_ino = ino; // FIXME: Debug output will show translated inodes
pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_block_iterate3(rfs->new_fs,
ino, 0, block_buf,
process_block, &pb);
rfs->new_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval)
goto errout;
if (pb.error) {
retval = pb.error;
goto errout;
}
} else if ((inode->i_flags & EXT4_INLINE_DATA_FL) && pb.is_dir) {
/* inline data dir; update it too */
retval = ext2fs_add_dir_block2(rfs->old_fs->dblist,
new_inode, 0, 0);
retval = ext2fs_add_dir_block2(rfs->new_fs->dblist,
tr_ino, 0, 0);
if (retval)
goto errout;
}
}
io_channel_flush(rfs->old_fs->io);
io_channel_flush(rfs->new_fs->io);
errout:
reset_com_err_hook();
@ -2150,6 +2377,140 @@ errout:
return retval;
}
static errcode_t move_inode_tables(ext2_resize_t rfs)
{
dgrp_t g, end_g, group_count;
dgrp_t move_start, move_count;
__u32 old_ipg = rfs->old_fs->super->s_inodes_per_group;
__u32 new_ipg = rfs->new_fs->super->s_inodes_per_group;
__u32 old_ibg = rfs->old_fs->inode_blocks_per_group;
__u32 new_ibg = rfs->new_fs->inode_blocks_per_group;
int retval;
blk64_t size, blk, ib;
int change_inodes = old_ibg != new_ibg;
__u32 unused, ino, old_ino, i;
ext2fs_block_bitmap meta_bmap = NULL;
retval = ext2fs_allocate_block_bitmap(rfs->new_fs, _("meta-data blocks"), &meta_bmap);
if (retval)
return retval;
retval = mark_table_blocks(rfs->new_fs, meta_bmap, 1);
printf("inode blocks per group: %u to %u\n", old_ibg, new_ibg);
size = old_ibg;
size = size < new_ibg ? size : new_ibg;
move_count = 0;
group_count = rfs->new_fs->group_desc_count;
group_count = group_count < rfs->old_fs->group_desc_count ? group_count : rfs->old_fs->group_desc_count;
for (g = 0; g < group_count; g++)
{
if (change_inodes)
{
/*
* new_fs may have incorrect itable_unused because we're not adjusting it
* before calling inode_alloc_stats2 in inodes_to_move().
* and it's OK to take free_inodes_count from old_fs because any inodes
* that are freed may only lay in shrinked inode table areas.
*/
unused = ext2fs_bg_itable_unused(rfs->old_fs, g);
if (new_ipg < old_ipg && unused < old_ipg-new_ipg)
unused = 0;
else
unused = unused + new_ipg - old_ipg;
ext2fs_bg_itable_unused_set(rfs->new_fs, g, unused);
ext2fs_bg_free_inodes_count_set(rfs->new_fs, g,
ext2fs_bg_free_inodes_count(rfs->old_fs, g) + new_ipg - old_ipg);
ext2fs_group_desc_csum_set(rfs->new_fs, g);
}
printf("g%u = [%u?] %u->%u free=%u->%u unused=%u->%u\n", g,
g > 0 ? ext2fs_inode_table_loc(rfs->old_fs, g-1)+old_ibg : 0,
ext2fs_inode_table_loc(rfs->old_fs, g),
ext2fs_inode_table_loc(rfs->new_fs, g),
ext2fs_bg_free_inodes_count(rfs->old_fs, g),
ext2fs_bg_free_inodes_count(rfs->new_fs, g),
ext2fs_bg_itable_unused(rfs->old_fs, g),
ext2fs_bg_itable_unused(rfs->new_fs, g));
if (change_inodes || ext2fs_inode_table_loc(rfs->new_fs, g) != ext2fs_inode_table_loc(rfs->old_fs, g))
{
move_count++;
if (move_count == 1)
move_start = g;
// check for overlap
if (g < rfs->new_fs->group_desc_count-1 &&
ext2fs_inode_table_loc(rfs->new_fs, g)+new_ibg > ext2fs_inode_table_loc(rfs->old_fs, g+1))
{
continue;
}
}
if (move_count > 0)
{
end_g = g;
for (i = 0, g = move_start+move_count-1; i < move_count; i++, g--)
{
if (!ext2fs_has_group_desc_csum(rfs->new_fs) ||
!ext2fs_bg_flags_test(rfs->new_fs, g, EXT2_BG_INODE_UNINIT))
{
retval = io_channel_read_blk64(rfs->new_fs->io, ext2fs_inode_table_loc(rfs->old_fs, g), size, rfs->itable_buf);
if (retval)
return retval;
retval = io_channel_write_blk64(rfs->new_fs->io, ext2fs_inode_table_loc(rfs->new_fs, g), size, rfs->itable_buf);
if (retval)
return retval;
if (new_ibg > old_ibg)
{
retval = ext2fs_zero_blocks2(rfs->new_fs,
ext2fs_inode_table_loc(rfs->new_fs, g)+old_ibg,
new_ibg-old_ibg, NULL, NULL);
if (retval)
return retval;
}
}
blk = ext2fs_inode_table_loc(rfs->old_fs, g);
for (ib = 0; ib < old_ibg; ib++)
if (!ext2fs_test_block_bitmap2(meta_bmap, blk+ib))
ext2fs_unmark_block_bitmap2(rfs->new_fs->block_map, blk+ib);
ext2fs_mark_block_bitmap_range2(rfs->new_fs->block_map, ext2fs_inode_table_loc(rfs->new_fs, g), new_ibg);
}
move_start = move_count = 0;
g = end_g;
}
}
if (old_ipg < new_ipg)
{
for (g = 0; g < group_count; g++)
{
for (i = 0, old_ino = 1 + old_ipg*g, ino = 1 + new_ipg*g; i < old_ipg; i++, ino++, old_ino++)
{
if (ext2fs_test_inode_bitmap2(rfs->old_fs->inode_map, old_ino))
ext2fs_mark_inode_bitmap2(rfs->new_fs->inode_map, ino);
else
ext2fs_unmark_inode_bitmap2(rfs->new_fs->inode_map, ino);
}
for (i = old_ipg, ino = 1 + new_ipg*g + old_ipg; i < new_ipg; i++, ino++)
ext2fs_unmark_inode_bitmap2(rfs->new_fs->inode_map, ino);
}
}
else if (old_ipg > new_ipg)
{
dgrp_t n;
for (n = 0, g = group_count-1; n < group_count; n++, g--)
{
for (i = 0, old_ino = 1 + old_ipg*g, ino = 1 + new_ipg*g; i < new_ipg; i++, ino++, old_ino++)
{
if (ext2fs_test_inode_bitmap2(rfs->old_fs->inode_map, old_ino))
ext2fs_mark_inode_bitmap2(rfs->new_fs->inode_map, ino);
else
ext2fs_unmark_inode_bitmap2(rfs->new_fs->inode_map, ino);
}
}
}
ext2fs_free_block_bitmap(meta_bmap);
return 0;
}
/* --------------------------------------------------------------------
*
* Resize processing, phase 4.
@ -2176,9 +2537,10 @@ static int check_and_change_inodes(ext2_ino_t dir,
ext2_ino_t new_inode;
errcode_t retval;
int ret = 0;
ext2_ino_t old_dir = TRANSLATE_IPG(dir, is->rfs->new_fs, is->rfs->old_fs);
if (is->rfs->progress && offset == 0) {
io_channel_flush(is->rfs->old_fs->io);
io_channel_flush(is->rfs->new_fs->io);
is->err = (is->rfs->progress)(is->rfs,
E2_RSZ_INODE_REF_UPD_PASS,
++is->num, is->max_dirs);
@ -2191,16 +2553,25 @@ static int check_and_change_inodes(ext2_ino_t dir,
* old fs, then we must rewrite all dir blocks with new checksums.
*/
if (ext2fs_has_feature_metadata_csum(is->rfs->old_fs->super) &&
!ext2fs_test_inode_bitmap2(is->rfs->old_fs->inode_map, dir))
!ext2fs_test_inode_bitmap2(is->rfs->old_fs->inode_map, old_dir))
ret |= DIRENT_CHANGED;
if (!dirent->inode)
{
if (old_dir != dir)
ret |= DIRENT_CHANGED;
return ret;
}
new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
new_inode = 0;
if (is->rfs->imap)
new_inode = ext2fs_extent_translate(is->rfs->imap, dirent->inode);
if (!new_inode)
new_inode = dirent->inode;
new_inode = TRANSLATE_IPG(new_inode, is->rfs->old_fs, is->rfs->new_fs);
if (new_inode == dirent->inode && old_dir == dir)
return ret;
#ifdef RESIZE2FS_DEBUG
if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n",
@ -2211,10 +2582,10 @@ static int check_and_change_inodes(ext2_ino_t dir,
dirent->inode = new_inode;
/* Update the directory mtime and ctime */
retval = ext2fs_read_inode(is->rfs->old_fs, dir, &inode);
retval = ext2fs_read_inode(is->rfs->new_fs, dir, &inode);
if (retval == 0) {
inode.i_mtime = inode.i_ctime = time(0);
is->err = ext2fs_write_inode(is->rfs->old_fs, dir, &inode);
is->err = ext2fs_write_inode(is->rfs->new_fs, dir, &inode);
if (is->err)
return ret | DIRENT_ABORT;
}
@ -2226,8 +2597,9 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
{
errcode_t retval;
struct istruct is;
int change_inodes = rfs->old_fs->inode_blocks_per_group != rfs->new_fs->inode_blocks_per_group;
if (!rfs->imap)
if (!change_inodes && !rfs->imap)
return 0;
/*
@ -2235,7 +2607,7 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
* inode references
*/
is.num = 0;
is.max_dirs = ext2fs_dblist_count2(rfs->old_fs->dblist);
is.max_dirs = ext2fs_dblist_count2(rfs->new_fs->dblist);
is.rfs = rfs;
is.err = 0;
@ -2246,11 +2618,11 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
goto errout;
}
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_dblist_dir_iterate(rfs->new_fs->dblist,
DIRENT_FLAG_INCLUDE_EMPTY, 0,
check_and_change_inodes, &is);
rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
rfs->new_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval)
goto errout;
if (is.err) {
@ -2263,7 +2635,8 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
is.max_dirs, is.max_dirs);
errout:
ext2fs_free_extent_table(rfs->imap);
if (rfs->imap)
ext2fs_free_extent_table(rfs->imap);
rfs->imap = 0;
return retval;
}
@ -2319,7 +2692,7 @@ static errcode_t move_itables(ext2_resize_t rfs)
if (retval)
return retval;
retval = mark_table_blocks(fs, new_bmap);
retval = mark_table_blocks(fs, new_bmap, 0);
if (retval)
goto errout;
}
@ -2431,7 +2804,7 @@ static errcode_t move_itables(ext2_resize_t rfs)
goto errout;
}
}
mark_table_blocks(fs, fs->block_map);
mark_table_blocks(fs, fs->block_map, 0);
ext2fs_flush(fs);
#ifdef RESIZE2FS_DEBUG
if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE)

View File

@ -141,7 +141,7 @@ struct ext2_resize_struct {
/* prototypes */
extern errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
extern errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, __u32 new_inodes, int flags,
errcode_t (*progress)(ext2_resize_t rfs,
int pass, unsigned long cur,
unsigned long max));