+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
#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;
* 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
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
#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 */
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.
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)
* 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) {
}
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)) {
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);
(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)
{
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;
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_blk(%lu, %d) returned %s\n",
+ 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",
+ offset, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
/*
* Flush data buffers to disk.
*/
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,
unix_set_blksize,
unix_read_blk,
unix_write_blk,
- unix_flush
+ unix_flush,
+ unix_write_byte
};
io_manager unix_io_manager = &struct_unix_manager;
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.
*/