Home | History | Annotate | Download | only in ext2fs
      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 Public
      8  * License.
      9  * %End-Header%
     10  *
     11  */
     12 
     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  * Check for uninit block bitmaps and deal with them appropriately
     31  */
     32 static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map,
     33 			  dgrp_t group)
     34 {
     35 	blk_t		i;
     36 	blk_t		blk, super_blk, old_desc_blk, new_desc_blk;
     37 	int		old_desc_blocks;
     38 
     39 	if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
     40 					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
     41 	    !(fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT))
     42 		return;
     43 
     44 	blk = (group * fs->super->s_blocks_per_group) +
     45 		fs->super->s_first_data_block;
     46 
     47 	ext2fs_super_and_bgd_loc(fs, group, &super_blk,
     48 				 &old_desc_blk, &new_desc_blk, 0);
     49 
     50 	if (fs->super->s_feature_incompat &
     51 	    EXT2_FEATURE_INCOMPAT_META_BG)
     52 		old_desc_blocks = fs->super->s_first_meta_bg;
     53 	else
     54 		old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
     55 
     56 	for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) {
     57 		if ((blk == super_blk) ||
     58 		    (old_desc_blk && old_desc_blocks &&
     59 		     (blk >= old_desc_blk) &&
     60 		     (blk < old_desc_blk + old_desc_blocks)) ||
     61 		    (new_desc_blk && (blk == new_desc_blk)) ||
     62 		    (blk == fs->group_desc[group].bg_block_bitmap) ||
     63 		    (blk == fs->group_desc[group].bg_inode_bitmap) ||
     64 		    (blk >= fs->group_desc[group].bg_inode_table &&
     65 		     (blk < fs->group_desc[group].bg_inode_table
     66 		      + fs->inode_blocks_per_group)))
     67 			ext2fs_fast_mark_block_bitmap(map, blk);
     68 		else
     69 			ext2fs_fast_unmark_block_bitmap(map, blk);
     70 	}
     71 	fs->group_desc[group].bg_flags &= ~EXT2_BG_BLOCK_UNINIT;
     72 	ext2fs_group_desc_csum_set(fs, group);
     73 }
     74 
     75 /*
     76  * Check for uninit inode bitmaps and deal with them appropriately
     77  */
     78 static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map,
     79 			  dgrp_t group)
     80 {
     81 	ext2_ino_t	i, ino;
     82 
     83 	if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
     84 					 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) ||
     85 	    !(fs->group_desc[group].bg_flags & EXT2_BG_INODE_UNINIT))
     86 		return;
     87 
     88 	ino = (group * fs->super->s_inodes_per_group) + 1;
     89 	for (i=0; i < fs->super->s_inodes_per_group; i++, ino++)
     90 		ext2fs_fast_unmark_inode_bitmap(map, ino);
     91 
     92 	fs->group_desc[group].bg_flags &= ~EXT2_BG_INODE_UNINIT;
     93 	check_block_uninit(fs, fs->block_map, group);
     94 }
     95 
     96 /*
     97  * Right now, just search forward from the parent directory's block
     98  * group to find the next free inode.
     99  *
    100  * Should have a special policy for directories.
    101  */
    102 errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir,
    103 			   int mode EXT2FS_ATTR((unused)),
    104 			   ext2fs_inode_bitmap map, ext2_ino_t *ret)
    105 {
    106 	ext2_ino_t	dir_group = 0;
    107 	ext2_ino_t	i;
    108 	ext2_ino_t	start_inode;
    109 
    110 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
    111 
    112 	if (!map)
    113 		map = fs->inode_map;
    114 	if (!map)
    115 		return EXT2_ET_NO_INODE_BITMAP;
    116 
    117 	if (dir > 0)
    118 		dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
    119 
    120 	start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1;
    121 	if (start_inode < EXT2_FIRST_INODE(fs->super))
    122 		start_inode = EXT2_FIRST_INODE(fs->super);
    123 	if (start_inode > fs->super->s_inodes_count)
    124 		return EXT2_ET_INODE_ALLOC_FAIL;
    125 	i = start_inode;
    126 
    127 	do {
    128 		if (((i - 1) % EXT2_INODES_PER_GROUP(fs->super)) == 0)
    129 			check_inode_uninit(fs, map, (i - 1) /
    130 					   EXT2_INODES_PER_GROUP(fs->super));
    131 
    132 		if (!ext2fs_fast_test_inode_bitmap(map, i))
    133 			break;
    134 		i++;
    135 		if (i > fs->super->s_inodes_count)
    136 			i = EXT2_FIRST_INODE(fs->super);
    137 	} while (i != start_inode);
    138 
    139 	if (ext2fs_test_inode_bitmap(map, i))
    140 		return EXT2_ET_INODE_ALLOC_FAIL;
    141 	*ret = i;
    142 	return 0;
    143 }
    144 
    145 /*
    146  * Stupid algorithm --- we now just search forward starting from the
    147  * goal.  Should put in a smarter one someday....
    148  */
    149 errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
    150 			   ext2fs_block_bitmap map, blk_t *ret)
    151 {
    152 	blk_t	i;
    153 
    154 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
    155 
    156 	if (!map)
    157 		map = fs->block_map;
    158 	if (!map)
    159 		return EXT2_ET_NO_BLOCK_BITMAP;
    160 	if (!goal || (goal >= fs->super->s_blocks_count))
    161 		goal = fs->super->s_first_data_block;
    162 	i = goal;
    163 	check_block_uninit(fs, map,
    164 			   (i - fs->super->s_first_data_block) /
    165 			   EXT2_BLOCKS_PER_GROUP(fs->super));
    166 	do {
    167 		if (((i - fs->super->s_first_data_block) %
    168 		     EXT2_BLOCKS_PER_GROUP(fs->super)) == 0)
    169 			check_block_uninit(fs, map,
    170 					   (i - fs->super->s_first_data_block) /
    171 					   EXT2_BLOCKS_PER_GROUP(fs->super));
    172 
    173 		if (!ext2fs_fast_test_block_bitmap(map, i)) {
    174 			*ret = i;
    175 			return 0;
    176 		}
    177 		i++;
    178 		if (i >= fs->super->s_blocks_count)
    179 			i = fs->super->s_first_data_block;
    180 	} while (i != goal);
    181 	return EXT2_ET_BLOCK_ALLOC_FAIL;
    182 }
    183 
    184 /*
    185  * This function zeros out the allocated block, and updates all of the
    186  * appropriate filesystem records.
    187  */
    188 errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
    189 			     char *block_buf, blk_t *ret)
    190 {
    191 	errcode_t	retval;
    192 	blk_t		block;
    193 	char		*buf = 0;
    194 
    195 	if (!block_buf) {
    196 		retval = ext2fs_get_mem(fs->blocksize, &buf);
    197 		if (retval)
    198 			return retval;
    199 		block_buf = buf;
    200 	}
    201 	memset(block_buf, 0, fs->blocksize);
    202 
    203 	if (fs->get_alloc_block) {
    204 		blk64_t	new;
    205 
    206 		retval = (fs->get_alloc_block)(fs, (blk64_t) goal, &new);
    207 		if (retval)
    208 			goto fail;
    209 		block = (blk_t) new;
    210 	} else {
    211 		if (!fs->block_map) {
    212 			retval = ext2fs_read_block_bitmap(fs);
    213 			if (retval)
    214 				goto fail;
    215 		}
    216 
    217 		retval = ext2fs_new_block(fs, goal, 0, &block);
    218 		if (retval)
    219 			goto fail;
    220 	}
    221 
    222 	retval = io_channel_write_blk(fs->io, block, 1, block_buf);
    223 	if (retval)
    224 		goto fail;
    225 
    226 	ext2fs_block_alloc_stats(fs, block, +1);
    227 	*ret = block;
    228 
    229 fail:
    230 	if (buf)
    231 		ext2fs_free_mem(&buf);
    232 	return retval;
    233 }
    234 
    235 errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
    236 				 int num, ext2fs_block_bitmap map, blk_t *ret)
    237 {
    238 	blk_t	b = start;
    239 
    240 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
    241 
    242 	if (!map)
    243 		map = fs->block_map;
    244 	if (!map)
    245 		return EXT2_ET_NO_BLOCK_BITMAP;
    246 	if (!b)
    247 		b = fs->super->s_first_data_block;
    248 	if (!finish)
    249 		finish = start;
    250 	if (!num)
    251 		num = 1;
    252 	do {
    253 		if (b+num-1 > fs->super->s_blocks_count)
    254 			b = fs->super->s_first_data_block;
    255 		if (ext2fs_fast_test_block_bitmap_range(map, b, num)) {
    256 			*ret = b;
    257 			return 0;
    258 		}
    259 		b++;
    260 	} while (b != finish);
    261 	return EXT2_ET_BLOCK_ALLOC_FAIL;
    262 }
    263 
    264 void ext2fs_set_alloc_block_callback(ext2_filsys fs,
    265 				     errcode_t (*func)(ext2_filsys fs,
    266 						       blk64_t goal,
    267 						       blk64_t *ret),
    268 				     errcode_t (**old)(ext2_filsys fs,
    269 						       blk64_t goal,
    270 						       blk64_t *ret))
    271 {
    272 	if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)
    273 		return;
    274 
    275 	if (old)
    276 		*old = fs->get_alloc_block;
    277 
    278 	fs->get_alloc_block = func;
    279 }
    280