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