bb_inode.c revision 9f8046fc6dfc13eee2f5c363214e60b533872cac
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 Public
12 * License.
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	blk_t				blk;
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				(void **) &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, (void **) &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		while (ext2fs_badblocks_list_iterate(rec.bb_iter, &blk)) {
109			ext2fs_mark_block_bitmap(fs->block_map, blk);
110		}
111		ext2fs_badblocks_list_iterate_end(rec.bb_iter);
112		ext2fs_mark_bb_dirty(fs);
113
114		retval = ext2fs_badblocks_list_iterate_begin(bb_list,
115							     &rec.bb_iter);
116		if (retval)
117			goto cleanup;
118		retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
119					       BLOCK_FLAG_APPEND, 0,
120					       set_bad_block_proc, &rec);
121		ext2fs_badblocks_list_iterate_end(rec.bb_iter);
122		if (retval)
123			goto cleanup;
124		if (rec.err) {
125			retval = rec.err;
126			goto cleanup;
127		}
128	}
129
130	/*
131	 * Update the bad block inode's mod time and block count
132	 * field.
133	 */
134	retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
135	if (retval)
136		goto cleanup;
137
138	inode.i_atime = inode.i_mtime = time(0);
139	if (!inode.i_ctime)
140		inode.i_ctime = time(0);
141	inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
142	inode.i_size = rec.bad_block_count * fs->blocksize;
143
144	retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
145	if (retval)
146		goto cleanup;
147
148cleanup:
149	ext2fs_free_mem((void **) &rec.ind_blocks);
150	ext2fs_free_mem((void **) &rec.block_buf);
151	return retval;
152}
153
154/*
155 * Helper function for update_bb_inode()
156 *
157 * Clear the bad blocks in the bad block inode, while saving the
158 * indirect blocks.
159 */
160#ifdef __TURBOC__
161 #pragma argsused
162#endif
163static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
164				e2_blkcnt_t blockcnt,
165				blk_t ref_block, int ref_offset,
166				void *priv_data)
167{
168	struct set_badblock_record *rec = (struct set_badblock_record *)
169		priv_data;
170	errcode_t	retval;
171	int		group;
172	unsigned long 	old_size;
173
174	if (!*block_nr)
175		return 0;
176
177	/*
178	 * If the block number is outrageous, clear it and ignore it.
179	 */
180	if (*block_nr >= fs->super->s_blocks_count ||
181	    *block_nr < fs->super->s_first_data_block) {
182		*block_nr = 0;
183		return BLOCK_CHANGED;
184	}
185
186	if (blockcnt < 0) {
187		if (rec->ind_blocks_size >= rec->max_ind_blocks) {
188			old_size = rec->max_ind_blocks * sizeof(blk_t);
189			rec->max_ind_blocks += 10;
190			retval = ext2fs_resize_mem(old_size,
191				   rec->max_ind_blocks * sizeof(blk_t),
192				   (void **) &rec->ind_blocks);
193			if (retval) {
194				rec->max_ind_blocks -= 10;
195				rec->err = retval;
196				return BLOCK_ABORT;
197			}
198		}
199		rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
200	}
201
202	/*
203	 * Mark the block as unused, and update accounting information
204	 */
205	ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
206	ext2fs_mark_bb_dirty(fs);
207	group = ext2fs_group_of_blk(fs, *block_nr);
208	fs->group_desc[group].bg_free_blocks_count++;
209	fs->super->s_free_blocks_count++;
210	ext2fs_mark_super_dirty(fs);
211
212	*block_nr = 0;
213	return BLOCK_CHANGED;
214}
215
216
217/*
218 * Helper function for update_bb_inode()
219 *
220 * Set the block list in the bad block inode, using the supplied bitmap.
221 */
222#ifdef __TURBOC__
223 #pragma argsused
224#endif
225static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
226			      e2_blkcnt_t blockcnt, blk_t ref_block,
227			      int ref_offset, void *priv_data)
228{
229	struct set_badblock_record *rec = (struct set_badblock_record *)
230		priv_data;
231	errcode_t	retval;
232	blk_t		blk;
233	int		group;
234
235	if (blockcnt >= 0) {
236		/*
237		 * Get the next bad block.
238		 */
239		if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
240			return BLOCK_ABORT;
241		rec->bad_block_count++;
242	} else {
243		/*
244		 * An indirect block; fetch a block from the
245		 * previously used indirect block list.  The block
246		 * most be not marked as used; if so, get another one.
247		 * If we run out of reserved indirect blocks, allocate
248		 * a new one.
249		 */
250	retry:
251		if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
252			blk = rec->ind_blocks[rec->ind_blocks_ptr++];
253			if (ext2fs_test_block_bitmap(fs->block_map, blk))
254				goto retry;
255		} else {
256			retval = ext2fs_new_block(fs, 0, 0, &blk);
257			if (retval) {
258				rec->err = retval;
259				return BLOCK_ABORT;
260			}
261		}
262		retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
263		if (retval) {
264			rec->err = retval;
265			return BLOCK_ABORT;
266		}
267		ext2fs_mark_block_bitmap(fs->block_map, blk);
268		ext2fs_mark_bb_dirty(fs);
269	}
270
271	/*
272	 * Update block counts
273	 */
274	group = ext2fs_group_of_blk(fs, blk);
275	fs->group_desc[group].bg_free_blocks_count--;
276	fs->super->s_free_blocks_count--;
277	ext2fs_mark_super_dirty(fs);
278
279	*block_nr = blk;
280	return BLOCK_CHANGED;
281}
282
283
284
285
286
287
288