]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/ext2fs/bb_inode.c
Merge remote-tracking branch 'josch/libarchive' into josch-libarchive
[thirdparty/e2fsprogs.git] / lib / ext2fs / bb_inode.c
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 "config.h"
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
34 struct 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
45 static 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);
49 static 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 */
58 errcode_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 time_t now;
64
65 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
66
67 if (!fs->block_map)
68 return EXT2_ET_NO_BLOCK_BITMAP;
69
70 memset(&rec, 0, sizeof(rec));
71 rec.max_ind_blocks = 10;
72 retval = ext2fs_get_array(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 indirect 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 now = fs->now ? fs->now : time(0);
129 ext2fs_inode_xtime_set(&inode, i_atime, now);
130 if (!ext2fs_inode_xtime_get(&inode, i_ctime))
131 ext2fs_inode_xtime_set(&inode, i_ctime, now);
132 ext2fs_inode_xtime_set(&inode, i_mtime, now);
133 ext2fs_iblk_set(fs, &inode, rec.bad_block_count);
134 retval = ext2fs_inode_size_set(fs, &inode,
135 rec.bad_block_count * fs->blocksize);
136 if (retval)
137 goto cleanup;
138
139 retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
140 if (retval)
141 goto cleanup;
142
143 cleanup:
144 ext2fs_free_mem(&rec.ind_blocks);
145 ext2fs_free_mem(&rec.block_buf);
146 return retval;
147 }
148
149 /*
150 * Helper function for update_bb_inode()
151 *
152 * Clear the bad blocks in the bad block inode, while saving the
153 * indirect blocks.
154 */
155 #ifdef __TURBOC__
156 #pragma argsused
157 #endif
158 static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
159 e2_blkcnt_t blockcnt,
160 blk_t ref_block EXT2FS_ATTR((unused)),
161 int ref_offset EXT2FS_ATTR((unused)),
162 void *priv_data)
163 {
164 struct set_badblock_record *rec = (struct set_badblock_record *)
165 priv_data;
166 errcode_t retval;
167 unsigned long old_size;
168
169 if (!*block_nr)
170 return 0;
171
172 /*
173 * If the block number is outrageous, clear it and ignore it.
174 */
175 if (*block_nr >= ext2fs_blocks_count(fs->super) ||
176 *block_nr < fs->super->s_first_data_block) {
177 *block_nr = 0;
178 return BLOCK_CHANGED;
179 }
180
181 if (blockcnt < 0) {
182 if (rec->ind_blocks_size >= rec->max_ind_blocks) {
183 old_size = rec->max_ind_blocks * sizeof(blk_t);
184 rec->max_ind_blocks += 10;
185 retval = ext2fs_resize_mem(old_size,
186 rec->max_ind_blocks * sizeof(blk_t),
187 &rec->ind_blocks);
188 if (retval) {
189 rec->max_ind_blocks -= 10;
190 rec->err = retval;
191 return BLOCK_ABORT;
192 }
193 }
194 rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
195 }
196
197 /*
198 * Mark the block as unused, and update accounting information
199 */
200 ext2fs_block_alloc_stats2(fs, *block_nr, -1);
201
202 *block_nr = 0;
203 return BLOCK_CHANGED;
204 }
205
206
207 /*
208 * Helper function for update_bb_inode()
209 *
210 * Set the block list in the bad block inode, using the supplied bitmap.
211 */
212 #ifdef __TURBOC__
213 #pragma argsused
214 #endif
215 static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
216 e2_blkcnt_t blockcnt,
217 blk_t ref_block EXT2FS_ATTR((unused)),
218 int ref_offset EXT2FS_ATTR((unused)),
219 void *priv_data)
220 {
221 struct set_badblock_record *rec = (struct set_badblock_record *)
222 priv_data;
223 errcode_t retval;
224 blk_t blk;
225
226 if (blockcnt >= 0) {
227 /*
228 * Get the next bad block.
229 */
230 if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
231 return BLOCK_ABORT;
232 rec->bad_block_count++;
233 } else {
234 /*
235 * An indirect block; fetch a block from the
236 * previously used indirect block list. The block
237 * most be not marked as used; if so, get another one.
238 * If we run out of reserved indirect blocks, allocate
239 * a new one.
240 */
241 retry:
242 if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
243 blk = rec->ind_blocks[rec->ind_blocks_ptr++];
244 if (ext2fs_test_block_bitmap2(fs->block_map, blk))
245 goto retry;
246 } else {
247 retval = ext2fs_new_block(fs, 0, 0, &blk);
248 if (retval) {
249 rec->err = retval;
250 return BLOCK_ABORT;
251 }
252 }
253 retval = io_channel_write_blk64(fs->io, blk, 1, rec->block_buf);
254 if (retval) {
255 rec->err = retval;
256 return BLOCK_ABORT;
257 }
258 }
259
260 /*
261 * Update block counts
262 */
263 ext2fs_block_alloc_stats2(fs, blk, +1);
264
265 *block_nr = blk;
266 return BLOCK_CHANGED;
267 }
268
269
270
271
272
273