closefs.c revision bfd418832d46da1767517b40a7857de449ea03fe
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 22static 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 35int 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/* 49 * This function forces out the primary superblock. We need to only 50 * write out those fields which we have changed, since if the 51 * filesystem is mounted, it may have changed some of the other 52 * fields. 53 * 54 * It takes as input a superblock which has already been byte swapped 55 * (if necessary). 56 * 57 */ 58static errcode_t write_primary_superblock(ext2_filsys fs, 59 struct ext2_super_block *super) 60{ 61 __u16 *old_super, *new_super; 62 int check_idx, write_idx, size; 63 errcode_t retval; 64 65 if (!fs->io->manager->write_byte || !fs->orig_super) { 66 io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); 67 retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, 68 super); 69 io_channel_set_blksize(fs->io, fs->blocksize); 70 return retval; 71 } 72 73 old_super = (__u16 *) fs->orig_super; 74 new_super = (__u16 *) super; 75 76 for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { 77 if (old_super[check_idx] == new_super[check_idx]) 78 continue; 79 write_idx = check_idx; 80 for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) 81 if (old_super[check_idx] == new_super[check_idx]) 82 break; 83 size = 2 * (check_idx - write_idx); 84#if 0 85 printf("Writing %d bytes starting at %d\n", 86 size, write_idx*2); 87#endif 88 retval = io_channel_write_byte(fs->io, 89 SUPERBLOCK_OFFSET + (2 * write_idx), size, 90 new_super + write_idx); 91 if (retval) 92 return retval; 93 } 94 memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); 95 return 0; 96} 97 98 99/* 100 * Updates the revision to EXT2_DYNAMIC_REV 101 */ 102void ext2fs_update_dynamic_rev(ext2_filsys fs) 103{ 104 struct ext2_super_block *sb = fs->super; 105 106 if (sb->s_rev_level > EXT2_GOOD_OLD_REV) 107 return; 108 109 sb->s_rev_level = EXT2_DYNAMIC_REV; 110 sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; 111 sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; 112 /* s_uuid is handled by e2fsck already */ 113 /* other fields should be left alone */ 114} 115 116/* 117 * This writes out the block group descriptors the original, 118 * old-fashioned way. 119 */ 120static errcode_t write_bgdesc(ext2_filsys fs, dgrp_t group, blk_t group_block, 121 struct ext2_group_desc *group_shadow) 122{ 123 errcode_t retval; 124 char *group_ptr = (char *) group_shadow; 125 int j, old_desc_blocks, mod; 126 int has_super = ext2fs_bg_has_super(fs, group); 127 dgrp_t meta_bg_size, meta_bg; 128 129 meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc)); 130 meta_bg = group / meta_bg_size; 131 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) 132 old_desc_blocks = fs->super->s_first_meta_bg; 133 else 134 old_desc_blocks = fs->desc_blocks; 135 if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || 136 (meta_bg < fs->super->s_first_meta_bg)) { 137 if (!has_super || 138 ((fs->flags & EXT2_FLAG_MASTER_SB_ONLY) && (group != 0))) 139 return 0; 140 for (j=0; j < old_desc_blocks; j++) { 141 retval = io_channel_write_blk(fs->io, 142 group_block+1+j, 1, 143 group_ptr); 144 if (retval) 145 return retval; 146 group_ptr += fs->blocksize; 147 } 148 } else { 149 if (has_super) 150 group_block++; 151 mod = group % meta_bg_size; 152 if ((mod == 0) || (mod == 1) || (mod == (meta_bg_size-1))) { 153 if (mod && (fs->flags & EXT2_FLAG_MASTER_SB_ONLY)) 154 return 0; 155 return io_channel_write_blk(fs->io, group_block, 156 1, group_ptr + (meta_bg*fs->blocksize)); 157 } 158 } 159 return 0; 160} 161 162 163static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, 164 blk_t group_block, 165 struct ext2_super_block *super_shadow) 166{ 167 dgrp_t sgrp = group; 168 169 if (sgrp > ((1 << 16) - 1)) 170 sgrp = (1 << 16) - 1; 171#ifdef EXT2FS_ENABLE_SWAPFS 172 if (fs->flags & EXT2_FLAG_SWAP_BYTES) 173 super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); 174 else 175#endif 176 fs->super->s_block_group_nr = sgrp; 177 178 return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, 179 super_shadow); 180} 181 182 183errcode_t ext2fs_flush(ext2_filsys fs) 184{ 185 dgrp_t i,j; 186 blk_t group_block; 187 errcode_t retval; 188 unsigned long fs_state; 189 struct ext2_super_block *super_shadow = 0; 190 struct ext2_group_desc *group_shadow = 0; 191 struct ext2_group_desc *s, *t; 192 193 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 194 195 fs_state = fs->super->s_state; 196 197 fs->super->s_wtime = time(NULL); 198 fs->super->s_block_group_nr = 0; 199#ifdef EXT2FS_ENABLE_SWAPFS 200 if (fs->flags & EXT2_FLAG_SWAP_BYTES) { 201 retval = EXT2_ET_NO_MEMORY; 202 retval = ext2fs_get_mem(SUPERBLOCK_SIZE, 203 (void **) &super_shadow); 204 if (retval) 205 goto errout; 206 retval = ext2fs_get_mem((size_t)(fs->blocksize * 207 fs->desc_blocks), 208 (void **) &group_shadow); 209 if (retval) 210 goto errout; 211 memset(group_shadow, 0, (size_t) fs->blocksize * 212 fs->desc_blocks); 213 214 /* swap the superblock */ 215 *super_shadow = *fs->super; 216 ext2fs_swap_super(super_shadow); 217 218 /* swap the group descriptors */ 219 for (j=0, s=fs->group_desc, t=group_shadow; 220 j < fs->group_desc_count; j++, t++, s++) { 221 *t = *s; 222 ext2fs_swap_group_desc(t); 223 } 224 } else { 225 super_shadow = fs->super; 226 group_shadow = fs->group_desc; 227 } 228#else 229 super_shadow = fs->super; 230 group_shadow = fs->group_desc; 231#endif 232 233 /* 234 * Write out master superblock. This has to be done 235 * separately, since it is located at a fixed location 236 * (SUPERBLOCK_OFFSET). 237 */ 238 retval = write_primary_superblock(fs, super_shadow); 239 if (retval) 240 goto errout; 241 242 /* 243 * If this is an external journal device, don't write out the 244 * block group descriptors or any of the backup superblocks 245 */ 246 if (fs->super->s_feature_incompat & 247 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { 248 retval = 0; 249 goto errout; 250 } 251 252 /* 253 * Set the state of the FS to be non-valid. (The state has 254 * already been backed up earlier, and will be restored when 255 * we exit.) 256 */ 257 fs->super->s_state &= ~EXT2_VALID_FS; 258#ifdef EXT2FS_ENABLE_SWAPFS 259 if (fs->flags & EXT2_FLAG_SWAP_BYTES) { 260 *super_shadow = *fs->super; 261 ext2fs_swap_super(super_shadow); 262 } 263#endif 264 265 /* 266 * Write out the master group descriptors, and the backup 267 * superblocks and group descriptors. 268 */ 269 group_block = fs->super->s_first_data_block; 270 for (i = 0; i < fs->group_desc_count; i++) { 271 if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) && 272 i && ext2fs_bg_has_super(fs, i)) { 273 retval = write_backup_super(fs, i, group_block, 274 super_shadow); 275 if (retval) 276 goto errout; 277 } 278 if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) { 279 if ((retval = write_bgdesc(fs, i, group_block, 280 group_shadow))) 281 goto errout; 282 } 283 group_block += EXT2_BLOCKS_PER_GROUP(fs->super); 284 } 285 fs->super->s_block_group_nr = 0; 286 287 /* 288 * If the write_bitmaps() function is present, call it to 289 * flush the bitmaps. This is done this way so that a simple 290 * program that doesn't mess with the bitmaps doesn't need to 291 * drag in the bitmaps.c code. 292 */ 293 if (fs->write_bitmaps) { 294 retval = fs->write_bitmaps(fs); 295 if (retval) 296 goto errout; 297 } 298 299 fs->flags &= ~EXT2_FLAG_DIRTY; 300 301 /* 302 * Flush the blocks out to disk 303 */ 304 retval = io_channel_flush(fs->io); 305errout: 306 fs->super->s_state = fs_state; 307 if (fs->flags & EXT2_FLAG_SWAP_BYTES) { 308 if (super_shadow) 309 ext2fs_free_mem((void **) &super_shadow); 310 if (group_shadow) 311 ext2fs_free_mem((void **) &group_shadow); 312 } 313 return retval; 314} 315 316errcode_t ext2fs_close(ext2_filsys fs) 317{ 318 errcode_t retval; 319 320 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 321 322 if (fs->flags & EXT2_FLAG_DIRTY) { 323 retval = ext2fs_flush(fs); 324 if (retval) 325 return retval; 326 } 327 if (fs->write_bitmaps) { 328 retval = fs->write_bitmaps(fs); 329 if (retval) 330 return retval; 331 } 332 ext2fs_free(fs); 333 return 0; 334} 335 336