From c180ac86533bcbfb1560bd4aa01464785a760f70 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 26 Oct 2000 20:24:43 +0000 Subject: [PATCH] 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. --- lib/ext2fs/ChangeLog | 27 ++++++++++++++++++++++++++ lib/ext2fs/closefs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++---- lib/ext2fs/ext2_io.h | 5 ++++- lib/ext2fs/ext2fs.h | 3 ++- lib/ext2fs/freefs.c | 2 ++ lib/ext2fs/openfs.c | 10 ++++++++++ lib/ext2fs/test_io.c | 30 +++++++++++++++++++++++++--- lib/ext2fs/unix_io.c | 35 ++++++++++++++++++++++++++++++++- 8 files changed, 157 insertions(+), 10 deletions(-) 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 + + * 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 * 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 @@ int ext2fs_bg_has_super(ext2_filsys fs, int group_block) #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 @@ errcode_t ext2fs_flush(ext2_filsys fs) * 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 @@ struct struct_io_manager { 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 @@ 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++) /* 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 @@ struct struct_ext2_filsys { 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 @@ void ext2fs_free(ext2_filsys fs) 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 @@ errcode_t ext2fs_open(const char *name, int flags, int superblock, * 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 @@ errcode_t ext2fs_open(const char *name, int flags, int superblock, } 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 @@ struct test_private_data { 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 @@ void (*test_io_cb_write_blk) (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 @@ 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; @@ -211,14 +215,34 @@ static errcode_t test_write_blk(io_channel channel, unsigned long block, 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. */ 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_read_blk(io_channel channel, unsigned long block, 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 @@ static struct struct_io_manager struct_unix_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; @@ -509,6 +512,36 @@ static errcode_t unix_write_blk(io_channel channel, unsigned long block, 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. */ -- 1.8.3.1