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 = ext2fs_cpu_to_le16(sgrp); 264 265 retval = ext2fs_superblock_csum_set(fs, super_shadow); 266 if (retval) 267 return retval; 268 269 return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE, 270 super_shadow); 271 } 272 273 errcode_t ext2fs_flush(ext2_filsys fs) 274 { 275 return ext2fs_flush2(fs, 0); 276 } 277 278 errcode_t ext2fs_flush2(ext2_filsys fs, int flags) 279 { 280 dgrp_t i; 281 errcode_t retval; 282 unsigned long fs_state; 283 __u32 feature_incompat; 284 struct ext2_super_block *super_shadow = 0; 285 struct opaque_ext2_group_desc *group_shadow = 0; 286 #ifdef WORDS_BIGENDIAN 287 struct ext2_group_desc *gdp; 288 dgrp_t j; 289 #endif 290 char *group_ptr; 291 blk64_t old_desc_blocks; 292 struct ext2fs_numeric_progress_struct progress; 293 294 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 295 296 fs_state = fs->super->s_state; 297 feature_incompat = fs->super->s_feature_incompat; 298 299 fs->super->s_wtime = fs->now ? fs->now : time(NULL); 300 fs->super->s_block_group_nr = 0; 301 302 /* 303 * If the write_bitmaps() function is present, call it to 304 * flush the bitmaps. This is done this way so that a simple 305 * program that doesn't mess with the bitmaps doesn't need to 306 * drag in the bitmaps.c code. 307 * 308 * Bitmap checksums live in the group descriptor, so the 309 * bitmaps need to be written before the descriptors. 310 */ 311 if (fs->write_bitmaps) { 312 retval = fs->write_bitmaps(fs); 313 if (retval) 314 goto errout; 315 } 316 317 /* 318 * Set the state of the FS to be non-valid. (The state has 319 * already been backed up earlier, and will be restored after 320 * we write out the backup superblocks.) 321 */ 322 fs->super->s_state &= ~EXT2_VALID_FS; 323 ext2fs_clear_feature_journal_needs_recovery(fs->super); 324 325 /* Byte swap the superblock and the group descriptors if necessary */ 326 #ifdef WORDS_BIGENDIAN 327 retval = EXT2_ET_NO_MEMORY; 328 retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); 329 if (retval) 330 goto errout; 331 retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, 332 &group_shadow); 333 if (retval) 334 goto errout; 335 memcpy(super_shadow, fs->super, sizeof(struct ext2_super_block)); 336 memcpy(group_shadow, fs->group_desc, (size_t) fs->blocksize * 337 fs->desc_blocks); 338 339 ext2fs_swap_super(super_shadow); 340 for (j = 0; j < fs->group_desc_count; j++) { 341 gdp = ext2fs_group_desc(fs, group_shadow, j); 342 ext2fs_swap_group_desc2(fs, gdp); 343 } 344 #else 345 super_shadow = fs->super; 346 group_shadow = fs->group_desc; 347 #endif 348 349 /* 350 * If this is an external journal device, don't write out the 351 * block group descriptors or any of the backup superblocks 352 */ 353 if (ext2fs_has_feature_journal_dev(fs->super)) 354 goto write_primary_superblock_only; 355 356 /* 357 * Write out the master group descriptors, and the backup 358 * superblocks and group descriptors. 359 */ 360 group_ptr = (char *) group_shadow; 361 if (ext2fs_has_feature_meta_bg(fs->super)) { 362 old_desc_blocks = fs->super->s_first_meta_bg; 363 if (old_desc_blocks > fs->desc_blocks) 364 old_desc_blocks = fs->desc_blocks; 365 } else 366 old_desc_blocks = fs->desc_blocks; 367 368 if (fs->progress_ops && fs->progress_ops->init) 369 (fs->progress_ops->init)(fs, &progress, NULL, 370 fs->group_desc_count); 371 372 373 for (i = 0; i < fs->group_desc_count; i++) { 374 blk64_t super_blk, old_desc_blk, new_desc_blk; 375 376 if (fs->progress_ops && fs->progress_ops->update) 377 (fs->progress_ops->update)(fs, &progress, i); 378 ext2fs_super_and_bgd_loc2(fs, i, &super_blk, &old_desc_blk, 379 &new_desc_blk, 0); 380 381 if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { 382 retval = write_backup_super(fs, i, super_blk, 383 super_shadow); 384 if (retval) 385 goto errout; 386 } 387 if (fs->flags & EXT2_FLAG_SUPER_ONLY) 388 continue; 389 if ((old_desc_blk) && 390 (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { 391 retval = io_channel_write_blk64(fs->io, 392 old_desc_blk, old_desc_blocks, group_ptr); 393 if (retval) 394 goto errout; 395 } 396 if (new_desc_blk) { 397 int meta_bg = i / EXT2_DESC_PER_BLOCK(fs->super); 398 399 retval = io_channel_write_blk64(fs->io, new_desc_blk, 400 1, group_ptr + (meta_bg*fs->blocksize)); 401 if (retval) 402 goto errout; 403 } 404 } 405 406 if (fs->progress_ops && fs->progress_ops->close) 407 (fs->progress_ops->close)(fs, &progress, NULL); 408 409 write_primary_superblock_only: 410 /* 411 * Write out master superblock. This has to be done 412 * separately, since it is located at a fixed location 413 * (SUPERBLOCK_OFFSET). We flush all other pending changes 414 * out to disk first, just to avoid a race condition with an 415 * insy-tinsy window.... 416 */ 417 418 fs->super->s_block_group_nr = 0; 419 fs->super->s_state = fs_state; 420 fs->super->s_feature_incompat = feature_incompat; 421 #ifdef WORDS_BIGENDIAN 422 *super_shadow = *fs->super; 423 ext2fs_swap_super(super_shadow); 424 #endif 425 426 retval = ext2fs_superblock_csum_set(fs, super_shadow); 427 if (retval) 428 return retval; 429 430 if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) { 431 retval = io_channel_flush(fs->io); 432 if (retval) 433 goto errout; 434 } 435 retval = write_primary_superblock(fs, super_shadow); 436 if (retval) 437 goto errout; 438 439 fs->flags &= ~EXT2_FLAG_DIRTY; 440 441 if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) { 442 retval = io_channel_flush(fs->io); 443 if (retval) 444 goto errout; 445 } 446 errout: 447 fs->super->s_state = fs_state; 448 #ifdef WORDS_BIGENDIAN 449 if (super_shadow) 450 ext2fs_free_mem(&super_shadow); 451 if (group_shadow) 452 ext2fs_free_mem(&group_shadow); 453 #endif 454 return retval; 455 } 456 457 errcode_t ext2fs_close_free(ext2_filsys *fs_ptr) 458 { 459 errcode_t ret; 460 ext2_filsys fs = *fs_ptr; 461 462 ret = ext2fs_close2(fs, 0); 463 if (ret) 464 ext2fs_free(fs); 465 *fs_ptr = NULL; 466 return ret; 467 } 468 469 errcode_t ext2fs_close(ext2_filsys fs) 470 { 471 return ext2fs_close2(fs, 0); 472 } 473 474 errcode_t ext2fs_close2(ext2_filsys fs, int flags) 475 { 476 errcode_t retval; 477 int meta_blks; 478 io_stats stats = 0; 479 480 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 481 482 if (fs->write_bitmaps) { 483 retval = fs->write_bitmaps(fs); 484 if (retval) 485 return retval; 486 } 487 if (fs->super->s_kbytes_written && 488 fs->io->manager->get_stats) 489 fs->io->manager->get_stats(fs->io, &stats); 490 if (stats && stats->bytes_written && (fs->flags & EXT2_FLAG_RW)) { 491 fs->super->s_kbytes_written += stats->bytes_written >> 10; 492 meta_blks = fs->desc_blocks + 1; 493 if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) 494 fs->super->s_kbytes_written += meta_blks / 495 (fs->blocksize / 1024); 496 if ((fs->flags & EXT2_FLAG_DIRTY) == 0) 497 fs->flags |= EXT2_FLAG_SUPER_ONLY | EXT2_FLAG_DIRTY; 498 } 499 if (fs->flags & EXT2_FLAG_DIRTY) { 500 retval = ext2fs_flush2(fs, flags); 501 if (retval) 502 return retval; 503 } 504 505 retval = ext2fs_mmp_stop(fs); 506 if (retval) 507 return retval; 508 509 ext2fs_free(fs); 510 return 0; 511 } 512 513