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