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