diff --git a/lib/ext2fs/Android.mk b/lib/ext2fs/Android.mk index 356232fc..b320b5c7 100644 --- a/lib/ext2fs/Android.mk +++ b/lib/ext2fs/Android.mk @@ -75,6 +75,8 @@ libext2fs_src_files := \ swapfs.c \ symlink.c \ undo_io.c \ + patch.c \ + patch_io.c \ unix_io.c \ unlink.c \ valid_blk.c \ diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 6a3656d0..d0456a2e 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -124,6 +124,8 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ symlink.o \ $(TDB_OBJ) \ undo_io.o \ + patch.o \ + patch_io.o \ unix_io.o \ unlink.o \ valid_blk.o \ @@ -211,6 +213,8 @@ SRCS= ext2_err.c \ $(srcdir)/tst_getsize.c \ $(srcdir)/tst_iscan.c \ $(srcdir)/undo_io.c \ + $(srcdir)/patch.c \ + $(srcdir)/patch_io.c \ $(srcdir)/unix_io.c \ $(srcdir)/unlink.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_io.h $(top_builddir)/lib/ext2fs/ext2_err.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 \ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h index 3a07b5d4..4999b375 100644 --- a/lib/ext2fs/ext2_io.h +++ b/lib/ext2fs/ext2_io.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_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 */ extern io_manager test_io_manager, test_io_backing_manager; extern void (*test_io_cb_read_blk) diff --git a/lib/ext2fs/patch.c b/lib/ext2fs/patch.c new file mode 100644 index 00000000..74f7c969 --- /dev/null +++ b/lib/ext2fs/patch.c @@ -0,0 +1,228 @@ +/** + * patch.c --- Common "patch" file functions + * + * Copyright (c) Vitaliy Filippov 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 +#include +#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); +} diff --git a/lib/ext2fs/patch.h b/lib/ext2fs/patch.h new file mode 100644 index 00000000..e3779348 --- /dev/null +++ b/lib/ext2fs/patch.h @@ -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 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 diff --git a/lib/ext2fs/patch_io.c b/lib/ext2fs/patch_io.c new file mode 100644 index 00000000..724fabaa --- /dev/null +++ b/lib/ext2fs/patch_io.c @@ -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 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 +#include +#include +#include + +#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; +}