Home | History | Annotate | Download | only in ext2fs
      1 /*
      2  * closefs.c --- close an ext2 filesystem
      3  *
      4  * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
      5  *
      6  * %Begin-Header%
      7  * This file may be redistributed under the terms of the GNU Public
      8  * License.
      9  * %End-Header%
     10  */
     11 
     12 #include <stdio.h>
     13 #if HAVE_UNISTD_H
     14 #include <unistd.h>
     15 #endif
     16 #include <time.h>
     17 #include <string.h>
     18 
     19 #include "ext2_fs.h"
     20 #include "ext2fsP.h"
     21 
     22 static int test_root(int a, int b)
     23 {
     24 	if (a == 0)
     25 		return 1;
     26 	while (1) {
     27 		if (a == 1)
     28 			return 1;
     29 		if (a % b)
     30 			return 0;
     31 		a = a / b;
     32 	}
     33 }
     34 
     35 int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
     36 {
     37 	if (!(fs->super->s_feature_ro_compat &
     38 	      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
     39 		return 1;
     40 
     41 	if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
     42 	    test_root(group_block, 7))
     43 		return 1;
     44 
     45 	return 0;
     46 }
     47 
     48 int ext2fs_super_and_bgd_loc(ext2_filsys fs,
     49 			     dgrp_t group,
     50 			     blk_t *ret_super_blk,
     51 			     blk_t *ret_old_desc_blk,
     52 			     blk_t *ret_new_desc_blk,
     53 			     int *ret_meta_bg)
     54 {
     55 	blk_t	group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0;
     56 	unsigned int meta_bg, meta_bg_size;
     57 	int	numblocks, has_super;
     58 	int	old_desc_blocks;
     59 
     60 	group_block = fs->super->s_first_data_block +
     61 		(group * fs->super->s_blocks_per_group);
     62 
     63 	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
     64 		old_desc_blocks = fs->super->s_first_meta_bg;
     65 	else
     66 		old_desc_blocks =
     67 			fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
     68 
     69 	if (group == fs->group_desc_count-1) {
     70 		numblocks = (fs->super->s_blocks_count -
     71 			     fs->super->s_first_data_block) %
     72 			fs->super->s_blocks_per_group;
     73 		if (!numblocks)
     74 			numblocks = fs->super->s_blocks_per_group;
     75 	} else
     76 		numblocks = fs->super->s_blocks_per_group;
     77 
     78 	has_super = ext2fs_bg_has_super(fs, group);
     79 
     80 	if (has_super) {
     81 		super_blk = group_block;
     82 		numblocks--;
     83 	}
     84 	meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
     85 	meta_bg = group / meta_bg_size;
     86 
     87 	if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
     88 	    (meta_bg < fs->super->s_first_meta_bg)) {
     89 		if (has_super) {
     90 			old_desc_blk = group_block + 1;
     91 			numblocks -= old_desc_blocks;
     92 		}
     93 	} else {
     94 		if (((group % meta_bg_size) == 0) ||
     95 		    ((group % meta_bg_size) == 1) ||
     96 		    ((group % meta_bg_size) == (meta_bg_size-1))) {
     97 			if (has_super)
     98 				has_super = 1;
     99 			new_desc_blk = group_block + has_super;
    100 			numblocks--;
    101 		}
    102 	}
    103 
    104 	numblocks -= 2 + fs->inode_blocks_per_group;
    105 
    106 	if (ret_super_blk)
    107 		*ret_super_blk = super_blk;
    108 	if (ret_old_desc_blk)
    109 		*ret_old_desc_blk = old_desc_blk;
    110 	if (ret_new_desc_blk)
    111 		*ret_new_desc_blk = new_desc_blk;
    112 	if (ret_meta_bg)
    113 		*ret_meta_bg = meta_bg;
    114 	return (numblocks);
    115 }
    116 
    117 
    118 /*
    119  * This function forces out the primary superblock.  We need to only
    120  * write out those fields which we have changed, since if the
    121  * filesystem is mounted, it may have changed some of the other
    122  * fields.
    123  *
    124  * It takes as input a superblock which has already been byte swapped
    125  * (if necessary).
    126  *
    127  */
    128 static errcode_t write_primary_superblock(ext2_filsys fs,
    129 					  struct ext2_super_block *super)
    130 {
    131 	__u16		*old_super, *new_super;
    132 	int		check_idx, write_idx, size;
    133 	errcode_t	retval;
    134 
    135 	if (!fs->io->manager->write_byte || !fs->orig_super) {
    136 		io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
    137 		retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
    138 					      super);
    139 		io_channel_set_blksize(fs->io, fs->blocksize);
    140 		return retval;
    141 	}
    142 
    143 	old_super = (__u16 *) fs->orig_super;
    144 	new_super = (__u16 *) super;
    145 
    146 	for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) {
    147 		if (old_super[check_idx] == new_super[check_idx])
    148 			continue;
    149 		write_idx = check_idx;
    150 		for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++)
    151 			if (old_super[check_idx] == new_super[check_idx])
    152 				break;
    153 		size = 2 * (check_idx - write_idx);
    154 #if 0
    155 		printf("Writing %d bytes starting at %d\n",
    156 		       size, write_idx*2);
    157 #endif
    158 		retval = io_channel_write_byte(fs->io,
    159 			       SUPERBLOCK_OFFSET + (2 * write_idx), size,
    160 					       new_super + write_idx);
    161 		if (retval)
    162 			return retval;
    163 	}
    164 	memcpy(fs->orig_super, super, SUPERBLOCK_SIZE);
    165 	return 0;
    166 }
    167 
    168 
    169 /*
    170  * Updates the revision to EXT2_DYNAMIC_REV
    171  */
    172 void ext2fs_update_dynamic_rev(ext2_filsys fs)
    173 {
    174 	struct ext2_super_block *sb = fs->super;
    175 
    176 	if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
    177 		return;
    178 
    179 	sb->s_rev_level = EXT2_DYNAMIC_REV;
    180 	sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
    181 	sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
    182 	/* s_uuid is handled by e2fsck already */
    183 	/* other fields should be left alone */
    184 }
    185 
    186 static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
    187 				    blk_t group_block,
    188 				    struct ext2_super_block *super_shadow)
    189 {
    190 	dgrp_t	sgrp = group;
    191 
    192 	if (sgrp > ((1 << 16) - 1))
    193 		sgrp = (1 << 16) - 1;
    194 #ifdef EXT2FS_ENABLE_SWAPFS
    195 	if (fs->flags & EXT2_FLAG_SWAP_BYTES)
    196 		super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
    197 	else
    198 #endif
    199 		fs->super->s_block_group_nr = sgrp;
    200 
    201 	return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE,
    202 				    super_shadow);
    203 }
    204 
    205 
    206 errcode_t ext2fs_flush(ext2_filsys fs)
    207 {
    208 	dgrp_t		i,j;
    209 	errcode_t	retval;
    210 	unsigned long	fs_state;
    211 	__u32		feature_incompat;
    212 	struct ext2_super_block *super_shadow = 0;
    213 	struct ext2_group_desc *group_shadow = 0;
    214 	struct ext2_group_desc *s, *t;
    215 	char	*group_ptr;
    216 	int	old_desc_blocks;
    217 
    218 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
    219 
    220 	fs_state = fs->super->s_state;
    221 	feature_incompat = fs->super->s_feature_incompat;
    222 
    223 	fs->super->s_wtime = fs->now ? fs->now : time(NULL);
    224 	fs->super->s_block_group_nr = 0;
    225 #ifdef EXT2FS_ENABLE_SWAPFS
    226 	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
    227 		retval = EXT2_ET_NO_MEMORY;
    228 		retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
    229 		if (retval)
    230 			goto errout;
    231 		retval = ext2fs_get_array(fs->blocksize, fs->desc_blocks,
    232 					&group_shadow);
    233 		if (retval)
    234 			goto errout;
    235 		memset(group_shadow, 0, (size_t) fs->blocksize *
    236 		       fs->desc_blocks);
    237 
    238 		/* swap the group descriptors */
    239 		for (j=0, s=fs->group_desc, t=group_shadow;
    240 		     j < fs->group_desc_count; j++, t++, s++) {
    241 			*t = *s;
    242 			ext2fs_swap_group_desc(t);
    243 		}
    244 	} else {
    245 		super_shadow = fs->super;
    246 		group_shadow = fs->group_desc;
    247 	}
    248 #else
    249 	super_shadow = fs->super;
    250 	group_shadow = fs->group_desc;
    251 #endif
    252 
    253 	/*
    254 	 * Set the state of the FS to be non-valid.  (The state has
    255 	 * already been backed up earlier, and will be restored after
    256 	 * we write out the backup superblocks.)
    257 	 */
    258 	fs->super->s_state &= ~EXT2_VALID_FS;
    259 	fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
    260 #ifdef EXT2FS_ENABLE_SWAPFS
    261 	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
    262 		*super_shadow = *fs->super;
    263 		ext2fs_swap_super(super_shadow);
    264 	}
    265 #endif
    266 
    267 	/*
    268 	 * If this is an external journal device, don't write out the
    269 	 * block group descriptors or any of the backup superblocks
    270 	 */
    271 	if (fs->super->s_feature_incompat &
    272 	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
    273 		goto write_primary_superblock_only;
    274 
    275 	/*
    276 	 * Write out the master group descriptors, and the backup
    277 	 * superblocks and group descriptors.
    278 	 */
    279 	group_ptr = (char *) group_shadow;
    280 	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
    281 		old_desc_blocks = fs->super->s_first_meta_bg;
    282 	else
    283 		old_desc_blocks = fs->desc_blocks;
    284 
    285 	for (i = 0; i < fs->group_desc_count; i++) {
    286 		blk_t	super_blk, old_desc_blk, new_desc_blk;
    287 		int	meta_bg;
    288 
    289 		ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk,
    290 					 &new_desc_blk, &meta_bg);
    291 
    292 		if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) {
    293 			retval = write_backup_super(fs, i, super_blk,
    294 						    super_shadow);
    295 			if (retval)
    296 				goto errout;
    297 		}
    298 		if (fs->flags & EXT2_FLAG_SUPER_ONLY)
    299 			continue;
    300 		if ((old_desc_blk) &&
    301 		    (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) {
    302 			retval = io_channel_write_blk(fs->io,
    303 			      old_desc_blk, old_desc_blocks, group_ptr);
    304 			if (retval)
    305 				goto errout;
    306 		}
    307 		if (new_desc_blk) {
    308 			retval = io_channel_write_blk(fs->io, new_desc_blk,
    309 				1, group_ptr + (meta_bg*fs->blocksize));
    310 			if (retval)
    311 				goto errout;
    312 		}
    313 	}
    314 
    315 	/*
    316 	 * If the write_bitmaps() function is present, call it to
    317 	 * flush the bitmaps.  This is done this way so that a simple
    318 	 * program that doesn't mess with the bitmaps doesn't need to
    319 	 * drag in the bitmaps.c code.
    320 	 */
    321 	if (fs->write_bitmaps) {
    322 		retval = fs->write_bitmaps(fs);
    323 		if (retval)
    324 			goto errout;
    325 	}
    326 
    327 write_primary_superblock_only:
    328 	/*
    329 	 * Write out master superblock.  This has to be done
    330 	 * separately, since it is located at a fixed location
    331 	 * (SUPERBLOCK_OFFSET).  We flush all other pending changes
    332 	 * out to disk first, just to avoid a race condition with an
    333 	 * insy-tinsy window....
    334 	 */
    335 
    336 	fs->super->s_block_group_nr = 0;
    337 	fs->super->s_state = fs_state;
    338 	fs->super->s_feature_incompat = feature_incompat;
    339 #ifdef EXT2FS_ENABLE_SWAPFS
    340 	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
    341 		*super_shadow = *fs->super;
    342 		ext2fs_swap_super(super_shadow);
    343 	}
    344 #endif
    345 
    346 	retval = io_channel_flush(fs->io);
    347 	retval = write_primary_superblock(fs, super_shadow);
    348 	if (retval)
    349 		goto errout;
    350 
    351 	fs->flags &= ~EXT2_FLAG_DIRTY;
    352 
    353 	retval = io_channel_flush(fs->io);
    354 errout:
    355 	fs->super->s_state = fs_state;
    356 	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
    357 		if (super_shadow)
    358 			ext2fs_free_mem(&super_shadow);
    359 		if (group_shadow)
    360 			ext2fs_free_mem(&group_shadow);
    361 	}
    362 	return retval;
    363 }
    364 
    365 errcode_t ext2fs_close(ext2_filsys fs)
    366 {
    367 	errcode_t	retval;
    368 
    369 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
    370 
    371 	if (fs->flags & EXT2_FLAG_DIRTY) {
    372 		retval = ext2fs_flush(fs);
    373 		if (retval)
    374 			return retval;
    375 	}
    376 	if (fs->write_bitmaps) {
    377 		retval = fs->write_bitmaps(fs);
    378 		if (retval)
    379 			return retval;
    380 	}
    381 	ext2fs_free(fs);
    382 	return 0;
    383 }
    384 
    385