summary refs log tree commit diff
path: root/fs/udf
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/udf
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
Diffstat (limited to 'fs/udf')
-rw-r--r--fs/udf/Makefile9
-rw-r--r--fs/udf/balloc.c959
-rw-r--r--fs/udf/crc.c178
-rw-r--r--fs/udf/dir.c268
-rw-r--r--fs/udf/directory.c343
-rw-r--r--fs/udf/ecma_167.h864
-rw-r--r--fs/udf/file.c270
-rw-r--r--fs/udf/fsync.c56
-rw-r--r--fs/udf/ialloc.c170
-rw-r--r--fs/udf/inode.c2010
-rw-r--r--fs/udf/lowlevel.c77
-rw-r--r--fs/udf/misc.c313
-rw-r--r--fs/udf/namei.c1334
-rw-r--r--fs/udf/osta_udf.h296
-rw-r--r--fs/udf/partition.c226
-rw-r--r--fs/udf/super.c1934
-rw-r--r--fs/udf/symlink.c123
-rw-r--r--fs/udf/truncate.c284
-rw-r--r--fs/udf/udf_i.h26
-rw-r--r--fs/udf/udf_sb.h139
-rw-r--r--fs/udf/udfdecl.h167
-rw-r--r--fs/udf/udfend.h81
-rw-r--r--fs/udf/udftime.c174
-rw-r--r--fs/udf/unicode.c516
24 files changed, 10817 insertions, 0 deletions
diff --git a/fs/udf/Makefile b/fs/udf/Makefile
new file mode 100644
index 000000000000..be845e7540ef
--- /dev/null
+++ b/fs/udf/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the linux udf-filesystem routines.
+#
+
+obj-$(CONFIG_UDF_FS) += udf.o
+
+udf-objs     := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \
+		partition.o super.o truncate.o symlink.o fsync.o \
+		crc.o directory.o misc.o udftime.o unicode.o
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
new file mode 100644
index 000000000000..b9ded26b10a9
--- /dev/null
+++ b/fs/udf/balloc.c
@@ -0,0 +1,959 @@
+/*
+ * balloc.c
+ *
+ * PURPOSE
+ *	Block allocation handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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
+
+extern 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;
+
+	down(&sbi->s_alloc_sem);
+	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;
+	}
+
+	block = bloc.logicalBlockNum + offset + (sizeof(struct spaceBitmapDesc) << 3);
+
+do_more:
+	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);
+			if (UDF_SB_LVIDBH(sb))
+			{
+				UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] =
+					cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1);
+			}
+		}
+	}
+	mark_buffer_dirty(bh);
+	if (overflow)
+	{
+		block += count;
+		count = overflow;
+		goto do_more;
+	}
+error_return:
+	sb->s_dirt = 1;
+	if (UDF_SB_LVIDBH(sb))
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	up(&sbi->s_alloc_sem);
+	return;
+}
+
+static int udf_bitmap_prealloc_blocks(struct super_block * sb,
+	struct inode * inode,
+	struct udf_bitmap *bitmap, uint16_t partition, uint32_t first_block,
+	uint32_t block_count)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int alloc_count = 0;
+	int bit, block, block_group, group_start;
+	int nr_groups, bitmap_nr;
+	struct buffer_head *bh;
+
+	down(&sbi->s_alloc_sem);
+	if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition))
+		goto out;
+
+	if (first_block + block_count > UDF_SB_PARTLEN(sb, partition))
+		block_count = UDF_SB_PARTLEN(sb, partition) - first_block;
+
+repeat:
+	nr_groups = (UDF_SB_PARTLEN(sb, partition) +
+		(sizeof(struct spaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8);
+	block = first_block + (sizeof(struct spaceBitmapDesc) << 3);
+	block_group = block >> (sb->s_blocksize_bits + 3);
+	group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
+
+	bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+	if (bitmap_nr < 0)
+		goto out;
+	bh = bitmap->s_block_bitmap[bitmap_nr];
+
+	bit = block % (sb->s_blocksize << 3);
+
+	while (bit < (sb->s_blocksize << 3) && block_count > 0)
+	{
+		if (!udf_test_bit(bit, bh->b_data))
+			goto out;
+		else if (DQUOT_PREALLOC_BLOCK(inode, 1))
+			goto out;
+		else if (!udf_clear_bit(bit, bh->b_data))
+		{
+			udf_debug("bit already cleared for block %d\n", bit);
+			DQUOT_FREE_BLOCK(inode, 1);
+			goto out;
+		}
+		block_count --;
+		alloc_count ++;
+		bit ++;
+		block ++;
+	}
+	mark_buffer_dirty(bh);
+	if (block_count > 0)
+		goto repeat;
+out:
+	if (UDF_SB_LVIDBH(sb))
+	{
+		UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+			cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count);
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+	sb->s_dirt = 1;
+	up(&sbi->s_alloc_sem);
+	return alloc_count;
+}
+
+static int udf_bitmap_new_block(struct super_block * sb,
+	struct inode * inode,
+	struct udf_bitmap *bitmap, uint16_t partition, uint32_t goal, int *err)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int newbit, bit=0, block, block_group, group_start;
+	int end_goal, nr_groups, bitmap_nr, i;
+	struct buffer_head *bh = NULL;
+	char *ptr;
+	int newblock = 0;
+
+	*err = -ENOSPC;
+	down(&sbi->s_alloc_sem);
+
+repeat:
+	if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition))
+		goal = 0;
+
+	nr_groups = bitmap->s_nr_groups;
+	block = goal + (sizeof(struct spaceBitmapDesc) << 3);
+	block_group = block >> (sb->s_blocksize_bits + 3);
+	group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
+
+	bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+	if (bitmap_nr < 0)
+		goto error_return;
+	bh = bitmap->s_block_bitmap[bitmap_nr];
+	ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start);
+
+	if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize)
+	{
+		bit = block % (sb->s_blocksize << 3);
+
+		if (udf_test_bit(bit, bh->b_data))
+		{
+			goto got_block;
+		}
+		end_goal = (bit + 63) & ~63;
+		bit = udf_find_next_one_bit(bh->b_data, end_goal, bit);
+		if (bit < end_goal)
+			goto got_block;
+		ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF, sb->s_blocksize - ((bit + 7) >> 3));
+		newbit = (ptr - ((char *)bh->b_data)) << 3;
+		if (newbit < sb->s_blocksize << 3)
+		{
+			bit = newbit;
+			goto search_back;
+		}
+		newbit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, bit);
+		if (newbit < sb->s_blocksize << 3)
+		{
+			bit = newbit;
+			goto got_block;
+		}
+	}
+
+	for (i=0; i<(nr_groups*2); i++)
+	{
+		block_group ++;
+		if (block_group >= nr_groups)
+			block_group = 0;
+		group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
+
+		bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+		if (bitmap_nr < 0)
+			goto error_return;
+		bh = bitmap->s_block_bitmap[bitmap_nr];
+		if (i < nr_groups)
+		{
+			ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start);
+			if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize)
+			{
+				bit = (ptr - ((char *)bh->b_data)) << 3;
+				break;
+			}
+		}
+		else
+		{
+			bit = udf_find_next_one_bit((char *)bh->b_data, sb->s_blocksize << 3, group_start << 3);
+			if (bit < sb->s_blocksize << 3)
+				break;
+		}
+	}
+	if (i >= (nr_groups*2))
+	{
+		up(&sbi->s_alloc_sem);
+		return newblock;
+	}
+	if (bit < sb->s_blocksize << 3)
+		goto search_back;
+	else
+		bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3);
+	if (bit >= sb->s_blocksize << 3)
+	{
+		up(&sbi->s_alloc_sem);
+		return 0;
+	}
+
+search_back:
+	for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--);
+
+got_block:
+
+	/*
+	 * Check quota for allocation of this block.
+	 */
+	if (inode && DQUOT_ALLOC_BLOCK(inode, 1))
+	{
+		up(&sbi->s_alloc_sem);
+		*err = -EDQUOT;
+		return 0;
+	}
+
+	newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) -
+		(sizeof(struct spaceBitmapDesc) << 3);
+
+	if (!udf_clear_bit(bit, bh->b_data))
+	{
+		udf_debug("bit already cleared for block %d\n", bit);
+		goto repeat;
+	}
+
+	mark_buffer_dirty(bh);
+
+	if (UDF_SB_LVIDBH(sb))
+	{
+		UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+			cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1);
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+	sb->s_dirt = 1;
+	up(&sbi->s_alloc_sem);
+	*err = 0;
+	return newblock;
+
+error_return:
+	*err = -EIO;
+	up(&sbi->s_alloc_sem);
+	return 0;
+}
+
+static void udf_table_free_blocks(struct super_block * sb,
+	struct inode * inode,
+	struct inode * table,
+	kernel_lb_addr bloc, uint32_t offset, uint32_t count)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	uint32_t start, end;
+	uint32_t nextoffset, oextoffset, elen;
+	kernel_lb_addr nbloc, obloc, eloc;
+	struct buffer_head *obh, *nbh;
+	int8_t etype;
+	int i;
+
+	down(&sbi->s_alloc_sem);
+	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;
+	}
+
+	/* We do this up front - There are some error conditions that could occure,
+	   but.. oh well */
+	if (inode)
+		DQUOT_FREE_BLOCK(inode, count);
+	if (UDF_SB_LVIDBH(sb))
+	{
+		UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] =
+			cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+count);
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+
+	start = bloc.logicalBlockNum + offset;
+	end = bloc.logicalBlockNum + offset + count - 1;
+
+	oextoffset = nextoffset = sizeof(struct unallocSpaceEntry);
+	elen = 0;
+	obloc = nbloc = UDF_I_LOCATION(table);
+
+	obh = nbh = NULL;
+
+	while (count && (etype =
+		udf_next_aext(table, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1)
+	{
+		if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) ==
+			start))
+		{
+			if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits))
+			{
+				count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+				start += ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+				elen = (etype << 30) | (0x40000000 - sb->s_blocksize);
+			}
+			else
+			{
+				elen = (etype << 30) |
+					(elen + (count << sb->s_blocksize_bits));
+				start += count;
+				count = 0;
+			}
+			udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1);
+		}
+		else if (eloc.logicalBlockNum == (end + 1))
+		{
+			if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits))
+			{
+				count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+				end -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+				eloc.logicalBlockNum -=
+					((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+				elen = (etype << 30) | (0x40000000 - sb->s_blocksize);
+			}
+			else
+			{
+				eloc.logicalBlockNum = start;
+				elen = (etype << 30) |
+					(elen + (count << sb->s_blocksize_bits));
+				end -= count;
+				count = 0;
+			}
+			udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1);
+		}
+
+		if (nbh != obh)
+		{
+			i = -1;
+			obloc = nbloc;
+			udf_release_data(obh);
+			atomic_inc(&nbh->b_count);
+			obh = nbh;
+			oextoffset = 0;
+		}
+		else
+			oextoffset = nextoffset;
+	}
+
+	if (count)
+	{
+		/* NOTE: we CANNOT use udf_add_aext here, as it can try to allocate
+				 a new block, and since we hold the super block lock already
+				 very bad things would happen :)
+
+				 We copy the behavior of udf_add_aext, but instead of
+				 trying to allocate a new block close to the existing one,
+				 we just steal a block from the extent we are trying to add.
+
+				 It would be nice if the blocks were close together, but it
+				 isn't required.
+		*/
+
+		int adsize;
+		short_ad *sad = NULL;
+		long_ad *lad = NULL;
+		struct allocExtDesc *aed;
+
+		eloc.logicalBlockNum = start;
+		elen = EXT_RECORDED_ALLOCATED |
+			(count << sb->s_blocksize_bits);
+
+		if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT)
+			adsize = sizeof(short_ad);
+		else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG)
+			adsize = sizeof(long_ad);
+		else
+		{
+			udf_release_data(obh);
+			udf_release_data(nbh);
+			goto error_return;
+		}
+
+		if (nextoffset + (2 * adsize) > sb->s_blocksize)
+		{
+			char *sptr, *dptr;
+			int loffset;
+	
+			udf_release_data(obh);
+			obh = nbh;
+			obloc = nbloc;
+			oextoffset = nextoffset;
+
+			/* Steal a block from the extent being free'd */
+			nbloc.logicalBlockNum = eloc.logicalBlockNum;
+			eloc.logicalBlockNum ++;
+			elen -= sb->s_blocksize;
+
+			if (!(nbh = udf_tread(sb,
+				udf_get_lb_pblock(sb, nbloc, 0))))
+			{
+				udf_release_data(obh);
+				goto error_return;
+			}
+			aed = (struct allocExtDesc *)(nbh->b_data);
+			aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum);
+			if (nextoffset + adsize > sb->s_blocksize)
+			{
+				loffset = nextoffset;
+				aed->lengthAllocDescs = cpu_to_le32(adsize);
+				if (obh)
+					sptr = UDF_I_DATA(inode) + nextoffset -  udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode) - adsize;
+				else
+					sptr = obh->b_data + nextoffset - adsize;
+				dptr = nbh->b_data + sizeof(struct allocExtDesc);
+				memcpy(dptr, sptr, adsize);
+				nextoffset = sizeof(struct allocExtDesc) + adsize;
+			}
+			else
+			{
+				loffset = nextoffset + adsize;
+				aed->lengthAllocDescs = cpu_to_le32(0);
+				sptr = (obh)->b_data + nextoffset;
+				nextoffset = sizeof(struct allocExtDesc);
+
+				if (obh)
+				{
+					aed = (struct allocExtDesc *)(obh)->b_data;
+					aed->lengthAllocDescs =
+						cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+				}
+				else
+				{
+					UDF_I_LENALLOC(table) += adsize;
+					mark_inode_dirty(table);
+				}
+			}
+			if (UDF_SB_UDFREV(sb) >= 0x0200)
+				udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1,
+					nbloc.logicalBlockNum, sizeof(tag));
+			else
+				udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1,
+					nbloc.logicalBlockNum, sizeof(tag));
+			switch (UDF_I_ALLOCTYPE(table))
+			{
+				case ICBTAG_FLAG_AD_SHORT:
+				{
+					sad = (short_ad *)sptr;
+					sad->extLength = cpu_to_le32(
+						EXT_NEXT_EXTENT_ALLOCDECS |
+						sb->s_blocksize);
+					sad->extPosition = cpu_to_le32(nbloc.logicalBlockNum);
+					break;
+				}
+				case ICBTAG_FLAG_AD_LONG:
+				{
+					lad = (long_ad *)sptr;
+					lad->extLength = cpu_to_le32(
+						EXT_NEXT_EXTENT_ALLOCDECS |
+						sb->s_blocksize);
+					lad->extLocation = cpu_to_lelb(nbloc);
+					break;
+				}
+			}
+			if (obh)
+			{
+				udf_update_tag(obh->b_data, loffset);
+				mark_buffer_dirty(obh);
+			}
+			else
+				mark_inode_dirty(table);
+		}
+
+		if (elen) /* It's possible that stealing the block emptied the extent */
+		{
+			udf_write_aext(table, nbloc, &nextoffset, eloc, elen, nbh, 1);
+
+			if (!nbh)
+			{
+				UDF_I_LENALLOC(table) += adsize;
+				mark_inode_dirty(table);
+			}
+			else
+			{
+				aed = (struct allocExtDesc *)nbh->b_data;
+				aed->lengthAllocDescs =
+					cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+				udf_update_tag(nbh->b_data, nextoffset);
+				mark_buffer_dirty(nbh);
+			}
+		}
+	}
+
+	udf_release_data(nbh);
+	udf_release_data(obh);
+
+error_return:
+	sb->s_dirt = 1;
+	up(&sbi->s_alloc_sem);
+	return;
+}
+
+static int udf_table_prealloc_blocks(struct super_block * sb,
+	struct inode * inode,
+	struct inode *table, uint16_t partition, uint32_t first_block,
+	uint32_t block_count)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int alloc_count = 0;
+	uint32_t extoffset, elen, adsize;
+	kernel_lb_addr bloc, eloc;
+	struct buffer_head *bh;
+	int8_t etype = -1;
+
+	if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition))
+		return 0;
+
+	if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(long_ad);
+	else
+		return 0;
+
+	down(&sbi->s_alloc_sem);
+	extoffset = sizeof(struct unallocSpaceEntry);
+	bloc = UDF_I_LOCATION(table);
+
+	bh = NULL;
+	eloc.logicalBlockNum = 0xFFFFFFFF;
+
+	while (first_block != eloc.logicalBlockNum && (etype =
+		udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+	{
+		udf_debug("eloc=%d, elen=%d, first_block=%d\n",
+			eloc.logicalBlockNum, elen, first_block);
+		; /* empty loop body */
+	}
+
+	if (first_block == eloc.logicalBlockNum)
+	{
+		extoffset -= adsize;
+
+		alloc_count = (elen >> sb->s_blocksize_bits);
+		if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count))
+			alloc_count = 0;
+		else if (alloc_count > block_count)
+		{
+			alloc_count = block_count;
+			eloc.logicalBlockNum += alloc_count;
+			elen -= (alloc_count << sb->s_blocksize_bits);
+			udf_write_aext(table, bloc, &extoffset, eloc, (etype << 30) | elen, bh, 1);
+		}
+		else
+			udf_delete_aext(table, bloc, extoffset, eloc, (etype << 30) | elen, bh);
+	}
+	else
+		alloc_count = 0;
+
+	udf_release_data(bh);
+
+	if (alloc_count && UDF_SB_LVIDBH(sb))
+	{
+		UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+			cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count);
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+		sb->s_dirt = 1;
+	}
+	up(&sbi->s_alloc_sem);
+	return alloc_count;
+}
+
+static int udf_table_new_block(struct super_block * sb,
+	struct inode * inode,
+	struct inode *table, uint16_t partition, uint32_t goal, int *err)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF;
+	uint32_t newblock = 0, adsize;
+	uint32_t extoffset, goal_extoffset, elen, goal_elen = 0;
+	kernel_lb_addr bloc, goal_bloc, eloc, goal_eloc;
+	struct buffer_head *bh, *goal_bh;
+	int8_t etype;
+
+	*err = -ENOSPC;
+
+	if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(long_ad);
+	else
+		return newblock;
+
+	down(&sbi->s_alloc_sem);
+	if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition))
+		goal = 0;
+
+	/* We search for the closest matching block to goal. If we find a exact hit,
+	   we stop. Otherwise we keep going till we run out of extents.
+	   We store the buffer_head, bloc, and extoffset of the current closest
+	   match and use that when we are done.
+	*/
+
+	extoffset = sizeof(struct unallocSpaceEntry);
+	bloc = UDF_I_LOCATION(table);
+
+	goal_bh = bh = NULL;
+
+	while (spread && (etype =
+		udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+	{
+		if (goal >= eloc.logicalBlockNum)
+		{
+			if (goal < eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits))
+				nspread = 0;
+			else
+				nspread = goal - eloc.logicalBlockNum -
+					(elen >> sb->s_blocksize_bits);
+		}
+		else
+			nspread = eloc.logicalBlockNum - goal;
+
+		if (nspread < spread)
+		{
+			spread = nspread;
+			if (goal_bh != bh)
+			{
+				udf_release_data(goal_bh);
+				goal_bh = bh;
+				atomic_inc(&goal_bh->b_count);
+			}
+			goal_bloc = bloc;
+			goal_extoffset = extoffset - adsize;
+			goal_eloc = eloc;
+			goal_elen = (etype << 30) | elen;
+		}
+	}
+
+	udf_release_data(bh);
+
+	if (spread == 0xFFFFFFFF)
+	{
+		udf_release_data(goal_bh);
+		up(&sbi->s_alloc_sem);
+		return 0;
+	}
+
+	/* Only allocate blocks from the beginning of the extent.
+	   That way, we only delete (empty) extents, never have to insert an
+	   extent because of splitting */
+	/* This works, but very poorly.... */
+
+	newblock = goal_eloc.logicalBlockNum;
+	goal_eloc.logicalBlockNum ++;
+	goal_elen -= sb->s_blocksize;
+
+	if (inode && DQUOT_ALLOC_BLOCK(inode, 1))
+	{
+		udf_release_data(goal_bh);
+		up(&sbi->s_alloc_sem);
+		*err = -EDQUOT;
+		return 0;
+	}
+
+	if (goal_elen)
+		udf_write_aext(table, goal_bloc, &goal_extoffset, goal_eloc, goal_elen, goal_bh, 1);
+	else
+		udf_delete_aext(table, goal_bloc, goal_extoffset, goal_eloc, goal_elen, goal_bh);
+	udf_release_data(goal_bh);
+
+	if (UDF_SB_LVIDBH(sb))
+	{
+		UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+			cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1);
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+
+	sb->s_dirt = 1;
+	up(&sbi->s_alloc_sem);
+	*err = 0;
+	return newblock;
+}
+
+inline void udf_free_blocks(struct super_block * sb,
+	struct inode * inode,
+	kernel_lb_addr bloc, uint32_t offset, uint32_t count)
+{
+	uint16_t partition = bloc.partitionReferenceNum;
+
+	if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		return udf_bitmap_free_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap,
+			bloc, offset, count);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE)
+	{
+		return udf_table_free_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table,
+			bloc, offset, count);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		return udf_bitmap_free_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap,
+			bloc, offset, count);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE)
+	{
+		return udf_table_free_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table,
+			bloc, offset, count);
+	}
+	else
+		return;
+}
+
+inline int udf_prealloc_blocks(struct super_block * sb,
+	struct inode * inode,
+	uint16_t partition, uint32_t first_block, uint32_t block_count)
+{
+	if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		return udf_bitmap_prealloc_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap,
+			partition, first_block, block_count);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE)
+	{
+		return udf_table_prealloc_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table,
+			partition, first_block, block_count);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		return udf_bitmap_prealloc_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap,
+			partition, first_block, block_count);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE)
+	{
+		return udf_table_prealloc_blocks(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table,
+			partition, first_block, block_count);
+	}
+	else
+		return 0;
+}
+
+inline int udf_new_block(struct super_block * sb,
+	struct inode * inode,
+	uint16_t partition, uint32_t goal, int *err)
+{
+	if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		return udf_bitmap_new_block(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap,
+			partition, goal, err);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE)
+	{
+		return udf_table_new_block(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table,
+			partition, goal, err);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		return udf_bitmap_new_block(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap,
+			partition, goal, err);
+	}
+	else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE)
+	{
+		return udf_table_new_block(sb, inode,
+			UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table,
+			partition, goal, err);
+	}
+	else
+	{
+		*err = -EIO;
+		return 0;
+	}
+}
diff --git a/fs/udf/crc.c b/fs/udf/crc.c
new file mode 100644
index 000000000000..d95c6e38a455
--- /dev/null
+++ b/fs/udf/crc.c
@@ -0,0 +1,178 @@
+/*
+ * crc.c
+ *
+ * PURPOSE
+ *	Routines to generate, calculate, and test a 16-bit CRC.
+ *
+ * DESCRIPTION
+ *	The CRC code was devised by Don P. Mitchell of AT&T Bell Laboratories
+ *	and Ned W. Rhodes of Software Systems Group. It has been published in
+ *	"Design and Validation of Computer Protocols", Prentice Hall,
+ *	Englewood Cliffs, NJ, 1991, Chapter 3, ISBN 0-13-539925-4.
+ *
+ *	Copyright is held by AT&T.
+ *
+ *	AT&T gives permission for the free use of the CRC source code.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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.
+ */
+
+#include "udfdecl.h"
+
+static uint16_t crc_table[256] = {
+	0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50a5U, 0x60c6U, 0x70e7U,
+	0x8108U, 0x9129U, 0xa14aU, 0xb16bU, 0xc18cU, 0xd1adU, 0xe1ceU, 0xf1efU,
+	0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52b5U, 0x4294U, 0x72f7U, 0x62d6U,
+	0x9339U, 0x8318U, 0xb37bU, 0xa35aU, 0xd3bdU, 0xc39cU, 0xf3ffU, 0xe3deU,
+	0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64e6U, 0x74c7U, 0x44a4U, 0x5485U,
+	0xa56aU, 0xb54bU, 0x8528U, 0x9509U, 0xe5eeU, 0xf5cfU, 0xc5acU, 0xd58dU,
+	0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76d7U, 0x66f6U, 0x5695U, 0x46b4U,
+	0xb75bU, 0xa77aU, 0x9719U, 0x8738U, 0xf7dfU, 0xe7feU, 0xd79dU, 0xc7bcU,
+	0x48c4U, 0x58e5U, 0x6886U, 0x78a7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U,
+	0xc9ccU, 0xd9edU, 0xe98eU, 0xf9afU, 0x8948U, 0x9969U, 0xa90aU, 0xb92bU,
+	0x5af5U, 0x4ad4U, 0x7ab7U, 0x6a96U, 0x1a71U, 0x0a50U, 0x3a33U, 0x2a12U,
+	0xdbfdU, 0xcbdcU, 0xfbbfU, 0xeb9eU, 0x9b79U, 0x8b58U, 0xbb3bU, 0xab1aU,
+	0x6ca6U, 0x7c87U, 0x4ce4U, 0x5cc5U, 0x2c22U, 0x3c03U, 0x0c60U, 0x1c41U,
+	0xedaeU, 0xfd8fU, 0xcdecU, 0xddcdU, 0xad2aU, 0xbd0bU, 0x8d68U, 0x9d49U,
+	0x7e97U, 0x6eb6U, 0x5ed5U, 0x4ef4U, 0x3e13U, 0x2e32U, 0x1e51U, 0x0e70U,
+	0xff9fU, 0xefbeU, 0xdfddU, 0xcffcU, 0xbf1bU, 0xaf3aU, 0x9f59U, 0x8f78U,
+	0x9188U, 0x81a9U, 0xb1caU, 0xa1ebU, 0xd10cU, 0xc12dU, 0xf14eU, 0xe16fU,
+	0x1080U, 0x00a1U, 0x30c2U, 0x20e3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U,
+	0x83b9U, 0x9398U, 0xa3fbU, 0xb3daU, 0xc33dU, 0xd31cU, 0xe37fU, 0xf35eU,
+	0x02b1U, 0x1290U, 0x22f3U, 0x32d2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U,
+	0xb5eaU, 0xa5cbU, 0x95a8U, 0x8589U, 0xf56eU, 0xe54fU, 0xd52cU, 0xc50dU,
+	0x34e2U, 0x24c3U, 0x14a0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U,
+	0xa7dbU, 0xb7faU, 0x8799U, 0x97b8U, 0xe75fU, 0xf77eU, 0xc71dU, 0xd73cU,
+	0x26d3U, 0x36f2U, 0x0691U, 0x16b0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U,
+	0xd94cU, 0xc96dU, 0xf90eU, 0xe92fU, 0x99c8U, 0x89e9U, 0xb98aU, 0xa9abU,
+	0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18c0U, 0x08e1U, 0x3882U, 0x28a3U,
+	0xcb7dU, 0xdb5cU, 0xeb3fU, 0xfb1eU, 0x8bf9U, 0x9bd8U, 0xabbbU, 0xbb9aU,
+	0x4a75U, 0x5a54U, 0x6a37U, 0x7a16U, 0x0af1U, 0x1ad0U, 0x2ab3U, 0x3a92U,
+	0xfd2eU, 0xed0fU, 0xdd6cU, 0xcd4dU, 0xbdaaU, 0xad8bU, 0x9de8U, 0x8dc9U,
+	0x7c26U, 0x6c07U, 0x5c64U, 0x4c45U, 0x3ca2U, 0x2c83U, 0x1ce0U, 0x0cc1U,
+	0xef1fU, 0xff3eU, 0xcf5dU, 0xdf7cU, 0xaf9bU, 0xbfbaU, 0x8fd9U, 0x9ff8U,
+	0x6e17U, 0x7e36U, 0x4e55U, 0x5e74U, 0x2e93U, 0x3eb2U, 0x0ed1U, 0x1ef0U
+};
+
+/*
+ * udf_crc
+ *
+ * PURPOSE
+ *	Calculate a 16-bit CRC checksum using ITU-T V.41 polynomial.
+ *
+ * DESCRIPTION
+ *	The OSTA-UDF(tm) 1.50 standard states that using CRCs is mandatory.
+ *	The polynomial used is:	x^16 + x^12 + x^15 + 1
+ *
+ * PRE-CONDITIONS
+ *	data		Pointer to the data block.
+ *	size		Size of the data block.
+ *
+ * POST-CONDITIONS
+ *	<return>	CRC of the data block.
+ *
+ * HISTORY
+ *	July 21, 1997 - Andrew E. Mileski
+ *	Adapted from OSTA-UDF(tm) 1.50 standard.
+ */
+uint16_t
+udf_crc(uint8_t *data, uint32_t size, uint16_t crc)
+{
+	while (size--)
+		crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8);
+
+	return crc;
+}
+
+/****************************************************************************/
+#if defined(TEST)
+
+/*
+ * PURPOSE
+ *	Test udf_crc()
+ *
+ * HISTORY
+ *	July 21, 1997 - Andrew E. Mileski
+ *	Adapted from OSTA-UDF(tm) 1.50 standard.
+ */
+
+unsigned char bytes[] = { 0x70U, 0x6AU, 0x77U };
+
+int main(void)
+{
+	unsigned short x;
+
+	x = udf_crc16(bytes, sizeof bytes);
+	printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U);
+
+	return 0;
+}
+
+#endif /* defined(TEST) */
+
+/****************************************************************************/
+#if defined(GENERATE)
+
+/*
+ * PURPOSE
+ *	Generate a table for fast 16-bit CRC calculations (any polynomial).
+ *
+ * DESCRIPTION
+ *	The ITU-T V.41 polynomial is 010041.
+ *
+ * HISTORY
+ *	July 21, 1997 - Andrew E. Mileski
+ *	Adapted from OSTA-UDF(tm) 1.50 standard.
+ */
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+	unsigned long crc, poly;
+	int n, i;
+
+	/* Get the polynomial */
+	sscanf(argv[1], "%lo", &poly);
+	if (poly & 0xffff0000U){
+		fprintf(stderr, "polynomial is too large\en");
+		exit(1);
+	}
+
+	printf("/* CRC 0%o */\n", poly);
+
+	/* Create a table */
+	printf("static unsigned short crc_table[256] = {\n");
+	for (n = 0; n < 256; n++){
+		if (n % 8 == 0)
+			printf("\t");
+		crc = n << 8;
+		for (i = 0; i < 8; i++){
+			if(crc & 0x8000U)
+				crc = (crc << 1) ^ poly;
+			else
+				crc <<= 1;
+		crc &= 0xFFFFU;
+		}
+		if (n == 255)
+			printf("0x%04xU ", crc);
+		else
+			printf("0x%04xU, ", crc);
+		if(n % 8 == 7)
+			printf("\n");
+	}
+	printf("};\n");
+
+	return 0;
+}
+
+#endif /* defined(GENERATE) */
diff --git a/fs/udf/dir.c b/fs/udf/dir.c
new file mode 100644
index 000000000000..82440b731142
--- /dev/null
+++ b/fs/udf/dir.c
@@ -0,0 +1,268 @@
+/*
+ * dir.c
+ *
+ * PURPOSE
+ *  Directory handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998-2004 Ben Fennema
+ *
+ * HISTORY
+ *
+ *  10/05/98 dgb  Split directory operations into its own file
+ *                Implemented directory reads via do_udf_readdir
+ *  10/06/98      Made directory operations work!
+ *  11/17/98      Rewrote directory to support ICBTAG_FLAG_AD_LONG
+ *  11/25/98 blf  Rewrote directory handling (readdir+lookup) to support reading
+ *                across blocks.
+ *  12/12/98      Split out the lookup code to namei.c. bulk of directory
+ *                code now in directory.c:udf_fileident_read.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+/* Prototypes for file operations */
+static int udf_readdir(struct file *, void *, filldir_t);
+static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *);
+
+/* readdir and lookup functions */
+
+struct file_operations udf_dir_operations = {
+	.read			= generic_read_dir,
+	.readdir		= udf_readdir,
+	.ioctl			= udf_ioctl,
+	.fsync			= udf_fsync_file,
+};
+
+/*
+ * udf_readdir
+ *
+ * PURPOSE
+ *	Read a directory entry.
+ *
+ * DESCRIPTION
+ *	Optional - sys_getdents() will return -ENOTDIR if this routine is not
+ *	available.
+ *
+ *	Refer to sys_getdents() in fs/readdir.c
+ *	sys_getdents() -> .
+ *
+ * PRE-CONDITIONS
+ *	filp			Pointer to directory file.
+ *	buf			Pointer to directory entry buffer.
+ *	filldir			Pointer to filldir function.
+ *
+ * POST-CONDITIONS
+ *	<return>		>=0 on success.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+
+int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *dir = filp->f_dentry->d_inode;
+	int result;
+
+	lock_kernel();
+
+	if ( filp->f_pos == 0 ) 
+	{
+		if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0)
+		{
+			unlock_kernel();
+			return 0;
+		}
+		filp->f_pos ++;
+	}
+
+	result = do_udf_readdir(dir, filp, filldir, dirent);
+	unlock_kernel();
+ 	return result;
+}
+
+static int 
+do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent)
+{
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc *fi=NULL;
+	struct fileIdentDesc cfi;
+	int block, iblock;
+	loff_t nf_pos = filp->f_pos - 1;
+	int flen;
+	char fname[UDF_NAME_LEN];
+	char *nameptr;
+	uint16_t liu;
+	uint8_t lfi;
+	loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+	struct buffer_head * bh = NULL, * tmp, * bha[16];
+	kernel_lb_addr bloc, eloc;
+	uint32_t extoffset, elen, offset;
+	int i, num;
+	unsigned int dt_type;
+
+	if (nf_pos >= size)
+		return 0;
+
+	if (nf_pos == 0)
+		nf_pos = (udf_ext0_offset(dir) >> 2);
+
+	fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+		fibh.sbh = fibh.ebh = NULL;
+	else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2),
+		&bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+	{
+		offset >>= dir->i_sb->s_blocksize_bits;
+		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+		if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+		{
+			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+				extoffset -= sizeof(short_ad);
+			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+				extoffset -= sizeof(long_ad);
+		}
+		else
+			offset = 0;
+
+		if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
+		{
+			udf_release_data(bh);
+			return -EIO;
+		}
+	
+		if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
+		{
+			i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
+			if (i+offset > (elen >> dir->i_sb->s_blocksize_bits))
+				i = (elen >> dir->i_sb->s_blocksize_bits)-offset;
+			for (num=0; i>0; i--)
+			{
+				block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i);
+				tmp = udf_tgetblk(dir->i_sb, block);
+				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
+					bha[num++] = tmp;
+				else
+					brelse(tmp);
+			}
+			if (num)
+			{
+				ll_rw_block(READA, num, bha);
+				for (i=0; i<num; i++)
+					brelse(bha[i]);
+			}
+		}
+	}
+	else
+	{
+		udf_release_data(bh);
+		return -ENOENT;
+	}
+
+	while ( nf_pos < size )
+	{
+		filp->f_pos = nf_pos + 1;
+
+		fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+		if (!fi)
+		{
+			if (fibh.sbh != fibh.ebh)
+				udf_release_data(fibh.ebh);
+			udf_release_data(fibh.sbh);
+			udf_release_data(bh);
+			return 0;
+		}
+
+		liu = le16_to_cpu(cfi.lengthOfImpUse);
+		lfi = cfi.lengthFileIdent;
+
+		if (fibh.sbh == fibh.ebh)
+			nameptr = fi->fileIdent + liu;
+		else
+		{
+			int poffset;	/* Unpaded ending offset */
+
+			poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
+
+			if (poffset >= lfi)
+				nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
+			else
+			{
+				nameptr = fname;
+				memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
+				memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset);
+			}
+		}
+
+		if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
+		{
+			if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
+				continue;
+		}
+		
+		if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
+		{
+			if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
+				continue;
+		}
+
+		if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT )
+		{
+			iblock = parent_ino(filp->f_dentry);
+			flen = 2;
+			memcpy(fname, "..", flen);
+			dt_type = DT_DIR;
+		}
+		else
+		{
+			kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation);
+
+			iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0);
+			flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
+			dt_type = DT_UNKNOWN;
+		}
+
+		if (flen)
+		{
+			if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0)
+			{
+				if (fibh.sbh != fibh.ebh)
+					udf_release_data(fibh.ebh);
+				udf_release_data(fibh.sbh);
+				udf_release_data(bh);
+	 			return 0;
+			}
+		}
+	} /* end while */
+
+	filp->f_pos = nf_pos + 1;
+
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	udf_release_data(bh);
+
+	return 0;
+}
diff --git a/fs/udf/directory.c b/fs/udf/directory.c
new file mode 100644
index 000000000000..9a61ecc5451b
--- /dev/null
+++ b/fs/udf/directory.c
@@ -0,0 +1,343 @@
+/*
+ * directory.c
+ *
+ * PURPOSE
+ *	Directory related functions
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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.
+ */
+
+#include "udfdecl.h"
+#include "udf_i.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/buffer_head.h>
+
+#if 0
+static uint8_t *
+udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size,
+		kernel_lb_addr fe_loc, int *pos, int *offset,
+		struct buffer_head **bh, int *error)
+{
+	int loffset = *offset;
+	int block;
+	uint8_t *ad;
+	int remainder;
+
+	*error = 0;
+
+	ad = (uint8_t *)(*bh)->b_data + *offset;
+	*offset += ad_size;
+
+	if (!ad)
+	{
+		udf_release_data(*bh);
+		*error = 1;
+		return NULL;
+	}
+
+	if (*offset == dir->i_sb->s_blocksize)
+	{
+		udf_release_data(*bh);
+		block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
+		if (!block)
+			return NULL;
+		if (!(*bh = udf_tread(dir->i_sb, block)))
+			return NULL;
+	}
+	else if (*offset > dir->i_sb->s_blocksize)
+	{
+		ad = tmpad;
+
+		remainder = dir->i_sb->s_blocksize - loffset;
+		memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder);
+
+		udf_release_data(*bh);
+		block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
+		if (!block)
+			return NULL;
+		if (!((*bh) = udf_tread(dir->i_sb, block)))
+			return NULL;
+
+		memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder);
+		*offset = ad_size - remainder;
+	}
+	return ad;
+}
+#endif
+
+struct fileIdentDesc *
+udf_fileident_read(struct inode *dir, loff_t *nf_pos,
+	struct udf_fileident_bh *fibh,
+	struct fileIdentDesc *cfi,
+	kernel_lb_addr *bloc, uint32_t *extoffset, 
+	kernel_lb_addr *eloc, uint32_t *elen,
+	uint32_t *offset, struct buffer_head **bh)
+{
+	struct fileIdentDesc *fi;
+	int i, num, block;
+	struct buffer_head * tmp, * bha[16];
+
+	fibh->soffset = fibh->eoffset;
+
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		fi = udf_get_fileident(UDF_I_DATA(dir) -
+			(UDF_I_EFE(dir) ?
+				sizeof(struct extendedFileEntry) :
+				sizeof(struct fileEntry)),
+			dir->i_sb->s_blocksize, &(fibh->eoffset));
+
+		if (!fi)
+			return NULL;
+
+		*nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
+
+		memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
+
+		return fi;
+	}
+
+	if (fibh->eoffset == dir->i_sb->s_blocksize)
+	{
+		int lextoffset = *extoffset;
+
+		if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) !=
+			(EXT_RECORDED_ALLOCATED >> 30))
+		{
+			return NULL;
+		}
+
+		block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
+
+		(*offset) ++;
+
+		if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
+			*offset = 0;
+		else
+			*extoffset = lextoffset;
+
+		udf_release_data(fibh->sbh);
+		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
+			return NULL;
+		fibh->soffset = fibh->eoffset = 0;
+
+		if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
+		{
+			i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
+			if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits))
+				i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset;
+			for (num=0; i>0; i--)
+			{
+				block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i);
+				tmp = udf_tgetblk(dir->i_sb, block);
+				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
+					bha[num++] = tmp;
+				else
+					brelse(tmp);
+			}
+			if (num)
+			{
+				ll_rw_block(READA, num, bha);
+				for (i=0; i<num; i++)
+					brelse(bha[i]);
+			}
+		}
+	}
+	else if (fibh->sbh != fibh->ebh)
+	{
+		udf_release_data(fibh->sbh);
+		fibh->sbh = fibh->ebh;
+	}
+
+	fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize,
+		&(fibh->eoffset));
+
+	if (!fi)
+		return NULL;
+
+	*nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
+
+	if (fibh->eoffset <= dir->i_sb->s_blocksize)
+	{
+		memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
+	}
+	else if (fibh->eoffset > dir->i_sb->s_blocksize)
+	{
+		int lextoffset = *extoffset;
+
+		if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) !=
+			(EXT_RECORDED_ALLOCATED >> 30))
+		{
+			return NULL;
+		}
+
+		block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
+
+		(*offset) ++;
+
+		if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
+			*offset = 0;
+		else
+			*extoffset = lextoffset;
+
+		fibh->soffset -= dir->i_sb->s_blocksize;
+		fibh->eoffset -= dir->i_sb->s_blocksize;
+
+		if (!(fibh->ebh = udf_tread(dir->i_sb, block)))
+			return NULL;
+
+		if (sizeof(struct fileIdentDesc) > - fibh->soffset)
+		{
+			int fi_len;
+
+			memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset);
+			memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data,
+				sizeof(struct fileIdentDesc) + fibh->soffset);
+
+			fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent +
+				le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3;
+
+			*nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2);
+			fibh->eoffset = fibh->soffset + fi_len;
+		}
+		else
+		{
+			memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
+		}
+	}
+	return fi;
+}
+
+struct fileIdentDesc * 
+udf_get_fileident(void * buffer, int bufsize, int * offset)
+{
+	struct fileIdentDesc *fi;
+	int lengthThisIdent;
+	uint8_t * ptr;
+	int padlen;
+
+	if ( (!buffer) || (!offset) ) {
+		udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset);
+		return NULL;
+	}
+
+	ptr = buffer;
+
+	if ( (*offset > 0) && (*offset < bufsize) ) {
+		ptr += *offset;
+	}
+	fi=(struct fileIdentDesc *)ptr;
+	if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID)
+	{
+		udf_debug("0x%x != TAG_IDENT_FID\n",
+			le16_to_cpu(fi->descTag.tagIdent));
+		udf_debug("offset: %u sizeof: %lu bufsize: %u\n",
+			*offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize);
+		return NULL;
+	}
+	if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize )
+	{
+		lengthThisIdent = sizeof(struct fileIdentDesc);
+	}
+	else
+		lengthThisIdent = sizeof(struct fileIdentDesc) +
+			fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
+
+	/* we need to figure padding, too! */
+	padlen = lengthThisIdent % UDF_NAME_PAD;
+	if (padlen)
+		lengthThisIdent += (UDF_NAME_PAD - padlen);
+	*offset = *offset + lengthThisIdent;
+
+	return fi;
+}
+
+#if 0
+static extent_ad *
+udf_get_fileextent(void * buffer, int bufsize, int * offset)
+{
+	extent_ad * ext;
+	struct fileEntry *fe;
+	uint8_t * ptr;
+
+	if ( (!buffer) || (!offset) )
+	{
+		printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n");
+		return NULL;
+	}
+
+	fe = (struct fileEntry *)buffer;
+
+	if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE )
+	{
+		udf_debug("0x%x != TAG_IDENT_FE\n",
+			le16_to_cpu(fe->descTag.tagIdent));
+		return NULL;
+	}
+
+	ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr);
+
+	if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) )
+	{
+		ptr += *offset;
+	}
+
+	ext = (extent_ad *)ptr;
+
+	*offset = *offset + sizeof(extent_ad);
+	return ext;
+}
+#endif
+
+short_ad *
+udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc)
+{
+	short_ad *sa;
+
+	if ( (!ptr) || (!offset) )
+	{
+		printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n");
+		return NULL;
+	}
+
+	if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) )
+		return NULL;
+	else if ((sa = (short_ad *)ptr)->extLength == 0)
+		return NULL;
+
+	if (inc)
+		*offset += sizeof(short_ad);
+	return sa;
+}
+
+long_ad *
+udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc)
+{
+	long_ad *la;
+
+	if ( (!ptr) || (!offset) ) 
+	{
+		printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n");
+		return NULL;
+	}
+
+	if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) )
+		return NULL;
+	else if ((la = (long_ad *)ptr)->extLength == 0)
+		return NULL;
+
+	if (inc)
+		*offset += sizeof(long_ad);
+	return la;
+}
diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h
new file mode 100644
index 000000000000..f81f2ebbf508
--- /dev/null
+++ b/fs/udf/ecma_167.h
@@ -0,0 +1,864 @@
+/*
+ * ecma_167.h
+ *
+ * This file is based on ECMA-167 3rd edition (June 1997)
+ * http://www.ecma.ch
+ *
+ * Copyright (c) 2001-2002  Ben Fennema <bfennema@falcon.csc.calpoly.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/types.h>
+
+#ifndef _ECMA_167_H
+#define _ECMA_167_H 1
+
+/* Character set specification (ECMA 167r3 1/7.2.1) */
+typedef struct
+{
+	uint8_t		charSetType;
+	uint8_t		charSetInfo[63];
+} __attribute__ ((packed)) charspec;
+
+/* Character Set Type (ECMA 167r3 1/7.2.1.1) */
+#define CHARSPEC_TYPE_CS0		0x00	/* (1/7.2.2) */
+#define CHARSPEC_TYPE_CS1		0x01	/* (1/7.2.3) */
+#define CHARSPEC_TYPE_CS2		0x02	/* (1/7.2.4) */
+#define CHARSPEC_TYPE_CS3		0x03	/* (1/7.2.5) */
+#define CHARSPEC_TYPE_CS4		0x04	/* (1/7.2.6) */
+#define CHARSPEC_TYPE_CS5		0x05	/* (1/7.2.7) */
+#define CHARSPEC_TYPE_CS6		0x06	/* (1/7.2.8) */
+#define CHARSPEC_TYPE_CS7		0x07	/* (1/7.2.9) */
+#define CHARSPEC_TYPE_CS8		0x08	/* (1/7.2.10) */
+
+typedef uint8_t		dstring;
+
+/* Timestamp (ECMA 167r3 1/7.3) */
+typedef struct
+{
+	__le16		typeAndTimezone;
+	__le16		year;
+	uint8_t		month;
+	uint8_t		day;
+	uint8_t		hour;
+	uint8_t		minute;
+	uint8_t		second;
+	uint8_t		centiseconds;
+	uint8_t		hundredsOfMicroseconds;
+	uint8_t		microseconds;
+} __attribute__ ((packed)) timestamp;
+
+typedef struct
+{
+	uint16_t	typeAndTimezone;
+	int16_t		year;
+	uint8_t		month;
+	uint8_t		day;
+	uint8_t		hour;
+	uint8_t		minute;
+	uint8_t		second;
+	uint8_t		centiseconds;
+	uint8_t		hundredsOfMicroseconds;
+	uint8_t		microseconds;
+} __attribute__ ((packed)) kernel_timestamp;
+
+/* Type and Time Zone (ECMA 167r3 1/7.3.1) */
+#define TIMESTAMP_TYPE_MASK		0xF000
+#define TIMESTAMP_TYPE_CUT		0x0000
+#define TIMESTAMP_TYPE_LOCAL		0x1000
+#define TIMESTAMP_TYPE_AGREEMENT	0x2000
+#define TIMESTAMP_TIMEZONE_MASK		0x0FFF
+
+/* Entity identifier (ECMA 167r3 1/7.4) */
+typedef struct
+{
+	uint8_t		flags;
+	uint8_t		ident[23];
+	uint8_t		identSuffix[8];
+} __attribute__ ((packed)) regid;
+
+/* Flags (ECMA 167r3 1/7.4.1) */
+#define ENTITYID_FLAGS_DIRTY		0x00
+#define ENTITYID_FLAGS_PROTECTED	0x01
+
+/* Volume Structure Descriptor (ECMA 167r3 2/9.1) */
+#define VSD_STD_ID_LEN			5
+struct volStructDesc
+{
+	uint8_t		structType;
+	uint8_t		stdIdent[VSD_STD_ID_LEN];
+	uint8_t		structVersion;
+	uint8_t		structData[2041];
+} __attribute__ ((packed));
+
+/* Standard Identifier (EMCA 167r2 2/9.1.2) */
+#define VSD_STD_ID_NSR02		"NSR02"	/* (3/9.1) */
+
+/* Standard Identifier (ECMA 167r3 2/9.1.2) */
+#define VSD_STD_ID_BEA01		"BEA01"	/* (2/9.2) */
+#define VSD_STD_ID_BOOT2		"BOOT2"	/* (2/9.4) */
+#define VSD_STD_ID_CD001		"CD001"	/* (ECMA-119) */
+#define VSD_STD_ID_CDW02		"CDW02"	/* (ECMA-168) */
+#define VSD_STD_ID_NSR03		"NSR03"	/* (3/9.1) */
+#define VSD_STD_ID_TEA01		"TEA01"	/* (2/9.3) */
+
+/* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */
+struct beginningExtendedAreaDesc
+{
+	uint8_t		structType;
+	uint8_t		stdIdent[VSD_STD_ID_LEN];
+	uint8_t		structVersion;
+	uint8_t		structData[2041];
+} __attribute__ ((packed));
+
+/* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */
+struct terminatingExtendedAreaDesc
+{
+	uint8_t		structType;
+	uint8_t		stdIdent[VSD_STD_ID_LEN];
+	uint8_t		structVersion;
+	uint8_t		structData[2041];
+} __attribute__ ((packed));
+
+/* Boot Descriptor (ECMA 167r3 2/9.4) */
+struct bootDesc
+{
+	uint8_t		structType;
+	uint8_t		stdIdent[VSD_STD_ID_LEN];
+	uint8_t		structVersion;
+	uint8_t		reserved1;
+	regid		archType;
+	regid		bootIdent;
+	__le32		bootExtLocation;
+	__le32		bootExtLength;
+	__le64		loadAddress;
+	__le64		startAddress;
+	timestamp	descCreationDateAndTime;
+	__le16		flags;
+	uint8_t		reserved2[32];
+	uint8_t		bootUse[1906];
+} __attribute__ ((packed));
+
+/* Flags (ECMA 167r3 2/9.4.12) */
+#define BOOT_FLAGS_ERASE		0x01
+
+/* Extent Descriptor (ECMA 167r3 3/7.1) */
+typedef struct
+{
+	__le32		extLength;
+	__le32		extLocation;
+} __attribute__ ((packed)) extent_ad;
+
+typedef struct
+{
+	uint32_t	extLength;
+	uint32_t	extLocation;
+} kernel_extent_ad;
+
+/* Descriptor Tag (ECMA 167r3 3/7.2) */
+typedef struct
+{
+	__le16		tagIdent;
+	__le16		descVersion;
+	uint8_t		tagChecksum;
+	uint8_t		reserved;
+	__le16		tagSerialNum;
+	__le16		descCRC;
+	__le16		descCRCLength;
+	__le32		tagLocation;
+} __attribute__ ((packed)) tag;
+
+/* Tag Identifier (ECMA 167r3 3/7.2.1) */
+#define TAG_IDENT_PVD			0x0001
+#define TAG_IDENT_AVDP			0x0002
+#define TAG_IDENT_VDP			0x0003
+#define TAG_IDENT_IUVD			0x0004
+#define TAG_IDENT_PD			0x0005
+#define TAG_IDENT_LVD			0x0006
+#define TAG_IDENT_USD			0x0007
+#define TAG_IDENT_TD			0x0008
+#define TAG_IDENT_LVID			0x0009
+
+/* NSR Descriptor (ECMA 167r3 3/9.1) */
+struct NSRDesc
+{
+	uint8_t		structType;
+	uint8_t		stdIdent[VSD_STD_ID_LEN];
+	uint8_t		structVersion;
+	uint8_t		reserved;
+	uint8_t		structData[2040];
+} __attribute__ ((packed));
+	
+/* Primary Volume Descriptor (ECMA 167r3 3/10.1) */
+struct primaryVolDesc
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+	__le32		primaryVolDescNum;
+	dstring		volIdent[32];
+	__le16		volSeqNum;
+	__le16		maxVolSeqNum;
+	__le16		interchangeLvl;
+	__le16		maxInterchangeLvl;
+	__le32		charSetList;
+	__le32		maxCharSetList;
+	dstring		volSetIdent[128];
+	charspec	descCharSet;
+	charspec	explanatoryCharSet;
+	extent_ad	volAbstract;
+	extent_ad	volCopyright;
+	regid		appIdent;
+	timestamp	recordingDateAndTime;
+	regid		impIdent;
+	uint8_t		impUse[64];
+	__le32		predecessorVolDescSeqLocation;
+	__le16		flags;
+	uint8_t		reserved[22];
+} __attribute__ ((packed));
+
+/* Flags (ECMA 167r3 3/10.1.21) */
+#define PVD_FLAGS_VSID_COMMON		0x0001
+
+/* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */
+struct anchorVolDescPtr
+{
+	tag		descTag;
+	extent_ad	mainVolDescSeqExt;
+	extent_ad	reserveVolDescSeqExt;
+	uint8_t	 	reserved[480];
+} __attribute__ ((packed));
+
+/* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */
+struct volDescPtr
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+	extent_ad	nextVolDescSeqExt;
+	uint8_t		reserved[484];
+} __attribute__ ((packed));
+
+/* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */
+struct impUseVolDesc
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+	regid		impIdent;
+	uint8_t		impUse[460];
+} __attribute__ ((packed));
+
+/* Partition Descriptor (ECMA 167r3 3/10.5) */
+struct partitionDesc
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+	__le16		partitionFlags;
+	__le16		partitionNumber;
+	regid		partitionContents;
+	uint8_t		partitionContentsUse[128];
+	__le32		accessType;
+	__le32		partitionStartingLocation;
+	__le32		partitionLength;
+	regid		impIdent;
+	uint8_t		impUse[128];
+	uint8_t		reserved[156];
+} __attribute__ ((packed));
+
+/* Partition Flags (ECMA 167r3 3/10.5.3) */
+#define PD_PARTITION_FLAGS_ALLOC	0x0001
+
+/* Partition Contents (ECMA 167r2 3/10.5.3) */
+#define PD_PARTITION_CONTENTS_NSR02	"+NSR02"
+
+/* Partition Contents (ECMA 167r3 3/10.5.5) */
+#define PD_PARTITION_CONTENTS_FDC01	"+FDC01"
+#define PD_PARTITION_CONTENTS_CD001	"+CD001"
+#define PD_PARTITION_CONTENTS_CDW02	"+CDW02"
+#define PD_PARTITION_CONTENTS_NSR03	"+NSR03"
+
+/* Access Type (ECMA 167r3 3/10.5.7) */
+#define PD_ACCESS_TYPE_NONE		0x00000000
+#define PD_ACCESS_TYPE_READ_ONLY	0x00000001
+#define PD_ACCESS_TYPE_WRITE_ONCE	0x00000002
+#define PD_ACCESS_TYPE_REWRITABLE	0x00000003
+#define PD_ACCESS_TYPE_OVERWRITABLE	0x00000004
+
+/* Logical Volume Descriptor (ECMA 167r3 3/10.6) */
+struct logicalVolDesc
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+	charspec	descCharSet;
+	dstring		logicalVolIdent[128];
+	__le32		logicalBlockSize;
+	regid		domainIdent;
+	uint8_t		logicalVolContentsUse[16];
+	__le32		mapTableLength;
+	__le32		numPartitionMaps;
+	regid		impIdent;
+	uint8_t		impUse[128];
+	extent_ad	integritySeqExt;
+	uint8_t		partitionMaps[0];
+} __attribute__ ((packed));
+
+/* Generic Partition Map (ECMA 167r3 3/10.7.1) */
+struct genericPartitionMap
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		partitionMapping[0];
+} __attribute__ ((packed));
+
+/* Partition Map Type (ECMA 167r3 3/10.7.1.1) */
+#define GP_PARTITION_MAP_TYPE_UNDEF	0x00
+#define GP_PARTIITON_MAP_TYPE_1		0x01
+#define GP_PARTITION_MAP_TYPE_2		0x02
+
+/* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */
+struct genericPartitionMap1
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+} __attribute__ ((packed));
+
+/* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */
+struct genericPartitionMap2
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength; 
+	uint8_t		partitionIdent[62];
+} __attribute__ ((packed));
+
+/* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */
+struct unallocSpaceDesc
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+	__le32		numAllocDescs;
+	extent_ad	allocDescs[0];
+} __attribute__ ((packed));
+
+/* Terminating Descriptor (ECMA 167r3 3/10.9) */
+struct terminatingDesc
+{
+	tag		descTag;
+	uint8_t		reserved[496];
+} __attribute__ ((packed));
+
+/* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */
+struct logicalVolIntegrityDesc
+{
+	tag		descTag;
+	timestamp	recordingDateAndTime;
+	__le32		integrityType;
+	extent_ad	nextIntegrityExt;
+	uint8_t		logicalVolContentsUse[32];
+	__le32		numOfPartitions;
+	__le32		lengthOfImpUse;
+	__le32		freeSpaceTable[0];
+	__le32		sizeTable[0];
+	uint8_t		impUse[0];
+} __attribute__ ((packed));
+
+/* Integrity Type (ECMA 167r3 3/10.10.3) */
+#define LVID_INTEGRITY_TYPE_OPEN	0x00000000
+#define LVID_INTEGRITY_TYPE_CLOSE	0x00000001
+
+/* Recorded Address (ECMA 167r3 4/7.1) */
+typedef struct 
+{
+	__le32		logicalBlockNum;
+	__le16	 	partitionReferenceNum;
+} __attribute__ ((packed)) lb_addr;
+
+/* ... and its in-core analog */
+typedef struct 
+{
+	uint32_t		logicalBlockNum;
+	uint16_t	 	partitionReferenceNum;
+} kernel_lb_addr;
+
+/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
+typedef struct
+{
+        __le32		extLength;
+        __le32		extPosition;
+} __attribute__ ((packed)) short_ad;
+
+/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
+typedef struct
+{
+	__le32		extLength;
+	lb_addr		extLocation;
+	uint8_t		impUse[6];
+} __attribute__ ((packed)) long_ad;
+
+typedef struct
+{
+	uint32_t	extLength;
+	kernel_lb_addr	extLocation;
+	uint8_t		impUse[6];
+} kernel_long_ad;
+
+/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
+typedef struct
+{
+	__le32		extLength;
+	__le32		recordedLength;
+	__le32		informationLength;
+	lb_addr		extLocation;
+} __attribute__ ((packed)) ext_ad;
+
+typedef struct
+{
+	uint32_t	extLength;
+	uint32_t	recordedLength;
+	uint32_t	informationLength;
+	kernel_lb_addr	extLocation;
+} kernel_ext_ad;
+
+/* Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */
+
+/* Tag Identifier (ECMA 167r3 4/7.2.1) */
+#define TAG_IDENT_FSD			0x0100
+#define TAG_IDENT_FID			0x0101
+#define TAG_IDENT_AED			0x0102
+#define TAG_IDENT_IE			0x0103
+#define TAG_IDENT_TE			0x0104
+#define TAG_IDENT_FE			0x0105
+#define TAG_IDENT_EAHD			0x0106
+#define TAG_IDENT_USE			0x0107
+#define TAG_IDENT_SBD			0x0108
+#define TAG_IDENT_PIE			0x0109
+#define TAG_IDENT_EFE			0x010A
+
+/* File Set Descriptor (ECMA 167r3 4/14.1) */
+struct fileSetDesc
+{
+	tag		descTag;
+	timestamp	recordingDateAndTime;
+	__le16		interchangeLvl;
+	__le16		maxInterchangeLvl;
+	__le32		charSetList;
+	__le32		maxCharSetList;
+	__le32		fileSetNum;
+	__le32		fileSetDescNum;
+	charspec	logicalVolIdentCharSet;
+	dstring		logicalVolIdent[128];
+	charspec	fileSetCharSet;
+	dstring		fileSetIdent[32];
+	dstring		copyrightFileIdent[32];
+	dstring		abstractFileIdent[32];
+	long_ad		rootDirectoryICB;
+	regid		domainIdent;
+	long_ad		nextExt;
+	long_ad		streamDirectoryICB;
+	uint8_t		reserved[32];
+} __attribute__ ((packed));
+
+/* Partition Header Descriptor (ECMA 167r3 4/14.3) */
+struct partitionHeaderDesc
+{
+	short_ad	unallocSpaceTable;
+	short_ad	unallocSpaceBitmap;
+	short_ad	partitionIntegrityTable;
+	short_ad	freedSpaceTable;
+	short_ad	freedSpaceBitmap;
+	uint8_t		reserved[88];
+} __attribute__ ((packed));
+
+/* File Identifier Descriptor (ECMA 167r3 4/14.4) */
+struct fileIdentDesc
+{
+	tag		descTag;
+	__le16		fileVersionNum;
+	uint8_t		fileCharacteristics;
+	uint8_t		lengthFileIdent;
+	long_ad		icb;
+	__le16		lengthOfImpUse;
+	uint8_t		impUse[0];
+	uint8_t		fileIdent[0];
+	uint8_t		padding[0];
+} __attribute__ ((packed));
+
+/* File Characteristics (ECMA 167r3 4/14.4.3) */
+#define FID_FILE_CHAR_HIDDEN		0x01
+#define FID_FILE_CHAR_DIRECTORY		0x02
+#define FID_FILE_CHAR_DELETED		0x04
+#define FID_FILE_CHAR_PARENT		0x08
+#define FID_FILE_CHAR_METADATA		0x10
+
+/* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */
+struct allocExtDesc
+{
+	tag		descTag;
+	__le32		previousAllocExtLocation;
+	__le32		lengthAllocDescs;
+} __attribute__ ((packed));
+
+/* ICB Tag (ECMA 167r3 4/14.6) */
+typedef struct
+{
+	__le32		priorRecordedNumDirectEntries;
+	__le16		strategyType;
+	__le16		strategyParameter;
+	__le16		numEntries;
+	uint8_t		reserved;
+	uint8_t		fileType;
+	lb_addr		parentICBLocation;
+	__le16		flags;
+} __attribute__ ((packed)) icbtag;
+
+/* Strategy Type (ECMA 167r3 4/14.6.2) */
+#define ICBTAG_STRATEGY_TYPE_UNDEF	0x0000
+#define ICBTAG_STRATEGY_TYPE_1		0x0001
+#define ICBTAG_STRATEGY_TYPE_2		0x0002
+#define ICBTAG_STRATEGY_TYPE_3		0x0003
+#define ICBTAG_STRATEGY_TYPE_4		0x0004
+
+/* File Type (ECMA 167r3 4/14.6.6) */
+#define ICBTAG_FILE_TYPE_UNDEF		0x00
+#define ICBTAG_FILE_TYPE_USE		0x01
+#define ICBTAG_FILE_TYPE_PIE		0x02
+#define ICBTAG_FILE_TYPE_IE		0x03
+#define ICBTAG_FILE_TYPE_DIRECTORY	0x04
+#define ICBTAG_FILE_TYPE_REGULAR	0x05
+#define ICBTAG_FILE_TYPE_BLOCK		0x06
+#define ICBTAG_FILE_TYPE_CHAR		0x07
+#define ICBTAG_FILE_TYPE_EA		0x08
+#define ICBTAG_FILE_TYPE_FIFO		0x09
+#define ICBTAG_FILE_TYPE_SOCKET		0x0A
+#define ICBTAG_FILE_TYPE_TE		0x0B
+#define ICBTAG_FILE_TYPE_SYMLINK	0x0C
+#define ICBTAG_FILE_TYPE_STREAMDIR	0x0D
+
+/* Flags (ECMA 167r3 4/14.6.8) */
+#define ICBTAG_FLAG_AD_MASK		0x0007
+#define ICBTAG_FLAG_AD_SHORT		0x0000
+#define ICBTAG_FLAG_AD_LONG		0x0001
+#define ICBTAG_FLAG_AD_EXTENDED		0x0002
+#define ICBTAG_FLAG_AD_IN_ICB		0x0003
+#define ICBTAG_FLAG_SORTED		0x0008
+#define ICBTAG_FLAG_NONRELOCATABLE	0x0010
+#define ICBTAG_FLAG_ARCHIVE		0x0020
+#define ICBTAG_FLAG_SETUID		0x0040
+#define ICBTAG_FLAG_SETGID		0x0080
+#define ICBTAG_FLAG_STICKY		0x0100
+#define ICBTAG_FLAG_CONTIGUOUS		0x0200
+#define ICBTAG_FLAG_SYSTEM		0x0400
+#define ICBTAG_FLAG_TRANSFORMED		0x0800
+#define ICBTAG_FLAG_MULTIVERSIONS	0x1000
+#define ICBTAG_FLAG_STREAM		0x2000
+
+/* Indirect Entry (ECMA 167r3 4/14.7) */
+struct indirectEntry
+{
+	tag		descTag;
+	icbtag		icbTag;
+	long_ad		indirectICB;
+} __attribute__ ((packed));
+
+/* Terminal Entry (ECMA 167r3 4/14.8) */
+struct terminalEntry
+{
+	tag		descTag;
+	icbtag		icbTag;
+} __attribute__ ((packed));
+
+/* File Entry (ECMA 167r3 4/14.9) */
+struct fileEntry
+{
+	tag		descTag;
+	icbtag		icbTag;
+	__le32		uid;
+	__le32		gid;
+	__le32		permissions;
+	__le16		fileLinkCount;
+	uint8_t		recordFormat;
+	uint8_t		recordDisplayAttr;
+	__le32		recordLength;
+	__le64		informationLength;
+	__le64		logicalBlocksRecorded;
+	timestamp	accessTime;
+	timestamp	modificationTime;
+	timestamp	attrTime;
+	__le32		checkpoint;
+	long_ad		extendedAttrICB;
+	regid		impIdent;
+	__le64		uniqueID;
+	__le32		lengthExtendedAttr;
+	__le32		lengthAllocDescs;
+	uint8_t		extendedAttr[0];
+	uint8_t		allocDescs[0];
+} __attribute__ ((packed));
+
+/* Permissions (ECMA 167r3 4/14.9.5) */
+#define FE_PERM_O_EXEC			0x00000001U
+#define FE_PERM_O_WRITE			0x00000002U
+#define FE_PERM_O_READ			0x00000004U
+#define FE_PERM_O_CHATTR		0x00000008U
+#define FE_PERM_O_DELETE		0x00000010U
+#define FE_PERM_G_EXEC			0x00000020U
+#define FE_PERM_G_WRITE			0x00000040U
+#define FE_PERM_G_READ			0x00000080U
+#define FE_PERM_G_CHATTR		0x00000100U
+#define FE_PERM_G_DELETE		0x00000200U
+#define FE_PERM_U_EXEC			0x00000400U
+#define FE_PERM_U_WRITE			0x00000800U
+#define FE_PERM_U_READ			0x00001000U
+#define FE_PERM_U_CHATTR		0x00002000U
+#define FE_PERM_U_DELETE		0x00004000U
+
+/* Record Format (ECMA 167r3 4/14.9.7) */
+#define FE_RECORD_FMT_UNDEF		0x00
+#define FE_RECORD_FMT_FIXED_PAD		0x01
+#define FE_RECORD_FMT_FIXED		0x02
+#define FE_RECORD_FMT_VARIABLE8		0x03
+#define FE_RECORD_FMT_VARIABLE16	0x04
+#define FE_RECORD_FMT_VARIABLE16_MSB	0x05
+#define FE_RECORD_FMT_VARIABLE32	0x06
+#define FE_RECORD_FMT_PRINT		0x07
+#define FE_RECORD_FMT_LF		0x08
+#define FE_RECORD_FMT_CR		0x09
+#define FE_RECORD_FMT_CRLF		0x0A
+#define FE_RECORD_FMT_LFCR		0x0B
+
+/* Record Display Attributes (ECMA 167r3 4/14.9.8) */
+#define FE_RECORD_DISPLAY_ATTR_UNDEF	0x00
+#define FE_RECORD_DISPLAY_ATTR_1	0x01
+#define FE_RECORD_DISPLAY_ATTR_2	0x02
+#define FE_RECORD_DISPLAY_ATTR_3	0x03
+
+/* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */
+struct extendedAttrHeaderDesc
+{
+	tag		descTag;
+	__le32		impAttrLocation;
+	__le32		appAttrLocation;
+} __attribute__ ((packed));
+
+/* Generic Format (ECMA 167r3 4/14.10.2) */
+struct genericFormat
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	uint8_t		attrData[0];
+} __attribute__ ((packed));
+
+/* Character Set Information (ECMA 167r3 4/14.10.3) */
+struct charSetInfo
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le32		escapeSeqLength;
+	uint8_t		charSetType;
+	uint8_t		escapeSeq[0];
+} __attribute__ ((packed));
+
+/* Alternate Permissions (ECMA 167r3 4/14.10.4) */
+struct altPerms
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le16		ownerIdent;
+	__le16		groupIdent;
+	__le16		permission;
+} __attribute__ ((packed));
+
+/* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */
+struct fileTimesExtAttr
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le32		dataLength;
+	__le32		fileTimeExistence;
+	uint8_t		fileTimes;
+} __attribute__ ((packed));
+
+/* FileTimeExistence (ECMA 167r3 4/14.10.5.6) */
+#define FTE_CREATION			0x00000001
+#define FTE_DELETION			0x00000004
+#define FTE_EFFECTIVE			0x00000008
+#define FTE_BACKUP			0x00000002
+
+/* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */
+struct infoTimesExtAttr
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le32		dataLength;
+	__le32		infoTimeExistence;
+	uint8_t		infoTimes[0];
+} __attribute__ ((packed));
+
+/* Device Specification (ECMA 167r3 4/14.10.7) */
+struct deviceSpec
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le32		impUseLength;
+	__le32		majorDeviceIdent;
+	__le32		minorDeviceIdent;
+	uint8_t		impUse[0];
+} __attribute__ ((packed));
+
+/* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */
+struct impUseExtAttr
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le32		impUseLength;
+	regid		impIdent;
+	uint8_t		impUse[0];
+} __attribute__ ((packed));
+
+/* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */
+struct appUseExtAttr
+{
+	__le32		attrType;
+	uint8_t		attrSubtype;
+	uint8_t		reserved[3];
+	__le32		attrLength;
+	__le32		appUseLength;
+	regid		appIdent;
+	uint8_t		appUse[0];
+} __attribute__ ((packed));
+
+#define EXTATTR_CHAR_SET		1
+#define EXTATTR_ALT_PERMS		3
+#define EXTATTR_FILE_TIMES		5
+#define EXTATTR_INFO_TIMES		6
+#define EXTATTR_DEV_SPEC		12
+#define EXTATTR_IMP_USE			2048
+#define EXTATTR_APP_USE			65536
+
+
+/* Unallocated Space Entry (ECMA 167r3 4/14.11) */
+struct unallocSpaceEntry
+{
+	tag		descTag;
+	icbtag		icbTag;
+	__le32		lengthAllocDescs;
+	uint8_t		allocDescs[0];
+} __attribute__ ((packed));
+
+/* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */
+struct spaceBitmapDesc
+{
+	tag		descTag;
+	__le32		numOfBits;
+	__le32		numOfBytes;
+	uint8_t		bitmap[0];
+} __attribute__ ((packed));
+
+/* Partition Integrity Entry (ECMA 167r3 4/14.13) */
+struct partitionIntegrityEntry
+{
+	tag		descTag;
+	icbtag		icbTag;
+	timestamp	recordingDateAndTime;
+	uint8_t		integrityType;
+	uint8_t		reserved[175];
+	regid		impIdent;
+	uint8_t		impUse[256];
+} __attribute__ ((packed));
+
+/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
+
+/* Extent Length (ECMA 167r3 4/14.14.1.1) */
+#define EXT_RECORDED_ALLOCATED		0x00000000
+#define EXT_NOT_RECORDED_ALLOCATED	0x40000000
+#define EXT_NOT_RECORDED_NOT_ALLOCATED	0x80000000
+#define EXT_NEXT_EXTENT_ALLOCDECS	0xC0000000
+
+/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
+
+/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
+
+/* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */
+struct logicalVolHeaderDesc
+{
+	__le64		uniqueID;
+	uint8_t		reserved[24];
+} __attribute__ ((packed));
+
+/* Path Component (ECMA 167r3 4/14.16.1) */
+struct pathComponent
+{
+	uint8_t		componentType;
+	uint8_t		lengthComponentIdent;
+	__le16		componentFileVersionNum;
+	dstring		componentIdent[0];
+} __attribute__ ((packed));
+
+/* File Entry (ECMA 167r3 4/14.17) */
+struct extendedFileEntry
+{
+	tag		descTag;
+	icbtag		icbTag;
+	__le32		uid;
+	__le32		gid;
+	__le32		permissions;
+	__le16		fileLinkCount;
+	uint8_t		recordFormat;
+	uint8_t		recordDisplayAttr;
+	__le32		recordLength;
+	__le64		informationLength;
+	__le64		objectSize;
+	__le64		logicalBlocksRecorded;
+	timestamp	accessTime;
+	timestamp	modificationTime;
+	timestamp	createTime;
+	timestamp	attrTime;
+	__le32		checkpoint;
+	__le32		reserved;
+	long_ad		extendedAttrICB;
+	long_ad		streamDirectoryICB;
+	regid		impIdent;
+	__le64		uniqueID;
+	__le32		lengthExtendedAttr;
+	__le32		lengthAllocDescs;
+	uint8_t		extendedAttr[0];
+	uint8_t		allocDescs[0];
+} __attribute__ ((packed));
+
+#endif /* _ECMA_167_H */
diff --git a/fs/udf/file.c b/fs/udf/file.c
new file mode 100644
index 000000000000..2faa4172b9f7
--- /dev/null
+++ b/fs/udf/file.c
@@ -0,0 +1,270 @@
+/*
+ * file.c
+ *
+ * PURPOSE
+ *  File handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *  E-mail regarding any portion of the Linux UDF file system should be
+ *  directed to the development team mailing list (run by majordomo):
+ *    linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998-1999 Dave Boynton
+ *  (C) 1998-2004 Ben Fennema
+ *  (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  10/02/98 dgb  Attempt to integrate into udf.o
+ *  10/07/98      Switched to using generic_readpage, etc., like isofs
+ *                And it works!
+ *  12/06/98 blf  Added udf_file_read. uses generic_file_read for all cases but
+ *                ICBTAG_FLAG_AD_IN_ICB.
+ *  04/06/99      64 bit file handling on 32 bit systems taken from ext2 file.c
+ *  05/12/99      Preliminary file write support
+ */
+
+#include "udfdecl.h"
+#include <linux/fs.h>
+#include <linux/udf_fs.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/string.h> /* memset */
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/pagemap.h>
+#include <linux/buffer_head.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+static int udf_adinicb_readpage(struct file *file, struct page * page)
+{
+	struct inode *inode = page->mapping->host;
+	char *kaddr;
+
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+
+	kaddr = kmap(page);
+	memset(kaddr, 0, PAGE_CACHE_SIZE);
+	memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), inode->i_size);
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	kunmap(page);
+	unlock_page(page);
+	return 0;
+}
+
+static int udf_adinicb_writepage(struct page *page, struct writeback_control *wbc)
+{
+	struct inode *inode = page->mapping->host;
+	char *kaddr;
+
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+
+	kaddr = kmap(page);
+	memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), kaddr, inode->i_size);
+	mark_inode_dirty(inode);
+	SetPageUptodate(page);
+	kunmap(page);
+	unlock_page(page);
+	return 0;
+}
+
+static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	kmap(page);
+	return 0;
+}
+
+static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+	struct inode *inode = page->mapping->host;
+	char *kaddr = page_address(page);
+
+	memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset,
+		kaddr + offset, to - offset);
+	mark_inode_dirty(inode);
+	SetPageUptodate(page);
+	kunmap(page);
+	/* only one page here */
+	if (to > inode->i_size)
+		inode->i_size = to;
+	return 0;
+}
+
+struct address_space_operations udf_adinicb_aops = {
+	.readpage		= udf_adinicb_readpage,
+	.writepage		= udf_adinicb_writepage,
+	.sync_page		= block_sync_page,
+	.prepare_write		= udf_adinicb_prepare_write,
+	.commit_write		= udf_adinicb_commit_write,
+};
+
+static ssize_t udf_file_write(struct file * file, const char __user * buf,
+	size_t count, loff_t *ppos)
+{
+	ssize_t retval;
+	struct inode *inode = file->f_dentry->d_inode;
+	int err, pos;
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		if (file->f_flags & O_APPEND)
+			pos = inode->i_size;
+		else
+			pos = *ppos;
+
+		if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
+			pos + count))
+		{
+			udf_expand_file_adinicb(inode, pos + count, &err);
+			if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+			{
+				udf_debug("udf_expand_adinicb: err=%d\n", err);
+				return err;
+			}
+		}
+		else
+		{
+			if (pos + count > inode->i_size)
+				UDF_I_LENALLOC(inode) = pos + count;
+			else
+				UDF_I_LENALLOC(inode) = inode->i_size;
+		}
+	}
+
+	retval = generic_file_write(file, buf, count, ppos);
+
+	if (retval > 0)
+		mark_inode_dirty(inode);
+	return retval;
+}
+
+/*
+ * udf_ioctl
+ *
+ * PURPOSE
+ *	Issue an ioctl.
+ *
+ * DESCRIPTION
+ *	Optional - sys_ioctl() will return -ENOTTY if this routine is not
+ *	available, and the ioctl cannot be handled without filesystem help.
+ *
+ *	sys_ioctl() handles these ioctls that apply only to regular files:
+ *		FIBMAP [requires udf_block_map()], FIGETBSZ, FIONREAD
+ *	These ioctls are also handled by sys_ioctl():
+ *		FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC
+ *	All other ioctls are passed to the filesystem.
+ *
+ *	Refer to sys_ioctl() in fs/ioctl.c
+ *	sys_ioctl() -> .
+ *
+ * PRE-CONDITIONS
+ *	inode			Pointer to inode that ioctl was issued on.
+ *	filp			Pointer to file that ioctl was issued on.
+ *	cmd			The ioctl command.
+ *	arg			The ioctl argument [can be interpreted as a
+ *				user-space pointer if desired].
+ *
+ * POST-CONDITIONS
+ *	<return>		Success (>=0) or an error code (<=0) that
+ *				sys_ioctl() will return.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	int result = -EINVAL;
+
+	if ( permission(inode, MAY_READ, NULL) != 0 )
+	{
+		udf_debug("no permission to access inode %lu\n",
+						inode->i_ino);
+		return -EPERM;
+	}
+
+	if ( !arg )
+	{
+		udf_debug("invalid argument to udf_ioctl\n");
+		return -EINVAL;
+	}
+
+	switch (cmd)
+	{
+		case UDF_GETVOLIDENT:
+			return copy_to_user((char __user *)arg,
+				UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0;
+		case UDF_RELOCATE_BLOCKS:
+		{
+			long old, new;
+
+			if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+			if (get_user(old, (long __user *)arg)) return -EFAULT;
+			if ((result = udf_relocate_blocks(inode->i_sb,
+					old, &new)) == 0)
+				result = put_user(new, (long __user *)arg);
+
+			return result;
+		}
+		case UDF_GETEASIZE:
+			result = put_user(UDF_I_LENEATTR(inode), (int __user *)arg);
+			break;
+
+		case UDF_GETEABLOCK:
+			result = copy_to_user((char __user *)arg, UDF_I_DATA(inode),
+				UDF_I_LENEATTR(inode)) ? -EFAULT : 0;
+			break;
+	}
+
+	return result;
+}
+
+/*
+ * udf_release_file
+ *
+ * PURPOSE
+ *  Called when all references to the file are closed
+ *
+ * DESCRIPTION
+ *  Discard prealloced blocks
+ *
+ * HISTORY
+ *
+ */
+static int udf_release_file(struct inode * inode, struct file * filp)
+{
+	if (filp->f_mode & FMODE_WRITE)
+	{
+		lock_kernel();
+		udf_discard_prealloc(inode);
+		unlock_kernel();
+	}
+	return 0;
+}
+
+struct file_operations udf_file_operations = {
+	.read			= generic_file_read,
+	.ioctl			= udf_ioctl,
+	.open			= generic_file_open,
+	.mmap			= generic_file_mmap,
+	.write			= udf_file_write,
+	.release		= udf_release_file,
+	.fsync			= udf_fsync_file,
+	.sendfile		= generic_file_sendfile,
+};
+
+struct inode_operations udf_file_inode_operations = {
+	.truncate		= udf_truncate,
+};
diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c
new file mode 100644
index 000000000000..2dde6b888c2b
--- /dev/null
+++ b/fs/udf/fsync.c
@@ -0,0 +1,56 @@
+/*
+ * fsync.c
+ *
+ * PURPOSE
+ *  Fsync handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *  E-mail regarding any portion of the Linux UDF file system should be
+ *  directed to the development team mailing list (run by majordomo):
+ *      linux_udf@hpesjro.fc.hp.com
+ *
+ * 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-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  05/22/99 blf  Created.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+
+static int udf_fsync_inode(struct inode *, int);
+
+/*
+ *	File may be NULL when we are called. Perhaps we shouldn't
+ *	even pass file to fsync ?
+ */
+
+int udf_fsync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+	struct inode *inode = dentry->d_inode;
+	return udf_fsync_inode(inode, datasync);
+}
+
+static int udf_fsync_inode(struct inode *inode, int datasync)
+{
+	int err;
+
+	err = sync_mapping_buffers(inode->i_mapping);
+	if (!(inode->i_state & I_DIRTY))
+		return err;
+	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+		return err;
+
+	err |= udf_sync_inode (inode);
+	return err ? -EIO : 0;
+}
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c
new file mode 100644
index 000000000000..a7e5d40f1ebc
--- /dev/null
+++ b/fs/udf/ialloc.c
@@ -0,0 +1,170 @@
+/*
+ * ialloc.c
+ *
+ * PURPOSE
+ *	Inode allocation handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ *  02/24/99 blf  Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <linux/fs.h>
+#include <linux/quotaops.h>
+#include <linux/udf_fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+void udf_free_inode(struct inode * inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+
+	/*
+	 * Note: we must free any quota before locking the superblock,
+	 * as writing the quota to disk may need the lock as well.
+	 */
+	DQUOT_FREE_INODE(inode);
+	DQUOT_DROP(inode);
+
+	clear_inode(inode);
+
+	down(&sbi->s_alloc_sem);
+	if (sbi->s_lvidbh) {
+		if (S_ISDIR(inode->i_mode))
+			UDF_SB_LVIDIU(sb)->numDirs =
+				cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) - 1);
+		else
+			UDF_SB_LVIDIU(sb)->numFiles =
+				cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1);
+		
+		mark_buffer_dirty(sbi->s_lvidbh);
+	}
+	up(&sbi->s_alloc_sem);
+
+	udf_free_blocks(sb, NULL, UDF_I_LOCATION(inode), 0, 1);
+}
+
+struct inode * udf_new_inode (struct inode *dir, int mode, int * err)
+{
+	struct super_block *sb = dir->i_sb;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct inode * inode;
+	int block;
+	uint32_t start = UDF_I_LOCATION(dir).logicalBlockNum;
+
+	inode = new_inode(sb);
+
+	if (!inode)
+	{
+		*err = -ENOMEM;
+		return NULL;
+	}
+	*err = -ENOSPC;
+
+	block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum,
+		start, err);
+	if (*err)
+	{
+		iput(inode);
+		return NULL;
+	}
+
+	down(&sbi->s_alloc_sem);
+	UDF_I_UNIQUE(inode) = 0;
+	UDF_I_LENEXTENTS(inode) = 0;
+	UDF_I_NEXT_ALLOC_BLOCK(inode) = 0;
+	UDF_I_NEXT_ALLOC_GOAL(inode) = 0;
+	UDF_I_STRAT4096(inode) = 0;
+	if (UDF_SB_LVIDBH(sb))
+	{
+		struct logicalVolHeaderDesc *lvhd;
+		uint64_t uniqueID;
+		lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse);
+		if (S_ISDIR(mode))
+			UDF_SB_LVIDIU(sb)->numDirs =
+				cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) + 1);
+		else
+			UDF_SB_LVIDIU(sb)->numFiles =
+				cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + 1);
+		UDF_I_UNIQUE(inode) = uniqueID = le64_to_cpu(lvhd->uniqueID);
+		if (!(++uniqueID & 0x00000000FFFFFFFFUL))
+			uniqueID += 16;
+		lvhd->uniqueID = cpu_to_le64(uniqueID);
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+	inode->i_mode = mode;
+	inode->i_uid = current->fsuid;
+	if (dir->i_mode & S_ISGID)
+	{
+		inode->i_gid = dir->i_gid;
+		if (S_ISDIR(mode))
+			mode |= S_ISGID;
+	}
+	else
+		inode->i_gid = current->fsgid;
+
+	UDF_I_LOCATION(inode).logicalBlockNum = block;
+	UDF_I_LOCATION(inode).partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum;
+	inode->i_ino = udf_get_lb_pblock(sb, UDF_I_LOCATION(inode), 0);
+	inode->i_blksize = PAGE_SIZE;
+	inode->i_blocks = 0;
+	UDF_I_LENEATTR(inode) = 0;
+	UDF_I_LENALLOC(inode) = 0;
+	UDF_I_USE(inode) = 0;
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE))
+	{
+		UDF_I_EFE(inode) = 1;
+		UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE);
+		UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL);
+		memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry));
+	}
+	else
+	{
+		UDF_I_EFE(inode) = 0;
+		UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL);
+		memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct fileEntry));
+	}
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB))
+		UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB;
+	else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+		UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+	else
+		UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+	inode->i_mtime = inode->i_atime = inode->i_ctime =
+		UDF_I_CRTIME(inode) = current_fs_time(inode->i_sb);
+	insert_inode_hash(inode);
+	mark_inode_dirty(inode);
+	up(&sbi->s_alloc_sem);
+
+	if (DQUOT_ALLOC_INODE(inode))
+	{
+		DQUOT_DROP(inode);
+		inode->i_flags |= S_NOQUOTA;
+		inode->i_nlink = 0;
+		iput(inode);
+		*err = -EDQUOT;
+		return NULL;
+	}
+
+	*err = 0;
+	return inode;
+}
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
new file mode 100644
index 000000000000..0506e1173784
--- /dev/null
+++ b/fs/udf/inode.c
@@ -0,0 +1,2010 @@
+/*
+ * inode.c
+ *
+ * PURPOSE
+ *  Inode handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *  E-mail regarding any portion of the Linux UDF file system should be
+ *  directed to the development team mailing list (run by majordomo):
+ *    linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998 Dave Boynton
+ *  (C) 1998-2004 Ben Fennema
+ *  (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  10/04/98 dgb  Added rudimentary directory functions
+ *  10/07/98      Fully working udf_block_map! It works!
+ *  11/25/98      bmap altered to better support extents
+ *  12/06/98 blf  partition support in udf_iget, udf_block_map and udf_read_inode
+ *  12/12/98      rewrote udf_block_map to handle next extents and descs across
+ *                block boundaries (which is not actually allowed)
+ *  12/20/98      added support for strategy 4096
+ *  03/07/99      rewrote udf_block_map (again)
+ *                New funcs, inode_bmap, udf_next_aext
+ *  04/19/99      Support for writing device EA's for major/minor #
+ */
+
+#include "udfdecl.h"
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/buffer_head.h>
+#include <linux/writeback.h>
+#include <linux/slab.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+MODULE_AUTHOR("Ben Fennema");
+MODULE_DESCRIPTION("Universal Disk Format Filesystem");
+MODULE_LICENSE("GPL");
+
+#define EXTENT_MERGE_SIZE 5
+
+static mode_t udf_convert_permissions(struct fileEntry *);
+static int udf_update_inode(struct inode *, int);
+static void udf_fill_inode(struct inode *, struct buffer_head *);
+static struct buffer_head *inode_getblk(struct inode *, long, int *,
+	long *, int *);
+static int8_t udf_insert_aext(struct inode *, kernel_lb_addr, int,
+	kernel_lb_addr, uint32_t, struct buffer_head *);
+static void udf_split_extents(struct inode *, int *, int, int,
+	kernel_long_ad [EXTENT_MERGE_SIZE], int *);
+static void udf_prealloc_extents(struct inode *, int, int,
+	 kernel_long_ad [EXTENT_MERGE_SIZE], int *);
+static void udf_merge_extents(struct inode *,
+	 kernel_long_ad [EXTENT_MERGE_SIZE], int *);
+static void udf_update_extents(struct inode *,
+	kernel_long_ad [EXTENT_MERGE_SIZE], int, int,
+	kernel_lb_addr, uint32_t, struct buffer_head **);
+static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
+
+/*
+ * udf_delete_inode
+ *
+ * PURPOSE
+ *	Clean-up before the specified inode is destroyed.
+ *
+ * DESCRIPTION
+ *	This routine is called when the kernel destroys an inode structure
+ *	ie. when iput() finds i_count == 0.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ *
+ *  Called at the last iput() if i_nlink is zero.
+ */
+void udf_delete_inode(struct inode * inode)
+{
+	if (is_bad_inode(inode))
+		goto no_delete;
+
+	inode->i_size = 0;
+	udf_truncate(inode);
+	lock_kernel();
+
+	udf_update_inode(inode, IS_SYNC(inode));
+	udf_free_inode(inode);
+
+	unlock_kernel();
+	return;
+no_delete:
+	clear_inode(inode);
+}
+
+void udf_clear_inode(struct inode *inode)
+{
+	if (!(inode->i_sb->s_flags & MS_RDONLY)) {
+		lock_kernel();
+		udf_discard_prealloc(inode);
+		unlock_kernel();
+	}
+
+	kfree(UDF_I_DATA(inode));
+	UDF_I_DATA(inode) = NULL;
+}
+
+static int udf_writepage(struct page *page, struct writeback_control *wbc)
+{
+	return block_write_full_page(page, udf_get_block, wbc);
+}
+
+static int udf_readpage(struct file *file, struct page *page)
+{
+	return block_read_full_page(page, udf_get_block);
+}
+
+static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+	return block_prepare_write(page, from, to, udf_get_block);
+}
+
+static sector_t udf_bmap(struct address_space *mapping, sector_t block)
+{
+	return generic_block_bmap(mapping,block,udf_get_block);
+}
+
+struct address_space_operations udf_aops = {
+	.readpage		= udf_readpage,
+	.writepage		= udf_writepage,
+	.sync_page		= block_sync_page,
+	.prepare_write		= udf_prepare_write,
+	.commit_write		= generic_commit_write,
+	.bmap			= udf_bmap,
+};
+
+void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err)
+{
+	struct page *page;
+	char *kaddr;
+	struct writeback_control udf_wbc = {
+		.sync_mode = WB_SYNC_NONE,
+		.nr_to_write = 1,
+	};
+
+	/* from now on we have normal address_space methods */
+	inode->i_data.a_ops = &udf_aops;
+
+	if (!UDF_I_LENALLOC(inode))
+	{
+		if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+			UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+		else
+			UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+		mark_inode_dirty(inode);
+		return;
+	}
+
+	page = grab_cache_page(inode->i_mapping, 0);
+	if (!PageLocked(page))
+		PAGE_BUG(page);
+	if (!PageUptodate(page))
+	{
+		kaddr = kmap(page);
+		memset(kaddr + UDF_I_LENALLOC(inode), 0x00,
+			PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode));
+		memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode),
+			UDF_I_LENALLOC(inode));
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+		kunmap(page);
+	}
+	memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0x00,
+		UDF_I_LENALLOC(inode));
+	UDF_I_LENALLOC(inode) = 0;
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+		UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+	else
+		UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+
+	inode->i_data.a_ops->writepage(page, &udf_wbc);
+	page_cache_release(page);
+
+	mark_inode_dirty(inode);
+}
+
+struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err)
+{
+	int newblock;
+	struct buffer_head *sbh = NULL, *dbh = NULL;
+	kernel_lb_addr bloc, eloc;
+	uint32_t elen, extoffset;
+	uint8_t alloctype;
+
+	struct udf_fileident_bh sfibh, dfibh;
+	loff_t f_pos = udf_ext0_offset(inode) >> 2;
+	int size = (udf_ext0_offset(inode) + inode->i_size) >> 2;
+	struct fileIdentDesc cfi, *sfi, *dfi;
+
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+		alloctype = ICBTAG_FLAG_AD_SHORT;
+	else
+		alloctype = ICBTAG_FLAG_AD_LONG;
+
+	if (!inode->i_size)
+	{
+		UDF_I_ALLOCTYPE(inode) = alloctype;
+		mark_inode_dirty(inode);
+		return NULL;
+	}
+
+	/* alloc block, and copy data to it */
+	*block = udf_new_block(inode->i_sb, inode,
+		UDF_I_LOCATION(inode).partitionReferenceNum,
+		UDF_I_LOCATION(inode).logicalBlockNum, err);
+
+	if (!(*block))
+		return NULL;
+	newblock = udf_get_pblock(inode->i_sb, *block,
+		UDF_I_LOCATION(inode).partitionReferenceNum, 0);
+	if (!newblock)
+		return NULL;
+	dbh = udf_tgetblk(inode->i_sb, newblock);
+	if (!dbh)
+		return NULL;
+	lock_buffer(dbh);
+	memset(dbh->b_data, 0x00, inode->i_sb->s_blocksize);
+	set_buffer_uptodate(dbh);
+	unlock_buffer(dbh);
+	mark_buffer_dirty_inode(dbh, inode);
+
+	sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2;
+	sbh = sfibh.sbh = sfibh.ebh = NULL;
+	dfibh.soffset = dfibh.eoffset = 0;
+	dfibh.sbh = dfibh.ebh = dbh;
+	while ( (f_pos < size) )
+	{
+		UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB;
+		sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL, NULL, NULL);
+		if (!sfi)
+		{
+			udf_release_data(dbh);
+			return NULL;
+		}
+		UDF_I_ALLOCTYPE(inode) = alloctype;
+		sfi->descTag.tagLocation = cpu_to_le32(*block);
+		dfibh.soffset = dfibh.eoffset;
+		dfibh.eoffset += (sfibh.eoffset - sfibh.soffset);
+		dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset);
+		if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse,
+			sfi->fileIdent + le16_to_cpu(sfi->lengthOfImpUse)))
+		{
+			UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB;
+			udf_release_data(dbh);
+			return NULL;
+		}
+	}
+	mark_buffer_dirty_inode(dbh, inode);
+
+	memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0, UDF_I_LENALLOC(inode));
+	UDF_I_LENALLOC(inode) = 0;
+	bloc = UDF_I_LOCATION(inode);
+	eloc.logicalBlockNum = *block;
+	eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+	elen = inode->i_size;
+	UDF_I_LENEXTENTS(inode) = elen;
+	extoffset = udf_file_entry_alloc_offset(inode);
+	udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0);
+	/* UniqueID stuff */
+
+	udf_release_data(sbh);
+	mark_inode_dirty(inode);
+	return dbh;
+}
+
+static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
+{
+	int err, new;
+	struct buffer_head *bh;
+	unsigned long phys;
+
+	if (!create)
+	{
+		phys = udf_block_map(inode, block);
+		if (phys)
+			map_bh(bh_result, inode->i_sb, phys);
+		return 0;
+	}
+
+	err = -EIO;
+	new = 0;
+	bh = NULL;
+
+	lock_kernel();
+
+	if (block < 0)
+		goto abort_negative;
+
+	if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1)
+	{
+		UDF_I_NEXT_ALLOC_BLOCK(inode) ++;
+		UDF_I_NEXT_ALLOC_GOAL(inode) ++;
+	}
+
+	err = 0;
+
+	bh = inode_getblk(inode, block, &err, &phys, &new);
+	if (bh)
+		BUG();
+	if (err)
+		goto abort;
+	if (!phys)
+		BUG();
+
+	if (new)
+		set_buffer_new(bh_result);
+	map_bh(bh_result, inode->i_sb, phys);
+abort:
+	unlock_kernel();
+	return err;
+
+abort_negative:
+	udf_warning(inode->i_sb, "udf_get_block", "block < 0");
+	goto abort;
+}
+
+static struct buffer_head *
+udf_getblk(struct inode *inode, long block, int create, int *err)
+{
+	struct buffer_head dummy;
+
+	dummy.b_state = 0;
+	dummy.b_blocknr = -1000;
+	*err = udf_get_block(inode, block, &dummy, create);
+	if (!*err && buffer_mapped(&dummy))
+	{
+		struct buffer_head *bh;
+		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
+		if (buffer_new(&dummy))
+		{
+			lock_buffer(bh);
+			memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
+			set_buffer_uptodate(bh);
+			unlock_buffer(bh);
+			mark_buffer_dirty_inode(bh, inode);
+		}
+		return bh;
+	}
+	return NULL;
+}
+
+static struct buffer_head * inode_getblk(struct inode * inode, long block,
+	int *err, long *phys, int *new)
+{
+	struct buffer_head *pbh = NULL, *cbh = NULL, *nbh = NULL, *result = NULL;
+	kernel_long_ad laarr[EXTENT_MERGE_SIZE];
+	uint32_t pextoffset = 0, cextoffset = 0, nextoffset = 0;
+	int count = 0, startnum = 0, endnum = 0;
+	uint32_t elen = 0;
+	kernel_lb_addr eloc, pbloc, cbloc, nbloc;
+	int c = 1;
+	uint64_t lbcount = 0, b_off = 0;
+	uint32_t newblocknum, newblock, offset = 0;
+	int8_t etype;
+	int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum;
+	char lastblock = 0;
+
+	pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode);
+	b_off = (uint64_t)block << inode->i_sb->s_blocksize_bits;
+	pbloc = cbloc = nbloc = UDF_I_LOCATION(inode);
+
+	/* find the extent which contains the block we are looking for.
+       alternate between laarr[0] and laarr[1] for locations of the
+       current extent, and the previous extent */
+	do
+	{
+		if (pbh != cbh)
+		{
+			udf_release_data(pbh);
+			atomic_inc(&cbh->b_count);
+			pbh = cbh;
+		}
+		if (cbh != nbh)
+		{
+			udf_release_data(cbh);
+			atomic_inc(&nbh->b_count);
+			cbh = nbh;
+		}
+
+		lbcount += elen;
+
+		pbloc = cbloc;
+		cbloc = nbloc;
+
+		pextoffset = cextoffset;
+		cextoffset = nextoffset;
+
+		if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) == -1)
+			break;
+
+		c = !c;
+
+		laarr[c].extLength = (etype << 30) | elen;
+		laarr[c].extLocation = eloc;
+
+		if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+			pgoal = eloc.logicalBlockNum +
+				((elen + inode->i_sb->s_blocksize - 1) >>
+				inode->i_sb->s_blocksize_bits);
+
+		count ++;
+	} while (lbcount + elen <= b_off);
+
+	b_off -= lbcount;
+	offset = b_off >> inode->i_sb->s_blocksize_bits;
+
+	/* if the extent is allocated and recorded, return the block
+       if the extent is not a multiple of the blocksize, round up */
+
+	if (etype == (EXT_RECORDED_ALLOCATED >> 30))
+	{
+		if (elen & (inode->i_sb->s_blocksize - 1))
+		{
+			elen = EXT_RECORDED_ALLOCATED |
+				((elen + inode->i_sb->s_blocksize - 1) &
+				~(inode->i_sb->s_blocksize - 1));
+			etype = udf_write_aext(inode, nbloc, &cextoffset, eloc, elen, nbh, 1);
+		}
+		udf_release_data(pbh);
+		udf_release_data(cbh);
+		udf_release_data(nbh);
+		newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset);
+		*phys = newblock;
+		return NULL;
+	}
+
+	if (etype == -1)
+	{
+		endnum = startnum = ((count > 1) ? 1 : count);
+		if (laarr[c].extLength & (inode->i_sb->s_blocksize - 1))
+		{
+			laarr[c].extLength =
+				(laarr[c].extLength & UDF_EXTENT_FLAG_MASK) |
+				(((laarr[c].extLength & UDF_EXTENT_LENGTH_MASK) +
+					inode->i_sb->s_blocksize - 1) &
+				~(inode->i_sb->s_blocksize - 1));
+			UDF_I_LENEXTENTS(inode) =
+				(UDF_I_LENEXTENTS(inode) + inode->i_sb->s_blocksize - 1) &
+					~(inode->i_sb->s_blocksize - 1);
+		}
+		c = !c;
+		laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+			((offset + 1) << inode->i_sb->s_blocksize_bits);
+		memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr));
+		count ++;
+		endnum ++;
+		lastblock = 1;
+	}
+	else
+		endnum = startnum = ((count > 2) ? 2 : count);
+
+	/* if the current extent is in position 0, swap it with the previous */
+	if (!c && count != 1)
+	{
+		laarr[2] = laarr[0];
+		laarr[0] = laarr[1];
+		laarr[1] = laarr[2];
+		c = 1;
+	}
+
+	/* if the current block is located in a extent, read the next extent */
+	if (etype != -1)
+	{
+		if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 0)) != -1)
+		{
+			laarr[c+1].extLength = (etype << 30) | elen;
+			laarr[c+1].extLocation = eloc;
+			count ++;
+			startnum ++;
+			endnum ++;
+		}
+		else
+			lastblock = 1;
+	}
+	udf_release_data(cbh);
+	udf_release_data(nbh);
+
+	/* if the current extent is not recorded but allocated, get the
+		block in the extent corresponding to the requested block */
+	if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+		newblocknum = laarr[c].extLocation.logicalBlockNum + offset;
+	else /* otherwise, allocate a new block */
+	{
+		if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block)
+			goal = UDF_I_NEXT_ALLOC_GOAL(inode);
+
+		if (!goal)
+		{
+			if (!(goal = pgoal))
+				goal = UDF_I_LOCATION(inode).logicalBlockNum + 1;
+		}
+
+		if (!(newblocknum = udf_new_block(inode->i_sb, inode,
+			UDF_I_LOCATION(inode).partitionReferenceNum, goal, err)))
+		{
+			udf_release_data(pbh);
+			*err = -ENOSPC;
+			return NULL;
+		}
+		UDF_I_LENEXTENTS(inode) += inode->i_sb->s_blocksize;
+	}
+
+	/* if the extent the requsted block is located in contains multiple blocks,
+       split the extent into at most three extents. blocks prior to requested
+       block, requested block, and blocks after requested block */
+	udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum);
+
+#ifdef UDF_PREALLOCATE
+	/* preallocate blocks */
+	udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
+#endif
+
+	/* merge any continuous blocks in laarr */
+	udf_merge_extents(inode, laarr, &endnum);
+
+	/* write back the new extents, inserting new extents if the new number
+       of extents is greater than the old number, and deleting extents if
+       the new number of extents is less than the old number */
+	udf_update_extents(inode, laarr, startnum, endnum, pbloc, pextoffset, &pbh);
+
+	udf_release_data(pbh);
+
+	if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum,
+		UDF_I_LOCATION(inode).partitionReferenceNum, 0)))
+	{
+		return NULL;
+	}
+	*phys = newblock;
+	*err = 0;
+	*new = 1;
+	UDF_I_NEXT_ALLOC_BLOCK(inode) = block;
+	UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum;
+	inode->i_ctime = current_fs_time(inode->i_sb);
+
+	if (IS_SYNC(inode))
+		udf_sync_inode(inode);
+	else
+		mark_inode_dirty(inode);
+	return result;
+}
+
+static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum,
+	kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum)
+{
+	if ((laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30) ||
+		(laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+	{
+		int curr = *c;
+		int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) +
+			inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+		int8_t etype = (laarr[curr].extLength >> 30);
+
+		if (blen == 1)
+			;
+		else if (!offset || blen == offset + 1)
+		{
+			laarr[curr+2] = laarr[curr+1];
+			laarr[curr+1] = laarr[curr];
+		}
+		else
+		{
+			laarr[curr+3] = laarr[curr+1];
+			laarr[curr+2] = laarr[curr+1] = laarr[curr];
+		}
+
+		if (offset)
+		{
+			if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+			{
+				udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset);
+				laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+					(offset << inode->i_sb->s_blocksize_bits);
+				laarr[curr].extLocation.logicalBlockNum = 0;
+				laarr[curr].extLocation.partitionReferenceNum = 0;
+			}
+			else
+				laarr[curr].extLength = (etype << 30) |
+					(offset << inode->i_sb->s_blocksize_bits);
+			curr ++;
+			(*c) ++;
+			(*endnum) ++;
+		}
+		
+		laarr[curr].extLocation.logicalBlockNum = newblocknum;
+		if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+			laarr[curr].extLocation.partitionReferenceNum =
+				UDF_I_LOCATION(inode).partitionReferenceNum;
+		laarr[curr].extLength = EXT_RECORDED_ALLOCATED |
+			inode->i_sb->s_blocksize;
+		curr ++;
+
+		if (blen != offset + 1)
+		{
+			if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+				laarr[curr].extLocation.logicalBlockNum += (offset + 1);
+			laarr[curr].extLength = (etype << 30) |
+				((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits);
+			curr ++;
+			(*endnum) ++;
+		}
+	}
+}
+
+static void udf_prealloc_extents(struct inode *inode, int c, int lastblock,
+	 kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum)
+{
+	int start, length = 0, currlength = 0, i;
+
+	if (*endnum >= (c+1))
+	{
+		if (!lastblock)
+			return;
+		else
+			start = c;
+	}
+	else
+	{
+		if ((laarr[c+1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+		{
+			start = c+1;
+			length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) +
+				inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+		}
+		else
+			start = c;
+	}
+
+	for (i=start+1; i<=*endnum; i++)
+	{
+		if (i == *endnum)
+		{
+			if (lastblock)
+				length += UDF_DEFAULT_PREALLOC_BLOCKS;
+		}
+		else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+			length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+				inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+		else
+			break;
+	}
+
+	if (length)
+	{
+		int next = laarr[start].extLocation.logicalBlockNum +
+			(((laarr[start].extLength & UDF_EXTENT_LENGTH_MASK) +
+			inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+		int numalloc = udf_prealloc_blocks(inode->i_sb, inode,
+			laarr[start].extLocation.partitionReferenceNum,
+			next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length :
+				UDF_DEFAULT_PREALLOC_BLOCKS) - currlength);
+
+		if (numalloc)
+		{
+			if (start == (c+1))
+				laarr[start].extLength +=
+					(numalloc << inode->i_sb->s_blocksize_bits);
+			else
+			{
+				memmove(&laarr[c+2], &laarr[c+1],
+					sizeof(long_ad) * (*endnum - (c+1)));
+				(*endnum) ++;
+				laarr[c+1].extLocation.logicalBlockNum = next;
+				laarr[c+1].extLocation.partitionReferenceNum =
+					laarr[c].extLocation.partitionReferenceNum;
+				laarr[c+1].extLength = EXT_NOT_RECORDED_ALLOCATED |
+					(numalloc << inode->i_sb->s_blocksize_bits);
+				start = c+1;
+			}
+
+			for (i=start+1; numalloc && i<*endnum; i++)
+			{
+				int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+					inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+
+				if (elen > numalloc)
+				{
+					laarr[i].extLength -=
+						(numalloc << inode->i_sb->s_blocksize_bits);
+					numalloc = 0;
+				}
+				else
+				{
+					numalloc -= elen;
+					if (*endnum > (i+1))
+						memmove(&laarr[i], &laarr[i+1], 
+							sizeof(long_ad) * (*endnum - (i+1)));
+					i --;
+					(*endnum) --;
+				}
+			}
+			UDF_I_LENEXTENTS(inode) += numalloc << inode->i_sb->s_blocksize_bits;
+		}
+	}
+}
+
+static void udf_merge_extents(struct inode *inode,
+	 kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum)
+{
+	int i;
+
+	for (i=0; i<(*endnum-1); i++)
+	{
+		if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30))
+		{
+			if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) ||
+				((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) ==
+				(((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+				inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits)))
+			{
+				if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+					(laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) +
+					inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK)
+				{
+					laarr[i+1].extLength = (laarr[i+1].extLength -
+						(laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+						UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1);
+					laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) +
+						(UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize;
+					laarr[i+1].extLocation.logicalBlockNum =
+						laarr[i].extLocation.logicalBlockNum +
+						((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >>
+							inode->i_sb->s_blocksize_bits);
+				}
+				else
+				{
+					laarr[i].extLength = laarr[i+1].extLength +
+						(((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+						inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1));
+					if (*endnum > (i+2))
+						memmove(&laarr[i+1], &laarr[i+2],
+							sizeof(long_ad) * (*endnum - (i+2)));
+					i --;
+					(*endnum) --;
+				}
+			}
+		}
+		else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) &&
+			((laarr[i+1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)))
+		{
+			udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0,
+				((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+				inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+			laarr[i].extLocation.logicalBlockNum = 0;
+			laarr[i].extLocation.partitionReferenceNum = 0;
+
+			if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+				(laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) +
+				inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK)
+			{
+				laarr[i+1].extLength = (laarr[i+1].extLength -
+					(laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+					UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1);
+				laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) +
+					(UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize;
+			}
+			else
+			{
+				laarr[i].extLength = laarr[i+1].extLength +
+					(((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+					inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1));
+				if (*endnum > (i+2))
+					memmove(&laarr[i+1], &laarr[i+2],
+						sizeof(long_ad) * (*endnum - (i+2)));
+				i --;
+				(*endnum) --;
+			}
+		}
+		else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+		{
+			udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0,
+				((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+			       inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+			laarr[i].extLocation.logicalBlockNum = 0;
+			laarr[i].extLocation.partitionReferenceNum = 0;
+			laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) |
+				EXT_NOT_RECORDED_NOT_ALLOCATED;
+		}
+	}
+}
+
+static void udf_update_extents(struct inode *inode,
+	kernel_long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum,
+	kernel_lb_addr pbloc, uint32_t pextoffset, struct buffer_head **pbh)
+{
+	int start = 0, i;
+	kernel_lb_addr tmploc;
+	uint32_t tmplen;
+
+	if (startnum > endnum)
+	{
+		for (i=0; i<(startnum-endnum); i++)
+		{
+			udf_delete_aext(inode, pbloc, pextoffset, laarr[i].extLocation,
+				laarr[i].extLength, *pbh);
+		}
+	}
+	else if (startnum < endnum)
+	{
+		for (i=0; i<(endnum-startnum); i++)
+		{
+			udf_insert_aext(inode, pbloc, pextoffset, laarr[i].extLocation,
+				laarr[i].extLength, *pbh);
+			udf_next_aext(inode, &pbloc, &pextoffset, &laarr[i].extLocation,
+				&laarr[i].extLength, pbh, 1);
+			start ++;
+		}
+	}
+
+	for (i=start; i<endnum; i++)
+	{
+		udf_next_aext(inode, &pbloc, &pextoffset, &tmploc, &tmplen, pbh, 0);
+		udf_write_aext(inode, pbloc, &pextoffset, laarr[i].extLocation,
+			laarr[i].extLength, *pbh, 1);
+	}
+}
+
+struct buffer_head * udf_bread(struct inode * inode, int block,
+	int create, int * err)
+{
+	struct buffer_head * bh = NULL;
+
+	bh = udf_getblk(inode, block, create, err);
+	if (!bh)
+		return NULL;
+
+	if (buffer_uptodate(bh))
+		return bh;
+	ll_rw_block(READ, 1, &bh);
+	wait_on_buffer(bh);
+	if (buffer_uptodate(bh))
+		return bh;
+	brelse(bh);
+	*err = -EIO;
+	return NULL;
+}
+
+void udf_truncate(struct inode * inode)
+{
+	int offset;
+	int err;
+
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+			S_ISLNK(inode->i_mode)))
+		return;
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return;
+
+	lock_kernel();
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
+			inode->i_size))
+		{
+			udf_expand_file_adinicb(inode, inode->i_size, &err);
+			if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+			{
+				inode->i_size = UDF_I_LENALLOC(inode);
+				unlock_kernel();
+				return;
+			}
+			else
+				udf_truncate_extents(inode);
+		}
+		else
+		{
+			offset = inode->i_size & (inode->i_sb->s_blocksize - 1);
+			memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, 0x00, inode->i_sb->s_blocksize - offset - udf_file_entry_alloc_offset(inode));
+			UDF_I_LENALLOC(inode) = inode->i_size;
+		}
+	}
+	else
+	{
+		block_truncate_page(inode->i_mapping, inode->i_size, udf_get_block);
+		udf_truncate_extents(inode);
+	}	
+
+	inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb);
+	if (IS_SYNC(inode))
+		udf_sync_inode (inode);
+	else
+		mark_inode_dirty(inode);
+	unlock_kernel();
+}
+
+static void
+__udf_read_inode(struct inode *inode)
+{
+	struct buffer_head *bh = NULL;
+	struct fileEntry *fe;
+	uint16_t ident;
+
+	/*
+	 * Set defaults, but the inode is still incomplete!
+	 * Note: get_new_inode() sets the following on a new inode:
+	 *      i_sb = sb
+	 *      i_no = ino
+	 *      i_flags = sb->s_flags
+	 *      i_state = 0
+	 * clean_inode(): zero fills and sets
+	 *      i_count = 1
+	 *      i_nlink = 1
+	 *      i_op = NULL;
+	 */
+	inode->i_blksize = PAGE_SIZE;
+
+	bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident);
+
+	if (!bh)
+	{
+		printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed !bh\n",
+			inode->i_ino);
+		make_bad_inode(inode);
+		return;
+	}
+
+	if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE &&
+		ident != TAG_IDENT_USE)
+	{
+		printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n",
+			inode->i_ino, ident);
+		udf_release_data(bh);
+		make_bad_inode(inode);
+		return;
+	}
+
+	fe = (struct fileEntry *)bh->b_data;
+
+	if (le16_to_cpu(fe->icbTag.strategyType) == 4096)
+	{
+		struct buffer_head *ibh = NULL, *nbh = NULL;
+		struct indirectEntry *ie;
+
+		ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident);
+		if (ident == TAG_IDENT_IE)
+		{
+			if (ibh)
+			{
+				kernel_lb_addr loc;
+				ie = (struct indirectEntry *)ibh->b_data;
+	
+				loc = lelb_to_cpu(ie->indirectICB.extLocation);
+	
+				if (ie->indirectICB.extLength && 
+					(nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident)))
+				{
+					if (ident == TAG_IDENT_FE ||
+						ident == TAG_IDENT_EFE)
+					{
+						memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(kernel_lb_addr));
+						udf_release_data(bh);
+						udf_release_data(ibh);
+						udf_release_data(nbh);
+						__udf_read_inode(inode);
+						return;
+					}
+					else
+					{
+						udf_release_data(nbh);
+						udf_release_data(ibh);
+					}
+				}
+				else
+					udf_release_data(ibh);
+			}
+		}
+		else
+			udf_release_data(ibh);
+	}
+	else if (le16_to_cpu(fe->icbTag.strategyType) != 4)
+	{
+		printk(KERN_ERR "udf: unsupported strategy type: %d\n",
+			le16_to_cpu(fe->icbTag.strategyType));
+		udf_release_data(bh);
+		make_bad_inode(inode);
+		return;
+	}
+	udf_fill_inode(inode, bh);
+	udf_release_data(bh);
+}
+
+static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
+{
+	struct fileEntry *fe;
+	struct extendedFileEntry *efe;
+	time_t convtime;
+	long convtime_usec;
+	int offset;
+
+	fe = (struct fileEntry *)bh->b_data;
+	efe = (struct extendedFileEntry *)bh->b_data;
+
+	if (le16_to_cpu(fe->icbTag.strategyType) == 4)
+		UDF_I_STRAT4096(inode) = 0;
+	else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */
+		UDF_I_STRAT4096(inode) = 1;
+
+	UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK;
+	UDF_I_UNIQUE(inode) = 0;
+	UDF_I_LENEATTR(inode) = 0;
+	UDF_I_LENEXTENTS(inode) = 0;
+	UDF_I_LENALLOC(inode) = 0;
+	UDF_I_NEXT_ALLOC_BLOCK(inode) = 0;
+	UDF_I_NEXT_ALLOC_GOAL(inode) = 0;
+	if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE)
+	{
+		UDF_I_EFE(inode) = 1;
+		UDF_I_USE(inode) = 0;
+		UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL);
+		memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct extendedFileEntry), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry));
+	}
+	else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE)
+	{
+		UDF_I_EFE(inode) = 0;
+		UDF_I_USE(inode) = 0;
+		UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL);
+		memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct fileEntry), inode->i_sb->s_blocksize - sizeof(struct fileEntry));
+	}
+	else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE)
+	{
+		UDF_I_EFE(inode) = 0;
+		UDF_I_USE(inode) = 1;
+		UDF_I_LENALLOC(inode) =
+			le32_to_cpu(
+				((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs);
+		UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry), GFP_KERNEL);
+		memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct unallocSpaceEntry), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry));
+		return;
+	}
+
+	inode->i_uid = le32_to_cpu(fe->uid);
+	if ( inode->i_uid == -1 ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid;
+
+	inode->i_gid = le32_to_cpu(fe->gid);
+	if ( inode->i_gid == -1 ) inode->i_gid = UDF_SB(inode->i_sb)->s_gid;
+
+	inode->i_nlink = le16_to_cpu(fe->fileLinkCount);
+	if (!inode->i_nlink)
+		inode->i_nlink = 1;
+	
+	inode->i_size = le64_to_cpu(fe->informationLength);
+	UDF_I_LENEXTENTS(inode) = inode->i_size;
+
+	inode->i_mode = udf_convert_permissions(fe);
+	inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask;
+
+	if (UDF_I_EFE(inode) == 0)
+	{
+		inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
+			(inode->i_sb->s_blocksize_bits - 9);
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(fe->accessTime)) )
+		{
+			inode->i_atime.tv_sec = convtime;
+			inode->i_atime.tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(fe->modificationTime)) )
+		{
+			inode->i_mtime.tv_sec = convtime;
+			inode->i_mtime.tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(fe->attrTime)) )
+		{
+			inode->i_ctime.tv_sec = convtime;
+			inode->i_ctime.tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID);
+		UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr);
+		UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs);
+		offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode);
+	}
+	else
+	{
+		inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << 
+			(inode->i_sb->s_blocksize_bits - 9);
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(efe->accessTime)) )
+		{
+			inode->i_atime.tv_sec = convtime;
+			inode->i_atime.tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(efe->modificationTime)) )
+		{
+			inode->i_mtime.tv_sec = convtime;
+			inode->i_mtime.tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(efe->createTime)) )
+		{
+			UDF_I_CRTIME(inode).tv_sec = convtime;
+			UDF_I_CRTIME(inode).tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		if ( udf_stamp_to_time(&convtime, &convtime_usec,
+			lets_to_cpu(efe->attrTime)) )
+		{
+			inode->i_ctime.tv_sec = convtime;
+			inode->i_ctime.tv_nsec = convtime_usec * 1000;
+		}
+		else
+		{
+			inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb);
+		}
+
+		UDF_I_UNIQUE(inode) = le64_to_cpu(efe->uniqueID);
+		UDF_I_LENEATTR(inode) = le32_to_cpu(efe->lengthExtendedAttr);
+		UDF_I_LENALLOC(inode) = le32_to_cpu(efe->lengthAllocDescs);
+		offset = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode);
+	}
+
+	switch (fe->icbTag.fileType)
+	{
+		case ICBTAG_FILE_TYPE_DIRECTORY:
+		{
+			inode->i_op = &udf_dir_inode_operations;
+			inode->i_fop = &udf_dir_operations;
+			inode->i_mode |= S_IFDIR;
+			inode->i_nlink ++;
+			break;
+		}
+		case ICBTAG_FILE_TYPE_REALTIME:
+		case ICBTAG_FILE_TYPE_REGULAR:
+		case ICBTAG_FILE_TYPE_UNDEF:
+		{
+			if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+				inode->i_data.a_ops = &udf_adinicb_aops;
+			else
+				inode->i_data.a_ops = &udf_aops;
+			inode->i_op = &udf_file_inode_operations;
+			inode->i_fop = &udf_file_operations;
+			inode->i_mode |= S_IFREG;
+			break;
+		}
+		case ICBTAG_FILE_TYPE_BLOCK:
+		{
+			inode->i_mode |= S_IFBLK;
+			break;
+		}
+		case ICBTAG_FILE_TYPE_CHAR:
+		{
+			inode->i_mode |= S_IFCHR;
+			break;
+		}
+		case ICBTAG_FILE_TYPE_FIFO:
+		{
+			init_special_inode(inode, inode->i_mode | S_IFIFO, 0);
+			break;
+		}
+		case ICBTAG_FILE_TYPE_SOCKET:
+		{
+			init_special_inode(inode, inode->i_mode | S_IFSOCK, 0);
+			break;
+		}
+		case ICBTAG_FILE_TYPE_SYMLINK:
+		{
+			inode->i_data.a_ops = &udf_symlink_aops;
+			inode->i_op = &page_symlink_inode_operations;
+			inode->i_mode = S_IFLNK|S_IRWXUGO;
+			break;
+		}
+		default:
+		{
+			printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n",
+				inode->i_ino, fe->icbTag.fileType);
+			make_bad_inode(inode);
+			return;
+		}
+	}
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+	{
+		struct deviceSpec *dsea =
+			(struct deviceSpec *)
+				udf_get_extendedattr(inode, 12, 1);
+
+		if (dsea)
+		{
+			init_special_inode(inode, inode->i_mode, MKDEV(
+				le32_to_cpu(dsea->majorDeviceIdent),
+				le32_to_cpu(dsea->minorDeviceIdent)));
+			/* Developer ID ??? */
+		}
+		else
+		{
+			make_bad_inode(inode);
+		}
+	}
+}
+
+static mode_t
+udf_convert_permissions(struct fileEntry *fe)
+{
+	mode_t mode;
+	uint32_t permissions;
+	uint32_t flags;
+
+	permissions = le32_to_cpu(fe->permissions);
+	flags = le16_to_cpu(fe->icbTag.flags);
+
+	mode =	(( permissions      ) & S_IRWXO) |
+		(( permissions >> 2 ) & S_IRWXG) |
+		(( permissions >> 4 ) & S_IRWXU) |
+		(( flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) |
+		(( flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) |
+		(( flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0);
+
+	return mode;
+}
+
+/*
+ * udf_write_inode
+ *
+ * PURPOSE
+ *	Write out the specified inode.
+ *
+ * DESCRIPTION
+ *	This routine is called whenever an inode is synced.
+ *	Currently this routine is just a placeholder.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+
+int udf_write_inode(struct inode * inode, int sync)
+{
+	int ret;
+	lock_kernel();
+	ret = udf_update_inode(inode, sync);
+	unlock_kernel();
+	return ret;
+}
+
+int udf_sync_inode(struct inode * inode)
+{
+	return udf_update_inode(inode, 1);
+}
+
+static int
+udf_update_inode(struct inode *inode, int do_sync)
+{
+	struct buffer_head *bh = NULL;
+	struct fileEntry *fe;
+	struct extendedFileEntry *efe;
+	uint32_t udfperms;
+	uint16_t icbflags;
+	uint16_t crclen;
+	int i;
+	kernel_timestamp cpu_time;
+	int err = 0;
+
+	bh = udf_tread(inode->i_sb,
+		udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0));
+
+	if (!bh)
+	{
+		udf_debug("bread failure\n");
+		return -EIO;
+	}
+
+	memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
+
+	fe = (struct fileEntry *)bh->b_data;
+	efe = (struct extendedFileEntry *)bh->b_data;
+
+	if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE)
+	{
+		struct unallocSpaceEntry *use =
+			(struct unallocSpaceEntry *)bh->b_data;
+
+		use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode));
+		memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry));
+		crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) -
+			sizeof(tag);
+		use->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
+		use->descTag.descCRCLength = cpu_to_le16(crclen);
+		use->descTag.descCRC = cpu_to_le16(udf_crc((char *)use + sizeof(tag), crclen, 0));
+
+		use->descTag.tagChecksum = 0;
+		for (i=0; i<16; i++)
+			if (i != 4)
+				use->descTag.tagChecksum += ((uint8_t *)&(use->descTag))[i];
+
+		mark_buffer_dirty(bh);
+		udf_release_data(bh);
+		return err;
+	}
+
+	if (inode->i_uid != UDF_SB(inode->i_sb)->s_uid)
+		fe->uid = cpu_to_le32(inode->i_uid);
+
+	if (inode->i_gid != UDF_SB(inode->i_sb)->s_gid)
+		fe->gid = cpu_to_le32(inode->i_gid);
+
+	udfperms =	((inode->i_mode & S_IRWXO)     ) |
+			((inode->i_mode & S_IRWXG) << 2) |
+			((inode->i_mode & S_IRWXU) << 4);
+
+	udfperms |=	(le32_to_cpu(fe->permissions) &
+			(FE_PERM_O_DELETE | FE_PERM_O_CHATTR |
+			 FE_PERM_G_DELETE | FE_PERM_G_CHATTR |
+			 FE_PERM_U_DELETE | FE_PERM_U_CHATTR));
+	fe->permissions = cpu_to_le32(udfperms);
+
+	if (S_ISDIR(inode->i_mode))
+		fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1);
+	else
+		fe->fileLinkCount = cpu_to_le16(inode->i_nlink);
+
+	fe->informationLength = cpu_to_le64(inode->i_size);
+
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+	{
+		regid *eid;
+		struct deviceSpec *dsea =
+			(struct deviceSpec *)
+				udf_get_extendedattr(inode, 12, 1);
+
+		if (!dsea)
+		{
+			dsea = (struct deviceSpec *)
+				udf_add_extendedattr(inode,
+					sizeof(struct deviceSpec) +
+					sizeof(regid), 12, 0x3);
+			dsea->attrType = cpu_to_le32(12);
+			dsea->attrSubtype = 1;
+			dsea->attrLength = cpu_to_le32(sizeof(struct deviceSpec) +
+				sizeof(regid));
+			dsea->impUseLength = cpu_to_le32(sizeof(regid));
+		}
+		eid = (regid *)dsea->impUse;
+		memset(eid, 0, sizeof(regid));
+		strcpy(eid->ident, UDF_ID_DEVELOPER);
+		eid->identSuffix[0] = UDF_OS_CLASS_UNIX;
+		eid->identSuffix[1] = UDF_OS_ID_LINUX;
+		dsea->majorDeviceIdent = cpu_to_le32(imajor(inode));
+		dsea->minorDeviceIdent = cpu_to_le32(iminor(inode));
+	}
+
+	if (UDF_I_EFE(inode) == 0)
+	{
+		memcpy(bh->b_data + sizeof(struct fileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct fileEntry));
+		fe->logicalBlocksRecorded = cpu_to_le64(
+			(inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >>
+			(inode->i_sb->s_blocksize_bits - 9));
+
+		if (udf_time_to_stamp(&cpu_time, inode->i_atime))
+			fe->accessTime = cpu_to_lets(cpu_time);
+		if (udf_time_to_stamp(&cpu_time, inode->i_mtime))
+			fe->modificationTime = cpu_to_lets(cpu_time);
+		if (udf_time_to_stamp(&cpu_time, inode->i_ctime))
+			fe->attrTime = cpu_to_lets(cpu_time);
+		memset(&(fe->impIdent), 0, sizeof(regid));
+		strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER);
+		fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+		fe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+		fe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode));
+		fe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode));
+		fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode));
+		fe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_FE);
+		crclen = sizeof(struct fileEntry);
+	}
+	else
+	{
+		memcpy(bh->b_data + sizeof(struct extendedFileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry));
+		efe->objectSize = cpu_to_le64(inode->i_size);
+		efe->logicalBlocksRecorded = cpu_to_le64(
+			(inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >>
+			(inode->i_sb->s_blocksize_bits - 9));
+
+		if (UDF_I_CRTIME(inode).tv_sec > inode->i_atime.tv_sec ||
+			(UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec &&
+			 UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec))
+		{
+			UDF_I_CRTIME(inode) = inode->i_atime;
+		}
+		if (UDF_I_CRTIME(inode).tv_sec > inode->i_mtime.tv_sec ||
+			(UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec &&
+			 UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec))
+		{
+			UDF_I_CRTIME(inode) = inode->i_mtime;
+		}
+		if (UDF_I_CRTIME(inode).tv_sec > inode->i_ctime.tv_sec ||
+			(UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec &&
+			 UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec))
+		{
+			UDF_I_CRTIME(inode) = inode->i_ctime;
+		}
+
+		if (udf_time_to_stamp(&cpu_time, inode->i_atime))
+			efe->accessTime = cpu_to_lets(cpu_time);
+		if (udf_time_to_stamp(&cpu_time, inode->i_mtime))
+			efe->modificationTime = cpu_to_lets(cpu_time);
+		if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode)))
+			efe->createTime = cpu_to_lets(cpu_time);
+		if (udf_time_to_stamp(&cpu_time, inode->i_ctime))
+			efe->attrTime = cpu_to_lets(cpu_time);
+
+		memset(&(efe->impIdent), 0, sizeof(regid));
+		strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER);
+		efe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+		efe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+		efe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode));
+		efe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode));
+		efe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode));
+		efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE);
+		crclen = sizeof(struct extendedFileEntry);
+	}
+	if (UDF_I_STRAT4096(inode))
+	{
+		fe->icbTag.strategyType = cpu_to_le16(4096);
+		fe->icbTag.strategyParameter = cpu_to_le16(1);
+		fe->icbTag.numEntries = cpu_to_le16(2);
+	}
+	else
+	{
+		fe->icbTag.strategyType = cpu_to_le16(4);
+		fe->icbTag.numEntries = cpu_to_le16(1);
+	}
+
+	if (S_ISDIR(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_DIRECTORY;
+	else if (S_ISREG(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_REGULAR;
+	else if (S_ISLNK(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_SYMLINK;
+	else if (S_ISBLK(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_BLOCK;
+	else if (S_ISCHR(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_CHAR;
+	else if (S_ISFIFO(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_FIFO;
+	else if (S_ISSOCK(inode->i_mode))
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_SOCKET;
+
+	icbflags =	UDF_I_ALLOCTYPE(inode) |
+			((inode->i_mode & S_ISUID) ? ICBTAG_FLAG_SETUID : 0) |
+			((inode->i_mode & S_ISGID) ? ICBTAG_FLAG_SETGID : 0) |
+			((inode->i_mode & S_ISVTX) ? ICBTAG_FLAG_STICKY : 0) |
+			(le16_to_cpu(fe->icbTag.flags) &
+				~(ICBTAG_FLAG_AD_MASK | ICBTAG_FLAG_SETUID |
+				ICBTAG_FLAG_SETGID | ICBTAG_FLAG_STICKY));
+
+	fe->icbTag.flags = cpu_to_le16(icbflags);
+	if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200)
+		fe->descTag.descVersion = cpu_to_le16(3);
+	else
+		fe->descTag.descVersion = cpu_to_le16(2);
+	fe->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb));
+	fe->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
+	crclen += UDF_I_LENEATTR(inode) + UDF_I_LENALLOC(inode) - sizeof(tag);
+	fe->descTag.descCRCLength = cpu_to_le16(crclen);
+	fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0));
+
+	fe->descTag.tagChecksum = 0;
+	for (i=0; i<16; i++)
+		if (i != 4)
+			fe->descTag.tagChecksum += ((uint8_t *)&(fe->descTag))[i];
+
+	/* write the data blocks */
+	mark_buffer_dirty(bh);
+	if (do_sync)
+	{
+		sync_dirty_buffer(bh);
+		if (buffer_req(bh) && !buffer_uptodate(bh))
+		{
+			printk("IO error syncing udf inode [%s:%08lx]\n",
+				inode->i_sb->s_id, inode->i_ino);
+			err = -EIO;
+		}
+	}
+	udf_release_data(bh);
+	return err;
+}
+
+struct inode *
+udf_iget(struct super_block *sb, kernel_lb_addr ino)
+{
+	unsigned long block = udf_get_lb_pblock(sb, ino, 0);
+	struct inode *inode = iget_locked(sb, block);
+
+	if (!inode)
+		return NULL;
+
+	if (inode->i_state & I_NEW) {
+		memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(kernel_lb_addr));
+		__udf_read_inode(inode);
+		unlock_new_inode(inode);
+	}
+
+	if (is_bad_inode(inode))
+		goto out_iput;
+
+	if (ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum)) {
+		udf_debug("block=%d, partition=%d out of range\n",
+			ino.logicalBlockNum, ino.partitionReferenceNum);
+		make_bad_inode(inode);
+		goto out_iput;
+	}
+
+	return inode;
+
+ out_iput:
+	iput(inode);
+	return NULL;
+}
+
+int8_t udf_add_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset,
+	kernel_lb_addr eloc, uint32_t elen, struct buffer_head **bh, int inc)
+{
+	int adsize;
+	short_ad *sad = NULL;
+	long_ad *lad = NULL;
+	struct allocExtDesc *aed;
+	int8_t etype;
+	uint8_t *ptr;
+
+	if (!*bh)
+		ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode);
+	else
+		ptr = (*bh)->b_data + *extoffset;
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(long_ad);
+	else
+		return -1;
+
+	if (*extoffset + (2 * adsize) > inode->i_sb->s_blocksize)
+	{
+		char *sptr, *dptr;
+		struct buffer_head *nbh;
+		int err, loffset;
+		kernel_lb_addr obloc = *bloc;
+
+		if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, NULL,
+			obloc.partitionReferenceNum, obloc.logicalBlockNum, &err)))
+		{
+			return -1;
+		}
+		if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb,
+			*bloc, 0))))
+		{
+			return -1;
+		}
+		lock_buffer(nbh);
+		memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize);
+		set_buffer_uptodate(nbh);
+		unlock_buffer(nbh);
+		mark_buffer_dirty_inode(nbh, inode);
+
+		aed = (struct allocExtDesc *)(nbh->b_data);
+		if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
+			aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum);
+		if (*extoffset + adsize > inode->i_sb->s_blocksize)
+		{
+			loffset = *extoffset;
+			aed->lengthAllocDescs = cpu_to_le32(adsize);
+			sptr = ptr - adsize;
+			dptr = nbh->b_data + sizeof(struct allocExtDesc);
+			memcpy(dptr, sptr, adsize);
+			*extoffset = sizeof(struct allocExtDesc) + adsize;
+		}
+		else
+		{
+			loffset = *extoffset + adsize;
+			aed->lengthAllocDescs = cpu_to_le32(0);
+			sptr = ptr;
+			*extoffset = sizeof(struct allocExtDesc);
+
+			if (*bh)
+			{
+				aed = (struct allocExtDesc *)(*bh)->b_data;
+				aed->lengthAllocDescs =
+					cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+			}
+			else
+			{
+				UDF_I_LENALLOC(inode) += adsize;
+				mark_inode_dirty(inode);
+			}
+		}
+		if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200)
+			udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1,
+				bloc->logicalBlockNum, sizeof(tag));
+		else
+			udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1,
+				bloc->logicalBlockNum, sizeof(tag));
+		switch (UDF_I_ALLOCTYPE(inode))
+		{
+			case ICBTAG_FLAG_AD_SHORT:
+			{
+				sad = (short_ad *)sptr;
+				sad->extLength = cpu_to_le32(
+					EXT_NEXT_EXTENT_ALLOCDECS |
+					inode->i_sb->s_blocksize);
+				sad->extPosition = cpu_to_le32(bloc->logicalBlockNum);
+				break;
+			}
+			case ICBTAG_FLAG_AD_LONG:
+			{
+				lad = (long_ad *)sptr;
+				lad->extLength = cpu_to_le32(
+					EXT_NEXT_EXTENT_ALLOCDECS |
+					inode->i_sb->s_blocksize);
+				lad->extLocation = cpu_to_lelb(*bloc);
+				memset(lad->impUse, 0x00, sizeof(lad->impUse));
+				break;
+			}
+		}
+		if (*bh)
+		{
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+				udf_update_tag((*bh)->b_data, loffset);
+			else
+				udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(*bh, inode);
+			udf_release_data(*bh);
+		}
+		else
+			mark_inode_dirty(inode);
+		*bh = nbh;
+	}
+
+	etype = udf_write_aext(inode, *bloc, extoffset, eloc, elen, *bh, inc);
+
+	if (!*bh)
+	{
+		UDF_I_LENALLOC(inode) += adsize;
+		mark_inode_dirty(inode);
+	}
+	else
+	{
+		aed = (struct allocExtDesc *)(*bh)->b_data;
+		aed->lengthAllocDescs =
+			cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+		if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+			udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize));
+		else
+			udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc));
+		mark_buffer_dirty_inode(*bh, inode);
+	}
+
+	return etype;
+}
+
+int8_t udf_write_aext(struct inode *inode, kernel_lb_addr bloc, int *extoffset,
+    kernel_lb_addr eloc, uint32_t elen, struct buffer_head *bh, int inc)
+{
+	int adsize;
+	uint8_t *ptr;
+
+	if (!bh)
+		ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode);
+	else
+	{
+		ptr = bh->b_data + *extoffset;
+		atomic_inc(&bh->b_count);
+	}
+
+	switch (UDF_I_ALLOCTYPE(inode))
+	{
+		case ICBTAG_FLAG_AD_SHORT:
+		{
+			short_ad *sad = (short_ad *)ptr;
+			sad->extLength = cpu_to_le32(elen);
+			sad->extPosition = cpu_to_le32(eloc.logicalBlockNum);
+			adsize = sizeof(short_ad);
+			break;
+		}
+		case ICBTAG_FLAG_AD_LONG:
+		{
+			long_ad *lad = (long_ad *)ptr;
+			lad->extLength = cpu_to_le32(elen);
+			lad->extLocation = cpu_to_lelb(eloc);
+			memset(lad->impUse, 0x00, sizeof(lad->impUse));
+			adsize = sizeof(long_ad);
+			break;
+		}
+		default:
+			return -1;
+	}
+
+	if (bh)
+	{
+		if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+		{
+			struct allocExtDesc *aed = (struct allocExtDesc *)(bh)->b_data;
+			udf_update_tag((bh)->b_data,
+				le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc));
+		}
+		mark_buffer_dirty_inode(bh, inode);
+		udf_release_data(bh);
+	}
+	else
+		mark_inode_dirty(inode);
+
+	if (inc)
+		*extoffset += adsize;
+	return (elen >> 30);
+}
+
+int8_t udf_next_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset,
+	kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc)
+{
+	int8_t etype;
+
+	while ((etype = udf_current_aext(inode, bloc, extoffset, eloc, elen, bh, inc)) ==
+		(EXT_NEXT_EXTENT_ALLOCDECS >> 30))
+	{
+		*bloc = *eloc;
+		*extoffset = sizeof(struct allocExtDesc);
+		udf_release_data(*bh);
+		if (!(*bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
+		{
+			udf_debug("reading block %d failed!\n",
+				udf_get_lb_pblock(inode->i_sb, *bloc, 0));
+			return -1;
+		}
+	}
+
+	return etype;
+}
+
+int8_t udf_current_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset,
+	kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc)
+{
+	int alen;
+	int8_t etype;
+	uint8_t *ptr;
+
+	if (!*bh)
+	{
+		if (!(*extoffset))
+			*extoffset = udf_file_entry_alloc_offset(inode);
+		ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode);
+		alen = udf_file_entry_alloc_offset(inode) + UDF_I_LENALLOC(inode);
+	}
+	else
+	{
+		if (!(*extoffset))
+			*extoffset = sizeof(struct allocExtDesc);
+		ptr = (*bh)->b_data + *extoffset;
+		alen = sizeof(struct allocExtDesc) + le32_to_cpu(((struct allocExtDesc *)(*bh)->b_data)->lengthAllocDescs);
+	}
+
+	switch (UDF_I_ALLOCTYPE(inode))
+	{
+		case ICBTAG_FLAG_AD_SHORT:
+		{
+			short_ad *sad;
+
+			if (!(sad = udf_get_fileshortad(ptr, alen, extoffset, inc)))
+				return -1;
+
+			etype = le32_to_cpu(sad->extLength) >> 30;
+			eloc->logicalBlockNum = le32_to_cpu(sad->extPosition);
+			eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+			*elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK;
+			break;
+		}
+		case ICBTAG_FLAG_AD_LONG:
+		{
+			long_ad *lad;
+
+			if (!(lad = udf_get_filelongad(ptr, alen, extoffset, inc)))
+				return -1;
+
+			etype = le32_to_cpu(lad->extLength) >> 30;
+			*eloc = lelb_to_cpu(lad->extLocation);
+			*elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK;
+			break;
+		}
+		default:
+		{
+			udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode));
+			return -1;
+		}
+	}
+
+	return etype;
+}
+
+static int8_t
+udf_insert_aext(struct inode *inode, kernel_lb_addr bloc, int extoffset,
+		kernel_lb_addr neloc, uint32_t nelen, struct buffer_head *bh)
+{
+	kernel_lb_addr oeloc;
+	uint32_t oelen;
+	int8_t etype;
+
+	if (bh)
+		atomic_inc(&bh->b_count);
+
+	while ((etype = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1)
+	{
+		udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
+
+		neloc = oeloc;
+		nelen = (etype << 30) | oelen;
+	}
+	udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1);
+	udf_release_data(bh);
+	return (nelen >> 30);
+}
+
+int8_t udf_delete_aext(struct inode *inode, kernel_lb_addr nbloc, int nextoffset,
+	kernel_lb_addr eloc, uint32_t elen, struct buffer_head *nbh)
+{
+	struct buffer_head *obh;
+	kernel_lb_addr obloc;
+	int oextoffset, adsize;
+	int8_t etype;
+	struct allocExtDesc *aed;
+
+	if (nbh)
+	{
+		atomic_inc(&nbh->b_count);
+		atomic_inc(&nbh->b_count);
+	}
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(long_ad);
+	else
+		adsize = 0;
+
+	obh = nbh;
+	obloc = nbloc;
+	oextoffset = nextoffset;
+
+	if (udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1) == -1)
+		return -1;
+
+	while ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1)
+	{
+		udf_write_aext(inode, obloc, &oextoffset, eloc, (etype << 30) | elen, obh, 1);
+		if (obh != nbh)
+		{
+			obloc = nbloc;
+			udf_release_data(obh);
+			atomic_inc(&nbh->b_count);
+			obh = nbh;
+			oextoffset = nextoffset - adsize;
+		}
+	}
+	memset(&eloc, 0x00, sizeof(kernel_lb_addr));
+	elen = 0;
+
+	if (nbh != obh)
+	{
+		udf_free_blocks(inode->i_sb, inode, nbloc, 0, 1);
+		udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1);
+		udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1);
+		if (!obh)
+		{
+			UDF_I_LENALLOC(inode) -= (adsize * 2);
+			mark_inode_dirty(inode);
+		}
+		else
+		{
+			aed = (struct allocExtDesc *)(obh)->b_data;
+			aed->lengthAllocDescs =
+				cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize));
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+				udf_update_tag((obh)->b_data, oextoffset - (2*adsize));
+			else
+				udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(obh, inode);
+		}
+	}
+	else
+	{
+		udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1);
+		if (!obh)
+		{
+			UDF_I_LENALLOC(inode) -= adsize;
+			mark_inode_dirty(inode);
+		}
+		else
+		{
+			aed = (struct allocExtDesc *)(obh)->b_data;
+			aed->lengthAllocDescs =
+				cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize);
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+				udf_update_tag((obh)->b_data, oextoffset - adsize);
+			else
+				udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(obh, inode);
+		}
+	}
+	
+	udf_release_data(nbh);
+	udf_release_data(obh);
+	return (elen >> 30);
+}
+
+int8_t inode_bmap(struct inode *inode, int block, kernel_lb_addr *bloc, uint32_t *extoffset,
+	kernel_lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh)
+{
+	uint64_t lbcount = 0, bcount = (uint64_t)block << inode->i_sb->s_blocksize_bits;
+	int8_t etype;
+
+	if (block < 0)
+	{
+		printk(KERN_ERR "udf: inode_bmap: block < 0\n");
+		return -1;
+	}
+	if (!inode)
+	{
+		printk(KERN_ERR "udf: inode_bmap: NULL inode\n");
+		return -1;
+	}
+
+	*extoffset = 0;
+	*elen = 0;
+	*bloc = UDF_I_LOCATION(inode);
+
+	do
+	{
+		if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1)
+		{
+			*offset = bcount - lbcount;
+			UDF_I_LENEXTENTS(inode) = lbcount;
+			return -1;
+		}
+		lbcount += *elen;
+	} while (lbcount <= bcount);
+
+	*offset = bcount + *elen - lbcount;
+
+	return etype;
+}
+
+long udf_block_map(struct inode *inode, long block)
+{
+	kernel_lb_addr eloc, bloc;
+	uint32_t offset, extoffset, elen;
+	struct buffer_head *bh = NULL;
+	int ret;
+
+	lock_kernel();
+
+	if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+		ret = udf_get_lb_pblock(inode->i_sb, eloc, offset >> inode->i_sb->s_blocksize_bits);
+	else
+		ret = 0;
+
+	unlock_kernel();
+	udf_release_data(bh);
+
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV))
+		return udf_fixed_to_variable(ret);
+	else
+		return ret;
+}
diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c
new file mode 100644
index 000000000000..2da5087dfe05
--- /dev/null
+++ b/fs/udf/lowlevel.c
@@ -0,0 +1,77 @@
+/*
+ * lowlevel.c
+ *
+ * PURPOSE
+ *  Low Level Device Routines for the UDF filesystem
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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
+ *
+ * HISTORY
+ *
+ *  03/26/99 blf  Created.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/blkdev.h>
+#include <linux/cdrom.h>
+#include <asm/uaccess.h>
+
+#include <linux/udf_fs.h>
+#include "udf_sb.h"
+
+unsigned int 
+udf_get_last_session(struct super_block *sb)
+{
+	struct cdrom_multisession ms_info;
+	unsigned int vol_desc_start;
+	struct block_device *bdev = sb->s_bdev;
+	int i;
+
+	vol_desc_start=0;
+	ms_info.addr_format=CDROM_LBA;
+	i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info);
+
+#define WE_OBEY_THE_WRITTEN_STANDARDS 1
+
+	if (i == 0)
+	{
+		udf_debug("XA disk: %s, vol_desc_start=%d\n",
+			(ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba);
+#if WE_OBEY_THE_WRITTEN_STANDARDS
+		if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
+#endif
+			vol_desc_start = ms_info.addr.lba;
+	}
+	else
+	{
+		udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i);
+	}
+	return vol_desc_start;
+}
+
+unsigned long
+udf_get_last_block(struct super_block *sb)
+{
+	struct block_device *bdev = sb->s_bdev;
+	unsigned long lblock = 0;
+
+	if (ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock))
+		lblock = bdev->bd_inode->i_size >> sb->s_blocksize_bits;
+
+	if (lblock)
+		return lblock - 1;
+	else
+		return 0;
+}
diff --git a/fs/udf/misc.c b/fs/udf/misc.c
new file mode 100644
index 000000000000..fd321f9ace83
--- /dev/null
+++ b/fs/udf/misc.c
@@ -0,0 +1,313 @@
+/*
+ * misc.c
+ *
+ * PURPOSE
+ *	Miscellaneous routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998 Dave Boynton
+ *  (C) 1998-2004 Ben Fennema
+ *  (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  04/19/99 blf  partial support for reading/writing specific EA's
+ */
+
+#include "udfdecl.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/udf_fs.h>
+#include <linux/buffer_head.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+struct buffer_head *
+udf_tgetblk(struct super_block *sb, int block)
+{
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
+		return sb_getblk(sb, udf_fixed_to_variable(block));
+	else
+		return sb_getblk(sb, block);
+}
+
+struct buffer_head *
+udf_tread(struct super_block *sb, int block)
+{
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
+		return sb_bread(sb, udf_fixed_to_variable(block));
+	else
+		return sb_bread(sb, block);
+}
+
+struct genericFormat *
+udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type,
+	uint8_t loc)
+{
+	uint8_t *ea = NULL, *ad = NULL;
+	int offset;
+	uint16_t crclen;
+	int i;
+
+	ea = UDF_I_DATA(inode);
+	if (UDF_I_LENEATTR(inode))
+		ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode);
+	else
+	{
+		ad = ea;
+		size += sizeof(struct extendedAttrHeaderDesc);
+	}
+
+	offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) -
+		UDF_I_LENALLOC(inode);
+
+	/* TODO - Check for FreeEASpace */
+
+	if (loc & 0x01 && offset >= size)
+	{
+		struct extendedAttrHeaderDesc *eahd;
+		eahd = (struct extendedAttrHeaderDesc *)ea;
+
+		if (UDF_I_LENALLOC(inode))
+		{
+			memmove(&ad[size], ad, UDF_I_LENALLOC(inode));
+		}
+
+		if (UDF_I_LENEATTR(inode))
+		{
+			/* check checksum/crc */
+			if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD ||
+				le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum)
+			{
+				return NULL;
+			}
+		}
+		else
+		{
+			size -= sizeof(struct extendedAttrHeaderDesc);
+			UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc);
+			eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD);
+			if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200)
+				eahd->descTag.descVersion = cpu_to_le16(3);
+			else
+				eahd->descTag.descVersion = cpu_to_le16(2);
+			eahd->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb));
+			eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
+			eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF);
+			eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF);
+		}
+
+		offset = UDF_I_LENEATTR(inode);
+		if (type < 2048)
+		{
+			if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode))
+			{
+				uint32_t aal = le32_to_cpu(eahd->appAttrLocation);
+				memmove(&ea[offset - aal + size],
+					&ea[aal], offset - aal);
+				offset -= aal;
+				eahd->appAttrLocation = cpu_to_le32(aal + size);
+			}
+			if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode))
+			{
+				uint32_t ial = le32_to_cpu(eahd->impAttrLocation);
+				memmove(&ea[offset - ial + size],
+					&ea[ial], offset - ial);
+				offset -= ial;
+				eahd->impAttrLocation = cpu_to_le32(ial + size);
+			}
+		}
+		else if (type < 65536)
+		{
+			if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode))
+			{
+				uint32_t aal = le32_to_cpu(eahd->appAttrLocation);
+				memmove(&ea[offset - aal + size],
+					&ea[aal], offset - aal);
+				offset -= aal;
+				eahd->appAttrLocation = cpu_to_le32(aal + size);
+			}
+		}
+		/* rewrite CRC + checksum of eahd */
+		crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag);
+		eahd->descTag.descCRCLength = cpu_to_le16(crclen);
+		eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0));
+		eahd->descTag.tagChecksum = 0;
+		for (i=0; i<16; i++)
+			if (i != 4)
+				eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i];
+		UDF_I_LENEATTR(inode) += size;
+		return (struct genericFormat *)&ea[offset];
+	}
+	if (loc & 0x02)
+	{
+	}
+	return NULL;
+}
+
+struct genericFormat *
+udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype)
+{
+	struct genericFormat *gaf;
+	uint8_t *ea = NULL;
+	uint32_t offset;
+
+	ea = UDF_I_DATA(inode);
+
+	if (UDF_I_LENEATTR(inode))
+	{
+		struct extendedAttrHeaderDesc *eahd;
+		eahd = (struct extendedAttrHeaderDesc *)ea;
+
+		/* check checksum/crc */
+		if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD ||
+			le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum)
+		{
+			return NULL;
+		}
+	
+		if (type < 2048)
+			offset = sizeof(struct extendedAttrHeaderDesc);
+		else if (type < 65536)
+			offset = le32_to_cpu(eahd->impAttrLocation);
+		else
+			offset = le32_to_cpu(eahd->appAttrLocation);
+
+		while (offset < UDF_I_LENEATTR(inode))
+		{
+			gaf = (struct genericFormat *)&ea[offset];
+			if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype)
+				return gaf;
+			else
+				offset += le32_to_cpu(gaf->attrLength);
+		}
+	}
+	return NULL;
+}
+
+/*
+ * udf_read_tagged
+ *
+ * PURPOSE
+ *	Read the first block of a tagged descriptor.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+struct buffer_head *
+udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident)
+{
+	tag *tag_p;
+	struct buffer_head *bh = NULL;
+	register uint8_t checksum;
+	register int i;
+
+	/* Read the block */
+	if (block == 0xFFFFFFFF)
+		return NULL;
+
+	bh = udf_tread(sb, block + UDF_SB_SESSION(sb));
+	if (!bh)
+	{
+		udf_debug("block=%d, location=%d: read failed\n", block + UDF_SB_SESSION(sb), location);
+		return NULL;
+	}
+
+	tag_p = (tag *)(bh->b_data);
+
+	*ident = le16_to_cpu(tag_p->tagIdent);
+
+	if ( location != le32_to_cpu(tag_p->tagLocation) )
+	{
+		udf_debug("location mismatch block %u, tag %u != %u\n",
+			block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location);
+		goto error_out;
+	}
+	
+	/* Verify the tag checksum */
+	checksum = 0U;
+	for (i = 0; i < 4; i++)
+		checksum += (uint8_t)(bh->b_data[i]);
+	for (i = 5; i < 16; i++)
+		checksum += (uint8_t)(bh->b_data[i]);
+	if (checksum != tag_p->tagChecksum) {
+		printk(KERN_ERR "udf: tag checksum failed block %d\n", block);
+		goto error_out;
+	}
+
+	/* Verify the tag version */
+	if (le16_to_cpu(tag_p->descVersion) != 0x0002U &&
+		le16_to_cpu(tag_p->descVersion) != 0x0003U)
+	{
+		udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n",
+			le16_to_cpu(tag_p->descVersion), block);
+		goto error_out;
+	}
+
+	/* Verify the descriptor CRC */
+	if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize ||
+		le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag),
+			le16_to_cpu(tag_p->descCRCLength), 0))
+	{
+		return bh;
+	}
+	udf_debug("Crc failure block %d: crc = %d, crclen = %d\n",
+		block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength));
+
+error_out:
+	brelse(bh);
+	return NULL;
+}
+
+struct buffer_head *
+udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, uint32_t offset, uint16_t *ident)
+{
+	return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset),
+		loc.logicalBlockNum + offset, ident);
+}
+
+void udf_release_data(struct buffer_head *bh)
+{
+	if (bh)
+		brelse(bh);
+}
+
+void udf_update_tag(char *data, int length)
+{
+	tag *tptr = (tag *)data;
+	int i;
+
+	length -= sizeof(tag);
+
+	tptr->tagChecksum = 0;
+	tptr->descCRCLength = cpu_to_le16(length);
+	tptr->descCRC = cpu_to_le16(udf_crc(data + sizeof(tag), length, 0));
+
+	for (i=0; i<16; i++)
+		if (i != 4)
+			tptr->tagChecksum += (uint8_t)(data[i]);
+}
+
+void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum,
+	uint32_t loc, int length)
+{
+	tag *tptr = (tag *)data;
+	tptr->tagIdent = cpu_to_le16(ident);
+	tptr->descVersion = cpu_to_le16(version);
+	tptr->tagSerialNum = cpu_to_le16(snum);
+	tptr->tagLocation = cpu_to_le32(loc);
+	udf_update_tag(data, length);
+}
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
new file mode 100644
index 000000000000..3f6dc7112bc6
--- /dev/null
+++ b/fs/udf/namei.c
@@ -0,0 +1,1334 @@
+/*
+ * namei.c
+ *
+ * PURPOSE
+ *      Inode name handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *      E-mail regarding any portion of the Linux UDF file system should be
+ *      directed to the development team mailing list (run by majordomo):
+ *              linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998-2004 Ben Fennema
+ *  (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  12/12/98 blf  Created. Split out the lookup code from dir.c
+ *  04/19/99 blf  link, mknod, symlink support
+ */
+
+#include "udfdecl.h"
+
+#include "udf_i.h"
+#include "udf_sb.h"
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/quotaops.h>
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+
+static inline int udf_match(int len1, const char *name1, int len2, const char *name2)
+{
+	if (len1 != len2)
+		return 0;
+	return !memcmp(name1, name2, len1);
+}
+
+int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
+	struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh,
+	uint8_t *impuse, uint8_t *fileident)
+{
+	uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag);
+	uint16_t crc;
+	uint8_t checksum = 0;
+	int i;
+	int offset;
+	uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse);
+	uint8_t lfi = cfi->lengthFileIdent;
+	int padlen = fibh->eoffset - fibh->soffset - liu - lfi -
+		sizeof(struct fileIdentDesc);
+	int adinicb = 0;
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+		adinicb = 1;
+
+	offset = fibh->soffset + sizeof(struct fileIdentDesc);
+
+	if (impuse)
+	{
+		if (adinicb || (offset + liu < 0))
+			memcpy((uint8_t *)sfi->impUse, impuse, liu);
+		else if (offset >= 0)
+			memcpy(fibh->ebh->b_data + offset, impuse, liu);
+		else
+		{
+			memcpy((uint8_t *)sfi->impUse, impuse, -offset);
+			memcpy(fibh->ebh->b_data, impuse - offset, liu + offset);
+		}
+	}
+
+	offset += liu;
+
+	if (fileident)
+	{
+		if (adinicb || (offset + lfi < 0))
+			memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi);
+		else if (offset >= 0)
+			memcpy(fibh->ebh->b_data + offset, fileident, lfi);
+		else
+		{
+			memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset);
+			memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset);
+		}
+	}
+
+	offset += lfi;
+
+	if (adinicb || (offset + padlen < 0))
+		memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen);
+	else if (offset >= 0)
+		memset(fibh->ebh->b_data + offset, 0x00, padlen);
+	else
+	{
+		memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset);
+		memset(fibh->ebh->b_data, 0x00, padlen + offset);
+	}
+
+	crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) -
+		sizeof(tag), 0);
+
+	if (fibh->sbh == fibh->ebh)
+		crc = udf_crc((uint8_t *)sfi->impUse,
+			crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc);
+	else if (sizeof(struct fileIdentDesc) >= -fibh->soffset)
+		crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset,
+			crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc);
+	else
+	{
+		crc = udf_crc((uint8_t *)sfi->impUse,
+			-fibh->soffset - sizeof(struct fileIdentDesc), crc);
+		crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc);
+	}
+
+	cfi->descTag.descCRC = cpu_to_le16(crc);
+	cfi->descTag.descCRCLength = cpu_to_le16(crclen);
+
+	for (i=0; i<16; i++)
+		if (i != 4)
+			checksum += ((uint8_t *)&cfi->descTag)[i];
+
+	cfi->descTag.tagChecksum = checksum;
+	if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset))
+		memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc));
+	else
+	{
+		memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset);
+		memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset,
+			sizeof(struct fileIdentDesc) + fibh->soffset);
+	}
+
+	if (adinicb)
+		mark_inode_dirty(inode);
+	else
+	{
+		if (fibh->sbh != fibh->ebh)
+			mark_buffer_dirty_inode(fibh->ebh, inode);
+		mark_buffer_dirty_inode(fibh->sbh, inode);
+	}
+	return 0;
+}
+
+static struct fileIdentDesc *
+udf_find_entry(struct inode *dir, struct dentry *dentry,
+	struct udf_fileident_bh *fibh,
+	struct fileIdentDesc *cfi)
+{
+	struct fileIdentDesc *fi=NULL;
+	loff_t f_pos;
+	int block, flen;
+	char fname[UDF_NAME_LEN];
+	char *nameptr;
+	uint8_t lfi;
+	uint16_t liu;
+	loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+	kernel_lb_addr bloc, eloc;
+	uint32_t extoffset, elen, offset;
+	struct buffer_head *bh = NULL;
+
+	if (!dir)
+		return NULL;
+
+	f_pos = (udf_ext0_offset(dir) >> 2);
+
+	fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+		fibh->sbh = fibh->ebh = NULL;
+	else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
+		&bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+	{
+		offset >>= dir->i_sb->s_blocksize_bits;
+		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+		if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+		{
+			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+				extoffset -= sizeof(short_ad);
+			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+				extoffset -= sizeof(long_ad);
+		}
+		else
+			offset = 0;
+
+		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
+		{
+			udf_release_data(bh);
+			return NULL;
+		}
+	}
+	else
+	{
+		udf_release_data(bh);
+		return NULL;
+	}
+
+	while ( (f_pos < size) )
+	{
+		fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+		if (!fi)
+		{
+			if (fibh->sbh != fibh->ebh)
+				udf_release_data(fibh->ebh);
+			udf_release_data(fibh->sbh);
+			udf_release_data(bh);
+			return NULL;
+		}
+
+		liu = le16_to_cpu(cfi->lengthOfImpUse);
+		lfi = cfi->lengthFileIdent;
+
+		if (fibh->sbh == fibh->ebh)
+		{
+			nameptr = fi->fileIdent + liu;
+		}
+		else
+		{
+			int poffset;	/* Unpaded ending offset */
+
+			poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi;
+
+			if (poffset >= lfi)
+				nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi);
+			else
+			{
+				nameptr = fname;
+				memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
+				memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset);
+			}
+		}
+
+		if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
+		{
+			if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
+				continue;
+		}
+	    
+		if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
+		{
+			if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
+				continue;
+		}
+
+		if (!lfi)
+			continue;
+
+		if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)))
+		{
+			if (udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name))
+			{
+				udf_release_data(bh);
+				return fi;
+			}
+		}
+	}
+	if (fibh->sbh != fibh->ebh)
+		udf_release_data(fibh->ebh);
+	udf_release_data(fibh->sbh);
+	udf_release_data(bh);
+	return NULL;
+}
+
+/*
+ * udf_lookup
+ *
+ * PURPOSE
+ *	Look-up the inode for a given name.
+ *
+ * DESCRIPTION
+ *	Required - lookup_dentry() will return -ENOTDIR if this routine is not
+ *	available for a directory. The filesystem is useless if this routine is
+ *	not available for at least the filesystem's root directory.
+ *
+ *	This routine is passed an incomplete dentry - it must be completed by
+ *	calling d_add(dentry, inode). If the name does not exist, then the
+ *	specified inode must be set to null. An error should only be returned
+ *	when the lookup fails for a reason other than the name not existing.
+ *	Note that the directory inode semaphore is held during the call.
+ *
+ *	Refer to lookup_dentry() in fs/namei.c
+ *	lookup_dentry() -> lookup() -> real_lookup() -> .
+ *
+ * PRE-CONDITIONS
+ *	dir			Pointer to inode of parent directory.
+ *	dentry			Pointer to dentry to complete.
+ *	nd			Pointer to lookup nameidata
+ *
+ * POST-CONDITIONS
+ *	<return>		Zero on success.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+
+static struct dentry *
+udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+{
+	struct inode *inode = NULL;
+	struct fileIdentDesc cfi, *fi;
+	struct udf_fileident_bh fibh;
+
+	if (dentry->d_name.len > UDF_NAME_LEN-2)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	lock_kernel();
+#ifdef UDF_RECOVERY
+	/* temporary shorthand for specifying files by inode number */
+	if (!strncmp(dentry->d_name.name, ".B=", 3) )
+	{
+		kernel_lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) };
+		inode = udf_iget(dir->i_sb, lb);
+		if (!inode)
+		{
+			unlock_kernel();
+			return ERR_PTR(-EACCES);
+		}
+	}
+	else
+#endif /* UDF_RECOVERY */
+
+	if ((fi = udf_find_entry(dir, dentry, &fibh, &cfi)))
+	{
+		if (fibh.sbh != fibh.ebh)
+			udf_release_data(fibh.ebh);
+		udf_release_data(fibh.sbh);
+
+		inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation));
+		if ( !inode )
+		{
+			unlock_kernel();
+			return ERR_PTR(-EACCES);
+		}
+	}
+	unlock_kernel();
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static struct fileIdentDesc *
+udf_add_entry(struct inode *dir, struct dentry *dentry,
+	struct udf_fileident_bh *fibh,
+	struct fileIdentDesc *cfi, int *err)
+{
+	struct super_block *sb;
+	struct fileIdentDesc *fi=NULL;
+	char name[UDF_NAME_LEN], fname[UDF_NAME_LEN];
+	int namelen;
+	loff_t f_pos;
+	int flen;
+	char *nameptr;
+	loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+	int nfidlen;
+	uint8_t lfi;
+	uint16_t liu;
+	int block;
+	kernel_lb_addr bloc, eloc;
+	uint32_t extoffset, elen, offset;
+	struct buffer_head *bh = NULL;
+
+	sb = dir->i_sb;
+
+	if (dentry)
+	{
+		if (!dentry->d_name.len)
+		{
+			*err = -EINVAL;
+			return NULL;
+		}
+
+		if ( !(namelen = udf_put_filename(sb, dentry->d_name.name, name, dentry->d_name.len)))
+		{
+			*err = -ENAMETOOLONG;
+			return NULL;
+		}
+	}
+	else
+		namelen = 0;
+
+	nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3;
+
+	f_pos = (udf_ext0_offset(dir) >> 2);
+
+	fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+		fibh->sbh = fibh->ebh = NULL;
+	else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
+		&bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+	{
+		offset >>= dir->i_sb->s_blocksize_bits;
+		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+		if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+		{
+			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+				extoffset -= sizeof(short_ad);
+			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+				extoffset -= sizeof(long_ad);
+		}
+		else
+			offset = 0;
+
+		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
+		{
+			udf_release_data(bh);
+			*err = -EIO;
+			return NULL;
+		}
+
+		block = UDF_I_LOCATION(dir).logicalBlockNum;
+
+	}
+	else
+	{
+		block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0);
+		fibh->sbh = fibh->ebh = NULL;
+		fibh->soffset = fibh->eoffset = sb->s_blocksize;
+		goto add;
+	}
+
+	while ( (f_pos < size) )
+	{
+		fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+		if (!fi)
+		{
+			if (fibh->sbh != fibh->ebh)
+				udf_release_data(fibh->ebh);
+			udf_release_data(fibh->sbh);
+			udf_release_data(bh);
+			*err = -EIO;
+			return NULL;
+		}
+
+		liu = le16_to_cpu(cfi->lengthOfImpUse);
+		lfi = cfi->lengthFileIdent;
+
+		if (fibh->sbh == fibh->ebh)
+			nameptr = fi->fileIdent + liu;
+		else
+		{
+			int poffset;	/* Unpaded ending offset */
+
+			poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi;
+
+			if (poffset >= lfi)
+				nameptr = (char *)(fibh->ebh->b_data + poffset - lfi);
+			else
+			{
+				nameptr = fname;
+				memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
+				memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset);
+			}
+		}
+
+		if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
+		{
+			if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen)
+			{
+				udf_release_data(bh);
+				cfi->descTag.tagSerialNum = cpu_to_le16(1);
+				cfi->fileVersionNum = cpu_to_le16(1);
+				cfi->fileCharacteristics = 0;
+				cfi->lengthFileIdent = namelen;
+				cfi->lengthOfImpUse = cpu_to_le16(0);
+				if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name))
+					return fi;
+				else
+				{
+					*err = -EIO;
+					return NULL;
+				}
+			}
+		}
+
+		if (!lfi || !dentry)
+			continue;
+
+		if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) &&
+			udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name))
+		{
+			if (fibh->sbh != fibh->ebh)
+				udf_release_data(fibh->ebh);
+			udf_release_data(fibh->sbh);
+			udf_release_data(bh);
+			*err = -EEXIST;
+			return NULL;
+		}
+	}
+
+add:
+	f_pos += nfidlen;
+
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB &&
+		sb->s_blocksize - fibh->eoffset < nfidlen)
+	{
+		udf_release_data(bh);
+		bh = NULL;
+		fibh->soffset -= udf_ext0_offset(dir);
+		fibh->eoffset -= udf_ext0_offset(dir);
+		f_pos -= (udf_ext0_offset(dir) >> 2);
+		if (fibh->sbh != fibh->ebh)
+			udf_release_data(fibh->ebh);
+		udf_release_data(fibh->sbh);
+		if (!(fibh->sbh = fibh->ebh = udf_expand_dir_adinicb(dir, &block, err)))
+			return NULL;
+		bloc = UDF_I_LOCATION(dir);
+		eloc.logicalBlockNum = block;
+		eloc.partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum;
+		elen = dir->i_sb->s_blocksize;
+		extoffset = udf_file_entry_alloc_offset(dir);
+		if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+			extoffset += sizeof(short_ad);
+		else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+			extoffset += sizeof(long_ad);
+	}
+
+	if (sb->s_blocksize - fibh->eoffset >= nfidlen)
+	{
+		fibh->soffset = fibh->eoffset;
+		fibh->eoffset += nfidlen;
+		if (fibh->sbh != fibh->ebh)
+		{
+			udf_release_data(fibh->sbh);
+			fibh->sbh = fibh->ebh;
+		}
+
+		if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+		{
+			block = UDF_I_LOCATION(dir).logicalBlockNum;
+			fi = (struct fileIdentDesc *)(UDF_I_DATA(dir) + fibh->soffset - udf_ext0_offset(dir) + UDF_I_LENEATTR(dir));
+		}
+		else
+		{
+			block = eloc.logicalBlockNum + ((elen - 1) >>
+				dir->i_sb->s_blocksize_bits);
+			fi = (struct fileIdentDesc *)(fibh->sbh->b_data + fibh->soffset);
+		}
+	}
+	else
+	{
+		fibh->soffset = fibh->eoffset - sb->s_blocksize;
+		fibh->eoffset += nfidlen - sb->s_blocksize;
+		if (fibh->sbh != fibh->ebh)
+		{
+			udf_release_data(fibh->sbh);
+			fibh->sbh = fibh->ebh;
+		}
+
+		block = eloc.logicalBlockNum + ((elen - 1) >>
+			dir->i_sb->s_blocksize_bits);
+
+		if (!(fibh->ebh = udf_bread(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err)))
+		{
+			udf_release_data(bh);
+			udf_release_data(fibh->sbh);
+			return NULL;
+		}
+
+		if (!(fibh->soffset))
+		{
+			if (udf_next_aext(dir, &bloc, &extoffset, &eloc, &elen, &bh, 1) ==
+				(EXT_RECORDED_ALLOCATED >> 30))
+			{
+				block = eloc.logicalBlockNum + ((elen - 1) >>
+					dir->i_sb->s_blocksize_bits);
+			}
+			else
+				block ++;
+
+			udf_release_data(fibh->sbh);
+			fibh->sbh = fibh->ebh;
+			fi = (struct fileIdentDesc *)(fibh->sbh->b_data);
+		}
+		else
+		{
+			fi = (struct fileIdentDesc *)
+				(fibh->sbh->b_data + sb->s_blocksize + fibh->soffset);
+		}
+	}
+
+	memset(cfi, 0, sizeof(struct fileIdentDesc));
+	if (UDF_SB_UDFREV(sb) >= 0x0200)
+		udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, sizeof(tag));
+	else
+		udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, sizeof(tag));
+	cfi->fileVersionNum = cpu_to_le16(1);
+	cfi->lengthFileIdent = namelen;
+	cfi->lengthOfImpUse = cpu_to_le16(0);
+	if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name))
+	{
+		udf_release_data(bh);
+		dir->i_size += nfidlen;
+		if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+			UDF_I_LENALLOC(dir) += nfidlen;
+		mark_inode_dirty(dir);
+		return fi;
+	}
+	else
+	{
+		udf_release_data(bh);
+		if (fibh->sbh != fibh->ebh)
+			udf_release_data(fibh->ebh);
+		udf_release_data(fibh->sbh);
+		*err = -EIO;
+		return NULL;
+	}
+}
+
+static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi,
+	struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi)
+{
+	cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED;
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
+		memset(&(cfi->icb), 0x00, sizeof(long_ad));
+	return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL);
+}
+
+static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
+{
+	struct udf_fileident_bh fibh;
+	struct inode *inode;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+
+	lock_kernel();
+	inode = udf_new_inode(dir, mode, &err);
+	if (!inode)
+	{
+		unlock_kernel();
+		return err;
+	}
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+		inode->i_data.a_ops = &udf_adinicb_aops;
+	else
+		inode->i_data.a_ops = &udf_aops;
+	inode->i_op = &udf_file_inode_operations;
+	inode->i_fop = &udf_file_operations;
+	inode->i_mode = mode;
+	mark_inode_dirty(inode);
+
+	if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+	{
+		inode->i_nlink --;
+		mark_inode_dirty(inode);
+		iput(inode);
+		unlock_kernel();
+		return err;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		mark_inode_dirty(dir);
+	}
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	unlock_kernel();
+	d_instantiate(dentry, inode);
+	return 0;
+}
+
+static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev)
+{
+	struct inode * inode;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+
+	if (!old_valid_dev(rdev))
+		return -EINVAL;
+
+	lock_kernel();
+	err = -EIO;
+	inode = udf_new_inode(dir, mode, &err);
+	if (!inode)
+		goto out;
+
+	inode->i_uid = current->fsuid;
+	init_special_inode(inode, mode, rdev);
+	if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+	{
+		inode->i_nlink --;
+		mark_inode_dirty(inode);
+		iput(inode);
+		unlock_kernel();
+		return err;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		mark_inode_dirty(dir);
+	}
+	mark_inode_dirty(inode);
+
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	d_instantiate(dentry, inode);
+	err = 0;
+out:
+	unlock_kernel();
+	return err;
+}
+
+static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+	struct inode * inode;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+
+	lock_kernel();
+	err = -EMLINK;
+	if (dir->i_nlink >= (256<<sizeof(dir->i_nlink))-1)
+		goto out;
+
+	err = -EIO;
+	inode = udf_new_inode(dir, S_IFDIR, &err);
+	if (!inode)
+		goto out;
+
+	inode->i_op = &udf_dir_inode_operations;
+	inode->i_fop = &udf_dir_operations;
+	if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err)))
+	{
+		inode->i_nlink--;
+		mark_inode_dirty(inode);
+		iput(inode);
+		goto out;
+	}
+	inode->i_nlink = 2;
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(dir));
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL);
+	cfi.fileCharacteristics = FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT;
+	udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL);
+	udf_release_data(fibh.sbh);
+	inode->i_mode = S_IFDIR | mode;
+	if (dir->i_mode & S_ISGID)
+		inode->i_mode |= S_ISGID;
+	mark_inode_dirty(inode);
+
+	if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+	{
+		inode->i_nlink = 0;
+		mark_inode_dirty(inode);
+		iput(inode);
+		goto out;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
+	cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	dir->i_nlink++;
+	mark_inode_dirty(dir);
+	d_instantiate(dentry, inode);
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	err = 0;
+out:
+	unlock_kernel();
+	return err;
+}
+
+static int empty_dir(struct inode *dir)
+{
+	struct fileIdentDesc *fi, cfi;
+	struct udf_fileident_bh fibh;
+	loff_t f_pos;
+	loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+	int block;
+	kernel_lb_addr bloc, eloc;
+	uint32_t extoffset, elen, offset;
+	struct buffer_head *bh = NULL;
+
+	f_pos = (udf_ext0_offset(dir) >> 2);
+
+	fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+		fibh.sbh = fibh.ebh = NULL;
+	else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
+		&bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+	{
+		offset >>= dir->i_sb->s_blocksize_bits;
+		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+		if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+		{
+			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+				extoffset -= sizeof(short_ad);
+			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+				extoffset -= sizeof(long_ad);
+		}
+		else
+			offset = 0;
+
+		if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
+		{
+			udf_release_data(bh);
+			return 0;
+		}
+	}
+	else
+	{
+		udf_release_data(bh);
+		return 0;
+	}
+
+
+	while ( (f_pos < size) )
+	{
+		fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+		if (!fi)
+		{
+			if (fibh.sbh != fibh.ebh)
+				udf_release_data(fibh.ebh);
+			udf_release_data(fibh.sbh);
+			udf_release_data(bh);
+			return 0;
+		}
+
+		if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0)
+		{
+			if (fibh.sbh != fibh.ebh)
+				udf_release_data(fibh.ebh);
+			udf_release_data(fibh.sbh);
+			udf_release_data(bh);
+			return 0;
+		}
+	}
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	udf_release_data(bh);
+	return 1;
+}
+
+static int udf_rmdir(struct inode * dir, struct dentry * dentry)
+{
+	int retval;
+	struct inode * inode = dentry->d_inode;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc *fi, cfi;
+	kernel_lb_addr tloc;
+
+	retval = -ENOENT;
+	lock_kernel();
+	fi = udf_find_entry(dir, dentry, &fibh, &cfi);
+	if (!fi)
+		goto out;
+
+	retval = -EIO;
+	tloc = lelb_to_cpu(cfi.icb.extLocation);
+	if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
+		goto end_rmdir;
+	retval = -ENOTEMPTY;
+	if (!empty_dir(inode))
+		goto end_rmdir;
+	retval = udf_delete_entry(dir, fi, &fibh, &cfi);
+	if (retval)
+		goto end_rmdir;
+	if (inode->i_nlink != 2)
+		udf_warning(inode->i_sb, "udf_rmdir",
+			"empty directory has nlink != 2 (%d)",
+			inode->i_nlink);
+	inode->i_nlink = 0;
+	inode->i_size = 0;
+	mark_inode_dirty(inode);
+	dir->i_nlink --;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
+	mark_inode_dirty(dir);
+
+end_rmdir:
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+out:
+	unlock_kernel();
+	return retval;
+}
+
+static int udf_unlink(struct inode * dir, struct dentry * dentry)
+{
+	int retval;
+	struct inode * inode = dentry->d_inode;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc *fi;
+	struct fileIdentDesc cfi;
+	kernel_lb_addr tloc;
+
+	retval = -ENOENT;
+	lock_kernel();
+	fi = udf_find_entry(dir, dentry, &fibh, &cfi);
+	if (!fi)
+		goto out;
+
+	retval = -EIO;
+	tloc = lelb_to_cpu(cfi.icb.extLocation);
+	if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino)
+		goto end_unlink;
+
+	if (!inode->i_nlink)
+	{
+		udf_debug("Deleting nonexistent file (%lu), %d\n",
+			inode->i_ino, inode->i_nlink);
+		inode->i_nlink = 1;
+	}
+	retval = udf_delete_entry(dir, fi, &fibh, &cfi);
+	if (retval)
+		goto end_unlink;
+	dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb);
+	mark_inode_dirty(dir);
+	inode->i_nlink--;
+	mark_inode_dirty(inode);
+	inode->i_ctime = dir->i_ctime;
+	retval = 0;
+
+end_unlink:
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+out:
+	unlock_kernel();
+	return retval;
+}
+
+static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname)
+{
+	struct inode * inode;
+	struct pathComponent *pc;
+	char *compstart;
+	struct udf_fileident_bh fibh;
+	struct buffer_head *bh = NULL;
+	int eoffset, elen = 0;
+	struct fileIdentDesc *fi;
+	struct fileIdentDesc cfi;
+	char *ea;
+	int err;
+	int block;
+	char name[UDF_NAME_LEN];
+	int namelen;
+
+	lock_kernel();
+	if (!(inode = udf_new_inode(dir, S_IFLNK, &err)))
+		goto out;
+
+	inode->i_mode = S_IFLNK | S_IRWXUGO;
+	inode->i_data.a_ops = &udf_symlink_aops;
+	inode->i_op = &page_symlink_inode_operations;
+
+	if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB)
+	{
+		struct buffer_head *bh = NULL;
+		kernel_lb_addr bloc, eloc;
+		uint32_t elen, extoffset;
+
+		block = udf_new_block(inode->i_sb, inode,
+			UDF_I_LOCATION(inode).partitionReferenceNum,
+			UDF_I_LOCATION(inode).logicalBlockNum, &err);
+		if (!block)
+			goto out_no_entry;
+		bloc = UDF_I_LOCATION(inode);
+		eloc.logicalBlockNum = block;
+		eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+		elen = inode->i_sb->s_blocksize;
+		UDF_I_LENEXTENTS(inode) = elen;
+		extoffset = udf_file_entry_alloc_offset(inode);
+		udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 0);
+		udf_release_data(bh);
+
+		block = udf_get_pblock(inode->i_sb, block,
+			UDF_I_LOCATION(inode).partitionReferenceNum, 0);
+		bh = udf_tread(inode->i_sb, block);
+		lock_buffer(bh);
+		memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
+		mark_buffer_dirty_inode(bh, inode);
+		ea = bh->b_data + udf_ext0_offset(inode);
+	}
+	else
+		ea = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode);
+
+	eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode);
+	pc = (struct pathComponent *)ea;
+
+	if (*symname == '/')
+	{
+		do
+		{
+			symname++;
+		} while (*symname == '/');
+
+		pc->componentType = 1;
+		pc->lengthComponentIdent = 0;
+		pc->componentFileVersionNum = 0;
+		pc += sizeof(struct pathComponent);
+		elen += sizeof(struct pathComponent);
+	}
+
+	err = -ENAMETOOLONG;
+
+	while (*symname)
+	{
+		if (elen + sizeof(struct pathComponent) > eoffset)
+			goto out_no_entry;
+
+		pc = (struct pathComponent *)(ea + elen);
+
+		compstart = (char *)symname;
+
+		do
+		{
+			symname++;
+		} while (*symname && *symname != '/');
+
+		pc->componentType = 5;
+		pc->lengthComponentIdent = 0;
+		pc->componentFileVersionNum = 0;
+		if (compstart[0] == '.')
+		{
+			if ((symname-compstart) == 1)
+				pc->componentType = 4;
+			else if ((symname-compstart) == 2 && compstart[1] == '.')
+				pc->componentType = 3;
+		}
+
+		if (pc->componentType == 5)
+		{
+			if ( !(namelen = udf_put_filename(inode->i_sb, compstart, name, symname-compstart)))
+				goto out_no_entry;
+
+			if (elen + sizeof(struct pathComponent) + namelen > eoffset)
+				goto out_no_entry;
+			else
+				pc->lengthComponentIdent = namelen;
+
+			memcpy(pc->componentIdent, name, namelen);
+		}
+
+		elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
+
+		if (*symname)
+		{
+			do
+			{
+				symname++;
+			} while (*symname == '/');
+		}
+	}
+
+	udf_release_data(bh);
+	inode->i_size = elen;
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+		UDF_I_LENALLOC(inode) = inode->i_size;
+	mark_inode_dirty(inode);
+
+	if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+		goto out_no_entry;
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+	if (UDF_SB_LVIDBH(inode->i_sb))
+	{
+		struct logicalVolHeaderDesc *lvhd;
+		uint64_t uniqueID;
+		lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse);
+		uniqueID = le64_to_cpu(lvhd->uniqueID);
+		*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+			cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
+		if (!(++uniqueID & 0x00000000FFFFFFFFUL))
+			uniqueID += 16;
+		lvhd->uniqueID = cpu_to_le64(uniqueID);
+		mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb));
+	}
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		mark_inode_dirty(dir);
+	}
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	d_instantiate(dentry, inode);
+	err = 0;
+
+out:
+	unlock_kernel();
+	return err;
+
+out_no_entry:
+	inode->i_nlink--;
+	mark_inode_dirty(inode);
+	iput(inode);
+	goto out;
+}
+
+static int udf_link(struct dentry * old_dentry, struct inode * dir,
+	 struct dentry *dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+
+	lock_kernel();
+	if (inode->i_nlink >= (256<<sizeof(inode->i_nlink))-1)
+	{
+		unlock_kernel();
+		return -EMLINK;
+	}
+
+	if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+	{
+		unlock_kernel();
+		return err;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+	if (UDF_SB_LVIDBH(inode->i_sb))
+	{
+		struct logicalVolHeaderDesc *lvhd;
+		uint64_t uniqueID;
+		lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse);
+		uniqueID = le64_to_cpu(lvhd->uniqueID);
+		*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+			cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
+		if (!(++uniqueID & 0x00000000FFFFFFFFUL))
+			uniqueID += 16;
+		lvhd->uniqueID = cpu_to_le64(uniqueID);
+		mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb));
+	}
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+	{
+		mark_inode_dirty(dir);
+	}
+	if (fibh.sbh != fibh.ebh)
+		udf_release_data(fibh.ebh);
+	udf_release_data(fibh.sbh);
+	inode->i_nlink ++;
+	inode->i_ctime = current_fs_time(inode->i_sb);
+	mark_inode_dirty(inode);
+	atomic_inc(&inode->i_count);
+	d_instantiate(dentry, inode);
+	unlock_kernel();
+	return 0;
+}
+
+/* Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int udf_rename (struct inode * old_dir, struct dentry * old_dentry,
+	struct inode * new_dir, struct dentry * new_dentry)
+{
+	struct inode * old_inode = old_dentry->d_inode;
+	struct inode * new_inode = new_dentry->d_inode;
+	struct udf_fileident_bh ofibh, nfibh;
+	struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi;
+	struct buffer_head *dir_bh = NULL;
+	int retval = -ENOENT;
+	kernel_lb_addr tloc;
+
+	lock_kernel();
+	if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi)))
+	{
+		if (ofibh.sbh != ofibh.ebh)
+			udf_release_data(ofibh.ebh);
+		udf_release_data(ofibh.sbh);
+	}
+	tloc = lelb_to_cpu(ocfi.icb.extLocation);
+	if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0)
+					!= old_inode->i_ino)
+		goto end_rename;
+
+	nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi);
+	if (nfi)
+	{
+		if (!new_inode)
+		{
+			if (nfibh.sbh != nfibh.ebh)
+				udf_release_data(nfibh.ebh);
+			udf_release_data(nfibh.sbh);
+			nfi = NULL;
+		}
+	}
+	if (S_ISDIR(old_inode->i_mode))
+	{
+		uint32_t offset = udf_ext0_offset(old_inode);
+
+		if (new_inode)
+		{
+			retval = -ENOTEMPTY;
+			if (!empty_dir(new_inode))
+				goto end_rename;
+		}
+		retval = -EIO;
+		if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB)
+		{
+			dir_fi = udf_get_fileident(UDF_I_DATA(old_inode) -
+				(UDF_I_EFE(old_inode) ?
+					sizeof(struct extendedFileEntry) :
+					sizeof(struct fileEntry)),
+				old_inode->i_sb->s_blocksize, &offset);
+		}
+		else
+		{
+			dir_bh = udf_bread(old_inode, 0, 0, &retval);
+			if (!dir_bh)
+				goto end_rename;
+			dir_fi = udf_get_fileident(dir_bh->b_data, old_inode->i_sb->s_blocksize, &offset);
+		}
+		if (!dir_fi)
+			goto end_rename;
+		tloc = lelb_to_cpu(dir_fi->icb.extLocation);
+		if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0)
+					!= old_dir->i_ino)
+			goto end_rename;
+
+		retval = -EMLINK;
+		if (!new_inode && new_dir->i_nlink >= (256<<sizeof(new_dir->i_nlink))-1)
+			goto end_rename;
+	}
+	if (!nfi)
+	{
+		nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval);
+		if (!nfi)
+			goto end_rename;
+	}
+
+	/*
+	 * Like most other Unix systems, set the ctime for inodes on a
+	 * rename.
+	 */
+	old_inode->i_ctime = current_fs_time(old_inode->i_sb);
+	mark_inode_dirty(old_inode);
+
+	/*
+	 * ok, that's it
+	 */
+	ncfi.fileVersionNum = ocfi.fileVersionNum;
+	ncfi.fileCharacteristics = ocfi.fileCharacteristics;
+	memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad));
+	udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL);
+
+	/* The old fid may have moved - find it again */
+	ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
+	udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
+
+	if (new_inode)
+	{
+		new_inode->i_nlink--;
+		new_inode->i_ctime = current_fs_time(new_inode->i_sb);
+		mark_inode_dirty(new_inode);
+	}
+	old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb);
+	mark_inode_dirty(old_dir);
+
+	if (dir_fi)
+	{
+		dir_fi->icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(new_dir));
+		udf_update_tag((char *)dir_fi, (sizeof(struct fileIdentDesc) +
+			le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3);
+		if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB)
+		{
+			mark_inode_dirty(old_inode);
+		}
+		else
+			mark_buffer_dirty_inode(dir_bh, old_inode);
+		old_dir->i_nlink --;
+		mark_inode_dirty(old_dir);
+		if (new_inode)
+		{
+			new_inode->i_nlink --;
+			mark_inode_dirty(new_inode);
+		}
+		else
+		{
+			new_dir->i_nlink ++;
+			mark_inode_dirty(new_dir);
+		}
+	}
+
+	if (ofi)
+	{
+		if (ofibh.sbh != ofibh.ebh)
+			udf_release_data(ofibh.ebh);
+		udf_release_data(ofibh.sbh);
+	}
+
+	retval = 0;
+
+end_rename:
+	udf_release_data(dir_bh);
+	if (nfi)
+	{
+		if (nfibh.sbh != nfibh.ebh)
+			udf_release_data(nfibh.ebh);
+		udf_release_data(nfibh.sbh);
+	}
+	unlock_kernel();
+	return retval;
+}
+
+struct inode_operations udf_dir_inode_operations = {
+	.lookup				= udf_lookup,
+	.create				= udf_create,
+	.link				= udf_link,
+	.unlink				= udf_unlink,
+	.symlink			= udf_symlink,
+	.mkdir				= udf_mkdir,
+	.rmdir				= udf_rmdir,
+	.mknod				= udf_mknod,
+	.rename				= udf_rename,
+};
diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h
new file mode 100644
index 000000000000..e82aae652697
--- /dev/null
+++ b/fs/udf/osta_udf.h
@@ -0,0 +1,296 @@
+/*
+ * osta_udf.h
+ *
+ * This file is based on OSTA UDF(tm) 2.50 (April 30, 2003)
+ * http://www.osta.org
+ *
+ * Copyright (c) 2001-2004  Ben Fennema <bfennema@falcon.csc.calpoly.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "ecma_167.h"
+
+#ifndef _OSTA_UDF_H
+#define _OSTA_UDF_H 1
+
+/* OSTA CS0 Charspec (UDF 2.50 2.1.2) */
+#define UDF_CHAR_SET_TYPE		0
+#define UDF_CHAR_SET_INFO		"OSTA Compressed Unicode"
+
+/* Entity Identifier (UDF 2.50 2.1.5) */
+/* Identifiers (UDF 2.50 2.1.5.2) */
+#define UDF_ID_DEVELOPER		"*Linux UDFFS"
+#define	UDF_ID_COMPLIANT		"*OSTA UDF Compliant"
+#define UDF_ID_LV_INFO			"*UDF LV Info"
+#define UDF_ID_FREE_EA			"*UDF FreeEASpace"
+#define UDF_ID_FREE_APP_EA		"*UDF FreeAppEASpace"
+#define UDF_ID_DVD_CGMS			"*UDF DVD CGMS Info"
+#define UDF_ID_OS2_EA			"*UDF OS/2 EA"
+#define UDF_ID_OS2_EA_LENGTH		"*UDF OS/2 EALength"
+#define UDF_ID_MAC_VOLUME		"*UDF Mac VolumeInfo"
+#define UDF_ID_MAC_FINDER		"*UDF Mac FinderInfo"
+#define UDF_ID_MAC_UNIQUE		"*UDF Mac UniqueIDTable"
+#define UDF_ID_MAC_RESOURCE		"*UDF Mac ResourceFork"
+#define UDF_ID_VIRTUAL			"*UDF Virtual Partition"
+#define UDF_ID_SPARABLE			"*UDF Sparable Partition"
+#define UDF_ID_ALLOC			"*UDF Virtual Alloc Tbl"
+#define UDF_ID_SPARING			"*UDF Sparing Table"
+#define UDF_ID_METADATA			"*UDF Metadata Partition"
+
+/* Identifier Suffix (UDF 2.50 2.1.5.3) */
+#define IS_DF_HARD_WRITE_PROTECT	0x01
+#define IS_DF_SOFT_WRITE_PROTECT	0x02
+
+struct UDFIdentSuffix
+{
+	__le16		UDFRevision;
+	uint8_t		OSClass;
+	uint8_t		OSIdentifier;
+	uint8_t		reserved[4];
+} __attribute__ ((packed));
+
+struct impIdentSuffix
+{
+	uint8_t		OSClass;
+	uint8_t		OSIdentifier;
+	uint8_t		reserved[6];
+} __attribute__ ((packed));
+
+struct appIdentSuffix
+{
+	uint8_t		impUse[8];
+} __attribute__ ((packed));
+
+/* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */
+/* Implementation Use (UDF 2.50 2.2.6.4) */
+struct logicalVolIntegrityDescImpUse
+{
+	regid		impIdent;
+	__le32		numFiles;
+	__le32		numDirs;
+	__le16		minUDFReadRev;
+	__le16		minUDFWriteRev;
+	__le16		maxUDFWriteRev;
+	uint8_t		impUse[0];
+} __attribute__ ((packed));
+
+/* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */
+/* Implementation Use (UDF 2.50 2.2.7.2) */
+struct impUseVolDescImpUse
+{
+	charspec	LVICharset;
+	dstring		logicalVolIdent[128];
+	dstring		LVInfo1[36];
+	dstring		LVInfo2[36];
+	dstring		LVInfo3[36];
+	regid		impIdent;
+	uint8_t		impUse[128];
+} __attribute__ ((packed));
+
+struct udfPartitionMap2
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	regid		partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+} __attribute__ ((packed));
+
+/* Virtual Partition Map (UDF 2.50 2.2.8) */
+struct virtualPartitionMap
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	regid		partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+	uint8_t		reserved2[24];
+} __attribute__ ((packed));
+
+/* Sparable Partition Map (UDF 2.50 2.2.9) */
+struct sparablePartitionMap
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	regid		partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+	__le16		packetLength;
+	uint8_t		numSparingTables;
+	uint8_t		reserved2[1];
+	__le32		sizeSparingTable;
+	__le32		locSparingTable[4];
+} __attribute__ ((packed));
+
+/* Metadata Partition Map (UDF 2.4.0 2.2.10) */
+struct metadataPartitionMap
+{
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	regid		partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+	__le32		metadataFileLoc;
+	__le32		metadataMirrorFileLoc;
+	__le32		metadataBitmapFileLoc;
+	__le32		allocUnitSize;
+	__le16		alignUnitSize;
+	uint8_t		flags;
+	uint8_t		reserved2[5];
+} __attribute__ ((packed));
+
+/* Virtual Allocation Table (UDF 1.5 2.2.10) */
+struct virtualAllocationTable15
+{
+	__le32		VirtualSector[0];
+	regid		vatIdent;
+	__le32		previousVATICBLoc;
+} __attribute__ ((packed));  
+
+#define ICBTAG_FILE_TYPE_VAT15		0x00U
+
+/* Virtual Allocation Table (UDF 2.50 2.2.11) */
+struct virtualAllocationTable20
+{
+	__le16		lengthHeader;
+	__le16		lengthImpUse;
+	dstring		logicalVolIdent[128];
+	__le32		previousVATICBLoc;
+	__le32		numFiles;
+	__le32		numDirs;
+	__le16		minReadRevision;
+	__le16		minWriteRevision;
+	__le16		maxWriteRevision;
+	__le16		reserved;
+	uint8_t		impUse[0];
+	__le32		vatEntry[0];
+} __attribute__ ((packed));
+
+#define ICBTAG_FILE_TYPE_VAT20		0xF8U
+
+/* Sparing Table (UDF 2.50 2.2.12) */
+struct sparingEntry
+{
+	__le32		origLocation;
+	__le32		mappedLocation;
+} __attribute__ ((packed));
+
+struct sparingTable
+{
+	tag 		descTag;
+	regid		sparingIdent;
+	__le16		reallocationTableLen;
+	__le16		reserved;
+	__le32		sequenceNum;
+	struct sparingEntry
+			mapEntry[0];
+} __attribute__ ((packed));
+
+/* Metadata File (and Metadata Mirror File) (UDF 2.50 2.2.13.1) */
+#define ICBTAG_FILE_TYPE_MAIN		0xFA
+#define ICBTAG_FILE_TYPE_MIRROR		0xFB
+#define ICBTAG_FILE_TYPE_BITMAP		0xFC
+
+/* struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */
+struct allocDescImpUse
+{
+	__le16		flags;
+	uint8_t		impUse[4];
+} __attribute__ ((packed));
+
+#define AD_IU_EXT_ERASED		0x0001
+
+/* Real-Time Files (UDF 2.50 6.11) */
+#define ICBTAG_FILE_TYPE_REALTIME	0xF9U
+
+/* Implementation Use Extended Attribute (UDF 2.50 3.3.4.5) */
+/* FreeEASpace (UDF 2.50 3.3.4.5.1.1) */
+struct freeEaSpace
+{
+	__le16		headerChecksum;
+	uint8_t		freeEASpace[0];
+} __attribute__ ((packed));
+
+/* DVD Copyright Management Information (UDF 2.50 3.3.4.5.1.2) */
+struct DVDCopyrightImpUse 
+{
+	__le16		headerChecksum;
+	uint8_t		CGMSInfo;
+	uint8_t		dataType;
+	uint8_t		protectionSystemInfo[4];
+} __attribute__ ((packed));
+
+/* Application Use Extended Attribute (UDF 2.50 3.3.4.6) */
+/* FreeAppEASpace (UDF 2.50 3.3.4.6.1) */
+struct freeAppEASpace
+{
+	__le16		headerChecksum;
+	uint8_t		freeEASpace[0];
+} __attribute__ ((packed));
+
+/* UDF Defined System Stream (UDF 2.50 3.3.7) */
+#define UDF_ID_UNIQUE_ID		"*UDF Unique ID Mapping Data"
+#define UDF_ID_NON_ALLOC		"*UDF Non-Allocatable Space"
+#define UDF_ID_POWER_CAL		"*UDF Power Cal Table"
+#define UDF_ID_BACKUP			"*UDF Backup"
+
+/* Operating System Identifiers (UDF 2.50 6.3) */
+#define UDF_OS_CLASS_UNDEF		0x00U
+#define UDF_OS_CLASS_DOS		0x01U
+#define UDF_OS_CLASS_OS2		0x02U
+#define UDF_OS_CLASS_MAC		0x03U
+#define UDF_OS_CLASS_UNIX		0x04U
+#define UDF_OS_CLASS_WIN9X		0x05U
+#define UDF_OS_CLASS_WINNT		0x06U
+#define UDF_OS_CLASS_OS400		0x07U
+#define UDF_OS_CLASS_BEOS		0x08U
+#define UDF_OS_CLASS_WINCE		0x09U
+
+#define UDF_OS_ID_UNDEF			0x00U
+#define UDF_OS_ID_DOS			0x00U
+#define UDF_OS_ID_OS2			0x00U
+#define UDF_OS_ID_MAC			0x00U
+#define UDF_OS_ID_MAX_OSX		0x01U
+#define UDF_OS_ID_UNIX			0x00U
+#define UDF_OS_ID_AIX			0x01U
+#define UDF_OS_ID_SOLARIS		0x02U
+#define UDF_OS_ID_HPUX			0x03U
+#define UDF_OS_ID_IRIX			0x04U
+#define UDF_OS_ID_LINUX			0x05U
+#define UDF_OS_ID_MKLINUX		0x06U
+#define UDF_OS_ID_FREEBSD		0x07U
+#define UDF_OS_ID_WIN9X			0x00U
+#define UDF_OS_ID_WINNT			0x00U
+#define UDF_OS_ID_OS400			0x00U
+#define UDF_OS_ID_BEOS			0x00U
+#define UDF_OS_ID_WINCE			0x00U
+
+#endif /* _OSTA_UDF_H */
diff --git a/fs/udf/partition.c b/fs/udf/partition.c
new file mode 100644
index 000000000000..4d36f264be0d
--- /dev/null
+++ b/fs/udf/partition.c
@@ -0,0 +1,226 @@
+/*
+ * partition.c
+ *
+ * PURPOSE
+ *      Partition handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *      E-mail regarding any portion of the Linux UDF file system should be
+ *      directed to the development team mailing list (run by majordomo):
+ *              linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ * 12/06/98 blf  Created file. 
+ *
+ */
+
+#include "udfdecl.h"
+#include "udf_sb.h"
+#include "udf_i.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/udf_fs.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+
+inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+	if (partition >= UDF_SB_NUMPARTS(sb))
+	{
+		udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n",
+			block, partition, offset);
+		return 0xFFFFFFFF;
+	}
+	if (UDF_SB_PARTFUNC(sb, partition))
+		return UDF_SB_PARTFUNC(sb, partition)(sb, block, partition, offset);
+	else
+		return UDF_SB_PARTROOT(sb, partition) + block + offset;
+}
+
+uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+	struct buffer_head *bh = NULL;
+	uint32_t newblock;
+	uint32_t index;
+	uint32_t loc;
+
+	index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(uint32_t);
+
+	if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries)
+	{
+		udf_debug("Trying to access block beyond end of VAT (%d max %d)\n",
+			block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries);
+		return 0xFFFFFFFF;
+	}
+
+	if (block >= index)
+	{
+		block -= index;
+		newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t)));
+		index = block % (sb->s_blocksize / sizeof(uint32_t));
+	}
+	else
+	{
+		newblock = 0;
+		index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(uint32_t) + block;
+	}
+
+	loc = udf_block_map(UDF_SB_VAT(sb), newblock);
+
+	if (!(bh = sb_bread(sb, loc)))
+	{
+		udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n",
+			sb, block, partition, loc, index);
+		return 0xFFFFFFFF;
+	}
+
+	loc = le32_to_cpu(((__le32 *)bh->b_data)[index]);
+
+	udf_release_data(bh);
+
+	if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition)
+	{
+		udf_debug("recursive call to udf_get_pblock!\n");
+		return 0xFFFFFFFF;
+	}
+
+	return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset);
+}
+
+inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+	return udf_get_pblock_virt15(sb, block, partition, offset);
+}
+
+uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+	int i;
+	struct sparingTable *st = NULL;
+	uint32_t packet = (block + offset) & ~(UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1);
+
+	for (i=0; i<4; i++)
+	{
+		if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL)
+		{
+			st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,partition).s_spar_map[i]->b_data;
+			break;
+		}
+	}
+
+	if (st)
+	{
+		for (i=0; i<le16_to_cpu(st->reallocationTableLen); i++)
+		{
+			if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0)
+				break;
+			else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet)
+			{
+				return le32_to_cpu(st->mapEntry[i].mappedLocation) +
+					((block + offset) & (UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1));
+			}
+			else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet)
+				break;
+		}
+	}
+	return UDF_SB_PARTROOT(sb,partition) + block + offset;
+}
+
+int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block)
+{
+	struct udf_sparing_data *sdata;
+	struct sparingTable *st = NULL;
+	struct sparingEntry mapEntry;
+	uint32_t packet;
+	int i, j, k, l;
+
+	for (i=0; i<UDF_SB_NUMPARTS(sb); i++)
+	{
+		if (old_block > UDF_SB_PARTROOT(sb,i) &&
+		    old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i))
+		{
+			sdata = &UDF_SB_TYPESPAR(sb,i);
+			packet = (old_block - UDF_SB_PARTROOT(sb,i)) & ~(sdata->s_packet_len - 1);
+
+			for (j=0; j<4; j++)
+			{
+				if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL)
+				{
+					st = (struct sparingTable *)sdata->s_spar_map[j]->b_data;
+					break;
+				}
+			}
+
+			if (!st)
+				return 1;
+
+			for (k=0; k<le16_to_cpu(st->reallocationTableLen); k++)
+			{
+				if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF)
+				{
+					for (; j<4; j++)
+					{
+						if (sdata->s_spar_map[j])
+						{
+							st = (struct sparingTable *)sdata->s_spar_map[j]->b_data;
+							st->mapEntry[k].origLocation = cpu_to_le32(packet);
+							udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry));
+							mark_buffer_dirty(sdata->s_spar_map[j]);
+						}
+					}
+					*new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) +
+						((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1));
+					return 0;
+				}
+				else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet)
+				{
+					*new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) +
+						((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1));
+					return 0;
+				}
+				else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet)
+					break;
+			}
+			for (l=k; l<le16_to_cpu(st->reallocationTableLen); l++)
+			{
+				if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF)
+				{
+					for (; j<4; j++)
+					{
+						if (sdata->s_spar_map[j])
+						{
+							st = (struct sparingTable *)sdata->s_spar_map[j]->b_data;
+							mapEntry = st->mapEntry[l];
+							mapEntry.origLocation = cpu_to_le32(packet);
+							memmove(&st->mapEntry[k+1], &st->mapEntry[k], (l-k)*sizeof(struct sparingEntry));
+							st->mapEntry[k] = mapEntry;
+							udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry));
+							mark_buffer_dirty(sdata->s_spar_map[j]);
+						}
+					}
+					*new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) +
+						((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1));
+					return 0;
+				}
+			}
+			return 1;
+		}
+	}
+	if (i == UDF_SB_NUMPARTS(sb))
+	{
+		/* outside of partitions */
+		/* for now, fail =) */
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/fs/udf/super.c b/fs/udf/super.c
new file mode 100644
index 000000000000..15bd4f24c5b7
--- /dev/null
+++ b/fs/udf/super.c
@@ -0,0 +1,1934 @@
+/*
+ * super.c
+ *
+ * PURPOSE
+ *  Super block routines for the OSTA-UDF(tm) filesystem.
+ *
+ * DESCRIPTION
+ *  OSTA-UDF(tm) = Optical Storage Technology Association
+ *  Universal Disk Format.
+ *
+ *  This code is based on version 2.00 of the UDF specification,
+ *  and revision 3 of the ECMA 167 standard [equivalent to ISO 13346].
+ *    http://www.osta.org/
+ *    http://www.ecma.ch/
+ *    http://www.iso.org/
+ *
+ * CONTACTS
+ *  E-mail regarding any portion of the Linux UDF file system should be
+ *  directed to the development team mailing list (run by majordomo):
+ *	  linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998 Dave Boynton
+ *  (C) 1998-2004 Ben Fennema
+ *  (C) 2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  09/24/98 dgb  changed to allow compiling outside of kernel, and
+ *                added some debugging.
+ *  10/01/98 dgb  updated to allow (some) possibility of compiling w/2.0.34
+ *  10/16/98      attempting some multi-session support
+ *  10/17/98      added freespace count for "df"
+ *  11/11/98 gr   added novrs option
+ *  11/26/98 dgb  added fileset,anchor mount options
+ *  12/06/98 blf  really hosed things royally. vat/sparing support. sequenced vol descs
+ *                rewrote option handling based on isofs
+ *  12/20/98      find the free space bitmap (if it exists)
+ */
+
+#include "udfdecl.h"    
+
+#include <linux/config.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parser.h>
+#include <linux/stat.h>
+#include <linux/cdrom.h>
+#include <linux/nls.h>
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/vmalloc.h>
+#include <asm/byteorder.h>
+
+#include <linux/udf_fs.h>
+#include "udf_sb.h"
+#include "udf_i.h"
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#define VDS_POS_PRIMARY_VOL_DESC	0
+#define VDS_POS_UNALLOC_SPACE_DESC	1
+#define VDS_POS_LOGICAL_VOL_DESC	2
+#define VDS_POS_PARTITION_DESC		3
+#define VDS_POS_IMP_USE_VOL_DESC	4
+#define VDS_POS_VOL_DESC_PTR		5
+#define VDS_POS_TERMINATING_DESC	6
+#define VDS_POS_LENGTH			7
+
+static char error_buf[1024];
+
+/* These are the "meat" - everything else is stuffing */
+static int udf_fill_super(struct super_block *, void *, int);
+static void udf_put_super(struct super_block *);
+static void udf_write_super(struct super_block *);
+static int udf_remount_fs(struct super_block *, int *, char *);
+static int udf_check_valid(struct super_block *, int, int);
+static int udf_vrs(struct super_block *sb, int silent);
+static int udf_load_partition(struct super_block *, kernel_lb_addr *);
+static int udf_load_logicalvol(struct super_block *, struct buffer_head *, kernel_lb_addr *);
+static void udf_load_logicalvolint(struct super_block *, kernel_extent_ad);
+static void udf_find_anchor(struct super_block *);
+static int udf_find_fileset(struct super_block *, kernel_lb_addr *, kernel_lb_addr *);
+static void udf_load_pvoldesc(struct super_block *, struct buffer_head *);
+static void udf_load_fileset(struct super_block *, struct buffer_head *, kernel_lb_addr *);
+static void udf_load_partdesc(struct super_block *, struct buffer_head *);
+static void udf_open_lvid(struct super_block *);
+static void udf_close_lvid(struct super_block *);
+static unsigned int udf_count_free(struct super_block *);
+static int udf_statfs(struct super_block *, struct kstatfs *);
+
+/* UDF filesystem type */
+static struct super_block *udf_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super);
+}
+
+static struct file_system_type udf_fstype = {
+	.owner		= THIS_MODULE,
+	.name		= "udf",
+	.get_sb		= udf_get_sb,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+static kmem_cache_t * udf_inode_cachep;
+
+static struct inode *udf_alloc_inode(struct super_block *sb)
+{
+	struct udf_inode_info *ei;
+	ei = (struct udf_inode_info *)kmem_cache_alloc(udf_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void udf_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(udf_inode_cachep, UDF_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct udf_inode_info *ei = (struct udf_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+	{
+		ei->i_ext.i_data = NULL;
+		inode_init_once(&ei->vfs_inode);
+	}
+}
+
+static int init_inodecache(void)
+{
+	udf_inode_cachep = kmem_cache_create("udf_inode_cache",
+					     sizeof(struct udf_inode_info),
+					     0, SLAB_RECLAIM_ACCOUNT,
+					     init_once, NULL);
+	if (udf_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(udf_inode_cachep))
+		printk(KERN_INFO "udf_inode_cache: not all structures were freed\n");
+}
+
+/* Superblock operations */
+static struct super_operations udf_sb_ops = {
+	.alloc_inode		= udf_alloc_inode,
+	.destroy_inode		= udf_destroy_inode,
+	.write_inode		= udf_write_inode,
+	.delete_inode		= udf_delete_inode,
+	.clear_inode		= udf_clear_inode,
+	.put_super		= udf_put_super,
+	.write_super		= udf_write_super,
+	.statfs			= udf_statfs,
+	.remount_fs		= udf_remount_fs,
+};
+
+struct udf_options
+{
+	unsigned char novrs;
+	unsigned int blocksize;
+	unsigned int session;
+	unsigned int lastblock;
+	unsigned int anchor;
+	unsigned int volume;
+	unsigned short partition;
+	unsigned int fileset;
+	unsigned int rootdir;
+	unsigned int flags;
+	mode_t umask;
+	gid_t gid;
+	uid_t uid;
+	struct nls_table *nls_map;
+};
+
+static int __init init_udf_fs(void)
+{
+	int err;
+	err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&udf_fstype);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
+}
+
+static void __exit exit_udf_fs(void)
+{
+	unregister_filesystem(&udf_fstype);
+	destroy_inodecache();
+}
+
+module_init(init_udf_fs)
+module_exit(exit_udf_fs)
+
+/*
+ * udf_parse_options
+ *
+ * PURPOSE
+ *	Parse mount options.
+ *
+ * DESCRIPTION
+ *	The following mount options are supported:
+ *
+ *	gid=		Set the default group.
+ *	umask=		Set the default umask.
+ *	uid=		Set the default user.
+ *	bs=		Set the block size.
+ *	unhide		Show otherwise hidden files.
+ *	undelete	Show deleted files in lists.
+ *	adinicb		Embed data in the inode (default)
+ *	noadinicb	Don't embed data in the inode
+ *	shortad		Use short ad's
+ *	longad		Use long ad's (default)
+ *	nostrict	Unset strict conformance
+ *	iocharset=	Set the NLS character set
+ *
+ *	The remaining are for debugging and disaster recovery:
+ *
+ *	novrs		Skip volume sequence recognition 
+ *
+ *	The following expect a offset from 0.
+ *
+ *	session=	Set the CDROM session (default= last session)
+ *	anchor=		Override standard anchor location. (default= 256)
+ *	volume=		Override the VolumeDesc location. (unused)
+ *	partition=	Override the PartitionDesc location. (unused)
+ *	lastblock=	Set the last block of the filesystem/
+ *
+ *	The following expect a offset from the partition root.
+ *
+ *	fileset=	Override the fileset block location. (unused)
+ *	rootdir=	Override the root directory location. (unused)
+ *		WARNING: overriding the rootdir to a non-directory may
+ *		yield highly unpredictable results.
+ *
+ * PRE-CONDITIONS
+ *	options		Pointer to mount options string.
+ *	uopts		Pointer to mount options variable.
+ *
+ * POST-CONDITIONS
+ *	<return>	1	Mount options parsed okay.
+ *	<return>	0	Error parsing mount options.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+
+enum {
+	Opt_novrs, Opt_nostrict, Opt_bs, Opt_unhide, Opt_undelete,
+	Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad,
+	Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock,
+	Opt_anchor, Opt_volume, Opt_partition, Opt_fileset,
+	Opt_rootdir, Opt_utf8, Opt_iocharset,
+	Opt_err
+};
+
+static match_table_t tokens = {
+	{Opt_novrs, "novrs"},
+	{Opt_nostrict, "nostrict"},
+	{Opt_bs, "bs=%u"},
+	{Opt_unhide, "unhide"},
+	{Opt_undelete, "undelete"},
+	{Opt_noadinicb, "noadinicb"},
+	{Opt_adinicb, "adinicb"},
+	{Opt_shortad, "shortad"},
+	{Opt_longad, "longad"},
+	{Opt_gid, "gid=%u"},
+	{Opt_uid, "uid=%u"},
+	{Opt_umask, "umask=%o"},
+	{Opt_session, "session=%u"},
+	{Opt_lastblock, "lastblock=%u"},
+	{Opt_anchor, "anchor=%u"},
+	{Opt_volume, "volume=%u"},
+	{Opt_partition, "partition=%u"},
+	{Opt_fileset, "fileset=%u"},
+	{Opt_rootdir, "rootdir=%u"},
+	{Opt_utf8, "utf8"},
+	{Opt_iocharset, "iocharset=%s"},
+	{Opt_err, NULL}
+};
+
+static int
+udf_parse_options(char *options, struct udf_options *uopt)
+{
+	char *p;
+	int option;
+
+	uopt->novrs = 0;
+	uopt->blocksize = 2048;
+	uopt->partition = 0xFFFF;
+	uopt->session = 0xFFFFFFFF;
+	uopt->lastblock = 0;
+	uopt->anchor = 0;
+	uopt->volume = 0xFFFFFFFF;
+	uopt->rootdir = 0xFFFFFFFF;
+	uopt->fileset = 0xFFFFFFFF;
+	uopt->nls_map = NULL;
+
+	if (!options)
+		return 1;
+
+	while ((p = strsep(&options, ",")) != NULL)
+	{
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token)
+		{
+			case Opt_novrs:
+				uopt->novrs = 1;
+			case Opt_bs:
+				if (match_int(&args[0], &option))
+					return 0;
+				uopt->blocksize = option;
+				break;
+			case Opt_unhide:
+				uopt->flags |= (1 << UDF_FLAG_UNHIDE);
+				break;
+			case Opt_undelete:
+				uopt->flags |= (1 << UDF_FLAG_UNDELETE);
+				break;
+			case Opt_noadinicb:
+				uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB);
+				break;
+			case Opt_adinicb:
+				uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB);
+				break;
+			case Opt_shortad:
+				uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD);
+				break;
+			case Opt_longad:
+				uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD);
+				break;
+			case Opt_gid:
+				if (match_int(args, &option))
+					return 0;
+				uopt->gid = option;
+				break;
+			case Opt_uid:
+				if (match_int(args, &option))
+					return 0;
+				uopt->uid = option;
+				break;
+			case Opt_umask:
+				if (match_octal(args, &option))
+					return 0;
+				uopt->umask = option;
+				break;
+			case Opt_nostrict:
+				uopt->flags &= ~(1 << UDF_FLAG_STRICT);
+				break;
+			case Opt_session:
+				if (match_int(args, &option))
+					return 0;
+				uopt->session = option;
+				break;
+			case Opt_lastblock:
+				if (match_int(args, &option))
+					return 0;
+				uopt->lastblock = option;
+				break;
+			case Opt_anchor:
+				if (match_int(args, &option))
+					return 0;
+				uopt->anchor = option;
+				break;
+			case Opt_volume:
+				if (match_int(args, &option))
+					return 0;
+				uopt->volume = option;
+				break;
+			case Opt_partition:
+				if (match_int(args, &option))
+					return 0;
+				uopt->partition = option;
+				break;
+			case Opt_fileset:
+				if (match_int(args, &option))
+					return 0;
+				uopt->fileset = option;
+				break;
+			case Opt_rootdir:
+				if (match_int(args, &option))
+					return 0;
+				uopt->rootdir = option;
+				break;
+			case Opt_utf8:
+				uopt->flags |= (1 << UDF_FLAG_UTF8);
+				break;
+#ifdef CONFIG_UDF_NLS
+			case Opt_iocharset:
+				uopt->nls_map = load_nls(args[0].from);
+				uopt->flags |= (1 << UDF_FLAG_NLS_MAP);
+				break;
+#endif
+			default:
+				printk(KERN_ERR "udf: bad mount option \"%s\" "
+						"or missing value\n", p);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+void
+udf_write_super(struct super_block *sb)
+{
+	lock_kernel();
+	if (!(sb->s_flags & MS_RDONLY))
+		udf_open_lvid(sb);
+	sb->s_dirt = 0;
+	unlock_kernel();
+}
+
+static int
+udf_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+	struct udf_options uopt;
+
+	uopt.flags = UDF_SB(sb)->s_flags ;
+	uopt.uid   = UDF_SB(sb)->s_uid ;
+	uopt.gid   = UDF_SB(sb)->s_gid ;
+	uopt.umask = UDF_SB(sb)->s_umask ;
+
+	if ( !udf_parse_options(options, &uopt) )
+		return -EINVAL;
+
+	UDF_SB(sb)->s_flags = uopt.flags;
+	UDF_SB(sb)->s_uid   = uopt.uid;
+	UDF_SB(sb)->s_gid   = uopt.gid;
+	UDF_SB(sb)->s_umask = uopt.umask;
+
+	if (UDF_SB_LVIDBH(sb)) {
+		int write_rev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev);
+		if (write_rev > UDF_MAX_WRITE_VERSION)
+			*flags |= MS_RDONLY;
+	}
+
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+	if (*flags & MS_RDONLY)
+		udf_close_lvid(sb);
+	else
+		udf_open_lvid(sb);
+
+	return 0;
+}
+
+/*
+ * udf_set_blocksize
+ *
+ * PURPOSE
+ *	Set the block size to be used in all transfers.
+ *
+ * DESCRIPTION
+ *	To allow room for a DMA transfer, it is best to guess big when unsure.
+ *	This routine picks 2048 bytes as the blocksize when guessing. This
+ *	should be adequate until devices with larger block sizes become common.
+ *
+ *	Note that the Linux kernel can currently only deal with blocksizes of
+ *	512, 1024, 2048, 4096, and 8192 bytes.
+ *
+ * PRE-CONDITIONS
+ *	sb			Pointer to _locked_ superblock.
+ *
+ * POST-CONDITIONS
+ *	sb->s_blocksize		Blocksize.
+ *	sb->s_blocksize_bits	log2 of blocksize.
+ *	<return>	0	Blocksize is valid.
+ *	<return>	1	Blocksize is invalid.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static  int
+udf_set_blocksize(struct super_block *sb, int bsize)
+{
+	if (!sb_min_blocksize(sb, bsize)) {
+		udf_debug("Bad block size (%d)\n", bsize);
+		printk(KERN_ERR "udf: bad block size (%d)\n", bsize);
+		return 0;
+	}
+	return sb->s_blocksize;
+}
+
+static int
+udf_vrs(struct super_block *sb, int silent)
+{
+	struct volStructDesc *vsd = NULL;
+	int sector = 32768;
+	int sectorsize;
+	struct buffer_head *bh = NULL;
+	int iso9660=0;
+	int nsr02=0;
+	int nsr03=0;
+
+	/* Block size must be a multiple of 512 */
+	if (sb->s_blocksize & 511)
+		return 0;
+
+	if (sb->s_blocksize < sizeof(struct volStructDesc))
+		sectorsize = sizeof(struct volStructDesc);
+	else
+		sectorsize = sb->s_blocksize;
+
+	sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits);
+
+	udf_debug("Starting at sector %u (%ld byte sectors)\n",
+		(sector >> sb->s_blocksize_bits), sb->s_blocksize);
+	/* Process the sequence (if applicable) */
+	for (;!nsr02 && !nsr03; sector += sectorsize)
+	{
+		/* Read a block */
+		bh = udf_tread(sb, sector >> sb->s_blocksize_bits);
+		if (!bh)
+			break;
+
+		/* Look for ISO  descriptors */
+		vsd = (struct volStructDesc *)(bh->b_data +
+			(sector & (sb->s_blocksize - 1)));
+
+		if (vsd->stdIdent[0] == 0)
+		{
+			udf_release_data(bh);
+			break;
+		}
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN))
+		{
+			iso9660 = sector;
+			switch (vsd->structType)
+			{
+				case 0: 
+					udf_debug("ISO9660 Boot Record found\n");
+					break;
+				case 1: 
+					udf_debug("ISO9660 Primary Volume Descriptor found\n");
+					break;
+				case 2: 
+					udf_debug("ISO9660 Supplementary Volume Descriptor found\n");
+					break;
+				case 3: 
+					udf_debug("ISO9660 Volume Partition Descriptor found\n");
+					break;
+				case 255: 
+					udf_debug("ISO9660 Volume Descriptor Set Terminator found\n");
+					break;
+				default: 
+					udf_debug("ISO9660 VRS (%u) found\n", vsd->structType);
+					break;
+			}
+		}
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN))
+		{
+		}
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN))
+		{
+			udf_release_data(bh);
+			break;
+		}
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN))
+		{
+			nsr02 = sector;
+		}
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN))
+		{
+			nsr03 = sector;
+		}
+		udf_release_data(bh);
+	}
+
+	if (nsr03)
+		return nsr03;
+	else if (nsr02)
+		return nsr02;
+	else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768)
+		return -1;
+	else
+		return 0;
+}
+
+/*
+ * udf_find_anchor
+ *
+ * PURPOSE
+ *	Find an anchor volume descriptor.
+ *
+ * PRE-CONDITIONS
+ *	sb			Pointer to _locked_ superblock.
+ *	lastblock		Last block on media.
+ *
+ * POST-CONDITIONS
+ *	<return>		1 if not found, 0 if ok
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static void
+udf_find_anchor(struct super_block *sb)
+{
+	int lastblock = UDF_SB_LASTBLOCK(sb);
+	struct buffer_head *bh = NULL;
+	uint16_t ident;
+	uint32_t location;
+	int i;
+
+	if (lastblock)
+	{
+		int varlastblock = udf_variable_to_fixed(lastblock);
+		int last[] =  { lastblock, lastblock - 2,
+				lastblock - 150, lastblock - 152,
+				varlastblock, varlastblock - 2,
+				varlastblock - 150, varlastblock - 152 };
+
+		lastblock = 0;
+
+		/* Search for an anchor volume descriptor pointer */
+
+		/*  according to spec, anchor is in either:
+		 *     block 256
+		 *     lastblock-256
+		 *     lastblock
+		 *  however, if the disc isn't closed, it could be 512 */
+
+		for (i=0; (!lastblock && i<sizeof(last)/sizeof(int)); i++)
+		{
+			if (last[i] < 0 || !(bh = sb_bread(sb, last[i])))
+			{
+				ident = location = 0;
+			}
+			else
+			{
+				ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+				location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+				udf_release_data(bh);
+			}
+	
+			if (ident == TAG_IDENT_AVDP)
+			{
+				if (location == last[i] - UDF_SB_SESSION(sb))
+				{
+					lastblock = UDF_SB_ANCHOR(sb)[0] = last[i] - UDF_SB_SESSION(sb);
+					UDF_SB_ANCHOR(sb)[1] = last[i] - 256 - UDF_SB_SESSION(sb);
+				}
+				else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb))
+				{
+					UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+					lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb);
+					UDF_SB_ANCHOR(sb)[1] = lastblock - 256 - UDF_SB_SESSION(sb);
+				}
+				else
+					udf_debug("Anchor found at block %d, location mismatch %d.\n",
+						last[i], location);
+			}
+			else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE)
+			{
+				lastblock = last[i];
+				UDF_SB_ANCHOR(sb)[3] = 512;
+			}
+			else
+			{
+				if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256)))
+				{
+					ident = location = 0;
+				}
+				else
+				{
+					ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+					location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+					udf_release_data(bh);
+				}
+	
+				if (ident == TAG_IDENT_AVDP &&
+					location == last[i] - 256 - UDF_SB_SESSION(sb))
+				{
+					lastblock = last[i];
+					UDF_SB_ANCHOR(sb)[1] = last[i] - 256;
+				}
+				else
+				{
+					if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb))))
+					{
+						ident = location = 0;
+					}
+					else
+					{
+						ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+						location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+						udf_release_data(bh);
+					}
+	
+					if (ident == TAG_IDENT_AVDP &&
+						location == udf_variable_to_fixed(last[i]) - 256)
+					{
+						UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+						lastblock = udf_variable_to_fixed(last[i]);
+						UDF_SB_ANCHOR(sb)[1] = lastblock - 256;
+					}
+				}
+			}
+		}
+	}
+
+	if (!lastblock)
+	{
+		/* We havn't found the lastblock. check 312 */
+		if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb))))
+		{
+			ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+			location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+			udf_release_data(bh);
+
+			if (ident == TAG_IDENT_AVDP && location == 256)
+				UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+		}
+	}
+
+	for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++)
+	{
+		if (UDF_SB_ANCHOR(sb)[i])
+		{
+			if (!(bh = udf_read_tagged(sb,
+				UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident)))
+			{
+				UDF_SB_ANCHOR(sb)[i] = 0;
+			}
+			else
+			{
+				udf_release_data(bh);
+				if ((ident != TAG_IDENT_AVDP) && (i ||
+					(ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE)))
+				{
+					UDF_SB_ANCHOR(sb)[i] = 0;
+				}
+			}
+		}
+	}
+
+	UDF_SB_LASTBLOCK(sb) = lastblock;
+}
+
+static int 
+udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr *root)
+{
+	struct buffer_head *bh = NULL;
+	long lastblock;
+	uint16_t ident;
+
+	if (fileset->logicalBlockNum != 0xFFFFFFFF ||
+		fileset->partitionReferenceNum != 0xFFFF)
+	{
+		bh = udf_read_ptagged(sb, *fileset, 0, &ident);
+
+		if (!bh)
+			return 1;
+		else if (ident != TAG_IDENT_FSD)
+		{
+			udf_release_data(bh);
+			return 1;
+		}
+			
+	}
+
+	if (!bh) /* Search backwards through the partitions */
+	{
+		kernel_lb_addr newfileset;
+
+		return 1;
+		
+		for (newfileset.partitionReferenceNum=UDF_SB_NUMPARTS(sb)-1;
+			(newfileset.partitionReferenceNum != 0xFFFF &&
+				fileset->logicalBlockNum == 0xFFFFFFFF &&
+				fileset->partitionReferenceNum == 0xFFFF);
+			newfileset.partitionReferenceNum--)
+		{
+			lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum);
+			newfileset.logicalBlockNum = 0;
+
+			do
+			{
+				bh = udf_read_ptagged(sb, newfileset, 0, &ident);
+				if (!bh)
+				{
+					newfileset.logicalBlockNum ++;
+					continue;
+				}
+
+				switch (ident)
+				{
+					case TAG_IDENT_SBD:
+					{
+						struct spaceBitmapDesc *sp;
+						sp = (struct spaceBitmapDesc *)bh->b_data;
+						newfileset.logicalBlockNum += 1 +
+							((le32_to_cpu(sp->numOfBytes) + sizeof(struct spaceBitmapDesc) - 1)
+								>> sb->s_blocksize_bits);
+						udf_release_data(bh);
+						break;
+					}
+					case TAG_IDENT_FSD:
+					{
+						*fileset = newfileset;
+						break;
+					}
+					default:
+					{
+						newfileset.logicalBlockNum ++;
+						udf_release_data(bh);
+						bh = NULL;
+						break;
+					}
+				}
+			}
+			while (newfileset.logicalBlockNum < lastblock &&
+				fileset->logicalBlockNum == 0xFFFFFFFF &&
+				fileset->partitionReferenceNum == 0xFFFF);
+		}
+	}
+
+	if ((fileset->logicalBlockNum != 0xFFFFFFFF ||
+		fileset->partitionReferenceNum != 0xFFFF) && bh)
+	{
+		udf_debug("Fileset at block=%d, partition=%d\n",
+			fileset->logicalBlockNum, fileset->partitionReferenceNum);
+
+		UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum;
+		udf_load_fileset(sb, bh, root);
+		udf_release_data(bh);
+		return 0;
+	}
+	return 1;
+}
+
+static void 
+udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh)
+{
+	struct primaryVolDesc *pvoldesc;
+	time_t recording;
+	long recording_usec;
+	struct ustr instr;
+	struct ustr outstr;
+
+	pvoldesc = (struct primaryVolDesc *)bh->b_data;
+
+	if ( udf_stamp_to_time(&recording, &recording_usec,
+		lets_to_cpu(pvoldesc->recordingDateAndTime)) )
+	{
+		kernel_timestamp ts;
+		ts = lets_to_cpu(pvoldesc->recordingDateAndTime);
+		udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n",
+			recording, recording_usec,
+			ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone);
+		UDF_SB_RECORDTIME(sb).tv_sec = recording;
+		UDF_SB_RECORDTIME(sb).tv_nsec = recording_usec * 1000;
+	}
+
+	if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) )
+	{
+		if (udf_CS0toUTF8(&outstr, &instr))
+		{
+			strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name,
+				outstr.u_len > 31 ? 31 : outstr.u_len);
+			udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb));
+		}
+	}
+
+	if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) )
+	{
+		if (udf_CS0toUTF8(&outstr, &instr))
+			udf_debug("volSetIdent[] = '%s'\n", outstr.u_name);
+	}
+}
+
+static void 
+udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr *root)
+{
+	struct fileSetDesc *fset;
+
+	fset = (struct fileSetDesc *)bh->b_data;
+
+	*root = lelb_to_cpu(fset->rootDirectoryICB.extLocation);
+
+	UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum);
+
+	udf_debug("Rootdir at block=%d, partition=%d\n", 
+		root->logicalBlockNum, root->partitionReferenceNum);
+}
+
+static void 
+udf_load_partdesc(struct super_block *sb, struct buffer_head *bh)
+{
+	struct partitionDesc *p;
+	int i;
+
+	p = (struct partitionDesc *)bh->b_data;
+
+	for (i=0; i<UDF_SB_NUMPARTS(sb); i++)
+	{
+		udf_debug("Searching map: (%d == %d)\n", 
+			UDF_SB_PARTMAPS(sb)[i].s_partition_num, le16_to_cpu(p->partitionNumber));
+		if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber))
+		{
+			UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */
+			UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation);
+			if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_READ_ONLY)
+				UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_READ_ONLY;
+			if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_WRITE_ONCE)
+				UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_WRITE_ONCE;
+			if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_REWRITABLE)
+				UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_REWRITABLE;
+			if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_OVERWRITABLE)
+				UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_OVERWRITABLE;
+
+			if (!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) ||
+				!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03))
+			{
+				struct partitionHeaderDesc *phd;
+
+				phd = (struct partitionHeaderDesc *)(p->partitionContentsUse);
+				if (phd->unallocSpaceTable.extLength)
+				{
+					kernel_lb_addr loc = { le32_to_cpu(phd->unallocSpaceTable.extPosition), i };
+
+					UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table =
+						udf_iget(sb, loc);
+					UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_TABLE;
+					udf_debug("unallocSpaceTable (part %d) @ %ld\n",
+						i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino);
+				}
+				if (phd->unallocSpaceBitmap.extLength)
+				{
+					UDF_SB_ALLOC_BITMAP(sb, i, s_uspace);
+					if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL)
+					{
+						UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength =
+							le32_to_cpu(phd->unallocSpaceBitmap.extLength);
+						UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition =
+							le32_to_cpu(phd->unallocSpaceBitmap.extPosition);
+						UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP;
+						udf_debug("unallocSpaceBitmap (part %d) @ %d\n",
+							i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition);
+					}
+				}
+				if (phd->partitionIntegrityTable.extLength)
+					udf_debug("partitionIntegrityTable (part %d)\n", i);
+				if (phd->freedSpaceTable.extLength)
+				{
+					kernel_lb_addr loc = { le32_to_cpu(phd->freedSpaceTable.extPosition), i };
+
+					UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table =
+						udf_iget(sb, loc);
+					UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_TABLE;
+					udf_debug("freedSpaceTable (part %d) @ %ld\n",
+						i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino);
+				}
+				if (phd->freedSpaceBitmap.extLength)
+				{
+					UDF_SB_ALLOC_BITMAP(sb, i, s_fspace);
+					if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL)
+					{
+						UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extLength =
+							le32_to_cpu(phd->freedSpaceBitmap.extLength);
+						UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition =
+							le32_to_cpu(phd->freedSpaceBitmap.extPosition);
+						UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP;
+						udf_debug("freedSpaceBitmap (part %d) @ %d\n",
+							i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition);
+					}
+				}
+			}
+			break;
+		}
+	}
+	if (i == UDF_SB_NUMPARTS(sb))
+	{
+		udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber));
+	}
+	else
+	{
+		udf_debug("Partition (%d:%d type %x) starts at physical %d, block length %d\n",
+			le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i),
+			UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i));
+	}
+}
+
+static int 
+udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_addr *fileset)
+{
+	struct logicalVolDesc *lvd;
+	int i, j, offset;
+	uint8_t type;
+
+	lvd = (struct logicalVolDesc *)bh->b_data;
+
+	UDF_SB_ALLOC_PARTMAPS(sb, le32_to_cpu(lvd->numPartitionMaps));
+
+	for (i=0,offset=0;
+		 i<UDF_SB_NUMPARTS(sb) && offset<le32_to_cpu(lvd->mapTableLength);
+		 i++,offset+=((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength)
+	{
+		type = ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType;
+		if (type == 1)
+		{
+			struct genericPartitionMap1 *gpm1 = (struct genericPartitionMap1 *)&(lvd->partitionMaps[offset]);
+			UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15;
+			UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum);
+			UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum);
+			UDF_SB_PARTFUNC(sb,i) = NULL;
+		}
+		else if (type == 2)
+		{
+			struct udfPartitionMap2 *upm2 = (struct udfPartitionMap2 *)&(lvd->partitionMaps[offset]);
+			if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL)))
+			{
+				if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150)
+				{
+					UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15;
+					UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15;
+				}
+				else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200)
+				{
+					UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20;
+					UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20;
+				}
+			}
+			else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE)))
+			{
+				uint32_t loc;
+				uint16_t ident;
+				struct sparingTable *st;
+				struct sparablePartitionMap *spm = (struct sparablePartitionMap *)&(lvd->partitionMaps[offset]);
+
+				UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15;
+				UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength);
+				for (j=0; j<spm->numSparingTables; j++)
+				{
+					loc = le32_to_cpu(spm->locSparingTable[j]);
+					UDF_SB_TYPESPAR(sb,i).s_spar_map[j] =
+						udf_read_tagged(sb, loc, loc, &ident);
+					if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL)
+					{
+						st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data;
+						if (ident != 0 ||
+							strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING)))
+						{
+							udf_release_data(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]);
+							UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL;
+						}
+					}
+				}
+				UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15;
+			}
+			else
+			{
+				udf_debug("Unknown ident: %s\n", upm2->partIdent.ident);
+				continue;
+			}
+			UDF_SB_PARTVSN(sb,i) = le16_to_cpu(upm2->volSeqNum);
+			UDF_SB_PARTNUM(sb,i) = le16_to_cpu(upm2->partitionNum);
+		}
+		udf_debug("Partition (%d:%d) type %d on volume %d\n",
+			i, UDF_SB_PARTNUM(sb,i), type, UDF_SB_PARTVSN(sb,i));
+	}
+
+	if (fileset)
+	{
+		long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]);
+
+		*fileset = lelb_to_cpu(la->extLocation);
+		udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n",
+			fileset->logicalBlockNum,
+			fileset->partitionReferenceNum);
+	}
+	if (lvd->integritySeqExt.extLength)
+		udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt));
+	return 0;
+}
+
+/*
+ * udf_load_logicalvolint
+ *
+ */
+static void
+udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc)
+{
+	struct buffer_head *bh = NULL;
+	uint16_t ident;
+
+	while (loc.extLength > 0 &&
+		(bh = udf_read_tagged(sb, loc.extLocation,
+			loc.extLocation, &ident)) &&
+		ident == TAG_IDENT_LVID)
+	{
+		UDF_SB_LVIDBH(sb) = bh;
+		
+		if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength)
+			udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt));
+		
+		if (UDF_SB_LVIDBH(sb) != bh)
+			udf_release_data(bh);
+		loc.extLength -= sb->s_blocksize;
+		loc.extLocation ++;
+	}
+	if (UDF_SB_LVIDBH(sb) != bh)
+		udf_release_data(bh);
+}
+
+/*
+ * udf_process_sequence
+ *
+ * PURPOSE
+ *	Process a main/reserve volume descriptor sequence.
+ *
+ * PRE-CONDITIONS
+ *	sb			Pointer to _locked_ superblock.
+ *	block			First block of first extent of the sequence.
+ *	lastblock		Lastblock of first extent of the sequence.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static  int
+udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_lb_addr *fileset)
+{
+	struct buffer_head *bh = NULL;
+	struct udf_vds_record vds[VDS_POS_LENGTH];
+	struct generic_desc *gd;
+	struct volDescPtr *vdp;
+	int done=0;
+	int i,j;
+	uint32_t vdsn;
+	uint16_t ident;
+	long next_s = 0, next_e = 0;
+
+	memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
+
+	/* Read the main descriptor sequence */
+	for (;(!done && block <= lastblock); block++)
+	{
+
+		bh = udf_read_tagged(sb, block, block, &ident);
+		if (!bh) 
+			break;
+
+		/* Process each descriptor (ISO 13346 3/8.3-8.4) */
+		gd = (struct generic_desc *)bh->b_data;
+		vdsn = le32_to_cpu(gd->volDescSeqNum);
+		switch (ident)
+		{
+			case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */
+				if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum)
+				{
+					vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn;
+					vds[VDS_POS_PRIMARY_VOL_DESC].block = block;
+				}
+				break;
+			case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */
+				if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum)
+				{
+					vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn;
+					vds[VDS_POS_VOL_DESC_PTR].block = block;
+
+					vdp = (struct volDescPtr *)bh->b_data;
+					next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation);
+					next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength);
+					next_e = next_e >> sb->s_blocksize_bits;
+					next_e += next_s;
+				}
+				break;
+			case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */
+				if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum)
+				{
+					vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn;
+					vds[VDS_POS_IMP_USE_VOL_DESC].block = block;
+				}
+				break;
+			case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
+				if (!vds[VDS_POS_PARTITION_DESC].block)
+					vds[VDS_POS_PARTITION_DESC].block = block;
+				break;
+			case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */
+				if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum)
+				{
+					vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn;
+					vds[VDS_POS_LOGICAL_VOL_DESC].block = block;
+				}
+				break;
+			case TAG_IDENT_USD: /* ISO 13346 3/10.8 */
+				if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum)
+				{
+					vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn;
+					vds[VDS_POS_UNALLOC_SPACE_DESC].block = block;
+				}
+				break;
+			case TAG_IDENT_TD: /* ISO 13346 3/10.9 */
+				vds[VDS_POS_TERMINATING_DESC].block = block;
+				if (next_e)
+				{
+					block = next_s;
+					lastblock = next_e;
+					next_s = next_e = 0;
+				}
+				else
+					done = 1;
+				break;
+		}
+		udf_release_data(bh);
+	}
+	for (i=0; i<VDS_POS_LENGTH; i++)
+	{
+		if (vds[i].block)
+		{
+			bh = udf_read_tagged(sb, vds[i].block, vds[i].block, &ident);
+
+			if (i == VDS_POS_PRIMARY_VOL_DESC)
+				udf_load_pvoldesc(sb, bh);
+			else if (i == VDS_POS_LOGICAL_VOL_DESC)
+				udf_load_logicalvol(sb, bh, fileset);
+			else if (i == VDS_POS_PARTITION_DESC)
+			{
+				struct buffer_head *bh2 = NULL;
+				udf_load_partdesc(sb, bh);
+				for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++)
+				{
+					bh2 = udf_read_tagged(sb, j, j, &ident);
+					gd = (struct generic_desc *)bh2->b_data;
+					if (ident == TAG_IDENT_PD)
+						udf_load_partdesc(sb, bh2);
+					udf_release_data(bh2);
+				}
+			}
+			udf_release_data(bh);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * udf_check_valid()
+ */
+static int
+udf_check_valid(struct super_block *sb, int novrs, int silent)
+{
+	long block;
+
+	if (novrs)
+	{
+		udf_debug("Validity check skipped because of novrs option\n");
+		return 0;
+	}
+	/* Check that it is NSR02 compliant */
+	/* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */
+	else if ((block = udf_vrs(sb, silent)) == -1)
+	{
+		udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n");
+		if (!UDF_SB_LASTBLOCK(sb))
+			UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb);
+		return 0;
+	}
+	else 
+		return !block;
+}
+
+static int
+udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset)
+{
+	struct anchorVolDescPtr *anchor;
+	uint16_t ident;
+	struct buffer_head *bh;
+	long main_s, main_e, reserve_s, reserve_e;
+	int i, j;
+
+	if (!sb)
+		return 1;
+
+	for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++)
+	{
+		if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb,
+			UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident)))
+		{
+			anchor = (struct anchorVolDescPtr *)bh->b_data;
+
+			/* Locate the main sequence */
+			main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation );
+			main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength );
+			main_e = main_e >> sb->s_blocksize_bits;
+			main_e += main_s;
+	
+			/* Locate the reserve sequence */
+			reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation);
+			reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength);
+			reserve_e = reserve_e >> sb->s_blocksize_bits;
+			reserve_e += reserve_s;
+
+			udf_release_data(bh);
+
+			/* Process the main & reserve sequences */
+			/* responsible for finding the PartitionDesc(s) */
+			if (!(udf_process_sequence(sb, main_s, main_e, fileset) &&
+				udf_process_sequence(sb, reserve_s, reserve_e, fileset)))
+			{
+				break;
+			}
+		}
+	}
+
+	if (i == sizeof(UDF_SB_ANCHOR(sb))/sizeof(int))
+	{
+		udf_debug("No Anchor block found\n");
+		return 1;
+	}
+	else
+		udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]);
+
+	for (i=0; i<UDF_SB_NUMPARTS(sb); i++)
+	{
+		switch UDF_SB_PARTTYPE(sb, i)
+		{
+			case UDF_VIRTUAL_MAP15:
+			case UDF_VIRTUAL_MAP20:
+			{
+				kernel_lb_addr ino;
+
+				if (!UDF_SB_LASTBLOCK(sb))
+				{
+					UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb);
+					udf_find_anchor(sb);
+				}
+
+				if (!UDF_SB_LASTBLOCK(sb))
+				{
+					udf_debug("Unable to determine Lastblock (For Virtual Partition)\n");
+					return 1;
+				}
+
+				for (j=0; j<UDF_SB_NUMPARTS(sb); j++)
+				{
+					if (j != i &&
+						UDF_SB_PARTVSN(sb,i) == UDF_SB_PARTVSN(sb,j) &&
+						UDF_SB_PARTNUM(sb,i) == UDF_SB_PARTNUM(sb,j))
+					{
+						ino.partitionReferenceNum = j;
+						ino.logicalBlockNum = UDF_SB_LASTBLOCK(sb) -
+							UDF_SB_PARTROOT(sb,j);
+						break;
+					}
+				}
+
+				if (j == UDF_SB_NUMPARTS(sb))
+					return 1;
+
+				if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino)))
+					return 1;
+
+				if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP15)
+				{
+					UDF_SB_TYPEVIRT(sb,i).s_start_offset = udf_ext0_offset(UDF_SB_VAT(sb));
+					UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - 36) >> 2;
+				}
+				else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20)
+				{
+					struct buffer_head *bh = NULL;
+					uint32_t pos;
+
+					pos = udf_block_map(UDF_SB_VAT(sb), 0);
+					bh = sb_bread(sb, pos);
+					UDF_SB_TYPEVIRT(sb,i).s_start_offset =
+						le16_to_cpu(((struct virtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) +
+							udf_ext0_offset(UDF_SB_VAT(sb));
+					UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size -
+						UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2;
+					udf_release_data(bh);
+				}
+				UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0);
+				UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum);
+			}
+		}
+	}
+	return 0;
+}
+
+static void udf_open_lvid(struct super_block *sb)
+{
+	if (UDF_SB_LVIDBH(sb))
+	{
+		int i;
+		kernel_timestamp cpu_time;
+
+		UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+		UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+		if (udf_time_to_stamp(&cpu_time, CURRENT_TIME))
+			UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time);
+		UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN;
+
+		UDF_SB_LVID(sb)->descTag.descCRC =
+			cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag),
+			le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0));
+
+		UDF_SB_LVID(sb)->descTag.tagChecksum = 0;
+		for (i=0; i<16; i++)
+			if (i != 4)
+				UDF_SB_LVID(sb)->descTag.tagChecksum +=
+					((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i];
+
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+}
+
+static void udf_close_lvid(struct super_block *sb)
+{
+	if (UDF_SB_LVIDBH(sb) &&
+		UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN)
+	{
+		int i;
+		kernel_timestamp cpu_time;
+
+		UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+		UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+		if (udf_time_to_stamp(&cpu_time, CURRENT_TIME))
+			UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time);
+		if (UDF_MAX_WRITE_VERSION > le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev))
+			UDF_SB_LVIDIU(sb)->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION);
+		if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev))
+			UDF_SB_LVIDIU(sb)->minUDFReadRev = cpu_to_le16(UDF_SB_UDFREV(sb));
+		if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev))
+			UDF_SB_LVIDIU(sb)->minUDFWriteRev = cpu_to_le16(UDF_SB_UDFREV(sb));
+		UDF_SB_LVID(sb)->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE);
+
+		UDF_SB_LVID(sb)->descTag.descCRC =
+			cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag),
+			le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0));
+
+		UDF_SB_LVID(sb)->descTag.tagChecksum = 0;
+		for (i=0; i<16; i++)
+			if (i != 4)
+				UDF_SB_LVID(sb)->descTag.tagChecksum +=
+					((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i];
+
+		mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+	}
+}
+
+/*
+ * udf_read_super
+ *
+ * PURPOSE
+ *	Complete the specified super block.
+ *
+ * PRE-CONDITIONS
+ *	sb			Pointer to superblock to complete - never NULL.
+ *	sb->s_dev		Device to read suberblock from.
+ *	options			Pointer to mount options.
+ *	silent			Silent flag.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static int udf_fill_super(struct super_block *sb, void *options, int silent)
+{
+	int i;
+	struct inode *inode=NULL;
+	struct udf_options uopt;
+	kernel_lb_addr rootdir, fileset;
+	struct udf_sb_info *sbi;
+
+	uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT);
+	uopt.uid = -1;
+	uopt.gid = -1;
+	uopt.umask = 0;
+
+	sbi = kmalloc(sizeof(struct udf_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	sb->s_fs_info = sbi;
+	memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info));
+
+	init_MUTEX(&sbi->s_alloc_sem);
+
+	if (!udf_parse_options((char *)options, &uopt))
+		goto error_out;
+
+	if (uopt.flags & (1 << UDF_FLAG_UTF8) &&
+	    uopt.flags & (1 << UDF_FLAG_NLS_MAP))
+	{
+		udf_error(sb, "udf_read_super",
+			"utf8 cannot be combined with iocharset\n");
+		goto error_out;
+	}
+#ifdef CONFIG_UDF_NLS
+	if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map)
+	{
+		uopt.nls_map = load_nls_default();
+		if (!uopt.nls_map)
+			uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP);
+		else
+			udf_debug("Using default NLS map\n");
+	}
+#endif
+	if (!(uopt.flags & (1 << UDF_FLAG_NLS_MAP)))
+		uopt.flags |= (1 << UDF_FLAG_UTF8);
+
+	fileset.logicalBlockNum = 0xFFFFFFFF;
+	fileset.partitionReferenceNum = 0xFFFF;
+
+	UDF_SB(sb)->s_flags = uopt.flags;
+	UDF_SB(sb)->s_uid = uopt.uid;
+	UDF_SB(sb)->s_gid = uopt.gid;
+	UDF_SB(sb)->s_umask = uopt.umask;
+	UDF_SB(sb)->s_nls_map = uopt.nls_map;
+
+	/* Set the block size for all transfers */
+	if (!udf_set_blocksize(sb, uopt.blocksize))
+		goto error_out;
+
+	if ( uopt.session == 0xFFFFFFFF )
+		UDF_SB_SESSION(sb) = udf_get_last_session(sb);
+	else
+		UDF_SB_SESSION(sb) = uopt.session;
+
+	udf_debug("Multi-session=%d\n", UDF_SB_SESSION(sb));
+
+	UDF_SB_LASTBLOCK(sb) = uopt.lastblock;
+	UDF_SB_ANCHOR(sb)[0] = UDF_SB_ANCHOR(sb)[1] = 0;
+	UDF_SB_ANCHOR(sb)[2] = uopt.anchor;
+	UDF_SB_ANCHOR(sb)[3] = 256;
+
+	if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */
+	{
+		printk("UDF-fs: No VRS found\n");
+ 		goto error_out;
+	}
+
+	udf_find_anchor(sb);
+
+	/* Fill in the rest of the superblock */
+	sb->s_op = &udf_sb_ops;
+	sb->dq_op = NULL;
+	sb->s_dirt = 0;
+	sb->s_magic = UDF_SUPER_MAGIC;
+	sb->s_time_gran = 1000;
+
+	if (udf_load_partition(sb, &fileset))
+	{
+		printk("UDF-fs: No partition found (1)\n");
+		goto error_out;
+	}
+
+	udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb));
+
+	if ( UDF_SB_LVIDBH(sb) )
+	{
+		uint16_t minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev);
+		uint16_t minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev);
+		/* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */
+
+		if (minUDFReadRev > UDF_MAX_READ_VERSION)
+		{
+			printk("UDF-fs: minUDFReadRev=%x (max is %x)\n",
+				le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev),
+				UDF_MAX_READ_VERSION);
+			goto error_out;
+		}
+		else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION)
+		{
+			sb->s_flags |= MS_RDONLY;
+		}
+
+		UDF_SB_UDFREV(sb) = minUDFWriteRev;
+
+		if (minUDFReadRev >= UDF_VERS_USE_EXTENDED_FE)
+			UDF_SET_FLAG(sb, UDF_FLAG_USE_EXTENDED_FE);
+		if (minUDFReadRev >= UDF_VERS_USE_STREAMS)
+			UDF_SET_FLAG(sb, UDF_FLAG_USE_STREAMS);
+	}
+
+	if ( !UDF_SB_NUMPARTS(sb) )
+	{
+		printk("UDF-fs: No partition found (2)\n");
+		goto error_out;
+	}
+
+	if ( udf_find_fileset(sb, &fileset, &rootdir) )
+	{
+		printk("UDF-fs: No fileset found\n");
+		goto error_out;
+	}
+
+	if (!silent)
+	{
+		kernel_timestamp ts;
+		udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb));
+		udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n",
+			UDFFS_VERSION, UDFFS_DATE,
+			UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute,
+			ts.typeAndTimezone);
+	}
+	if (!(sb->s_flags & MS_RDONLY))
+		udf_open_lvid(sb);
+
+	/* Assign the root inode */
+	/* assign inodes by physical block number */
+	/* perhaps it's not extensible enough, but for now ... */
+	inode = udf_iget(sb, rootdir); 
+	if (!inode)
+	{
+		printk("UDF-fs: Error in udf_iget, block=%d, partition=%d\n",
+			rootdir.logicalBlockNum, rootdir.partitionReferenceNum);
+		goto error_out;
+	}
+
+	/* Allocate a dentry for the root inode */
+	sb->s_root = d_alloc_root(inode);
+	if (!sb->s_root)
+	{
+		printk("UDF-fs: Couldn't allocate root dentry\n");
+		iput(inode);
+		goto error_out;
+	}
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+	return 0;
+
+error_out:
+	if (UDF_SB_VAT(sb))
+		iput(UDF_SB_VAT(sb));
+	if (UDF_SB_NUMPARTS(sb))
+	{
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE)
+			iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table);
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE)
+			iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table);
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+			UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace);
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+			UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace);
+		if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15)
+		{
+			for (i=0; i<4; i++)
+				udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]);
+		}
+	}
+#ifdef CONFIG_UDF_NLS
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+		unload_nls(UDF_SB(sb)->s_nls_map);
+#endif
+	if (!(sb->s_flags & MS_RDONLY))
+		udf_close_lvid(sb);
+	udf_release_data(UDF_SB_LVIDBH(sb));
+	UDF_SB_FREE(sb);
+	kfree(sbi);
+	sb->s_fs_info = NULL;
+	return -EINVAL;
+}
+
+void udf_error(struct super_block *sb, const char *function,
+	const char *fmt, ...)
+{
+	va_list args;
+
+	if (!(sb->s_flags & MS_RDONLY))
+	{
+		/* mark sb error */
+		sb->s_dirt = 1;
+	}
+	va_start(args, fmt);
+	vsprintf(error_buf, fmt, args);
+	va_end(args);
+	printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n",
+		sb->s_id, function, error_buf);
+}
+
+void udf_warning(struct super_block *sb, const char *function,
+	const char *fmt, ...)
+{
+	va_list args;
+
+	va_start (args, fmt);
+	vsprintf(error_buf, fmt, args);
+	va_end(args);
+	printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n",
+		sb->s_id, function, error_buf);
+}
+
+/*
+ * udf_put_super
+ *
+ * PURPOSE
+ *	Prepare for destruction of the superblock.
+ *
+ * DESCRIPTION
+ *	Called before the filesystem is unmounted.
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static void
+udf_put_super(struct super_block *sb)
+{
+	int i;
+
+	if (UDF_SB_VAT(sb))
+		iput(UDF_SB_VAT(sb));
+	if (UDF_SB_NUMPARTS(sb))
+	{
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE)
+			iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table);
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE)
+			iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table);
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+			UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace);
+		if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+			UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace);
+		if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15)
+		{
+			for (i=0; i<4; i++)
+				udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]);
+		}
+	}
+#ifdef CONFIG_UDF_NLS
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+		unload_nls(UDF_SB(sb)->s_nls_map);
+#endif
+	if (!(sb->s_flags & MS_RDONLY))
+		udf_close_lvid(sb);
+	udf_release_data(UDF_SB_LVIDBH(sb));
+	UDF_SB_FREE(sb);
+	kfree(sb->s_fs_info);
+	sb->s_fs_info = NULL;
+}
+
+/*
+ * udf_stat_fs
+ *
+ * PURPOSE
+ *	Return info about the filesystem.
+ *
+ * DESCRIPTION
+ *	Called by sys_statfs()
+ *
+ * HISTORY
+ *	July 1, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static int
+udf_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	buf->f_type = UDF_SUPER_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb));
+	buf->f_bfree = udf_count_free(sb);
+	buf->f_bavail = buf->f_bfree;
+	buf->f_files = (UDF_SB_LVIDBH(sb) ?
+		(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) +
+		le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree;
+	buf->f_ffree = buf->f_bfree;
+	/* __kernel_fsid_t f_fsid */
+	buf->f_namelen = UDF_NAME_LEN-2;
+
+	return 0;
+}
+
+static unsigned char udf_bitmap_lookup[16] = {
+	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
+};
+
+static unsigned int
+udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap)
+{
+	struct buffer_head *bh = NULL;
+	unsigned int accum = 0;
+	int index;
+	int block = 0, newblock;
+	kernel_lb_addr loc;
+	uint32_t bytes;
+	uint8_t value;
+	uint8_t *ptr;
+	uint16_t ident;
+	struct spaceBitmapDesc *bm;
+
+	lock_kernel();
+
+	loc.logicalBlockNum = bitmap->s_extPosition;
+	loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
+	bh = udf_read_ptagged(sb, loc, 0, &ident);
+
+	if (!bh)
+	{
+		printk(KERN_ERR "udf: udf_count_free failed\n");
+		goto out;
+	}
+	else if (ident != TAG_IDENT_SBD)
+	{
+		udf_release_data(bh);
+		printk(KERN_ERR "udf: udf_count_free failed\n");
+		goto out;
+	}
+
+	bm = (struct spaceBitmapDesc *)bh->b_data;
+	bytes = le32_to_cpu(bm->numOfBytes);
+	index = sizeof(struct spaceBitmapDesc); /* offset in first block only */
+	ptr = (uint8_t *)bh->b_data;
+
+	while ( bytes > 0 )
+	{
+		while ((bytes > 0) && (index < sb->s_blocksize))
+		{
+			value = ptr[index];
+			accum += udf_bitmap_lookup[ value & 0x0f ];
+			accum += udf_bitmap_lookup[ value >> 4 ];
+			index++;
+			bytes--;
+		}
+		if ( bytes )
+		{
+			udf_release_data(bh);
+			newblock = udf_get_lb_pblock(sb, loc, ++block);
+			bh = udf_tread(sb, newblock);
+			if (!bh)
+			{
+				udf_debug("read failed\n");
+				goto out;
+			}
+			index = 0;
+			ptr = (uint8_t *)bh->b_data;
+		}
+	}
+	udf_release_data(bh);
+
+out:
+	unlock_kernel();
+
+	return accum;
+}
+
+static unsigned int
+udf_count_free_table(struct super_block *sb, struct inode * table)
+{
+	unsigned int accum = 0;
+	uint32_t extoffset, elen;
+	kernel_lb_addr bloc, eloc;
+	int8_t etype;
+	struct buffer_head *bh = NULL;
+
+	lock_kernel();
+
+	bloc = UDF_I_LOCATION(table);
+	extoffset = sizeof(struct unallocSpaceEntry);
+
+	while ((etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+	{
+		accum += (elen >> table->i_sb->s_blocksize_bits);
+	}
+	udf_release_data(bh);
+
+	unlock_kernel();
+
+	return accum;
+}
+	
+static unsigned int
+udf_count_free(struct super_block *sb)
+{
+	unsigned int accum = 0;
+
+	if (UDF_SB_LVIDBH(sb))
+	{
+		if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb))
+		{
+			accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]);
+
+			if (accum == 0xFFFFFFFF)
+				accum = 0;
+		}
+	}
+
+	if (accum)
+		return accum;
+
+	if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+	{
+		accum += udf_count_free_bitmap(sb,
+			UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap);
+	}
+	if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+	{
+		accum += udf_count_free_bitmap(sb,
+			UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap);
+	}
+	if (accum)
+		return accum;
+
+	if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE)
+	{
+		accum += udf_count_free_table(sb,
+			UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table);
+	}
+	if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE)
+	{
+		accum += udf_count_free_table(sb,
+			UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table);
+	}
+
+	return accum;
+}
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
new file mode 100644
index 000000000000..43f3051ef756
--- /dev/null
+++ b/fs/udf/symlink.c
@@ -0,0 +1,123 @@
+/*
+ * symlink.c
+ *
+ * PURPOSE
+ *	Symlink handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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) 1998-2001 Ben Fennema
+ *  (C) 1999 Stelias Computing Inc 
+ *
+ * HISTORY
+ *
+ *  04/16/99 blf  Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/udf_fs.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+#include "udf_i.h"
+
+static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen, char *to)
+{
+	struct pathComponent *pc;
+	int elen = 0;
+	char *p = to;
+
+	while (elen < fromlen)
+	{
+		pc = (struct pathComponent *)(from + elen);
+		switch (pc->componentType)
+		{
+			case 1:
+				if (pc->lengthComponentIdent == 0)
+				{
+					p = to;
+					*p++ = '/';
+				}
+				break;
+			case 3:
+				memcpy(p, "../", 3);
+				p += 3;
+				break;
+			case 4:
+				memcpy(p, "./", 2);
+				p += 2;
+				/* that would be . - just ignore */
+				break;
+			case 5:
+				p += udf_get_filename(sb, pc->componentIdent, p, pc->lengthComponentIdent);
+				*p++ = '/';
+				break;
+		}
+		elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
+	}
+	if (p > to+1)
+		p[-1] = '\0';
+	else
+		p[0] = '\0';
+}
+
+static int udf_symlink_filler(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct buffer_head *bh = NULL;
+	char *symlink;
+	int err = -EIO;
+	char *p = kmap(page);
+
+	lock_kernel();
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+		symlink = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode);
+	else
+	{
+		bh = sb_bread(inode->i_sb, udf_block_map(inode, 0));
+
+		if (!bh)
+			goto out;
+
+		symlink = bh->b_data;
+	}
+
+	udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p);
+	udf_release_data(bh);
+
+	unlock_kernel();
+	SetPageUptodate(page);
+	kunmap(page);
+	unlock_page(page);
+	return 0;
+out:
+	unlock_kernel();
+	SetPageError(page);
+	kunmap(page);
+	unlock_page(page);
+	return err;
+}
+
+/*
+ * symlinks can't do much...
+ */
+struct address_space_operations udf_symlink_aops = {
+	.readpage		= udf_symlink_filler,
+};
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
new file mode 100644
index 000000000000..7dc8a5572ca1
--- /dev/null
+++ b/fs/udf/truncate.c
@@ -0,0 +1,284 @@
+/*
+ * truncate.c
+ *
+ * PURPOSE
+ *	Truncate handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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-2004 Ben Fennema
+ *  (C) 1999 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  02/24/99 blf  Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/udf_fs.h>
+#include <linux/buffer_head.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+static void extent_trunc(struct inode * inode, kernel_lb_addr bloc, int extoffset,
+	kernel_lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen)
+{
+	kernel_lb_addr neloc = { 0, 0 };
+	int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+	int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+
+	if (nelen)
+	{
+		if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+		{
+			udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block);
+			etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
+		}
+		else
+			neloc = eloc;
+		nelen = (etype << 30) | nelen;
+	}
+
+	if (elen != nelen)
+	{
+		udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
+		if (last_block - first_block > 0)
+		{
+			if (etype == (EXT_RECORDED_ALLOCATED >> 30))
+				mark_inode_dirty(inode);
+
+			if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+				udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block);
+		}
+	}
+}
+
+void udf_discard_prealloc(struct inode * inode)
+{
+	kernel_lb_addr bloc, eloc;
+	uint32_t extoffset = 0, elen, nelen;
+	uint64_t lbcount = 0;
+	int8_t etype = -1, netype;
+	struct buffer_head *bh = NULL;
+	int adsize;
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ||
+		inode->i_size == UDF_I_LENEXTENTS(inode))
+	{
+		return;
+	}
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(long_ad);
+	else
+		adsize = 0;
+
+	bloc = UDF_I_LOCATION(inode);
+
+	while ((netype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+	{
+		etype = netype;
+		lbcount += elen;
+		if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize)
+		{
+			nelen = elen - (lbcount - inode->i_size);
+			extent_trunc(inode, bloc, extoffset-adsize, eloc, etype, elen, bh, nelen);
+			lbcount = inode->i_size;
+		}
+	}
+	if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+	{
+		extoffset -= adsize;
+		lbcount -= elen;
+		extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
+		if (!bh)
+		{
+			UDF_I_LENALLOC(inode) = extoffset - udf_file_entry_alloc_offset(inode);
+			mark_inode_dirty(inode);
+		}
+		else
+		{
+			struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
+			aed->lengthAllocDescs = cpu_to_le32(extoffset - sizeof(struct allocExtDesc));
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+				udf_update_tag(bh->b_data, extoffset);
+			else
+				udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(bh, inode);
+		}
+	}
+	UDF_I_LENEXTENTS(inode) = lbcount;
+
+	udf_release_data(bh);
+}
+
+void udf_truncate_extents(struct inode * inode)
+{
+	kernel_lb_addr bloc, eloc, neloc = { 0, 0 };
+	uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc;
+	int8_t etype;
+	int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits;
+	struct buffer_head *bh = NULL;
+	int adsize;
+
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(long_ad);
+	else
+		adsize = 0;
+
+	etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+	offset += (inode->i_size & (inode->i_sb->s_blocksize - 1));
+	if (etype != -1)
+	{
+		extoffset -= adsize;
+		extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset);
+		extoffset += adsize;
+
+		if (offset)
+			lenalloc = extoffset;
+		else
+			lenalloc = extoffset - adsize;
+
+		if (!bh)
+			lenalloc -= udf_file_entry_alloc_offset(inode);
+		else
+			lenalloc -= sizeof(struct allocExtDesc);
+
+		while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1)
+		{
+			if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
+			{
+				udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
+				extoffset = 0;
+				if (lelen)
+				{
+					if (!bh)
+						BUG();
+					else
+						memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
+					udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
+				}
+				else
+				{
+					if (!bh)
+					{
+						UDF_I_LENALLOC(inode) = lenalloc;
+						mark_inode_dirty(inode);
+					}
+					else
+					{
+						struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
+						aed->lengthAllocDescs = cpu_to_le32(lenalloc);
+						if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+							udf_update_tag(bh->b_data, lenalloc +
+								sizeof(struct allocExtDesc));
+						else
+							udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
+						mark_buffer_dirty_inode(bh, inode);
+					}
+				}
+
+				udf_release_data(bh);
+				extoffset = sizeof(struct allocExtDesc);
+				bloc = eloc;
+				bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, bloc, 0));
+				if (elen)
+					lelen = (elen + inode->i_sb->s_blocksize - 1) >>
+						inode->i_sb->s_blocksize_bits;
+				else
+					lelen = 1;
+			}
+			else
+			{
+				extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
+				extoffset += adsize;
+			}
+		}
+
+		if (lelen)
+		{
+			if (!bh)
+				BUG();
+			else
+				memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
+			udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
+		}
+		else
+		{
+			if (!bh)
+			{
+				UDF_I_LENALLOC(inode) = lenalloc;
+				mark_inode_dirty(inode);
+			}
+			else
+			{
+				struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
+				aed->lengthAllocDescs = cpu_to_le32(lenalloc);
+				if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+					udf_update_tag(bh->b_data, lenalloc +
+						sizeof(struct allocExtDesc));
+				else
+					udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
+				mark_buffer_dirty_inode(bh, inode);
+			}
+		}
+	}
+	else if (inode->i_size)
+	{
+		if (offset)
+		{
+			extoffset -= adsize;
+			etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
+			if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+			{
+				extoffset -= adsize;
+				elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
+				udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
+			}
+			else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+			{
+				kernel_lb_addr neloc = { 0, 0 };
+				extoffset -= adsize;
+				nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
+					((elen + offset + inode->i_sb->s_blocksize - 1) &
+					~(inode->i_sb->s_blocksize - 1));
+				udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
+				udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
+			}
+			else
+			{
+				if (elen & (inode->i_sb->s_blocksize - 1))
+				{
+					extoffset -= adsize;
+					elen = EXT_RECORDED_ALLOCATED |
+						((elen + inode->i_sb->s_blocksize - 1) &
+						~(inode->i_sb->s_blocksize - 1));
+					udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
+				}
+				memset(&eloc, 0x00, sizeof(kernel_lb_addr));
+				elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
+				udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
+			}
+		}
+	}
+	UDF_I_LENEXTENTS(inode) = inode->i_size;
+
+	udf_release_data(bh);
+}
diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h
new file mode 100644
index 000000000000..d7dbe6f3ba0c
--- /dev/null
+++ b/fs/udf/udf_i.h
@@ -0,0 +1,26 @@
+#ifndef __LINUX_UDF_I_H
+#define __LINUX_UDF_I_H
+
+#include <linux/udf_fs_i.h>
+static inline struct udf_inode_info *UDF_I(struct inode *inode)
+{
+	return list_entry(inode, struct udf_inode_info, vfs_inode);
+}
+
+#define UDF_I_LOCATION(X)	( UDF_I(X)->i_location )
+#define UDF_I_LENEATTR(X)	( UDF_I(X)->i_lenEAttr )
+#define UDF_I_LENALLOC(X)	( UDF_I(X)->i_lenAlloc )
+#define UDF_I_LENEXTENTS(X)	( UDF_I(X)->i_lenExtents )
+#define UDF_I_UNIQUE(X)		( UDF_I(X)->i_unique )
+#define UDF_I_ALLOCTYPE(X)	( UDF_I(X)->i_alloc_type )
+#define UDF_I_EFE(X)		( UDF_I(X)->i_efe )
+#define UDF_I_USE(X)		( UDF_I(X)->i_use )
+#define UDF_I_STRAT4096(X)	( UDF_I(X)->i_strat4096 )
+#define UDF_I_NEXT_ALLOC_BLOCK(X)	( UDF_I(X)->i_next_alloc_block )
+#define UDF_I_NEXT_ALLOC_GOAL(X)	( UDF_I(X)->i_next_alloc_goal )
+#define UDF_I_CRTIME(X)		( UDF_I(X)->i_crtime )
+#define UDF_I_SAD(X)		( UDF_I(X)->i_ext.i_sad )
+#define UDF_I_LAD(X)		( UDF_I(X)->i_ext.i_lad )
+#define UDF_I_DATA(X)		( UDF_I(X)->i_ext.i_data )
+
+#endif /* !defined(_LINUX_UDF_I_H) */
diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h
new file mode 100644
index 000000000000..0e54922daa09
--- /dev/null
+++ b/fs/udf/udf_sb.h
@@ -0,0 +1,139 @@
+#ifndef __LINUX_UDF_SB_H
+#define __LINUX_UDF_SB_H
+
+/* Since UDF 2.01 is ISO 13346 based... */
+#define UDF_SUPER_MAGIC			0x15013346
+
+#define UDF_MAX_READ_VERSION		0x0201
+#define UDF_MAX_WRITE_VERSION		0x0201
+
+#define UDF_FLAG_USE_EXTENDED_FE	0
+#define UDF_VERS_USE_EXTENDED_FE	0x0200
+#define UDF_FLAG_USE_STREAMS		1
+#define UDF_VERS_USE_STREAMS		0x0200
+#define UDF_FLAG_USE_SHORT_AD		2
+#define UDF_FLAG_USE_AD_IN_ICB		3
+#define UDF_FLAG_USE_FILE_CTIME_EA	4
+#define UDF_FLAG_STRICT			5
+#define UDF_FLAG_UNDELETE		6
+#define UDF_FLAG_UNHIDE			7
+#define UDF_FLAG_VARCONV		8
+#define UDF_FLAG_NLS_MAP		9
+#define UDF_FLAG_UTF8			10
+
+#define UDF_PART_FLAG_UNALLOC_BITMAP	0x0001
+#define UDF_PART_FLAG_UNALLOC_TABLE	0x0002
+#define UDF_PART_FLAG_FREED_BITMAP	0x0004
+#define UDF_PART_FLAG_FREED_TABLE	0x0008
+#define UDF_PART_FLAG_READ_ONLY		0x0010
+#define UDF_PART_FLAG_WRITE_ONCE	0x0020
+#define UDF_PART_FLAG_REWRITABLE	0x0040
+#define UDF_PART_FLAG_OVERWRITABLE	0x0080
+
+static inline struct udf_sb_info *UDF_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+#define UDF_SB_FREE(X)\
+{\
+	if (UDF_SB(X))\
+	{\
+		if (UDF_SB_PARTMAPS(X))\
+			kfree(UDF_SB_PARTMAPS(X));\
+		UDF_SB_PARTMAPS(X) = NULL;\
+	}\
+}
+
+#define UDF_SB_ALLOC_PARTMAPS(X,Y)\
+{\
+	UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\
+	if (UDF_SB_PARTMAPS(X) != NULL)\
+	{\
+		UDF_SB_NUMPARTS(X) = Y;\
+		memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\
+	}\
+	else\
+	{\
+		UDF_SB_NUMPARTS(X) = 0;\
+		udf_error(X, __FUNCTION__, "Unable to allocate space for %d partition maps", Y);\
+	}\
+}
+
+#define UDF_SB_ALLOC_BITMAP(X,Y,Z)\
+{\
+	int nr_groups = ((UDF_SB_PARTLEN((X),(Y)) + (sizeof(struct spaceBitmapDesc) << 3) +\
+		((X)->s_blocksize * 8) - 1) / ((X)->s_blocksize * 8));\
+	int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\
+	if (size <= PAGE_SIZE)\
+		UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(size, GFP_KERNEL);\
+	else\
+		UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = vmalloc(size);\
+	if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\
+	{\
+		memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00, size);\
+		UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\
+			(struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\
+		UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\
+	}\
+	else\
+	{\
+		udf_error(X, __FUNCTION__, "Unable to allocate space for bitmap and %d buffer_head pointers", nr_groups);\
+	}\
+}
+
+#define UDF_SB_FREE_BITMAP(X,Y,Z)\
+{\
+	int i;\
+	int nr_groups = UDF_SB_BITMAP_NR_GROUPS(X,Y,Z);\
+	int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\
+	for (i=0; i<nr_groups; i++)\
+	{\
+		if (UDF_SB_BITMAP(X,Y,Z,i))\
+			udf_release_data(UDF_SB_BITMAP(X,Y,Z,i));\
+	}\
+	if (size <= PAGE_SIZE)\
+		kfree(UDF_SB_PARTMAPS(X)[Y].Z.s_bitmap);\
+	else\
+		vfree(UDF_SB_PARTMAPS(X)[Y].Z.s_bitmap);\
+}
+
+#define UDF_QUERY_FLAG(X,Y)			( UDF_SB(X)->s_flags & ( 1 << (Y) ) )
+#define UDF_SET_FLAG(X,Y)			( UDF_SB(X)->s_flags |= ( 1 << (Y) ) )
+#define UDF_CLEAR_FLAG(X,Y)			( UDF_SB(X)->s_flags &= ~( 1 << (Y) ) )
+
+#define UDF_UPDATE_UDFREV(X,Y)			( ((Y) > UDF_SB_UDFREV(X)) ? UDF_SB_UDFREV(X) = (Y) : UDF_SB_UDFREV(X) )
+
+#define UDF_SB_PARTMAPS(X)			( UDF_SB(X)->s_partmaps )
+#define UDF_SB_PARTTYPE(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_type )
+#define UDF_SB_PARTROOT(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_root )
+#define UDF_SB_PARTLEN(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_len )
+#define UDF_SB_PARTVSN(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_volumeseqnum )
+#define UDF_SB_PARTNUM(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_num )
+#define UDF_SB_TYPESPAR(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing )
+#define UDF_SB_TYPEVIRT(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual )
+#define UDF_SB_PARTFUNC(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func )
+#define UDF_SB_PARTFLAGS(X,Y)			( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags )
+#define UDF_SB_BITMAP(X,Y,Z,I)			( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap[I] )
+#define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z)		( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups )
+
+#define UDF_SB_VOLIDENT(X)			( UDF_SB(X)->s_volident )
+#define UDF_SB_NUMPARTS(X)			( UDF_SB(X)->s_partitions )
+#define UDF_SB_PARTITION(X)			( UDF_SB(X)->s_partition )
+#define UDF_SB_SESSION(X)			( UDF_SB(X)->s_session )
+#define UDF_SB_ANCHOR(X)			( UDF_SB(X)->s_anchor )
+#define UDF_SB_LASTBLOCK(X)			( UDF_SB(X)->s_lastblock )
+#define UDF_SB_LVIDBH(X)			( UDF_SB(X)->s_lvidbh )
+#define UDF_SB_LVID(X)				( (struct logicalVolIntegrityDesc *)UDF_SB_LVIDBH(X)->b_data )
+#define UDF_SB_LVIDIU(X)			( (struct logicalVolIntegrityDescImpUse *)&(UDF_SB_LVID(X)->impUse[le32_to_cpu(UDF_SB_LVID(X)->numOfPartitions) * 2 * sizeof(uint32_t)/sizeof(uint8_t)]) )
+
+#define UDF_SB_UMASK(X)				( UDF_SB(X)->s_umask )
+#define UDF_SB_GID(X)				( UDF_SB(X)->s_gid )
+#define UDF_SB_UID(X)				( UDF_SB(X)->s_uid )
+#define UDF_SB_RECORDTIME(X)			( UDF_SB(X)->s_recordtime )
+#define UDF_SB_SERIALNUM(X)			( UDF_SB(X)->s_serialnum )
+#define UDF_SB_UDFREV(X)			( UDF_SB(X)->s_udfrev )
+#define UDF_SB_FLAGS(X)				( UDF_SB(X)->s_flags )
+#define UDF_SB_VAT(X)				( UDF_SB(X)->s_vat )
+
+#endif /* __LINUX_UDF_SB_H */
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
new file mode 100644
index 000000000000..1d5800e0cbe7
--- /dev/null
+++ b/fs/udf/udfdecl.h
@@ -0,0 +1,167 @@
+#ifndef __UDF_DECL_H
+#define __UDF_DECL_H
+
+#include <linux/udf_fs.h>
+#include "ecma_167.h"
+#include "osta_udf.h"
+
+#include <linux/fs.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/udf_fs_i.h>
+#include <linux/udf_fs_sb.h>
+#include <linux/buffer_head.h>
+
+#include "udfend.h"
+
+#define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) )
+#define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) )
+
+#define UDF_EXTENT_LENGTH_MASK	0x3FFFFFFF
+#define UDF_EXTENT_FLAG_MASK	0xC0000000
+
+#define UDF_NAME_PAD		4
+#define UDF_NAME_LEN		256
+#define UDF_PATH_LEN		1023
+
+#define udf_file_entry_alloc_offset(inode)\
+	(UDF_I_USE(inode) ?\
+		sizeof(struct unallocSpaceEntry) :\
+		((UDF_I_EFE(inode) ?\
+			sizeof(struct extendedFileEntry) :\
+			sizeof(struct fileEntry)) + UDF_I_LENEATTR(inode)))
+
+#define udf_ext0_offset(inode)\
+	(UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ?\
+		udf_file_entry_alloc_offset(inode) : 0)
+
+#define udf_get_lb_pblock(sb,loc,offset) udf_get_pblock((sb), (loc).logicalBlockNum, (loc).partitionReferenceNum, (offset))
+
+struct dentry;
+struct inode;
+struct task_struct;
+struct buffer_head;
+struct super_block;
+
+extern struct inode_operations udf_dir_inode_operations;
+extern struct file_operations udf_dir_operations;
+extern struct inode_operations udf_file_inode_operations;
+extern struct file_operations udf_file_operations;
+extern struct address_space_operations udf_aops;
+extern struct address_space_operations udf_adinicb_aops;
+extern struct address_space_operations udf_symlink_aops;
+
+struct udf_fileident_bh
+{
+	struct buffer_head *sbh;
+	struct buffer_head *ebh;
+	int soffset;
+	int eoffset;
+};
+
+struct udf_vds_record
+{
+	uint32_t block;
+	uint32_t volDescSeqNum;
+};
+
+struct generic_desc
+{
+	tag		descTag;
+	__le32		volDescSeqNum;
+};
+
+struct ustr
+{
+	uint8_t u_cmpID;
+	uint8_t u_name[UDF_NAME_LEN-2];
+	uint8_t u_len;
+};
+
+/* super.c */
+extern void udf_error(struct super_block *, const char *, const char *, ...);
+extern void udf_warning(struct super_block *, const char *, const char *, ...);
+
+/* namei.c */
+extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, struct fileIdentDesc *, struct udf_fileident_bh *, uint8_t *, uint8_t *);
+
+/* file.c */
+extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* inode.c */
+extern struct inode *udf_iget(struct super_block *, kernel_lb_addr);
+extern int udf_sync_inode(struct inode *);
+extern void udf_expand_file_adinicb(struct inode *, int, int *);
+extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *);
+extern struct buffer_head * udf_bread(struct inode *, int, int, int *);
+extern void udf_truncate(struct inode *);
+extern void udf_read_inode(struct inode *);
+extern void udf_delete_inode(struct inode *);
+extern void udf_clear_inode(struct inode *);
+extern int udf_write_inode(struct inode *, int);
+extern long udf_block_map(struct inode *, long);
+extern int8_t inode_bmap(struct inode *, int, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **);
+extern int8_t udf_add_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr, uint32_t, struct buffer_head **, int);
+extern int8_t udf_write_aext(struct inode *, kernel_lb_addr, int *, kernel_lb_addr, uint32_t, struct buffer_head *, int);
+extern int8_t udf_delete_aext(struct inode *, kernel_lb_addr, int, kernel_lb_addr, uint32_t, struct buffer_head *);
+extern int8_t udf_next_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int);
+extern int8_t udf_current_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int);
+
+/* misc.c */
+extern struct buffer_head *udf_tgetblk(struct super_block *, int);
+extern struct buffer_head *udf_tread(struct super_block *, int);
+extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t);
+extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t);
+extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *);
+extern struct buffer_head *udf_read_ptagged(struct super_block *, kernel_lb_addr, uint32_t, uint16_t *);
+extern void udf_release_data(struct buffer_head *);
+extern void udf_update_tag(char *, int);
+extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int);
+
+/* lowlevel.c */
+extern unsigned int udf_get_last_session(struct super_block *);
+extern unsigned long udf_get_last_block(struct super_block *);
+
+/* partition.c */
+extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern int udf_relocate_blocks(struct super_block *, long, long *);
+
+/* unicode.c */
+extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int);
+extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, int);
+extern int udf_build_ustr(struct ustr *, dstring *, int);
+extern int udf_CS0toUTF8(struct ustr *, struct ustr *);
+
+/* ialloc.c */
+extern void udf_free_inode(struct inode *);
+extern struct inode * udf_new_inode (struct inode *, int, int *);
+
+/* truncate.c */
+extern void udf_discard_prealloc(struct inode *);
+extern void udf_truncate_extents(struct inode *);
+
+/* balloc.c */
+extern void udf_free_blocks(struct super_block *, struct inode *, kernel_lb_addr, uint32_t, uint32_t);
+extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, uint32_t, uint32_t);
+extern int udf_new_block(struct super_block *, struct inode *, uint16_t, uint32_t, int *);
+
+/* fsync.c */
+extern int udf_fsync_file(struct file *, struct dentry *, int);
+
+/* directory.c */
+extern struct fileIdentDesc * udf_fileident_read(struct inode *, loff_t *, struct udf_fileident_bh *, struct fileIdentDesc *, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **);
+extern struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset);
+extern long_ad * udf_get_filelongad(uint8_t *, int, int *, int);
+extern short_ad * udf_get_fileshortad(uint8_t *, int, int *, int);
+
+/* crc.c */
+extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t);
+
+/* udftime.c */
+extern time_t *udf_stamp_to_time(time_t *, long *, kernel_timestamp);
+extern kernel_timestamp *udf_time_to_stamp(kernel_timestamp *, struct timespec);
+
+#endif /* __UDF_DECL_H */
diff --git a/fs/udf/udfend.h b/fs/udf/udfend.h
new file mode 100644
index 000000000000..17d378879561
--- /dev/null
+++ b/fs/udf/udfend.h
@@ -0,0 +1,81 @@
+#ifndef __UDF_ENDIAN_H
+#define __UDF_ENDIAN_H
+
+#include <asm/byteorder.h>
+#include <linux/string.h>
+
+static inline kernel_lb_addr lelb_to_cpu(lb_addr in)
+{
+	kernel_lb_addr out;
+	out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum);
+	out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum);
+	return out;
+}
+
+static inline lb_addr cpu_to_lelb(kernel_lb_addr in)
+{
+	lb_addr out;
+	out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum);
+	out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum);
+	return out;
+}
+
+static inline kernel_timestamp lets_to_cpu(timestamp in)
+{
+	kernel_timestamp out;
+	memcpy(&out, &in, sizeof(timestamp));
+	out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone);
+	out.year = le16_to_cpu(in.year);
+	return out;
+}
+
+static inline short_ad lesa_to_cpu(short_ad in)
+{
+	short_ad out;
+	out.extLength = le32_to_cpu(in.extLength);
+	out.extPosition = le32_to_cpu(in.extPosition);
+	return out;
+}
+
+static inline short_ad cpu_to_lesa(short_ad in)
+{
+	short_ad out;
+	out.extLength = cpu_to_le32(in.extLength);
+	out.extPosition = cpu_to_le32(in.extPosition);
+	return out;
+}
+
+static inline kernel_long_ad lela_to_cpu(long_ad in)
+{
+	kernel_long_ad out;
+	out.extLength = le32_to_cpu(in.extLength);
+	out.extLocation = lelb_to_cpu(in.extLocation);
+	return out;
+}
+
+static inline long_ad cpu_to_lela(kernel_long_ad in)
+{
+	long_ad out;
+	out.extLength = cpu_to_le32(in.extLength);
+	out.extLocation = cpu_to_lelb(in.extLocation);
+	return out;
+}
+
+static inline kernel_extent_ad leea_to_cpu(extent_ad in)
+{
+	kernel_extent_ad out;
+	out.extLength = le32_to_cpu(in.extLength);
+	out.extLocation = le32_to_cpu(in.extLocation);
+	return out;
+}
+
+static inline timestamp cpu_to_lets(kernel_timestamp in)
+{
+	timestamp out;
+	memcpy(&out, &in, sizeof(timestamp));
+	out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone);
+	out.year = cpu_to_le16(in.year);
+	return out;
+}
+
+#endif /* __UDF_ENDIAN_H */
diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c
new file mode 100644
index 000000000000..c2634bda6b50
--- /dev/null
+++ b/fs/udf/udftime.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Paul Eggert (eggert@twinsun.com).
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/*
+ * dgb 10/02/98: ripped this from glibc source to help convert timestamps to unix time 
+ *     10/04/98: added new table-based lookup after seeing how ugly the gnu code is
+ * blf 09/27/99: ripped out all the old code and inserted new table from
+ *					John Brockmeyer (without leap second corrections)
+ *				 rewrote udf_stamp_to_time and fixed timezone accounting in
+					udf_time_to_stamp.
+ */
+
+/*
+ * We don't take into account leap seconds. This may be correct or incorrect.
+ * For more NIST information (especially dealing with leap seconds), see:
+ *  http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include "udfdecl.h"
+
+#define EPOCH_YEAR 1970
+
+#ifndef __isleap
+/* Nonzero if YEAR is a leap year (every 4 years,
+   except every 100th isn't, and every 400th is).  */
+#define	__isleap(year)	\
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+#endif
+
+/* How many days come before each month (0-12).  */
+const unsigned short int __mon_yday[2][13] =
+{
+	/* Normal years.  */
+	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+	/* Leap years.  */
+	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+#define MAX_YEAR_SECONDS	69
+#define SPD 0x15180 /*3600*24*/
+#define SPY(y,l,s) (SPD * (365*y+l)+s)
+
+static time_t year_seconds[MAX_YEAR_SECONDS]= {
+/*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), 
+/*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), 
+/*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), 
+/*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), 
+/*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), 
+/*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), 
+/*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), 
+/*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), 
+/*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), 
+/*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), 
+/*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), 
+/*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), 
+/*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), 
+/*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), 
+/*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), 
+/*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), 
+/*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), 
+/*2038*/ SPY(68,17,0)
+};
+
+extern struct timezone sys_tz;
+
+#define SECS_PER_HOUR	(60 * 60)
+#define SECS_PER_DAY	(SECS_PER_HOUR * 24)
+
+time_t *
+udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src)
+{
+	int yday;
+	uint8_t type = src.typeAndTimezone >> 12;
+	int16_t offset;
+
+	if (type == 1)
+	{
+		offset = src.typeAndTimezone << 4;
+		/* sign extent offset */
+		offset = (offset >> 4);
+		if (offset == -2047) /* unspecified offset */
+			offset = 0;
+	}
+	else
+		offset = 0;
+
+	if ((src.year < EPOCH_YEAR) ||
+		(src.year > EPOCH_YEAR+MAX_YEAR_SECONDS))
+	{
+		*dest = -1;
+		*dest_usec = -1;
+		return NULL;
+	}
+	*dest = year_seconds[src.year - EPOCH_YEAR];
+	*dest -= offset * 60;
+
+	yday = ((__mon_yday[__isleap (src.year)]
+		[src.month-1]) + (src.day-1));
+	*dest += ( ( (yday* 24) + src.hour ) * 60 + src.minute ) * 60 + src.second;
+	*dest_usec = src.centiseconds * 10000 + src.hundredsOfMicroseconds * 100 + src.microseconds;
+	return dest;
+}
+
+
+kernel_timestamp *
+udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts)
+{
+	long int days, rem, y;
+	const unsigned short int *ip;
+	int16_t offset;
+
+	offset = -sys_tz.tz_minuteswest;
+
+	if (!dest)
+		return NULL;
+
+	dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF);
+
+	ts.tv_sec += offset * 60;
+	days = ts.tv_sec / SECS_PER_DAY;
+	rem = ts.tv_sec % SECS_PER_DAY;
+	dest->hour = rem / SECS_PER_HOUR;
+	rem %= SECS_PER_HOUR;
+	dest->minute = rem / 60;
+	dest->second = rem % 60;
+	y = 1970;
+
+#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+
+	while (days < 0 || days >= (__isleap(y) ? 366 : 365))
+	{
+		long int yg = y + days / 365 - (days % 365 < 0);
+
+		/* Adjust DAYS and Y to match the guessed year.  */
+		days -= ((yg - y) * 365
+			+ LEAPS_THRU_END_OF (yg - 1)
+			- LEAPS_THRU_END_OF (y - 1));
+		y = yg;
+	}
+	dest->year = y;
+	ip = __mon_yday[__isleap(y)];
+	for (y = 11; days < (long int) ip[y]; --y)
+		continue;
+	days -= ip[y];
+	dest->month = y + 1;
+	dest->day = days + 1;
+
+	dest->centiseconds = ts.tv_nsec / 10000000;
+	dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000) / 100;
+	dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 -
+		dest->hundredsOfMicroseconds * 100);
+	return dest;
+}
+
+/* EOF */
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
new file mode 100644
index 000000000000..5a80efd8debc
--- /dev/null
+++ b/fs/udf/unicode.c
@@ -0,0 +1,516 @@
+/*
+ * unicode.c
+ *
+ * PURPOSE
+ *	Routines for converting between UTF-8 and OSTA Compressed Unicode.
+ *      Also handles filename mangling
+ *
+ * DESCRIPTION
+ *	OSTA Compressed Unicode is explained in the OSTA UDF specification.
+ *		http://www.osta.org/
+ *	UTF-8 is explained in the IETF RFC XXXX.
+ *		ftp://ftp.internic.net/rfc/rfcxxxx.txt
+ *
+ * CONTACTS
+ *	E-mail regarding any portion of the Linux UDF file system should be
+ *	directed to the development team's mailing list (run by majordomo):
+ *		linux_udf@hpesjro.fc.hp.com
+ *
+ * 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.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/kernel.h>
+#include <linux/string.h>	/* for memset */
+#include <linux/nls.h>
+#include <linux/udf_fs.h>
+
+#include "udf_sb.h"
+
+static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int);
+
+static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
+{
+	if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN-2) )
+		return 0;
+	memset(dest, 0, sizeof(struct ustr));
+	memcpy(dest->u_name, src, strlen);
+	dest->u_cmpID = 0x08;
+	dest->u_len = strlen;
+	return strlen;
+}
+
+/*
+ * udf_build_ustr
+ */
+int udf_build_ustr(struct ustr *dest, dstring *ptr, int size)
+{
+	int usesize;
+
+	if ( (!dest) || (!ptr) || (!size) )
+		return -1;
+
+	memset(dest, 0, sizeof(struct ustr));
+	usesize= (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size;
+	dest->u_cmpID=ptr[0];
+	dest->u_len=ptr[size-1];
+	memcpy(dest->u_name, ptr+1, usesize-1);
+	return 0;
+}
+
+/*
+ * udf_build_ustr_exact
+ */
+static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize)
+{
+	if ( (!dest) || (!ptr) || (!exactsize) )
+		return -1;
+
+	memset(dest, 0, sizeof(struct ustr));
+	dest->u_cmpID=ptr[0];
+	dest->u_len=exactsize-1;
+	memcpy(dest->u_name, ptr+1, exactsize-1);
+	return 0;
+}
+
+/*
+ * udf_ocu_to_utf8
+ *
+ * PURPOSE
+ *	Convert OSTA Compressed Unicode to the UTF-8 equivalent.
+ *
+ * DESCRIPTION
+ *	This routine is only called by udf_filldir().
+ *
+ * PRE-CONDITIONS
+ *	utf			Pointer to UTF-8 output buffer.
+ *	ocu			Pointer to OSTA Compressed Unicode input buffer
+ *				of size UDF_NAME_LEN bytes.
+ * 				both of type "struct ustr *"
+ *
+ * POST-CONDITIONS
+ *	<return>		Zero on success.
+ *
+ * HISTORY
+ *	November 12, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i)
+{
+	uint8_t *ocu;
+	uint32_t c;
+	uint8_t cmp_id, ocu_len;
+	int i;
+
+	ocu = ocu_i->u_name;
+
+	ocu_len = ocu_i->u_len;
+	cmp_id = ocu_i->u_cmpID;
+	utf_o->u_len = 0;
+
+	if (ocu_len == 0)
+	{
+		memset(utf_o, 0, sizeof(struct ustr));
+		utf_o->u_cmpID = 0;
+		utf_o->u_len = 0;
+		return 0;
+	}
+
+	if ((cmp_id != 8) && (cmp_id != 16))
+	{
+		printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name);
+		return 0;
+	}
+
+	for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;)
+	{
+
+		/* Expand OSTA compressed Unicode to Unicode */
+		c = ocu[i++];
+		if (cmp_id == 16)
+			c = (c << 8) | ocu[i++];
+
+		/* Compress Unicode to UTF-8 */
+		if (c < 0x80U)
+			utf_o->u_name[utf_o->u_len++] = (uint8_t)c;
+		else if (c < 0x800U)
+		{
+			utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6));
+			utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f));
+		}
+		else
+		{
+			utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12));
+			utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f));
+			utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f));
+		}
+	}
+	utf_o->u_cmpID=8;
+
+	return utf_o->u_len;
+}
+
+/*
+ *
+ * udf_utf8_to_ocu
+ *
+ * PURPOSE
+ *	Convert UTF-8 to the OSTA Compressed Unicode equivalent.
+ *
+ * DESCRIPTION
+ *	This routine is only called by udf_lookup().
+ *
+ * PRE-CONDITIONS
+ *	ocu			Pointer to OSTA Compressed Unicode output
+ *				buffer of size UDF_NAME_LEN bytes.
+ *	utf			Pointer to UTF-8 input buffer.
+ *	utf_len			Length of UTF-8 input buffer in bytes.
+ *
+ * POST-CONDITIONS
+ *	<return>		Zero on success.
+ *
+ * HISTORY
+ *	November 12, 1997 - Andrew E. Mileski
+ *	Written, tested, and released.
+ */
+static int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length)
+{
+	unsigned c, i, max_val, utf_char;
+	int utf_cnt, u_len;
+
+	memset(ocu, 0, sizeof(dstring) * length);
+	ocu[0] = 8;
+	max_val = 0xffU;
+
+try_again:
+	u_len = 0U;
+	utf_char = 0U;
+	utf_cnt = 0U;
+	for (i = 0U; i < utf->u_len; i++)
+	{
+		c = (uint8_t)utf->u_name[i];
+
+		/* Complete a multi-byte UTF-8 character */
+		if (utf_cnt)
+		{
+			utf_char = (utf_char << 6) | (c & 0x3fU);
+			if (--utf_cnt)
+				continue;
+		}
+		else
+		{
+			/* Check for a multi-byte UTF-8 character */
+			if (c & 0x80U)
+			{
+				/* Start a multi-byte UTF-8 character */
+				if ((c & 0xe0U) == 0xc0U)
+				{
+					utf_char = c & 0x1fU;
+					utf_cnt = 1;
+				}
+				else if ((c & 0xf0U) == 0xe0U)
+				{
+					utf_char = c & 0x0fU;
+					utf_cnt = 2;
+				}
+				else if ((c & 0xf8U) == 0xf0U)
+				{
+					utf_char = c & 0x07U;
+					utf_cnt = 3;
+				}
+				else if ((c & 0xfcU) == 0xf8U)
+				{
+					utf_char = c & 0x03U;
+					utf_cnt = 4;
+				}
+				else if ((c & 0xfeU) == 0xfcU)
+				{
+					utf_char = c & 0x01U;
+					utf_cnt = 5;
+				}
+				else
+					goto error_out;
+				continue;
+			} else
+				/* Single byte UTF-8 character (most common) */
+				utf_char = c;
+		}
+
+		/* Choose no compression if necessary */
+		if (utf_char > max_val)
+		{
+			if ( 0xffU == max_val )
+			{
+				max_val = 0xffffU;
+				ocu[0] = (uint8_t)0x10U;
+				goto try_again;
+			}
+			goto error_out;
+		}
+
+		if (max_val == 0xffffU)
+		{
+			ocu[++u_len] = (uint8_t)(utf_char >> 8);
+		}
+		ocu[++u_len] = (uint8_t)(utf_char & 0xffU);
+	}
+
+
+	if (utf_cnt)
+	{
+error_out:
+		ocu[++u_len] = '?';
+		printk(KERN_DEBUG "udf: bad UTF-8 character\n");
+	}
+
+	ocu[length - 1] = (uint8_t)u_len + 1;
+	return u_len + 1;
+}
+
+static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr *ocu_i)
+{
+	uint8_t *ocu;
+	uint32_t c;
+	uint8_t cmp_id, ocu_len;
+	int i;
+
+	ocu = ocu_i->u_name;
+
+	ocu_len = ocu_i->u_len;
+	cmp_id = ocu_i->u_cmpID;
+	utf_o->u_len = 0;
+
+	if (ocu_len == 0)
+	{
+		memset(utf_o, 0, sizeof(struct ustr));
+		utf_o->u_cmpID = 0;
+		utf_o->u_len = 0;
+		return 0;
+	}
+
+	if ((cmp_id != 8) && (cmp_id != 16))
+	{
+		printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name);
+		return 0;
+	}
+
+	for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;)
+	{
+		/* Expand OSTA compressed Unicode to Unicode */
+		c = ocu[i++];
+		if (cmp_id == 16)
+			c = (c << 8) | ocu[i++];
+
+		utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len], 
+			UDF_NAME_LEN - utf_o->u_len);
+	}
+	utf_o->u_cmpID=8;
+
+	return utf_o->u_len;
+}
+
+static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length)
+{
+	unsigned len, i, max_val;
+	uint16_t uni_char;
+	int u_len;
+
+	memset(ocu, 0, sizeof(dstring) * length);
+	ocu[0] = 8;
+	max_val = 0xffU;
+
+try_again:
+	u_len = 0U;
+	for (i = 0U; i < uni->u_len; i++)
+	{
+		len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char);
+		if (len <= 0)
+			continue;
+
+		if (uni_char > max_val)
+		{
+			max_val = 0xffffU;
+			ocu[0] = (uint8_t)0x10U;
+			goto try_again;
+		}
+		
+		if (max_val == 0xffffU)
+			ocu[++u_len] = (uint8_t)(uni_char >> 8);
+		ocu[++u_len] = (uint8_t)(uni_char & 0xffU);
+		i += len - 1;
+	}
+
+	ocu[length - 1] = (uint8_t)u_len + 1;
+	return u_len + 1;
+}
+
+int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, int flen)
+{
+	struct ustr filename, unifilename;
+	int len;
+
+	if (udf_build_ustr_exact(&unifilename, sname, flen))
+	{
+		return 0;
+	}
+
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8))
+	{
+		if (!udf_CS0toUTF8(&filename, &unifilename) )
+		{
+			udf_debug("Failed in udf_get_filename: sname = %s\n", sname);
+			return 0;
+		}
+	}
+	else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+	{
+		if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename) )
+		{
+			udf_debug("Failed in udf_get_filename: sname = %s\n", sname);
+			return 0;
+		}
+	}
+	else
+		return 0;
+
+	if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len,
+		unifilename.u_name, unifilename.u_len)))
+	{
+		return len;
+	}
+	return 0;
+}
+
+int udf_put_filename(struct super_block *sb, const uint8_t *sname, uint8_t *dname, int flen)
+{
+	struct ustr unifilename;
+	int namelen;
+
+	if ( !(udf_char_to_ustr(&unifilename, sname, flen)) )
+	{
+		return 0;
+	}
+
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8))
+	{
+		if ( !(namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN)) )
+		{
+			return 0;
+		}
+	}
+	else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+	{
+		if ( !(namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN)) )
+		{
+			return 0;
+		}
+	}
+	else
+		return 0;
+
+	return namelen;
+}
+
+#define ILLEGAL_CHAR_MARK	'_'
+#define EXT_MARK			'.'
+#define CRC_MARK			'#'
+#define EXT_SIZE			5
+
+static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen)
+{
+	int index, newIndex = 0, needsCRC = 0;	
+	int extIndex = 0, newExtIndex = 0, hasExt = 0;
+	unsigned short valueCRC;
+	uint8_t curr;
+	const uint8_t hexChar[] = "0123456789ABCDEF";
+
+	if (udfName[0] == '.' && (udfLen == 1 ||
+		(udfLen == 2 && udfName[1] == '.')))
+	{
+		needsCRC = 1;
+		newIndex = udfLen;
+		memcpy(newName, udfName, udfLen);
+	}
+	else
+	{	
+		for (index = 0; index < udfLen; index++)
+		{
+			curr = udfName[index];
+			if (curr == '/' || curr == 0)
+			{
+				needsCRC = 1;
+				curr = ILLEGAL_CHAR_MARK;
+				while (index+1 < udfLen && (udfName[index+1] == '/' ||
+					udfName[index+1] == 0))
+					index++;
+			}
+			if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE)
+			{
+				if (udfLen == index + 1)
+					hasExt = 0;
+				else
+				{
+					hasExt = 1;
+					extIndex = index;
+					newExtIndex = newIndex;
+				}
+			}
+			if (newIndex < 256)
+				newName[newIndex++] = curr;
+			else
+				needsCRC = 1;
+		}
+	}
+	if (needsCRC)
+	{
+		uint8_t ext[EXT_SIZE];
+		int localExtIndex = 0;
+
+		if (hasExt)
+		{
+			int maxFilenameLen;
+			for(index = 0; index<EXT_SIZE && extIndex + index +1 < udfLen;
+				index++ )
+			{
+				curr = udfName[extIndex + index + 1];
+
+				if (curr == '/' || curr == 0)
+				{
+					needsCRC = 1;
+					curr = ILLEGAL_CHAR_MARK;
+					while(extIndex + index + 2 < udfLen && (index + 1 < EXT_SIZE
+						&& (udfName[extIndex + index + 2] == '/' ||
+							udfName[extIndex + index + 2] == 0)))
+						index++;
+				}
+				ext[localExtIndex++] = curr;
+			}
+			maxFilenameLen = 250 - localExtIndex;
+			if (newIndex > maxFilenameLen)
+				newIndex = maxFilenameLen;
+			else
+				newIndex = newExtIndex;
+		}
+		else if (newIndex > 250)
+			newIndex = 250;
+		newName[newIndex++] = CRC_MARK;
+		valueCRC = udf_crc(fidName, fidNameLen, 0);
+		newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
+		newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
+		newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
+		newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
+
+		if (hasExt)
+		{
+			newName[newIndex++] = EXT_MARK;
+			for (index = 0;index < localExtIndex ;index++ )
+				newName[newIndex++] = ext[index];
+		}
+	}
+	return newIndex;
+}