mirror of https://github.com/vitalif/e2fsprogs
Add patch_io manager
parent
2051d63a83
commit
460c0af190
|
@ -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 \
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue