/*
* balloc.c
*
* PURPOSE
* Block allocation handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License (GPL). Copies of the GPL can be obtained from:
* ftp://prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work.
*
* (C) 1999-2001 Ben Fennema
* (C) 1999 Stelias Computing Inc
*
* HISTORY
*
* 02/24/99 blf Created.
*
*/
#include "udfdecl.h"
#include <linux/quotaops.h>
#include <linux/buffer_head.h>
#include <linux/bitops.h>
#include "udf_i.h"
#include "udf_sb.h"
#define udf_clear_bit(nr, addr) ext2_clear_bit(nr, addr)
#define udf_set_bit(nr, addr) ext2_set_bit(nr, addr)
#define udf_test_bit(nr, addr) ext2_test_bit(nr, addr)
#define udf_find_next_one_bit(addr, size, offset) \
ext2_find_next_bit(addr, size, offset)
static int read_block_bitmap(struct super_block *sb,
struct udf_bitmap *bitmap, unsigned int block,
unsigned long bitmap_nr)
{
struct buffer_head *bh = NULL;
int retval = 0;
struct kernel_lb_addr loc;
loc.logicalBlockNum = bitmap->s_extPosition;
loc.partitionReferenceNum = UDF_SB(sb)->s_partition;
bh = udf_tread(sb, udf_get_lb_pblock(sb, &loc, block));
if (!bh)
retval = -EIO;
bitmap->s_block_bitmap[bitmap_nr] = bh;
return retval;
}
static int __load_block_bitmap(struct super_block *sb,
struct udf_bitmap *bitmap,
unsigned int block_group)
{
int retval = 0;
int nr_groups = bitmap->s_nr_groups;
if (block_group >= nr_groups) {
udf_debug("block_group (%d) > nr_groups (%d)\n", block_group,
nr_groups);
}
if (bitmap->s_block_bitmap[block_group]) {
return block_group;
} else {
retval = read_block_bitmap(sb, bitmap, block_group,
block_group);
if (retval < 0)
return retval;
return block_group;
}
}
static inline int load_block_bitmap(struct super_block *sb,
struct udf_bitmap *bitmap,
unsigned int block_group)
{
int slot;
slot = __load_block_bitmap(sb, bitmap, block_group);
if (slot < 0)
return slot;
if (!bitmap->s_block_bitmap[slot])
return -EIO;
return slot;
}
static void udf_add_free_space(struct super_block *sb, u16 partition, u32 cnt)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct logicalVolIntegrityDesc *lvid;
if (!sbi->s_lvid_bh)
return;
lvid = (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data;
le32_add_cpu(&lvid->freeSpaceTable[partition], cnt);
udf_updated_lvid(sb);
}
static void udf_bitmap_free_blocks(struct super_block *sb,
struct inode *inode,
struct udf_bitmap *bitmap,
struct kernel_lb_addr *bloc,
uint32_t offset,
uint32_t count)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct buffer_head *bh = NULL;
struct udf_part_map *partmap;
unsigned long block;
unsigned long block_group;
unsigned long bit;
unsigned long i;
int bitmap_nr;
unsigned long overflow;
mutex_lock(&sbi->s_alloc_mutex);
partmap = &sbi->s_partmaps[bloc->partitionReferenceNum];
if (bloc->logicalBlockNum < 0 ||
(bloc->logicalBlockNum + count) >
partmap->s_partition_len) {
udf_debug("%d < %d || %d + %d > %d\n",
bloc->logicalBlockNum, 0, bloc->logicalBlockNum,
count, partmap->s_partition_len);
goto error_return;
}
block = bloc->logicalBlockNum + offset +
(sizeof(struct spaceBitmapDesc) << 3);
do {
overflow = 0;
block_group = block >> (sb->s_blocksize_bits + 3);
bit = block % (sb->s_blocksize << 3);
/*
* Check to see if we are freeing blocks across a group boundary.
*/
if (bit + count > (sb->s_blocksize << 3)) {
overflow = bit + count - (sb->s_blocksize << 3);
count -= overflow;
}
bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
if (bitmap_nr < 0)
goto error_return;
bh = bitmap->s_block_bitmap[bitmap_nr];
for (i = 0; i < count; i++) {
if (udf_set_bit(bit + i, bh->b_data)) {
udf_debug("bit %ld already set\n", bit + i);
udf_debug("byte=%2x\n",
((char *)bh->b_data)[(bit + i) >> 3]);
} else {
if (inode)
dquot_free_block(inode, 1);
udf_add_free_space(sb, sbi->s_partition, 1);
}
}
mark_buffer_dirty(bh);
if (