Whamcloud - gitweb
e2fsprogs: remove checker infrastructure
[tools/e2fsprogs.git] / lib / ext2fs / alloc.c
1 /*
2  * alloc.c --- allocate new inodes, blocks for ext2fs
3  *
4  * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Library
8  * General Public License, version 2.
9  * %End-Header%
10  */
11
12 #include "config.h"
13 #include <stdio.h>
14 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <time.h>
18 #include <string.h>
19 #if HAVE_SYS_STAT_H
20 #include <sys/stat.h>
21 #endif
22 #if HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25
26 #include "ext2_fs.h"
27 #include "ext2fs.h"
28
29 /*
30  * Clear the uninit block bitmap flag if necessary
31  */
32 static void clear_block_uninit(ext2_filsys fs, dgrp_t group)
33 {
34         if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
35                                          EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
36             !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
37                 return;
38
39         /* uninit block bitmaps are now initialized in read_bitmaps() */
40
41         ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
42         ext2fs_group_desc_csum_set(fs, group);
43         ext2fs_mark_super_dirty(fs);
44         ext2fs_mark_bb_dirty(fs);
45 }
46
47 /*
48  * Check for uninit inode bitmaps and deal with them appropriately
49  */
50 static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map,
51                           dgrp_t group)
52 {
53         ext2_ino_t      i, ino;
54
55         if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
56                                          EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
57             !(ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)))
58                 return;
59
60         ino = (group * fs->super->s_inodes_per_group) + 1;
61         for (i=0; i < fs->super->s_inodes_per_group; i++, ino++)
62                 ext2fs_fast_unmark_inode_bitmap2(map, ino);
63
64         ext2fs_bg_flags_clear(fs, group, EXT2_BG_INODE_UNINIT);
65         /* Mimics what the kernel does */
66         ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
67         ext2fs_group_desc_csum_set(fs, group);
68         ext2fs_mark_ib_dirty(fs);
69         ext2fs_mark_super_dirty(fs);
70 }
71
72 /*
73  * Right now, just search forward from the parent directory's block
74  * group to find the next free inode.
75  *
76  * Should have a special policy for directories.
77  */
78 errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir,
79                            int mode EXT2FS_ATTR((unused)),
80                            ext2fs_inode_bitmap map, ext2_ino_t *ret)
81 {
82         ext2_ino_t      start_inode = 0;
83         ext2_ino_t      i, ino_in_group, upto, first_zero;
84         errcode_t       retval;
85         dgrp_t          group;
86
87         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
88
89         if (!map)
90                 map = fs->inode_map;
91         if (!map)
92                 return EXT2_ET_NO_INODE_BITMAP;
93
94         if (dir > 0) {
95                 group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
96                 start_inode = (group * EXT2_INODES_PER_GROUP(fs->super)) + 1;
97         }
98         if (start_inode < EXT2_FIRST_INODE(fs->super))
99                 start_inode = EXT2_FIRST_INODE(fs->super);
100         if (start_inode > fs->super->s_inodes_count)
101                 return EXT2_ET_INODE_ALLOC_FAIL;
102         i = start_inode;
103         do {
104                 ino_in_group = (i - 1) % EXT2_INODES_PER_GROUP(fs->super);
105                 group = (i - 1) / EXT2_INODES_PER_GROUP(fs->super);
106
107                 check_inode_uninit(fs, map, group);
108                 upto = i + (EXT2_INODES_PER_GROUP(fs->super) - ino_in_group);
109                 if (i < start_inode && upto >= start_inode)
110                         upto = start_inode - 1;
111                 if (upto > fs->super->s_inodes_count)
112                         upto = fs->super->s_inodes_count;
113
114                 retval = ext2fs_find_first_zero_inode_bitmap2(map, i, upto,
115                                                               &first_zero);
116                 if (retval == 0) {
117                         i = first_zero;
118                         break;
119                 }
120                 if (retval != ENOENT)
121                         return EXT2_ET_INODE_ALLOC_FAIL;
122                 i = upto + 1;
123                 if (i > fs->super->s_inodes_count)
124                         i = EXT2_FIRST_INODE(fs->super);
125         } while (i != start_inode);
126
127         if (ext2fs_test_inode_bitmap2(map, i))
128                 return EXT2_ET_INODE_ALLOC_FAIL;
129         *ret = i;
130         return 0;
131 }
132
133 /*
134  * Stupid algorithm --- we now just search forward starting from the
135  * goal.  Should put in a smarter one someday....
136  */
137 errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal,
138                            ext2fs_block_bitmap map, blk64_t *ret)
139 {
140         errcode_t retval;
141         blk64_t b = 0;
142
143         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
144
145         if (!map)
146                 map = fs->block_map;
147         if (!map)
148                 return EXT2_ET_NO_BLOCK_BITMAP;
149         if (!goal || (goal >= ext2fs_blocks_count(fs->super)))
150                 goal = fs->super->s_first_data_block;
151         goal &= ~EXT2FS_CLUSTER_MASK(fs);
152
153         retval = ext2fs_find_first_zero_block_bitmap2(map,
154                         goal, ext2fs_blocks_count(fs->super) - 1, &b);
155         if ((retval == ENOENT) && (goal != fs->super->s_first_data_block))
156                 retval = ext2fs_find_first_zero_block_bitmap2(map,
157                         fs->super->s_first_data_block, goal - 1, &b);
158         if (retval == ENOENT)
159                 return EXT2_ET_BLOCK_ALLOC_FAIL;
160         if (retval)
161                 return retval;
162
163         clear_block_uninit(fs, ext2fs_group_of_blk2(fs, b));
164         *ret = b;
165         return 0;
166 }
167
168 errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
169                            ext2fs_block_bitmap map, blk_t *ret)
170 {
171         errcode_t retval;
172         blk64_t val;
173         retval = ext2fs_new_block2(fs, goal, map, &val);
174         if (!retval)
175                 *ret = (blk_t) val;
176         return retval;
177 }
178
179 /*
180  * This function zeros out the allocated block, and updates all of the
181  * appropriate filesystem records.
182  */
183 errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal,
184                              char *block_buf, blk64_t *ret)
185 {
186         errcode_t       retval;
187         blk64_t         block;
188         char            *buf = 0;
189
190         if (!block_buf) {
191                 retval = ext2fs_get_mem(fs->blocksize, &buf);
192                 if (retval)
193                         return retval;
194                 block_buf = buf;
195         }
196         memset(block_buf, 0, fs->blocksize);
197
198         if (fs->get_alloc_block) {
199                 retval = (fs->get_alloc_block)(fs, goal, &block);
200                 if (retval)
201                         goto fail;
202         } else {
203                 if (!fs->block_map) {
204                         retval = ext2fs_read_block_bitmap(fs);
205                         if (retval)
206                                 goto fail;
207                 }
208
209                 retval = ext2fs_new_block2(fs, goal, 0, &block);
210                 if (retval)
211                         goto fail;
212         }
213
214         retval = io_channel_write_blk64(fs->io, block, 1, block_buf);
215         if (retval)
216                 goto fail;
217
218         ext2fs_block_alloc_stats2(fs, block, +1);
219         *ret = block;
220
221 fail:
222         if (buf)
223                 ext2fs_free_mem(&buf);
224         return retval;
225 }
226
227 errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
228                              char *block_buf, blk_t *ret)
229 {
230         errcode_t retval;
231         blk64_t val;
232         retval = ext2fs_alloc_block2(fs, goal, block_buf, &val);
233         if (!retval)
234                 *ret = (blk_t) val;
235         return retval;
236 }
237
238 errcode_t ext2fs_get_free_blocks2(ext2_filsys fs, blk64_t start, blk64_t finish,
239                                  int num, ext2fs_block_bitmap map, blk64_t *ret)
240 {
241         blk64_t b = start;
242         int     c_ratio;
243
244         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
245
246         if (!map)
247                 map = fs->block_map;
248         if (!map)
249                 return EXT2_ET_NO_BLOCK_BITMAP;
250         if (!b)
251                 b = fs->super->s_first_data_block;
252         if (!finish)
253                 finish = start;
254         if (!num)
255                 num = 1;
256         c_ratio = 1 << ext2fs_get_bitmap_granularity(map);
257         b &= ~(c_ratio - 1);
258         finish &= ~(c_ratio -1);
259         do {
260                 if (b+num-1 > ext2fs_blocks_count(fs->super))
261                         b = fs->super->s_first_data_block;
262                 if (ext2fs_fast_test_block_bitmap_range2(map, b, num)) {
263                         *ret = b;
264                         return 0;
265                 }
266                 b += c_ratio;
267         } while (b != finish);
268         return EXT2_ET_BLOCK_ALLOC_FAIL;
269 }
270
271 errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
272                                  int num, ext2fs_block_bitmap map, blk_t *ret)
273 {
274         errcode_t retval;
275         blk64_t val;
276         retval = ext2fs_get_free_blocks2(fs, start, finish, num, map, &val);
277         if(!retval)
278                 *ret = (blk_t) val;
279         return retval;
280 }
281
282 void ext2fs_set_alloc_block_callback(ext2_filsys fs,
283                                      errcode_t (*func)(ext2_filsys fs,
284                                                        blk64_t goal,
285                                                        blk64_t *ret),
286                                      errcode_t (**old)(ext2_filsys fs,
287                                                        blk64_t goal,
288                                                        blk64_t *ret))
289 {
290         if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)
291                 return;
292
293         if (old)
294                 *old = fs->get_alloc_block;
295
296         fs->get_alloc_block = func;
297 }