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