/*
* 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_first_one_bit(addr, size) find_first_one_bit(addr, size)
#define udf_find_next_one_bit(addr, size, offset) find_next_one_bit(addr, size, offset)
#define leBPL_to_cpup(x) leNUM_to_cpup(BITS_PER_LONG, x)
#define leNUM_to_cpup(x,y) xleNUM_to_cpup(x,y)
#define xleNUM_to_cpup(x,y) (le ## x ## _to_cpup(y))
#define uintBPL_t uint(BITS_PER_LONG)
#define uint(x) xuint(x)
#define xuint(x) __le ## x
static inline int find_next_one_bit (void * addr, int size, int offset)
{
uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG);
int result = offset & ~(BITS_PER_LONG-1);
unsigned long tmp;
if (offset >= size)
return size;
size -= result;
offset &= (BITS_PER_LONG-1);
if (offset)
{
tmp = leBPL_to_cpup(p++);
tmp &= ~0UL << offset;
if (size < BITS_PER_LONG)
goto found_first;
if (tmp)
goto found_middle;
size -= BITS_PER_LONG;
result += BITS_PER_LONG;
}
while (size & ~(BITS_PER_LONG-1))
{
if ((tmp = leBPL_to_cpup(p++)))
goto found_middle;
result += BITS_PER_LONG;
size -= BITS_PER_LONG;
}
if (!size)
return result;
tmp = leBPL_to_cpup(p);
found_first:
tmp &= ~0UL >> (BITS_PER_LONG-size);
found_middle:
return result + ffz(~tmp);
}
#define find_first_one_bit(addr, size)\
find_next_one_bit((addr), (size), 0)
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;
kernel_lb_addr loc;
loc.logicalBlockNum = bitmap->s_extPosition;
loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
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_bitmap_free_blocks(struct super_block * sb,
struct inode * inode,
struct udf_bitmap *bitmap,
kernel_lb_addr bloc, uint32_t offset, uint32_t count)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct buffer_head * bh = NULL;
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);
if (bloc.logicalBlockNum < 0 ||
(bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum))
{
udf_debug("%d < %d || %d + %d > %d\n",
bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count,
UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum));
goto error_return;