blob: a5dd3a965a8721e943a5a50bba5aa95b12fc7ad5 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
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 *
Theodore Ts'o21c84b71997-04-29 16:15:03 +00008 * Copyright (C) 1994, 1995 Theodore Ts'o.
9 *
10 * %Begin-Header%
11 * This file may be redistributed under the terms of the GNU Public
12 * License.
13 * %End-Header%
Theodore Ts'o3839e651997-04-26 13:21:57 +000014 */
15
16#include <stdio.h>
17#include <string.h>
18#include <unistd.h>
19#include <stdlib.h>
20#include <fcntl.h>
21#include <time.h>
22#include <sys/stat.h>
23#include <sys/types.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000024#if HAVE_ERRNO_H
25#include <errno.h>
26#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000027
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <linux/ext2_fs.h>
29
30#include "ext2fs.h"
31
32struct set_badblock_record {
Theodore Ts'o21c84b71997-04-29 16:15:03 +000033 ext2_badblocks_iterate bb_iter;
Theodore Ts'o3839e651997-04-26 13:21:57 +000034 int bad_block_count;
35 blk_t *ind_blocks;
36 int max_ind_blocks;
37 int ind_blocks_size;
38 int ind_blocks_ptr;
39 char *block_buf;
40 errcode_t err;
41};
42
43static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
Theodore Ts'o21c84b71997-04-29 16:15:03 +000044 blk_t ref_block, int ref_offset, void *private);
Theodore Ts'o3839e651997-04-26 13:21:57 +000045static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
Theodore Ts'o21c84b71997-04-29 16:15:03 +000046 blk_t ref_block, int ref_offset,
47 void *private);
Theodore Ts'o3839e651997-04-26 13:21:57 +000048
49/*
50 * Given a bad blocks bitmap, update the bad blocks inode to reflect
51 * the map.
52 */
Theodore Ts'o21c84b71997-04-29 16:15:03 +000053errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
Theodore Ts'o3839e651997-04-26 13:21:57 +000054{
55 errcode_t retval;
56 struct set_badblock_record rec;
57 struct ext2_inode inode;
Theodore Ts'of3db3561997-04-26 13:34:30 +000058 blk_t blk;
Theodore Ts'o3839e651997-04-26 13:21:57 +000059
Theodore Ts'of3db3561997-04-26 13:34:30 +000060 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
61
Theodore Ts'o3839e651997-04-26 13:21:57 +000062 if (!fs->block_map)
63 return EXT2_ET_NO_BLOCK_BITMAP;
64
65 rec.bad_block_count = 0;
66 rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
67 rec.max_ind_blocks = 10;
68 rec.ind_blocks = malloc(rec.max_ind_blocks * sizeof(blk_t));
69 if (!rec.ind_blocks)
70 return ENOMEM;
71 memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
72 rec.block_buf = malloc(fs->blocksize);
73 if (!rec.block_buf) {
74 retval = ENOMEM;
75 goto cleanup;
76 }
77 memset(rec.block_buf, 0, fs->blocksize);
78 rec.err = 0;
79
80 /*
81 * First clear the old bad blocks (while saving the indirect blocks)
82 */
Theodore Ts'o21c84b71997-04-29 16:15:03 +000083 retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
84 BLOCK_FLAG_DEPTH_TRAVERSE, 0,
85 clear_bad_block_proc, &rec);
Theodore Ts'o3839e651997-04-26 13:21:57 +000086 if (retval)
87 goto cleanup;
88 if (rec.err) {
89 retval = rec.err;
90 goto cleanup;
91 }
92
93 /*
94 * Now set the bad blocks!
Theodore Ts'of3db3561997-04-26 13:34:30 +000095 *
96 * First, mark the bad blocks as used. This prevents a bad
97 * block from being used as an indirecto block for the bad
98 * block inode (!).
Theodore Ts'o3839e651997-04-26 13:21:57 +000099 */
100 if (bb_list) {
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000101 retval = ext2fs_badblocks_list_iterate_begin(bb_list,
102 &rec.bb_iter);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000103 if (retval)
104 goto cleanup;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000105 while (ext2fs_badblocks_list_iterate(rec.bb_iter, &blk)) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000106 ext2fs_mark_block_bitmap(fs->block_map, blk);
107 }
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000108 ext2fs_badblocks_list_iterate_end(rec.bb_iter);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000109 ext2fs_mark_bb_dirty(fs);
110
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000111 retval = ext2fs_badblocks_list_iterate_begin(bb_list,
112 &rec.bb_iter);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000113 if (retval)
114 goto cleanup;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000115 retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
116 BLOCK_FLAG_APPEND, 0,
117 set_bad_block_proc, &rec);
118 ext2fs_badblocks_list_iterate_end(rec.bb_iter);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000119 if (retval)
120 goto cleanup;
121 if (rec.err) {
122 retval = rec.err;
123 goto cleanup;
124 }
125 }
126
127 /*
128 * Update the bad block inode's mod time and block count
129 * field.
130 */
131 retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
132 if (retval)
133 goto cleanup;
134
135 inode.i_atime = inode.i_mtime = time(0);
136 if (!inode.i_ctime)
137 inode.i_ctime = time(0);
138 inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
139 inode.i_size = rec.bad_block_count * fs->blocksize;
140
141 retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
142 if (retval)
143 goto cleanup;
144
145cleanup:
146 free(rec.ind_blocks);
147 free(rec.block_buf);
148 return retval;
149}
150
151/*
152 * Helper function for update_bb_inode()
153 *
154 * Clear the bad blocks in the bad block inode, while saving the
155 * indirect blocks.
156 */
157static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000158 blk_t ref_block, int ref_offset, void *private)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000159{
160 struct set_badblock_record *rec = (struct set_badblock_record *)
161 private;
162 int group;
163
164 if (!*block_nr)
165 return 0;
166
Theodore Ts'of3db3561997-04-26 13:34:30 +0000167 /*
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
Theodore Ts'o3839e651997-04-26 13:21:57 +0000176 if (blockcnt < 0) {
177 if (rec->ind_blocks_size >= rec->max_ind_blocks) {
178 rec->max_ind_blocks += 10;
179 rec->ind_blocks = realloc(rec->ind_blocks,
180 rec->max_ind_blocks *
181 sizeof(blk_t));
182 if (!rec->ind_blocks) {
183 rec->err = ENOMEM;
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 */
Theodore Ts'of3db3561997-04-26 13:34:30 +0000193 ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000194 ext2fs_mark_bb_dirty(fs);
195 group = ext2fs_group_of_blk(fs, *block_nr);
196 fs->group_desc[group].bg_free_blocks_count++;
197 fs->super->s_free_blocks_count++;
198 ext2fs_mark_super_dirty(fs);
199
200 *block_nr = 0;
201 return BLOCK_CHANGED;
202}
203
204
205/*
206 * Helper function for update_bb_inode()
207 *
208 * Set the block list in the bad block inode, using the supplied bitmap.
209 */
210static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000211 int blockcnt, blk_t ref_block,
212 int ref_offset, void *private)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000213{
214 struct set_badblock_record *rec = (struct set_badblock_record *)
215 private;
216 errcode_t retval;
217 blk_t blk;
218 int group;
219
220 if (blockcnt >= 0) {
221 /*
222 * Get the next bad block.
223 */
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000224 if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000225 return BLOCK_ABORT;
226 rec->bad_block_count++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000227 } else {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000228 /*
229 * An indirect block; fetch a block from the
Theodore Ts'of3db3561997-04-26 13:34:30 +0000230 * previously used indirect block list. The block
231 * most be not marked as used; if so, get another one.
232 * If we run out of reserved indirect blocks, allocate
233 * a new one.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000234 */
Theodore Ts'of3db3561997-04-26 13:34:30 +0000235 retry:
236 if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
237 blk = rec->ind_blocks[rec->ind_blocks_ptr++];
238 if (ext2fs_test_block_bitmap(fs->block_map, blk))
239 goto retry;
240 } else {
241 retval = ext2fs_new_block(fs, 0, 0, &blk);
242 if (retval) {
243 rec->err = retval;
244 return BLOCK_ABORT;
245 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000246 }
247 retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
248 if (retval) {
249 rec->err = retval;
250 return BLOCK_ABORT;
251 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000252 ext2fs_mark_block_bitmap(fs->block_map, blk);
253 ext2fs_mark_bb_dirty(fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000254 }
255
256 /*
Theodore Ts'of3db3561997-04-26 13:34:30 +0000257 * Update block counts
Theodore Ts'o3839e651997-04-26 13:21:57 +0000258 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000259 group = ext2fs_group_of_blk(fs, blk);
260 fs->group_desc[group].bg_free_blocks_count--;
261 fs->super->s_free_blocks_count--;
262 ext2fs_mark_super_dirty(fs);
263
264 *block_nr = blk;
265 return BLOCK_CHANGED;
266}
267
268
269
270
271
272