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