e2patch utility for applying/restoring patches

master
Vitaliy Filippov 2014-01-07 10:31:13 +00:00
parent f94c155aab
commit ee1b499ca9
5 changed files with 282 additions and 44 deletions

166
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 "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
View File

@ -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
View File

@ -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

View File

@ -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)

View File

@ -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