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.

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  <tytso@mit.edu>
 
+	* 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 @@
 	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 @@
 	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 @@
 	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  <tytso@mit.edu>
 
+	* 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 @@
 	initialize.o \
 	inline.o \
 	inode.o \
+	io_manager.o \
 	ismounted.o \
 	link.o \
 	llseek.o \
@@ -96,11 +97,12 @@
 	$(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 @@
 	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 @@
 #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_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 @@
 	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 <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 @@
 	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 @@
  *				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 @@
 	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,
-			       &fs->io);
-	if (retval)
-		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);
+	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(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_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 @@
 	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 @@
 #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 @@
 	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 @@
 	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_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 @@
 	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 @@
 	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 @@
 	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 @@
 			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 @@
 
 	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 @@
 		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 @@
 	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  <tytso@mit.edu>
 
+	* 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 @@
 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 @@
 		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 @@
 		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 @@
 #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  <tytso@mit.edu>
 
+	* 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 @@
 
 #include "../version.h"
 
-char *program_name, *device_name;
+char *program_name, *device_name, *io_options;
 
 static void usage (char *prog)
 {
@@ -195,6 +195,10 @@
 	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 @@
 	} 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);