e2patch utility for applying/restoring patches
parent
f94c155aab
commit
ee1b499ca9
|
@ -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 "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, backup;
|
||||
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;
|
||||
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 = retry_read_at(patch.patch_fd, 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;
|
||||
}
|
54
patch.c
54
patch.c
|
@ -1,3 +1,24 @@
|
|||
/**
|
||||
* 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
|
||||
|
||||
|
@ -20,7 +41,7 @@ errcode_t retry_read(int fd, ssize_t size, void *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
errcode_t retry_write(int fd, ssize_t size, void *buf)
|
||||
errcode_t retry_write(int fd, ssize_t size, const void *buf)
|
||||
{
|
||||
ssize_t r, done = 0;
|
||||
while (done < size)
|
||||
|
@ -37,14 +58,14 @@ errcode_t retry_write(int fd, ssize_t size, void *buf)
|
|||
|
||||
errcode_t retry_read_at(int fd, unsigned long long offset, ssize_t size, void *buf)
|
||||
{
|
||||
if ((unsigned)ext2fs_llseek(fd, offset, SEEK_SET) != offset)
|
||||
if ((unsigned long long)ext2fs_llseek(fd, offset, SEEK_SET) != offset)
|
||||
return errno ? errno : EXT2_ET_LLSEEK_FAILED;
|
||||
return retry_read(fd, size, buf);
|
||||
}
|
||||
|
||||
errcode_t retry_write_at(int fd, unsigned long long offset, ssize_t size, void *buf)
|
||||
errcode_t retry_write_at(int fd, unsigned long long offset, ssize_t size, const void *buf)
|
||||
{
|
||||
if ((unsigned)ext2fs_llseek(fd, offset, SEEK_SET) != offset)
|
||||
if ((unsigned long long)ext2fs_llseek(fd, offset, SEEK_SET) != offset)
|
||||
return errno ? errno : EXT2_ET_LLSEEK_FAILED;
|
||||
return retry_write(fd, size, buf);
|
||||
}
|
||||
|
@ -101,7 +122,7 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
errcode_t ext2fs_patch_open(struct ext2fs_patch_file *data, char *patch_file)
|
||||
errcode_t ext2fs_patch_open(struct ext2fs_patch_file *data, char *patch_file, int flags)
|
||||
{
|
||||
errcode_t retval = 0;
|
||||
ext2_loff_t size;
|
||||
|
@ -109,7 +130,7 @@ errcode_t ext2fs_patch_open(struct ext2fs_patch_file *data, char *patch_file)
|
|||
data->size = 0;
|
||||
data->bmap = NULL;
|
||||
data->patch_file = strdup(patch_file);
|
||||
data->patch_fd = open(data->patch_file, O_CREAT|O_RDWR, 0666);
|
||||
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);
|
||||
|
@ -120,8 +141,7 @@ errcode_t ext2fs_patch_open(struct ext2fs_patch_file *data, char *patch_file)
|
|||
size = ext2fs_llseek(data->patch_fd, size-sizeof(__u32)-sizeof(blk64_t), SEEK_SET);
|
||||
read(data->patch_fd, &data->block_size, sizeof(__u32));
|
||||
read(data->patch_fd, &data->size, sizeof(blk64_t));
|
||||
retval = ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, NULL,
|
||||
0, data->size, data->size, "overwritten blocks", 0, &data->bmap);
|
||||
retval = ext2fs_patch_init_bmap(data, NULL);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = ext2fs_patch_read_bmap(data);
|
||||
|
@ -173,3 +193,21 @@ errcode_t ext2fs_patch_init_bmap(struct ext2fs_patch_file *data, io_channel chan
|
|||
}
|
||||
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 retry_write_at(data->patch_fd, block*data->block_size, size, buf);
|
||||
}
|
||||
|
|
34
patch.h
34
patch.h
|
@ -1,3 +1,30 @@
|
|||
/**
|
||||
* 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
|
||||
|
||||
|
@ -14,13 +41,14 @@ struct ext2fs_patch_file
|
|||
};
|
||||
|
||||
errcode_t retry_read(int fd, ssize_t size, void *buf);
|
||||
errcode_t retry_write(int fd, ssize_t size, void *buf);
|
||||
errcode_t retry_write(int fd, ssize_t size, const void *buf);
|
||||
errcode_t retry_read_at(int fd, unsigned long long offset, ssize_t size, void *buf);
|
||||
errcode_t retry_write_at(int fd, unsigned long long offset, ssize_t size, void *buf);
|
||||
errcode_t 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);
|
||||
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
|
||||
|
|
50
patch_io.c
50
patch_io.c
|
@ -2,19 +2,22 @@
|
|||
* patch_io.c --- This is the "patch" io manager that writes the new data into
|
||||
* a separate sparse file to apply it later.
|
||||
*
|
||||
* Patch file format:
|
||||
* 1) sparse data blocks - same size as the 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
|
||||
*
|
||||
* %Begin-Header%
|
||||
* This file may be redistributed under the terms of the GNU Library
|
||||
* General Public License, version 2 or later.
|
||||
* %End-Header%
|
||||
* 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
|
||||
|
@ -133,7 +136,7 @@ static errcode_t patch_open(const char *name, int flags, io_channel *channel)
|
|||
|
||||
if (patch_file)
|
||||
{
|
||||
retval = ext2fs_patch_open(&data->patch, patch_file);
|
||||
retval = ext2fs_patch_open(&data->patch, patch_file, O_CREAT);
|
||||
if (retval)
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -232,9 +235,9 @@ static errcode_t patch_read_blk64(io_channel channel, unsigned long long block,
|
|||
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 && data->real)
|
||||
if (n > 0)
|
||||
{
|
||||
retval = io_channel_read_blk64(data->real, block+b, n, buf+(block+b)*channel->block_size);
|
||||
retval = io_channel_read_blk64(data->real, block+b, n, buf+b*channel->block_size);
|
||||
if (retval)
|
||||
break;
|
||||
b += n;
|
||||
|
@ -253,34 +256,15 @@ static errcode_t patch_write_blk64(io_channel channel, unsigned long long block,
|
|||
{
|
||||
struct patch_private_data *data;
|
||||
errcode_t retval = 0;
|
||||
ssize_t r, left = count*channel->block_size;
|
||||
|
||||
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 ((unsigned)ext2fs_llseek(data->patch.patch_fd, block*channel->block_size, SEEK_SET) != block*channel->block_size)
|
||||
return errno ? errno : EXT2_ET_LLSEEK_FAILED;
|
||||
retval = ext2fs_patch_init_bmap(&data->patch, channel);
|
||||
if (retval)
|
||||
return retval;
|
||||
if (count < 0)
|
||||
{
|
||||
if (-count > channel->block_size)
|
||||
return EINVAL;
|
||||
left = -count;
|
||||
count = 1;
|
||||
}
|
||||
ext2fs_mark_block_bitmap_range2(data->patch.bmap, block, count);
|
||||
buf += left;
|
||||
while (left > 0)
|
||||
{
|
||||
r = write(data->patch.patch_fd, buf-left, left);
|
||||
if (r < 0 && errno != EAGAIN)
|
||||
break;
|
||||
left -= r;
|
||||
}
|
||||
return left ? errno : 0;
|
||||
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)
|
||||
|
|
22
patch_io.h
22
patch_io.h
|
@ -1,3 +1,25 @@
|
|||
/*
|
||||
* patch_io.h --- Definitions to use "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
|
||||
*/
|
||||
|
||||
#ifndef PATCH_IO_H
|
||||
#define PATCH_IO_H
|
||||
|
||||
|
|
Loading…
Reference in New Issue