Whamcloud - gitweb
Many files:
[tools/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 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 <stdlib.h>
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 #if HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33
34 #include <linux/ext2_fs.h>
35
36 #include "ext2fs.h"
37
38 struct set_badblock_record {
39         ext2_badblocks_iterate  bb_iter;
40         int             bad_block_count;
41         blk_t           *ind_blocks;
42         int             max_ind_blocks;
43         int             ind_blocks_size;
44         int             ind_blocks_ptr;
45         char            *block_buf;
46         errcode_t       err;
47 };
48
49 static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
50                               blk_t ref_block, int ref_offset, void *private);
51 static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
52                                 blk_t ref_block, int ref_offset,
53                                 void *private);
54         
55 /*
56  * Given a bad blocks bitmap, update the bad blocks inode to reflect
57  * the map.
58  */
59 errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
60 {
61         errcode_t                       retval;
62         struct set_badblock_record      rec;
63         struct ext2_inode               inode;
64         blk_t                           blk;
65         
66         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
67
68         if (!fs->block_map)
69                 return EXT2_ET_NO_BLOCK_BITMAP;
70         
71         rec.bad_block_count = 0;
72         rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
73         rec.max_ind_blocks = 10;
74         rec.ind_blocks = malloc(rec.max_ind_blocks * sizeof(blk_t));
75         if (!rec.ind_blocks)
76                 return ENOMEM;
77         memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
78         rec.block_buf = malloc(fs->blocksize);
79         if (!rec.block_buf) {
80                 retval = ENOMEM;
81                 goto cleanup;
82         }
83         memset(rec.block_buf, 0, fs->blocksize);
84         rec.err = 0;
85         
86         /*
87          * First clear the old bad blocks (while saving the indirect blocks) 
88          */
89         retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
90                                        BLOCK_FLAG_DEPTH_TRAVERSE, 0,
91                                        clear_bad_block_proc, &rec);
92         if (retval)
93                 goto cleanup;
94         if (rec.err) {
95                 retval = rec.err;
96                 goto cleanup;
97         }
98         
99         /*
100          * Now set the bad blocks!
101          *
102          * First, mark the bad blocks as used.  This prevents a bad
103          * block from being used as an indirecto block for the bad
104          * block inode (!).
105          */
106         if (bb_list) {
107                 retval = ext2fs_badblocks_list_iterate_begin(bb_list,
108                                                              &rec.bb_iter);
109                 if (retval)
110                         goto cleanup;
111                 while (ext2fs_badblocks_list_iterate(rec.bb_iter, &blk)) {
112                         ext2fs_mark_block_bitmap(fs->block_map, blk); 
113                 }
114                 ext2fs_badblocks_list_iterate_end(rec.bb_iter);
115                 ext2fs_mark_bb_dirty(fs);
116                 
117                 retval = ext2fs_badblocks_list_iterate_begin(bb_list,
118                                                              &rec.bb_iter);
119                 if (retval)
120                         goto cleanup;
121                 retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
122                                                BLOCK_FLAG_APPEND, 0,
123                                                set_bad_block_proc, &rec);
124                 ext2fs_badblocks_list_iterate_end(rec.bb_iter);
125                 if (retval) 
126                         goto cleanup;
127                 if (rec.err) {
128                         retval = rec.err;
129                         goto cleanup;
130                 }
131         }
132         
133         /*
134          * Update the bad block inode's mod time and block count
135          * field.  
136          */
137         retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
138         if (retval)
139                 goto cleanup;
140         
141         inode.i_atime = inode.i_mtime = time(0);
142         if (!inode.i_ctime)
143                 inode.i_ctime = time(0);
144         inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
145         inode.i_size = rec.bad_block_count * fs->blocksize;
146
147         retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
148         if (retval)
149                 goto cleanup;
150         
151 cleanup:
152         free(rec.ind_blocks);
153         free(rec.block_buf);
154         return retval;
155 }
156
157 /*
158  * Helper function for update_bb_inode()
159  *
160  * Clear the bad blocks in the bad block inode, while saving the
161  * indirect blocks.
162  */
163 #ifdef __TURBOC__
164 #pragma argsused
165 #endif
166 static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
167                                 blk_t ref_block, int ref_offset, void *private)
168 {
169         struct set_badblock_record *rec = (struct set_badblock_record *)
170                 private;
171         int     group;
172
173         if (!*block_nr)
174                 return 0;
175
176         /*
177          * If the block number is outrageous, clear it and ignore it.
178          */
179         if (*block_nr >= fs->super->s_blocks_count ||
180             *block_nr < fs->super->s_first_data_block) {
181                 *block_nr = 0;
182                 return BLOCK_CHANGED;
183         }
184
185         if (blockcnt < 0) {
186                 if (rec->ind_blocks_size >= rec->max_ind_blocks) {
187                         rec->max_ind_blocks += 10;
188                         rec->ind_blocks = realloc(rec->ind_blocks,
189                                                   rec->max_ind_blocks *
190                                                   sizeof(blk_t));
191                         if (!rec->ind_blocks) {
192                                 rec->err = ENOMEM;
193                                 return BLOCK_ABORT;
194                         }
195                 }
196                 rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
197         }
198
199         /*
200          * Mark the block as unused, and update accounting information
201          */
202         ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
203         ext2fs_mark_bb_dirty(fs);
204         group = ext2fs_group_of_blk(fs, *block_nr);
205         fs->group_desc[group].bg_free_blocks_count++;
206         fs->super->s_free_blocks_count++;
207         ext2fs_mark_super_dirty(fs);
208         
209         *block_nr = 0;
210         return BLOCK_CHANGED;
211 }
212
213         
214 /*
215  * Helper function for update_bb_inode()
216  *
217  * Set the block list in the bad block inode, using the supplied bitmap.
218  */
219 #ifdef __TURBOC__
220 #pragma argsused
221 #endif
222 static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
223                               int blockcnt, blk_t ref_block, 
224                               int ref_offset, void *private)
225 {
226         struct set_badblock_record *rec = (struct set_badblock_record *)
227                 private;
228         errcode_t       retval;
229         blk_t           blk;
230         int             group;
231
232         if (blockcnt >= 0) {
233                 /*
234                  * Get the next bad block.
235                  */
236                 if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
237                         return BLOCK_ABORT;
238                 rec->bad_block_count++;
239         } else {
240                 /*
241                  * An indirect block; fetch a block from the
242                  * previously used indirect block list.  The block
243                  * most be not marked as used; if so, get another one.
244                  * If we run out of reserved indirect blocks, allocate
245                  * a new one.
246                  */
247         retry:
248                 if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
249                         blk = rec->ind_blocks[rec->ind_blocks_ptr++];
250                         if (ext2fs_test_block_bitmap(fs->block_map, blk))
251                                 goto retry;
252                 } else {
253                         retval = ext2fs_new_block(fs, 0, 0, &blk);
254                         if (retval) {
255                                 rec->err = retval;
256                                 return BLOCK_ABORT;
257                         }
258                 }
259                 retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
260                 if (retval) {
261                         rec->err = retval;
262                         return BLOCK_ABORT;
263                 }
264                 ext2fs_mark_block_bitmap(fs->block_map, blk); 
265                 ext2fs_mark_bb_dirty(fs);
266         }
267         
268         /*
269          * Update block counts
270          */
271         group = ext2fs_group_of_blk(fs, blk);
272         fs->group_desc[group].bg_free_blocks_count--;
273         fs->super->s_free_blocks_count--;
274         ext2fs_mark_super_dirty(fs);
275         
276         *block_nr = blk;
277         return BLOCK_CHANGED;
278 }
279
280
281
282
283
284