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