2014-01-07 03:06:01 +04:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*
|
2014-01-07 14:31:13 +04:00
|
|
|
* 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
|
2014-01-07 03:06:01 +04:00
|
|
|
*/
|
|
|
|
|
|
|
|
#define _LARGEFILE_SOURCE
|
|
|
|
#define _LARGEFILE64_SOURCE
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include <ext2fs/ext2_fs.h>
|
|
|
|
#include <ext2fs/ext2fs.h>
|
|
|
|
|
2014-01-07 12:07:24 +04:00
|
|
|
#include "patch.h"
|
|
|
|
|
2014-01-07 03:06:01 +04:00
|
|
|
#ifdef __GNUC__
|
|
|
|
#define ATTR(x) __attribute__(x)
|
|
|
|
#else
|
|
|
|
#define ATTR(x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define EXT2_CHECK_MAGIC(struct, code) if ((struct)->magic != (code)) return (code)
|
|
|
|
|
2014-01-07 12:07:24 +04:00
|
|
|
struct patch_private_data
|
|
|
|
{
|
|
|
|
int magic;
|
|
|
|
struct ext2fs_patch_file patch;
|
2014-01-07 03:06:01 +04:00
|
|
|
/* 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,
|
2014-01-07 11:28:59 +04:00
|
|
|
"Patch I/O Manager",
|
2014-01-07 03:06:01 +04:00
|
|
|
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)
|
|
|
|
{
|
2014-01-07 16:31:36 +04:00
|
|
|
retval = patch_io_backing_manager->open(name, flags & ~IO_FLAG_RW, &data->real);
|
2014-01-07 03:06:01 +04:00
|
|
|
if (retval)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (patch_file)
|
|
|
|
{
|
2014-01-07 14:31:13 +04:00
|
|
|
retval = ext2fs_patch_open(&data->patch, patch_file, O_CREAT);
|
2014-01-07 12:07:24 +04:00
|
|
|
if (retval)
|
2014-01-07 03:06:01 +04:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*channel = io;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (data)
|
|
|
|
{
|
2014-01-07 12:07:24 +04:00
|
|
|
ext2fs_patch_close(&data->patch);
|
2014-01-07 03:06:01 +04:00
|
|
|
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;
|
|
|
|
|
2014-01-07 12:07:24 +04:00
|
|
|
ext2fs_patch_close(&data->patch);
|
2014-01-07 03:06:01 +04:00
|
|
|
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);
|
|
|
|
|
2014-01-07 12:07:24 +04:00
|
|
|
if (data->patch.block_size && data->patch.block_size != (unsigned)blksize)
|
2014-01-07 03:06:01 +04:00
|
|
|
return EINVAL;
|
|
|
|
if (data->real)
|
|
|
|
retval = io_channel_set_blksize(data->real, blksize);
|
2014-01-07 12:07:24 +04:00
|
|
|
channel->block_size = (unsigned)blksize;
|
2014-01-07 03:06:01 +04:00
|
|
|
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)
|
|
|
|
{
|
2014-01-07 12:07:24 +04:00
|
|
|
if (data->patch.bmap && ext2fs_test_generic_bitmap(data->patch.bmap, block))
|
|
|
|
retval = retry_read_at(data->patch.patch_fd, block*channel->block_size, -count, buf);
|
2014-01-07 03:06:01 +04:00
|
|
|
else
|
|
|
|
retval = io_channel_read_blk64(data->real, block, count, buf);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
for (b = 0; b < count; )
|
|
|
|
{
|
2014-01-07 12:07:24 +04:00
|
|
|
for (n = 0; (b+n < count) && data->patch.bmap && ext2fs_test_generic_bitmap(data->patch.bmap, block+b+n); n++) {}
|
2014-01-07 03:06:01 +04:00
|
|
|
if (n > 0)
|
|
|
|
{
|
2014-01-07 12:07:24 +04:00
|
|
|
retval = retry_read_at(data->patch.patch_fd, (block+b)*channel->block_size, n*channel->block_size, buf+b*channel->block_size);
|
2014-01-07 03:06:01 +04:00
|
|
|
if (retval)
|
|
|
|
break;
|
|
|
|
b += n;
|
|
|
|
}
|
2014-01-07 12:07:24 +04:00
|
|
|
for (n = 0; (b+n < count) && (!data->patch.bmap || !ext2fs_test_generic_bitmap(data->patch.bmap, block+b+n)); n++) {}
|
2014-01-07 14:31:13 +04:00
|
|
|
if (n > 0)
|
2014-01-07 03:06:01 +04:00
|
|
|
{
|
2014-01-07 14:31:13 +04:00
|
|
|
retval = io_channel_read_blk64(data->real, block+b, n, buf+b*channel->block_size);
|
2014-01-07 03:06:01 +04:00
|
|
|
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);
|
|
|
|
|
2014-01-07 12:07:24 +04:00
|
|
|
retval = ext2fs_patch_init_bmap(&data->patch, channel);
|
2014-01-07 03:06:01 +04:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
2014-01-07 14:31:13 +04:00
|
|
|
return ext2fs_patch_write_blk64(&data->patch, block, count, buf);
|
2014-01-07 03:06:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2014-01-07 12:07:24 +04:00
|
|
|
if (data->patch.patch_fd)
|
|
|
|
fsync(data->patch.patch_fd);
|
2014-01-07 03:06:01 +04:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
2014-01-07 11:28:59 +04:00
|
|
|
if (data->real && data->real->manager->set_option)
|
2014-01-07 03:06:01 +04:00
|
|
|
retval = data->real->manager->set_option(data->real, option, arg);
|
2014-01-07 11:28:59 +04:00
|
|
|
if (!retval && !strcmp(option, "offset"))
|
|
|
|
{
|
2014-01-07 03:06:01 +04:00
|
|
|
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;
|
|
|
|
}
|