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