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 Theodore Ts'o.  This file may be redistributed
9  * under the terms of the GNU Public License.
10  */
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <fcntl.h>
17 #include <time.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20
21 #include <linux/fs.h>
22 #include <linux/ext2_fs.h>
23
24 #include "ext2fs.h"
25
26 struct set_badblock_record {
27         badblocks_iterate       bb_iter;
28         int             bad_block_count;
29         blk_t           *ind_blocks;
30         int             max_ind_blocks;
31         int             ind_blocks_size;
32         int             ind_blocks_ptr;
33         char            *block_buf;
34         errcode_t       err;
35 };
36
37 static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
38                               void *private);
39 static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
40                              void *private);
41         
42 /*
43  * Given a bad blocks bitmap, update the bad blocks inode to reflect
44  * the map.
45  */
46 errcode_t ext2fs_update_bb_inode(ext2_filsys fs, badblocks_list bb_list)
47 {
48         errcode_t                       retval;
49         struct set_badblock_record      rec;
50         struct ext2_inode               inode;
51         
52         if (!fs->block_map)
53                 return EXT2_ET_NO_BLOCK_BITMAP;
54         
55         rec.bad_block_count = 0;
56         rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
57         rec.max_ind_blocks = 10;
58         rec.ind_blocks = malloc(rec.max_ind_blocks * sizeof(blk_t));
59         if (!rec.ind_blocks)
60                 return ENOMEM;
61         memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
62         rec.block_buf = malloc(fs->blocksize);
63         if (!rec.block_buf) {
64                 retval = ENOMEM;
65                 goto cleanup;
66         }
67         memset(rec.block_buf, 0, fs->blocksize);
68         rec.err = 0;
69         
70         /*
71          * First clear the old bad blocks (while saving the indirect blocks) 
72          */
73         retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, 0, 0,
74                                       clear_bad_block_proc, &rec);
75         if (retval)
76                 goto cleanup;
77         if (rec.err) {
78                 retval = rec.err;
79                 goto cleanup;
80         }
81         
82         /*
83          * Now set the bad blocks!
84          */
85         if (bb_list) {
86                 retval = badblocks_list_iterate_begin(bb_list, &rec.bb_iter);
87                 if (retval)
88                         goto cleanup;
89                 retval = ext2fs_block_iterate(fs, EXT2_BAD_INO,
90                                               BLOCK_FLAG_APPEND, 0,
91                                               set_bad_block_proc, &rec);
92                 badblocks_list_iterate_end(rec.bb_iter);
93                 if (retval) 
94                         goto cleanup;
95                 if (rec.err) {
96                         retval = rec.err;
97                         goto cleanup;
98                 }
99         }
100         
101         /*
102          * Update the bad block inode's mod time and block count
103          * field.  
104          */
105         retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
106         if (retval)
107                 goto cleanup;
108         
109         inode.i_atime = inode.i_mtime = time(0);
110         if (!inode.i_ctime)
111                 inode.i_ctime = time(0);
112         inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
113         inode.i_size = rec.bad_block_count * fs->blocksize;
114
115         retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
116         if (retval)
117                 goto cleanup;
118         
119 cleanup:
120         free(rec.ind_blocks);
121         free(rec.block_buf);
122         return retval;
123 }
124
125 /*
126  * Helper function for update_bb_inode()
127  *
128  * Clear the bad blocks in the bad block inode, while saving the
129  * indirect blocks.
130  */
131 static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, int blockcnt,
132                                 void *private)
133 {
134         struct set_badblock_record *rec = (struct set_badblock_record *)
135                 private;
136         int     group;
137
138         if (!*block_nr)
139                 return 0;
140
141         if (blockcnt < 0) {
142                 if (rec->ind_blocks_size >= rec->max_ind_blocks) {
143                         rec->max_ind_blocks += 10;
144                         rec->ind_blocks = realloc(rec->ind_blocks,
145                                                   rec->max_ind_blocks *
146                                                   sizeof(blk_t));
147                         if (!rec->ind_blocks) {
148                                 rec->err = ENOMEM;
149                                 return BLOCK_ABORT;
150                         }
151                 }
152                 rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
153         }
154
155         /*
156          * Mark the block as unused, and update accounting information
157          */
158         ext2fs_unmark_block_bitmap(fs, fs->block_map, *block_nr);
159         ext2fs_mark_bb_dirty(fs);
160         group = ext2fs_group_of_blk(fs, *block_nr);
161         fs->group_desc[group].bg_free_blocks_count++;
162         fs->super->s_free_blocks_count++;
163         ext2fs_mark_super_dirty(fs);
164         
165         *block_nr = 0;
166         return BLOCK_CHANGED;
167 }
168
169         
170 /*
171  * Helper function for update_bb_inode()
172  *
173  * Set the block list in the bad block inode, using the supplied bitmap.
174  */
175 static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
176                          int blockcnt, void *private)
177 {
178         struct set_badblock_record *rec = (struct set_badblock_record *)
179                 private;
180         errcode_t       retval;
181         blk_t           blk;
182         int             group;
183
184         if (blockcnt >= 0) {
185                 /*
186                  * Get the next bad block.
187                  */
188                 if (!badblocks_list_iterate(rec->bb_iter, &blk))
189                         return BLOCK_ABORT;
190                 rec->bad_block_count++;
191         } else if (rec->ind_blocks_ptr < rec->ind_blocks_size)
192                 /*
193                  * An indirect block; fetch a block from the
194                  * previously used indirect block list.
195                  */
196                 blk = rec->ind_blocks[rec->ind_blocks_ptr++];
197         else {
198                 /*
199                  * An indirect block, and we're out of reserved
200                  * indirect blocks.  Allocate a new one.
201                  */
202                 retval = ext2fs_new_block(fs, 0, 0, &blk);
203                 if (retval) {
204                         rec->err = retval;
205                         return BLOCK_ABORT;
206                 }
207                 retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
208                 if (retval) {
209                         rec->err = retval;
210                         return BLOCK_ABORT;
211                 }
212         }
213         
214         /*
215          * Mark the block as used, and update block counts
216          */
217         ext2fs_mark_block_bitmap(fs, fs->block_map, blk); 
218         ext2fs_mark_bb_dirty(fs);
219         group = ext2fs_group_of_blk(fs, blk);
220         fs->group_desc[group].bg_free_blocks_count--;
221         fs->super->s_free_blocks_count--;
222         ext2fs_mark_super_dirty(fs);
223         
224         *block_nr = blk;
225         return BLOCK_CHANGED;
226 }
227
228
229
230
231
232