1/* 2 * bb_inode.c --- routines to update the bad block inode. 3 * 4 * WARNING: This routine modifies a lot of state in the filesystem; if 5 * this routine returns an error, the bad block inode may be in an 6 * inconsistent state. 7 * 8 * Copyright (C) 1994, 1995 Theodore Ts'o. 9 * 10 * %Begin-Header% 11 * This file may be redistributed under the terms of the GNU Library 12 * General Public License, version 2. 13 * %End-Header% 14 */ 15 16#include <stdio.h> 17#include <string.h> 18#if HAVE_UNISTD_H 19#include <unistd.h> 20#endif 21#include <fcntl.h> 22#include <time.h> 23#if HAVE_SYS_STAT_H 24#include <sys/stat.h> 25#endif 26#if HAVE_SYS_TYPES_H 27#include <sys/types.h> 28#endif 29 30#include "ext2_fs.h" 31#include "ext2fs.h" 32 33struct set_badblock_record { 34 ext2_badblocks_iterate bb_iter; 35 int bad_block_count; 36 blk_t *ind_blocks; 37 int max_ind_blocks; 38 int ind_blocks_size; 39 int ind_blocks_ptr; 40 char *block_buf; 41 errcode_t err; 42}; 43 44static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, 45 e2_blkcnt_t blockcnt, 46 blk_t ref_block, int ref_offset, 47 void *priv_data); 48static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, 49 e2_blkcnt_t blockcnt, 50 blk_t ref_block, int ref_offset, 51 void *priv_data); 52 53/* 54 * Given a bad blocks bitmap, update the bad blocks inode to reflect 55 * the map. 56 */ 57errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) 58{ 59 errcode_t retval; 60 struct set_badblock_record rec; 61 struct ext2_inode inode; 62 63 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); 64 65 if (!fs->block_map) 66 return EXT2_ET_NO_BLOCK_BITMAP; 67 68 memset(&rec, 0, sizeof(rec)); 69 rec.max_ind_blocks = 10; 70 retval = ext2fs_get_array(rec.max_ind_blocks, sizeof(blk_t), 71 &rec.ind_blocks); 72 if (retval) 73 return retval; 74 memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); 75 retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); 76 if (retval) 77 goto cleanup; 78 memset(rec.block_buf, 0, fs->blocksize); 79 rec.err = 0; 80 81 /* 82 * First clear the old bad blocks (while saving the indirect blocks) 83 */ 84 retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 85 BLOCK_FLAG_DEPTH_TRAVERSE, 0, 86 clear_bad_block_proc, &rec); 87 if (retval) 88 goto cleanup; 89 if (rec.err) { 90 retval = rec.err; 91 goto cleanup; 92 } 93 94 /* 95 * Now set the bad blocks! 96 * 97 * First, mark the bad blocks as used. This prevents a bad 98 * block from being used as an indirecto block for the bad 99 * block inode (!). 100 */ 101 if (bb_list) { 102 retval = ext2fs_badblocks_list_iterate_begin(bb_list, 103 &rec.bb_iter); 104 if (retval) 105 goto cleanup; 106 retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 107 BLOCK_FLAG_APPEND, 0, 108 set_bad_block_proc, &rec); 109 ext2fs_badblocks_list_iterate_end(rec.bb_iter); 110 if (retval) 111 goto cleanup; 112 if (rec.err) { 113 retval = rec.err; 114 goto cleanup; 115 } 116 } 117 118 /* 119 * Update the bad block inode's mod time and block count 120 * field. 121 */ 122 retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); 123 if (retval) 124 goto cleanup; 125 126 inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); 127 if (!inode.i_ctime) 128 inode.i_ctime = fs->now ? fs->now : time(0); 129 ext2fs_iblk_set(fs, &inode, rec.bad_block_count); 130 inode.i_size = rec.bad_block_count * fs->blocksize; 131 132 retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); 133 if (retval) 134 goto cleanup; 135 136cleanup: 137 ext2fs_free_mem(&rec.ind_blocks); 138 ext2fs_free_mem(&rec.block_buf); 139 return retval; 140} 141 142/* 143 * Helper function for update_bb_inode() 144 * 145 * Clear the bad blocks in the bad block inode, while saving the 146 * indirect blocks. 147 */ 148#ifdef __TURBOC__ 149 #pragma argsused 150#endif 151static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, 152 e2_blkcnt_t blockcnt, 153 blk_t ref_block EXT2FS_ATTR((unused)), 154 int ref_offset EXT2FS_ATTR((unused)), 155 void *priv_data) 156{ 157 struct set_badblock_record *rec = (struct set_badblock_record *) 158 priv_data; 159 errcode_t retval; 160 unsigned long old_size; 161 162 if (!*block_nr) 163 return 0; 164 165 /* 166 * If the block number is outrageous, clear it and ignore it. 167 */ 168 if (*block_nr >= ext2fs_blocks_count(fs->super) || 169 *block_nr < fs->super->s_first_data_block) { 170 *block_nr = 0; 171 return BLOCK_CHANGED; 172 } 173 174 if (blockcnt < 0) { 175 if (rec->ind_blocks_size >= rec->max_ind_blocks) { 176 old_size = rec->max_ind_blocks * sizeof(blk_t); 177 rec->max_ind_blocks += 10; 178 retval = ext2fs_resize_mem(old_size, 179 rec->max_ind_blocks * sizeof(blk_t), 180 &rec->ind_blocks); 181 if (retval) { 182 rec->max_ind_blocks -= 10; 183 rec->err = retval; 184 return BLOCK_ABORT; 185 } 186 } 187 rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; 188 } 189 190 /* 191 * Mark the block as unused, and update accounting information 192 */ 193 ext2fs_block_alloc_stats2(fs, *block_nr, -1); 194 195 *block_nr = 0; 196 return BLOCK_CHANGED; 197} 198 199 200/* 201 * Helper function for update_bb_inode() 202 * 203 * Set the block list in the bad block inode, using the supplied bitmap. 204 */ 205#ifdef __TURBOC__ 206 #pragma argsused 207#endif 208static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, 209 e2_blkcnt_t blockcnt, 210 blk_t ref_block EXT2FS_ATTR((unused)), 211 int ref_offset EXT2FS_ATTR((unused)), 212 void *priv_data) 213{ 214 struct set_badblock_record *rec = (struct set_badblock_record *) 215 priv_data; 216 errcode_t retval; 217 blk_t blk; 218 219 if (blockcnt >= 0) { 220 /* 221 * Get the next bad block. 222 */ 223 if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) 224 return BLOCK_ABORT; 225 rec->bad_block_count++; 226 } else { 227 /* 228 * An indirect block; fetch a block from the 229 * previously used indirect block list. The block 230 * most be not marked as used; if so, get another one. 231 * If we run out of reserved indirect blocks, allocate 232 * a new one. 233 */ 234 retry: 235 if (rec->ind_blocks_ptr < rec->ind_blocks_size) { 236 blk = rec->ind_blocks[rec->ind_blocks_ptr++]; 237 if (ext2fs_test_block_bitmap2(fs->block_map, blk)) 238 goto retry; 239 } else { 240 retval = ext2fs_new_block(fs, 0, 0, &blk); 241 if (retval) { 242 rec->err = retval; 243 return BLOCK_ABORT; 244 } 245 } 246 retval = io_channel_write_blk64(fs->io, blk, 1, rec->block_buf); 247 if (retval) { 248 rec->err = retval; 249 return BLOCK_ABORT; 250 } 251 } 252 253 /* 254 * Update block counts 255 */ 256 ext2fs_block_alloc_stats2(fs, blk, +1); 257 258 *block_nr = blk; 259 return BLOCK_CHANGED; 260} 261 262 263 264 265 266 267