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