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/e2label.8
misc/e2undo misc/e2undo
misc/e2undo.8 misc/e2undo.8
misc/e2patch
misc/e2patch.8
misc/e4defrag misc/e4defrag
misc/e4defrag.8 misc/e4defrag.8
misc/ext4.5 misc/ext4.5

View File

@ -108,7 +108,7 @@ AC_DEFUN(
[CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command, [CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command,
_cv_gnu_make_command='' ; _cv_gnu_make_command='' ;
dnl Search all the common names for GNU make 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" MAKES="make"
else else
MAKES="make gmake gnumake" 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 $as_echo_n "(cached) " >&6
else else
_cv_gnu_make_command='' ; _cv_gnu_make_command='' ;
if test -z "$FORCE_NATIVE_MAKE" ; then if test -n "$FORCE_NATIVE_MAKE" ; then
MAKES="make" MAKES="make"
else else
MAKES="make gmake gnumake" MAKES="make gmake gnumake"

View File

@ -345,6 +345,12 @@ or
.B \-p .B \-p
options. options.
.TP .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" .BI \-z " undo_file"
Before overwriting a file system block, write the old contents of the block to 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 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; ext2fs_inode_bitmap inodes_to_rebuild;
/* Patch file */
char *patch_file;
/* Undo file */ /* Undo file */
char *undo_file; char *undo_file;
}; };

View File

@ -76,7 +76,7 @@ static void usage(e2fsck_t ctx)
fprintf(stderr, fprintf(stderr,
_("Usage: %s [-panyrcdfktvDFV] [-b superblock] [-B blocksize]\n" _("Usage: %s [-panyrcdfktvDFV] [-b superblock] [-B blocksize]\n"
"\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\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); ctx->program_name);
fprintf(stderr, "%s", _("\nEmergency help:\n" 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" " -j external_journal Set location of the external journal\n"
" -l bad_blocks_file Add to badblocks list\n" " -l bad_blocks_file Add to badblocks list\n"
" -L bad_blocks_file Set 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" " -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; phys_mem_kb = get_memory_size() / 1024;
ctx->readahead_kb = ~0ULL; 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) { switch (c) {
case 'C': case 'C':
ctx->progress = e2fsck_update_progress; ctx->progress = e2fsck_update_progress;
@ -936,6 +937,9 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
case 'k': case 'k':
keep_bad_blocks++; keep_bad_blocks++;
break; break;
case 'T':
ctx->patch_file = optarg;
break;
case 'z': case 'z':
ctx->undo_file = optarg; ctx->undo_file = optarg;
break; break;
@ -1233,6 +1237,19 @@ check_error:
return retval; 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) static int e2fsck_setup_tdb(e2fsck_t ctx, io_manager *io_ptr)
{ {
errcode_t retval = ENOMEM; errcode_t retval = ENOMEM;
@ -1430,7 +1447,11 @@ restart:
flags &= ~EXT2_FLAG_EXCLUSIVE; 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); retval = e2fsck_setup_tdb(ctx, &io_ptr);
if (retval) if (retval)
exit(FSCK_ERROR); exit(FSCK_ERROR);

View File

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

View File

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

View File

@ -124,6 +124,8 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
symlink.o \ symlink.o \
$(TDB_OBJ) \ $(TDB_OBJ) \
undo_io.o \ undo_io.o \
patch.o \
patch_io.o \
unix_io.o \ unix_io.o \
unlink.o \ unlink.o \
valid_blk.o \ valid_blk.o \
@ -211,6 +213,8 @@ SRCS= ext2_err.c \
$(srcdir)/tst_getsize.c \ $(srcdir)/tst_getsize.c \
$(srcdir)/tst_iscan.c \ $(srcdir)/tst_iscan.c \
$(srcdir)/undo_io.c \ $(srcdir)/undo_io.c \
$(srcdir)/patch.c \
$(srcdir)/patch_io.c \
$(srcdir)/unix_io.c \ $(srcdir)/unix_io.c \
$(srcdir)/unlink.c \ $(srcdir)/unlink.c \
$(srcdir)/valid_blk.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_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_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.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 \ unix_io.o: $(srcdir)/unix_io.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.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_backing_manager(io_manager manager);
extern errcode_t set_undo_io_backup_file(char *file_name); 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 */ /* test_io.c */
extern io_manager test_io_manager, test_io_backing_manager; extern io_manager test_io_manager, test_io_backing_manager;
extern void (*test_io_cb_read_blk) 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; goto short_write;
size -= actual; size -= actual;
buf += actual; buf += actual;
location += actual;
} }
return 0; return 0;

View File

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

View File

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

View File

@ -46,8 +46,8 @@ static char *device_name, *io_options;
static void usage (char *prog) static void usage (char *prog)
{ {
fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-P] " fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-N new_inodes] [-P] "
"[-p] device [-b|-s|new_size] [-z undo_file]\n\n"), "[-p] device [-b|-s|new_size] [-T patch_file] [-z undo_file]\n\n"),
prog); prog);
exit (1); 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, static int resize2fs_setup_tdb(const char *device, char *undo_file,
io_manager *io_ptr) io_manager *io_ptr)
{ {
@ -258,6 +272,7 @@ int main (int argc, char ** argv)
blk64_t new_size = 0; blk64_t new_size = 0;
blk64_t max_size = 0; blk64_t max_size = 0;
blk64_t min_size = 0; blk64_t min_size = 0;
__u32 new_inodes = 0;
io_manager io_ptr; io_manager io_ptr;
char *new_size_str = 0; char *new_size_str = 0;
int use_stride = -1; int use_stride = -1;
@ -267,7 +282,7 @@ int main (int argc, char ** argv)
unsigned int blocksize; unsigned int blocksize;
long sysval; long sysval;
int len, mount_flags; int len, mount_flags;
char *mtpt, *undo_file = NULL; char *mtpt, *undo_file = NULL, *patch_file = NULL;
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
setlocale(LC_MESSAGES, ""); setlocale(LC_MESSAGES, "");
@ -284,7 +299,7 @@ int main (int argc, char ** argv)
if (argc && *argv) if (argc && *argv)
program_name = *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) { switch (c) {
case 'h': case 'h':
usage(program_name); usage(program_name);
@ -301,6 +316,9 @@ int main (int argc, char ** argv)
case 'P': case 'P':
print_min_size = 1; print_min_size = 1;
break; break;
case 'N':
new_inodes = strtoul(optarg, 0, 0);
break;
case 'd': case 'd':
flags |= atoi(optarg); flags |= atoi(optarg);
break; break;
@ -316,6 +334,9 @@ int main (int argc, char ** argv)
case 's': case 's':
flags |= RESIZE_DISABLE_64BIT; flags |= RESIZE_DISABLE_64BIT;
break; break;
case 'T':
patch_file = optarg;
break;
case 'z': case 'z':
undo_file = optarg; undo_file = optarg;
break; break;
@ -402,7 +423,11 @@ int main (int argc, char ** argv)
io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE;
io_flags |= EXT2_FLAG_64BITS; 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); retval = resize2fs_setup_tdb(device_name, undo_file, &io_ptr);
if (retval) if (retval)
exit(1); exit(1);
@ -583,7 +608,7 @@ int main (int argc, char ** argv)
"feature.\n")); "feature.\n"));
exit(1); 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) " fprintf(stderr, _("The filesystem is already %llu (%dk) "
"blocks long. Nothing to do!\n\n"), new_size, "blocks long. Nothing to do!\n\n"), new_size,
blocksize / 1024); blocksize / 1024);
@ -612,7 +637,7 @@ int main (int argc, char ** argv)
printf(_("Resizing the filesystem on " printf(_("Resizing the filesystem on "
"%s to %llu (%dk) blocks.\n"), "%s to %llu (%dk) blocks.\n"),
device_name, new_size, blocksize / 1024); 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) ? ((flags & RESIZE_PERCENT_COMPLETE) ?
resize_progress_func : 0)); 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 when the filesystem was created. This option allows the user to
explicitly specify a RAID stride setting to be used by resize2fs instead. explicitly specify a RAID stride setting to be used by resize2fs instead.
.TP .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" .BI \-z " undo_file"
Before overwriting a file system block, write the old contents of the block to 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 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 adjust_superblock(ext2_resize_t rfs, blk64_t new_size);
static errcode_t blocks_to_move(ext2_resize_t rfs); static errcode_t blocks_to_move(ext2_resize_t rfs);
static errcode_t block_mover(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_scan_and_fix(ext2_resize_t rfs);
static errcode_t inode_ref_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_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 fix_resize_inode(ext2_filsys fs);
static errcode_t ext2fs_calculate_summary_stats(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 fix_sb_journal_backup(ext2_filsys fs);
static errcode_t mark_table_blocks(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 clear_sparse_super2_last_group(ext2_resize_t rfs);
static errcode_t reserve_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); ext2fs_block_bitmap meta_bmap);
static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size); 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); 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.... * 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, errcode_t (*progress)(ext2_resize_t rfs, int pass,
unsigned long cur, unsigned long cur,
unsigned long max_val)) 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); print_resource_track(rfs, &rtrack, fs->io);
init_resource_track(&rtrack, "move_bg_metadata", 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) if (retval)
goto errout; goto errout;
print_resource_track(rfs, &rtrack, fs->io); 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; goto errout;
print_resource_track(rfs, &rtrack, fs->io); 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); init_resource_track(&rtrack, "inode_scan_and_fix", fs->io);
retval = inode_scan_and_fix(rfs); retval = inode_scan_and_fix(rfs);
if (retval) if (retval)
@ -200,11 +214,11 @@ errcode_t resize_fs(ext2_filsys fs, blk64_t *new_size, int flags,
goto errout; goto errout;
print_resource_track(rfs, &rtrack, fs->io); 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); retval = move_itables(rfs);
if (retval) if (retval)
goto errout; goto errout;
print_resource_track(rfs, &rtrack, fs->io); print_resource_track(rfs, &rtrack, fs->io);*/
retval = clear_sparse_super2_last_group(rfs); retval = clear_sparse_super2_last_group(rfs);
if (retval) if (retval)
@ -344,8 +358,39 @@ static errcode_t resize_group_descriptors(ext2_resize_t rfs, blk64_t new_size)
return 0; 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. */ /* 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; dgrp_t i;
blk64_t b, c, d, old_desc_blocks, new_desc_blocks, j; 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; errcode_t retval;
int cluster_ratio; 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; return 0;
retval = ext2fs_allocate_block_bitmap(rfs->old_fs, "oldfs", &old_map); 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) if (retval)
goto out; goto out;
retval = mark_table_blocks(fs, bg_map); retval = mark_table_blocks(fs, bg_map, 0);
if (retval) if (retval)
goto out; goto out;
} }
@ -1011,7 +1061,7 @@ retry:
* number of the block group descriptors. * number of the block group descriptors.
*/ */
if (reserve_blocks) if (reserve_blocks)
mark_table_blocks(fs, reserve_blocks); mark_table_blocks(fs, reserve_blocks, 0);
errout: errout:
return (retval); return (retval);
@ -1145,7 +1195,7 @@ errout:
* filesystem meta-data blocks. * filesystem meta-data blocks.
*/ */
static errcode_t mark_table_blocks(ext2_filsys fs, static errcode_t mark_table_blocks(ext2_filsys fs,
ext2fs_block_bitmap bmap) ext2fs_block_bitmap bmap, int skip_inode_tables)
{ {
dgrp_t i; dgrp_t i;
blk64_t blk; blk64_t blk;
@ -1156,10 +1206,12 @@ static errcode_t mark_table_blocks(ext2_filsys fs,
/* /*
* Mark the blocks used for the inode table * Mark the blocks used for the inode table
*/ */
blk = ext2fs_inode_table_loc(fs, i); if (!skip_inode_tables) {
if (blk) blk = ext2fs_inode_table_loc(fs, i);
ext2fs_mark_block_bitmap_range2(bmap, blk, if (blk)
fs->inode_blocks_per_group); ext2fs_mark_block_bitmap_range2(bmap, blk,
fs->inode_blocks_per_group);
}
/* /*
* Mark block used for the block bitmap * Mark block used for the block bitmap
@ -1283,12 +1335,69 @@ static errcode_t blocks_to_move(ext2_resize_t rfs)
if (retval) if (retval)
return retval; return retval;
retval = mark_table_blocks(old_fs, meta_bmap); retval = mark_table_blocks(old_fs, meta_bmap, 0);
if (retval) if (retval)
return retval; return retval;
fs = rfs->new_fs; 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 * If we're shrinking the filesystem, we need to move any
* group's metadata blocks (either allocation bitmaps or the * 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) if (retval)
goto errout; goto errout;
retval = mark_table_blocks(fs, new_meta_bmap); retval = mark_table_blocks(fs, new_meta_bmap, 0);
if (retval) if (retval)
goto errout; 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, retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
block, (int) blockcnt); block, (int) blockcnt);
if (retval) { if (retval) {
@ -1895,27 +2004,27 @@ static errcode_t migrate_ea_block(ext2_resize_t rfs, ext2_ino_t ino,
errcode_t err = 0; errcode_t err = 0;
/* No EA block or no remapping? Quit early. */ /* 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; return 0;
new_block = extent_translate(rfs->old_fs, rfs->bmap, new_block = extent_translate(rfs->new_fs, rfs->bmap,
ext2fs_file_acl_block(rfs->old_fs, inode)); ext2fs_file_acl_block(rfs->new_fs, inode));
if (new_block == 0) if (new_block == 0)
return 0; return 0;
/* Set the new ACL block */ /* 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 */ /* Update checksum */
if (ext2fs_has_feature_metadata_csum(rfs->new_fs->super)) { 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) if (err)
return err; return err;
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
err = ext2fs_read_ext_attr3(rfs->old_fs, new_block, buf, ino); err = ext2fs_read_ext_attr3(rfs->new_fs, new_block, buf, ino);
rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; rfs->new_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (err) if (err)
goto out; 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) if (err)
goto out; 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; struct process_block_struct pb;
ext2_ino_t ino, new_inode; ext2_ino_t ino, new_inode, tr_ino;
struct ext2_inode *inode = NULL; struct ext2_inode *inode = NULL;
ext2_inode_scan scan = NULL; ext2_inode_scan scan = NULL;
errcode_t retval; errcode_t retval;
dgrp_t g, old_g;
char *block_buf = 0; char *block_buf = 0;
ext2_ino_t start_to_move; ext2_ino_t start_to_move;
int inode_size; 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 <= if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) && rfs->new_fs->group_desc_count) &&
!rfs->bmap) !rfs->bmap &&
!change_inodes)
return 0; return 0;
set_com_err_hook(quiet_com_err_proc);
retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan); retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
if (retval) goto errout; 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 * start_to_move = (rfs->new_fs->group_desc_count *
rfs->new_fs->super->s_inodes_per_group); 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++) {
if (rfs->progress) { ext2fs_mark_inode_bitmap2(rfs->old_fs->inode_map, ino);
retval = (rfs->progress)(rfs, E2_RSZ_INODE_SCAN_PASS,
0, rfs->old_fs->group_desc_count);
if (retval)
goto errout;
} }
ext2fs_set_inode_callback(scan, progress_callback, (void *) rfs); if (shrink_inodes) {
pb.rfs = rfs; for (g = 0; g < rfs->old_fs->group_desc_count; g++) {
pb.inode = inode; ext2_ino_t first_ino = 1+rfs->old_fs->super->s_inodes_per_group*g;
pb.error = 0; for (ino = first_ino+rfs->new_fs->super->s_inodes_per_group; ino < first_ino+rfs->old_fs->super->s_inodes_per_group; ino++) {
new_inode = EXT2_FIRST_INODE(rfs->new_fs->super); 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_size = EXT2_INODE_SIZE(rfs->new_fs->super);
inode = malloc(inode_size); inode = malloc(inode_size);
if (!inode) { if (!inode) {
retval = ENOMEM; retval = ENOMEM;
goto errout; goto errout;
} }
pb.rfs = rfs;
pb.inode = inode;
pb.error = 0;
/* /*
* First, copy all of the inodes that need to be moved * First, copy all of the inodes that need to be moved
* elsewhere in the inode table * 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) if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
continue; /* inode not in use */ 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; new_inode = ino;
if (ino <= start_to_move) if (ino > start_to_move ||
goto remap_blocks; /* Don't need to move inode. */ (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;
/* /* Translate inode number according to new inodes_per_group */
* Find a new inode. Now that extents and directory blocks tr_ino = TRANSLATE_IPG(new_inode, rfs->old_fs, rfs->new_fs);
* 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;
ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1, old_g = ext2fs_group_of_ino(rfs->old_fs, ino);
pb.is_dir); ext2fs_inode_alloc_stats2(rfs->old_fs, new_inode, +1, LINUX_S_ISDIR(inode->i_mode));
inode->i_ctime = time(0); if (old_g < rfs->new_fs->group_desc_count)
retval = ext2fs_write_inode_full(rfs->old_fs, new_inode, {
inode, inode_size); /*
if (retval) * Don't bother to adjust free_inodes_count for group because freed
goto errout; * inodes will be accounted as part of inodes_per_group change
pb.changed = 0; */
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 #ifdef RESIZE2FS_DEBUG
if (rfs->flags & RESIZE_DEBUG_INODEMAP) if (rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode moved %u->%u\n", ino, new_inode); printf("Inode moved %u->%u\n", ino, new_inode);
#endif #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 (!rfs->imap) {
if (pb.changed) retval = ext2fs_create_extent_table(&rfs->imap, 0);
retval = ext2fs_write_inode_full(rfs->old_fs, if (retval)
new_inode, goto errout;
inode, inode_size); }
if (retval) ext2fs_add_extent_entry(rfs->imap, ino, new_inode);
goto errout;
/* Rewrite extent block checksums with new inode number */ // continue with rewritten inode number
if (ext2fs_has_feature_metadata_csum(rfs->old_fs->super) && ino = new_inode;
(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;
} }
/* /*
* Update inodes to point to new blocks; schedule directory * Update inodes to point to new blocks.
* 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->old_fs, inode) && if (ext2fs_inode_has_valid_blocks2(rfs->old_fs, inode) && rfs->bmap) {
(rfs->bmap || pb.is_dir)) { pb.changed = 0;
pb.ino = new_inode; pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
pb.old_ino = ino; pb.ino = ino;
pb.old_ino = ino; // FIXME debug output will show translated inodes
pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL; pb.has_extents = inode->i_flags & EXT4_EXTENTS_FL;
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_block_iterate3(rfs->old_fs, retval = ext2fs_block_iterate3(rfs->old_fs,
new_inode, 0, block_buf, ino, 0, block_buf,
process_block, &pb); process_block, &pb);
rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS; rfs->old_fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (retval) if (retval)
@ -2125,16 +2234,134 @@ remap_blocks:
retval = pb.error; retval = pb.error;
goto errout; 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 */ /* inline data dir; update it too */
retval = ext2fs_add_dir_block2(rfs->old_fs->dblist, retval = ext2fs_add_dir_block2(rfs->new_fs->dblist,
new_inode, 0, 0); tr_ino, 0, 0);
if (retval) if (retval)
goto errout; goto errout;
} }
} }
io_channel_flush(rfs->old_fs->io); io_channel_flush(rfs->new_fs->io);
errout: errout:
reset_com_err_hook(); reset_com_err_hook();
@ -2150,6 +2377,140 @@ errout:
return retval; 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. * Resize processing, phase 4.
@ -2176,9 +2537,10 @@ static int check_and_change_inodes(ext2_ino_t dir,
ext2_ino_t new_inode; ext2_ino_t new_inode;
errcode_t retval; errcode_t retval;
int ret = 0; 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) { 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, is->err = (is->rfs->progress)(is->rfs,
E2_RSZ_INODE_REF_UPD_PASS, E2_RSZ_INODE_REF_UPD_PASS,
++is->num, is->max_dirs); ++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. * old fs, then we must rewrite all dir blocks with new checksums.
*/ */
if (ext2fs_has_feature_metadata_csum(is->rfs->old_fs->super) && 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; ret |= DIRENT_CHANGED;
if (!dirent->inode) if (!dirent->inode)
{
if (old_dir != dir)
ret |= DIRENT_CHANGED;
return ret; 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) 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; return ret;
#ifdef RESIZE2FS_DEBUG #ifdef RESIZE2FS_DEBUG
if (is->rfs->flags & RESIZE_DEBUG_INODEMAP) if (is->rfs->flags & RESIZE_DEBUG_INODEMAP)
printf("Inode translate (dir=%u, name=%.*s, %u->%u)\n", 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; dirent->inode = new_inode;
/* Update the directory mtime and ctime */ /* 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) { if (retval == 0) {
inode.i_mtime = inode.i_ctime = time(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) if (is->err)
return ret | DIRENT_ABORT; return ret | DIRENT_ABORT;
} }
@ -2226,8 +2597,9 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
{ {
errcode_t retval; errcode_t retval;
struct istruct is; 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; return 0;
/* /*
@ -2235,7 +2607,7 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
* inode references * inode references
*/ */
is.num = 0; 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.rfs = rfs;
is.err = 0; is.err = 0;
@ -2246,11 +2618,11 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
goto errout; goto errout;
} }
rfs->old_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS; rfs->new_fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist, retval = ext2fs_dblist_dir_iterate(rfs->new_fs->dblist,
DIRENT_FLAG_INCLUDE_EMPTY, 0, DIRENT_FLAG_INCLUDE_EMPTY, 0,
check_and_change_inodes, &is); 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) if (retval)
goto errout; goto errout;
if (is.err) { if (is.err) {
@ -2263,7 +2635,8 @@ static errcode_t inode_ref_fix(ext2_resize_t rfs)
is.max_dirs, is.max_dirs); is.max_dirs, is.max_dirs);
errout: errout:
ext2fs_free_extent_table(rfs->imap); if (rfs->imap)
ext2fs_free_extent_table(rfs->imap);
rfs->imap = 0; rfs->imap = 0;
return retval; return retval;
} }
@ -2319,7 +2692,7 @@ static errcode_t move_itables(ext2_resize_t rfs)
if (retval) if (retval)
return retval; return retval;
retval = mark_table_blocks(fs, new_bmap); retval = mark_table_blocks(fs, new_bmap, 0);
if (retval) if (retval)
goto errout; goto errout;
} }
@ -2431,7 +2804,7 @@ static errcode_t move_itables(ext2_resize_t rfs)
goto errout; goto errout;
} }
} }
mark_table_blocks(fs, fs->block_map); mark_table_blocks(fs, fs->block_map, 0);
ext2fs_flush(fs); ext2fs_flush(fs);
#ifdef RESIZE2FS_DEBUG #ifdef RESIZE2FS_DEBUG
if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE)

View File

@ -141,7 +141,7 @@ struct ext2_resize_struct {
/* prototypes */ /* 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, errcode_t (*progress)(ext2_resize_t rfs,
int pass, unsigned long cur, int pass, unsigned long cur,
unsigned long max)); unsigned long max));