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