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);