Many files:
ext2_io.h (io_channel_write_byte): Add new interface to allow callers
to write specific byte ranges. This is an optional interface, which
not all IO channels may implement.
unix_io.c (unix_write_byte): test_io.c (test_write_byte): Add
implementation of the write_byte function.
closefs.c (write_primary_superblock, ext2fs_flush): Add a new function
which writes the primary superblock. If the IO channel supports
writing raw bytes directly, only fields which were modified are
written to the disk. This makes it safe(r) to use utilities like
tune2fs on a mounted filesystem.
freefs.c (ext2fs_free): Free the original superblock if it is available.
openfs.c (ext2fs_open): Store a copy of the original superblock when
opening it.
ext2fs.h: Add a field to store the original superblock in the ext2
context structure.
diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog
index b72b52a..5a646b8 100644
--- a/lib/ext2fs/ChangeLog
+++ b/lib/ext2fs/ChangeLog
@@ -1,3 +1,30 @@
+2000-10-26 <tytso@snap.thunk.org>
+
+ * ext2_io.h (io_channel_write_byte): Add new interface to allow
+ callers to write specific byte ranges. This is an
+ optional interface, which not all IO channels may
+ implement.
+
+ * unix_io.c (unix_write_byte):
+ * test_io.c (test_write_byte): Add implementation of the
+ write_byte function.
+
+ * closefs.c (write_primary_superblock, ext2fs_flush): Add a new
+ function which writes the primary superblock. If the IO
+ channel supports writing raw bytes directly, only fields
+ which were modified are written to the disk. This makes
+ it safe(r) to use utilities like tune2fs on a mounted
+ filesystem.
+
+ * freefs.c (ext2fs_free): Free the original superblock if it is
+ available.
+
+ * openfs.c (ext2fs_open): Store a copy of the original superblock
+ when opening it.
+
+ * ext2fs.h: Add a field to store the original superblock in the
+ ext2 context structure.
+
2000-10-24 <tytso@snap.thunk.org>
* llseek.c: Add #ifdef's for IA64 (it's a 64-bit platform, so we
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 1a615f3..8ca6b47 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -56,6 +56,56 @@
#endif
}
+/*
+ * This function forces out the primary superblock. We need to only
+ * write out those fields which we have changed, since if the
+ * filesystem is mounted, it may have changed some of the other
+ * fields.
+ *
+ * It takes as input a superblock which has already been byte swapped
+ * (if necessary).
+ *
+ */
+static errcode_t write_primary_superblock(ext2_filsys fs,
+ struct ext2_super_block *super)
+{
+ __u16 *old_super, *new_super;
+ int check_idx, write_idx, size;
+ errcode_t retval;
+
+ if (!fs->io->manager->write_byte || !fs->orig_super) {
+ io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+ retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
+ super);
+ io_channel_set_blksize(fs->io, fs->blocksize);
+ return retval;
+ }
+
+ old_super = (__u16 *) fs->orig_super;
+ new_super = (__u16 *) super;
+
+ for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) {
+ if (old_super[check_idx] == new_super[check_idx])
+ continue;
+ write_idx = check_idx;
+ for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++)
+ if (old_super[check_idx] == new_super[check_idx])
+ break;
+ size = 2 * (check_idx - write_idx);
+#if 0
+ printf("Writing %d bytes starting at %d\n",
+ size, write_idx*2);
+#endif
+ retval = io_channel_write_byte(fs->io,
+ SUPERBLOCK_OFFSET + (2 * write_idx), size,
+ new_super + write_idx);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+
+
errcode_t ext2fs_flush(ext2_filsys fs)
{
dgrp_t i,j,maxgroup,sgrp;
@@ -111,12 +161,9 @@
* separately, since it is located at a fixed location
* (SUPERBLOCK_OFFSET).
*/
- io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
- retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
- super_shadow);
+ retval = write_primary_superblock(fs, super_shadow);
if (retval)
goto errout;
- io_channel_set_blksize(fs->io, fs->blocksize);
/*
* Set the state of the FS to be non-valid. (The state has
diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
index 49c09f3..b9ba0b6 100644
--- a/lib/ext2fs/ext2_io.h
+++ b/lib/ext2fs/ext2_io.h
@@ -66,7 +66,9 @@
errcode_t (*write_blk)(io_channel channel, unsigned long block,
int count, const void *data);
errcode_t (*flush)(io_channel channel);
- int reserved[16];
+ errcode_t (*write_byte)(io_channel channel, unsigned long offset,
+ int count, const void *data);
+ int reserved[15];
};
#define IO_FLAG_RW 1
@@ -79,6 +81,7 @@
#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++)
/* unix_io.c */
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index da46b69..791bd3d 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -203,10 +203,11 @@
badblocks_list badblocks;
ext2_dblist dblist;
__u32 stride; /* for mke2fs */
+ struct ext2_super_block * orig_super;
/*
* Reserved for future expansion
*/
- __u32 reserved[11];
+ __u32 reserved[10];
/*
* Reserved for the use of the calling application.
diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c
index d595972..485f921 100644
--- a/lib/ext2fs/freefs.c
+++ b/lib/ext2fs/freefs.c
@@ -35,6 +35,8 @@
ext2fs_free_mem((void **) &fs->device_name);
if (fs->super)
ext2fs_free_mem((void **) &fs->super);
+ if (fs->orig_super)
+ ext2fs_free_mem((void **) &fs->orig_super);
if (fs->group_desc)
ext2fs_free_mem((void **) &fs->group_desc);
if (fs->block_map)
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index b0b433f..cc98311 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -80,6 +80,9 @@
* superblock, then he/she must also specify the block size!
* Otherwise, read the master superblock located at offset
* SUPERBLOCK_OFFSET from the start of the partition.
+ *
+ * Note: we only save a backup copy of the superblock if we
+ * are reading the superblock from the primary superblock location.
*/
if (superblock) {
if (!block_size) {
@@ -88,15 +91,22 @@
}
io_channel_set_blksize(fs->io, block_size);
group_block = superblock + 1;
+ fs->orig_super = 0;
} else {
io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
superblock = 1;
group_block = 0;
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE,
+ (void **) &fs->orig_super);
+ if (retval)
+ goto cleanup;
}
retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE,
fs->super);
if (retval)
goto cleanup;
+ if (fs->orig_super)
+ memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ||
(fs->flags & EXT2_FLAG_SWAP_BYTES)) {
diff --git a/lib/ext2fs/test_io.c b/lib/ext2fs/test_io.c
index 10cf3a5..56ffb47 100644
--- a/lib/ext2fs/test_io.c
+++ b/lib/ext2fs/test_io.c
@@ -44,6 +44,7 @@
void (*read_blk)(unsigned long block, int count, errcode_t err);
void (*write_blk)(unsigned long block, int count, errcode_t err);
void (*set_blksize)(int blksize, errcode_t err);
+ void (*write_byte)(unsigned long block, int count, errcode_t err);
};
static errcode_t test_open(const char *name, int flags, io_channel *channel);
@@ -79,6 +80,8 @@
(unsigned long block, int count, errcode_t err) = 0;
void (*test_io_cb_set_blksize)
(int blksize, errcode_t err) = 0;
+void (*test_io_cb_write_byte)
+ (unsigned long block, int count, errcode_t err) = 0;
static errcode_t test_open(const char *name, int flags, io_channel *channel)
{
@@ -124,6 +127,7 @@
data->read_blk = test_io_cb_read_blk;
data->write_blk = test_io_cb_write_blk;
data->set_blksize = test_io_cb_set_blksize;
+ data->write_byte = test_io_cb_write_byte;
*channel = io;
return 0;
@@ -211,11 +215,31 @@
if (data->real)
retval = io_channel_write_blk(data->real, block, count, buf);
- if (data->write_blk)
- data->write_blk(block, count, retval);
+ if (data->write_byte)
+ data->write_byte(block, count, retval);
+ else
+ printf("Test_io: write_byte(%lu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+ int count, const void *buf)
+{
+ 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->real && data->real->manager->write_byte)
+ retval = io_channel_write_byte(data->real, offset, count, buf);
+ if (data->write_byte)
+ data->write_byte(offset, count, retval);
else
printf("Test_io: write_blk(%lu, %d) returned %s\n",
- block, count, retval ? error_message(retval) : "OK");
+ offset, count, retval ? error_message(retval) : "OK");
return retval;
}
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 19c6926..142d149 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -73,6 +73,8 @@
static errcode_t unix_write_blk(io_channel channel, unsigned long block,
int count, const void *data);
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 struct struct_io_manager struct_unix_manager = {
EXT2_ET_MAGIC_IO_MANAGER,
@@ -82,7 +84,8 @@
unix_set_blksize,
unix_read_blk,
unix_write_blk,
- unix_flush
+ unix_flush,
+ unix_write_byte
};
io_manager unix_io_manager = &struct_unix_manager;
@@ -509,6 +512,36 @@
return retval;
}
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *buf)
+{
+ struct unix_private_data *data;
+ struct unix_cache *cache;
+ errcode_t retval = 0, retval2;
+ char *cp;
+ int i, writethrough;
+ size_t actual;
+
+ 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);
+
+ /*
+ * Flush out the cache completely
+ */
+ if ((retval = flush_cached_blocks(channel, data, 1)))
+ return retval;
+
+ if (lseek(data->dev, offset, SEEK_SET) < 0)
+ return errno;
+
+ actual = write(data->dev, buf, size);
+ if (actual != size)
+ return EXT2_ET_SHORT_WRITE;
+
+ return 0;
+}
+
/*
* Flush data buffers to disk.
*/