diff options
Diffstat (limited to 'ext2fs/hyper.c')
-rw-r--r-- | ext2fs/hyper.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c new file mode 100644 index 00000000..4012f9f7 --- /dev/null +++ b/ext2fs/hyper.c @@ -0,0 +1,210 @@ +/* Fetching and storing the hypermetadata (superblock and bg summary info) + + Copyright (C) 1994,95,96,99, 1999 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <string.h> +#include <stdio.h> +#include <error.h> +#include <hurd/store.h> +#include "ext2fs.h" + +vm_address_t zeroblock = 0; +char *modified_global_blocks = 0; + +static void +allocate_mod_map (void) +{ + static vm_size_t mod_map_size = 0; + + if (modified_global_blocks && mod_map_size) + /* Get rid of the old one. */ + munmap (modified_global_blocks, mod_map_size); + + if (!diskfs_readonly && block_size < vm_page_size) + /* If the block size is too small, we have to take extra care when + writing out pages from the global pager, to make sure we don't stomp + on any file pager blocks. In this case use a bitmap to record which + global blocks are actually modified so the pager can write only them. */ + { + /* One bit per filesystem block. */ + mod_map_size = sblock->s_blocks_count >> 3; + modified_global_blocks = mmap (0, mod_map_size, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + assert (modified_global_blocks != (void *) -1); + } + else + modified_global_blocks = 0; +} + +unsigned int sblock_block = SBLOCK_BLOCK; /* in 1k blocks */ + +static int ext2fs_clean; /* fs clean before we started writing? */ + +void +get_hypermetadata (void) +{ + error_t err = diskfs_catch_exception (); + if (err) + ext2_panic ("can't read superblock: %s", strerror (err)); + + if (zeroblock) + munmap ((caddr_t) zeroblock, block_size); + + sblock = (struct ext2_super_block *) boffs_ptr (SBLOCK_OFFS); + + if (sblock->s_magic != EXT2_SUPER_MAGIC +#ifdef EXT2FS_PRE_02B_COMPAT + && sblock->s_magic != EXT2_PRE_02B_MAGIC +#endif + ) + ext2_panic ("bad magic number %#x (should be %#x)", + sblock->s_magic, EXT2_SUPER_MAGIC); + + log2_block_size = EXT2_MIN_BLOCK_LOG_SIZE + sblock->s_log_block_size; + block_size = 1 << log2_block_size; + + if (block_size > EXT2_MAX_BLOCK_SIZE) + ext2_panic ("block size %d is too big (max is %d bytes)", + block_size, EXT2_MAX_BLOCK_SIZE); + + log2_dev_blocks_per_fs_block = log2_block_size - store->log2_block_size; + if (log2_dev_blocks_per_fs_block < 0) + ext2_panic ("block size %d isn't a power-of-two multiple of the device" + " block size (%d)!", + block_size, store->block_size); + + log2_stat_blocks_per_fs_block = 0; + while ((512 << log2_stat_blocks_per_fs_block) < block_size) + log2_stat_blocks_per_fs_block++; + if ((512 << log2_stat_blocks_per_fs_block) != block_size) + ext2_panic ("block size %d isn't a power-of-two multiple of 512!", + block_size); + + if ((store->size >> log2_block_size) < sblock->s_blocks_count) + ext2_panic ("disk size (%qd bytes) too small; superblock says we need %qd", + (long long int) store->size, + (long long int) sblock->s_blocks_count << log2_block_size); + if (log2_dev_blocks_per_fs_block != 0 + && (store->size & ((1 << log2_dev_blocks_per_fs_block) - 1)) != 0) + ext2_warning ("%ld (%d byte) device blocks " + " unused after last filesystem (%d byte) block", + (store->size & ((1 << log2_dev_blocks_per_fs_block) - 1)), + store->block_size, block_size); + + /* Set these handy variables. */ + inodes_per_block = block_size / EXT2_INODE_SIZE (sblock); + + frag_size = EXT2_MIN_FRAG_SIZE << sblock->s_log_frag_size; + if (frag_size) + frags_per_block = block_size / frag_size; + else + ext2_panic ("frag size is zero!"); + + if (sblock->s_rev_level > EXT2_GOOD_OLD_REV) + { + if (sblock->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP) + ext2_panic ("could not mount because of unsupported optional features" + " (0x%x)", + sblock->s_feature_incompat & ~EXT2_FEATURE_INCOMPAT_SUPP); + if (sblock->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP) + { + ext2_warning ("mounted readonly because of" + " unsupported optional features (0x%x)", + sblock->s_feature_ro_compat & ~EXT2_FEATURE_RO_COMPAT_SUPP); + diskfs_readonly = 1; + } + if (sblock->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) + ext2_panic ("inode size %d isn't supported", sblock->s_inode_size); + } + + groups_count = + ((sblock->s_blocks_count - sblock->s_first_data_block + + sblock->s_blocks_per_group - 1) + / sblock->s_blocks_per_group); + + itb_per_group = sblock->s_inodes_per_group / inodes_per_block; + desc_per_block = block_size / sizeof (struct ext2_group_desc); + addr_per_block = block_size / sizeof (block_t); + db_per_group = (groups_count + desc_per_block - 1) / desc_per_block; + + ext2fs_clean = sblock->s_state & EXT2_VALID_FS; + if (! ext2fs_clean) + { + ext2_warning ("FILESYSTEM NOT UNMOUNTED CLEANLY; PLEASE fsck"); + if (! diskfs_readonly) + { + diskfs_readonly = 1; + ext2_warning ("MOUNTED READ-ONLY; MUST USE `fsysopts --writable'"); + } + } + + allocate_mod_map (); + + diskfs_end_catch_exception (); + + /* Cache a convenient pointer to the block group descriptors for allocation. + These are stored in the filesystem blocks following the superblock. */ + group_desc_image = (struct ext2_group_desc *) bptr (bptr_block (sblock) + 1); + + /* A handy source of page-aligned zeros. */ + zeroblock = (vm_address_t) mmap (0, block_size, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); +} + +error_t +diskfs_set_hypermetadata (int wait, int clean) +{ + if (clean && ext2fs_clean && !(sblock->s_state & EXT2_VALID_FS)) + /* The filesystem is clean, so we need to set the clean flag. */ + { + sblock->s_state |= EXT2_VALID_FS; + sblock_dirty = 1; + } + else if (!clean && (sblock->s_state & EXT2_VALID_FS)) + /* The filesystem just became dirty, so clear the clean flag. */ + { + sblock->s_state &= ~EXT2_VALID_FS; + sblock_dirty = 1; + wait = 1; + } + + if (sblock_dirty) + { + sblock_dirty = 0; + record_global_poke (sblock); + } + + sync_global (wait); + + /* Should check writability here and return EROFS if necessary. XXX */ + return 0; +} + +void +diskfs_readonly_changed (int readonly) +{ + allocate_mod_map (); + + (*(readonly ? store_set_flags : store_clear_flags)) (store, STORE_READONLY); + + mprotect (disk_image, store->size, PROT_READ | (readonly ? 0 : PROT_WRITE)); + + if (!readonly && !(sblock->s_state & EXT2_VALID_FS)) + ext2_warning ("UNCLEANED FILESYSTEM NOW WRITABLE"); +} |