6c597561bc
.enable all compile time warnings and make them errors .refactor functions with unused parameters .fix null pointer dereference before checking for null .proper variable initialization .use safe string copy functions .fix massive memory corruption bug in fs_getdents
352 lines
10 KiB
C
352 lines
10 KiB
C
/* This files manages blocks allocation and deallocation.
|
|
*
|
|
* The entry points into this file are:
|
|
* discard_preallocated_blocks: Discard preallocated blocks.
|
|
* alloc_block: somebody wants to allocate a block; find one.
|
|
* free_block: indicate that a block is available for new allocation.
|
|
*
|
|
* Created:
|
|
* June 2010 (Evgeniy Ivanov)
|
|
*/
|
|
|
|
#include "fs.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <minix/com.h>
|
|
#include <minix/u64.h>
|
|
#include "buf.h"
|
|
#include "inode.h"
|
|
#include "super.h"
|
|
#include "const.h"
|
|
|
|
|
|
static block_t alloc_block_bit(struct super_block *sp, block_t origin,
|
|
struct inode *rip);
|
|
|
|
/*===========================================================================*
|
|
* discard_preallocated_blocks *
|
|
*===========================================================================*/
|
|
void discard_preallocated_blocks(struct inode *rip)
|
|
{
|
|
/* When called for rip, discard (free) blocks preallocated for rip,
|
|
* otherwise discard all preallocated blocks.
|
|
* Normally it should be called in following situations:
|
|
* 1. File is closed.
|
|
* 2. File is truncated.
|
|
* 3. Non-sequential write.
|
|
* 4. inode is "unloaded" from the memory.
|
|
* 5. No free blocks left (discard all preallocated blocks).
|
|
*/
|
|
int i;
|
|
|
|
if (rip) {
|
|
rip->i_prealloc_count = rip->i_prealloc_index = 0;
|
|
for (i = 0; i < EXT2_PREALLOC_BLOCKS; i++) {
|
|
if (rip->i_prealloc_blocks[i] != NO_BLOCK) {
|
|
free_block(rip->i_sp, rip->i_prealloc_blocks[i]);
|
|
rip->i_prealloc_blocks[i] = NO_BLOCK;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Discard all allocated blocks.
|
|
* Probably there are just few blocks on the disc, so forbid preallocation.*/
|
|
for(rip = &inode[0]; rip < &inode[NR_INODES]; rip++) {
|
|
rip->i_prealloc_count = rip->i_prealloc_index = 0;
|
|
rip->i_preallocation = 0; /* forbid preallocation */
|
|
for (i = 0; i < EXT2_PREALLOC_BLOCKS; i++) {
|
|
if (rip->i_prealloc_blocks[i] != NO_BLOCK) {
|
|
free_block(rip->i_sp, rip->i_prealloc_blocks[i]);
|
|
rip->i_prealloc_blocks[i] = NO_BLOCK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* alloc_block *
|
|
*===========================================================================*/
|
|
block_t alloc_block(struct inode *rip, block_t block)
|
|
{
|
|
/* Allocate a block for inode. If block is provided, then use it as a goal:
|
|
* try to allocate this block or his neghbors.
|
|
* If block is not provided then goal is group, where inode lives.
|
|
*/
|
|
block_t goal;
|
|
block_t b;
|
|
struct super_block *sp = rip->i_sp;
|
|
|
|
if (sp->s_rd_only)
|
|
panic("can't alloc block on read-only filesys.");
|
|
|
|
/* Check for free blocks. First time discard preallocation,
|
|
* next time return NO_BLOCK
|
|
*/
|
|
if (!opt.use_reserved_blocks &&
|
|
sp->s_free_blocks_count <= sp->s_r_blocks_count) {
|
|
discard_preallocated_blocks(NULL);
|
|
} else if (sp->s_free_blocks_count <= EXT2_PREALLOC_BLOCKS) {
|
|
discard_preallocated_blocks(NULL);
|
|
}
|
|
|
|
if (!opt.use_reserved_blocks &&
|
|
sp->s_free_blocks_count <= sp->s_r_blocks_count) {
|
|
return(NO_BLOCK);
|
|
} else if (sp->s_free_blocks_count == 0) {
|
|
return(NO_BLOCK);
|
|
}
|
|
|
|
if (block != NO_BLOCK) {
|
|
goal = block;
|
|
if (rip->i_preallocation && rip->i_prealloc_count > 0) {
|
|
/* check if goal is preallocated */
|
|
b = rip->i_prealloc_blocks[rip->i_prealloc_index];
|
|
if (block == b || (block + 1) == b) {
|
|
/* use preallocated block */
|
|
rip->i_prealloc_blocks[rip->i_prealloc_index] = NO_BLOCK;
|
|
rip->i_prealloc_count--;
|
|
rip->i_prealloc_index++;
|
|
if (rip->i_prealloc_index >= EXT2_PREALLOC_BLOCKS) {
|
|
rip->i_prealloc_index = 0;
|
|
ASSERT(rip->i_prealloc_count == 0);
|
|
}
|
|
rip->i_bsearch = b;
|
|
return b;
|
|
} else {
|
|
/* probably non-sequential write operation,
|
|
* disable preallocation for this inode.
|
|
*/
|
|
rip->i_preallocation = 0;
|
|
discard_preallocated_blocks(rip);
|
|
}
|
|
}
|
|
} else {
|
|
int group = (rip->i_num - 1) / sp->s_inodes_per_group;
|
|
goal = sp->s_blocks_per_group*group + sp->s_first_data_block;
|
|
}
|
|
|
|
if (rip->i_preallocation && rip->i_prealloc_count) {
|
|
ext2_debug("There're preallocated blocks, but they're\
|
|
neither used or freed!");
|
|
}
|
|
|
|
b = alloc_block_bit(sp, goal, rip);
|
|
|
|
if (b != NO_BLOCK)
|
|
rip->i_bsearch = b;
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
static void check_block_number(block_t block, struct super_block *sp,
|
|
struct group_desc *gd);
|
|
|
|
/*===========================================================================*
|
|
* alloc_block_bit *
|
|
*===========================================================================*/
|
|
static block_t alloc_block_bit(sp, goal, rip)
|
|
struct super_block *sp; /* the filesystem to allocate from */
|
|
block_t goal; /* try to allocate near this block */
|
|
struct inode *rip; /* used for preallocation */
|
|
{
|
|
block_t block = NO_BLOCK; /* allocated block */
|
|
int word; /* word in block bitmap */
|
|
bit_t bit = -1;
|
|
int group;
|
|
char update_bsearch = FALSE;
|
|
int i;
|
|
|
|
if (goal >= sp->s_blocks_count ||
|
|
(goal < sp->s_first_data_block && goal != 0)) {
|
|
goal = sp->s_bsearch;
|
|
}
|
|
|
|
if (goal <= sp->s_bsearch) {
|
|
/* No reason to search in a place with no free blocks */
|
|
goal = sp->s_bsearch;
|
|
update_bsearch = TRUE;
|
|
}
|
|
|
|
/* Figure out where to start the bit search. */
|
|
word = ((goal - sp->s_first_data_block) % sp->s_blocks_per_group)
|
|
/ FS_BITCHUNK_BITS;
|
|
|
|
/* Try to allocate block at any group starting from the goal's group.
|
|
* First time goal's group is checked from the word=goal, after all
|
|
* groups checked, it's checked again from word=0, that's why "i <=".
|
|
*/
|
|
group = (goal - sp->s_first_data_block) / sp->s_blocks_per_group;
|
|
for (i = 0; i <= sp->s_groups_count; i++, group++) {
|
|
struct buf *bp;
|
|
struct group_desc *gd;
|
|
|
|
if (group >= sp->s_groups_count)
|
|
group = 0;
|
|
|
|
gd = get_group_desc(group);
|
|
if (gd == NULL)
|
|
panic("can't get group_desc to alloc block");
|
|
|
|
if (gd->free_blocks_count == 0) {
|
|
word = 0;
|
|
continue;
|
|
}
|
|
|
|
bp = get_block(sp->s_dev, gd->block_bitmap, NORMAL);
|
|
|
|
if (rip->i_preallocation &&
|
|
gd->free_blocks_count >= (EXT2_PREALLOC_BLOCKS * 4) ) {
|
|
/* Try to preallocate blocks */
|
|
if (rip->i_prealloc_count != 0) {
|
|
/* kind of glitch... */
|
|
discard_preallocated_blocks(rip);
|
|
ext2_debug("warning, discarding previously preallocated\
|
|
blocks! It had to be done by another code.");
|
|
}
|
|
ASSERT(rip->i_prealloc_count == 0);
|
|
/* we preallocate bytes only */
|
|
ASSERT(EXT2_PREALLOC_BLOCKS == sizeof(char)*CHAR_BIT);
|
|
|
|
bit = setbyte(bp->b_bitmap, sp->s_blocks_per_group);
|
|
if (bit != -1) {
|
|
block = bit + sp->s_first_data_block +
|
|
group * sp->s_blocks_per_group;
|
|
check_block_number(block, sp, gd);
|
|
|
|
/* We preallocate a byte starting from block.
|
|
* First preallocated block will be returned as
|
|
* normally allocated block.
|
|
*/
|
|
for (i = 1; i < EXT2_PREALLOC_BLOCKS; i++) {
|
|
check_block_number(block + i, sp, gd);
|
|
rip->i_prealloc_blocks[i-1] = block + i;
|
|
}
|
|
rip->i_prealloc_index = 0;
|
|
rip->i_prealloc_count = EXT2_PREALLOC_BLOCKS - 1;
|
|
|
|
bp->b_dirt = DIRTY; /* by setbyte */
|
|
put_block(bp, MAP_BLOCK);
|
|
|
|
gd->free_blocks_count -= EXT2_PREALLOC_BLOCKS;
|
|
sp->s_free_blocks_count -= EXT2_PREALLOC_BLOCKS;
|
|
group_descriptors_dirty = DIRTY;
|
|
return block;
|
|
}
|
|
}
|
|
|
|
bit = setbit(bp->b_bitmap, sp->s_blocks_per_group, word);
|
|
if (bit == -1) {
|
|
if (word == 0) {
|
|
panic("ext2: allocator failed to allocate a bit in bitmap\
|
|
with free bits.");
|
|
} else {
|
|
word = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
block = sp->s_first_data_block + group * sp->s_blocks_per_group + bit;
|
|
check_block_number(block, sp, gd);
|
|
|
|
bp->b_dirt = DIRTY; /* Now it's safe to mark it as dirty */
|
|
put_block(bp, MAP_BLOCK);
|
|
|
|
gd->free_blocks_count--;
|
|
sp->s_free_blocks_count--;
|
|
group_descriptors_dirty = DIRTY;
|
|
|
|
if (update_bsearch && block != -1 && block != NO_BLOCK) {
|
|
/* We searched from the beginning, update bsearch. */
|
|
sp->s_bsearch = block;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* free_block *
|
|
*===========================================================================*/
|
|
void free_block(struct super_block *sp, bit_t bit_returned)
|
|
{
|
|
/* Return a block by turning off its bitmap bit. */
|
|
int group; /* group number of bit_returned */
|
|
int bit; /* bit_returned number within its group */
|
|
struct buf *bp;
|
|
struct group_desc *gd;
|
|
|
|
if (sp->s_rd_only)
|
|
panic("can't free bit on read-only filesys.");
|
|
|
|
if (bit_returned >= sp->s_blocks_count ||
|
|
bit_returned < sp->s_first_data_block)
|
|
panic("trying to free block %d beyond blocks scope.",
|
|
bit_returned);
|
|
|
|
/* At first search group, to which bit_returned belongs to
|
|
* and figure out in what word bit is stored.
|
|
*/
|
|
group = (bit_returned - sp->s_first_data_block) / sp->s_blocks_per_group;
|
|
bit = (bit_returned - sp->s_first_data_block) % sp->s_blocks_per_group;
|
|
|
|
gd = get_group_desc(group);
|
|
if (gd == NULL)
|
|
panic("can't get group_desc to alloc block");
|
|
|
|
/* We might be buggy (No way! :P), so check if we deallocate
|
|
* data block, but not control (system) block.
|
|
* This should never happen.
|
|
*/
|
|
if (bit_returned == gd->inode_bitmap || bit_returned == gd->block_bitmap
|
|
|| (bit_returned >= gd->inode_table
|
|
&& bit_returned < (gd->inode_table + sp->s_itb_per_group))) {
|
|
ext2_debug("ext2: freeing non-data block %d\n", bit_returned);
|
|
panic("trying to deallocate \
|
|
system/control block, hardly poke author.");
|
|
}
|
|
|
|
bp = get_block(sp->s_dev, gd->block_bitmap, NORMAL);
|
|
|
|
if (unsetbit(bp->b_bitmap, bit))
|
|
panic("Tried to free unused block", bit_returned);
|
|
|
|
bp->b_dirt = DIRTY;
|
|
put_block(bp, MAP_BLOCK);
|
|
|
|
gd->free_blocks_count++;
|
|
sp->s_free_blocks_count++;
|
|
|
|
group_descriptors_dirty = DIRTY;
|
|
|
|
if (bit_returned < sp->s_bsearch)
|
|
sp->s_bsearch = bit_returned;
|
|
}
|
|
|
|
|
|
static void check_block_number(block_t block, struct super_block *sp,
|
|
struct group_desc *gd)
|
|
{
|
|
|
|
/* Check if we allocated a data block, but not control (system) block.
|
|
* Only major bug can cause us to allocate wrong block. If it happens,
|
|
* we panic (and don't bloat filesystem's bitmap).
|
|
*/
|
|
if (block == gd->inode_bitmap || block == gd->block_bitmap ||
|
|
(block >= gd->inode_table
|
|
&& block < (gd->inode_table + sp->s_itb_per_group))) {
|
|
ext2_debug("ext2: allocating non-data block %d\n", block);
|
|
panic("ext2: block allocator tryed to return \
|
|
system/control block, poke author.\n");
|
|
}
|
|
|
|
if (block >= sp->s_blocks_count) {
|
|
panic("ext2: allocator returned blocknum greater, than \
|
|
total number of blocks.\n");
|
|
}
|
|
}
|