From 2e8ca9a26b0bd7dae546a3f9a98df67b043fe3be Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 30 Nov 2004 14:07:11 -0500 Subject: [PATCH] Add support for passing options to the io layer using the URL syntax. For example, /tmp/test.img?offset=1024. Multiple options can separated using the & character, although at the moment the only option implemented is the offset option in the unix_io layer. --- e2fsck/ChangeLog | 6 +++++ e2fsck/e2fsck.h | 1 + e2fsck/unix.c | 25 ++++++++++++------ lib/ext2fs/ChangeLog | 19 ++++++++++++++ lib/ext2fs/Makefile.in | 4 ++- lib/ext2fs/ext2_io.h | 12 +++++++-- lib/ext2fs/ext2fs.h | 4 +++ lib/ext2fs/freefs.c | 3 ++- lib/ext2fs/io_manager.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/ext2fs/openfs.c | 37 +++++++++++++++++++------- lib/ext2fs/test_io.c | 33 +++++++++++++++++++++-- lib/ext2fs/unix_io.c | 45 +++++++++++++++++++++++++------- misc/ChangeLog | 4 +++ misc/tune2fs.c | 10 ++++++- resize/ChangeLog | 4 +++ resize/main.c | 10 ++++--- 16 files changed, 250 insertions(+), 36 deletions(-) create mode 100644 lib/ext2fs/io_manager.c diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index 9d74ecd..cf99a87 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,5 +1,11 @@ 2004-11-30 Theodore Ts'o + * e2fsck.h: Add io_options to e2fsck_struct + + * unix.c: If there is a question mark in the device name, separate + out the options to the IO layer, and pass it on to + ext2fs_open2(). + * Makefile.in: Use Linux-kernel-style makefile output to make it easier to see errors/warnings. diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h index ad27745..99ee20a 100644 --- a/e2fsck/e2fsck.h +++ b/e2fsck/e2fsck.h @@ -189,6 +189,7 @@ struct e2fsck_struct { const char *program_name; char *filesystem_name; char *device_name; + char *io_options; int flags; /* E2fsck internal flags */ int options; blk_t use_superblock; /* sb requested by user */ diff --git a/e2fsck/unix.c b/e2fsck/unix.c index 43ef4c9..7e55f87 100644 --- a/e2fsck/unix.c +++ b/e2fsck/unix.c @@ -708,6 +708,9 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx) if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file && !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS)) ctx->options |= E2F_OPT_READONLY; + ctx->io_options = strchr(argv[optind], '?'); + if (ctx->io_options) + *ctx->io_options++ = 0; ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0); if (!ctx->filesystem_name) { com_err(ctx->program_name, 0, _("Unable to resolve '%s'"), @@ -869,23 +872,29 @@ restart: if ((ctx->options & E2F_OPT_READONLY) == 0) flags |= EXT2_FLAG_RW; + if (ctx->io_options) { + int len = strlen(ctx->filesystem_name) + + strlen(ctx->io_options) + 2; + } + if (ctx->superblock && ctx->blocksize) { - retval = ext2fs_open(ctx->filesystem_name, flags, - ctx->superblock, ctx->blocksize, - io_ptr, &fs); + retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, + flags, ctx->superblock, ctx->blocksize, + io_ptr, &fs); } else if (ctx->superblock) { int blocksize; for (blocksize = EXT2_MIN_BLOCK_SIZE; blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) { - retval = ext2fs_open(ctx->filesystem_name, flags, - ctx->superblock, blocksize, - io_ptr, &fs); + retval = ext2fs_open2(ctx->filesystem_name, + ctx->io_options, flags, + ctx->superblock, blocksize, + io_ptr, &fs); if (!retval) break; } } else - retval = ext2fs_open(ctx->filesystem_name, flags, - 0, 0, io_ptr, &fs); + retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, + flags, 0, 0, io_ptr, &fs); if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && !(ctx->flags & E2F_FLAG_SB_SPECIFIED) && ((retval == EXT2_ET_BAD_MAGIC) || diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog index 5a201d4..bcb699a 100644 --- a/lib/ext2fs/ChangeLog +++ b/lib/ext2fs/ChangeLog @@ -1,5 +1,24 @@ 2004-11-30 Theodore Ts'o + * unix_io.c (unix_set_option): Add support for the offset option. + + * test_io.c (test_set_option): Add support for the set_option method. + + * ext2_io.h: Add new io_channel method, set_option(), and change + io_channel_write_byte() from a macro to a library function. + + * ext2fs.h, openfs.c(ext2fs_open2): New version of ext2fs_open + which adds a new parameter, io_options. + (ext2fs_open): If there is a question mark in the + filename, and no io_options are specified, assumed that + the text following the question mark are io_options. + + * io_manager.c, Makefile.in: New source file which contains + high-level functions for the io_channel layer. + + * freefs.c (ext2fs_free): Make sure we don't free the io_channel + if image_io is NULL. + * Makefile.in: Use Linux-kernel-style makefile output to make it easier to see errors/warnings. diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in index 5ebcd6c..2c7edf6 100644 --- a/lib/ext2fs/Makefile.in +++ b/lib/ext2fs/Makefile.in @@ -46,6 +46,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ initialize.o \ inline.o \ inode.o \ + io_manager.o \ ismounted.o \ link.o \ llseek.o \ @@ -96,11 +97,12 @@ SRCS= ext2_err.c \ $(srcdir)/getsize.c \ $(srcdir)/getsectsize.c \ $(srcdir)/icount.c \ - $(srcdir)/imager.c \ $(srcdir)/initialize.c \ $(srcdir)/inline.c \ $(srcdir)/inode.c \ $(srcdir)/inode_io.c \ + $(srcdir)/imager.c \ + $(srcdir)/io_manager.c \ $(srcdir)/ismounted.c \ $(srcdir)/link.c \ $(srcdir)/llseek.c \ diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h index b9ba0b6..e17886c 100644 --- a/lib/ext2fs/ext2_io.h +++ b/lib/ext2fs/ext2_io.h @@ -68,7 +68,9 @@ struct struct_io_manager { errcode_t (*flush)(io_channel channel); errcode_t (*write_byte)(io_channel channel, unsigned long offset, int count, const void *data); - int reserved[15]; + errcode_t (*set_option)(io_channel channel, const char *option, + const char *arg); + int reserved[14]; }; #define IO_FLAG_RW 1 @@ -81,9 +83,15 @@ struct struct_io_manager { #define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) #define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) #define io_channel_flush(c) ((c)->manager->flush((c))) -#define io_channel_write_byte(c,b,n,d) ((c)->manager->write_byte((c),b,n,d)) #define io_channel_bumpcount(c) ((c)->refcount++) +/* io_manager.c */ +extern errcode_t io_channel_set_options(io_channel channel, + const char *options); +extern errcode_t io_channel_write_byte(io_channel channel, + unsigned long offset, + int count, const void *data); + /* unix_io.c */ extern io_manager unix_io_manager; diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 19be9c5..6a02127 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -851,6 +851,10 @@ extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, extern errcode_t ext2fs_open(const char *name, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs); +extern errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs); extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i); errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c index 9dda403..029ffaa 100644 --- a/lib/ext2fs/freefs.c +++ b/lib/ext2fs/freefs.c @@ -24,7 +24,8 @@ void ext2fs_free(ext2_filsys fs) if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) return; if (fs->image_io != fs->io) { - io_channel_close(fs->image_io); + if (fs->image_io) + io_channel_close(fs->image_io); } if (fs->io) { io_channel_close(fs->io); diff --git a/lib/ext2fs/io_manager.c b/lib/ext2fs/io_manager.c new file mode 100644 index 0000000..e50d7e4 --- /dev/null +++ b/lib/ext2fs/io_manager.c @@ -0,0 +1,69 @@ +/* + * io_manager.c --- the I/O manager abstraction + */ + +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_SYS_STAT_H +#include +#endif +#if HAVE_SYS_TYPES_H +#include +#endif + +#include "ext2_fs.h" +#include "ext2fs.h" + +errcode_t io_channel_set_options(io_channel channel, const char *opts) +{ + errcode_t retval = 0; + char *next, *ptr, *options, *arg; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (!opts) + return 0; + + if (!channel->manager->set_option) + return EXT2_ET_INVALID_ARGUMENT; + + options = malloc(strlen(opts)+1); + if (!options) + return EXT2_ET_NO_MEMORY; + strcpy(options, opts); + ptr = options; + + while (ptr && *ptr) { + next = strchr(ptr, '&'); + if (next) + *next++ = 0; + + arg = strchr(ptr, '='); + if (arg) + *arg++ = 0; + + retval = (channel->manager->set_option)(channel, ptr, arg); + if (retval) + break; + ptr = next; + } + free(options); + return retval; +} + +errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, + int count, const void *data) +{ + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + + if (channel->manager->write_byte) + return channel->manager->write_byte(channel, offset, + count, data); + + return EXT2_ET_UNIMPLEMENTED; +} diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c index dac8a38..05de84f 100644 --- a/lib/ext2fs/openfs.c +++ b/lib/ext2fs/openfs.c @@ -59,6 +59,14 @@ blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) return ret_blk; } +errcode_t ext2fs_open(const char *name, int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs) +{ + return ext2fs_open2(name, 0, flags, superblock, block_size, + manager, ret_fs); +} + /* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. @@ -70,16 +78,17 @@ blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device */ -errcode_t ext2fs_open(const char *name, int flags, int superblock, - unsigned int block_size, io_manager manager, - ext2_filsys *ret_fs) +errcode_t ext2fs_open2(const char *name, const char *io_options, + int flags, int superblock, + unsigned int block_size, io_manager manager, + ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; unsigned long i; int j, groups_per_block, blocks_per_group; blk_t group_block, blk; - char *dest; + char *dest, *cp; struct ext2_group_desc *gdp; EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); @@ -92,16 +101,26 @@ errcode_t ext2fs_open(const char *name, int flags, int superblock, fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; fs->umask = 022; - retval = manager->open(name, (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, + retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); + if (retval) + goto cleanup; + strcpy(fs->device_name, name); + cp = strchr(fs->device_name, '?'); + if (!io_options && cp) { + *cp++ = 0; + io_options = cp; + } + + retval = manager->open(fs->device_name, + (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0, &fs->io); if (retval) goto cleanup; + if (io_options && + (retval = io_channel_set_options(fs->io, io_options))) + goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; - retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); - if (retval) - goto cleanup; - strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); if (retval) goto cleanup; diff --git a/lib/ext2fs/test_io.c b/lib/ext2fs/test_io.c index 02b6e18..6a3b248 100644 --- a/lib/ext2fs/test_io.c +++ b/lib/ext2fs/test_io.c @@ -56,6 +56,8 @@ static errcode_t test_write_blk(io_channel channel, unsigned long block, static errcode_t test_flush(io_channel channel); static errcode_t test_write_byte(io_channel channel, unsigned long offset, int count, const void *buf); +static errcode_t test_set_option(io_channel channel, const char *option, + const char *arg); static struct struct_io_manager struct_test_manager = { EXT2_ET_MAGIC_IO_MANAGER, @@ -66,8 +68,8 @@ static struct struct_io_manager struct_test_manager = { test_read_blk, test_write_blk, test_flush, - test_write_byte - + test_write_byte, + test_set_option }; io_manager test_io_manager = &struct_test_manager; @@ -94,6 +96,7 @@ void (*test_io_cb_write_byte) #define TEST_FLAG_SET_BLKSIZE 0x04 #define TEST_FLAG_FLUSH 0x08 #define TEST_FLAG_DUMP 0x10 +#define TEST_FLAG_SET_OPTION 0x20 static void test_dump_block(io_channel channel, struct test_private_data *data, @@ -351,3 +354,29 @@ static errcode_t test_flush(io_channel channel) return retval; } +static errcode_t test_set_option(io_channel channel, const char *option, + const char *arg) +{ + struct test_private_data *data; + errcode_t retval = 0; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct test_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); + + + if (data->flags & TEST_FLAG_SET_OPTION) + fprintf(data->outfile, "Test_io: set_option(%s, %s) ", + option, arg); + if (data->real && data->real->manager->set_option) { + retval = (data->real->manager->set_option)(data->real, + option, arg); + if (data->flags & TEST_FLAG_SET_OPTION) + fprintf(data->outfile, "returned %s\n", + retval ? error_message(retval) : "OK"); + } else { + if (data->flags & TEST_FLAG_SET_OPTION) + fprintf(data->outfile, "not implemented\n"); + } + return retval; +} diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c index 7df3243..e1df0d6 100644 --- a/lib/ext2fs/unix_io.c +++ b/lib/ext2fs/unix_io.c @@ -68,6 +68,7 @@ struct unix_private_data { int dev; int flags; int access_time; + ext2_loff_t offset; struct unix_cache cache[CACHE_SIZE]; }; @@ -81,6 +82,8 @@ static errcode_t unix_write_blk(io_channel channel, unsigned long block, static errcode_t unix_flush(io_channel channel); static errcode_t unix_write_byte(io_channel channel, unsigned long offset, int size, const void *data); +static errcode_t unix_set_option(io_channel channel, const char *option, + const char *arg); static void reuse_cache(io_channel channel, struct unix_private_data *data, struct unix_cache *cache, unsigned long block); @@ -103,10 +106,11 @@ static struct struct_io_manager struct_unix_manager = { unix_write_blk, unix_flush, #ifdef NEED_BOUNCE_BUFFER - 0 + 0, #else - unix_write_byte + unix_write_byte, #endif + unix_set_option }; io_manager unix_io_manager = &struct_unix_manager; @@ -126,7 +130,7 @@ static errcode_t raw_read_blk(io_channel channel, int actual = 0; size = (count < 0) ? -count : count * channel->block_size; - location = (ext2_loff_t) block * channel->block_size; + location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; @@ -164,7 +168,7 @@ static errcode_t raw_read_blk(io_channel channel, char sector[BLOCKALIGN]; size = (count < 0) ? -count : count * channel->block_size; - location = (ext2_loff_t) block * channel->block_size; + location = ((ext2_loff_t) block * channel->block_size) + data->offset; #ifdef DEBUG printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n", count, size, block, channel->block_size, location); @@ -221,7 +225,7 @@ static errcode_t raw_write_blk(io_channel channel, size = count * channel->block_size; } - location = (ext2_loff_t) block * channel->block_size; + location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; @@ -406,12 +410,12 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel) if ((retval = alloc_cache(io, data))) goto cleanup; - + open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; #ifdef HAVE_OPEN64 - data->dev = open64(name, open_flags); + data->dev = open64(io->name, open_flags); #else - data->dev = open(name, open_flags); + data->dev = open(io->name, open_flags); #endif if (data->dev < 0) { retval = errno; @@ -652,7 +656,7 @@ static errcode_t unix_write_byte(io_channel channel, unsigned long offset, return retval; #endif - if (lseek(data->dev, offset, SEEK_SET) < 0) + if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) return errno; actual = write(data->dev, buf, size); @@ -681,3 +685,26 @@ static errcode_t unix_flush(io_channel channel) return retval; } +static errcode_t unix_set_option(io_channel channel, const char *option, + const char *arg) +{ + struct unix_private_data *data; + unsigned long tmp; + char *end; + + EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); + data = (struct unix_private_data *) channel->private_data; + EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); + + if (!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 0; + } + return EXT2_ET_INVALID_ARGUMENT; +} diff --git a/misc/ChangeLog b/misc/ChangeLog index 06c8c51..796968d 100644 --- a/misc/ChangeLog +++ b/misc/ChangeLog @@ -1,5 +1,9 @@ 2004-11-30 Theodore Ts'o + * tune2fs.c: If there is a question mark in the device name, + separate out the options to the IO layer, and pass it on + to ext2fs_open2(). + * Makefile.in: Use Linux-kernel-style makefile output to make it easier to see errors/warnings. diff --git a/misc/tune2fs.c b/misc/tune2fs.c index e7c3baf..2a8ac02 100644 --- a/misc/tune2fs.c +++ b/misc/tune2fs.c @@ -58,6 +58,7 @@ extern int optind; const char * program_name = "tune2fs"; char * device_name; char * new_label, *new_last_mounted, *new_UUID; +char * io_options; static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; static time_t last_check_time; @@ -449,6 +450,9 @@ static void parse_e2label_options(int argc, char ** argv) fputs(_("Usage: e2label device [newlabel]\n"), stderr); exit(1); } + io_options = strchr(argv[1], '?'); + if (io_options) + *io_options++ = 0; device_name = blkid_get_devname(NULL, argv[1], NULL); if (!device_name) { com_err("e2label", 0, _("Unable to resolve '%s'"), @@ -704,6 +708,9 @@ static void parse_tune2fs_options(int argc, char **argv) usage(); if (!open_flag && !l_flag) usage(); + io_options = strchr(argv[optind], '?'); + if (io_options) + *io_options++ = 0; device_name = blkid_get_devname(NULL, argv[optind], NULL); if (!device_name) { com_err("tune2fs", 0, _("Unable to resolve '%s'"), @@ -762,7 +769,8 @@ int main (int argc, char ** argv) #else io_ptr = unix_io_manager; #endif - retval = ext2fs_open (device_name, open_flag, 0, 0, io_ptr, &fs); + retval = ext2fs_open2(device_name, io_options, open_flag, + 0, 0, io_ptr, &fs); if (retval) { com_err (program_name, retval, _("while trying to open %s"), device_name); diff --git a/resize/ChangeLog b/resize/ChangeLog index 50c7469..d676707 100644 --- a/resize/ChangeLog +++ b/resize/ChangeLog @@ -1,5 +1,9 @@ 2004-11-30 Theodore Ts'o + * main.c: If there is a question mark in the device name, + separate out the options to the IO layer, and pass it on + to ext2fs_open2(). + * Makefile.in: Use Linux-kernel-style makefile output to make it easier to see errors/warnings. diff --git a/resize/main.c b/resize/main.c index b32a7ea..3d137ef 100644 --- a/resize/main.c +++ b/resize/main.c @@ -25,7 +25,7 @@ extern int optind; #include "../version.h" -char *program_name, *device_name; +char *program_name, *device_name, *io_options; static void usage (char *prog) { @@ -195,6 +195,10 @@ int main (int argc, char ** argv) if (optind < argc) usage(program_name); + io_options = strchr(device_name, '?'); + if (io_options) + *io_options++ = 0; + check_mount(device_name); if (flush) { @@ -222,8 +226,8 @@ int main (int argc, char ** argv) } else io_ptr = unix_io_manager; - retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, - io_ptr, &fs); + retval = ext2fs_open2(device_name, io_options, EXT2_FLAG_RW, + 0, 0, io_ptr, &fs); if (retval) { com_err (program_name, retval, _("while trying to open %s"), device_name); -- 1.8.3.1