closefs.c revision 11d1116a7c0b833af317249d12025413fecbcd70
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 Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include "config.h" 13#include <stdio.h> 14#if HAVE_UNISTD_H 15#include <unistd.h> 16#endif 17#include <time.h> 18#include <string.h> 19 20#include "ext2_fs.h" 21#include "ext2fsP.h" 22 23static int test_root(unsigned int a, unsigned int b) 24{ 25 while (1) { 26 if (a < b) 27 return 0; 28 if (a == b) 29 return 1; 30 if (a % b) 31 return 0; 32 a = a / b; 33 } 34} 35 36int ext2fs_bg_has_super(ext2_filsys fs, dgrp_t group) 37{ 38 if (!(fs->super->s_feature_ro_compat & 39 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) || group <= 1) 40 return 1; 41 if (!(group & 1)) 42 return 0; 43 if (test_root(group, 3) || (test_root(group, 5)) || 44 test_root(group, 7)) 45 return 1; 46 47 return 0; 48} 49 50/* 51 * ext2fs_super_and_bgd_loc2() 52 * @fs: ext2 fs pointer 53 * @group given block group 54 * @ret_super_blk: if !NULL, returns super block location 55 * @ret_old_desc_blk: if !NULL, returns location of the old block 56 * group descriptor 57 * @ret_new_desc_blk: if !NULL, returns location of meta_bg block 58 * group descriptor 59 * @ret_used_blks: if !NULL, returns number of blocks used by 60 * super block and group_descriptors. 61 * 62 * Returns errcode_t of 0 63 */ 64errcode_t ext2fs_super_and_bgd_loc2(ext2_filsys fs, 65 dgrp_t group, 66 blk64_t *ret_super_blk, 67 blk64_t *ret_old_desc_blk, 68 blk64_t *ret_new_desc_blk, 69 blk_t *ret_used_blks) 70{ 71 blk64_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; 72 unsigned int meta_bg, meta_bg_size; 73 blk_t numblocks = 0; 74 blk64_t old_desc_blocks; 75 int has_super; 76 77 group_block = ext2fs_group_first_block2(fs, group); 78 if (group_block == 0 && fs->blocksize == 1024) 79 group_block = 1; /* Deal with 1024 blocksize && bigalloc */ 80 81 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) 82 old_desc_blocks = fs->super->s_first_meta_bg; 83 else 84 old_desc_blocks = 85 fs->desc_blocks + fs->super->s_reserved_gdt_blocks; 86 87 has_super = ext2fs_bg_has_super(fs, group); 88 89 if (has_super) { 90 super_blk = group_block; 91 numblocks++; 92 } 93 meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); 94 meta_bg = group / meta_bg_size; 95 96 if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || 97 (meta_bg < fs->super->s_first_meta_bg)) { 98 if (has_super) { 99 old_desc_blk = group_block + 1; 100 numblocks += old_desc_blocks; 101 } 102 } else { 103 if (((group % meta_bg_size) == 0) || 104 ((group % meta_bg_size) == 1) || 105 ((group % meta_bg_size) == (meta_bg_size-1))) { 106 if (has_super) 107 has_super = 1; 108 new_desc_blk = group_block + has_super; 109 numblocks++; 110 } 111 } 112 113 if (ret_super_blk) 114 *ret_super_blk = super_blk; 115 if (ret_old_desc_blk) 116 *ret_old_desc_blk = old_desc_blk; 117 if (ret_new_desc_blk) 118 *ret_new_desc_blk = new_desc_blk; 119 if (ret_used_blks) 120 *ret_used_blks = numblocks; 121 122 return 0; 123} 124 125/* 126 * This function returns the location of the superblock, block group 127 * descriptors for a given block group. It currently returns the 128 * number of free blocks assuming that inode table and allocation 129 * bitmaps will be in the group. This is not necessarily the case 130 * when the flex_bg feature is enabled, so callers should take care! 131 * It was only really intended for use by mke2fs, and even there it's 132 * not that useful. 133 * 134 * The ext2fs_super_and_bgd_loc2() function is 64-bit block number 135 * capable and returns the number of blocks used by super block and 136 * group descriptors. 137 */ 138int ext2fs_super_and_bgd_loc(ext2_filsys fs, 139 dgrp_t group, 140 blk_t *ret_super_blk, 141 blk_t *ret_old_desc_blk, 142 blk_t *ret_new_desc_blk, 143 int *ret_meta_bg) 144{ 145 blk64_t ret_super_blk2; 146 blk64_t ret_old_desc_blk2; 147 blk64_t ret_new_desc_blk2; 148 blk_t ret_used_blks; 149 blk_t numblocks; 150 unsigned int meta_bg_size; 151 152 ext2fs_super_and_bgd_loc2(fs, group, &ret_super_blk2, 153 &ret_old_desc_blk2, 154 &ret_new_desc_blk2, 155 &ret_used_blks); 156 157 numblocks = ext2fs_group_blocks_count(fs, group); 158 159 if (ret_super_blk) 160 *ret_super_blk = (blk_t)ret_super_blk2; 161 if (ret_old_desc_blk) 162 *ret_old_desc_blk = (blk_t)ret_old_desc_blk2; 163 if (ret_new_desc_blk) 164 *ret_new_desc_blk = (blk_t)ret_new_desc_blk2; 165 if (ret_meta_bg) { 166 meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); 167 *ret_meta_bg = group / meta_bg_size; 168 } 169 170 numblocks -= 2 + fs->inode_blocks_per_group + ret_used_blks; 171 172 return numblocks; 173} 174 175/* 176 * This function forces out the primary superblock. We need to only 177 * write out those fields which we have changed, since if the 178 * filesystem is mounted, it may have changed some of the other 179 * fields. 180 * 181 * It takes as input a superblock which has already been byte swapped 182 * (if necessary). 183 * 184 */ 185static errcode_t write_primary_superblock(ext2_filsys fs, 186 struct ext2_super_block *super) 187{ 188 __u16 *old_super, *new_super; 189 int check_idx, write_idx, size; 190 errcode_t retval; 191 192 if (!fs->io->manager->write_byte || !fs->orig_super) { 193 fallback: 194 io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); 195 retval = io_channel_write_blk64(fs->io, 1, -SUPERBLOCK_SIZE, 196 super); 197 io_channel_set_blksize(fs->io, fs->blocksize); 198 return retval; 199 } 200 201 old_super = (__u16 *) fs->orig_super; 202 new_super = (__u16 *) super; 203 204 for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { 205 if (old_super[check_idx] == new_super[check_idx]) 206 continue; 207 write_idx = check_idx; 208 for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) 209 if (old_super[check_idx] == new_super[check_idx]) 210 break; 211 size = 2 * (check_idx - write_idx); 212#if 0 213 printf("Writing %d bytes starting at %d\n", 214 size, write_idx*2); 215#endif 216 retval = io_channel_write_byte(fs->io, 217 SUPERBLOCK_OFFSET + (2 * write_idx), size, 218 new_super + write_idx); 219 if (retval == EXT2_ET_UNIMPLEMENTED) 220 goto fallback; 221 if (retval) 222 return retval; 223 } 224 memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); 225 return 0; 226} 227 228 229/* 230 * Updates the revision to EXT2_DYNAMIC_REV 231 */ 232void ext2fs_update_dynamic_rev(ext2_filsys fs) 233{ 234 struct ext2_super_block *sb = fs->super; 235 236 if (sb->s_rev_level > EXT2_GOOD_OLD_REV) 237 return; 238 239 sb->s_rev_level = EXT2_DYNAMIC_REV; 240 sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; 241 sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; 242 /* s_uuid is handled by e2fsck already */ 243 /* other fields should be left alone */ 244} 245 246static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, 247 blk64_t group_block, 248 struct ext2_super_block *super_shadow) 249{ 250 dgrp_t sgrp = group; 251 252 if (sgrp > ((1 << 16) - 1)) 253 sgrp = (1 << 16) - 1; 254#ifdef WORDS_BIGENDIAN 255 super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); 256#else 257 fs->super->s_block_group_nr = sgrp; 258#endif 259 260 return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE, 261 super_shadow); 262} 263 264errcode_t ext2fs_flush(ext2_filsys fs) 265{ 266 return ext2fs_flush2(fs, 0); 267} 268 269errcode_t ext2fs_flush2(ext2_filsys fs, int flags) 270{ 271 dgrp_t i; 272 errcode_t retval; 273 unsigned long fs_state; 274 __u32 feature_incompat; 275 struct ext2_super_block *super_shadow = 0; 276 struct ext2_group_desc *group_shadow = 0; 277#ifdef WORDS_BIGENDIAN 278 struct ext2_group_desc *gdp; 279 dgrp_t j; 280#endif 281 char *group_ptr; 282 int old_desc_blocks; 283 struct ext2fs_numeric_progress_struct progress; 284 285 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 286 287 fs_state = fs->super->s_state; 288 feature_incompat = fs->super->s_feature_incompat; 289 290 fs->super->s_wtime = fs->now ? fs->now : time(NULL); 291 fs->super->s_block_group_nr = 0; 292#ifdef WORDS_BIGENDIAN 293 retval = EXT2_ET_NO_MEMORY; 294 retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); 295 if (retval) 296 goto errout; 297 retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, 298 &group_shadow); 299 if (retval) 300 goto errout; 301 memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize * 302 fs->desc_blocks); 303 304 /* swap the group descriptors */ 305 for (j = 0; j < fs->group_desc_count; j++) { 306 gdp = ext2fs_group_desc(fs, group_shadow, j); 307 ext2fs_swap_group_desc2(fs, gdp); 308 } 309#else 310 super_shadow = fs->super; 311 group_shadow = ext2fs_group_desc(fs, fs->group_desc, 0); 312#endif 313 314 /* 315 * Set the state of the FS to be non-valid. (The state has 316 * already been backed up earlier, and will be restored after 317 * we write out the backup superblocks.) 318 */ 319 fs->super->s_state &= ~EXT2_VALID_FS; 320 fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; 321#ifdef WORDS_BIGENDIAN 322 *super_shadow = *fs->super; 323 ext2fs_swap_super(super_shadow); 324#endif 325 326 /* 327 * If this is an external journal device, don't write out the 328 * block group descriptors or any of the backup superblocks 329 */ 330 if (fs->super->s_feature_incompat & 331 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) 332 goto write_primary_superblock_only; 333 334 /* 335 * Write out the master group descriptors, and the backup 336 * superblocks and group descriptors. 337 */ 338 group_ptr = (char *) group_shadow; 339 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) 340 old_desc_blocks = fs->super->s_first_meta_bg; 341 else 342 old_desc_blocks = fs->desc_blocks; 343 344 ext2fs_numeric_progress_init(fs, &progress, NULL, 345 fs->group_desc_count); 346 347 348 for (i = 0; i < fs->group_desc_count; i++) { 349 blk64_t super_blk, old_desc_blk, new_desc_blk; 350 351 ext2fs_numeric_progress_update(fs, &progress, i); 352 ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, 353 &new_desc_blk, 0); 354 355 if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { 356 retval = write_backup_super(fs, i, super_blk, 357 super_shadow); 358 if (retval) 359 goto errout; 360 } 361 if (fs->flags & EXT2_FLAG_SUPER_ONLY) 362 continue; 363 if ((old_desc_blk) && 364 (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { 365 retval = io_channel_write_blk64(fs->io, 366 old_desc_blk, old_desc_blocks, group_ptr); 367 if (retval) 368 goto errout; 369 } 370 if (new_desc_blk) { 371 int meta_bg = i / EXT2_DESC_PER_BLOCK(fs->super); 372 373 retval = io_channel_write_blk64(fs->io, new_desc_blk, 374 1, group_ptr + (meta_bg*fs->blocksize)); 375 if (retval) 376 goto errout; 377 } 378 } 379 380 ext2fs_numeric_progress_close(fs, &progress, NULL); 381 382 /* 383 * If the write_bitmaps() function is present, call it to 384 * flush the bitmaps. This is done this way so that a simple 385 * program that doesn't mess with the bitmaps doesn't need to 386 * drag in the bitmaps.c code. 387 */ 388 if (fs->write_bitmaps) { 389 retval = fs->write_bitmaps(fs); 390 if (retval) 391 goto errout; 392 } 393 394write_primary_superblock_only: 395 /* 396 * Write out master superblock. This has to be done 397 * separately, since it is located at a fixed location 398 * (SUPERBLOCK_OFFSET). We flush all other pending changes 399 * out to disk first, just to avoid a race condition with an 400 * insy-tinsy window.... 401 */ 402 403 fs->super->s_block_group_nr = 0; 404 fs->super->s_state = fs_state; 405 fs->super->s_feature_incompat = feature_incompat; 406#ifdef WORDS_BIGENDIAN 407 *super_shadow = *fs->super; 408 ext2fs_swap_super(super_shadow); 409#endif 410 411 if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) 412 retval = io_channel_flush(fs->io); 413 retval = write_primary_superblock(fs, super_shadow); 414 if (retval) 415 goto errout; 416 417 fs->flags &= ~EXT2_FLAG_DIRTY; 418 419 if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) 420 retval = io_channel_flush(fs->io); 421errout: 422 fs->super->s_state = fs_state; 423#ifdef WORDS_BIGENDIAN 424 if (super_shadow) 425 ext2fs_free_mem(&super_shadow); 426 if (group_shadow) 427 ext2fs_free_mem(&group_shadow); 428#endif 429 return retval; 430} 431 432errcode_t ext2fs_close(ext2_filsys fs) 433{ 434 return ext2fs_close2(fs, 0); 435} 436 437errcode_t ext2fs_close2(ext2_filsys fs, int flags) 438{ 439 errcode_t retval; 440 int meta_blks; 441 io_stats stats = 0; 442 443 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 444 445 if (fs->write_bitmaps) { 446 retval = fs->write_bitmaps(fs); 447 if (retval) 448 return retval; 449 } 450 if (fs->super->s_kbytes_written && 451 fs->io->manager->get_stats) 452 fs->io->manager->get_stats(fs->io, &stats); 453 if (stats && stats->bytes_written && (fs->flags & EXT2_FLAG_RW)) { 454 fs->super->s_kbytes_written += stats->bytes_written >> 10; 455 meta_blks = fs->desc_blocks + 1; 456 if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) 457 fs->super->s_kbytes_written += meta_blks / 458 (fs->blocksize / 1024); 459 if ((fs->flags & EXT2_FLAG_DIRTY) == 0) 460 fs->flags |= EXT2_FLAG_SUPER_ONLY | EXT2_FLAG_DIRTY; 461 } 462 if (fs->flags & EXT2_FLAG_DIRTY) { 463 retval = ext2fs_flush2(fs, flags); 464 if (retval) 465 return retval; 466 } 467 468 retval = ext2fs_mmp_stop(fs); 469 if (retval) 470 return retval; 471 472 ext2fs_free(fs); 473 return 0; 474} 475 476