/*
* 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)) {
tmp = leBPL_to_cpup(p++);
if (tmp)
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(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 bool udf_add_free_space(struct udf_sb_info *sbi,
u16 partition, u32 cnt)
{
struct logicalVolIntegrityDesc *lvid;
if (sbi->s_lvid_bh == NULL)
return false;
lvid = (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data;
le32_add_cpu(&lvid->freeSpaceTable[partition], cnt);
return true;
}
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) >