v4.19.13 snapshot.
diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig
new file mode 100644
index 0000000..aa41505
--- /dev/null
+++ b/fs/udf/Kconfig
@@ -0,0 +1,16 @@
+config UDF_FS
+	tristate "UDF file system support"
+	select CRC_ITU_T
+	select NLS
+	help
+	  This is a file system used on some CD-ROMs and DVDs. Since the
+	  file system is supported by multiple operating systems and is more
+	  compatible with standard unix file systems, it is also suitable for
+	  removable USB disks. Say Y if you intend to mount DVD discs or CDRW's
+	  written in packet mode, or if you want to use UDF for removable USB
+	  disks. Please read <file:Documentation/filesystems/udf.txt>.
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called udf.
+
+	  If unsure, say N.
diff --git a/fs/udf/Makefile b/fs/udf/Makefile
new file mode 100644
index 0000000..eb880f6
--- /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 \
+		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 0000000..fcda0fc
--- /dev/null
+++ b/fs/udf/balloc.c
@@ -0,0 +1,735 @@
+/*
+ * balloc.c
+ *
+ * PURPOSE
+ *	Block allocation handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 1999-2001 Ben Fennema
+ *  (C) 1999 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  02/24/99 blf  Created.
+ *
+ */
+
+#include "udfdecl.h"
+
+#include <linux/bitops.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+#define udf_clear_bit	__test_and_clear_bit_le
+#define udf_set_bit	__test_and_set_bit_le
+#define udf_test_bit	test_bit_le
+#define udf_find_next_one_bit	find_next_bit_le
+
+static int read_block_bitmap(struct super_block *sb,
+			     struct udf_bitmap *bitmap, unsigned int block,
+			     unsigned long bitmap_nr)
+{
+	struct buffer_head *bh = NULL;
+	int retval = 0;
+	struct kernel_lb_addr loc;
+
+	loc.logicalBlockNum = bitmap->s_extPosition;
+	loc.partitionReferenceNum = UDF_SB(sb)->s_partition;
+
+	bh = udf_tread(sb, udf_get_lb_pblock(sb, &loc, block));
+	if (!bh)
+		retval = -EIO;
+
+	bitmap->s_block_bitmap[bitmap_nr] = bh;
+	return retval;
+}
+
+static int __load_block_bitmap(struct super_block *sb,
+			       struct udf_bitmap *bitmap,
+			       unsigned int block_group)
+{
+	int retval = 0;
+	int nr_groups = bitmap->s_nr_groups;
+
+	if (block_group >= nr_groups) {
+		udf_debug("block_group (%u) > nr_groups (%d)\n",
+			  block_group, nr_groups);
+	}
+
+	if (bitmap->s_block_bitmap[block_group])
+		return block_group;
+
+	retval = read_block_bitmap(sb, bitmap, block_group, block_group);
+	if (retval < 0)
+		return retval;
+
+	return block_group;
+}
+
+static inline int load_block_bitmap(struct super_block *sb,
+				    struct udf_bitmap *bitmap,
+				    unsigned int block_group)
+{
+	int slot;
+
+	slot = __load_block_bitmap(sb, bitmap, block_group);
+
+	if (slot < 0)
+		return slot;
+
+	if (!bitmap->s_block_bitmap[slot])
+		return -EIO;
+
+	return slot;
+}
+
+static void udf_add_free_space(struct super_block *sb, u16 partition, u32 cnt)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct logicalVolIntegrityDesc *lvid;
+
+	if (!sbi->s_lvid_bh)
+		return;
+
+	lvid = (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data;
+	le32_add_cpu(&lvid->freeSpaceTable[partition], cnt);
+	udf_updated_lvid(sb);
+}
+
+static void udf_bitmap_free_blocks(struct super_block *sb,
+				   struct udf_bitmap *bitmap,
+				   struct kernel_lb_addr *bloc,
+				   uint32_t offset,
+				   uint32_t count)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct buffer_head *bh = NULL;
+	struct udf_part_map *partmap;
+	unsigned long block;
+	unsigned long block_group;
+	unsigned long bit;
+	unsigned long i;
+	int bitmap_nr;
+	unsigned long overflow;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	partmap = &sbi->s_partmaps[bloc->partitionReferenceNum];
+	if (bloc->logicalBlockNum + count < count ||
+	    (bloc->logicalBlockNum + count) > partmap->s_partition_len) {
+		udf_debug("%u < %d || %u + %u > %u\n",
+			  bloc->logicalBlockNum, 0,
+			  bloc->logicalBlockNum, count,
+			  partmap->s_partition_len);
+		goto error_return;
+	}
+
+	block = bloc->logicalBlockNum + offset +
+		(sizeof(struct spaceBitmapDesc) << 3);
+
+	do {
+		overflow = 0;
+		block_group = block >> (sb->s_blocksize_bits + 3);
+		bit = block % (sb->s_blocksize << 3);
+
+		/*
+		* Check to see if we are freeing blocks across a group boundary.
+		*/
+		if (bit + count > (sb->s_blocksize << 3)) {
+			overflow = bit + count - (sb->s_blocksize << 3);
+			count -= overflow;
+		}
+		bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+		if (bitmap_nr < 0)
+			goto error_return;
+
+		bh = bitmap->s_block_bitmap[bitmap_nr];
+		for (i = 0; i < count; i++) {
+			if (udf_set_bit(bit + i, bh->b_data)) {
+				udf_debug("bit %lu already set\n", bit + i);
+				udf_debug("byte=%2x\n",
+					  ((__u8 *)bh->b_data)[(bit + i) >> 3]);
+			}
+		}
+		udf_add_free_space(sb, sbi->s_partition, count);
+		mark_buffer_dirty(bh);
+		if (overflow) {
+			block += count;
+			count = overflow;
+		}
+	} while (overflow);
+
+error_return:
+	mutex_unlock(&sbi->s_alloc_mutex);
+}
+
+static int udf_bitmap_prealloc_blocks(struct super_block *sb,
+				      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;
+	__u32 part_len;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	part_len = sbi->s_partmaps[partition].s_partition_len;
+	if (first_block >= part_len)
+		goto out;
+
+	if (first_block + block_count > part_len)
+		block_count = part_len - first_block;
+
+	do {
+		nr_groups = udf_compute_nr_groups(sb, partition);
+		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_clear_bit(bit, bh->b_data))
+				goto out;
+			block_count--;
+			alloc_count++;
+			bit++;
+			block++;
+		}
+		mark_buffer_dirty(bh);
+	} while (block_count > 0);
+
+out:
+	udf_add_free_space(sb, partition, -alloc_count);
+	mutex_unlock(&sbi->s_alloc_mutex);
+	return alloc_count;
+}
+
+static udf_pblk_t udf_bitmap_new_block(struct super_block *sb,
+				struct udf_bitmap *bitmap, uint16_t partition,
+				uint32_t goal, int *err)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int newbit, bit = 0;
+	udf_pblk_t block;
+	int block_group, group_start;
+	int end_goal, nr_groups, bitmap_nr, i;
+	struct buffer_head *bh = NULL;
+	char *ptr;
+	udf_pblk_t newblock = 0;
+
+	*err = -ENOSPC;
+	mutex_lock(&sbi->s_alloc_mutex);
+
+repeat:
+	if (goal >= sbi->s_partmaps[partition].s_partition_len)
+		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(bh->b_data,
+						    sb->s_blocksize << 3,
+						    group_start << 3);
+			if (bit < sb->s_blocksize << 3)
+				break;
+		}
+	}
+	if (i >= (nr_groups * 2)) {
+		mutex_unlock(&sbi->s_alloc_mutex);
+		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) {
+		mutex_unlock(&sbi->s_alloc_mutex);
+		return 0;
+	}
+
+search_back:
+	i = 0;
+	while (i < 7 && bit > (group_start << 3) &&
+	       udf_test_bit(bit - 1, bh->b_data)) {
+		++i;
+		--bit;
+	}
+
+got_block:
+	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);
+
+	udf_add_free_space(sb, partition, -1);
+	mutex_unlock(&sbi->s_alloc_mutex);
+	*err = 0;
+	return newblock;
+
+error_return:
+	*err = -EIO;
+	mutex_unlock(&sbi->s_alloc_mutex);
+	return 0;
+}
+
+static void udf_table_free_blocks(struct super_block *sb,
+				  struct inode *table,
+				  struct kernel_lb_addr *bloc,
+				  uint32_t offset,
+				  uint32_t count)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *partmap;
+	uint32_t start, end;
+	uint32_t elen;
+	struct kernel_lb_addr eloc;
+	struct extent_position oepos, epos;
+	int8_t etype;
+	struct udf_inode_info *iinfo;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	partmap = &sbi->s_partmaps[bloc->partitionReferenceNum];
+	if (bloc->logicalBlockNum + count < count ||
+	    (bloc->logicalBlockNum + count) > partmap->s_partition_len) {
+		udf_debug("%u < %d || %u + %u > %u\n",
+			  bloc->logicalBlockNum, 0,
+			  bloc->logicalBlockNum, count,
+			  partmap->s_partition_len);
+		goto error_return;
+	}
+
+	iinfo = UDF_I(table);
+	udf_add_free_space(sb, sbi->s_partition, count);
+
+	start = bloc->logicalBlockNum + offset;
+	end = bloc->logicalBlockNum + offset + count - 1;
+
+	epos.offset = oepos.offset = sizeof(struct unallocSpaceEntry);
+	elen = 0;
+	epos.block = oepos.block = iinfo->i_location;
+	epos.bh = oepos.bh = NULL;
+
+	while (count &&
+	       (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) {
+		if (((eloc.logicalBlockNum +
+			(elen >> sb->s_blocksize_bits)) == start)) {
+			if ((0x3FFFFFFF - elen) <
+					(count << sb->s_blocksize_bits)) {
+				uint32_t tmp = ((0x3FFFFFFF - elen) >>
+							sb->s_blocksize_bits);
+				count -= tmp;
+				start += tmp;
+				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, &oepos, &eloc, elen, 1);
+		} else if (eloc.logicalBlockNum == (end + 1)) {
+			if ((0x3FFFFFFF - elen) <
+					(count << sb->s_blocksize_bits)) {
+				uint32_t tmp = ((0x3FFFFFFF - elen) >>
+						sb->s_blocksize_bits);
+				count -= tmp;
+				end -= tmp;
+				eloc.logicalBlockNum -= tmp;
+				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, &oepos, &eloc, elen, 1);
+		}
+
+		if (epos.bh != oepos.bh) {
+			oepos.block = epos.block;
+			brelse(oepos.bh);
+			get_bh(epos.bh);
+			oepos.bh = epos.bh;
+			oepos.offset = 0;
+		} else {
+			oepos.offset = epos.offset;
+		}
+	}
+
+	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;
+
+		eloc.logicalBlockNum = start;
+		elen = EXT_RECORDED_ALLOCATED |
+			(count << sb->s_blocksize_bits);
+
+		if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+			adsize = sizeof(struct short_ad);
+		else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+			adsize = sizeof(struct long_ad);
+		else {
+			brelse(oepos.bh);
+			brelse(epos.bh);
+			goto error_return;
+		}
+
+		if (epos.offset + (2 * adsize) > sb->s_blocksize) {
+			/* Steal a block from the extent being free'd */
+			udf_setup_indirect_aext(table, eloc.logicalBlockNum,
+						&epos);
+
+			eloc.logicalBlockNum++;
+			elen -= sb->s_blocksize;
+		}
+
+		/* It's possible that stealing the block emptied the extent */
+		if (elen)
+			__udf_add_aext(table, &epos, &eloc, elen, 1);
+	}
+
+	brelse(epos.bh);
+	brelse(oepos.bh);
+
+error_return:
+	mutex_unlock(&sbi->s_alloc_mutex);
+	return;
+}
+
+static int udf_table_prealloc_blocks(struct super_block *sb,
+				     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 elen, adsize;
+	struct kernel_lb_addr eloc;
+	struct extent_position epos;
+	int8_t etype = -1;
+	struct udf_inode_info *iinfo;
+
+	if (first_block >= sbi->s_partmaps[partition].s_partition_len)
+		return 0;
+
+	iinfo = UDF_I(table);
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		return 0;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	epos.offset = sizeof(struct unallocSpaceEntry);
+	epos.block = iinfo->i_location;
+	epos.bh = NULL;
+	eloc.logicalBlockNum = 0xFFFFFFFF;
+
+	while (first_block != eloc.logicalBlockNum &&
+	       (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) {
+		udf_debug("eloc=%u, elen=%u, first_block=%u\n",
+			  eloc.logicalBlockNum, elen, first_block);
+		; /* empty loop body */
+	}
+
+	if (first_block == eloc.logicalBlockNum) {
+		epos.offset -= adsize;
+
+		alloc_count = (elen >> sb->s_blocksize_bits);
+		if (alloc_count > block_count) {
+			alloc_count = block_count;
+			eloc.logicalBlockNum += alloc_count;
+			elen -= (alloc_count << sb->s_blocksize_bits);
+			udf_write_aext(table, &epos, &eloc,
+					(etype << 30) | elen, 1);
+		} else
+			udf_delete_aext(table, epos);
+	} else {
+		alloc_count = 0;
+	}
+
+	brelse(epos.bh);
+
+	if (alloc_count)
+		udf_add_free_space(sb, partition, -alloc_count);
+	mutex_unlock(&sbi->s_alloc_mutex);
+	return alloc_count;
+}
+
+static udf_pblk_t udf_table_new_block(struct super_block *sb,
+			       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;
+	udf_pblk_t newblock = 0;
+	uint32_t adsize;
+	uint32_t elen, goal_elen = 0;
+	struct kernel_lb_addr eloc, uninitialized_var(goal_eloc);
+	struct extent_position epos, goal_epos;
+	int8_t etype;
+	struct udf_inode_info *iinfo = UDF_I(table);
+
+	*err = -ENOSPC;
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		return newblock;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	if (goal >= sbi->s_partmaps[partition].s_partition_len)
+		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.
+	 */
+	epos.offset = sizeof(struct unallocSpaceEntry);
+	epos.block = iinfo->i_location;
+	epos.bh = goal_epos.bh = NULL;
+
+	while (spread &&
+	       (etype = udf_next_aext(table, &epos, &eloc, &elen, 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_epos.bh != epos.bh) {
+				brelse(goal_epos.bh);
+				goal_epos.bh = epos.bh;
+				get_bh(goal_epos.bh);
+			}
+			goal_epos.block = epos.block;
+			goal_epos.offset = epos.offset - adsize;
+			goal_eloc = eloc;
+			goal_elen = (etype << 30) | elen;
+		}
+	}
+
+	brelse(epos.bh);
+
+	if (spread == 0xFFFFFFFF) {
+		brelse(goal_epos.bh);
+		mutex_unlock(&sbi->s_alloc_mutex);
+		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 (goal_elen)
+		udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1);
+	else
+		udf_delete_aext(table, goal_epos);
+	brelse(goal_epos.bh);
+
+	udf_add_free_space(sb, partition, -1);
+
+	mutex_unlock(&sbi->s_alloc_mutex);
+	*err = 0;
+	return newblock;
+}
+
+void udf_free_blocks(struct super_block *sb, struct inode *inode,
+		     struct kernel_lb_addr *bloc, uint32_t offset,
+		     uint32_t count)
+{
+	uint16_t partition = bloc->partitionReferenceNum;
+	struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
+
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) {
+		udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap,
+				       bloc, offset, count);
+	} else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) {
+		udf_table_free_blocks(sb, map->s_uspace.s_table,
+				      bloc, offset, count);
+	} else if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) {
+		udf_bitmap_free_blocks(sb, map->s_fspace.s_bitmap,
+				       bloc, offset, count);
+	} else if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) {
+		udf_table_free_blocks(sb, map->s_fspace.s_table,
+				      bloc, offset, count);
+	}
+
+	if (inode) {
+		inode_sub_bytes(inode,
+				((sector_t)count) << sb->s_blocksize_bits);
+	}
+}
+
+inline int udf_prealloc_blocks(struct super_block *sb,
+			       struct inode *inode,
+			       uint16_t partition, uint32_t first_block,
+			       uint32_t block_count)
+{
+	struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
+	int allocated;
+
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
+		allocated = udf_bitmap_prealloc_blocks(sb,
+						       map->s_uspace.s_bitmap,
+						       partition, first_block,
+						       block_count);
+	else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE)
+		allocated = udf_table_prealloc_blocks(sb,
+						      map->s_uspace.s_table,
+						      partition, first_block,
+						      block_count);
+	else if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP)
+		allocated = udf_bitmap_prealloc_blocks(sb,
+						       map->s_fspace.s_bitmap,
+						       partition, first_block,
+						       block_count);
+	else if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE)
+		allocated = udf_table_prealloc_blocks(sb,
+						      map->s_fspace.s_table,
+						      partition, first_block,
+						      block_count);
+	else
+		return 0;
+
+	if (inode && allocated > 0)
+		inode_add_bytes(inode, allocated << sb->s_blocksize_bits);
+	return allocated;
+}
+
+inline udf_pblk_t udf_new_block(struct super_block *sb,
+			 struct inode *inode,
+			 uint16_t partition, uint32_t goal, int *err)
+{
+	struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
+	udf_pblk_t block;
+
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
+		block = udf_bitmap_new_block(sb,
+					     map->s_uspace.s_bitmap,
+					     partition, goal, err);
+	else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE)
+		block = udf_table_new_block(sb,
+					    map->s_uspace.s_table,
+					    partition, goal, err);
+	else if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP)
+		block = udf_bitmap_new_block(sb,
+					     map->s_fspace.s_bitmap,
+					     partition, goal, err);
+	else if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE)
+		block = udf_table_new_block(sb,
+					    map->s_fspace.s_table,
+					    partition, goal, err);
+	else {
+		*err = -EIO;
+		return 0;
+	}
+	if (inode && block)
+		inode_add_bytes(inode, sb->s_blocksize);
+	return block;
+}
diff --git a/fs/udf/dir.c b/fs/udf/dir.c
new file mode 100644
index 0000000..c19dba4
--- /dev/null
+++ b/fs/udf/dir.c
@@ -0,0 +1,209 @@
+/*
+ * dir.c
+ *
+ * PURPOSE
+ *  Directory handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 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/bio.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+
+static int udf_readdir(struct file *file, struct dir_context *ctx)
+{
+	struct inode *dir = file_inode(file);
+	struct udf_inode_info *iinfo = UDF_I(dir);
+	struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL};
+	struct fileIdentDesc *fi = NULL;
+	struct fileIdentDesc cfi;
+	udf_pblk_t block, iblock;
+	loff_t nf_pos;
+	int flen;
+	unsigned char *fname = NULL, *copy_name = NULL;
+	unsigned char *nameptr;
+	uint16_t liu;
+	uint8_t lfi;
+	loff_t size = udf_ext0_offset(dir) + dir->i_size;
+	struct buffer_head *tmp, *bha[16];
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	sector_t offset;
+	int i, num, ret = 0;
+	struct extent_position epos = { NULL, 0, {0, 0} };
+	struct super_block *sb = dir->i_sb;
+
+	if (ctx->pos == 0) {
+		if (!dir_emit_dot(file, ctx))
+			return 0;
+		ctx->pos = 1;
+	}
+	nf_pos = (ctx->pos - 1) << 2;
+	if (nf_pos >= size)
+		goto out;
+
+	fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+	if (!fname) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (nf_pos == 0)
+		nf_pos = udf_ext0_offset(dir);
+
+	fibh.soffset = fibh.eoffset = nf_pos & (sb->s_blocksize - 1);
+	if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+		if (inode_bmap(dir, nf_pos >> sb->s_blocksize_bits,
+		    &epos, &eloc, &elen, &offset)
+		    != (EXT_RECORDED_ALLOCATED >> 30)) {
+			ret = -ENOENT;
+			goto out;
+		}
+		block = udf_get_lb_pblock(sb, &eloc, offset);
+		if ((++offset << sb->s_blocksize_bits) < elen) {
+			if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+				epos.offset -= sizeof(struct short_ad);
+			else if (iinfo->i_alloc_type ==
+					ICBTAG_FLAG_AD_LONG)
+				epos.offset -= sizeof(struct long_ad);
+		} else {
+			offset = 0;
+		}
+
+		if (!(fibh.sbh = fibh.ebh = udf_tread(sb, block))) {
+			ret = -EIO;
+			goto out;
+		}
+
+		if (!(offset & ((16 >> (sb->s_blocksize_bits - 9)) - 1))) {
+			i = 16 >> (sb->s_blocksize_bits - 9);
+			if (i + offset > (elen >> sb->s_blocksize_bits))
+				i = (elen >> sb->s_blocksize_bits) - offset;
+			for (num = 0; i > 0; i--) {
+				block = udf_get_lb_pblock(sb, &eloc, offset + i);
+				tmp = udf_tgetblk(sb, block);
+				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
+					bha[num++] = tmp;
+				else
+					brelse(tmp);
+			}
+			if (num) {
+				ll_rw_block(REQ_OP_READ, REQ_RAHEAD, num, bha);
+				for (i = 0; i < num; i++)
+					brelse(bha[i]);
+			}
+		}
+	}
+
+	while (nf_pos < size) {
+		struct kernel_lb_addr tloc;
+
+		ctx->pos = (nf_pos >> 2) + 1;
+
+		fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc,
+					&elen, &offset);
+		if (!fi)
+			goto out;
+
+		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 {
+				if (!copy_name) {
+					copy_name = kmalloc(UDF_NAME_LEN,
+							    GFP_NOFS);
+					if (!copy_name) {
+						ret = -ENOMEM;
+						goto out;
+					}
+				}
+				nameptr = copy_name;
+				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(sb, UDF_FLAG_UNDELETE))
+				continue;
+		}
+
+		if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
+			if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
+				continue;
+		}
+
+		if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
+			if (!dir_emit_dotdot(file, ctx))
+				goto out;
+			continue;
+		}
+
+		flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN);
+		if (flen < 0)
+			continue;
+
+		tloc = lelb_to_cpu(cfi.icb.extLocation);
+		iblock = udf_get_lb_pblock(sb, &tloc, 0);
+		if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN))
+			goto out;
+	} /* end while */
+
+	ctx->pos = (nf_pos >> 2) + 1;
+
+out:
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+	brelse(epos.bh);
+	kfree(fname);
+	kfree(copy_name);
+
+	return ret;
+}
+
+/* readdir and lookup functions */
+const struct file_operations udf_dir_operations = {
+	.llseek			= generic_file_llseek,
+	.read			= generic_read_dir,
+	.iterate_shared		= udf_readdir,
+	.unlocked_ioctl		= udf_ioctl,
+	.fsync			= generic_file_fsync,
+};
diff --git a/fs/udf/directory.c b/fs/udf/directory.c
new file mode 100644
index 0000000..d952301
--- /dev/null
+++ b/fs/udf/directory.c
@@ -0,0 +1,242 @@
+/*
+ * directory.c
+ *
+ * PURPOSE
+ *	Directory related functions
+ *
+ * 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/bio.h>
+
+struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
+					 struct udf_fileident_bh *fibh,
+					 struct fileIdentDesc *cfi,
+					 struct extent_position *epos,
+					 struct kernel_lb_addr *eloc, uint32_t *elen,
+					 sector_t *offset)
+{
+	struct fileIdentDesc *fi;
+	int i, num;
+	udf_pblk_t block;
+	struct buffer_head *tmp, *bha[16];
+	struct udf_inode_info *iinfo = UDF_I(dir);
+
+	fibh->soffset = fibh->eoffset;
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+		fi = udf_get_fileident(iinfo->i_ext.i_data -
+				       (iinfo->i_efe ?
+					sizeof(struct extendedFileEntry) :
+					sizeof(struct fileEntry)),
+				       dir->i_sb->s_blocksize,
+				       &(fibh->eoffset));
+		if (!fi)
+			return NULL;
+
+		*nf_pos += fibh->eoffset - fibh->soffset;
+
+		memcpy((uint8_t *)cfi, (uint8_t *)fi,
+		       sizeof(struct fileIdentDesc));
+
+		return fi;
+	}
+
+	if (fibh->eoffset == dir->i_sb->s_blocksize) {
+		uint32_t lextoffset = epos->offset;
+		unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits;
+
+		if (udf_next_aext(dir, epos, eloc, elen, 1) !=
+		    (EXT_RECORDED_ALLOCATED >> 30))
+			return NULL;
+
+		block = udf_get_lb_pblock(dir->i_sb, eloc, *offset);
+
+		(*offset)++;
+
+		if ((*offset << blocksize_bits) >= *elen)
+			*offset = 0;
+		else
+			epos->offset = lextoffset;
+
+		brelse(fibh->sbh);
+		fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
+		if (!fibh->sbh)
+			return NULL;
+		fibh->soffset = fibh->eoffset = 0;
+
+		if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) {
+			i = 16 >> (blocksize_bits - 9);
+			if (i + *offset > (*elen >> blocksize_bits))
+				i = (*elen >> 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(REQ_OP_READ, REQ_RAHEAD, num, bha);
+				for (i = 0; i < num; i++)
+					brelse(bha[i]);
+			}
+		}
+	} else if (fibh->sbh != fibh->ebh) {
+		brelse(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;
+
+	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) {
+		uint32_t lextoffset = epos->offset;
+
+		if (udf_next_aext(dir, epos, eloc, elen, 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
+			epos->offset = lextoffset;
+
+		fibh->soffset -= dir->i_sb->s_blocksize;
+		fibh->eoffset -= dir->i_sb->s_blocksize;
+
+		fibh->ebh = udf_tread(dir->i_sb, block);
+		if (!fibh->ebh)
+			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 = udf_dir_entry_len(cfi);
+			*nf_pos += fi_len - (fibh->eoffset - fibh->soffset);
+			fibh->eoffset = fibh->soffset + fi_len;
+		} else {
+			memcpy((uint8_t *)cfi, (uint8_t *)fi,
+			       sizeof(struct fileIdentDesc));
+		}
+	}
+	/* Got last entry outside of dir size - fs is corrupted! */
+	if (*nf_pos > dir->i_size)
+		return NULL;
+	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, buffer=%p, offset=%p\n",
+			  buffer, offset);
+		return NULL;
+	}
+
+	ptr = buffer;
+
+	if ((*offset > 0) && (*offset < bufsize))
+		ptr += *offset;
+	fi = (struct fileIdentDesc *)ptr;
+	if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
+		udf_debug("0x%x != TAG_IDENT_FID\n",
+			  le16_to_cpu(fi->descTag.tagIdent));
+		udf_debug("offset: %d sizeof: %lu bufsize: %d\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;
+}
+
+struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
+			      int inc)
+{
+	struct short_ad *sa;
+
+	if ((!ptr) || (!offset)) {
+		pr_err("%s: invalidparms\n", __func__);
+		return NULL;
+	}
+
+	if ((*offset + sizeof(struct short_ad)) > maxoffset)
+		return NULL;
+	else {
+		sa = (struct short_ad *)ptr;
+		if (sa->extLength == 0)
+			return NULL;
+	}
+
+	if (inc)
+		*offset += sizeof(struct short_ad);
+	return sa;
+}
+
+struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)
+{
+	struct long_ad *la;
+
+	if ((!ptr) || (!offset)) {
+		pr_err("%s: invalidparms\n", __func__);
+		return NULL;
+	}
+
+	if ((*offset + sizeof(struct long_ad)) > maxoffset)
+		return NULL;
+	else {
+		la = (struct long_ad *)ptr;
+		if (la->extLength == 0)
+			return NULL;
+	}
+
+	if (inc)
+		*offset += sizeof(struct long_ad);
+	return la;
+}
diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h
new file mode 100644
index 0000000..9f24bd1
--- /dev/null
+++ b/fs/udf/ecma_167.h
@@ -0,0 +1,796 @@
+/*
+ * 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) */
+struct charspec {
+	uint8_t		charSetType;
+	uint8_t		charSetInfo[63];
+} __packed;
+
+/* 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) */
+struct timestamp {
+	__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;
+} __packed;
+
+/* 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) */
+struct regid {
+	uint8_t		flags;
+	uint8_t		ident[23];
+	uint8_t		identSuffix[8];
+} __packed;
+
+/* 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];
+} __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];
+} __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];
+} __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;
+	struct regid		archType;
+	struct regid		bootIdent;
+	__le32			bootExtLocation;
+	__le32			bootExtLength;
+	__le64			loadAddress;
+	__le64			startAddress;
+	struct timestamp	descCreationDateAndTime;
+	__le16			flags;
+	uint8_t			reserved2[32];
+	uint8_t			bootUse[1906];
+} __packed;
+
+/* Flags (ECMA 167r3 2/9.4.12) */
+#define BOOT_FLAGS_ERASE		0x01
+
+/* Extent Descriptor (ECMA 167r3 3/7.1) */
+struct extent_ad {
+	__le32		extLength;
+	__le32		extLocation;
+} __packed;
+
+struct kernel_extent_ad {
+	uint32_t	extLength;
+	uint32_t	extLocation;
+};
+
+/* Descriptor Tag (ECMA 167r3 3/7.2) */
+struct tag {
+	__le16		tagIdent;
+	__le16		descVersion;
+	uint8_t		tagChecksum;
+	uint8_t		reserved;
+	__le16		tagSerialNum;
+	__le16		descCRC;
+	__le16		descCRCLength;
+	__le32		tagLocation;
+} __packed;
+
+/* 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];
+} __packed;
+
+/* Primary Volume Descriptor (ECMA 167r3 3/10.1) */
+struct primaryVolDesc {
+	struct tag		descTag;
+	__le32			volDescSeqNum;
+	__le32			primaryVolDescNum;
+	dstring			volIdent[32];
+	__le16			volSeqNum;
+	__le16			maxVolSeqNum;
+	__le16			interchangeLvl;
+	__le16			maxInterchangeLvl;
+	__le32			charSetList;
+	__le32			maxCharSetList;
+	dstring			volSetIdent[128];
+	struct charspec		descCharSet;
+	struct charspec		explanatoryCharSet;
+	struct extent_ad	volAbstract;
+	struct extent_ad	volCopyright;
+	struct regid		appIdent;
+	struct timestamp	recordingDateAndTime;
+	struct regid		impIdent;
+	uint8_t			impUse[64];
+	__le32			predecessorVolDescSeqLocation;
+	__le16			flags;
+	uint8_t			reserved[22];
+} __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 {
+	struct tag		descTag;
+	struct extent_ad	mainVolDescSeqExt;
+	struct extent_ad	reserveVolDescSeqExt;
+	uint8_t	 		reserved[480];
+} __packed;
+
+/* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */
+struct volDescPtr {
+	struct tag		descTag;
+	__le32			volDescSeqNum;
+	struct extent_ad	nextVolDescSeqExt;
+	uint8_t			reserved[484];
+} __packed;
+
+/* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */
+struct impUseVolDesc {
+	struct tag	descTag;
+	__le32		volDescSeqNum;
+	struct regid	impIdent;
+	uint8_t		impUse[460];
+} __packed;
+
+/* Partition Descriptor (ECMA 167r3 3/10.5) */
+struct partitionDesc {
+	struct tag descTag;
+	__le32 volDescSeqNum;
+	__le16 partitionFlags;
+	__le16 partitionNumber;
+	struct regid partitionContents;
+	uint8_t partitionContentsUse[128];
+	__le32 accessType;
+	__le32 partitionStartingLocation;
+	__le32 partitionLength;
+	struct regid impIdent;
+	uint8_t impUse[128];
+	uint8_t reserved[156];
+} __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 {
+	struct tag		descTag;
+	__le32			volDescSeqNum;
+	struct charspec		descCharSet;
+	dstring			logicalVolIdent[128];
+	__le32			logicalBlockSize;
+	struct regid		domainIdent;
+	uint8_t			logicalVolContentsUse[16];
+	__le32			mapTableLength;
+	__le32			numPartitionMaps;
+	struct regid		impIdent;
+	uint8_t			impUse[128];
+	struct extent_ad	integritySeqExt;
+	uint8_t			partitionMaps[0];
+} __packed;
+
+/* Generic Partition Map (ECMA 167r3 3/10.7.1) */
+struct genericPartitionMap {
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		partitionMapping[0];
+} __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;
+} __packed;
+
+/* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */
+struct genericPartitionMap2 {
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		partitionIdent[62];
+} __packed;
+
+/* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */
+struct unallocSpaceDesc {
+	struct tag		descTag;
+	__le32			volDescSeqNum;
+	__le32			numAllocDescs;
+	struct extent_ad	allocDescs[0];
+} __packed;
+
+/* Terminating Descriptor (ECMA 167r3 3/10.9) */
+struct terminatingDesc {
+	struct tag	descTag;
+	uint8_t		reserved[496];
+} __packed;
+
+/* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */
+struct logicalVolIntegrityDesc {
+	struct tag		descTag;
+	struct timestamp	recordingDateAndTime;
+	__le32			integrityType;
+	struct extent_ad	nextIntegrityExt;
+	uint8_t			logicalVolContentsUse[32];
+	__le32			numOfPartitions;
+	__le32			lengthOfImpUse;
+	__le32			freeSpaceTable[0];
+	__le32			sizeTable[0];
+	uint8_t			impUse[0];
+} __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) */
+struct lb_addr {
+	__le32		logicalBlockNum;
+	__le16	 	partitionReferenceNum;
+} __packed;
+
+/* ... and its in-core analog */
+struct kernel_lb_addr {
+	uint32_t		logicalBlockNum;
+	uint16_t	 	partitionReferenceNum;
+};
+
+/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
+struct short_ad {
+        __le32		extLength;
+        __le32		extPosition;
+} __packed;
+
+/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
+struct long_ad {
+	__le32		extLength;
+	struct lb_addr	extLocation;
+	uint8_t		impUse[6];
+} __packed;
+
+struct kernel_long_ad {
+	uint32_t		extLength;
+	struct kernel_lb_addr	extLocation;
+	uint8_t			impUse[6];
+};
+
+/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
+struct ext_ad {
+	__le32		extLength;
+	__le32		recordedLength;
+	__le32		informationLength;
+	struct lb_addr	extLocation;
+} __packed;
+
+struct kernel_ext_ad {
+	uint32_t		extLength;
+	uint32_t		recordedLength;
+	uint32_t		informationLength;
+	struct kernel_lb_addr	extLocation;
+};
+
+/* 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 {
+	struct tag		descTag;
+	struct timestamp	recordingDateAndTime;
+	__le16			interchangeLvl;
+	__le16			maxInterchangeLvl;
+	__le32			charSetList;
+	__le32			maxCharSetList;
+	__le32			fileSetNum;
+	__le32			fileSetDescNum;
+	struct charspec		logicalVolIdentCharSet;
+	dstring			logicalVolIdent[128];
+	struct charspec		fileSetCharSet;
+	dstring			fileSetIdent[32];
+	dstring			copyrightFileIdent[32];
+	dstring			abstractFileIdent[32];
+	struct long_ad		rootDirectoryICB;
+	struct regid		domainIdent;
+	struct long_ad		nextExt;
+	struct long_ad		streamDirectoryICB;
+	uint8_t			reserved[32];
+} __packed;
+
+/* Partition Header Descriptor (ECMA 167r3 4/14.3) */
+struct partitionHeaderDesc {
+	struct short_ad	unallocSpaceTable;
+	struct short_ad	unallocSpaceBitmap;
+	struct short_ad	partitionIntegrityTable;
+	struct short_ad	freedSpaceTable;
+	struct short_ad	freedSpaceBitmap;
+	uint8_t		reserved[88];
+} __packed;
+
+/* File Identifier Descriptor (ECMA 167r3 4/14.4) */
+struct fileIdentDesc {
+	struct tag	descTag;
+	__le16		fileVersionNum;
+	uint8_t		fileCharacteristics;
+	uint8_t		lengthFileIdent;
+	struct long_ad	icb;
+	__le16		lengthOfImpUse;
+	uint8_t		impUse[0];
+	uint8_t		fileIdent[0];
+	uint8_t		padding[0];
+} __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 {
+	struct tag	descTag;
+	__le32		previousAllocExtLocation;
+	__le32		lengthAllocDescs;
+} __packed;
+
+/* ICB Tag (ECMA 167r3 4/14.6) */
+struct icbtag {
+	__le32		priorRecordedNumDirectEntries;
+	__le16		strategyType;
+	__le16		strategyParameter;
+	__le16		numEntries;
+	uint8_t		reserved;
+	uint8_t		fileType;
+	struct lb_addr	parentICBLocation;
+	__le16		flags;
+} __packed;
+
+/* 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 {
+	struct tag	descTag;
+	struct icbtag	icbTag;
+	struct long_ad	indirectICB;
+} __packed;
+
+/* Terminal Entry (ECMA 167r3 4/14.8) */
+struct terminalEntry {
+	struct tag	descTag;
+	struct icbtag	icbTag;
+} __packed;
+
+/* File Entry (ECMA 167r3 4/14.9) */
+struct fileEntry {
+	struct tag		descTag;
+	struct icbtag		icbTag;
+	__le32			uid;
+	__le32			gid;
+	__le32			permissions;
+	__le16			fileLinkCount;
+	uint8_t			recordFormat;
+	uint8_t			recordDisplayAttr;
+	__le32			recordLength;
+	__le64			informationLength;
+	__le64			logicalBlocksRecorded;
+	struct timestamp	accessTime;
+	struct timestamp	modificationTime;
+	struct timestamp	attrTime;
+	__le32			checkpoint;
+	struct long_ad		extendedAttrICB;
+	struct regid		impIdent;
+	__le64			uniqueID;
+	__le32			lengthExtendedAttr;
+	__le32			lengthAllocDescs;
+	uint8_t			extendedAttr[0];
+	uint8_t			allocDescs[0];
+} __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 {
+	struct tag	descTag;
+	__le32		impAttrLocation;
+	__le32		appAttrLocation;
+} __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];
+} __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];
+} __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;
+} __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;
+} __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];
+} __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];
+} __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;
+	struct regid	impIdent;
+	uint8_t		impUse[0];
+} __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;
+	struct regid	appIdent;
+	uint8_t		appUse[0];
+} __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 {
+	struct tag	descTag;
+	struct icbtag	icbTag;
+	__le32		lengthAllocDescs;
+	uint8_t		allocDescs[0];
+} __packed;
+
+/* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */
+struct spaceBitmapDesc {
+	struct tag	descTag;
+	__le32		numOfBits;
+	__le32		numOfBytes;
+	uint8_t		bitmap[0];
+} __packed;
+
+/* Partition Integrity Entry (ECMA 167r3 4/14.13) */
+struct partitionIntegrityEntry {
+	struct tag		descTag;
+	struct icbtag		icbTag;
+	struct timestamp	recordingDateAndTime;
+	uint8_t			integrityType;
+	uint8_t			reserved[175];
+	struct regid		impIdent;
+	uint8_t			impUse[256];
+} __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];
+} __packed;
+
+/* Path Component (ECMA 167r3 4/14.16.1) */
+struct pathComponent {
+	uint8_t		componentType;
+	uint8_t		lengthComponentIdent;
+	__le16		componentFileVersionNum;
+	dstring		componentIdent[0];
+} __packed;
+
+/* File Entry (ECMA 167r3 4/14.17) */
+struct extendedFileEntry {
+	struct tag		descTag;
+	struct icbtag		icbTag;
+	__le32			uid;
+	__le32			gid;
+	__le32			permissions;
+	__le16			fileLinkCount;
+	uint8_t			recordFormat;
+	uint8_t			recordDisplayAttr;
+	__le32			recordLength;
+	__le64			informationLength;
+	__le64			objectSize;
+	__le64			logicalBlocksRecorded;
+	struct timestamp	accessTime;
+	struct timestamp	modificationTime;
+	struct timestamp	createTime;
+	struct timestamp	attrTime;
+	__le32			checkpoint;
+	__le32			reserved;
+	struct long_ad		extendedAttrICB;
+	struct long_ad		streamDirectoryICB;
+	struct regid		impIdent;
+	__le64			uniqueID;
+	__le32			lengthExtendedAttr;
+	__le32			lengthAllocDescs;
+	uint8_t			extendedAttr[0];
+	uint8_t			allocDescs[0];
+} __packed;
+
+#endif /* _ECMA_167_H */
diff --git a/fs/udf/file.c b/fs/udf/file.c
new file mode 100644
index 0000000..cd31e4f
--- /dev/null
+++ b/fs/udf/file.c
@@ -0,0 +1,290 @@
+/*
+ * file.c
+ *
+ * PURPOSE
+ *  File handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *  This file is distributed under the terms of the GNU General Public
+ *  License (GPL). Copies of the GPL can be obtained from:
+ *    ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *  Each contributing author retains all rights to their own work.
+ *
+ *  (C) 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/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/string.h> /* memset */
+#include <linux/capability.h>
+#include <linux/errno.h>
+#include <linux/pagemap.h>
+#include <linux/uio.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+static void __udf_adinicb_readpage(struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	char *kaddr;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	loff_t isize = i_size_read(inode);
+
+	/*
+	 * We have to be careful here as truncate can change i_size under us.
+	 * So just sample it once and use the same value everywhere.
+	 */
+	kaddr = kmap_atomic(page);
+	memcpy(kaddr, iinfo->i_ext.i_data + iinfo->i_lenEAttr, isize);
+	memset(kaddr + isize, 0, PAGE_SIZE - isize);
+	flush_dcache_page(page);
+	SetPageUptodate(page);
+	kunmap_atomic(kaddr);
+}
+
+static int udf_adinicb_readpage(struct file *file, struct page *page)
+{
+	BUG_ON(!PageLocked(page));
+	__udf_adinicb_readpage(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;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	BUG_ON(!PageLocked(page));
+
+	kaddr = kmap_atomic(page);
+	memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr, kaddr,
+		i_size_read(inode));
+	SetPageUptodate(page);
+	kunmap_atomic(kaddr);
+	mark_inode_dirty(inode);
+	unlock_page(page);
+
+	return 0;
+}
+
+static int udf_adinicb_write_begin(struct file *file,
+			struct address_space *mapping, loff_t pos,
+			unsigned len, unsigned flags, struct page **pagep,
+			void **fsdata)
+{
+	struct page *page;
+
+	if (WARN_ON_ONCE(pos >= PAGE_SIZE))
+		return -EIO;
+	page = grab_cache_page_write_begin(mapping, 0, flags);
+	if (!page)
+		return -ENOMEM;
+	*pagep = page;
+
+	if (!PageUptodate(page))
+		__udf_adinicb_readpage(page);
+	return 0;
+}
+
+static ssize_t udf_adinicb_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
+{
+	/* Fallback to buffered I/O. */
+	return 0;
+}
+
+static int udf_adinicb_write_end(struct file *file, struct address_space *mapping,
+				 loff_t pos, unsigned len, unsigned copied,
+				 struct page *page, void *fsdata)
+{
+	struct inode *inode = page->mapping->host;
+	loff_t last_pos = pos + copied;
+	if (last_pos > inode->i_size)
+		i_size_write(inode, last_pos);
+	set_page_dirty(page);
+	unlock_page(page);
+	put_page(page);
+	return copied;
+}
+
+const struct address_space_operations udf_adinicb_aops = {
+	.readpage	= udf_adinicb_readpage,
+	.writepage	= udf_adinicb_writepage,
+	.write_begin	= udf_adinicb_write_begin,
+	.write_end	= udf_adinicb_write_end,
+	.direct_IO	= udf_adinicb_direct_IO,
+};
+
+static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	ssize_t retval;
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file_inode(file);
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	int err;
+
+	inode_lock(inode);
+
+	retval = generic_write_checks(iocb, from);
+	if (retval <= 0)
+		goto out;
+
+	down_write(&iinfo->i_data_sem);
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+		loff_t end = iocb->ki_pos + iov_iter_count(from);
+
+		if (inode->i_sb->s_blocksize <
+				(udf_file_entry_alloc_offset(inode) + end)) {
+			err = udf_expand_file_adinicb(inode);
+			if (err) {
+				inode_unlock(inode);
+				udf_debug("udf_expand_adinicb: err=%d\n", err);
+				return err;
+			}
+		} else {
+			iinfo->i_lenAlloc = max(end, inode->i_size);
+			up_write(&iinfo->i_data_sem);
+		}
+	} else
+		up_write(&iinfo->i_data_sem);
+
+	retval = __generic_file_write_iter(iocb, from);
+out:
+	inode_unlock(inode);
+
+	if (retval > 0) {
+		mark_inode_dirty(inode);
+		retval = generic_write_sync(iocb, retval);
+	}
+
+	return retval;
+}
+
+long udf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	long old_block, new_block;
+	int result;
+
+	if (inode_permission(inode, MAY_READ) != 0) {
+		udf_debug("no permission to access inode %lu\n", inode->i_ino);
+		return -EPERM;
+	}
+
+	if (!arg && ((cmd == UDF_GETVOLIDENT) || (cmd == UDF_GETEASIZE) ||
+		     (cmd == UDF_RELOCATE_BLOCKS) || (cmd == UDF_GETEABLOCK))) {
+		udf_debug("invalid argument to udf_ioctl\n");
+		return -EINVAL;
+	}
+
+	switch (cmd) {
+	case UDF_GETVOLIDENT:
+		if (copy_to_user((char __user *)arg,
+				 UDF_SB(inode->i_sb)->s_volume_ident, 32))
+			return -EFAULT;
+		return 0;
+	case UDF_RELOCATE_BLOCKS:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (get_user(old_block, (long __user *)arg))
+			return -EFAULT;
+		result = udf_relocate_blocks(inode->i_sb,
+						old_block, &new_block);
+		if (result == 0)
+			result = put_user(new_block, (long __user *)arg);
+		return result;
+	case UDF_GETEASIZE:
+		return put_user(UDF_I(inode)->i_lenEAttr, (int __user *)arg);
+	case UDF_GETEABLOCK:
+		return copy_to_user((char __user *)arg,
+				    UDF_I(inode)->i_ext.i_data,
+				    UDF_I(inode)->i_lenEAttr) ? -EFAULT : 0;
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static int udf_release_file(struct inode *inode, struct file *filp)
+{
+	if (filp->f_mode & FMODE_WRITE &&
+	    atomic_read(&inode->i_writecount) == 1) {
+		/*
+		 * Grab i_mutex to avoid races with writes changing i_size
+		 * while we are running.
+		 */
+		inode_lock(inode);
+		down_write(&UDF_I(inode)->i_data_sem);
+		udf_discard_prealloc(inode);
+		udf_truncate_tail_extent(inode);
+		up_write(&UDF_I(inode)->i_data_sem);
+		inode_unlock(inode);
+	}
+	return 0;
+}
+
+const struct file_operations udf_file_operations = {
+	.read_iter		= generic_file_read_iter,
+	.unlocked_ioctl		= udf_ioctl,
+	.open			= generic_file_open,
+	.mmap			= generic_file_mmap,
+	.write_iter		= udf_file_write_iter,
+	.release		= udf_release_file,
+	.fsync			= generic_file_fsync,
+	.splice_read		= generic_file_splice_read,
+	.llseek			= generic_file_llseek,
+};
+
+static int udf_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = d_inode(dentry);
+	struct super_block *sb = inode->i_sb;
+	int error;
+
+	error = setattr_prepare(dentry, attr);
+	if (error)
+		return error;
+
+	if ((attr->ia_valid & ATTR_UID) &&
+	    UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET) &&
+	    !uid_eq(attr->ia_uid, UDF_SB(sb)->s_uid))
+		return -EPERM;
+	if ((attr->ia_valid & ATTR_GID) &&
+	    UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET) &&
+	    !gid_eq(attr->ia_gid, UDF_SB(sb)->s_gid))
+		return -EPERM;
+
+	if ((attr->ia_valid & ATTR_SIZE) &&
+	    attr->ia_size != i_size_read(inode)) {
+		error = udf_setsize(inode, attr->ia_size);
+		if (error)
+			return error;
+	}
+
+	setattr_copy(inode, attr);
+	mark_inode_dirty(inode);
+	return 0;
+}
+
+const struct inode_operations udf_file_inode_operations = {
+	.setattr		= udf_setattr,
+};
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c
new file mode 100644
index 0000000..f8e5872
--- /dev/null
+++ b/fs/udf/ialloc.c
@@ -0,0 +1,137 @@
+/*
+ * ialloc.c
+ *
+ * PURPOSE
+ *	Inode allocation handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 1998-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ *  02/24/99 blf  Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <linux/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);
+	struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb);
+
+	if (lvidiu) {
+		mutex_lock(&sbi->s_alloc_mutex);
+		if (S_ISDIR(inode->i_mode))
+			le32_add_cpu(&lvidiu->numDirs, -1);
+		else
+			le32_add_cpu(&lvidiu->numFiles, -1);
+		udf_updated_lvid(sb);
+		mutex_unlock(&sbi->s_alloc_mutex);
+	}
+
+	udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1);
+}
+
+struct inode *udf_new_inode(struct inode *dir, umode_t mode)
+{
+	struct super_block *sb = dir->i_sb;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct inode *inode;
+	udf_pblk_t block;
+	uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
+	struct udf_inode_info *iinfo;
+	struct udf_inode_info *dinfo = UDF_I(dir);
+	struct logicalVolIntegrityDescImpUse *lvidiu;
+	int err;
+
+	inode = new_inode(sb);
+
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	iinfo = UDF_I(inode);
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) {
+		iinfo->i_efe = 1;
+		if (UDF_VERS_USE_EXTENDED_FE > sbi->s_udfrev)
+			sbi->s_udfrev = UDF_VERS_USE_EXTENDED_FE;
+		iinfo->i_ext.i_data = kzalloc(inode->i_sb->s_blocksize -
+					    sizeof(struct extendedFileEntry),
+					    GFP_KERNEL);
+	} else {
+		iinfo->i_efe = 0;
+		iinfo->i_ext.i_data = kzalloc(inode->i_sb->s_blocksize -
+					    sizeof(struct fileEntry),
+					    GFP_KERNEL);
+	}
+	if (!iinfo->i_ext.i_data) {
+		iput(inode);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	err = -ENOSPC;
+	block = udf_new_block(dir->i_sb, NULL,
+			      dinfo->i_location.partitionReferenceNum,
+			      start, &err);
+	if (err) {
+		iput(inode);
+		return ERR_PTR(err);
+	}
+
+	lvidiu = udf_sb_lvidiu(sb);
+	if (lvidiu) {
+		iinfo->i_unique = lvid_get_unique_id(sb);
+		inode->i_generation = iinfo->i_unique;
+		mutex_lock(&sbi->s_alloc_mutex);
+		if (S_ISDIR(mode))
+			le32_add_cpu(&lvidiu->numDirs, 1);
+		else
+			le32_add_cpu(&lvidiu->numFiles, 1);
+		udf_updated_lvid(sb);
+		mutex_unlock(&sbi->s_alloc_mutex);
+	}
+
+	inode_init_owner(inode, dir, mode);
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET))
+		inode->i_uid = sbi->s_uid;
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET))
+		inode->i_gid = sbi->s_gid;
+
+	iinfo->i_location.logicalBlockNum = block;
+	iinfo->i_location.partitionReferenceNum =
+				dinfo->i_location.partitionReferenceNum;
+	inode->i_ino = udf_get_lb_pblock(sb, &iinfo->i_location, 0);
+	inode->i_blocks = 0;
+	iinfo->i_lenEAttr = 0;
+	iinfo->i_lenAlloc = 0;
+	iinfo->i_use = 0;
+	iinfo->i_checkpoint = 1;
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB))
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
+	else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
+	else
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+	iinfo->i_crtime = inode->i_mtime;
+	if (unlikely(insert_inode_locked(inode) < 0)) {
+		make_bad_inode(inode);
+		iput(inode);
+		return ERR_PTR(-EIO);
+	}
+	mark_inode_dirty(inode);
+
+	return inode;
+}
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
new file mode 100644
index 0000000..5df554a
--- /dev/null
+++ b/fs/udf/inode.c
@@ -0,0 +1,2304 @@
+/*
+ * inode.c
+ *
+ * PURPOSE
+ *  Inode handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *  This file is distributed under the terms of the GNU General Public
+ *  License (GPL). Copies of the GPL can be obtained from:
+ *    ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *  Each contributing author retains all rights to their own work.
+ *
+ *  (C) 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/module.h>
+#include <linux/pagemap.h>
+#include <linux/writeback.h>
+#include <linux/slab.h>
+#include <linux/crc-itu-t.h>
+#include <linux/mpage.h>
+#include <linux/uio.h>
+#include <linux/bio.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+#define EXTENT_MERGE_SIZE 5
+
+static umode_t udf_convert_permissions(struct fileEntry *);
+static int udf_update_inode(struct inode *, int);
+static int udf_sync_inode(struct inode *inode);
+static int udf_alloc_i_data(struct inode *inode, size_t size);
+static sector_t inode_getblk(struct inode *, sector_t, int *, int *);
+static int8_t udf_insert_aext(struct inode *, struct extent_position,
+			      struct kernel_lb_addr, uint32_t);
+static void udf_split_extents(struct inode *, int *, int, udf_pblk_t,
+			      struct kernel_long_ad *, int *);
+static void udf_prealloc_extents(struct inode *, int, int,
+				 struct kernel_long_ad *, int *);
+static void udf_merge_extents(struct inode *, struct kernel_long_ad *, int *);
+static void udf_update_extents(struct inode *, struct kernel_long_ad *, int,
+			       int, struct extent_position *);
+static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
+
+static void __udf_clear_extent_cache(struct inode *inode)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	if (iinfo->cached_extent.lstart != -1) {
+		brelse(iinfo->cached_extent.epos.bh);
+		iinfo->cached_extent.lstart = -1;
+	}
+}
+
+/* Invalidate extent cache */
+static void udf_clear_extent_cache(struct inode *inode)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	spin_lock(&iinfo->i_extent_cache_lock);
+	__udf_clear_extent_cache(inode);
+	spin_unlock(&iinfo->i_extent_cache_lock);
+}
+
+/* Return contents of extent cache */
+static int udf_read_extent_cache(struct inode *inode, loff_t bcount,
+				 loff_t *lbcount, struct extent_position *pos)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	int ret = 0;
+
+	spin_lock(&iinfo->i_extent_cache_lock);
+	if ((iinfo->cached_extent.lstart <= bcount) &&
+	    (iinfo->cached_extent.lstart != -1)) {
+		/* Cache hit */
+		*lbcount = iinfo->cached_extent.lstart;
+		memcpy(pos, &iinfo->cached_extent.epos,
+		       sizeof(struct extent_position));
+		if (pos->bh)
+			get_bh(pos->bh);
+		ret = 1;
+	}
+	spin_unlock(&iinfo->i_extent_cache_lock);
+	return ret;
+}
+
+/* Add extent to extent cache */
+static void udf_update_extent_cache(struct inode *inode, loff_t estart,
+				    struct extent_position *pos)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	spin_lock(&iinfo->i_extent_cache_lock);
+	/* Invalidate previously cached extent */
+	__udf_clear_extent_cache(inode);
+	if (pos->bh)
+		get_bh(pos->bh);
+	memcpy(&iinfo->cached_extent.epos, pos, sizeof(*pos));
+	iinfo->cached_extent.lstart = estart;
+	switch (iinfo->i_alloc_type) {
+	case ICBTAG_FLAG_AD_SHORT:
+		iinfo->cached_extent.epos.offset -= sizeof(struct short_ad);
+		break;
+	case ICBTAG_FLAG_AD_LONG:
+		iinfo->cached_extent.epos.offset -= sizeof(struct long_ad);
+		break;
+	}
+	spin_unlock(&iinfo->i_extent_cache_lock);
+}
+
+void udf_evict_inode(struct inode *inode)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	int want_delete = 0;
+
+	if (!inode->i_nlink && !is_bad_inode(inode)) {
+		want_delete = 1;
+		udf_setsize(inode, 0);
+		udf_update_inode(inode, IS_SYNC(inode));
+	}
+	truncate_inode_pages_final(&inode->i_data);
+	invalidate_inode_buffers(inode);
+	clear_inode(inode);
+	if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB &&
+	    inode->i_size != iinfo->i_lenExtents) {
+		udf_warn(inode->i_sb, "Inode %lu (mode %o) has inode size %llu different from extent length %llu. Filesystem need not be standards compliant.\n",
+			 inode->i_ino, inode->i_mode,
+			 (unsigned long long)inode->i_size,
+			 (unsigned long long)iinfo->i_lenExtents);
+	}
+	kfree(iinfo->i_ext.i_data);
+	iinfo->i_ext.i_data = NULL;
+	udf_clear_extent_cache(inode);
+	if (want_delete) {
+		udf_free_inode(inode);
+	}
+}
+
+static void udf_write_failed(struct address_space *mapping, loff_t to)
+{
+	struct inode *inode = mapping->host;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	loff_t isize = inode->i_size;
+
+	if (to > isize) {
+		truncate_pagecache(inode, isize);
+		if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+			down_write(&iinfo->i_data_sem);
+			udf_clear_extent_cache(inode);
+			udf_truncate_extents(inode);
+			up_write(&iinfo->i_data_sem);
+		}
+	}
+}
+
+static int udf_writepage(struct page *page, struct writeback_control *wbc)
+{
+	return block_write_full_page(page, udf_get_block, wbc);
+}
+
+static int udf_writepages(struct address_space *mapping,
+			struct writeback_control *wbc)
+{
+	return mpage_writepages(mapping, wbc, udf_get_block);
+}
+
+static int udf_readpage(struct file *file, struct page *page)
+{
+	return mpage_readpage(page, udf_get_block);
+}
+
+static int udf_readpages(struct file *file, struct address_space *mapping,
+			struct list_head *pages, unsigned nr_pages)
+{
+	return mpage_readpages(mapping, pages, nr_pages, udf_get_block);
+}
+
+static int udf_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	int ret;
+
+	ret = block_write_begin(mapping, pos, len, flags, pagep, udf_get_block);
+	if (unlikely(ret))
+		udf_write_failed(mapping, pos + len);
+	return ret;
+}
+
+static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
+{
+	struct file *file = iocb->ki_filp;
+	struct address_space *mapping = file->f_mapping;
+	struct inode *inode = mapping->host;
+	size_t count = iov_iter_count(iter);
+	ssize_t ret;
+
+	ret = blockdev_direct_IO(iocb, inode, iter, udf_get_block);
+	if (unlikely(ret < 0 && iov_iter_rw(iter) == WRITE))
+		udf_write_failed(mapping, iocb->ki_pos + count);
+	return ret;
+}
+
+static sector_t udf_bmap(struct address_space *mapping, sector_t block)
+{
+	return generic_block_bmap(mapping, block, udf_get_block);
+}
+
+const struct address_space_operations udf_aops = {
+	.readpage	= udf_readpage,
+	.readpages	= udf_readpages,
+	.writepage	= udf_writepage,
+	.writepages	= udf_writepages,
+	.write_begin	= udf_write_begin,
+	.write_end	= generic_write_end,
+	.direct_IO	= udf_direct_IO,
+	.bmap		= udf_bmap,
+};
+
+/*
+ * Expand file stored in ICB to a normal one-block-file
+ *
+ * This function requires i_data_sem for writing and releases it.
+ * This function requires i_mutex held
+ */
+int udf_expand_file_adinicb(struct inode *inode)
+{
+	struct page *page;
+	char *kaddr;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	int err;
+	struct writeback_control udf_wbc = {
+		.sync_mode = WB_SYNC_NONE,
+		.nr_to_write = 1,
+	};
+
+	WARN_ON_ONCE(!inode_is_locked(inode));
+	if (!iinfo->i_lenAlloc) {
+		if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+			iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
+		else
+			iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
+		/* from now on we have normal address_space methods */
+		inode->i_data.a_ops = &udf_aops;
+		up_write(&iinfo->i_data_sem);
+		mark_inode_dirty(inode);
+		return 0;
+	}
+	/*
+	 * Release i_data_sem so that we can lock a page - page lock ranks
+	 * above i_data_sem. i_mutex still protects us against file changes.
+	 */
+	up_write(&iinfo->i_data_sem);
+
+	page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
+	if (!page)
+		return -ENOMEM;
+
+	if (!PageUptodate(page)) {
+		kaddr = kmap_atomic(page);
+		memset(kaddr + iinfo->i_lenAlloc, 0x00,
+		       PAGE_SIZE - iinfo->i_lenAlloc);
+		memcpy(kaddr, iinfo->i_ext.i_data + iinfo->i_lenEAttr,
+			iinfo->i_lenAlloc);
+		flush_dcache_page(page);
+		SetPageUptodate(page);
+		kunmap_atomic(kaddr);
+	}
+	down_write(&iinfo->i_data_sem);
+	memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr, 0x00,
+	       iinfo->i_lenAlloc);
+	iinfo->i_lenAlloc = 0;
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
+	else
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
+	/* from now on we have normal address_space methods */
+	inode->i_data.a_ops = &udf_aops;
+	up_write(&iinfo->i_data_sem);
+	err = inode->i_data.a_ops->writepage(page, &udf_wbc);
+	if (err) {
+		/* Restore everything back so that we don't lose data... */
+		lock_page(page);
+		down_write(&iinfo->i_data_sem);
+		kaddr = kmap_atomic(page);
+		memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr, kaddr,
+		       inode->i_size);
+		kunmap_atomic(kaddr);
+		unlock_page(page);
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
+		inode->i_data.a_ops = &udf_adinicb_aops;
+		up_write(&iinfo->i_data_sem);
+	}
+	put_page(page);
+	mark_inode_dirty(inode);
+
+	return err;
+}
+
+struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
+					    udf_pblk_t *block, int *err)
+{
+	udf_pblk_t newblock;
+	struct buffer_head *dbh = NULL;
+	struct kernel_lb_addr eloc;
+	uint8_t alloctype;
+	struct extent_position epos;
+
+	struct udf_fileident_bh sfibh, dfibh;
+	loff_t f_pos = udf_ext0_offset(inode);
+	int size = udf_ext0_offset(inode) + inode->i_size;
+	struct fileIdentDesc cfi, *sfi, *dfi;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	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) {
+		iinfo->i_alloc_type = alloctype;
+		mark_inode_dirty(inode);
+		return NULL;
+	}
+
+	/* alloc block, and copy data to it */
+	*block = udf_new_block(inode->i_sb, inode,
+			       iinfo->i_location.partitionReferenceNum,
+			       iinfo->i_location.logicalBlockNum, err);
+	if (!(*block))
+		return NULL;
+	newblock = udf_get_pblock(inode->i_sb, *block,
+				  iinfo->i_location.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);
+	sfibh.sbh = sfibh.ebh = NULL;
+	dfibh.soffset = dfibh.eoffset = 0;
+	dfibh.sbh = dfibh.ebh = dbh;
+	while (f_pos < size) {
+		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
+		sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL,
+					 NULL, NULL, NULL);
+		if (!sfi) {
+			brelse(dbh);
+			return NULL;
+		}
+		iinfo->i_alloc_type = 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))) {
+			iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
+			brelse(dbh);
+			return NULL;
+		}
+	}
+	mark_buffer_dirty_inode(dbh, inode);
+
+	memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr, 0,
+		iinfo->i_lenAlloc);
+	iinfo->i_lenAlloc = 0;
+	eloc.logicalBlockNum = *block;
+	eloc.partitionReferenceNum =
+				iinfo->i_location.partitionReferenceNum;
+	iinfo->i_lenExtents = inode->i_size;
+	epos.bh = NULL;
+	epos.block = iinfo->i_location;
+	epos.offset = udf_file_entry_alloc_offset(inode);
+	udf_add_aext(inode, &epos, &eloc, inode->i_size, 0);
+	/* UniqueID stuff */
+
+	brelse(epos.bh);
+	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;
+	sector_t phys = 0;
+	struct udf_inode_info *iinfo;
+
+	if (!create) {
+		phys = udf_block_map(inode, block);
+		if (phys)
+			map_bh(bh_result, inode->i_sb, phys);
+		return 0;
+	}
+
+	err = -EIO;
+	new = 0;
+	iinfo = UDF_I(inode);
+
+	down_write(&iinfo->i_data_sem);
+	if (block == iinfo->i_next_alloc_block + 1) {
+		iinfo->i_next_alloc_block++;
+		iinfo->i_next_alloc_goal++;
+	}
+
+	udf_clear_extent_cache(inode);
+	phys = inode_getblk(inode, block, &err, &new);
+	if (!phys)
+		goto abort;
+
+	if (new)
+		set_buffer_new(bh_result);
+	map_bh(bh_result, inode->i_sb, phys);
+
+abort:
+	up_write(&iinfo->i_data_sem);
+	return err;
+}
+
+static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block,
+				      int create, int *err)
+{
+	struct buffer_head *bh;
+	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)) {
+		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;
+}
+
+/* Extend the file by 'blocks' blocks, return the number of extents added */
+static int udf_do_extend_file(struct inode *inode,
+			      struct extent_position *last_pos,
+			      struct kernel_long_ad *last_ext,
+			      sector_t blocks)
+{
+	sector_t add;
+	int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+	struct super_block *sb = inode->i_sb;
+	struct kernel_lb_addr prealloc_loc = {};
+	uint32_t prealloc_len = 0;
+	struct udf_inode_info *iinfo;
+	int err;
+
+	/* The previous extent is fake and we should not extend by anything
+	 * - there's nothing to do... */
+	if (!blocks && fake)
+		return 0;
+
+	iinfo = UDF_I(inode);
+	/* Round the last extent up to a multiple of block size */
+	if (last_ext->extLength & (sb->s_blocksize - 1)) {
+		last_ext->extLength =
+			(last_ext->extLength & UDF_EXTENT_FLAG_MASK) |
+			(((last_ext->extLength & UDF_EXTENT_LENGTH_MASK) +
+			  sb->s_blocksize - 1) & ~(sb->s_blocksize - 1));
+		iinfo->i_lenExtents =
+			(iinfo->i_lenExtents + sb->s_blocksize - 1) &
+			~(sb->s_blocksize - 1);
+	}
+
+	/* Last extent are just preallocated blocks? */
+	if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
+						EXT_NOT_RECORDED_ALLOCATED) {
+		/* Save the extent so that we can reattach it to the end */
+		prealloc_loc = last_ext->extLocation;
+		prealloc_len = last_ext->extLength;
+		/* Mark the extent as a hole */
+		last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+			(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+		last_ext->extLocation.logicalBlockNum = 0;
+		last_ext->extLocation.partitionReferenceNum = 0;
+	}
+
+	/* Can we merge with the previous extent? */
+	if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
+					EXT_NOT_RECORDED_NOT_ALLOCATED) {
+		add = ((1 << 30) - sb->s_blocksize -
+			(last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >>
+			sb->s_blocksize_bits;
+		if (add > blocks)
+			add = blocks;
+		blocks -= add;
+		last_ext->extLength += add << sb->s_blocksize_bits;
+	}
+
+	if (fake) {
+		udf_add_aext(inode, last_pos, &last_ext->extLocation,
+			     last_ext->extLength, 1);
+		count++;
+	} else {
+		struct kernel_lb_addr tmploc;
+		uint32_t tmplen;
+
+		udf_write_aext(inode, last_pos, &last_ext->extLocation,
+				last_ext->extLength, 1);
+		/*
+		 * We've rewritten the last extent but there may be empty
+		 * indirect extent after it - enter it.
+		 */
+		udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
+	}
+
+	/* Managed to do everything necessary? */
+	if (!blocks)
+		goto out;
+
+	/* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
+	last_ext->extLocation.logicalBlockNum = 0;
+	last_ext->extLocation.partitionReferenceNum = 0;
+	add = (1 << (30-sb->s_blocksize_bits)) - 1;
+	last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+				(add << sb->s_blocksize_bits);
+
+	/* Create enough extents to cover the whole hole */
+	while (blocks > add) {
+		blocks -= add;
+		err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
+				   last_ext->extLength, 1);
+		if (err)
+			return err;
+		count++;
+	}
+	if (blocks) {
+		last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+			(blocks << sb->s_blocksize_bits);
+		err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
+				   last_ext->extLength, 1);
+		if (err)
+			return err;
+		count++;
+	}
+
+out:
+	/* Do we have some preallocated blocks saved? */
+	if (prealloc_len) {
+		err = udf_add_aext(inode, last_pos, &prealloc_loc,
+				   prealloc_len, 1);
+		if (err)
+			return err;
+		last_ext->extLocation = prealloc_loc;
+		last_ext->extLength = prealloc_len;
+		count++;
+	}
+
+	/* last_pos should point to the last written extent... */
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		last_pos->offset -= sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		last_pos->offset -= sizeof(struct long_ad);
+	else
+		return -EIO;
+
+	return count;
+}
+
+static int udf_extend_file(struct inode *inode, loff_t newsize)
+{
+
+	struct extent_position epos;
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	int8_t etype;
+	struct super_block *sb = inode->i_sb;
+	sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
+	int adsize;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	struct kernel_long_ad extent;
+	int err;
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		BUG();
+
+	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
+
+	/* File has extent covering the new size (could happen when extending
+	 * inside a block)? */
+	if (etype != -1)
+		return 0;
+	if (newsize & (sb->s_blocksize - 1))
+		offset++;
+	/* Extended file just to the boundary of the last file block? */
+	if (offset == 0)
+		return 0;
+
+	/* Truncate is extending the file by 'offset' blocks */
+	if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
+	    (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
+		/* File has no extents at all or has empty last
+		 * indirect extent! Create a fake extent... */
+		extent.extLocation.logicalBlockNum = 0;
+		extent.extLocation.partitionReferenceNum = 0;
+		extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
+	} else {
+		epos.offset -= adsize;
+		etype = udf_next_aext(inode, &epos, &extent.extLocation,
+				      &extent.extLength, 0);
+		extent.extLength |= etype << 30;
+	}
+	err = udf_do_extend_file(inode, &epos, &extent, offset);
+	if (err < 0)
+		goto out;
+	err = 0;
+	iinfo->i_lenExtents = newsize;
+out:
+	brelse(epos.bh);
+	return err;
+}
+
+static sector_t inode_getblk(struct inode *inode, sector_t block,
+			     int *err, int *new)
+{
+	struct kernel_long_ad laarr[EXTENT_MERGE_SIZE];
+	struct extent_position prev_epos, cur_epos, next_epos;
+	int count = 0, startnum = 0, endnum = 0;
+	uint32_t elen = 0, tmpelen;
+	struct kernel_lb_addr eloc, tmpeloc;
+	int c = 1;
+	loff_t lbcount = 0, b_off = 0;
+	udf_pblk_t newblocknum, newblock;
+	sector_t offset = 0;
+	int8_t etype;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	udf_pblk_t goal = 0, pgoal = iinfo->i_location.logicalBlockNum;
+	int lastblock = 0;
+	bool isBeyondEOF;
+
+	*err = 0;
+	*new = 0;
+	prev_epos.offset = udf_file_entry_alloc_offset(inode);
+	prev_epos.block = iinfo->i_location;
+	prev_epos.bh = NULL;
+	cur_epos = next_epos = prev_epos;
+	b_off = (loff_t)block << inode->i_sb->s_blocksize_bits;
+
+	/* 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 (prev_epos.bh != cur_epos.bh) {
+			brelse(prev_epos.bh);
+			get_bh(cur_epos.bh);
+			prev_epos.bh = cur_epos.bh;
+		}
+		if (cur_epos.bh != next_epos.bh) {
+			brelse(cur_epos.bh);
+			get_bh(next_epos.bh);
+			cur_epos.bh = next_epos.bh;
+		}
+
+		lbcount += elen;
+
+		prev_epos.block = cur_epos.block;
+		cur_epos.block = next_epos.block;
+
+		prev_epos.offset = cur_epos.offset;
+		cur_epos.offset = next_epos.offset;
+
+		etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 1);
+		if (etype == -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;
+	/*
+	 * Move prev_epos and cur_epos into indirect extent if we are at
+	 * the pointer to it
+	 */
+	udf_next_aext(inode, &prev_epos, &tmpeloc, &tmpelen, 0);
+	udf_next_aext(inode, &cur_epos, &tmpeloc, &tmpelen, 0);
+
+	/* 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));
+			udf_write_aext(inode, &cur_epos, &eloc, elen, 1);
+		}
+		newblock = udf_get_lb_pblock(inode->i_sb, &eloc, offset);
+		goto out_free;
+	}
+
+	/* Are we beyond EOF? */
+	if (etype == -1) {
+		int ret;
+		isBeyondEOF = true;
+		if (count) {
+			if (c)
+				laarr[0] = laarr[1];
+			startnum = 1;
+		} else {
+			/* Create a fake extent when there's not one */
+			memset(&laarr[0].extLocation, 0x00,
+				sizeof(struct kernel_lb_addr));
+			laarr[0].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
+			/* Will udf_do_extend_file() create real extent from
+			   a fake one? */
+			startnum = (offset > 0);
+		}
+		/* Create extents for the hole between EOF and offset */
+		ret = udf_do_extend_file(inode, &prev_epos, laarr, offset);
+		if (ret < 0) {
+			*err = ret;
+			newblock = 0;
+			goto out_free;
+		}
+		c = 0;
+		offset = 0;
+		count += ret;
+		/* We are not covered by a preallocated extent? */
+		if ((laarr[0].extLength & UDF_EXTENT_FLAG_MASK) !=
+						EXT_NOT_RECORDED_ALLOCATED) {
+			/* Is there any real extent? - otherwise we overwrite
+			 * the fake one... */
+			if (count)
+				c = !c;
+			laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+				inode->i_sb->s_blocksize;
+			memset(&laarr[c].extLocation, 0x00,
+				sizeof(struct kernel_lb_addr));
+			count++;
+		}
+		endnum = c + 1;
+		lastblock = 1;
+	} else {
+		isBeyondEOF = false;
+		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 an extent,
+		   read the next extent */
+		etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 0);
+		if (etype != -1) {
+			laarr[c + 1].extLength = (etype << 30) | elen;
+			laarr[c + 1].extLocation = eloc;
+			count++;
+			startnum++;
+			endnum++;
+		} else
+			lastblock = 1;
+	}
+
+	/* 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 (iinfo->i_next_alloc_block == block)
+			goal = iinfo->i_next_alloc_goal;
+
+		if (!goal) {
+			if (!(goal = pgoal)) /* XXX: what was intended here? */
+				goal = iinfo->i_location.logicalBlockNum + 1;
+		}
+
+		newblocknum = udf_new_block(inode->i_sb, inode,
+				iinfo->i_location.partitionReferenceNum,
+				goal, err);
+		if (!newblocknum) {
+			*err = -ENOSPC;
+			newblock = 0;
+			goto out_free;
+		}
+		if (isBeyondEOF)
+			iinfo->i_lenExtents += 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);
+
+	/* We preallocate blocks only for regular files. It also makes sense
+	 * for directories but there's a problem when to drop the
+	 * preallocation. We might use some delayed work for that but I feel
+	 * it's overengineering for a filesystem like UDF. */
+	if (S_ISREG(inode->i_mode))
+		udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
+
+	/* 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, &prev_epos);
+
+	newblock = udf_get_pblock(inode->i_sb, newblocknum,
+				iinfo->i_location.partitionReferenceNum, 0);
+	if (!newblock) {
+		*err = -EIO;
+		goto out_free;
+	}
+	*new = 1;
+	iinfo->i_next_alloc_block = block;
+	iinfo->i_next_alloc_goal = newblocknum;
+	inode->i_ctime = current_time(inode);
+
+	if (IS_SYNC(inode))
+		udf_sync_inode(inode);
+	else
+		mark_inode_dirty(inode);
+out_free:
+	brelse(prev_epos.bh);
+	brelse(cur_epos.bh);
+	brelse(next_epos.bh);
+	return newblock;
+}
+
+static void udf_split_extents(struct inode *inode, int *c, int offset,
+			       udf_pblk_t newblocknum,
+			       struct kernel_long_ad *laarr, int *endnum)
+{
+	unsigned long blocksize = inode->i_sb->s_blocksize;
+	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
+
+	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) +
+			    blocksize - 1) >> 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 << blocksize_bits);
+				laarr[curr].extLocation.logicalBlockNum = 0;
+				laarr[curr].extLocation.
+						partitionReferenceNum = 0;
+			} else
+				laarr[curr].extLength = (etype << 30) |
+					(offset << blocksize_bits);
+			curr++;
+			(*c)++;
+			(*endnum)++;
+		}
+
+		laarr[curr].extLocation.logicalBlockNum = newblocknum;
+		if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+			laarr[curr].extLocation.partitionReferenceNum =
+				UDF_I(inode)->i_location.partitionReferenceNum;
+		laarr[curr].extLength = EXT_RECORDED_ALLOCATED |
+			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)) << blocksize_bits);
+			curr++;
+			(*endnum)++;
+		}
+	}
+}
+
+static void udf_prealloc_extents(struct inode *inode, int c, int lastblock,
+				 struct kernel_long_ad *laarr,
+				 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(struct 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(struct long_ad) *
+							(*endnum - (i + 1)));
+					i--;
+					(*endnum)--;
+				}
+			}
+			UDF_I(inode)->i_lenExtents +=
+				numalloc << inode->i_sb->s_blocksize_bits;
+		}
+	}
+}
+
+static void udf_merge_extents(struct inode *inode, struct kernel_long_ad *laarr,
+			      int *endnum)
+{
+	int i;
+	unsigned long blocksize = inode->i_sb->s_blocksize;
+	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
+
+	for (i = 0; i < (*endnum - 1); i++) {
+		struct kernel_long_ad *li /*l[i]*/ = &laarr[i];
+		struct kernel_long_ad *lip1 /*l[i plus 1]*/ = &laarr[i + 1];
+
+		if (((li->extLength >> 30) == (lip1->extLength >> 30)) &&
+			(((li->extLength >> 30) ==
+				(EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) ||
+			((lip1->extLocation.logicalBlockNum -
+			  li->extLocation.logicalBlockNum) ==
+			(((li->extLength & UDF_EXTENT_LENGTH_MASK) +
+			blocksize - 1) >> blocksize_bits)))) {
+
+			if (((li->extLength & UDF_EXTENT_LENGTH_MASK) +
+				(lip1->extLength & UDF_EXTENT_LENGTH_MASK) +
+				blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) {
+				lip1->extLength = (lip1->extLength -
+						  (li->extLength &
+						   UDF_EXTENT_LENGTH_MASK) +
+						   UDF_EXTENT_LENGTH_MASK) &
+							~(blocksize - 1);
+				li->extLength = (li->extLength &
+						 UDF_EXTENT_FLAG_MASK) +
+						(UDF_EXTENT_LENGTH_MASK + 1) -
+						blocksize;
+				lip1->extLocation.logicalBlockNum =
+					li->extLocation.logicalBlockNum +
+					((li->extLength &
+						UDF_EXTENT_LENGTH_MASK) >>
+						blocksize_bits);
+			} else {
+				li->extLength = lip1->extLength +
+					(((li->extLength &
+						UDF_EXTENT_LENGTH_MASK) +
+					 blocksize - 1) & ~(blocksize - 1));
+				if (*endnum > (i + 2))
+					memmove(&laarr[i + 1], &laarr[i + 2],
+						sizeof(struct long_ad) *
+						(*endnum - (i + 2)));
+				i--;
+				(*endnum)--;
+			}
+		} else if (((li->extLength >> 30) ==
+				(EXT_NOT_RECORDED_ALLOCATED >> 30)) &&
+			   ((lip1->extLength >> 30) ==
+				(EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) {
+			udf_free_blocks(inode->i_sb, inode, &li->extLocation, 0,
+					((li->extLength &
+					  UDF_EXTENT_LENGTH_MASK) +
+					 blocksize - 1) >> blocksize_bits);
+			li->extLocation.logicalBlockNum = 0;
+			li->extLocation.partitionReferenceNum = 0;
+
+			if (((li->extLength & UDF_EXTENT_LENGTH_MASK) +
+			     (lip1->extLength & UDF_EXTENT_LENGTH_MASK) +
+			     blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) {
+				lip1->extLength = (lip1->extLength -
+						   (li->extLength &
+						   UDF_EXTENT_LENGTH_MASK) +
+						   UDF_EXTENT_LENGTH_MASK) &
+						   ~(blocksize - 1);
+				li->extLength = (li->extLength &
+						 UDF_EXTENT_FLAG_MASK) +
+						(UDF_EXTENT_LENGTH_MASK + 1) -
+						blocksize;
+			} else {
+				li->extLength = lip1->extLength +
+					(((li->extLength &
+						UDF_EXTENT_LENGTH_MASK) +
+					  blocksize - 1) & ~(blocksize - 1));
+				if (*endnum > (i + 2))
+					memmove(&laarr[i + 1], &laarr[i + 2],
+						sizeof(struct long_ad) *
+						(*endnum - (i + 2)));
+				i--;
+				(*endnum)--;
+			}
+		} else if ((li->extLength >> 30) ==
+					(EXT_NOT_RECORDED_ALLOCATED >> 30)) {
+			udf_free_blocks(inode->i_sb, inode,
+					&li->extLocation, 0,
+					((li->extLength &
+						UDF_EXTENT_LENGTH_MASK) +
+					 blocksize - 1) >> blocksize_bits);
+			li->extLocation.logicalBlockNum = 0;
+			li->extLocation.partitionReferenceNum = 0;
+			li->extLength = (li->extLength &
+						UDF_EXTENT_LENGTH_MASK) |
+						EXT_NOT_RECORDED_NOT_ALLOCATED;
+		}
+	}
+}
+
+static void udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr,
+			       int startnum, int endnum,
+			       struct extent_position *epos)
+{
+	int start = 0, i;
+	struct kernel_lb_addr tmploc;
+	uint32_t tmplen;
+
+	if (startnum > endnum) {
+		for (i = 0; i < (startnum - endnum); i++)
+			udf_delete_aext(inode, *epos);
+	} else if (startnum < endnum) {
+		for (i = 0; i < (endnum - startnum); i++) {
+			udf_insert_aext(inode, *epos, laarr[i].extLocation,
+					laarr[i].extLength);
+			udf_next_aext(inode, epos, &laarr[i].extLocation,
+				      &laarr[i].extLength, 1);
+			start++;
+		}
+	}
+
+	for (i = start; i < endnum; i++) {
+		udf_next_aext(inode, epos, &tmploc, &tmplen, 0);
+		udf_write_aext(inode, epos, &laarr[i].extLocation,
+			       laarr[i].extLength, 1);
+	}
+}
+
+struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t 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(REQ_OP_READ, 0, 1, &bh);
+
+	wait_on_buffer(bh);
+	if (buffer_uptodate(bh))
+		return bh;
+
+	brelse(bh);
+	*err = -EIO;
+	return NULL;
+}
+
+int udf_setsize(struct inode *inode, loff_t newsize)
+{
+	int err;
+	struct udf_inode_info *iinfo;
+	unsigned int bsize = i_blocksize(inode);
+
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	      S_ISLNK(inode->i_mode)))
+		return -EINVAL;
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return -EPERM;
+
+	iinfo = UDF_I(inode);
+	if (newsize > inode->i_size) {
+		down_write(&iinfo->i_data_sem);
+		if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+			if (bsize <
+			    (udf_file_entry_alloc_offset(inode) + newsize)) {
+				err = udf_expand_file_adinicb(inode);
+				if (err)
+					return err;
+				down_write(&iinfo->i_data_sem);
+			} else {
+				iinfo->i_lenAlloc = newsize;
+				goto set_size;
+			}
+		}
+		err = udf_extend_file(inode, newsize);
+		if (err) {
+			up_write(&iinfo->i_data_sem);
+			return err;
+		}
+set_size:
+		up_write(&iinfo->i_data_sem);
+		truncate_setsize(inode, newsize);
+	} else {
+		if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+			down_write(&iinfo->i_data_sem);
+			udf_clear_extent_cache(inode);
+			memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr + newsize,
+			       0x00, bsize - newsize -
+			       udf_file_entry_alloc_offset(inode));
+			iinfo->i_lenAlloc = newsize;
+			truncate_setsize(inode, newsize);
+			up_write(&iinfo->i_data_sem);
+			goto update_time;
+		}
+		err = block_truncate_page(inode->i_mapping, newsize,
+					  udf_get_block);
+		if (err)
+			return err;
+		truncate_setsize(inode, newsize);
+		down_write(&iinfo->i_data_sem);
+		udf_clear_extent_cache(inode);
+		udf_truncate_extents(inode);
+		up_write(&iinfo->i_data_sem);
+	}
+update_time:
+	inode->i_mtime = inode->i_ctime = current_time(inode);
+	if (IS_SYNC(inode))
+		udf_sync_inode(inode);
+	else
+		mark_inode_dirty(inode);
+	return 0;
+}
+
+/*
+ * Maximum length of linked list formed by ICB hierarchy. The chosen number is
+ * arbitrary - just that we hopefully don't limit any real use of rewritten
+ * inode on write-once media but avoid looping for too long on corrupted media.
+ */
+#define UDF_MAX_ICB_NESTING 1024
+
+static int udf_read_inode(struct inode *inode, bool hidden_inode)
+{
+	struct buffer_head *bh = NULL;
+	struct fileEntry *fe;
+	struct extendedFileEntry *efe;
+	uint16_t ident;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
+	struct kernel_lb_addr *iloc = &iinfo->i_location;
+	unsigned int link_count;
+	unsigned int indirections = 0;
+	int bs = inode->i_sb->s_blocksize;
+	int ret = -EIO;
+	uint32_t uid, gid;
+
+reread:
+	if (iloc->partitionReferenceNum >= sbi->s_partitions) {
+		udf_debug("partition reference: %u > logical volume partitions: %u\n",
+			  iloc->partitionReferenceNum, sbi->s_partitions);
+		return -EIO;
+	}
+
+	if (iloc->logicalBlockNum >=
+	    sbi->s_partmaps[iloc->partitionReferenceNum].s_partition_len) {
+		udf_debug("block=%u, partition=%u out of range\n",
+			  iloc->logicalBlockNum, iloc->partitionReferenceNum);
+		return -EIO;
+	}
+
+	/*
+	 * 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;
+	 */
+	bh = udf_read_ptagged(inode->i_sb, iloc, 0, &ident);
+	if (!bh) {
+		udf_err(inode->i_sb, "(ino %lu) failed !bh\n", inode->i_ino);
+		return -EIO;
+	}
+
+	if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE &&
+	    ident != TAG_IDENT_USE) {
+		udf_err(inode->i_sb, "(ino %lu) failed ident=%u\n",
+			inode->i_ino, ident);
+		goto out;
+	}
+
+	fe = (struct fileEntry *)bh->b_data;
+	efe = (struct extendedFileEntry *)bh->b_data;
+
+	if (fe->icbTag.strategyType == cpu_to_le16(4096)) {
+		struct buffer_head *ibh;
+
+		ibh = udf_read_ptagged(inode->i_sb, iloc, 1, &ident);
+		if (ident == TAG_IDENT_IE && ibh) {
+			struct kernel_lb_addr loc;
+			struct indirectEntry *ie;
+
+			ie = (struct indirectEntry *)ibh->b_data;
+			loc = lelb_to_cpu(ie->indirectICB.extLocation);
+
+			if (ie->indirectICB.extLength) {
+				brelse(ibh);
+				memcpy(&iinfo->i_location, &loc,
+				       sizeof(struct kernel_lb_addr));
+				if (++indirections > UDF_MAX_ICB_NESTING) {
+					udf_err(inode->i_sb,
+						"too many ICBs in ICB hierarchy"
+						" (max %d supported)\n",
+						UDF_MAX_ICB_NESTING);
+					goto out;
+				}
+				brelse(bh);
+				goto reread;
+			}
+		}
+		brelse(ibh);
+	} else if (fe->icbTag.strategyType != cpu_to_le16(4)) {
+		udf_err(inode->i_sb, "unsupported strategy type: %u\n",
+			le16_to_cpu(fe->icbTag.strategyType));
+		goto out;
+	}
+	if (fe->icbTag.strategyType == cpu_to_le16(4))
+		iinfo->i_strat4096 = 0;
+	else /* if (fe->icbTag.strategyType == cpu_to_le16(4096)) */
+		iinfo->i_strat4096 = 1;
+
+	iinfo->i_alloc_type = le16_to_cpu(fe->icbTag.flags) &
+							ICBTAG_FLAG_AD_MASK;
+	iinfo->i_unique = 0;
+	iinfo->i_lenEAttr = 0;
+	iinfo->i_lenExtents = 0;
+	iinfo->i_lenAlloc = 0;
+	iinfo->i_next_alloc_block = 0;
+	iinfo->i_next_alloc_goal = 0;
+	if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) {
+		iinfo->i_efe = 1;
+		iinfo->i_use = 0;
+		ret = udf_alloc_i_data(inode, bs -
+					sizeof(struct extendedFileEntry));
+		if (ret)
+			goto out;
+		memcpy(iinfo->i_ext.i_data,
+		       bh->b_data + sizeof(struct extendedFileEntry),
+		       bs - sizeof(struct extendedFileEntry));
+	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) {
+		iinfo->i_efe = 0;
+		iinfo->i_use = 0;
+		ret = udf_alloc_i_data(inode, bs - sizeof(struct fileEntry));
+		if (ret)
+			goto out;
+		memcpy(iinfo->i_ext.i_data,
+		       bh->b_data + sizeof(struct fileEntry),
+		       bs - sizeof(struct fileEntry));
+	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) {
+		iinfo->i_efe = 0;
+		iinfo->i_use = 1;
+		iinfo->i_lenAlloc = le32_to_cpu(
+				((struct unallocSpaceEntry *)bh->b_data)->
+				 lengthAllocDescs);
+		ret = udf_alloc_i_data(inode, bs -
+					sizeof(struct unallocSpaceEntry));
+		if (ret)
+			goto out;
+		memcpy(iinfo->i_ext.i_data,
+		       bh->b_data + sizeof(struct unallocSpaceEntry),
+		       bs - sizeof(struct unallocSpaceEntry));
+		return 0;
+	}
+
+	ret = -EIO;
+	read_lock(&sbi->s_cred_lock);
+	uid = le32_to_cpu(fe->uid);
+	if (uid == UDF_INVALID_ID ||
+	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_SET))
+		inode->i_uid = sbi->s_uid;
+	else
+		i_uid_write(inode, uid);
+
+	gid = le32_to_cpu(fe->gid);
+	if (gid == UDF_INVALID_ID ||
+	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_SET))
+		inode->i_gid = sbi->s_gid;
+	else
+		i_gid_write(inode, gid);
+
+	if (fe->icbTag.fileType != ICBTAG_FILE_TYPE_DIRECTORY &&
+			sbi->s_fmode != UDF_INVALID_MODE)
+		inode->i_mode = sbi->s_fmode;
+	else if (fe->icbTag.fileType == ICBTAG_FILE_TYPE_DIRECTORY &&
+			sbi->s_dmode != UDF_INVALID_MODE)
+		inode->i_mode = sbi->s_dmode;
+	else
+		inode->i_mode = udf_convert_permissions(fe);
+	inode->i_mode &= ~sbi->s_umask;
+	read_unlock(&sbi->s_cred_lock);
+
+	link_count = le16_to_cpu(fe->fileLinkCount);
+	if (!link_count) {
+		if (!hidden_inode) {
+			ret = -ESTALE;
+			goto out;
+		}
+		link_count = 1;
+	}
+	set_nlink(inode, link_count);
+
+	inode->i_size = le64_to_cpu(fe->informationLength);
+	iinfo->i_lenExtents = inode->i_size;
+
+	if (iinfo->i_efe == 0) {
+		inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
+			(inode->i_sb->s_blocksize_bits - 9);
+
+		udf_disk_stamp_to_time(&inode->i_atime, fe->accessTime);
+		udf_disk_stamp_to_time(&inode->i_mtime, fe->modificationTime);
+		udf_disk_stamp_to_time(&inode->i_ctime, fe->attrTime);
+
+		iinfo->i_unique = le64_to_cpu(fe->uniqueID);
+		iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr);
+		iinfo->i_lenAlloc = le32_to_cpu(fe->lengthAllocDescs);
+		iinfo->i_checkpoint = le32_to_cpu(fe->checkpoint);
+	} else {
+		inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) <<
+		    (inode->i_sb->s_blocksize_bits - 9);
+
+		udf_disk_stamp_to_time(&inode->i_atime, efe->accessTime);
+		udf_disk_stamp_to_time(&inode->i_mtime, efe->modificationTime);
+		udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime);
+		udf_disk_stamp_to_time(&inode->i_ctime, efe->attrTime);
+
+		iinfo->i_unique = le64_to_cpu(efe->uniqueID);
+		iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr);
+		iinfo->i_lenAlloc = le32_to_cpu(efe->lengthAllocDescs);
+		iinfo->i_checkpoint = le32_to_cpu(efe->checkpoint);
+	}
+	inode->i_generation = iinfo->i_unique;
+
+	/*
+	 * Sanity check length of allocation descriptors and extended attrs to
+	 * avoid integer overflows
+	 */
+	if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs)
+		goto out;
+	/* Now do exact checks */
+	if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs)
+		goto out;
+	/* Sanity checks for files in ICB so that we don't get confused later */
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+		/*
+		 * For file in ICB data is stored in allocation descriptor
+		 * so sizes should match
+		 */
+		if (iinfo->i_lenAlloc != inode->i_size)
+			goto out;
+		/* File in ICB has to fit in there... */
+		if (inode->i_size > bs - udf_file_entry_alloc_offset(inode))
+			goto out;
+	}
+
+	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;
+		inc_nlink(inode);
+		break;
+	case ICBTAG_FILE_TYPE_REALTIME:
+	case ICBTAG_FILE_TYPE_REGULAR:
+	case ICBTAG_FILE_TYPE_UNDEF:
+	case ICBTAG_FILE_TYPE_VAT20:
+		if (iinfo->i_alloc_type == 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 = &udf_symlink_inode_operations;
+		inode_nohighmem(inode);
+		inode->i_mode = S_IFLNK | 0777;
+		break;
+	case ICBTAG_FILE_TYPE_MAIN:
+		udf_debug("METADATA FILE-----\n");
+		break;
+	case ICBTAG_FILE_TYPE_MIRROR:
+		udf_debug("METADATA MIRROR FILE-----\n");
+		break;
+	case ICBTAG_FILE_TYPE_BITMAP:
+		udf_debug("METADATA BITMAP FILE-----\n");
+		break;
+	default:
+		udf_err(inode->i_sb, "(ino %lu) failed unknown file type=%u\n",
+			inode->i_ino, fe->icbTag.fileType);
+		goto out;
+	}
+	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
+			goto out;
+	}
+	ret = 0;
+out:
+	brelse(bh);
+	return ret;
+}
+
+static int udf_alloc_i_data(struct inode *inode, size_t size)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	iinfo->i_ext.i_data = kmalloc(size, GFP_KERNEL);
+	if (!iinfo->i_ext.i_data)
+		return -ENOMEM;
+	return 0;
+}
+
+static umode_t udf_convert_permissions(struct fileEntry *fe)
+{
+	umode_t mode;
+	uint32_t permissions;
+	uint32_t flags;
+
+	permissions = le32_to_cpu(fe->permissions);
+	flags = le16_to_cpu(fe->icbTag.flags);
+
+	mode =	((permissions) & 0007) |
+		((permissions >> 2) & 0070) |
+		((permissions >> 4) & 0700) |
+		((flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) |
+		((flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) |
+		((flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0);
+
+	return mode;
+}
+
+int udf_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+	return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
+}
+
+static int udf_sync_inode(struct inode *inode)
+{
+	return udf_update_inode(inode, 1);
+}
+
+static void udf_adjust_time(struct udf_inode_info *iinfo, struct timespec64 time)
+{
+	if (iinfo->i_crtime.tv_sec > time.tv_sec ||
+	    (iinfo->i_crtime.tv_sec == time.tv_sec &&
+	     iinfo->i_crtime.tv_nsec > time.tv_nsec))
+		iinfo->i_crtime = time;
+}
+
+static int udf_update_inode(struct inode *inode, int do_sync)
+{
+	struct buffer_head *bh = NULL;
+	struct fileEntry *fe;
+	struct extendedFileEntry *efe;
+	uint64_t lb_recorded;
+	uint32_t udfperms;
+	uint16_t icbflags;
+	uint16_t crclen;
+	int err = 0;
+	struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
+	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	bh = udf_tgetblk(inode->i_sb,
+			udf_get_lb_pblock(inode->i_sb, &iinfo->i_location, 0));
+	if (!bh) {
+		udf_debug("getblk failure\n");
+		return -EIO;
+	}
+
+	lock_buffer(bh);
+	memset(bh->b_data, 0, inode->i_sb->s_blocksize);
+	fe = (struct fileEntry *)bh->b_data;
+	efe = (struct extendedFileEntry *)bh->b_data;
+
+	if (iinfo->i_use) {
+		struct unallocSpaceEntry *use =
+			(struct unallocSpaceEntry *)bh->b_data;
+
+		use->lengthAllocDescs = cpu_to_le32(iinfo->i_lenAlloc);
+		memcpy(bh->b_data + sizeof(struct unallocSpaceEntry),
+		       iinfo->i_ext.i_data, inode->i_sb->s_blocksize -
+					sizeof(struct unallocSpaceEntry));
+		use->descTag.tagIdent = cpu_to_le16(TAG_IDENT_USE);
+		crclen = sizeof(struct unallocSpaceEntry);
+
+		goto finish;
+	}
+
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_FORGET))
+		fe->uid = cpu_to_le32(UDF_INVALID_ID);
+	else
+		fe->uid = cpu_to_le32(i_uid_read(inode));
+
+	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_FORGET))
+		fe->gid = cpu_to_le32(UDF_INVALID_ID);
+	else
+		fe->gid = cpu_to_le32(i_gid_read(inode));
+
+	udfperms = ((inode->i_mode & 0007)) |
+		   ((inode->i_mode & 0070) << 2) |
+		   ((inode->i_mode & 0700) << 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) && inode->i_nlink > 0)
+		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)) {
+		struct 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(struct regid), 12, 0x3);
+			dsea->attrType = cpu_to_le32(12);
+			dsea->attrSubtype = 1;
+			dsea->attrLength = cpu_to_le32(
+						sizeof(struct deviceSpec) +
+						sizeof(struct regid));
+			dsea->impUseLength = cpu_to_le32(sizeof(struct regid));
+		}
+		eid = (struct regid *)dsea->impUse;
+		memset(eid, 0, sizeof(*eid));
+		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 (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+		lb_recorded = 0; /* No extents => no blocks! */
+	else
+		lb_recorded =
+			(inode->i_blocks + (1 << (blocksize_bits - 9)) - 1) >>
+			(blocksize_bits - 9);
+
+	if (iinfo->i_efe == 0) {
+		memcpy(bh->b_data + sizeof(struct fileEntry),
+		       iinfo->i_ext.i_data,
+		       inode->i_sb->s_blocksize - sizeof(struct fileEntry));
+		fe->logicalBlocksRecorded = cpu_to_le64(lb_recorded);
+
+		udf_time_to_disk_stamp(&fe->accessTime, inode->i_atime);
+		udf_time_to_disk_stamp(&fe->modificationTime, inode->i_mtime);
+		udf_time_to_disk_stamp(&fe->attrTime, inode->i_ctime);
+		memset(&(fe->impIdent), 0, sizeof(struct 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(iinfo->i_unique);
+		fe->lengthExtendedAttr = cpu_to_le32(iinfo->i_lenEAttr);
+		fe->lengthAllocDescs = cpu_to_le32(iinfo->i_lenAlloc);
+		fe->checkpoint = cpu_to_le32(iinfo->i_checkpoint);
+		fe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_FE);
+		crclen = sizeof(struct fileEntry);
+	} else {
+		memcpy(bh->b_data + sizeof(struct extendedFileEntry),
+		       iinfo->i_ext.i_data,
+		       inode->i_sb->s_blocksize -
+					sizeof(struct extendedFileEntry));
+		efe->objectSize = cpu_to_le64(inode->i_size);
+		efe->logicalBlocksRecorded = cpu_to_le64(lb_recorded);
+
+		udf_adjust_time(iinfo, inode->i_atime);
+		udf_adjust_time(iinfo, inode->i_mtime);
+		udf_adjust_time(iinfo, inode->i_ctime);
+
+		udf_time_to_disk_stamp(&efe->accessTime, inode->i_atime);
+		udf_time_to_disk_stamp(&efe->modificationTime, inode->i_mtime);
+		udf_time_to_disk_stamp(&efe->createTime, iinfo->i_crtime);
+		udf_time_to_disk_stamp(&efe->attrTime, inode->i_ctime);
+
+		memset(&(efe->impIdent), 0, sizeof(efe->impIdent));
+		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(iinfo->i_unique);
+		efe->lengthExtendedAttr = cpu_to_le32(iinfo->i_lenEAttr);
+		efe->lengthAllocDescs = cpu_to_le32(iinfo->i_lenAlloc);
+		efe->checkpoint = cpu_to_le32(iinfo->i_checkpoint);
+		efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE);
+		crclen = sizeof(struct extendedFileEntry);
+	}
+
+finish:
+	if (iinfo->i_strat4096) {
+		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 (iinfo->i_use)
+		fe->icbTag.fileType = ICBTAG_FILE_TYPE_USE;
+	else 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 =	iinfo->i_alloc_type |
+			((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 (sbi->s_udfrev >= 0x0200)
+		fe->descTag.descVersion = cpu_to_le16(3);
+	else
+		fe->descTag.descVersion = cpu_to_le16(2);
+	fe->descTag.tagSerialNum = cpu_to_le16(sbi->s_serial_number);
+	fe->descTag.tagLocation = cpu_to_le32(
+					iinfo->i_location.logicalBlockNum);
+	crclen += iinfo->i_lenEAttr + iinfo->i_lenAlloc - sizeof(struct tag);
+	fe->descTag.descCRCLength = cpu_to_le16(crclen);
+	fe->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)fe + sizeof(struct tag),
+						  crclen));
+	fe->descTag.tagChecksum = udf_tag_checksum(&fe->descTag);
+
+	set_buffer_uptodate(bh);
+	unlock_buffer(bh);
+
+	/* write the data blocks */
+	mark_buffer_dirty(bh);
+	if (do_sync) {
+		sync_dirty_buffer(bh);
+		if (buffer_write_io_error(bh)) {
+			udf_warn(inode->i_sb, "IO error syncing udf inode [%08lx]\n",
+				 inode->i_ino);
+			err = -EIO;
+		}
+	}
+	brelse(bh);
+
+	return err;
+}
+
+struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
+			 bool hidden_inode)
+{
+	unsigned long block = udf_get_lb_pblock(sb, ino, 0);
+	struct inode *inode = iget_locked(sb, block);
+	int err;
+
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	if (!(inode->i_state & I_NEW))
+		return inode;
+
+	memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr));
+	err = udf_read_inode(inode, hidden_inode);
+	if (err < 0) {
+		iget_failed(inode);
+		return ERR_PTR(err);
+	}
+	unlock_new_inode(inode);
+
+	return inode;
+}
+
+int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
+			    struct extent_position *epos)
+{
+	struct super_block *sb = inode->i_sb;
+	struct buffer_head *bh;
+	struct allocExtDesc *aed;
+	struct extent_position nepos;
+	struct kernel_lb_addr neloc;
+	int ver, adsize;
+
+	if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		return -EIO;
+
+	neloc.logicalBlockNum = block;
+	neloc.partitionReferenceNum = epos->block.partitionReferenceNum;
+
+	bh = udf_tgetblk(sb, udf_get_lb_pblock(sb, &neloc, 0));
+	if (!bh)
+		return -EIO;
+	lock_buffer(bh);
+	memset(bh->b_data, 0x00, sb->s_blocksize);
+	set_buffer_uptodate(bh);
+	unlock_buffer(bh);
+	mark_buffer_dirty_inode(bh, inode);
+
+	aed = (struct allocExtDesc *)(bh->b_data);
+	if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT)) {
+		aed->previousAllocExtLocation =
+				cpu_to_le32(epos->block.logicalBlockNum);
+	}
+	aed->lengthAllocDescs = cpu_to_le32(0);
+	if (UDF_SB(sb)->s_udfrev >= 0x0200)
+		ver = 3;
+	else
+		ver = 2;
+	udf_new_tag(bh->b_data, TAG_IDENT_AED, ver, 1, block,
+		    sizeof(struct tag));
+
+	nepos.block = neloc;
+	nepos.offset = sizeof(struct allocExtDesc);
+	nepos.bh = bh;
+
+	/*
+	 * Do we have to copy current last extent to make space for indirect
+	 * one?
+	 */
+	if (epos->offset + adsize > sb->s_blocksize) {
+		struct kernel_lb_addr cp_loc;
+		uint32_t cp_len;
+		int cp_type;
+
+		epos->offset -= adsize;
+		cp_type = udf_current_aext(inode, epos, &cp_loc, &cp_len, 0);
+		cp_len |= ((uint32_t)cp_type) << 30;
+
+		__udf_add_aext(inode, &nepos, &cp_loc, cp_len, 1);
+		udf_write_aext(inode, epos, &nepos.block,
+			       sb->s_blocksize | EXT_NEXT_EXTENT_ALLOCDECS, 0);
+	} else {
+		__udf_add_aext(inode, epos, &nepos.block,
+			       sb->s_blocksize | EXT_NEXT_EXTENT_ALLOCDECS, 0);
+	}
+
+	brelse(epos->bh);
+	*epos = nepos;
+
+	return 0;
+}
+
+/*
+ * Append extent at the given position - should be the first free one in inode
+ * / indirect extent. This function assumes there is enough space in the inode
+ * or indirect extent. Use udf_add_aext() if you didn't check for this before.
+ */
+int __udf_add_aext(struct inode *inode, struct extent_position *epos,
+		   struct kernel_lb_addr *eloc, uint32_t elen, int inc)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	struct allocExtDesc *aed;
+	int adsize;
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		return -EIO;
+
+	if (!epos->bh) {
+		WARN_ON(iinfo->i_lenAlloc !=
+			epos->offset - udf_file_entry_alloc_offset(inode));
+	} else {
+		aed = (struct allocExtDesc *)epos->bh->b_data;
+		WARN_ON(le32_to_cpu(aed->lengthAllocDescs) !=
+			epos->offset - sizeof(struct allocExtDesc));
+		WARN_ON(epos->offset + adsize > inode->i_sb->s_blocksize);
+	}
+
+	udf_write_aext(inode, epos, eloc, elen, inc);
+
+	if (!epos->bh) {
+		iinfo->i_lenAlloc += adsize;
+		mark_inode_dirty(inode);
+	} else {
+		aed = (struct allocExtDesc *)epos->bh->b_data;
+		le32_add_cpu(&aed->lengthAllocDescs, adsize);
+		if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
+				UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
+			udf_update_tag(epos->bh->b_data,
+					epos->offset + (inc ? 0 : adsize));
+		else
+			udf_update_tag(epos->bh->b_data,
+					sizeof(struct allocExtDesc));
+		mark_buffer_dirty_inode(epos->bh, inode);
+	}
+
+	return 0;
+}
+
+/*
+ * Append extent at given position - should be the first free one in inode
+ * / indirect extent. Takes care of allocating and linking indirect blocks.
+ */
+int udf_add_aext(struct inode *inode, struct extent_position *epos,
+		 struct kernel_lb_addr *eloc, uint32_t elen, int inc)
+{
+	int adsize;
+	struct super_block *sb = inode->i_sb;
+
+	if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		return -EIO;
+
+	if (epos->offset + (2 * adsize) > sb->s_blocksize) {
+		int err;
+		udf_pblk_t new_block;
+
+		new_block = udf_new_block(sb, NULL,
+					  epos->block.partitionReferenceNum,
+					  epos->block.logicalBlockNum, &err);
+		if (!new_block)
+			return -ENOSPC;
+
+		err = udf_setup_indirect_aext(inode, new_block, epos);
+		if (err)
+			return err;
+	}
+
+	return __udf_add_aext(inode, epos, eloc, elen, inc);
+}
+
+void udf_write_aext(struct inode *inode, struct extent_position *epos,
+		    struct kernel_lb_addr *eloc, uint32_t elen, int inc)
+{
+	int adsize;
+	uint8_t *ptr;
+	struct short_ad *sad;
+	struct long_ad *lad;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	if (!epos->bh)
+		ptr = iinfo->i_ext.i_data + epos->offset -
+			udf_file_entry_alloc_offset(inode) +
+			iinfo->i_lenEAttr;
+	else
+		ptr = epos->bh->b_data + epos->offset;
+
+	switch (iinfo->i_alloc_type) {
+	case ICBTAG_FLAG_AD_SHORT:
+		sad = (struct short_ad *)ptr;
+		sad->extLength = cpu_to_le32(elen);
+		sad->extPosition = cpu_to_le32(eloc->logicalBlockNum);
+		adsize = sizeof(struct short_ad);
+		break;
+	case ICBTAG_FLAG_AD_LONG:
+		lad = (struct long_ad *)ptr;
+		lad->extLength = cpu_to_le32(elen);
+		lad->extLocation = cpu_to_lelb(*eloc);
+		memset(lad->impUse, 0x00, sizeof(lad->impUse));
+		adsize = sizeof(struct long_ad);
+		break;
+	default:
+		return;
+	}
+
+	if (epos->bh) {
+		if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
+		    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) {
+			struct allocExtDesc *aed =
+				(struct allocExtDesc *)epos->bh->b_data;
+			udf_update_tag(epos->bh->b_data,
+				       le32_to_cpu(aed->lengthAllocDescs) +
+				       sizeof(struct allocExtDesc));
+		}
+		mark_buffer_dirty_inode(epos->bh, inode);
+	} else {
+		mark_inode_dirty(inode);
+	}
+
+	if (inc)
+		epos->offset += adsize;
+}
+
+/*
+ * Only 1 indirect extent in a row really makes sense but allow upto 16 in case
+ * someone does some weird stuff.
+ */
+#define UDF_MAX_INDIR_EXTS 16
+
+int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,
+		     struct kernel_lb_addr *eloc, uint32_t *elen, int inc)
+{
+	int8_t etype;
+	unsigned int indirections = 0;
+
+	while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
+	       (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
+		udf_pblk_t block;
+
+		if (++indirections > UDF_MAX_INDIR_EXTS) {
+			udf_err(inode->i_sb,
+				"too many indirect extents in inode %lu\n",
+				inode->i_ino);
+			return -1;
+		}
+
+		epos->block = *eloc;
+		epos->offset = sizeof(struct allocExtDesc);
+		brelse(epos->bh);
+		block = udf_get_lb_pblock(inode->i_sb, &epos->block, 0);
+		epos->bh = udf_tread(inode->i_sb, block);
+		if (!epos->bh) {
+			udf_debug("reading block %u failed!\n", block);
+			return -1;
+		}
+	}
+
+	return etype;
+}
+
+int8_t udf_current_aext(struct inode *inode, struct extent_position *epos,
+			struct kernel_lb_addr *eloc, uint32_t *elen, int inc)
+{
+	int alen;
+	int8_t etype;
+	uint8_t *ptr;
+	struct short_ad *sad;
+	struct long_ad *lad;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	if (!epos->bh) {
+		if (!epos->offset)
+			epos->offset = udf_file_entry_alloc_offset(inode);
+		ptr = iinfo->i_ext.i_data + epos->offset -
+			udf_file_entry_alloc_offset(inode) +
+			iinfo->i_lenEAttr;
+		alen = udf_file_entry_alloc_offset(inode) +
+							iinfo->i_lenAlloc;
+	} else {
+		if (!epos->offset)
+			epos->offset = sizeof(struct allocExtDesc);
+		ptr = epos->bh->b_data + epos->offset;
+		alen = sizeof(struct allocExtDesc) +
+			le32_to_cpu(((struct allocExtDesc *)epos->bh->b_data)->
+							lengthAllocDescs);
+	}
+
+	switch (iinfo->i_alloc_type) {
+	case ICBTAG_FLAG_AD_SHORT:
+		sad = udf_get_fileshortad(ptr, alen, &epos->offset, inc);
+		if (!sad)
+			return -1;
+		etype = le32_to_cpu(sad->extLength) >> 30;
+		eloc->logicalBlockNum = le32_to_cpu(sad->extPosition);
+		eloc->partitionReferenceNum =
+				iinfo->i_location.partitionReferenceNum;
+		*elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK;
+		break;
+	case ICBTAG_FLAG_AD_LONG:
+		lad = udf_get_filelongad(ptr, alen, &epos->offset, inc);
+		if (!lad)
+			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 = %u unsupported\n", iinfo->i_alloc_type);
+		return -1;
+	}
+
+	return etype;
+}
+
+static int8_t udf_insert_aext(struct inode *inode, struct extent_position epos,
+			      struct kernel_lb_addr neloc, uint32_t nelen)
+{
+	struct kernel_lb_addr oeloc;
+	uint32_t oelen;
+	int8_t etype;
+
+	if (epos.bh)
+		get_bh(epos.bh);
+
+	while ((etype = udf_next_aext(inode, &epos, &oeloc, &oelen, 0)) != -1) {
+		udf_write_aext(inode, &epos, &neloc, nelen, 1);
+		neloc = oeloc;
+		nelen = (etype << 30) | oelen;
+	}
+	udf_add_aext(inode, &epos, &neloc, nelen, 1);
+	brelse(epos.bh);
+
+	return (nelen >> 30);
+}
+
+int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
+{
+	struct extent_position oepos;
+	int adsize;
+	int8_t etype;
+	struct allocExtDesc *aed;
+	struct udf_inode_info *iinfo;
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+
+	if (epos.bh) {
+		get_bh(epos.bh);
+		get_bh(epos.bh);
+	}
+
+	iinfo = UDF_I(inode);
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		adsize = 0;
+
+	oepos = epos;
+	if (udf_next_aext(inode, &epos, &eloc, &elen, 1) == -1)
+		return -1;
+
+	while ((etype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
+		udf_write_aext(inode, &oepos, &eloc, (etype << 30) | elen, 1);
+		if (oepos.bh != epos.bh) {
+			oepos.block = epos.block;
+			brelse(oepos.bh);
+			get_bh(epos.bh);
+			oepos.bh = epos.bh;
+			oepos.offset = epos.offset - adsize;
+		}
+	}
+	memset(&eloc, 0x00, sizeof(struct kernel_lb_addr));
+	elen = 0;
+
+	if (epos.bh != oepos.bh) {
+		udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1);
+		udf_write_aext(inode, &oepos, &eloc, elen, 1);
+		udf_write_aext(inode, &oepos, &eloc, elen, 1);
+		if (!oepos.bh) {
+			iinfo->i_lenAlloc -= (adsize * 2);
+			mark_inode_dirty(inode);
+		} else {
+			aed = (struct allocExtDesc *)oepos.bh->b_data;
+			le32_add_cpu(&aed->lengthAllocDescs, -(2 * adsize));
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
+			    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
+				udf_update_tag(oepos.bh->b_data,
+						oepos.offset - (2 * adsize));
+			else
+				udf_update_tag(oepos.bh->b_data,
+						sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(oepos.bh, inode);
+		}
+	} else {
+		udf_write_aext(inode, &oepos, &eloc, elen, 1);
+		if (!oepos.bh) {
+			iinfo->i_lenAlloc -= adsize;
+			mark_inode_dirty(inode);
+		} else {
+			aed = (struct allocExtDesc *)oepos.bh->b_data;
+			le32_add_cpu(&aed->lengthAllocDescs, -adsize);
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
+			    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
+				udf_update_tag(oepos.bh->b_data,
+						epos.offset - adsize);
+			else
+				udf_update_tag(oepos.bh->b_data,
+						sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(oepos.bh, inode);
+		}
+	}
+
+	brelse(epos.bh);
+	brelse(oepos.bh);
+
+	return (elen >> 30);
+}
+
+int8_t inode_bmap(struct inode *inode, sector_t block,
+		  struct extent_position *pos, struct kernel_lb_addr *eloc,
+		  uint32_t *elen, sector_t *offset)
+{
+	unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits;
+	loff_t lbcount = 0, bcount = (loff_t) block << blocksize_bits;
+	int8_t etype;
+	struct udf_inode_info *iinfo;
+
+	iinfo = UDF_I(inode);
+	if (!udf_read_extent_cache(inode, bcount, &lbcount, pos)) {
+		pos->offset = 0;
+		pos->block = iinfo->i_location;
+		pos->bh = NULL;
+	}
+	*elen = 0;
+	do {
+		etype = udf_next_aext(inode, pos, eloc, elen, 1);
+		if (etype == -1) {
+			*offset = (bcount - lbcount) >> blocksize_bits;
+			iinfo->i_lenExtents = lbcount;
+			return -1;
+		}
+		lbcount += *elen;
+	} while (lbcount <= bcount);
+	/* update extent cache */
+	udf_update_extent_cache(inode, lbcount - *elen, pos);
+	*offset = (bcount + *elen - lbcount) >> blocksize_bits;
+
+	return etype;
+}
+
+udf_pblk_t udf_block_map(struct inode *inode, sector_t block)
+{
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	sector_t offset;
+	struct extent_position epos = {};
+	udf_pblk_t ret;
+
+	down_read(&UDF_I(inode)->i_data_sem);
+
+	if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) ==
+						(EXT_RECORDED_ALLOCATED >> 30))
+		ret = udf_get_lb_pblock(inode->i_sb, &eloc, offset);
+	else
+		ret = 0;
+
+	up_read(&UDF_I(inode)->i_data_sem);
+	brelse(epos.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 0000000..5c7ec12
--- /dev/null
+++ b/fs/udf/lowlevel.c
@@ -0,0 +1,67 @@
+/*
+ * lowlevel.c
+ *
+ * PURPOSE
+ *  Low Level Device Routines for the UDF filesystem
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 1999-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ *  03/26/99 blf  Created.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/blkdev.h>
+#include <linux/cdrom.h>
+#include <linux/uaccess.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);
+
+	if (i == 0) {
+		udf_debug("XA disk: %s, vol_desc_start=%d\n",
+			  ms_info.xa_flag ? "yes" : "no", ms_info.addr.lba);
+		if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
+			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;
+
+	/*
+	 * ioctl failed or returned obviously bogus value?
+	 * Try using the device size...
+	 */
+	if (ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock) ||
+	    lblock == 0)
+		lblock = i_size_read(bdev->bd_inode) >> 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 0000000..401e64c
--- /dev/null
+++ b/fs/udf/misc.c
@@ -0,0 +1,296 @@
+/*
+ * misc.c
+ *
+ * PURPOSE
+ *	Miscellaneous routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 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/crc-itu-t.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+struct buffer_head *udf_tgetblk(struct super_block *sb, udf_pblk_t 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, udf_pblk_t 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;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	ea = iinfo->i_ext.i_data;
+	if (iinfo->i_lenEAttr) {
+		ad = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
+	} else {
+		ad = ea;
+		size += sizeof(struct extendedAttrHeaderDesc);
+	}
+
+	offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) -
+		iinfo->i_lenAlloc;
+
+	/* TODO - Check for FreeEASpace */
+
+	if (loc & 0x01 && offset >= size) {
+		struct extendedAttrHeaderDesc *eahd;
+		eahd = (struct extendedAttrHeaderDesc *)ea;
+
+		if (iinfo->i_lenAlloc)
+			memmove(&ad[size], ad, iinfo->i_lenAlloc);
+
+		if (iinfo->i_lenEAttr) {
+			/* check checksum/crc */
+			if (eahd->descTag.tagIdent !=
+					cpu_to_le16(TAG_IDENT_EAHD) ||
+			    le32_to_cpu(eahd->descTag.tagLocation) !=
+					iinfo->i_location.logicalBlockNum)
+				return NULL;
+		} else {
+			struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
+
+			size -= sizeof(struct extendedAttrHeaderDesc);
+			iinfo->i_lenEAttr +=
+				sizeof(struct extendedAttrHeaderDesc);
+			eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD);
+			if (sbi->s_udfrev >= 0x0200)
+				eahd->descTag.descVersion = cpu_to_le16(3);
+			else
+				eahd->descTag.descVersion = cpu_to_le16(2);
+			eahd->descTag.tagSerialNum =
+					cpu_to_le16(sbi->s_serial_number);
+			eahd->descTag.tagLocation = cpu_to_le32(
+					iinfo->i_location.logicalBlockNum);
+			eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF);
+			eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF);
+		}
+
+		offset = iinfo->i_lenEAttr;
+		if (type < 2048) {
+			if (le32_to_cpu(eahd->appAttrLocation) <
+					iinfo->i_lenEAttr) {
+				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) <
+					iinfo->i_lenEAttr) {
+				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) <
+					iinfo->i_lenEAttr) {
+				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(struct tag);
+		eahd->descTag.descCRCLength = cpu_to_le16(crclen);
+		eahd->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)eahd +
+						sizeof(struct tag), crclen));
+		eahd->descTag.tagChecksum = udf_tag_checksum(&eahd->descTag);
+		iinfo->i_lenEAttr += size;
+		return (struct genericFormat *)&ea[offset];
+	}
+
+	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;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	ea = iinfo->i_ext.i_data;
+
+	if (iinfo->i_lenEAttr) {
+		struct extendedAttrHeaderDesc *eahd;
+		eahd = (struct extendedAttrHeaderDesc *)ea;
+
+		/* check checksum/crc */
+		if (eahd->descTag.tagIdent !=
+				cpu_to_le16(TAG_IDENT_EAHD) ||
+		    le32_to_cpu(eahd->descTag.tagLocation) !=
+				iinfo->i_location.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 < iinfo->i_lenEAttr) {
+			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)
+{
+	struct tag *tag_p;
+	struct buffer_head *bh = NULL;
+	u8 checksum;
+
+	/* Read the block */
+	if (block == 0xFFFFFFFF)
+		return NULL;
+
+	bh = udf_tread(sb, block);
+	if (!bh) {
+		udf_err(sb, "read failed, block=%u, location=%u\n",
+			block, location);
+		return NULL;
+	}
+
+	tag_p = (struct 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, le32_to_cpu(tag_p->tagLocation), location);
+		goto error_out;
+	}
+
+	/* Verify the tag checksum */
+	checksum = udf_tag_checksum(tag_p);
+	if (checksum != tag_p->tagChecksum) {
+		udf_err(sb, "tag checksum failed, block %u: 0x%02x != 0x%02x\n",
+			block, checksum, tag_p->tagChecksum);
+		goto error_out;
+	}
+
+	/* Verify the tag version */
+	if (tag_p->descVersion != cpu_to_le16(0x0002U) &&
+	    tag_p->descVersion != cpu_to_le16(0x0003U)) {
+		udf_err(sb, "tag version 0x%04x != 0x0002 || 0x0003, block %u\n",
+			le16_to_cpu(tag_p->descVersion), block);
+		goto error_out;
+	}
+
+	/* Verify the descriptor CRC */
+	if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize ||
+	    le16_to_cpu(tag_p->descCRC) == crc_itu_t(0,
+					bh->b_data + sizeof(struct tag),
+					le16_to_cpu(tag_p->descCRCLength)))
+		return bh;
+
+	udf_debug("Crc failure block %u: crc = %u, crclen = %u\n", block,
+		  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,
+				     struct 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_update_tag(char *data, int length)
+{
+	struct tag *tptr = (struct tag *)data;
+	length -= sizeof(struct tag);
+
+	tptr->descCRCLength = cpu_to_le16(length);
+	tptr->descCRC = cpu_to_le16(crc_itu_t(0, data + sizeof(struct tag), length));
+	tptr->tagChecksum = udf_tag_checksum(tptr);
+}
+
+void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum,
+		 uint32_t loc, int length)
+{
+	struct tag *tptr = (struct 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);
+}
+
+u8 udf_tag_checksum(const struct tag *t)
+{
+	u8 *data = (u8 *)t;
+	u8 checksum = 0;
+	int i;
+	for (i = 0; i < sizeof(struct tag); ++i)
+		if (i != 4) /* position of checksum */
+			checksum += data[i];
+	return checksum;
+}
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
new file mode 100644
index 0000000..58cc241
--- /dev/null
+++ b/fs/udf/namei.c
@@ -0,0 +1,1349 @@
+/*
+ * namei.c
+ *
+ * PURPOSE
+ *      Inode name handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *      This file is distributed under the terms of the GNU General Public
+ *      License (GPL). Copies of the GPL can be obtained from:
+ *              ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *      Each contributing author retains all rights to their own work.
+ *
+ *  (C) 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/sched.h>
+#include <linux/crc-itu-t.h>
+#include <linux/exportfs.h>
+
+static inline int udf_match(int len1, const unsigned char *name1, int len2,
+			    const unsigned 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(struct tag);
+	uint16_t crc;
+	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(inode)->i_alloc_type == 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 = crc_itu_t(0, (uint8_t *)cfi + sizeof(struct tag),
+		      sizeof(struct fileIdentDesc) - sizeof(struct tag));
+
+	if (fibh->sbh == fibh->ebh) {
+		crc = crc_itu_t(crc, (uint8_t *)sfi->impUse,
+			      crclen + sizeof(struct tag) -
+			      sizeof(struct fileIdentDesc));
+	} else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) {
+		crc = crc_itu_t(crc, fibh->ebh->b_data +
+					sizeof(struct fileIdentDesc) +
+					fibh->soffset,
+			      crclen + sizeof(struct tag) -
+					sizeof(struct fileIdentDesc));
+	} else {
+		crc = crc_itu_t(crc, (uint8_t *)sfi->impUse,
+			      -fibh->soffset - sizeof(struct fileIdentDesc));
+		crc = crc_itu_t(crc, fibh->ebh->b_data, fibh->eoffset);
+	}
+
+	cfi->descTag.descCRC = cpu_to_le16(crc);
+	cfi->descTag.descCRCLength = cpu_to_le16(crclen);
+	cfi->descTag.tagChecksum = udf_tag_checksum(&cfi->descTag);
+
+	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;
+}
+
+/**
+ * udf_find_entry - find entry in given directory.
+ *
+ * @dir:	directory inode to search in
+ * @child:	qstr of the name
+ * @fibh:	buffer head / inode with file identifier descriptor we found
+ * @cfi:	found file identifier descriptor with given name
+ *
+ * This function searches in the directory @dir for a file name @child. When
+ * found, @fibh points to the buffer head(s) (bh is NULL for in ICB
+ * directories) containing the file identifier descriptor (FID). In that case
+ * the function returns pointer to the FID in the buffer or inode - but note
+ * that FID may be split among two buffers (blocks) so accessing it via that
+ * pointer isn't easily possible. This pointer can be used only as an iterator
+ * for other directory manipulation functions. For inspection of the FID @cfi
+ * can be used - the found FID is copied there.
+ *
+ * Returns pointer to FID, NULL when nothing found, or error code.
+ */
+static struct fileIdentDesc *udf_find_entry(struct inode *dir,
+					    const struct qstr *child,
+					    struct udf_fileident_bh *fibh,
+					    struct fileIdentDesc *cfi)
+{
+	struct fileIdentDesc *fi = NULL;
+	loff_t f_pos;
+	udf_pblk_t block;
+	int flen;
+	unsigned char *fname = NULL, *copy_name = NULL;
+	unsigned char *nameptr;
+	uint8_t lfi;
+	uint16_t liu;
+	loff_t size;
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	sector_t offset;
+	struct extent_position epos = {};
+	struct udf_inode_info *dinfo = UDF_I(dir);
+	int isdotdot = child->len == 2 &&
+		child->name[0] == '.' && child->name[1] == '.';
+	struct super_block *sb = dir->i_sb;
+
+	size = udf_ext0_offset(dir) + dir->i_size;
+	f_pos = udf_ext0_offset(dir);
+
+	fibh->sbh = fibh->ebh = NULL;
+	fibh->soffset = fibh->eoffset = f_pos & (sb->s_blocksize - 1);
+	if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+		if (inode_bmap(dir, f_pos >> sb->s_blocksize_bits, &epos,
+		    &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) {
+			fi = ERR_PTR(-EIO);
+			goto out_err;
+		}
+
+		block = udf_get_lb_pblock(sb, &eloc, offset);
+		if ((++offset << sb->s_blocksize_bits) < elen) {
+			if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+				epos.offset -= sizeof(struct short_ad);
+			else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+				epos.offset -= sizeof(struct long_ad);
+		} else
+			offset = 0;
+
+		fibh->sbh = fibh->ebh = udf_tread(sb, block);
+		if (!fibh->sbh) {
+			fi = ERR_PTR(-EIO);
+			goto out_err;
+		}
+	}
+
+	fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+	if (!fname) {
+		fi = ERR_PTR(-ENOMEM);
+		goto out_err;
+	}
+
+	while (f_pos < size) {
+		fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
+					&elen, &offset);
+		if (!fi) {
+			fi = ERR_PTR(-EIO);
+			goto out_err;
+		}
+
+		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 {
+				if (!copy_name) {
+					copy_name = kmalloc(UDF_NAME_LEN,
+							    GFP_NOFS);
+					if (!copy_name) {
+						fi = ERR_PTR(-ENOMEM);
+						goto out_err;
+					}
+				}
+				nameptr = copy_name;
+				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(sb, UDF_FLAG_UNDELETE))
+				continue;
+		}
+
+		if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
+			if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
+				continue;
+		}
+
+		if ((cfi->fileCharacteristics & FID_FILE_CHAR_PARENT) &&
+		    isdotdot)
+			goto out_ok;
+
+		if (!lfi)
+			continue;
+
+		flen = udf_get_filename(sb, nameptr, lfi, fname, UDF_NAME_LEN);
+		if (flen < 0) {
+			fi = ERR_PTR(flen);
+			goto out_err;
+		}
+
+		if (udf_match(flen, fname, child->len, child->name))
+			goto out_ok;
+	}
+
+	fi = NULL;
+out_err:
+	if (fibh->sbh != fibh->ebh)
+		brelse(fibh->ebh);
+	brelse(fibh->sbh);
+out_ok:
+	brelse(epos.bh);
+	kfree(fname);
+	kfree(copy_name);
+
+	return fi;
+}
+
+static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry,
+				 unsigned int flags)
+{
+	struct inode *inode = NULL;
+	struct fileIdentDesc cfi;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc *fi;
+
+	if (dentry->d_name.len > UDF_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+#ifdef UDF_RECOVERY
+	/* temporary shorthand for specifying files by inode number */
+	if (!strncmp(dentry->d_name.name, ".B=", 3)) {
+		struct kernel_lb_addr lb = {
+			.logicalBlockNum = 0,
+			.partitionReferenceNum =
+				simple_strtoul(dentry->d_name.name + 3,
+						NULL, 0),
+		};
+		inode = udf_iget(dir->i_sb, lb);
+		if (IS_ERR(inode))
+			return inode;
+	} else
+#endif /* UDF_RECOVERY */
+
+	fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
+	if (IS_ERR(fi))
+		return ERR_CAST(fi);
+
+	if (fi) {
+		struct kernel_lb_addr loc;
+
+		if (fibh.sbh != fibh.ebh)
+			brelse(fibh.ebh);
+		brelse(fibh.sbh);
+
+		loc = lelb_to_cpu(cfi.icb.extLocation);
+		inode = udf_iget(dir->i_sb, &loc);
+		if (IS_ERR(inode))
+			return ERR_CAST(inode);
+	}
+
+	return d_splice_alias(inode, dentry);
+}
+
+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 = dir->i_sb;
+	struct fileIdentDesc *fi = NULL;
+	unsigned char *name = NULL;
+	int namelen;
+	loff_t f_pos;
+	loff_t size = udf_ext0_offset(dir) + dir->i_size;
+	int nfidlen;
+	udf_pblk_t block;
+	struct kernel_lb_addr eloc;
+	uint32_t elen = 0;
+	sector_t offset;
+	struct extent_position epos = {};
+	struct udf_inode_info *dinfo;
+
+	fibh->sbh = fibh->ebh = NULL;
+	name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS);
+	if (!name) {
+		*err = -ENOMEM;
+		goto out_err;
+	}
+
+	if (dentry) {
+		if (!dentry->d_name.len) {
+			*err = -EINVAL;
+			goto out_err;
+		}
+		namelen = udf_put_filename(sb, dentry->d_name.name,
+					   dentry->d_name.len,
+					   name, UDF_NAME_LEN_CS0);
+		if (!namelen) {
+			*err = -ENAMETOOLONG;
+			goto out_err;
+		}
+	} else {
+		namelen = 0;
+	}
+
+	nfidlen = ALIGN(sizeof(struct fileIdentDesc) + namelen, UDF_NAME_PAD);
+
+	f_pos = udf_ext0_offset(dir);
+
+	fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
+	dinfo = UDF_I(dir);
+	if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+		if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos,
+		    &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) {
+			block = udf_get_lb_pblock(dir->i_sb,
+					&dinfo->i_location, 0);
+			fibh->soffset = fibh->eoffset = sb->s_blocksize;
+			goto add;
+		}
+		block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
+		if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
+			if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+				epos.offset -= sizeof(struct short_ad);
+			else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+				epos.offset -= sizeof(struct long_ad);
+		} else
+			offset = 0;
+
+		fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
+		if (!fibh->sbh) {
+			*err = -EIO;
+			goto out_err;
+		}
+
+		block = dinfo->i_location.logicalBlockNum;
+	}
+
+	while (f_pos < size) {
+		fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc,
+					&elen, &offset);
+
+		if (!fi) {
+			*err = -EIO;
+			goto out_err;
+		}
+
+		if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
+			if (udf_dir_entry_len(cfi) == nfidlen) {
+				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))
+					goto out_ok;
+				else {
+					*err = -EIO;
+					goto out_err;
+				}
+			}
+		}
+	}
+
+add:
+	f_pos += nfidlen;
+
+	if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
+	    sb->s_blocksize - fibh->eoffset < nfidlen) {
+		brelse(epos.bh);
+		epos.bh = NULL;
+		fibh->soffset -= udf_ext0_offset(dir);
+		fibh->eoffset -= udf_ext0_offset(dir);
+		f_pos -= udf_ext0_offset(dir);
+		if (fibh->sbh != fibh->ebh)
+			brelse(fibh->ebh);
+		brelse(fibh->sbh);
+		fibh->sbh = fibh->ebh =
+				udf_expand_dir_adinicb(dir, &block, err);
+		if (!fibh->sbh)
+			goto out_err;
+		epos.block = dinfo->i_location;
+		epos.offset = udf_file_entry_alloc_offset(dir);
+		/* Load extent udf_expand_dir_adinicb() has created */
+		udf_current_aext(dir, &epos, &eloc, &elen, 1);
+	}
+
+	/* Entry fits into current block? */
+	if (sb->s_blocksize - fibh->eoffset >= nfidlen) {
+		fibh->soffset = fibh->eoffset;
+		fibh->eoffset += nfidlen;
+		if (fibh->sbh != fibh->ebh) {
+			brelse(fibh->sbh);
+			fibh->sbh = fibh->ebh;
+		}
+
+		if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+			block = dinfo->i_location.logicalBlockNum;
+			fi = (struct fileIdentDesc *)
+					(dinfo->i_ext.i_data +
+					 fibh->soffset -
+					 udf_ext0_offset(dir) +
+					 dinfo->i_lenEAttr);
+		} else {
+			block = eloc.logicalBlockNum +
+					((elen - 1) >>
+						dir->i_sb->s_blocksize_bits);
+			fi = (struct fileIdentDesc *)
+				(fibh->sbh->b_data + fibh->soffset);
+		}
+	} else {
+		/* Round up last extent in the file */
+		elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1);
+		if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+			epos.offset -= sizeof(struct short_ad);
+		else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+			epos.offset -= sizeof(struct long_ad);
+		udf_write_aext(dir, &epos, &eloc, elen, 1);
+		dinfo->i_lenExtents = (dinfo->i_lenExtents + sb->s_blocksize
+					- 1) & ~(sb->s_blocksize - 1);
+
+		fibh->soffset = fibh->eoffset - sb->s_blocksize;
+		fibh->eoffset += nfidlen - sb->s_blocksize;
+		if (fibh->sbh != fibh->ebh) {
+			brelse(fibh->sbh);
+			fibh->sbh = fibh->ebh;
+		}
+
+		block = eloc.logicalBlockNum + ((elen - 1) >>
+						dir->i_sb->s_blocksize_bits);
+		fibh->ebh = udf_bread(dir,
+				f_pos >> dir->i_sb->s_blocksize_bits, 1, err);
+		if (!fibh->ebh)
+			goto out_err;
+		/* Extents could have been merged, invalidate our position */
+		brelse(epos.bh);
+		epos.bh = NULL;
+		epos.block = dinfo->i_location;
+		epos.offset = udf_file_entry_alloc_offset(dir);
+
+		if (!fibh->soffset) {
+			/* Find the freshly allocated block */
+			while (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
+				(EXT_RECORDED_ALLOCATED >> 30))
+				;
+			block = eloc.logicalBlockNum + ((elen - 1) >>
+					dir->i_sb->s_blocksize_bits);
+			brelse(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(sb)->s_udfrev >= 0x0200)
+		udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block,
+			    sizeof(struct tag));
+	else
+		udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block,
+			    sizeof(struct 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)) {
+		dir->i_size += nfidlen;
+		if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+			dinfo->i_lenAlloc += nfidlen;
+		else {
+			/* Find the last extent and truncate it to proper size */
+			while (udf_next_aext(dir, &epos, &eloc, &elen, 1) ==
+				(EXT_RECORDED_ALLOCATED >> 30))
+				;
+			elen -= dinfo->i_lenExtents - dir->i_size;
+			if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+				epos.offset -= sizeof(struct short_ad);
+			else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+				epos.offset -= sizeof(struct long_ad);
+			udf_write_aext(dir, &epos, &eloc, elen, 1);
+			dinfo->i_lenExtents = dir->i_size;
+		}
+
+		mark_inode_dirty(dir);
+		goto out_ok;
+	} else {
+		*err = -EIO;
+		goto out_err;
+	}
+
+out_err:
+	fi = NULL;
+	if (fibh->sbh != fibh->ebh)
+		brelse(fibh->ebh);
+	brelse(fibh->sbh);
+out_ok:
+	brelse(epos.bh);
+	kfree(name);
+	return fi;
+}
+
+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(struct long_ad));
+
+	return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL);
+}
+
+static int udf_add_nondir(struct dentry *dentry, struct inode *inode)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	struct inode *dir = d_inode(dentry->d_parent);
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+
+	fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
+	if (unlikely(!fi)) {
+		inode_dec_link_count(inode);
+		discard_new_inode(inode);
+		return err;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	dir->i_ctime = dir->i_mtime = current_time(dir);
+	mark_inode_dirty(dir);
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+	d_instantiate_new(dentry, inode);
+
+	return 0;
+}
+
+static int udf_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+		      bool excl)
+{
+	struct inode *inode = udf_new_inode(dir, mode);
+
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	if (UDF_I(inode)->i_alloc_type == 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;
+	mark_inode_dirty(inode);
+
+	return udf_add_nondir(dentry, inode);
+}
+
+static int udf_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	struct inode *inode = udf_new_inode(dir, mode);
+
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	if (UDF_I(inode)->i_alloc_type == 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;
+	mark_inode_dirty(inode);
+	d_tmpfile(dentry, inode);
+	unlock_new_inode(inode);
+	return 0;
+}
+
+static int udf_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
+		     dev_t rdev)
+{
+	struct inode *inode;
+
+	if (!old_valid_dev(rdev))
+		return -EINVAL;
+
+	inode = udf_new_inode(dir, mode);
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	init_special_inode(inode, mode, rdev);
+	return udf_add_nondir(dentry, inode);
+}
+
+static int udf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+	struct inode *inode;
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+	struct udf_inode_info *dinfo = UDF_I(dir);
+	struct udf_inode_info *iinfo;
+
+	inode = udf_new_inode(dir, S_IFDIR | mode);
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	iinfo = UDF_I(inode);
+	inode->i_op = &udf_dir_inode_operations;
+	inode->i_fop = &udf_dir_operations;
+	fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err);
+	if (!fi) {
+		inode_dec_link_count(inode);
+		discard_new_inode(inode);
+		goto out;
+	}
+	set_nlink(inode, 2);
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(dinfo->i_location);
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL);
+	cfi.fileCharacteristics =
+			FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT;
+	udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL);
+	brelse(fibh.sbh);
+	mark_inode_dirty(inode);
+
+	fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
+	if (!fi) {
+		clear_nlink(inode);
+		mark_inode_dirty(inode);
+		discard_new_inode(inode);
+		goto out;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location);
+	*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+		cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL);
+	cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	inc_nlink(dir);
+	dir->i_ctime = dir->i_mtime = current_time(dir);
+	mark_inode_dirty(dir);
+	d_instantiate_new(dentry, inode);
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+	err = 0;
+
+out:
+	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;
+	udf_pblk_t block;
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	sector_t offset;
+	struct extent_position epos = {};
+	struct udf_inode_info *dinfo = UDF_I(dir);
+
+	f_pos = udf_ext0_offset(dir);
+	fibh.soffset = fibh.eoffset = f_pos & (dir->i_sb->s_blocksize - 1);
+
+	if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+		fibh.sbh = fibh.ebh = NULL;
+	else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits,
+			      &epos, &eloc, &elen, &offset) ==
+					(EXT_RECORDED_ALLOCATED >> 30)) {
+		block = udf_get_lb_pblock(dir->i_sb, &eloc, offset);
+		if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
+			if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+				epos.offset -= sizeof(struct short_ad);
+			else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+				epos.offset -= sizeof(struct long_ad);
+		} else
+			offset = 0;
+
+		fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block);
+		if (!fibh.sbh) {
+			brelse(epos.bh);
+			return 0;
+		}
+	} else {
+		brelse(epos.bh);
+		return 0;
+	}
+
+	while (f_pos < size) {
+		fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc,
+					&elen, &offset);
+		if (!fi) {
+			if (fibh.sbh != fibh.ebh)
+				brelse(fibh.ebh);
+			brelse(fibh.sbh);
+			brelse(epos.bh);
+			return 0;
+		}
+
+		if (cfi.lengthFileIdent &&
+		    (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) {
+			if (fibh.sbh != fibh.ebh)
+				brelse(fibh.ebh);
+			brelse(fibh.sbh);
+			brelse(epos.bh);
+			return 0;
+		}
+	}
+
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+	brelse(epos.bh);
+
+	return 1;
+}
+
+static int udf_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	int retval;
+	struct inode *inode = d_inode(dentry);
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc *fi, cfi;
+	struct kernel_lb_addr tloc;
+
+	retval = -ENOENT;
+	fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
+	if (IS_ERR_OR_NULL(fi)) {
+		if (fi)
+			retval = PTR_ERR(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_warn(inode->i_sb, "empty directory has nlink != 2 (%u)\n",
+			 inode->i_nlink);
+	clear_nlink(inode);
+	inode->i_size = 0;
+	inode_dec_link_count(dir);
+	inode->i_ctime = dir->i_ctime = dir->i_mtime =
+						current_time(inode);
+	mark_inode_dirty(dir);
+
+end_rmdir:
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+
+out:
+	return retval;
+}
+
+static int udf_unlink(struct inode *dir, struct dentry *dentry)
+{
+	int retval;
+	struct inode *inode = d_inode(dentry);
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc *fi;
+	struct fileIdentDesc cfi;
+	struct kernel_lb_addr tloc;
+
+	retval = -ENOENT;
+	fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi);
+
+	if (IS_ERR_OR_NULL(fi)) {
+		if (fi)
+			retval = PTR_ERR(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), %u\n",
+			  inode->i_ino, inode->i_nlink);
+		set_nlink(inode, 1);
+	}
+	retval = udf_delete_entry(dir, fi, &fibh, &cfi);
+	if (retval)
+		goto end_unlink;
+	dir->i_ctime = dir->i_mtime = current_time(dir);
+	mark_inode_dirty(dir);
+	inode_dec_link_count(inode);
+	inode->i_ctime = dir->i_ctime;
+	retval = 0;
+
+end_unlink:
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+
+out:
+	return retval;
+}
+
+static int udf_symlink(struct inode *dir, struct dentry *dentry,
+		       const char *symname)
+{
+	struct inode *inode = udf_new_inode(dir, S_IFLNK | 0777);
+	struct pathComponent *pc;
+	const char *compstart;
+	struct extent_position epos = {};
+	int eoffset, elen = 0;
+	uint8_t *ea;
+	int err;
+	udf_pblk_t block;
+	unsigned char *name = NULL;
+	int namelen;
+	struct udf_inode_info *iinfo;
+	struct super_block *sb = dir->i_sb;
+
+	if (IS_ERR(inode))
+		return PTR_ERR(inode);
+
+	iinfo = UDF_I(inode);
+	down_write(&iinfo->i_data_sem);
+	name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS);
+	if (!name) {
+		err = -ENOMEM;
+		goto out_no_entry;
+	}
+
+	inode->i_data.a_ops = &udf_symlink_aops;
+	inode->i_op = &udf_symlink_inode_operations;
+	inode_nohighmem(inode);
+
+	if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+		struct kernel_lb_addr eloc;
+		uint32_t bsize;
+
+		block = udf_new_block(sb, inode,
+				iinfo->i_location.partitionReferenceNum,
+				iinfo->i_location.logicalBlockNum, &err);
+		if (!block)
+			goto out_no_entry;
+		epos.block = iinfo->i_location;
+		epos.offset = udf_file_entry_alloc_offset(inode);
+		epos.bh = NULL;
+		eloc.logicalBlockNum = block;
+		eloc.partitionReferenceNum =
+				iinfo->i_location.partitionReferenceNum;
+		bsize = sb->s_blocksize;
+		iinfo->i_lenExtents = bsize;
+		udf_add_aext(inode, &epos, &eloc, bsize, 0);
+		brelse(epos.bh);
+
+		block = udf_get_pblock(sb, block,
+				iinfo->i_location.partitionReferenceNum,
+				0);
+		epos.bh = udf_tgetblk(sb, block);
+		lock_buffer(epos.bh);
+		memset(epos.bh->b_data, 0x00, bsize);
+		set_buffer_uptodate(epos.bh);
+		unlock_buffer(epos.bh);
+		mark_buffer_dirty_inode(epos.bh, inode);
+		ea = epos.bh->b_data + udf_ext0_offset(inode);
+	} else
+		ea = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
+
+	eoffset = 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;
+		elen += sizeof(struct pathComponent);
+	}
+
+	err = -ENAMETOOLONG;
+
+	while (*symname) {
+		if (elen + sizeof(struct pathComponent) > eoffset)
+			goto out_no_entry;
+
+		pc = (struct pathComponent *)(ea + elen);
+
+		compstart = 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) {
+			namelen = udf_put_filename(sb, compstart,
+						   symname - compstart,
+						   name, UDF_NAME_LEN_CS0);
+			if (!namelen)
+				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 == '/');
+		}
+	}
+
+	brelse(epos.bh);
+	inode->i_size = elen;
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+		iinfo->i_lenAlloc = inode->i_size;
+	else
+		udf_truncate_tail_extent(inode);
+	mark_inode_dirty(inode);
+	up_write(&iinfo->i_data_sem);
+
+	err = udf_add_nondir(dentry, inode);
+out:
+	kfree(name);
+	return err;
+
+out_no_entry:
+	up_write(&iinfo->i_data_sem);
+	inode_dec_link_count(inode);
+	discard_new_inode(inode);
+	goto out;
+}
+
+static int udf_link(struct dentry *old_dentry, struct inode *dir,
+		    struct dentry *dentry)
+{
+	struct inode *inode = d_inode(old_dentry);
+	struct udf_fileident_bh fibh;
+	struct fileIdentDesc cfi, *fi;
+	int err;
+
+	fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);
+	if (!fi) {
+		return err;
+	}
+	cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+	cfi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location);
+	if (UDF_SB(inode->i_sb)->s_lvid_bh) {
+		*(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+			cpu_to_le32(lvid_get_unique_id(inode->i_sb));
+	}
+	udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+	if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+		mark_inode_dirty(dir);
+
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+	inc_nlink(inode);
+	inode->i_ctime = current_time(inode);
+	mark_inode_dirty(inode);
+	dir->i_ctime = dir->i_mtime = current_time(dir);
+	mark_inode_dirty(dir);
+	ihold(inode);
+	d_instantiate(dentry, inode);
+
+	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,
+		      unsigned int flags)
+{
+	struct inode *old_inode = d_inode(old_dentry);
+	struct inode *new_inode = d_inode(new_dentry);
+	struct udf_fileident_bh ofibh, nfibh;
+	struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL;
+	struct fileIdentDesc ocfi, ncfi;
+	struct buffer_head *dir_bh = NULL;
+	int retval = -ENOENT;
+	struct kernel_lb_addr tloc;
+	struct udf_inode_info *old_iinfo = UDF_I(old_inode);
+
+	if (flags & ~RENAME_NOREPLACE)
+		return -EINVAL;
+
+	ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi);
+	if (IS_ERR(ofi)) {
+		retval = PTR_ERR(ofi);
+		goto end_rename;
+	}
+
+	if (ofibh.sbh != ofibh.ebh)
+		brelse(ofibh.ebh);
+
+	brelse(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->d_name, &nfibh, &ncfi);
+	if (IS_ERR(nfi)) {
+		retval = PTR_ERR(nfi);
+		goto end_rename;
+	}
+	if (nfi && !new_inode) {
+		if (nfibh.sbh != nfibh.ebh)
+			brelse(nfibh.ebh);
+		brelse(nfibh.sbh);
+		nfi = NULL;
+	}
+	if (S_ISDIR(old_inode->i_mode)) {
+		int offset = udf_ext0_offset(old_inode);
+
+		if (new_inode) {
+			retval = -ENOTEMPTY;
+			if (!empty_dir(new_inode))
+				goto end_rename;
+		}
+		retval = -EIO;
+		if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+			dir_fi = udf_get_fileident(
+					old_iinfo->i_ext.i_data -
+					  (old_iinfo->i_efe ?
+					   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;
+	}
+	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_time(old_inode);
+	mark_inode_dirty(old_inode);
+
+	/*
+	 * ok, that's it
+	 */
+	ncfi.fileVersionNum = ocfi.fileVersionNum;
+	ncfi.fileCharacteristics = ocfi.fileCharacteristics;
+	memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(ocfi.icb));
+	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->d_name, &ofibh, &ocfi);
+	udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
+
+	if (new_inode) {
+		new_inode->i_ctime = current_time(new_inode);
+		inode_dec_link_count(new_inode);
+	}
+	old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
+	new_dir->i_ctime = new_dir->i_mtime = current_time(new_dir);
+	mark_inode_dirty(old_dir);
+	mark_inode_dirty(new_dir);
+
+	if (dir_fi) {
+		dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location);
+		udf_update_tag((char *)dir_fi, udf_dir_entry_len(dir_fi));
+		if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+			mark_inode_dirty(old_inode);
+		else
+			mark_buffer_dirty_inode(dir_bh, old_inode);
+
+		inode_dec_link_count(old_dir);
+		if (new_inode)
+			inode_dec_link_count(new_inode);
+		else {
+			inc_nlink(new_dir);
+			mark_inode_dirty(new_dir);
+		}
+	}
+
+	if (ofi) {
+		if (ofibh.sbh != ofibh.ebh)
+			brelse(ofibh.ebh);
+		brelse(ofibh.sbh);
+	}
+
+	retval = 0;
+
+end_rename:
+	brelse(dir_bh);
+	if (nfi) {
+		if (nfibh.sbh != nfibh.ebh)
+			brelse(nfibh.ebh);
+		brelse(nfibh.sbh);
+	}
+
+	return retval;
+}
+
+static struct dentry *udf_get_parent(struct dentry *child)
+{
+	struct kernel_lb_addr tloc;
+	struct inode *inode = NULL;
+	struct qstr dotdot = QSTR_INIT("..", 2);
+	struct fileIdentDesc cfi;
+	struct udf_fileident_bh fibh;
+
+	if (!udf_find_entry(d_inode(child), &dotdot, &fibh, &cfi))
+		return ERR_PTR(-EACCES);
+
+	if (fibh.sbh != fibh.ebh)
+		brelse(fibh.ebh);
+	brelse(fibh.sbh);
+
+	tloc = lelb_to_cpu(cfi.icb.extLocation);
+	inode = udf_iget(child->d_sb, &tloc);
+	if (IS_ERR(inode))
+		return ERR_CAST(inode);
+
+	return d_obtain_alias(inode);
+}
+
+
+static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block,
+					u16 partref, __u32 generation)
+{
+	struct inode *inode;
+	struct kernel_lb_addr loc;
+
+	if (block == 0)
+		return ERR_PTR(-ESTALE);
+
+	loc.logicalBlockNum = block;
+	loc.partitionReferenceNum = partref;
+	inode = udf_iget(sb, &loc);
+
+	if (IS_ERR(inode))
+		return ERR_CAST(inode);
+
+	if (generation && inode->i_generation != generation) {
+		iput(inode);
+		return ERR_PTR(-ESTALE);
+	}
+	return d_obtain_alias(inode);
+}
+
+static struct dentry *udf_fh_to_dentry(struct super_block *sb,
+				       struct fid *fid, int fh_len, int fh_type)
+{
+	if (fh_len < 3 ||
+	    (fh_type != FILEID_UDF_WITH_PARENT &&
+	     fh_type != FILEID_UDF_WITHOUT_PARENT))
+		return NULL;
+
+	return udf_nfs_get_inode(sb, fid->udf.block, fid->udf.partref,
+			fid->udf.generation);
+}
+
+static struct dentry *udf_fh_to_parent(struct super_block *sb,
+				       struct fid *fid, int fh_len, int fh_type)
+{
+	if (fh_len < 5 || fh_type != FILEID_UDF_WITH_PARENT)
+		return NULL;
+
+	return udf_nfs_get_inode(sb, fid->udf.parent_block,
+				 fid->udf.parent_partref,
+				 fid->udf.parent_generation);
+}
+static int udf_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
+			 struct inode *parent)
+{
+	int len = *lenp;
+	struct kernel_lb_addr location = UDF_I(inode)->i_location;
+	struct fid *fid = (struct fid *)fh;
+	int type = FILEID_UDF_WITHOUT_PARENT;
+
+	if (parent && (len < 5)) {
+		*lenp = 5;
+		return FILEID_INVALID;
+	} else if (len < 3) {
+		*lenp = 3;
+		return FILEID_INVALID;
+	}
+
+	*lenp = 3;
+	fid->udf.block = location.logicalBlockNum;
+	fid->udf.partref = location.partitionReferenceNum;
+	fid->udf.parent_partref = 0;
+	fid->udf.generation = inode->i_generation;
+
+	if (parent) {
+		location = UDF_I(parent)->i_location;
+		fid->udf.parent_block = location.logicalBlockNum;
+		fid->udf.parent_partref = location.partitionReferenceNum;
+		fid->udf.parent_generation = inode->i_generation;
+		*lenp = 5;
+		type = FILEID_UDF_WITH_PARENT;
+	}
+
+	return type;
+}
+
+const struct export_operations udf_export_ops = {
+	.encode_fh	= udf_encode_fh,
+	.fh_to_dentry   = udf_fh_to_dentry,
+	.fh_to_parent   = udf_fh_to_parent,
+	.get_parent     = udf_get_parent,
+};
+
+const 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,
+	.tmpfile			= udf_tmpfile,
+};
diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h
new file mode 100644
index 0000000..a4da59e
--- /dev/null
+++ b/fs/udf/osta_udf.h
@@ -0,0 +1,279 @@
+/*
+ * 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];
+} __packed;
+
+struct impIdentSuffix {
+	uint8_t		OSClass;
+	uint8_t		OSIdentifier;
+	uint8_t		reserved[6];
+} __packed;
+
+struct appIdentSuffix {
+	uint8_t		impUse[8];
+} __packed;
+
+/* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */
+/* Implementation Use (UDF 2.50 2.2.6.4) */
+struct logicalVolIntegrityDescImpUse {
+	struct regid	impIdent;
+	__le32		numFiles;
+	__le32		numDirs;
+	__le16		minUDFReadRev;
+	__le16		minUDFWriteRev;
+	__le16		maxUDFWriteRev;
+	uint8_t		impUse[0];
+} __packed;
+
+/* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */
+/* Implementation Use (UDF 2.50 2.2.7.2) */
+struct impUseVolDescImpUse {
+	struct charspec	LVICharset;
+	dstring		logicalVolIdent[128];
+	dstring		LVInfo1[36];
+	dstring		LVInfo2[36];
+	dstring		LVInfo3[36];
+	struct regid	impIdent;
+	uint8_t		impUse[128];
+} __packed;
+
+struct udfPartitionMap2 {
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	struct regid	partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+} __packed;
+
+/* Virtual Partition Map (UDF 2.50 2.2.8) */
+struct virtualPartitionMap {
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	struct regid	partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+	uint8_t		reserved2[24];
+} __packed;
+
+/* Sparable Partition Map (UDF 2.50 2.2.9) */
+struct sparablePartitionMap {
+	uint8_t partitionMapType;
+	uint8_t partitionMapLength;
+	uint8_t reserved1[2];
+	struct regid partIdent;
+	__le16 volSeqNum;
+	__le16 partitionNum;
+	__le16 packetLength;
+	uint8_t numSparingTables;
+	uint8_t reserved2[1];
+	__le32 sizeSparingTable;
+	__le32 locSparingTable[4];
+} __packed;
+
+/* Metadata Partition Map (UDF 2.4.0 2.2.10) */
+struct metadataPartitionMap {
+	uint8_t		partitionMapType;
+	uint8_t		partitionMapLength;
+	uint8_t		reserved1[2];
+	struct regid	partIdent;
+	__le16		volSeqNum;
+	__le16		partitionNum;
+	__le32		metadataFileLoc;
+	__le32		metadataMirrorFileLoc;
+	__le32		metadataBitmapFileLoc;
+	__le32		allocUnitSize;
+	__le16		alignUnitSize;
+	uint8_t		flags;
+	uint8_t		reserved2[5];
+} __packed;
+
+/* Virtual Allocation Table (UDF 1.5 2.2.10) */
+struct virtualAllocationTable15 {
+	__le32		VirtualSector[0];
+	struct regid	vatIdent;
+	__le32		previousVATICBLoc;
+} __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];
+} __packed;
+
+#define ICBTAG_FILE_TYPE_VAT20		0xF8U
+
+/* Sparing Table (UDF 2.50 2.2.12) */
+struct sparingEntry {
+	__le32		origLocation;
+	__le32		mappedLocation;
+} __packed;
+
+struct sparingTable {
+	struct tag	descTag;
+	struct regid	sparingIdent;
+	__le16		reallocationTableLen;
+	__le16		reserved;
+	__le32		sequenceNum;
+	struct sparingEntry
+			mapEntry[0];
+} __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 struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */
+struct allocDescImpUse {
+	__le16		flags;
+	uint8_t		impUse[4];
+} __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];
+} __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];
+} __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];
+} __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 0000000..090baff
--- /dev/null
+++ b/fs/udf/partition.c
@@ -0,0 +1,343 @@
+/*
+ * partition.c
+ *
+ * PURPOSE
+ *      Partition handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *      This file is distributed under the terms of the GNU General Public
+ *      License (GPL). Copies of the GPL can be obtained from:
+ *              ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *      Each contributing author retains all rights to their own work.
+ *
+ *  (C) 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/mutex.h>
+
+uint32_t udf_get_pblock(struct super_block *sb, uint32_t block,
+			uint16_t partition, uint32_t offset)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map;
+	if (partition >= sbi->s_partitions) {
+		udf_debug("block=%u, partition=%u, offset=%u: invalid partition\n",
+			  block, partition, offset);
+		return 0xFFFFFFFF;
+	}
+	map = &sbi->s_partmaps[partition];
+	if (map->s_partition_func)
+		return map->s_partition_func(sb, block, partition, offset);
+	else
+		return map->s_partition_root + 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;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map;
+	struct udf_virtual_data *vdata;
+	struct udf_inode_info *iinfo = UDF_I(sbi->s_vat_inode);
+
+	map = &sbi->s_partmaps[partition];
+	vdata = &map->s_type_specific.s_virtual;
+
+	if (block > vdata->s_num_entries) {
+		udf_debug("Trying to access block beyond end of VAT (%u max %u)\n",
+			  block, vdata->s_num_entries);
+		return 0xFFFFFFFF;
+	}
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+		loc = le32_to_cpu(((__le32 *)(iinfo->i_ext.i_data +
+			vdata->s_start_offset))[block]);
+		goto translate;
+	}
+	index = (sb->s_blocksize - vdata->s_start_offset) / sizeof(uint32_t);
+	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 = vdata->s_start_offset / sizeof(uint32_t) + block;
+	}
+
+	loc = udf_block_map(sbi->s_vat_inode, newblock);
+
+	bh = sb_bread(sb, loc);
+	if (!bh) {
+		udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%u,%u) VAT: %u[%u]\n",
+			  sb, block, partition, loc, index);
+		return 0xFFFFFFFF;
+	}
+
+	loc = le32_to_cpu(((__le32 *)bh->b_data)[index]);
+
+	brelse(bh);
+
+translate:
+	if (iinfo->i_location.partitionReferenceNum == partition) {
+		udf_debug("recursive call to udf_get_pblock!\n");
+		return 0xFFFFFFFF;
+	}
+
+	return udf_get_pblock(sb, loc,
+			      iinfo->i_location.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;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map;
+	uint32_t packet;
+	struct udf_sparing_data *sdata;
+
+	map = &sbi->s_partmaps[partition];
+	sdata = &map->s_type_specific.s_sparing;
+	packet = (block + offset) & ~(sdata->s_packet_len - 1);
+
+	for (i = 0; i < 4; i++) {
+		if (sdata->s_spar_map[i] != NULL) {
+			st = (struct sparingTable *)
+					sdata->s_spar_map[i]->b_data;
+			break;
+		}
+	}
+
+	if (st) {
+		for (i = 0; i < le16_to_cpu(st->reallocationTableLen); i++) {
+			struct sparingEntry *entry = &st->mapEntry[i];
+			u32 origLoc = le32_to_cpu(entry->origLocation);
+			if (origLoc >= 0xFFFFFFF0)
+				break;
+			else if (origLoc == packet)
+				return le32_to_cpu(entry->mappedLocation) +
+					((block + offset) &
+						(sdata->s_packet_len - 1));
+			else if (origLoc > packet)
+				break;
+		}
+	}
+
+	return map->s_partition_root + 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;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	u16 reallocationTableLen;
+	struct buffer_head *bh;
+	int ret = 0;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	for (i = 0; i < sbi->s_partitions; i++) {
+		struct udf_part_map *map = &sbi->s_partmaps[i];
+		if (old_block > map->s_partition_root &&
+		    old_block < map->s_partition_root + map->s_partition_len) {
+			sdata = &map->s_type_specific.s_sparing;
+			packet = (old_block - map->s_partition_root) &
+						~(sdata->s_packet_len - 1);
+
+			for (j = 0; j < 4; j++)
+				if (sdata->s_spar_map[j] != NULL) {
+					st = (struct sparingTable *)
+						sdata->s_spar_map[j]->b_data;
+					break;
+				}
+
+			if (!st) {
+				ret = 1;
+				goto out;
+			}
+
+			reallocationTableLen =
+					le16_to_cpu(st->reallocationTableLen);
+			for (k = 0; k < reallocationTableLen; k++) {
+				struct sparingEntry *entry = &st->mapEntry[k];
+				u32 origLoc = le32_to_cpu(entry->origLocation);
+
+				if (origLoc == 0xFFFFFFFF) {
+					for (; j < 4; j++) {
+						int len;
+						bh = sdata->s_spar_map[j];
+						if (!bh)
+							continue;
+
+						st = (struct sparingTable *)
+								bh->b_data;
+						entry->origLocation =
+							cpu_to_le32(packet);
+						len =
+						  sizeof(struct sparingTable) +
+						  reallocationTableLen *
+						  sizeof(struct sparingEntry);
+						udf_update_tag((char *)st, len);
+						mark_buffer_dirty(bh);
+					}
+					*new_block = le32_to_cpu(
+							entry->mappedLocation) +
+						     ((old_block -
+							map->s_partition_root) &
+						     (sdata->s_packet_len - 1));
+					ret = 0;
+					goto out;
+				} else if (origLoc == packet) {
+					*new_block = le32_to_cpu(
+							entry->mappedLocation) +
+						     ((old_block -
+							map->s_partition_root) &
+						     (sdata->s_packet_len - 1));
+					ret = 0;
+					goto out;
+				} else if (origLoc > packet)
+					break;
+			}
+
+			for (l = k; l < reallocationTableLen; l++) {
+				struct sparingEntry *entry = &st->mapEntry[l];
+				u32 origLoc = le32_to_cpu(entry->origLocation);
+
+				if (origLoc != 0xFFFFFFFF)
+					continue;
+
+				for (; j < 4; j++) {
+					bh = sdata->s_spar_map[j];
+					if (!bh)
+						continue;
+
+					st = (struct sparingTable *)bh->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) +
+						reallocationTableLen *
+						sizeof(struct sparingEntry));
+					mark_buffer_dirty(bh);
+				}
+				*new_block =
+					le32_to_cpu(
+					      st->mapEntry[k].mappedLocation) +
+					((old_block - map->s_partition_root) &
+					 (sdata->s_packet_len - 1));
+				ret = 0;
+				goto out;
+			}
+
+			ret = 1;
+			goto out;
+		} /* if old_block */
+	}
+
+	if (i == sbi->s_partitions) {
+		/* outside of partitions */
+		/* for now, fail =) */
+		ret = 1;
+	}
+
+out:
+	mutex_unlock(&sbi->s_alloc_mutex);
+	return ret;
+}
+
+static uint32_t udf_try_read_meta(struct inode *inode, uint32_t block,
+					uint16_t partition, uint32_t offset)
+{
+	struct super_block *sb = inode->i_sb;
+	struct udf_part_map *map;
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	sector_t ext_offset;
+	struct extent_position epos = {};
+	uint32_t phyblock;
+
+	if (inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset) !=
+						(EXT_RECORDED_ALLOCATED >> 30))
+		phyblock = 0xFFFFFFFF;
+	else {
+		map = &UDF_SB(sb)->s_partmaps[partition];
+		/* map to sparable/physical partition desc */
+		phyblock = udf_get_pblock(sb, eloc.logicalBlockNum,
+			map->s_type_specific.s_metadata.s_phys_partition_ref,
+			ext_offset + offset);
+	}
+
+	brelse(epos.bh);
+	return phyblock;
+}
+
+uint32_t udf_get_pblock_meta25(struct super_block *sb, uint32_t block,
+				uint16_t partition, uint32_t offset)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map;
+	struct udf_meta_data *mdata;
+	uint32_t retblk;
+	struct inode *inode;
+
+	udf_debug("READING from METADATA\n");
+
+	map = &sbi->s_partmaps[partition];
+	mdata = &map->s_type_specific.s_metadata;
+	inode = mdata->s_metadata_fe ? : mdata->s_mirror_fe;
+
+	if (!inode)
+		return 0xFFFFFFFF;
+
+	retblk = udf_try_read_meta(inode, block, partition, offset);
+	if (retblk == 0xFFFFFFFF && mdata->s_metadata_fe) {
+		udf_warn(sb, "error reading from METADATA, trying to read from MIRROR\n");
+		if (!(mdata->s_flags & MF_MIRROR_FE_LOADED)) {
+			mdata->s_mirror_fe = udf_find_metadata_inode_efe(sb,
+				mdata->s_mirror_file_loc,
+				mdata->s_phys_partition_ref);
+			if (IS_ERR(mdata->s_mirror_fe))
+				mdata->s_mirror_fe = NULL;
+			mdata->s_flags |= MF_MIRROR_FE_LOADED;
+		}
+
+		inode = mdata->s_mirror_fe;
+		if (!inode)
+			return 0xFFFFFFFF;
+		retblk = udf_try_read_meta(inode, block, partition, offset);
+	}
+
+	return retblk;
+}
diff --git a/fs/udf/super.c b/fs/udf/super.c
new file mode 100644
index 0000000..c495db7
--- /dev/null
+++ b/fs/udf/super.c
@@ -0,0 +1,2465 @@
+/*
+ * 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/
+ *
+ * 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/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/vfs.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/mount.h>
+#include <linux/seq_file.h>
+#include <linux/bitmap.h>
+#include <linux/crc-itu-t.h>
+#include <linux/log2.h>
+#include <asm/byteorder.h>
+
+#include "udf_sb.h"
+#include "udf_i.h"
+
+#include <linux/init.h>
+#include <linux/uaccess.h>
+
+enum {
+	VDS_POS_PRIMARY_VOL_DESC,
+	VDS_POS_UNALLOC_SPACE_DESC,
+	VDS_POS_LOGICAL_VOL_DESC,
+	VDS_POS_IMP_USE_VOL_DESC,
+	VDS_POS_LENGTH
+};
+
+#define VSD_FIRST_SECTOR_OFFSET		32768
+#define VSD_MAX_SECTOR_OFFSET		0x800000
+
+/*
+ * Maximum number of Terminating Descriptor / Logical Volume Integrity
+ * Descriptor redirections. The chosen numbers are arbitrary - just that we
+ * hopefully don't limit any real use of rewritten inode on write-once media
+ * but avoid looping for too long on corrupted media.
+ */
+#define UDF_MAX_TD_NESTING 64
+#define UDF_MAX_LVID_NESTING 1000
+
+enum { UDF_MAX_LINKS = 0xffff };
+
+/* 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 int udf_sync_fs(struct super_block *, int);
+static int udf_remount_fs(struct super_block *, int *, char *);
+static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad);
+static int udf_find_fileset(struct super_block *, struct kernel_lb_addr *,
+			    struct kernel_lb_addr *);
+static void udf_load_fileset(struct super_block *, struct buffer_head *,
+			     struct kernel_lb_addr *);
+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 dentry *, struct kstatfs *);
+static int udf_show_options(struct seq_file *, struct dentry *);
+
+struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb)
+{
+	struct logicalVolIntegrityDesc *lvid;
+	unsigned int partnum;
+	unsigned int offset;
+
+	if (!UDF_SB(sb)->s_lvid_bh)
+		return NULL;
+	lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data;
+	partnum = le32_to_cpu(lvid->numOfPartitions);
+	if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) -
+	     offsetof(struct logicalVolIntegrityDesc, impUse)) /
+	     (2 * sizeof(uint32_t)) < partnum) {
+		udf_err(sb, "Logical volume integrity descriptor corrupted "
+			"(numOfPartitions = %u)!\n", partnum);
+		return NULL;
+	}
+	/* The offset is to skip freeSpaceTable and sizeTable arrays */
+	offset = partnum * 2 * sizeof(uint32_t);
+	return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]);
+}
+
+/* UDF filesystem type */
+static struct dentry *udf_mount(struct file_system_type *fs_type,
+		      int flags, const char *dev_name, void *data)
+{
+	return mount_bdev(fs_type, flags, dev_name, data, udf_fill_super);
+}
+
+static struct file_system_type udf_fstype = {
+	.owner		= THIS_MODULE,
+	.name		= "udf",
+	.mount		= udf_mount,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+MODULE_ALIAS_FS("udf");
+
+static struct kmem_cache *udf_inode_cachep;
+
+static struct inode *udf_alloc_inode(struct super_block *sb)
+{
+	struct udf_inode_info *ei;
+	ei = kmem_cache_alloc(udf_inode_cachep, GFP_KERNEL);
+	if (!ei)
+		return NULL;
+
+	ei->i_unique = 0;
+	ei->i_lenExtents = 0;
+	ei->i_next_alloc_block = 0;
+	ei->i_next_alloc_goal = 0;
+	ei->i_strat4096 = 0;
+	init_rwsem(&ei->i_data_sem);
+	ei->cached_extent.lstart = -1;
+	spin_lock_init(&ei->i_extent_cache_lock);
+
+	return &ei->vfs_inode;
+}
+
+static void udf_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	kmem_cache_free(udf_inode_cachep, UDF_I(inode));
+}
+
+static void udf_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, udf_i_callback);
+}
+
+static void init_once(void *foo)
+{
+	struct udf_inode_info *ei = (struct udf_inode_info *)foo;
+
+	ei->i_ext.i_data = NULL;
+	inode_init_once(&ei->vfs_inode);
+}
+
+static int __init init_inodecache(void)
+{
+	udf_inode_cachep = kmem_cache_create("udf_inode_cache",
+					     sizeof(struct udf_inode_info),
+					     0, (SLAB_RECLAIM_ACCOUNT |
+						 SLAB_MEM_SPREAD |
+						 SLAB_ACCOUNT),
+					     init_once);
+	if (!udf_inode_cachep)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	/*
+	 * Make sure all delayed rcu free inodes are flushed before we
+	 * destroy cache.
+	 */
+	rcu_barrier();
+	kmem_cache_destroy(udf_inode_cachep);
+}
+
+/* Superblock operations */
+static const struct super_operations udf_sb_ops = {
+	.alloc_inode	= udf_alloc_inode,
+	.destroy_inode	= udf_destroy_inode,
+	.write_inode	= udf_write_inode,
+	.evict_inode	= udf_evict_inode,
+	.put_super	= udf_put_super,
+	.sync_fs	= udf_sync_fs,
+	.statfs		= udf_statfs,
+	.remount_fs	= udf_remount_fs,
+	.show_options	= udf_show_options,
+};
+
+struct udf_options {
+	unsigned char novrs;
+	unsigned int blocksize;
+	unsigned int session;
+	unsigned int lastblock;
+	unsigned int anchor;
+	unsigned int flags;
+	umode_t umask;
+	kgid_t gid;
+	kuid_t uid;
+	umode_t fmode;
+	umode_t dmode;
+	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();
+}
+
+static int udf_sb_alloc_partition_maps(struct super_block *sb, u32 count)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+
+	sbi->s_partmaps = kcalloc(count, sizeof(*sbi->s_partmaps), GFP_KERNEL);
+	if (!sbi->s_partmaps) {
+		sbi->s_partitions = 0;
+		return -ENOMEM;
+	}
+
+	sbi->s_partitions = count;
+	return 0;
+}
+
+static void udf_sb_free_bitmap(struct udf_bitmap *bitmap)
+{
+	int i;
+	int nr_groups = bitmap->s_nr_groups;
+
+	for (i = 0; i < nr_groups; i++)
+		if (bitmap->s_block_bitmap[i])
+			brelse(bitmap->s_block_bitmap[i]);
+
+	kvfree(bitmap);
+}
+
+static void udf_free_partition(struct udf_part_map *map)
+{
+	int i;
+	struct udf_meta_data *mdata;
+
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE)
+		iput(map->s_uspace.s_table);
+	if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE)
+		iput(map->s_fspace.s_table);
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
+		udf_sb_free_bitmap(map->s_uspace.s_bitmap);
+	if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP)
+		udf_sb_free_bitmap(map->s_fspace.s_bitmap);
+	if (map->s_partition_type == UDF_SPARABLE_MAP15)
+		for (i = 0; i < 4; i++)
+			brelse(map->s_type_specific.s_sparing.s_spar_map[i]);
+	else if (map->s_partition_type == UDF_METADATA_MAP25) {
+		mdata = &map->s_type_specific.s_metadata;
+		iput(mdata->s_metadata_fe);
+		mdata->s_metadata_fe = NULL;
+
+		iput(mdata->s_mirror_fe);
+		mdata->s_mirror_fe = NULL;
+
+		iput(mdata->s_bitmap_fe);
+		mdata->s_bitmap_fe = NULL;
+	}
+}
+
+static void udf_sb_free_partitions(struct super_block *sb)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int i;
+
+	if (!sbi->s_partmaps)
+		return;
+	for (i = 0; i < sbi->s_partitions; i++)
+		udf_free_partition(&sbi->s_partmaps[i]);
+	kfree(sbi->s_partmaps);
+	sbi->s_partmaps = NULL;
+}
+
+static int udf_show_options(struct seq_file *seq, struct dentry *root)
+{
+	struct super_block *sb = root->d_sb;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+
+	if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT))
+		seq_puts(seq, ",nostrict");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_BLOCKSIZE_SET))
+		seq_printf(seq, ",bs=%lu", sb->s_blocksize);
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
+		seq_puts(seq, ",unhide");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
+		seq_puts(seq, ",undelete");
+	if (!UDF_QUERY_FLAG(sb, UDF_FLAG_USE_AD_IN_ICB))
+		seq_puts(seq, ",noadinicb");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_USE_SHORT_AD))
+		seq_puts(seq, ",shortad");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_FORGET))
+		seq_puts(seq, ",uid=forget");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_FORGET))
+		seq_puts(seq, ",gid=forget");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET))
+		seq_printf(seq, ",uid=%u", from_kuid(&init_user_ns, sbi->s_uid));
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET))
+		seq_printf(seq, ",gid=%u", from_kgid(&init_user_ns, sbi->s_gid));
+	if (sbi->s_umask != 0)
+		seq_printf(seq, ",umask=%ho", sbi->s_umask);
+	if (sbi->s_fmode != UDF_INVALID_MODE)
+		seq_printf(seq, ",mode=%ho", sbi->s_fmode);
+	if (sbi->s_dmode != UDF_INVALID_MODE)
+		seq_printf(seq, ",dmode=%ho", sbi->s_dmode);
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_SESSION_SET))
+		seq_printf(seq, ",session=%d", sbi->s_session);
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_LASTBLOCK_SET))
+		seq_printf(seq, ",lastblock=%u", sbi->s_last_block);
+	if (sbi->s_anchor != 0)
+		seq_printf(seq, ",anchor=%u", sbi->s_anchor);
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8))
+		seq_puts(seq, ",utf8");
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP) && sbi->s_nls_map)
+		seq_printf(seq, ",iocharset=%s", sbi->s_nls_map->charset);
+
+	return 0;
+}
+
+/*
+ * udf_parse_options
+ *
+ * PURPOSE
+ *	Parse mount options.
+ *
+ * DESCRIPTION
+ *	The following mount options are supported:
+ *
+ *	gid=		Set the default group.
+ *	umask=		Set the default umask.
+ *	mode=		Set the default file permissions.
+ *	dmode=		Set the default directory permissions.
+ *	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, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore,
+	Opt_fmode, Opt_dmode
+};
+
+static const 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_uforget,	"uid=forget"},
+	{Opt_uignore,	"uid=ignore"},
+	{Opt_gforget,	"gid=forget"},
+	{Opt_gignore,	"gid=ignore"},
+	{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_fmode,     "mode=%o"},
+	{Opt_dmode,     "dmode=%o"},
+	{Opt_err,	NULL}
+};
+
+static int udf_parse_options(char *options, struct udf_options *uopt,
+			     bool remount)
+{
+	char *p;
+	int option;
+
+	uopt->novrs = 0;
+	uopt->session = 0xFFFFFFFF;
+	uopt->lastblock = 0;
+	uopt->anchor = 0;
+
+	if (!options)
+		return 1;
+
+	while ((p = strsep(&options, ",")) != NULL) {
+		substring_t args[MAX_OPT_ARGS];
+		int token;
+		unsigned n;
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+		case Opt_novrs:
+			uopt->novrs = 1;
+			break;
+		case Opt_bs:
+			if (match_int(&args[0], &option))
+				return 0;
+			n = option;
+			if (n != 512 && n != 1024 && n != 2048 && n != 4096)
+				return 0;
+			uopt->blocksize = n;
+			uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET);
+			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 = make_kgid(current_user_ns(), option);
+			if (!gid_valid(uopt->gid))
+				return 0;
+			uopt->flags |= (1 << UDF_FLAG_GID_SET);
+			break;
+		case Opt_uid:
+			if (match_int(args, &option))
+				return 0;
+			uopt->uid = make_kuid(current_user_ns(), option);
+			if (!uid_valid(uopt->uid))
+				return 0;
+			uopt->flags |= (1 << UDF_FLAG_UID_SET);
+			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;
+			if (!remount)
+				uopt->flags |= (1 << UDF_FLAG_SESSION_SET);
+			break;
+		case Opt_lastblock:
+			if (match_int(args, &option))
+				return 0;
+			uopt->lastblock = option;
+			if (!remount)
+				uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET);
+			break;
+		case Opt_anchor:
+			if (match_int(args, &option))
+				return 0;
+			uopt->anchor = option;
+			break;
+		case Opt_volume:
+		case Opt_partition:
+		case Opt_fileset:
+		case Opt_rootdir:
+			/* Ignored (never implemented properly) */
+			break;
+		case Opt_utf8:
+			uopt->flags |= (1 << UDF_FLAG_UTF8);
+			break;
+		case Opt_iocharset:
+			if (!remount) {
+				if (uopt->nls_map)
+					unload_nls(uopt->nls_map);
+				uopt->nls_map = load_nls(args[0].from);
+				uopt->flags |= (1 << UDF_FLAG_NLS_MAP);
+			}
+			break;
+		case Opt_uforget:
+			uopt->flags |= (1 << UDF_FLAG_UID_FORGET);
+			break;
+		case Opt_uignore:
+		case Opt_gignore:
+			/* These options are superseeded by uid=<number> */
+			break;
+		case Opt_gforget:
+			uopt->flags |= (1 << UDF_FLAG_GID_FORGET);
+			break;
+		case Opt_fmode:
+			if (match_octal(args, &option))
+				return 0;
+			uopt->fmode = option & 0777;
+			break;
+		case Opt_dmode:
+			if (match_octal(args, &option))
+				return 0;
+			uopt->dmode = option & 0777;
+			break;
+		default:
+			pr_err("bad mount option \"%s\" or missing value\n", p);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+	struct udf_options uopt;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int error = 0;
+
+	if (!(*flags & SB_RDONLY) && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT))
+		return -EACCES;
+
+	sync_filesystem(sb);
+
+	uopt.flags = sbi->s_flags;
+	uopt.uid   = sbi->s_uid;
+	uopt.gid   = sbi->s_gid;
+	uopt.umask = sbi->s_umask;
+	uopt.fmode = sbi->s_fmode;
+	uopt.dmode = sbi->s_dmode;
+	uopt.nls_map = NULL;
+
+	if (!udf_parse_options(options, &uopt, true))
+		return -EINVAL;
+
+	write_lock(&sbi->s_cred_lock);
+	sbi->s_flags = uopt.flags;
+	sbi->s_uid   = uopt.uid;
+	sbi->s_gid   = uopt.gid;
+	sbi->s_umask = uopt.umask;
+	sbi->s_fmode = uopt.fmode;
+	sbi->s_dmode = uopt.dmode;
+	write_unlock(&sbi->s_cred_lock);
+
+	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
+		goto out_unlock;
+
+	if (*flags & SB_RDONLY)
+		udf_close_lvid(sb);
+	else
+		udf_open_lvid(sb);
+
+out_unlock:
+	return error;
+}
+
+/* Check Volume Structure Descriptors (ECMA 167 2/9.1) */
+/* We also check any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */
+static loff_t udf_check_vsd(struct super_block *sb)
+{
+	struct volStructDesc *vsd = NULL;
+	loff_t sector = VSD_FIRST_SECTOR_OFFSET;
+	int sectorsize;
+	struct buffer_head *bh = NULL;
+	int nsr02 = 0;
+	int nsr03 = 0;
+	struct udf_sb_info *sbi;
+
+	sbi = UDF_SB(sb);
+	if (sb->s_blocksize < sizeof(struct volStructDesc))
+		sectorsize = sizeof(struct volStructDesc);
+	else
+		sectorsize = sb->s_blocksize;
+
+	sector += (((loff_t)sbi->s_session) << sb->s_blocksize_bits);
+
+	udf_debug("Starting at sector %u (%lu byte sectors)\n",
+		  (unsigned int)(sector >> sb->s_blocksize_bits),
+		  sb->s_blocksize);
+	/* Process the sequence (if applicable). The hard limit on the sector
+	 * offset is arbitrary, hopefully large enough so that all valid UDF
+	 * filesystems will be recognised. There is no mention of an upper
+	 * bound to the size of the volume recognition area in the standard.
+	 *  The limit will prevent the code to read all the sectors of a
+	 * specially crafted image (like a bluray disc full of CD001 sectors),
+	 * potentially causing minutes or even hours of uninterruptible I/O
+	 * activity. This actually happened with uninitialised SSD partitions
+	 * (all 0xFF) before the check for the limit and all valid IDs were
+	 * added */
+	for (; !nsr02 && !nsr03 && sector < VSD_MAX_SECTOR_OFFSET;
+	     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 (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001,
+				    VSD_STD_ID_LEN)) {
+			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))
+			; /* nothing */
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01,
+				    VSD_STD_ID_LEN)) {
+			brelse(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;
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BOOT2,
+				    VSD_STD_ID_LEN))
+			; /* nothing */
+		else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CDW02,
+				    VSD_STD_ID_LEN))
+			; /* nothing */
+		else {
+			/* invalid id : end of volume recognition area */
+			brelse(bh);
+			break;
+		}
+		brelse(bh);
+	}
+
+	if (nsr03)
+		return nsr03;
+	else if (nsr02)
+		return nsr02;
+	else if (!bh && sector - (sbi->s_session << sb->s_blocksize_bits) ==
+			VSD_FIRST_SECTOR_OFFSET)
+		return -1;
+	else
+		return 0;
+}
+
+static int udf_find_fileset(struct super_block *sb,
+			    struct kernel_lb_addr *fileset,
+			    struct kernel_lb_addr *root)
+{
+	struct buffer_head *bh = NULL;
+	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) {
+			brelse(bh);
+			return 1;
+		}
+
+		udf_debug("Fileset at block=%u, partition=%u\n",
+			  fileset->logicalBlockNum,
+			  fileset->partitionReferenceNum);
+
+		UDF_SB(sb)->s_partition = fileset->partitionReferenceNum;
+		udf_load_fileset(sb, bh, root);
+		brelse(bh);
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Load primary Volume Descriptor Sequence
+ *
+ * Return <0 on error, 0 on success. -EAGAIN is special meaning next sequence
+ * should be tried.
+ */
+static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
+{
+	struct primaryVolDesc *pvoldesc;
+	uint8_t *outstr;
+	struct buffer_head *bh;
+	uint16_t ident;
+	int ret = -ENOMEM;
+#ifdef UDFFS_DEBUG
+	struct timestamp *ts;
+#endif
+
+	outstr = kmalloc(128, GFP_NOFS);
+	if (!outstr)
+		return -ENOMEM;
+
+	bh = udf_read_tagged(sb, block, block, &ident);
+	if (!bh) {
+		ret = -EAGAIN;
+		goto out2;
+	}
+
+	if (ident != TAG_IDENT_PVD) {
+		ret = -EIO;
+		goto out_bh;
+	}
+
+	pvoldesc = (struct primaryVolDesc *)bh->b_data;
+
+	udf_disk_stamp_to_time(&UDF_SB(sb)->s_record_time,
+			      pvoldesc->recordingDateAndTime);
+#ifdef UDFFS_DEBUG
+	ts = &pvoldesc->recordingDateAndTime;
+	udf_debug("recording time %04u/%02u/%02u %02u:%02u (%x)\n",
+		  le16_to_cpu(ts->year), ts->month, ts->day, ts->hour,
+		  ts->minute, le16_to_cpu(ts->typeAndTimezone));
+#endif
+
+
+	ret = udf_dstrCS0toChar(sb, outstr, 31, pvoldesc->volIdent, 32);
+	if (ret < 0) {
+		strcpy(UDF_SB(sb)->s_volume_ident, "InvalidName");
+		pr_warn("incorrect volume identification, setting to "
+			"'InvalidName'\n");
+	} else {
+		strncpy(UDF_SB(sb)->s_volume_ident, outstr, ret);
+	}
+	udf_debug("volIdent[] = '%s'\n", UDF_SB(sb)->s_volume_ident);
+
+	ret = udf_dstrCS0toChar(sb, outstr, 127, pvoldesc->volSetIdent, 128);
+	if (ret < 0) {
+		ret = 0;
+		goto out_bh;
+	}
+	outstr[ret] = 0;
+	udf_debug("volSetIdent[] = '%s'\n", outstr);
+
+	ret = 0;
+out_bh:
+	brelse(bh);
+out2:
+	kfree(outstr);
+	return ret;
+}
+
+struct inode *udf_find_metadata_inode_efe(struct super_block *sb,
+					u32 meta_file_loc, u32 partition_ref)
+{
+	struct kernel_lb_addr addr;
+	struct inode *metadata_fe;
+
+	addr.logicalBlockNum = meta_file_loc;
+	addr.partitionReferenceNum = partition_ref;
+
+	metadata_fe = udf_iget_special(sb, &addr);
+
+	if (IS_ERR(metadata_fe)) {
+		udf_warn(sb, "metadata inode efe not found\n");
+		return metadata_fe;
+	}
+	if (UDF_I(metadata_fe)->i_alloc_type != ICBTAG_FLAG_AD_SHORT) {
+		udf_warn(sb, "metadata inode efe does not have short allocation descriptors!\n");
+		iput(metadata_fe);
+		return ERR_PTR(-EIO);
+	}
+
+	return metadata_fe;
+}
+
+static int udf_load_metadata_files(struct super_block *sb, int partition,
+				   int type1_index)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map;
+	struct udf_meta_data *mdata;
+	struct kernel_lb_addr addr;
+	struct inode *fe;
+
+	map = &sbi->s_partmaps[partition];
+	mdata = &map->s_type_specific.s_metadata;
+	mdata->s_phys_partition_ref = type1_index;
+
+	/* metadata address */
+	udf_debug("Metadata file location: block = %u part = %u\n",
+		  mdata->s_meta_file_loc, mdata->s_phys_partition_ref);
+
+	fe = udf_find_metadata_inode_efe(sb, mdata->s_meta_file_loc,
+					 mdata->s_phys_partition_ref);
+	if (IS_ERR(fe)) {
+		/* mirror file entry */
+		udf_debug("Mirror metadata file location: block = %u part = %u\n",
+			  mdata->s_mirror_file_loc, mdata->s_phys_partition_ref);
+
+		fe = udf_find_metadata_inode_efe(sb, mdata->s_mirror_file_loc,
+						 mdata->s_phys_partition_ref);
+
+		if (IS_ERR(fe)) {
+			udf_err(sb, "Both metadata and mirror metadata inode efe can not found\n");
+			return PTR_ERR(fe);
+		}
+		mdata->s_mirror_fe = fe;
+	} else
+		mdata->s_metadata_fe = fe;
+
+
+	/*
+	 * bitmap file entry
+	 * Note:
+	 * Load only if bitmap file location differs from 0xFFFFFFFF (DCN-5102)
+	*/
+	if (mdata->s_bitmap_file_loc != 0xFFFFFFFF) {
+		addr.logicalBlockNum = mdata->s_bitmap_file_loc;
+		addr.partitionReferenceNum = mdata->s_phys_partition_ref;
+
+		udf_debug("Bitmap file location: block = %u part = %u\n",
+			  addr.logicalBlockNum, addr.partitionReferenceNum);
+
+		fe = udf_iget_special(sb, &addr);
+		if (IS_ERR(fe)) {
+			if (sb_rdonly(sb))
+				udf_warn(sb, "bitmap inode efe not found but it's ok since the disc is mounted read-only\n");
+			else {
+				udf_err(sb, "bitmap inode efe not found and attempted read-write mount\n");
+				return PTR_ERR(fe);
+			}
+		} else
+			mdata->s_bitmap_fe = fe;
+	}
+
+	udf_debug("udf_load_metadata_files Ok\n");
+	return 0;
+}
+
+static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh,
+			     struct kernel_lb_addr *root)
+{
+	struct fileSetDesc *fset;
+
+	fset = (struct fileSetDesc *)bh->b_data;
+
+	*root = lelb_to_cpu(fset->rootDirectoryICB.extLocation);
+
+	UDF_SB(sb)->s_serial_number = le16_to_cpu(fset->descTag.tagSerialNum);
+
+	udf_debug("Rootdir at block=%u, partition=%u\n",
+		  root->logicalBlockNum, root->partitionReferenceNum);
+}
+
+int udf_compute_nr_groups(struct super_block *sb, u32 partition)
+{
+	struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
+	return DIV_ROUND_UP(map->s_partition_len +
+			    (sizeof(struct spaceBitmapDesc) << 3),
+			    sb->s_blocksize * 8);
+}
+
+static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index)
+{
+	struct udf_bitmap *bitmap;
+	int nr_groups;
+	int size;
+
+	nr_groups = udf_compute_nr_groups(sb, index);
+	size = sizeof(struct udf_bitmap) +
+		(sizeof(struct buffer_head *) * nr_groups);
+
+	if (size <= PAGE_SIZE)
+		bitmap = kzalloc(size, GFP_KERNEL);
+	else
+		bitmap = vzalloc(size); /* TODO: get rid of vzalloc */
+
+	if (!bitmap)
+		return NULL;
+
+	bitmap->s_nr_groups = nr_groups;
+	return bitmap;
+}
+
+static int udf_fill_partdesc_info(struct super_block *sb,
+		struct partitionDesc *p, int p_index)
+{
+	struct udf_part_map *map;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct partitionHeaderDesc *phd;
+
+	map = &sbi->s_partmaps[p_index];
+
+	map->s_partition_len = le32_to_cpu(p->partitionLength); /* blocks */
+	map->s_partition_root = le32_to_cpu(p->partitionStartingLocation);
+
+	if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_READ_ONLY))
+		map->s_partition_flags |= UDF_PART_FLAG_READ_ONLY;
+	if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_WRITE_ONCE))
+		map->s_partition_flags |= UDF_PART_FLAG_WRITE_ONCE;
+	if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_REWRITABLE))
+		map->s_partition_flags |= UDF_PART_FLAG_REWRITABLE;
+	if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_OVERWRITABLE))
+		map->s_partition_flags |= UDF_PART_FLAG_OVERWRITABLE;
+
+	udf_debug("Partition (%d type %x) starts at physical %u, block length %u\n",
+		  p_index, map->s_partition_type,
+		  map->s_partition_root, map->s_partition_len);
+
+	if (strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) &&
+	    strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03))
+		return 0;
+
+	phd = (struct partitionHeaderDesc *)p->partitionContentsUse;
+	if (phd->unallocSpaceTable.extLength) {
+		struct kernel_lb_addr loc = {
+			.logicalBlockNum = le32_to_cpu(
+				phd->unallocSpaceTable.extPosition),
+			.partitionReferenceNum = p_index,
+		};
+		struct inode *inode;
+
+		inode = udf_iget_special(sb, &loc);
+		if (IS_ERR(inode)) {
+			udf_debug("cannot load unallocSpaceTable (part %d)\n",
+				  p_index);
+			return PTR_ERR(inode);
+		}
+		map->s_uspace.s_table = inode;
+		map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_TABLE;
+		udf_debug("unallocSpaceTable (part %d) @ %lu\n",
+			  p_index, map->s_uspace.s_table->i_ino);
+	}
+
+	if (phd->unallocSpaceBitmap.extLength) {
+		struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index);
+		if (!bitmap)
+			return -ENOMEM;
+		map->s_uspace.s_bitmap = bitmap;
+		bitmap->s_extPosition = le32_to_cpu(
+				phd->unallocSpaceBitmap.extPosition);
+		map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_BITMAP;
+		udf_debug("unallocSpaceBitmap (part %d) @ %u\n",
+			  p_index, bitmap->s_extPosition);
+	}
+
+	if (phd->partitionIntegrityTable.extLength)
+		udf_debug("partitionIntegrityTable (part %d)\n", p_index);
+
+	if (phd->freedSpaceTable.extLength) {
+		struct kernel_lb_addr loc = {
+			.logicalBlockNum = le32_to_cpu(
+				phd->freedSpaceTable.extPosition),
+			.partitionReferenceNum = p_index,
+		};
+		struct inode *inode;
+
+		inode = udf_iget_special(sb, &loc);
+		if (IS_ERR(inode)) {
+			udf_debug("cannot load freedSpaceTable (part %d)\n",
+				  p_index);
+			return PTR_ERR(inode);
+		}
+		map->s_fspace.s_table = inode;
+		map->s_partition_flags |= UDF_PART_FLAG_FREED_TABLE;
+		udf_debug("freedSpaceTable (part %d) @ %lu\n",
+			  p_index, map->s_fspace.s_table->i_ino);
+	}
+
+	if (phd->freedSpaceBitmap.extLength) {
+		struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index);
+		if (!bitmap)
+			return -ENOMEM;
+		map->s_fspace.s_bitmap = bitmap;
+		bitmap->s_extPosition = le32_to_cpu(
+				phd->freedSpaceBitmap.extPosition);
+		map->s_partition_flags |= UDF_PART_FLAG_FREED_BITMAP;
+		udf_debug("freedSpaceBitmap (part %d) @ %u\n",
+			  p_index, bitmap->s_extPosition);
+	}
+	return 0;
+}
+
+static void udf_find_vat_block(struct super_block *sb, int p_index,
+			       int type1_index, sector_t start_block)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map = &sbi->s_partmaps[p_index];
+	sector_t vat_block;
+	struct kernel_lb_addr ino;
+	struct inode *inode;
+
+	/*
+	 * VAT file entry is in the last recorded block. Some broken disks have
+	 * it a few blocks before so try a bit harder...
+	 */
+	ino.partitionReferenceNum = type1_index;
+	for (vat_block = start_block;
+	     vat_block >= map->s_partition_root &&
+	     vat_block >= start_block - 3; vat_block--) {
+		ino.logicalBlockNum = vat_block - map->s_partition_root;
+		inode = udf_iget_special(sb, &ino);
+		if (!IS_ERR(inode)) {
+			sbi->s_vat_inode = inode;
+			break;
+		}
+	}
+}
+
+static int udf_load_vat(struct super_block *sb, int p_index, int type1_index)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct udf_part_map *map = &sbi->s_partmaps[p_index];
+	struct buffer_head *bh = NULL;
+	struct udf_inode_info *vati;
+	uint32_t pos;
+	struct virtualAllocationTable20 *vat20;
+	sector_t blocks = i_size_read(sb->s_bdev->bd_inode) >>
+			  sb->s_blocksize_bits;
+
+	udf_find_vat_block(sb, p_index, type1_index, sbi->s_last_block);
+	if (!sbi->s_vat_inode &&
+	    sbi->s_last_block != blocks - 1) {
+		pr_notice("Failed to read VAT inode from the last recorded block (%lu), retrying with the last block of the device (%lu).\n",
+			  (unsigned long)sbi->s_last_block,
+			  (unsigned long)blocks - 1);
+		udf_find_vat_block(sb, p_index, type1_index, blocks - 1);
+	}
+	if (!sbi->s_vat_inode)
+		return -EIO;
+
+	if (map->s_partition_type == UDF_VIRTUAL_MAP15) {
+		map->s_type_specific.s_virtual.s_start_offset = 0;
+		map->s_type_specific.s_virtual.s_num_entries =
+			(sbi->s_vat_inode->i_size - 36) >> 2;
+	} else if (map->s_partition_type == UDF_VIRTUAL_MAP20) {
+		vati = UDF_I(sbi->s_vat_inode);
+		if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+			pos = udf_block_map(sbi->s_vat_inode, 0);
+			bh = sb_bread(sb, pos);
+			if (!bh)
+				return -EIO;
+			vat20 = (struct virtualAllocationTable20 *)bh->b_data;
+		} else {
+			vat20 = (struct virtualAllocationTable20 *)
+							vati->i_ext.i_data;
+		}
+
+		map->s_type_specific.s_virtual.s_start_offset =
+			le16_to_cpu(vat20->lengthHeader);
+		map->s_type_specific.s_virtual.s_num_entries =
+			(sbi->s_vat_inode->i_size -
+				map->s_type_specific.s_virtual.
+					s_start_offset) >> 2;
+		brelse(bh);
+	}
+	return 0;
+}
+
+/*
+ * Load partition descriptor block
+ *
+ * Returns <0 on error, 0 on success, -EAGAIN is special - try next descriptor
+ * sequence.
+ */
+static int udf_load_partdesc(struct super_block *sb, sector_t block)
+{
+	struct buffer_head *bh;
+	struct partitionDesc *p;
+	struct udf_part_map *map;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int i, type1_idx;
+	uint16_t partitionNumber;
+	uint16_t ident;
+	int ret;
+
+	bh = udf_read_tagged(sb, block, block, &ident);
+	if (!bh)
+		return -EAGAIN;
+	if (ident != TAG_IDENT_PD) {
+		ret = 0;
+		goto out_bh;
+	}
+
+	p = (struct partitionDesc *)bh->b_data;
+	partitionNumber = le16_to_cpu(p->partitionNumber);
+
+	/* First scan for TYPE1 and SPARABLE partitions */
+	for (i = 0; i < sbi->s_partitions; i++) {
+		map = &sbi->s_partmaps[i];
+		udf_debug("Searching map: (%u == %u)\n",
+			  map->s_partition_num, partitionNumber);
+		if (map->s_partition_num == partitionNumber &&
+		    (map->s_partition_type == UDF_TYPE1_MAP15 ||
+		     map->s_partition_type == UDF_SPARABLE_MAP15))
+			break;
+	}
+
+	if (i >= sbi->s_partitions) {
+		udf_debug("Partition (%u) not found in partition map\n",
+			  partitionNumber);
+		ret = 0;
+		goto out_bh;
+	}
+
+	ret = udf_fill_partdesc_info(sb, p, i);
+	if (ret < 0)
+		goto out_bh;
+
+	/*
+	 * Now rescan for VIRTUAL or METADATA partitions when SPARABLE and
+	 * PHYSICAL partitions are already set up
+	 */
+	type1_idx = i;
+#ifdef UDFFS_DEBUG
+	map = NULL; /* supress 'maybe used uninitialized' warning */
+#endif
+	for (i = 0; i < sbi->s_partitions; i++) {
+		map = &sbi->s_partmaps[i];
+
+		if (map->s_partition_num == partitionNumber &&
+		    (map->s_partition_type == UDF_VIRTUAL_MAP15 ||
+		     map->s_partition_type == UDF_VIRTUAL_MAP20 ||
+		     map->s_partition_type == UDF_METADATA_MAP25))
+			break;
+	}
+
+	if (i >= sbi->s_partitions) {
+		ret = 0;
+		goto out_bh;
+	}
+
+	ret = udf_fill_partdesc_info(sb, p, i);
+	if (ret < 0)
+		goto out_bh;
+
+	if (map->s_partition_type == UDF_METADATA_MAP25) {
+		ret = udf_load_metadata_files(sb, i, type1_idx);
+		if (ret < 0) {
+			udf_err(sb, "error loading MetaData partition map %d\n",
+				i);
+			goto out_bh;
+		}
+	} else {
+		/*
+		 * If we have a partition with virtual map, we don't handle
+		 * writing to it (we overwrite blocks instead of relocating
+		 * them).
+		 */
+		if (!sb_rdonly(sb)) {
+			ret = -EACCES;
+			goto out_bh;
+		}
+		UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
+		ret = udf_load_vat(sb, i, type1_idx);
+		if (ret < 0)
+			goto out_bh;
+	}
+	ret = 0;
+out_bh:
+	/* In case loading failed, we handle cleanup in udf_fill_super */
+	brelse(bh);
+	return ret;
+}
+
+static int udf_load_sparable_map(struct super_block *sb,
+				 struct udf_part_map *map,
+				 struct sparablePartitionMap *spm)
+{
+	uint32_t loc;
+	uint16_t ident;
+	struct sparingTable *st;
+	struct udf_sparing_data *sdata = &map->s_type_specific.s_sparing;
+	int i;
+	struct buffer_head *bh;
+
+	map->s_partition_type = UDF_SPARABLE_MAP15;
+	sdata->s_packet_len = le16_to_cpu(spm->packetLength);
+	if (!is_power_of_2(sdata->s_packet_len)) {
+		udf_err(sb, "error loading logical volume descriptor: "
+			"Invalid packet length %u\n",
+			(unsigned)sdata->s_packet_len);
+		return -EIO;
+	}
+	if (spm->numSparingTables > 4) {
+		udf_err(sb, "error loading logical volume descriptor: "
+			"Too many sparing tables (%d)\n",
+			(int)spm->numSparingTables);
+		return -EIO;
+	}
+
+	for (i = 0; i < spm->numSparingTables; i++) {
+		loc = le32_to_cpu(spm->locSparingTable[i]);
+		bh = udf_read_tagged(sb, loc, loc, &ident);
+		if (!bh)
+			continue;
+
+		st = (struct sparingTable *)bh->b_data;
+		if (ident != 0 ||
+		    strncmp(st->sparingIdent.ident, UDF_ID_SPARING,
+			    strlen(UDF_ID_SPARING)) ||
+		    sizeof(*st) + le16_to_cpu(st->reallocationTableLen) >
+							sb->s_blocksize) {
+			brelse(bh);
+			continue;
+		}
+
+		sdata->s_spar_map[i] = bh;
+	}
+	map->s_partition_func = udf_get_pblock_spar15;
+	return 0;
+}
+
+static int udf_load_logicalvol(struct super_block *sb, sector_t block,
+			       struct kernel_lb_addr *fileset)
+{
+	struct logicalVolDesc *lvd;
+	int i, offset;
+	uint8_t type;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct genericPartitionMap *gpm;
+	uint16_t ident;
+	struct buffer_head *bh;
+	unsigned int table_len;
+	int ret;
+
+	bh = udf_read_tagged(sb, block, block, &ident);
+	if (!bh)
+		return -EAGAIN;
+	BUG_ON(ident != TAG_IDENT_LVD);
+	lvd = (struct logicalVolDesc *)bh->b_data;
+	table_len = le32_to_cpu(lvd->mapTableLength);
+	if (table_len > sb->s_blocksize - sizeof(*lvd)) {
+		udf_err(sb, "error loading logical volume descriptor: "
+			"Partition table too long (%u > %lu)\n", table_len,
+			sb->s_blocksize - sizeof(*lvd));
+		ret = -EIO;
+		goto out_bh;
+	}
+
+	ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps));
+	if (ret)
+		goto out_bh;
+
+	for (i = 0, offset = 0;
+	     i < sbi->s_partitions && offset < table_len;
+	     i++, offset += gpm->partitionMapLength) {
+		struct udf_part_map *map = &sbi->s_partmaps[i];
+		gpm = (struct genericPartitionMap *)
+				&(lvd->partitionMaps[offset]);
+		type = gpm->partitionMapType;
+		if (type == 1) {
+			struct genericPartitionMap1 *gpm1 =
+				(struct genericPartitionMap1 *)gpm;
+			map->s_partition_type = UDF_TYPE1_MAP15;
+			map->s_volumeseqnum = le16_to_cpu(gpm1->volSeqNum);
+			map->s_partition_num = le16_to_cpu(gpm1->partitionNum);
+			map->s_partition_func = NULL;
+		} else if (type == 2) {
+			struct udfPartitionMap2 *upm2 =
+						(struct udfPartitionMap2 *)gpm;
+			if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL,
+						strlen(UDF_ID_VIRTUAL))) {
+				u16 suf =
+					le16_to_cpu(((__le16 *)upm2->partIdent.
+							identSuffix)[0]);
+				if (suf < 0x0200) {
+					map->s_partition_type =
+							UDF_VIRTUAL_MAP15;
+					map->s_partition_func =
+							udf_get_pblock_virt15;
+				} else {
+					map->s_partition_type =
+							UDF_VIRTUAL_MAP20;
+					map->s_partition_func =
+							udf_get_pblock_virt20;
+				}
+			} else if (!strncmp(upm2->partIdent.ident,
+						UDF_ID_SPARABLE,
+						strlen(UDF_ID_SPARABLE))) {
+				ret = udf_load_sparable_map(sb, map,
+					(struct sparablePartitionMap *)gpm);
+				if (ret < 0)
+					goto out_bh;
+			} else if (!strncmp(upm2->partIdent.ident,
+						UDF_ID_METADATA,
+						strlen(UDF_ID_METADATA))) {
+				struct udf_meta_data *mdata =
+					&map->s_type_specific.s_metadata;
+				struct metadataPartitionMap *mdm =
+						(struct metadataPartitionMap *)
+						&(lvd->partitionMaps[offset]);
+				udf_debug("Parsing Logical vol part %d type %u  id=%s\n",
+					  i, type, UDF_ID_METADATA);
+
+				map->s_partition_type = UDF_METADATA_MAP25;
+				map->s_partition_func = udf_get_pblock_meta25;
+
+				mdata->s_meta_file_loc   =
+					le32_to_cpu(mdm->metadataFileLoc);
+				mdata->s_mirror_file_loc =
+					le32_to_cpu(mdm->metadataMirrorFileLoc);
+				mdata->s_bitmap_file_loc =
+					le32_to_cpu(mdm->metadataBitmapFileLoc);
+				mdata->s_alloc_unit_size =
+					le32_to_cpu(mdm->allocUnitSize);
+				mdata->s_align_unit_size =
+					le16_to_cpu(mdm->alignUnitSize);
+				if (mdm->flags & 0x01)
+					mdata->s_flags |= MF_DUPLICATE_MD;
+
+				udf_debug("Metadata Ident suffix=0x%x\n",
+					  le16_to_cpu(*(__le16 *)
+						      mdm->partIdent.identSuffix));
+				udf_debug("Metadata part num=%u\n",
+					  le16_to_cpu(mdm->partitionNum));
+				udf_debug("Metadata part alloc unit size=%u\n",
+					  le32_to_cpu(mdm->allocUnitSize));
+				udf_debug("Metadata file loc=%u\n",
+					  le32_to_cpu(mdm->metadataFileLoc));
+				udf_debug("Mirror file loc=%u\n",
+					  le32_to_cpu(mdm->metadataMirrorFileLoc));
+				udf_debug("Bitmap file loc=%u\n",
+					  le32_to_cpu(mdm->metadataBitmapFileLoc));
+				udf_debug("Flags: %d %u\n",
+					  mdata->s_flags, mdm->flags);
+			} else {
+				udf_debug("Unknown ident: %s\n",
+					  upm2->partIdent.ident);
+				continue;
+			}
+			map->s_volumeseqnum = le16_to_cpu(upm2->volSeqNum);
+			map->s_partition_num = le16_to_cpu(upm2->partitionNum);
+		}
+		udf_debug("Partition (%d:%u) type %u on volume %u\n",
+			  i, map->s_partition_num, type, map->s_volumeseqnum);
+	}
+
+	if (fileset) {
+		struct long_ad *la = (struct long_ad *)&(lvd->logicalVolContentsUse[0]);
+
+		*fileset = lelb_to_cpu(la->extLocation);
+		udf_debug("FileSet found in LogicalVolDesc at block=%u, partition=%u\n",
+			  fileset->logicalBlockNum,
+			  fileset->partitionReferenceNum);
+	}
+	if (lvd->integritySeqExt.extLength)
+		udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt));
+	ret = 0;
+out_bh:
+	brelse(bh);
+	return ret;
+}
+
+/*
+ * Find the prevailing Logical Volume Integrity Descriptor.
+ */
+static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ad loc)
+{
+	struct buffer_head *bh, *final_bh;
+	uint16_t ident;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct logicalVolIntegrityDesc *lvid;
+	int indirections = 0;
+
+	while (++indirections <= UDF_MAX_LVID_NESTING) {
+		final_bh = NULL;
+		while (loc.extLength > 0 &&
+			(bh = udf_read_tagged(sb, loc.extLocation,
+					loc.extLocation, &ident))) {
+			if (ident != TAG_IDENT_LVID) {
+				brelse(bh);
+				break;
+			}
+
+			brelse(final_bh);
+			final_bh = bh;
+
+			loc.extLength -= sb->s_blocksize;
+			loc.extLocation++;
+		}
+
+		if (!final_bh)
+			return;
+
+		brelse(sbi->s_lvid_bh);
+		sbi->s_lvid_bh = final_bh;
+
+		lvid = (struct logicalVolIntegrityDesc *)final_bh->b_data;
+		if (lvid->nextIntegrityExt.extLength == 0)
+			return;
+
+		loc = leea_to_cpu(lvid->nextIntegrityExt);
+	}
+
+	udf_warn(sb, "Too many LVID indirections (max %u), ignoring.\n",
+		UDF_MAX_LVID_NESTING);
+	brelse(sbi->s_lvid_bh);
+	sbi->s_lvid_bh = NULL;
+}
+
+/*
+ * Step for reallocation of table of partition descriptor sequence numbers.
+ * Must be power of 2.
+ */
+#define PART_DESC_ALLOC_STEP 32
+
+struct part_desc_seq_scan_data {
+	struct udf_vds_record rec;
+	u32 partnum;
+};
+
+struct desc_seq_scan_data {
+	struct udf_vds_record vds[VDS_POS_LENGTH];
+	unsigned int size_part_descs;
+	unsigned int num_part_descs;
+	struct part_desc_seq_scan_data *part_descs_loc;
+};
+
+static struct udf_vds_record *handle_partition_descriptor(
+				struct buffer_head *bh,
+				struct desc_seq_scan_data *data)
+{
+	struct partitionDesc *desc = (struct partitionDesc *)bh->b_data;
+	int partnum;
+	int i;
+
+	partnum = le16_to_cpu(desc->partitionNumber);
+	for (i = 0; i < data->num_part_descs; i++)
+		if (partnum == data->part_descs_loc[i].partnum)
+			return &(data->part_descs_loc[i].rec);
+	if (data->num_part_descs >= data->size_part_descs) {
+		struct part_desc_seq_scan_data *new_loc;
+		unsigned int new_size = ALIGN(partnum, PART_DESC_ALLOC_STEP);
+
+		new_loc = kcalloc(new_size, sizeof(*new_loc), GFP_KERNEL);
+		if (!new_loc)
+			return ERR_PTR(-ENOMEM);
+		memcpy(new_loc, data->part_descs_loc,
+		       data->size_part_descs * sizeof(*new_loc));
+		kfree(data->part_descs_loc);
+		data->part_descs_loc = new_loc;
+		data->size_part_descs = new_size;
+	}
+	return &(data->part_descs_loc[data->num_part_descs++].rec);
+}
+
+
+static struct udf_vds_record *get_volume_descriptor_record(uint16_t ident,
+		struct buffer_head *bh, struct desc_seq_scan_data *data)
+{
+	switch (ident) {
+	case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */
+		return &(data->vds[VDS_POS_PRIMARY_VOL_DESC]);
+	case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */
+		return &(data->vds[VDS_POS_IMP_USE_VOL_DESC]);
+	case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */
+		return &(data->vds[VDS_POS_LOGICAL_VOL_DESC]);
+	case TAG_IDENT_USD: /* ISO 13346 3/10.8 */
+		return &(data->vds[VDS_POS_UNALLOC_SPACE_DESC]);
+	case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
+		return handle_partition_descriptor(bh, data);
+	}
+	return NULL;
+}
+
+/*
+ * Process a main/reserve volume descriptor sequence.
+ *   @block		First block of first extent of the sequence.
+ *   @lastblock		Lastblock of first extent of the sequence.
+ *   @fileset		There we store extent containing root fileset
+ *
+ * Returns <0 on error, 0 on success. -EAGAIN is special - try next descriptor
+ * sequence
+ */
+static noinline int udf_process_sequence(
+		struct super_block *sb,
+		sector_t block, sector_t lastblock,
+		struct kernel_lb_addr *fileset)
+{
+	struct buffer_head *bh = NULL;
+	struct udf_vds_record *curr;
+	struct generic_desc *gd;
+	struct volDescPtr *vdp;
+	bool done = false;
+	uint32_t vdsn;
+	uint16_t ident;
+	int ret;
+	unsigned int indirections = 0;
+	struct desc_seq_scan_data data;
+	unsigned int i;
+
+	memset(data.vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
+	data.size_part_descs = PART_DESC_ALLOC_STEP;
+	data.num_part_descs = 0;
+	data.part_descs_loc = kcalloc(data.size_part_descs,
+				      sizeof(*data.part_descs_loc),
+				      GFP_KERNEL);
+	if (!data.part_descs_loc)
+		return -ENOMEM;
+
+	/*
+	 * Read the main descriptor sequence and find which descriptors
+	 * are in it.
+	 */
+	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_VDP: /* ISO 13346 3/10.3 */
+			if (++indirections > UDF_MAX_TD_NESTING) {
+				udf_err(sb, "too many Volume Descriptor "
+					"Pointers (max %u supported)\n",
+					UDF_MAX_TD_NESTING);
+				brelse(bh);
+				return -EIO;
+			}
+
+			vdp = (struct volDescPtr *)bh->b_data;
+			block = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation);
+			lastblock = le32_to_cpu(
+				vdp->nextVolDescSeqExt.extLength) >>
+				sb->s_blocksize_bits;
+			lastblock += block - 1;
+			/* For loop is going to increment 'block' again */
+			block--;
+			break;
+		case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */
+		case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */
+		case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */
+		case TAG_IDENT_USD: /* ISO 13346 3/10.8 */
+		case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
+			curr = get_volume_descriptor_record(ident, bh, &data);
+			if (IS_ERR(curr)) {
+				brelse(bh);
+				return PTR_ERR(curr);
+			}
+			/* Descriptor we don't care about? */
+			if (!curr)
+				break;
+			if (vdsn >= curr->volDescSeqNum) {
+				curr->volDescSeqNum = vdsn;
+				curr->block = block;
+			}
+			break;
+		case TAG_IDENT_TD: /* ISO 13346 3/10.9 */
+			done = true;
+			break;
+		}
+		brelse(bh);
+	}
+	/*
+	 * Now read interesting descriptors again and process them
+	 * in a suitable order
+	 */
+	if (!data.vds[VDS_POS_PRIMARY_VOL_DESC].block) {
+		udf_err(sb, "Primary Volume Descriptor not found!\n");
+		return -EAGAIN;
+	}
+	ret = udf_load_pvoldesc(sb, data.vds[VDS_POS_PRIMARY_VOL_DESC].block);
+	if (ret < 0)
+		return ret;
+
+	if (data.vds[VDS_POS_LOGICAL_VOL_DESC].block) {
+		ret = udf_load_logicalvol(sb,
+				data.vds[VDS_POS_LOGICAL_VOL_DESC].block,
+				fileset);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Now handle prevailing Partition Descriptors */
+	for (i = 0; i < data.num_part_descs; i++) {
+		ret = udf_load_partdesc(sb, data.part_descs_loc[i].rec.block);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Load Volume Descriptor Sequence described by anchor in bh
+ *
+ * Returns <0 on error, 0 on success
+ */
+static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh,
+			     struct kernel_lb_addr *fileset)
+{
+	struct anchorVolDescPtr *anchor;
+	sector_t main_s, main_e, reserve_s, reserve_e;
+	int ret;
+
+	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 - 1;
+
+	/* 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 - 1;
+
+	/* Process the main & reserve sequences */
+	/* responsible for finding the PartitionDesc(s) */
+	ret = udf_process_sequence(sb, main_s, main_e, fileset);
+	if (ret != -EAGAIN)
+		return ret;
+	udf_sb_free_partitions(sb);
+	ret = udf_process_sequence(sb, reserve_s, reserve_e, fileset);
+	if (ret < 0) {
+		udf_sb_free_partitions(sb);
+		/* No sequence was OK, return -EIO */
+		if (ret == -EAGAIN)
+			ret = -EIO;
+	}
+	return ret;
+}
+
+/*
+ * Check whether there is an anchor block in the given block and
+ * load Volume Descriptor Sequence if so.
+ *
+ * Returns <0 on error, 0 on success, -EAGAIN is special - try next anchor
+ * block
+ */
+static int udf_check_anchor_block(struct super_block *sb, sector_t block,
+				  struct kernel_lb_addr *fileset)
+{
+	struct buffer_head *bh;
+	uint16_t ident;
+	int ret;
+
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV) &&
+	    udf_fixed_to_variable(block) >=
+	    i_size_read(sb->s_bdev->bd_inode) >> sb->s_blocksize_bits)
+		return -EAGAIN;
+
+	bh = udf_read_tagged(sb, block, block, &ident);
+	if (!bh)
+		return -EAGAIN;
+	if (ident != TAG_IDENT_AVDP) {
+		brelse(bh);
+		return -EAGAIN;
+	}
+	ret = udf_load_sequence(sb, bh, fileset);
+	brelse(bh);
+	return ret;
+}
+
+/*
+ * Search for an anchor volume descriptor pointer.
+ *
+ * Returns < 0 on error, 0 on success. -EAGAIN is special - try next set
+ * of anchors.
+ */
+static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock,
+			    struct kernel_lb_addr *fileset)
+{
+	sector_t last[6];
+	int i;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	int last_count = 0;
+	int ret;
+
+	/* First try user provided anchor */
+	if (sbi->s_anchor) {
+		ret = udf_check_anchor_block(sb, sbi->s_anchor, fileset);
+		if (ret != -EAGAIN)
+			return ret;
+	}
+	/*
+	 * according to spec, anchor is in either:
+	 *     block 256
+	 *     lastblock-256
+	 *     lastblock
+	 *  however, if the disc isn't closed, it could be 512.
+	 */
+	ret = udf_check_anchor_block(sb, sbi->s_session + 256, fileset);
+	if (ret != -EAGAIN)
+		return ret;
+	/*
+	 * The trouble is which block is the last one. Drives often misreport
+	 * this so we try various possibilities.
+	 */
+	last[last_count++] = *lastblock;
+	if (*lastblock >= 1)
+		last[last_count++] = *lastblock - 1;
+	last[last_count++] = *lastblock + 1;
+	if (*lastblock >= 2)
+		last[last_count++] = *lastblock - 2;
+	if (*lastblock >= 150)
+		last[last_count++] = *lastblock - 150;
+	if (*lastblock >= 152)
+		last[last_count++] = *lastblock - 152;
+
+	for (i = 0; i < last_count; i++) {
+		if (last[i] >= i_size_read(sb->s_bdev->bd_inode) >>
+				sb->s_blocksize_bits)
+			continue;
+		ret = udf_check_anchor_block(sb, last[i], fileset);
+		if (ret != -EAGAIN) {
+			if (!ret)
+				*lastblock = last[i];
+			return ret;
+		}
+		if (last[i] < 256)
+			continue;
+		ret = udf_check_anchor_block(sb, last[i] - 256, fileset);
+		if (ret != -EAGAIN) {
+			if (!ret)
+				*lastblock = last[i];
+			return ret;
+		}
+	}
+
+	/* Finally try block 512 in case media is open */
+	return udf_check_anchor_block(sb, sbi->s_session + 512, fileset);
+}
+
+/*
+ * Find an anchor volume descriptor and load Volume Descriptor Sequence from
+ * area specified by it. The function expects sbi->s_lastblock to be the last
+ * block on the media.
+ *
+ * Return <0 on error, 0 if anchor found. -EAGAIN is special meaning anchor
+ * was not found.
+ */
+static int udf_find_anchor(struct super_block *sb,
+			   struct kernel_lb_addr *fileset)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	sector_t lastblock = sbi->s_last_block;
+	int ret;
+
+	ret = udf_scan_anchors(sb, &lastblock, fileset);
+	if (ret != -EAGAIN)
+		goto out;
+
+	/* No anchor found? Try VARCONV conversion of block numbers */
+	UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+	lastblock = udf_variable_to_fixed(sbi->s_last_block);
+	/* Firstly, we try to not convert number of the last block */
+	ret = udf_scan_anchors(sb, &lastblock, fileset);
+	if (ret != -EAGAIN)
+		goto out;
+
+	lastblock = sbi->s_last_block;
+	/* Secondly, we try with converted number of the last block */
+	ret = udf_scan_anchors(sb, &lastblock, fileset);
+	if (ret < 0) {
+		/* VARCONV didn't help. Clear it. */
+		UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV);
+	}
+out:
+	if (ret == 0)
+		sbi->s_last_block = lastblock;
+	return ret;
+}
+
+/*
+ * Check Volume Structure Descriptor, find Anchor block and load Volume
+ * Descriptor Sequence.
+ *
+ * Returns < 0 on error, 0 on success. -EAGAIN is special meaning anchor
+ * block was not found.
+ */
+static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
+			int silent, struct kernel_lb_addr *fileset)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	loff_t nsr_off;
+	int ret;
+
+	if (!sb_set_blocksize(sb, uopt->blocksize)) {
+		if (!silent)
+			udf_warn(sb, "Bad block size\n");
+		return -EINVAL;
+	}
+	sbi->s_last_block = uopt->lastblock;
+	if (!uopt->novrs) {
+		/* Check that it is NSR02 compliant */
+		nsr_off = udf_check_vsd(sb);
+		if (!nsr_off) {
+			if (!silent)
+				udf_warn(sb, "No VRS found\n");
+			return -EINVAL;
+		}
+		if (nsr_off == -1)
+			udf_debug("Failed to read sector at offset %d. "
+				  "Assuming open disc. Skipping validity "
+				  "check\n", VSD_FIRST_SECTOR_OFFSET);
+		if (!sbi->s_last_block)
+			sbi->s_last_block = udf_get_last_block(sb);
+	} else {
+		udf_debug("Validity check skipped because of novrs option\n");
+	}
+
+	/* Look for anchor block and load Volume Descriptor Sequence */
+	sbi->s_anchor = uopt->anchor;
+	ret = udf_find_anchor(sb, fileset);
+	if (ret < 0) {
+		if (!silent && ret == -EAGAIN)
+			udf_warn(sb, "No anchor found\n");
+		return ret;
+	}
+	return 0;
+}
+
+static void udf_open_lvid(struct super_block *sb)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct buffer_head *bh = sbi->s_lvid_bh;
+	struct logicalVolIntegrityDesc *lvid;
+	struct logicalVolIntegrityDescImpUse *lvidiu;
+	struct timespec64 ts;
+
+	if (!bh)
+		return;
+	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
+	lvidiu = udf_sb_lvidiu(sb);
+	if (!lvidiu)
+		return;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+	lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+	ktime_get_real_ts64(&ts);
+	udf_time_to_disk_stamp(&lvid->recordingDateAndTime, ts);
+	if (le32_to_cpu(lvid->integrityType) == LVID_INTEGRITY_TYPE_CLOSE)
+		lvid->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_OPEN);
+	else
+		UDF_SET_FLAG(sb, UDF_FLAG_INCONSISTENT);
+
+	lvid->descTag.descCRC = cpu_to_le16(
+		crc_itu_t(0, (char *)lvid + sizeof(struct tag),
+			le16_to_cpu(lvid->descTag.descCRCLength)));
+
+	lvid->descTag.tagChecksum = udf_tag_checksum(&lvid->descTag);
+	mark_buffer_dirty(bh);
+	sbi->s_lvid_dirty = 0;
+	mutex_unlock(&sbi->s_alloc_mutex);
+	/* Make opening of filesystem visible on the media immediately */
+	sync_dirty_buffer(bh);
+}
+
+static void udf_close_lvid(struct super_block *sb)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct buffer_head *bh = sbi->s_lvid_bh;
+	struct logicalVolIntegrityDesc *lvid;
+	struct logicalVolIntegrityDescImpUse *lvidiu;
+	struct timespec64 ts;
+
+	if (!bh)
+		return;
+	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
+	lvidiu = udf_sb_lvidiu(sb);
+	if (!lvidiu)
+		return;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+	lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+	ktime_get_real_ts64(&ts);
+	udf_time_to_disk_stamp(&lvid->recordingDateAndTime, ts);
+	if (UDF_MAX_WRITE_VERSION > le16_to_cpu(lvidiu->maxUDFWriteRev))
+		lvidiu->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION);
+	if (sbi->s_udfrev > le16_to_cpu(lvidiu->minUDFReadRev))
+		lvidiu->minUDFReadRev = cpu_to_le16(sbi->s_udfrev);
+	if (sbi->s_udfrev > le16_to_cpu(lvidiu->minUDFWriteRev))
+		lvidiu->minUDFWriteRev = cpu_to_le16(sbi->s_udfrev);
+	if (!UDF_QUERY_FLAG(sb, UDF_FLAG_INCONSISTENT))
+		lvid->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE);
+
+	lvid->descTag.descCRC = cpu_to_le16(
+			crc_itu_t(0, (char *)lvid + sizeof(struct tag),
+				le16_to_cpu(lvid->descTag.descCRCLength)));
+
+	lvid->descTag.tagChecksum = udf_tag_checksum(&lvid->descTag);
+	/*
+	 * We set buffer uptodate unconditionally here to avoid spurious
+	 * warnings from mark_buffer_dirty() when previous EIO has marked
+	 * the buffer as !uptodate
+	 */
+	set_buffer_uptodate(bh);
+	mark_buffer_dirty(bh);
+	sbi->s_lvid_dirty = 0;
+	mutex_unlock(&sbi->s_alloc_mutex);
+	/* Make closing of filesystem visible on the media immediately */
+	sync_dirty_buffer(bh);
+}
+
+u64 lvid_get_unique_id(struct super_block *sb)
+{
+	struct buffer_head *bh;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct logicalVolIntegrityDesc *lvid;
+	struct logicalVolHeaderDesc *lvhd;
+	u64 uniqueID;
+	u64 ret;
+
+	bh = sbi->s_lvid_bh;
+	if (!bh)
+		return 0;
+
+	lvid = (struct logicalVolIntegrityDesc *)bh->b_data;
+	lvhd = (struct logicalVolHeaderDesc *)lvid->logicalVolContentsUse;
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	ret = uniqueID = le64_to_cpu(lvhd->uniqueID);
+	if (!(++uniqueID & 0xFFFFFFFF))
+		uniqueID += 16;
+	lvhd->uniqueID = cpu_to_le64(uniqueID);
+	mutex_unlock(&sbi->s_alloc_mutex);
+	mark_buffer_dirty(bh);
+
+	return ret;
+}
+
+static int udf_fill_super(struct super_block *sb, void *options, int silent)
+{
+	int ret = -EINVAL;
+	struct inode *inode = NULL;
+	struct udf_options uopt;
+	struct kernel_lb_addr rootdir, fileset;
+	struct udf_sb_info *sbi;
+	bool lvid_open = false;
+
+	uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT);
+	/* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */
+	uopt.uid = make_kuid(current_user_ns(), overflowuid);
+	uopt.gid = make_kgid(current_user_ns(), overflowgid);
+	uopt.umask = 0;
+	uopt.fmode = UDF_INVALID_MODE;
+	uopt.dmode = UDF_INVALID_MODE;
+	uopt.nls_map = NULL;
+
+	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+
+	sb->s_fs_info = sbi;
+
+	mutex_init(&sbi->s_alloc_mutex);
+
+	if (!udf_parse_options((char *)options, &uopt, false))
+		goto parse_options_failure;
+
+	if (uopt.flags & (1 << UDF_FLAG_UTF8) &&
+	    uopt.flags & (1 << UDF_FLAG_NLS_MAP)) {
+		udf_err(sb, "utf8 cannot be combined with iocharset\n");
+		goto parse_options_failure;
+	}
+	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");
+	}
+	if (!(uopt.flags & (1 << UDF_FLAG_NLS_MAP)))
+		uopt.flags |= (1 << UDF_FLAG_UTF8);
+
+	fileset.logicalBlockNum = 0xFFFFFFFF;
+	fileset.partitionReferenceNum = 0xFFFF;
+
+	sbi->s_flags = uopt.flags;
+	sbi->s_uid = uopt.uid;
+	sbi->s_gid = uopt.gid;
+	sbi->s_umask = uopt.umask;
+	sbi->s_fmode = uopt.fmode;
+	sbi->s_dmode = uopt.dmode;
+	sbi->s_nls_map = uopt.nls_map;
+	rwlock_init(&sbi->s_cred_lock);
+
+	if (uopt.session == 0xFFFFFFFF)
+		sbi->s_session = udf_get_last_session(sb);
+	else
+		sbi->s_session = uopt.session;
+
+	udf_debug("Multi-session=%d\n", sbi->s_session);
+
+	/* Fill in the rest of the superblock */
+	sb->s_op = &udf_sb_ops;
+	sb->s_export_op = &udf_export_ops;
+
+	sb->s_magic = UDF_SUPER_MAGIC;
+	sb->s_time_gran = 1000;
+
+	if (uopt.flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) {
+		ret = udf_load_vrs(sb, &uopt, silent, &fileset);
+	} else {
+		uopt.blocksize = bdev_logical_block_size(sb->s_bdev);
+		while (uopt.blocksize <= 4096) {
+			ret = udf_load_vrs(sb, &uopt, silent, &fileset);
+			if (ret < 0) {
+				if (!silent && ret != -EACCES) {
+					pr_notice("Scanning with blocksize %u failed\n",
+						  uopt.blocksize);
+				}
+				brelse(sbi->s_lvid_bh);
+				sbi->s_lvid_bh = NULL;
+				/*
+				 * EACCES is special - we want to propagate to
+				 * upper layers that we cannot handle RW mount.
+				 */
+				if (ret == -EACCES)
+					break;
+			} else
+				break;
+
+			uopt.blocksize <<= 1;
+		}
+	}
+	if (ret < 0) {
+		if (ret == -EAGAIN) {
+			udf_warn(sb, "No partition found (1)\n");
+			ret = -EINVAL;
+		}
+		goto error_out;
+	}
+
+	udf_debug("Lastblock=%u\n", sbi->s_last_block);
+
+	if (sbi->s_lvid_bh) {
+		struct logicalVolIntegrityDescImpUse *lvidiu =
+							udf_sb_lvidiu(sb);
+		uint16_t minUDFReadRev;
+		uint16_t minUDFWriteRev;
+
+		if (!lvidiu) {
+			ret = -EINVAL;
+			goto error_out;
+		}
+		minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev);
+		minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev);
+		if (minUDFReadRev > UDF_MAX_READ_VERSION) {
+			udf_err(sb, "minUDFReadRev=%x (max is %x)\n",
+				minUDFReadRev,
+				UDF_MAX_READ_VERSION);
+			ret = -EINVAL;
+			goto error_out;
+		} else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) {
+			if (!sb_rdonly(sb)) {
+				ret = -EACCES;
+				goto error_out;
+			}
+			UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
+		}
+
+		sbi->s_udfrev = 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 (!sbi->s_partitions) {
+		udf_warn(sb, "No partition found (2)\n");
+		ret = -EINVAL;
+		goto error_out;
+	}
+
+	if (sbi->s_partmaps[sbi->s_partition].s_partition_flags &
+			UDF_PART_FLAG_READ_ONLY) {
+		if (!sb_rdonly(sb)) {
+			ret = -EACCES;
+			goto error_out;
+		}
+		UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT);
+	}
+
+	if (udf_find_fileset(sb, &fileset, &rootdir)) {
+		udf_warn(sb, "No fileset found\n");
+		ret = -EINVAL;
+		goto error_out;
+	}
+
+	if (!silent) {
+		struct timestamp ts;
+		udf_time_to_disk_stamp(&ts, sbi->s_record_time);
+		udf_info("Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n",
+			 sbi->s_volume_ident,
+			 le16_to_cpu(ts.year), ts.month, ts.day,
+			 ts.hour, ts.minute, le16_to_cpu(ts.typeAndTimezone));
+	}
+	if (!sb_rdonly(sb)) {
+		udf_open_lvid(sb);
+		lvid_open = true;
+	}
+
+	/* 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 (IS_ERR(inode)) {
+		udf_err(sb, "Error in udf_iget, block=%u, partition=%u\n",
+		       rootdir.logicalBlockNum, rootdir.partitionReferenceNum);
+		ret = PTR_ERR(inode);
+		goto error_out;
+	}
+
+	/* Allocate a dentry for the root inode */
+	sb->s_root = d_make_root(inode);
+	if (!sb->s_root) {
+		udf_err(sb, "Couldn't allocate root dentry\n");
+		ret = -ENOMEM;
+		goto error_out;
+	}
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
+	sb->s_max_links = UDF_MAX_LINKS;
+	return 0;
+
+error_out:
+	iput(sbi->s_vat_inode);
+parse_options_failure:
+	if (uopt.nls_map)
+		unload_nls(uopt.nls_map);
+	if (lvid_open)
+		udf_close_lvid(sb);
+	brelse(sbi->s_lvid_bh);
+	udf_sb_free_partitions(sb);
+	kfree(sbi);
+	sb->s_fs_info = NULL;
+
+	return ret;
+}
+
+void _udf_err(struct super_block *sb, const char *function,
+	      const char *fmt, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, fmt);
+
+	vaf.fmt = fmt;
+	vaf.va = &args;
+
+	pr_err("error (device %s): %s: %pV", sb->s_id, function, &vaf);
+
+	va_end(args);
+}
+
+void _udf_warn(struct super_block *sb, const char *function,
+	       const char *fmt, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, fmt);
+
+	vaf.fmt = fmt;
+	vaf.va = &args;
+
+	pr_warn("warning (device %s): %s: %pV", sb->s_id, function, &vaf);
+
+	va_end(args);
+}
+
+static void udf_put_super(struct super_block *sb)
+{
+	struct udf_sb_info *sbi;
+
+	sbi = UDF_SB(sb);
+
+	iput(sbi->s_vat_inode);
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+		unload_nls(sbi->s_nls_map);
+	if (!sb_rdonly(sb))
+		udf_close_lvid(sb);
+	brelse(sbi->s_lvid_bh);
+	udf_sb_free_partitions(sb);
+	mutex_destroy(&sbi->s_alloc_mutex);
+	kfree(sb->s_fs_info);
+	sb->s_fs_info = NULL;
+}
+
+static int udf_sync_fs(struct super_block *sb, int wait)
+{
+	struct udf_sb_info *sbi = UDF_SB(sb);
+
+	mutex_lock(&sbi->s_alloc_mutex);
+	if (sbi->s_lvid_dirty) {
+		/*
+		 * Blockdevice will be synced later so we don't have to submit
+		 * the buffer for IO
+		 */
+		mark_buffer_dirty(sbi->s_lvid_bh);
+		sbi->s_lvid_dirty = 0;
+	}
+	mutex_unlock(&sbi->s_alloc_mutex);
+
+	return 0;
+}
+
+static int udf_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	struct super_block *sb = dentry->d_sb;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+	struct logicalVolIntegrityDescImpUse *lvidiu;
+	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
+
+	lvidiu = udf_sb_lvidiu(sb);
+	buf->f_type = UDF_SUPER_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len;
+	buf->f_bfree = udf_count_free(sb);
+	buf->f_bavail = buf->f_bfree;
+	buf->f_files = (lvidiu != NULL ? (le32_to_cpu(lvidiu->numFiles) +
+					  le32_to_cpu(lvidiu->numDirs)) : 0)
+			+ buf->f_bfree;
+	buf->f_ffree = buf->f_bfree;
+	buf->f_namelen = UDF_NAME_LEN;
+	buf->f_fsid.val[0] = (u32)id;
+	buf->f_fsid.val[1] = (u32)(id >> 32);
+
+	return 0;
+}
+
+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;
+	udf_pblk_t block = 0, newblock;
+	struct kernel_lb_addr loc;
+	uint32_t bytes;
+	uint8_t *ptr;
+	uint16_t ident;
+	struct spaceBitmapDesc *bm;
+
+	loc.logicalBlockNum = bitmap->s_extPosition;
+	loc.partitionReferenceNum = UDF_SB(sb)->s_partition;
+	bh = udf_read_ptagged(sb, &loc, 0, &ident);
+
+	if (!bh) {
+		udf_err(sb, "udf_count_free failed\n");
+		goto out;
+	} else if (ident != TAG_IDENT_SBD) {
+		brelse(bh);
+		udf_err(sb, "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) {
+		u32 cur_bytes = min_t(u32, bytes, sb->s_blocksize - index);
+		accum += bitmap_weight((const unsigned long *)(ptr + index),
+					cur_bytes * 8);
+		bytes -= cur_bytes;
+		if (bytes) {
+			brelse(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;
+		}
+	}
+	brelse(bh);
+out:
+	return accum;
+}
+
+static unsigned int udf_count_free_table(struct super_block *sb,
+					 struct inode *table)
+{
+	unsigned int accum = 0;
+	uint32_t elen;
+	struct kernel_lb_addr eloc;
+	int8_t etype;
+	struct extent_position epos;
+
+	mutex_lock(&UDF_SB(sb)->s_alloc_mutex);
+	epos.block = UDF_I(table)->i_location;
+	epos.offset = sizeof(struct unallocSpaceEntry);
+	epos.bh = NULL;
+
+	while ((etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1)
+		accum += (elen >> table->i_sb->s_blocksize_bits);
+
+	brelse(epos.bh);
+	mutex_unlock(&UDF_SB(sb)->s_alloc_mutex);
+
+	return accum;
+}
+
+static unsigned int udf_count_free(struct super_block *sb)
+{
+	unsigned int accum = 0;
+	struct udf_sb_info *sbi;
+	struct udf_part_map *map;
+
+	sbi = UDF_SB(sb);
+	if (sbi->s_lvid_bh) {
+		struct logicalVolIntegrityDesc *lvid =
+			(struct logicalVolIntegrityDesc *)
+			sbi->s_lvid_bh->b_data;
+		if (le32_to_cpu(lvid->numOfPartitions) > sbi->s_partition) {
+			accum = le32_to_cpu(
+					lvid->freeSpaceTable[sbi->s_partition]);
+			if (accum == 0xFFFFFFFF)
+				accum = 0;
+		}
+	}
+
+	if (accum)
+		return accum;
+
+	map = &sbi->s_partmaps[sbi->s_partition];
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) {
+		accum += udf_count_free_bitmap(sb,
+					       map->s_uspace.s_bitmap);
+	}
+	if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) {
+		accum += udf_count_free_bitmap(sb,
+					       map->s_fspace.s_bitmap);
+	}
+	if (accum)
+		return accum;
+
+	if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) {
+		accum += udf_count_free_table(sb,
+					      map->s_uspace.s_table);
+	}
+	if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) {
+		accum += udf_count_free_table(sb,
+					      map->s_fspace.s_table);
+	}
+
+	return accum;
+}
+
+MODULE_AUTHOR("Ben Fennema");
+MODULE_DESCRIPTION("Universal Disk Format Filesystem");
+MODULE_LICENSE("GPL");
+module_init(init_udf_fs)
+module_exit(exit_udf_fs)
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
new file mode 100644
index 0000000..6023c97
--- /dev/null
+++ b/fs/udf/symlink.c
@@ -0,0 +1,191 @@
+/*
+ * symlink.c
+ *
+ * PURPOSE
+ *	Symlink handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 1998-2001 Ben Fennema
+ *  (C) 1999 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ *  04/16/99 blf  Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include <linux/pagemap.h>
+#include "udf_i.h"
+
+static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
+			  int fromlen, unsigned char *to, int tolen)
+{
+	struct pathComponent *pc;
+	int elen = 0;
+	int comp_len;
+	unsigned char *p = to;
+
+	/* Reserve one byte for terminating \0 */
+	tolen--;
+	while (elen < fromlen) {
+		pc = (struct pathComponent *)(from + elen);
+		elen += sizeof(struct pathComponent);
+		switch (pc->componentType) {
+		case 1:
+			/*
+			 * Symlink points to some place which should be agreed
+ 			 * upon between originator and receiver of the media. Ignore.
+			 */
+			if (pc->lengthComponentIdent > 0) {
+				elen += pc->lengthComponentIdent;
+				break;
+			}
+			/* Fall through */
+		case 2:
+			if (tolen == 0)
+				return -ENAMETOOLONG;
+			p = to;
+			*p++ = '/';
+			tolen--;
+			break;
+		case 3:
+			if (tolen < 3)
+				return -ENAMETOOLONG;
+			memcpy(p, "../", 3);
+			p += 3;
+			tolen -= 3;
+			break;
+		case 4:
+			if (tolen < 2)
+				return -ENAMETOOLONG;
+			memcpy(p, "./", 2);
+			p += 2;
+			tolen -= 2;
+			/* that would be . - just ignore */
+			break;
+		case 5:
+			elen += pc->lengthComponentIdent;
+			if (elen > fromlen)
+				return -EIO;
+			comp_len = udf_get_filename(sb, pc->componentIdent,
+						    pc->lengthComponentIdent,
+						    p, tolen);
+			if (comp_len < 0)
+				return comp_len;
+
+			p += comp_len;
+			tolen -= comp_len;
+			if (tolen == 0)
+				return -ENAMETOOLONG;
+			*p++ = '/';
+			tolen--;
+			break;
+		}
+	}
+	if (p > to + 1)
+		p[-1] = '\0';
+	else
+		p[0] = '\0';
+	return 0;
+}
+
+static int udf_symlink_filler(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	struct buffer_head *bh = NULL;
+	unsigned char *symlink;
+	int err;
+	unsigned char *p = page_address(page);
+	struct udf_inode_info *iinfo;
+	uint32_t pos;
+
+	/* We don't support symlinks longer than one block */
+	if (inode->i_size > inode->i_sb->s_blocksize) {
+		err = -ENAMETOOLONG;
+		goto out_unmap;
+	}
+
+	iinfo = UDF_I(inode);
+	pos = udf_block_map(inode, 0);
+
+	down_read(&iinfo->i_data_sem);
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+		symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr;
+	} else {
+		bh = sb_bread(inode->i_sb, pos);
+
+		if (!bh) {
+			err = -EIO;
+			goto out_unlock_inode;
+		}
+
+		symlink = bh->b_data;
+	}
+
+	err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
+	brelse(bh);
+	if (err)
+		goto out_unlock_inode;
+
+	up_read(&iinfo->i_data_sem);
+	SetPageUptodate(page);
+	unlock_page(page);
+	return 0;
+
+out_unlock_inode:
+	up_read(&iinfo->i_data_sem);
+	SetPageError(page);
+out_unmap:
+	unlock_page(page);
+	return err;
+}
+
+static int udf_symlink_getattr(const struct path *path, struct kstat *stat,
+				u32 request_mask, unsigned int flags)
+{
+	struct dentry *dentry = path->dentry;
+	struct inode *inode = d_backing_inode(dentry);
+	struct page *page;
+
+	generic_fillattr(inode, stat);
+	page = read_mapping_page(inode->i_mapping, 0, NULL);
+	if (IS_ERR(page))
+		return PTR_ERR(page);
+	/*
+	 * UDF uses non-trivial encoding of symlinks so i_size does not match
+	 * number of characters reported by readlink(2) which apparently some
+	 * applications expect. Also POSIX says that "The value returned in the
+	 * st_size field shall be the length of the contents of the symbolic
+	 * link, and shall not count a trailing null if one is present." So
+	 * let's report the length of string returned by readlink(2) for
+	 * st_size.
+	 */
+	stat->size = strlen(page_address(page));
+	put_page(page);
+
+	return 0;
+}
+
+/*
+ * symlinks can't do much...
+ */
+const struct address_space_operations udf_symlink_aops = {
+	.readpage		= udf_symlink_filler,
+};
+
+const struct inode_operations udf_symlink_inode_operations = {
+	.get_link	= page_get_link,
+	.getattr	= udf_symlink_getattr,
+};
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
new file mode 100644
index 0000000..b647f0b
--- /dev/null
+++ b/fs/udf/truncate.c
@@ -0,0 +1,286 @@
+/*
+ * truncate.c
+ *
+ * PURPOSE
+ *	Truncate handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * COPYRIGHT
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from:
+ *		ftp://prep.ai.mit.edu/pub/gnu/GPL
+ *	Each contributing author retains all rights to their own work.
+ *
+ *  (C) 1999-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 "udf_i.h"
+#include "udf_sb.h"
+
+static void extent_trunc(struct inode *inode, struct extent_position *epos,
+			 struct kernel_lb_addr *eloc, int8_t etype, uint32_t elen,
+			 uint32_t nelen)
+{
+	struct kernel_lb_addr neloc = {};
+	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, epos, &neloc, nelen, 0);
+		if (last_block > first_block) {
+			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);
+		}
+	}
+}
+
+/*
+ * Truncate the last extent to match i_size. This function assumes
+ * that preallocation extent is already truncated.
+ */
+void udf_truncate_tail_extent(struct inode *inode)
+{
+	struct extent_position epos = {};
+	struct kernel_lb_addr eloc;
+	uint32_t elen, nelen;
+	uint64_t lbcount = 0;
+	int8_t etype = -1, netype;
+	int adsize;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
+	    inode->i_size == iinfo->i_lenExtents)
+		return;
+	/* Are we going to delete the file anyway? */
+	if (inode->i_nlink == 0)
+		return;
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		BUG();
+
+	/* Find the last extent in the file */
+	while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
+		etype = netype;
+		lbcount += elen;
+		if (lbcount > inode->i_size) {
+			if (lbcount - inode->i_size >= inode->i_sb->s_blocksize)
+				udf_warn(inode->i_sb,
+					 "Too long extent after EOF in inode %u: i_size: %lld lbcount: %lld extent %u+%u\n",
+					 (unsigned)inode->i_ino,
+					 (long long)inode->i_size,
+					 (long long)lbcount,
+					 (unsigned)eloc.logicalBlockNum,
+					 (unsigned)elen);
+			nelen = elen - (lbcount - inode->i_size);
+			epos.offset -= adsize;
+			extent_trunc(inode, &epos, &eloc, etype, elen, nelen);
+			epos.offset += adsize;
+			if (udf_next_aext(inode, &epos, &eloc, &elen, 1) != -1)
+				udf_err(inode->i_sb,
+					"Extent after EOF in inode %u\n",
+					(unsigned)inode->i_ino);
+			break;
+		}
+	}
+	/* This inode entry is in-memory only and thus we don't have to mark
+	 * the inode dirty */
+	iinfo->i_lenExtents = inode->i_size;
+	brelse(epos.bh);
+}
+
+void udf_discard_prealloc(struct inode *inode)
+{
+	struct extent_position epos = { NULL, 0, {0, 0} };
+	struct kernel_lb_addr eloc;
+	uint32_t elen;
+	uint64_t lbcount = 0;
+	int8_t etype = -1, netype;
+	int adsize;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
+	    inode->i_size == iinfo->i_lenExtents)
+		return;
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		adsize = 0;
+
+	epos.block = iinfo->i_location;
+
+	/* Find the last extent in the file */
+	while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
+		etype = netype;
+		lbcount += elen;
+	}
+	if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
+		epos.offset -= adsize;
+		lbcount -= elen;
+		extent_trunc(inode, &epos, &eloc, etype, elen, 0);
+		if (!epos.bh) {
+			iinfo->i_lenAlloc =
+				epos.offset -
+				udf_file_entry_alloc_offset(inode);
+			mark_inode_dirty(inode);
+		} else {
+			struct allocExtDesc *aed =
+				(struct allocExtDesc *)(epos.bh->b_data);
+			aed->lengthAllocDescs =
+				cpu_to_le32(epos.offset -
+					    sizeof(struct allocExtDesc));
+			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
+			    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
+				udf_update_tag(epos.bh->b_data, epos.offset);
+			else
+				udf_update_tag(epos.bh->b_data,
+					       sizeof(struct allocExtDesc));
+			mark_buffer_dirty_inode(epos.bh, inode);
+		}
+	}
+	/* This inode entry is in-memory only and thus we don't have to mark
+	 * the inode dirty */
+	iinfo->i_lenExtents = lbcount;
+	brelse(epos.bh);
+}
+
+static void udf_update_alloc_ext_desc(struct inode *inode,
+				      struct extent_position *epos,
+				      u32 lenalloc)
+{
+	struct super_block *sb = inode->i_sb;
+	struct udf_sb_info *sbi = UDF_SB(sb);
+
+	struct allocExtDesc *aed = (struct allocExtDesc *) (epos->bh->b_data);
+	int len = sizeof(struct allocExtDesc);
+
+	aed->lengthAllocDescs =	cpu_to_le32(lenalloc);
+	if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || sbi->s_udfrev >= 0x0201)
+		len += lenalloc;
+
+	udf_update_tag(epos->bh->b_data, len);
+	mark_buffer_dirty_inode(epos->bh, inode);
+}
+
+/*
+ * Truncate extents of inode to inode->i_size. This function can be used only
+ * for making file shorter. For making file longer, udf_extend_file() has to
+ * be used.
+ */
+void udf_truncate_extents(struct inode *inode)
+{
+	struct extent_position epos;
+	struct kernel_lb_addr eloc, neloc = {};
+	uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc;
+	int8_t etype;
+	struct super_block *sb = inode->i_sb;
+	sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset;
+	loff_t byte_offset;
+	int adsize;
+	struct udf_inode_info *iinfo = UDF_I(inode);
+
+	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+		adsize = sizeof(struct short_ad);
+	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+		adsize = sizeof(struct long_ad);
+	else
+		BUG();
+
+	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
+	byte_offset = (offset << sb->s_blocksize_bits) +
+		(inode->i_size & (sb->s_blocksize - 1));
+	if (etype == -1) {
+		/* We should extend the file? */
+		WARN_ON(byte_offset);
+		return;
+	}
+	epos.offset -= adsize;
+	extent_trunc(inode, &epos, &eloc, etype, elen, byte_offset);
+	epos.offset += adsize;
+	if (byte_offset)
+		lenalloc = epos.offset;
+	else
+		lenalloc = epos.offset - adsize;
+
+	if (!epos.bh)
+		lenalloc -= udf_file_entry_alloc_offset(inode);
+	else
+		lenalloc -= sizeof(struct allocExtDesc);
+
+	while ((etype = udf_current_aext(inode, &epos, &eloc,
+					 &elen, 0)) != -1) {
+		if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
+			udf_write_aext(inode, &epos, &neloc, nelen, 0);
+			if (indirect_ext_len) {
+				/* We managed to free all extents in the
+				 * indirect extent - free it too */
+				BUG_ON(!epos.bh);
+				udf_free_blocks(sb, NULL, &epos.block,
+						0, indirect_ext_len);
+			} else if (!epos.bh) {
+				iinfo->i_lenAlloc = lenalloc;
+				mark_inode_dirty(inode);
+			} else
+				udf_update_alloc_ext_desc(inode,
+						&epos, lenalloc);
+			brelse(epos.bh);
+			epos.offset = sizeof(struct allocExtDesc);
+			epos.block = eloc;
+			epos.bh = udf_tread(sb,
+					udf_get_lb_pblock(sb, &eloc, 0));
+			if (elen)
+				indirect_ext_len =
+					(elen + sb->s_blocksize - 1) >>
+					sb->s_blocksize_bits;
+			else
+				indirect_ext_len = 1;
+		} else {
+			extent_trunc(inode, &epos, &eloc, etype, elen, 0);
+			epos.offset += adsize;
+		}
+	}
+
+	if (indirect_ext_len) {
+		BUG_ON(!epos.bh);
+		udf_free_blocks(sb, NULL, &epos.block, 0, indirect_ext_len);
+	} else if (!epos.bh) {
+		iinfo->i_lenAlloc = lenalloc;
+		mark_inode_dirty(inode);
+	} else
+		udf_update_alloc_ext_desc(inode, &epos, lenalloc);
+	iinfo->i_lenExtents = inode->i_size;
+
+	brelse(epos.bh);
+}
diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h
new file mode 100644
index 0000000..2ef0e21
--- /dev/null
+++ b/fs/udf/udf_i.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _UDF_I_H
+#define _UDF_I_H
+
+struct extent_position {
+	struct buffer_head *bh;
+	uint32_t offset;
+	struct kernel_lb_addr block;
+};
+
+struct udf_ext_cache {
+	/* Extent position */
+	struct extent_position epos;
+	/* Start logical offset in bytes */
+	loff_t lstart;
+};
+
+/*
+ * The i_data_sem and i_mutex serve for protection of allocation information
+ * of a regular files and symlinks. This includes all extents belonging to
+ * the file/symlink, a fact whether data are in-inode or in external data
+ * blocks, preallocation, goal block information... When extents are read,
+ * i_mutex or i_data_sem must be held (for reading is enough in case of
+ * i_data_sem). When extents are changed, i_data_sem must be held for writing
+ * and also i_mutex must be held.
+ *
+ * For directories i_mutex is used for all the necessary protection.
+ */
+
+struct udf_inode_info {
+	struct timespec64	i_crtime;
+	/* Physical address of inode */
+	struct kernel_lb_addr		i_location;
+	__u64			i_unique;
+	__u32			i_lenEAttr;
+	__u32			i_lenAlloc;
+	__u64			i_lenExtents;
+	__u32			i_next_alloc_block;
+	__u32			i_next_alloc_goal;
+	__u32			i_checkpoint;
+	unsigned		i_alloc_type : 3;
+	unsigned		i_efe : 1;	/* extendedFileEntry */
+	unsigned		i_use : 1;	/* unallocSpaceEntry */
+	unsigned		i_strat4096 : 1;
+	unsigned		reserved : 26;
+	union {
+		struct short_ad	*i_sad;
+		struct long_ad		*i_lad;
+		__u8		*i_data;
+	} i_ext;
+	struct rw_semaphore	i_data_sem;
+	struct udf_ext_cache cached_extent;
+	/* Spinlock for protecting extent cache */
+	spinlock_t i_extent_cache_lock;
+	struct inode vfs_inode;
+};
+
+static inline struct udf_inode_info *UDF_I(struct inode *inode)
+{
+	return container_of(inode, struct udf_inode_info, vfs_inode);
+}
+
+#endif /* _UDF_I_H) */
diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h
new file mode 100644
index 0000000..d12e507
--- /dev/null
+++ b/fs/udf/udf_sb.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_UDF_SB_H
+#define __LINUX_UDF_SB_H
+
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/magic.h>
+
+#define UDF_MAX_READ_VERSION		0x0250
+#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_FLAG_UID_FORGET     11    /* save -1 for uid to disk */
+#define UDF_FLAG_GID_FORGET     12
+#define UDF_FLAG_UID_SET	13
+#define UDF_FLAG_GID_SET	14
+#define UDF_FLAG_SESSION_SET	15
+#define UDF_FLAG_LASTBLOCK_SET	16
+#define UDF_FLAG_BLOCKSIZE_SET	17
+#define UDF_FLAG_INCONSISTENT	18
+#define UDF_FLAG_RW_INCOMPAT	19	/* Set when we find RW incompatible
+					 * feature */
+
+#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
+
+#define UDF_MAX_BLOCK_LOADED	8
+
+#define UDF_TYPE1_MAP15			0x1511U
+#define UDF_VIRTUAL_MAP15		0x1512U
+#define UDF_VIRTUAL_MAP20		0x2012U
+#define UDF_SPARABLE_MAP15		0x1522U
+#define UDF_METADATA_MAP25		0x2511U
+
+#define UDF_INVALID_MODE		((umode_t)-1)
+
+#pragma pack(1) /* XXX(hch): Why?  This file just defines in-core structures */
+
+#define MF_DUPLICATE_MD		0x01
+#define MF_MIRROR_FE_LOADED	0x02
+
+struct udf_meta_data {
+	__u32	s_meta_file_loc;
+	__u32	s_mirror_file_loc;
+	__u32	s_bitmap_file_loc;
+	__u32	s_alloc_unit_size;
+	__u16	s_align_unit_size;
+	/*
+	 * Partition Reference Number of the associated physical / sparable
+	 * partition
+	 */
+	__u16   s_phys_partition_ref;
+	int	s_flags;
+	struct inode *s_metadata_fe;
+	struct inode *s_mirror_fe;
+	struct inode *s_bitmap_fe;
+};
+
+struct udf_sparing_data {
+	__u16	s_packet_len;
+	struct buffer_head *s_spar_map[4];
+};
+
+struct udf_virtual_data {
+	__u32	s_num_entries;
+	__u16	s_start_offset;
+};
+
+struct udf_bitmap {
+	__u32			s_extPosition;
+	int			s_nr_groups;
+	struct buffer_head 	*s_block_bitmap[0];
+};
+
+struct udf_part_map {
+	union {
+		struct udf_bitmap	*s_bitmap;
+		struct inode		*s_table;
+	} s_uspace;
+	union {
+		struct udf_bitmap	*s_bitmap;
+		struct inode		*s_table;
+	} s_fspace;
+	__u32	s_partition_root;
+	__u32	s_partition_len;
+	__u16	s_partition_type;
+	__u16	s_partition_num;
+	union {
+		struct udf_sparing_data s_sparing;
+		struct udf_virtual_data s_virtual;
+		struct udf_meta_data s_metadata;
+	} s_type_specific;
+	__u32	(*s_partition_func)(struct super_block *, __u32, __u16, __u32);
+	__u16	s_volumeseqnum;
+	__u16	s_partition_flags;
+};
+
+#pragma pack()
+
+struct udf_sb_info {
+	struct udf_part_map	*s_partmaps;
+	__u8			s_volume_ident[32];
+
+	/* Overall info */
+	__u16			s_partitions;
+	__u16			s_partition;
+
+	/* Sector headers */
+	__s32			s_session;
+	__u32			s_anchor;
+	__u32			s_last_block;
+
+	struct buffer_head	*s_lvid_bh;
+
+	/* Default permissions */
+	umode_t			s_umask;
+	kgid_t			s_gid;
+	kuid_t			s_uid;
+	umode_t			s_fmode;
+	umode_t			s_dmode;
+	/* Lock protecting consistency of above permission settings */
+	rwlock_t		s_cred_lock;
+
+	/* Root Info */
+	struct timespec64	s_record_time;
+
+	/* Fileset Info */
+	__u16			s_serial_number;
+
+	/* highest UDF revision we have recorded to this media */
+	__u16			s_udfrev;
+
+	/* Miscellaneous flags */
+	unsigned long		s_flags;
+
+	/* Encoding info */
+	struct nls_table	*s_nls_map;
+
+	/* VAT inode */
+	struct inode		*s_vat_inode;
+
+	struct mutex		s_alloc_mutex;
+	/* Protected by s_alloc_mutex */
+	unsigned int		s_lvid_dirty;
+};
+
+static inline struct udf_sb_info *UDF_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb);
+
+int udf_compute_nr_groups(struct super_block *sb, u32 partition);
+
+static inline int UDF_QUERY_FLAG(struct super_block *sb, int flag)
+{
+	return test_bit(flag, &UDF_SB(sb)->s_flags);
+}
+
+static inline void UDF_SET_FLAG(struct super_block *sb, int flag)
+{
+	set_bit(flag, &UDF_SB(sb)->s_flags);
+}
+
+static inline void UDF_CLEAR_FLAG(struct super_block *sb, int flag)
+{
+	clear_bit(flag, &UDF_SB(sb)->s_flags);
+}
+
+#endif /* __LINUX_UDF_SB_H */
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
new file mode 100644
index 0000000..ee24676
--- /dev/null
+++ b/fs/udf/udfdecl.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __UDF_DECL_H
+#define __UDF_DECL_H
+
+#define pr_fmt(fmt) "UDF-fs: " fmt
+
+#include "ecma_167.h"
+#include "osta_udf.h"
+
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/buffer_head.h>
+#include <linux/udf_fs_i.h>
+
+#include "udf_sb.h"
+#include "udfend.h"
+#include "udf_i.h"
+
+#define UDF_DEFAULT_PREALLOC_BLOCKS	8
+
+extern __printf(3, 4) void _udf_err(struct super_block *sb,
+		const char *function, const char *fmt, ...);
+#define udf_err(sb, fmt, ...)					\
+	_udf_err(sb, __func__, fmt, ##__VA_ARGS__)
+
+extern __printf(3, 4) void _udf_warn(struct super_block *sb,
+		const char *function, const char *fmt, ...);
+#define udf_warn(sb, fmt, ...)					\
+	_udf_warn(sb, __func__, fmt, ##__VA_ARGS__)
+
+#define udf_info(fmt, ...)					\
+	pr_info("INFO " fmt, ##__VA_ARGS__)
+
+#undef UDFFS_DEBUG
+
+#ifdef UDFFS_DEBUG
+#define udf_debug(fmt, ...)					\
+	printk(KERN_DEBUG pr_fmt("%s:%d:%s: " fmt),		\
+	       __FILE__, __LINE__, __func__, ##__VA_ARGS__)
+#else
+#define udf_debug(fmt, ...)					\
+	no_printk(fmt, ##__VA_ARGS__)
+#endif
+
+#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_INVALID_ID ((uint32_t)-1)
+
+#define UDF_NAME_PAD		4
+#define UDF_NAME_LEN		254
+#define UDF_NAME_LEN_CS0	255
+
+static inline size_t udf_file_entry_alloc_offset(struct inode *inode)
+{
+	struct udf_inode_info *iinfo = UDF_I(inode);
+	if (iinfo->i_use)
+		return sizeof(struct unallocSpaceEntry);
+	else if (iinfo->i_efe)
+		return sizeof(struct extendedFileEntry) + iinfo->i_lenEAttr;
+	else
+		return sizeof(struct fileEntry) + iinfo->i_lenEAttr;
+}
+
+static inline size_t udf_ext0_offset(struct inode *inode)
+{
+	if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
+		return udf_file_entry_alloc_offset(inode);
+	else
+		return 0;
+}
+
+/* computes tag checksum */
+u8 udf_tag_checksum(const struct tag *t);
+
+typedef uint32_t udf_pblk_t;
+
+struct dentry;
+struct inode;
+struct task_struct;
+struct buffer_head;
+struct super_block;
+
+extern const struct export_operations udf_export_ops;
+extern const struct inode_operations udf_dir_inode_operations;
+extern const struct file_operations udf_dir_operations;
+extern const struct inode_operations udf_file_inode_operations;
+extern const struct file_operations udf_file_operations;
+extern const struct inode_operations udf_symlink_inode_operations;
+extern const struct address_space_operations udf_aops;
+extern const struct address_space_operations udf_adinicb_aops;
+extern const 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 {
+	struct tag	descTag;
+	__le32		volDescSeqNum;
+};
+
+
+/* super.c */
+
+static inline void udf_updated_lvid(struct super_block *sb)
+{
+	struct buffer_head *bh = UDF_SB(sb)->s_lvid_bh;
+
+	BUG_ON(!bh);
+	WARN_ON_ONCE(((struct logicalVolIntegrityDesc *)
+		     bh->b_data)->integrityType !=
+		     cpu_to_le32(LVID_INTEGRITY_TYPE_OPEN));
+	UDF_SB(sb)->s_lvid_dirty = 1;
+}
+extern u64 lvid_get_unique_id(struct super_block *sb);
+struct inode *udf_find_metadata_inode_efe(struct super_block *sb,
+					u32 meta_file_loc, u32 partition_num);
+
+/* namei.c */
+extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *,
+			struct fileIdentDesc *, struct udf_fileident_bh *,
+			uint8_t *, uint8_t *);
+static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
+{
+	return ALIGN(sizeof(struct fileIdentDesc) +
+		le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent,
+		UDF_NAME_PAD);
+}
+
+/* file.c */
+extern long udf_ioctl(struct file *, unsigned int, unsigned long);
+/* inode.c */
+extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
+				bool hidden_inode);
+static inline struct inode *udf_iget_special(struct super_block *sb,
+					     struct kernel_lb_addr *ino)
+{
+	return __udf_iget(sb, ino, true);
+}
+static inline struct inode *udf_iget(struct super_block *sb,
+				     struct kernel_lb_addr *ino)
+{
+	return __udf_iget(sb, ino, false);
+}
+extern int udf_expand_file_adinicb(struct inode *);
+extern struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
+						  udf_pblk_t *block, int *err);
+extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
+				      int create, int *err);
+extern int udf_setsize(struct inode *, loff_t);
+extern void udf_evict_inode(struct inode *);
+extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
+extern udf_pblk_t udf_block_map(struct inode *inode, sector_t block);
+extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *,
+			 struct kernel_lb_addr *, uint32_t *, sector_t *);
+extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
+				   struct extent_position *epos);
+extern int __udf_add_aext(struct inode *inode, struct extent_position *epos,
+			  struct kernel_lb_addr *eloc, uint32_t elen, int inc);
+extern int udf_add_aext(struct inode *, struct extent_position *,
+			struct kernel_lb_addr *, uint32_t, int);
+extern void udf_write_aext(struct inode *, struct extent_position *,
+			   struct kernel_lb_addr *, uint32_t, int);
+extern int8_t udf_delete_aext(struct inode *, struct extent_position);
+extern int8_t udf_next_aext(struct inode *, struct extent_position *,
+			    struct kernel_lb_addr *, uint32_t *, int);
+extern int8_t udf_current_aext(struct inode *, struct extent_position *,
+			       struct kernel_lb_addr *, uint32_t *, int);
+
+/* misc.c */
+extern struct buffer_head *udf_tgetblk(struct super_block *sb,
+					udf_pblk_t block);
+extern struct buffer_head *udf_tread(struct super_block *sb, udf_pblk_t block);
+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 *,
+					    struct kernel_lb_addr *, uint32_t,
+					    uint16_t *);
+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 uint32_t udf_get_pblock_meta25(struct super_block *, uint32_t, uint16_t,
+					  uint32_t);
+extern int udf_relocate_blocks(struct super_block *, long, long *);
+
+static inline uint32_t
+udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc,
+		  uint32_t offset)
+{
+	return udf_get_pblock(sb, loc->logicalBlockNum,
+			loc->partitionReferenceNum, offset);
+}
+
+/* unicode.c */
+extern int udf_get_filename(struct super_block *, const uint8_t *, int,
+			    uint8_t *, int);
+extern int udf_put_filename(struct super_block *, const uint8_t *, int,
+			    uint8_t *, int);
+extern int udf_dstrCS0toChar(struct super_block *, uint8_t *, int,
+			     const uint8_t *, int);
+
+/* ialloc.c */
+extern void udf_free_inode(struct inode *);
+extern struct inode *udf_new_inode(struct inode *, umode_t);
+
+/* truncate.c */
+extern void udf_truncate_tail_extent(struct inode *);
+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 *,
+			    struct kernel_lb_addr *, uint32_t, uint32_t);
+extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t,
+			       uint32_t, uint32_t);
+extern udf_pblk_t udf_new_block(struct super_block *sb, struct inode *inode,
+				 uint16_t partition, uint32_t goal, int *err);
+
+/* directory.c */
+extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *,
+						struct udf_fileident_bh *,
+						struct fileIdentDesc *,
+						struct extent_position *,
+						struct kernel_lb_addr *, uint32_t *,
+						sector_t *);
+extern struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize,
+					       int *offset);
+extern struct long_ad *udf_get_filelongad(uint8_t *, int, uint32_t *, int);
+extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int);
+
+/* udftime.c */
+extern void udf_disk_stamp_to_time(struct timespec64 *dest,
+						struct timestamp src);
+extern void udf_time_to_disk_stamp(struct timestamp *dest, struct timespec64 src);
+
+#endif				/* __UDF_DECL_H */
diff --git a/fs/udf/udfend.h b/fs/udf/udfend.h
new file mode 100644
index 0000000..a4363ac
--- /dev/null
+++ b/fs/udf/udfend.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __UDF_ENDIAN_H
+#define __UDF_ENDIAN_H
+
+#include <asm/byteorder.h>
+#include <linux/string.h>
+
+static inline struct kernel_lb_addr lelb_to_cpu(struct lb_addr in)
+{
+	struct kernel_lb_addr out;
+
+	out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum);
+	out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum);
+
+	return out;
+}
+
+static inline struct lb_addr cpu_to_lelb(struct kernel_lb_addr in)
+{
+	struct lb_addr out;
+
+	out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum);
+	out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum);
+
+	return out;
+}
+
+static inline struct short_ad lesa_to_cpu(struct short_ad in)
+{
+	struct short_ad out;
+
+	out.extLength = le32_to_cpu(in.extLength);
+	out.extPosition = le32_to_cpu(in.extPosition);
+
+	return out;
+}
+
+static inline struct short_ad cpu_to_lesa(struct short_ad in)
+{
+	struct short_ad out;
+
+	out.extLength = cpu_to_le32(in.extLength);
+	out.extPosition = cpu_to_le32(in.extPosition);
+
+	return out;
+}
+
+static inline struct kernel_long_ad lela_to_cpu(struct long_ad in)
+{
+	struct kernel_long_ad out;
+
+	out.extLength = le32_to_cpu(in.extLength);
+	out.extLocation = lelb_to_cpu(in.extLocation);
+
+	return out;
+}
+
+static inline struct long_ad cpu_to_lela(struct kernel_long_ad in)
+{
+	struct long_ad out;
+
+	out.extLength = cpu_to_le32(in.extLength);
+	out.extLocation = cpu_to_lelb(in.extLocation);
+
+	return out;
+}
+
+static inline struct kernel_extent_ad leea_to_cpu(struct extent_ad in)
+{
+	struct kernel_extent_ad out;
+
+	out.extLength = le32_to_cpu(in.extLength);
+	out.extLocation = le32_to_cpu(in.extLocation);
+
+	return out;
+}
+
+#endif /* __UDF_ENDIAN_H */
diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c
new file mode 100644
index 0000000..fce4ad9
--- /dev/null
+++ b/fs/udf/udftime.c
@@ -0,0 +1,98 @@
+/* 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 "udfdecl.h"
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+
+void
+udf_disk_stamp_to_time(struct timespec64 *dest, struct timestamp src)
+{
+	u16 typeAndTimezone = le16_to_cpu(src.typeAndTimezone);
+	u16 year = le16_to_cpu(src.year);
+	uint8_t type = typeAndTimezone >> 12;
+	int16_t offset;
+
+	if (type == 1) {
+		offset = typeAndTimezone << 4;
+		/* sign extent offset */
+		offset = (offset >> 4);
+		if (offset == -2047) /* unspecified offset */
+			offset = 0;
+	} else
+		offset = 0;
+
+	dest->tv_sec = mktime64(year, src.month, src.day, src.hour, src.minute,
+			src.second);
+	dest->tv_sec -= offset * 60;
+	dest->tv_nsec = 1000 * (src.centiseconds * 10000 +
+			src.hundredsOfMicroseconds * 100 + src.microseconds);
+	/*
+	 * Sanitize nanosecond field since reportedly some filesystems are
+	 * recorded with bogus sub-second values.
+	 */
+	dest->tv_nsec %= NSEC_PER_SEC;
+}
+
+void
+udf_time_to_disk_stamp(struct timestamp *dest, struct timespec64 ts)
+{
+	time64_t seconds;
+	int16_t offset;
+	struct tm tm;
+
+	offset = -sys_tz.tz_minuteswest;
+
+	dest->typeAndTimezone = cpu_to_le16(0x1000 | (offset & 0x0FFF));
+
+	seconds = ts.tv_sec + offset * 60;
+	time64_to_tm(seconds, 0, &tm);
+	dest->year = cpu_to_le16(tm.tm_year + 1900);
+	dest->month = tm.tm_mon + 1;
+	dest->day = tm.tm_mday;
+	dest->hour = tm.tm_hour;
+	dest->minute = tm.tm_min;
+	dest->second = tm.tm_sec;
+	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);
+}
+
+/* EOF */
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
new file mode 100644
index 0000000..5fcfa96
--- /dev/null
+++ b/fs/udf/unicode.c
@@ -0,0 +1,402 @@
+/*
+ * 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
+ *
+ * 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/crc-itu-t.h>
+#include <linux/slab.h>
+
+#include "udf_sb.h"
+
+#define PLANE_SIZE 0x10000
+#define UNICODE_MAX 0x10ffff
+#define SURROGATE_MASK 0xfffff800
+#define SURROGATE_PAIR 0x0000d800
+#define SURROGATE_LOW  0x00000400
+#define SURROGATE_CHAR_BITS 10
+#define SURROGATE_CHAR_MASK ((1 << SURROGATE_CHAR_BITS) - 1)
+
+#define ILLEGAL_CHAR_MARK	'_'
+#define EXT_MARK		'.'
+#define CRC_MARK		'#'
+#define EXT_SIZE		5
+/* Number of chars we need to store generated CRC to make filename unique */
+#define CRC_LEN			5
+
+static unicode_t get_utf16_char(const uint8_t *str_i, int str_i_max_len,
+				int str_i_idx, int u_ch, unicode_t *ret)
+{
+	unicode_t c;
+	int start_idx = str_i_idx;
+
+	/* Expand OSTA compressed Unicode to Unicode */
+	c = str_i[str_i_idx++];
+	if (u_ch > 1)
+		c = (c << 8) | str_i[str_i_idx++];
+	if ((c & SURROGATE_MASK) == SURROGATE_PAIR) {
+		unicode_t next;
+
+		/* Trailing surrogate char */
+		if (str_i_idx >= str_i_max_len) {
+			c = UNICODE_MAX + 1;
+			goto out;
+		}
+
+		/* Low surrogate must follow the high one... */
+		if (c & SURROGATE_LOW) {
+			c = UNICODE_MAX + 1;
+			goto out;
+		}
+
+		WARN_ON_ONCE(u_ch != 2);
+		next = str_i[str_i_idx++] << 8;
+		next |= str_i[str_i_idx++];
+		if ((next & SURROGATE_MASK) != SURROGATE_PAIR ||
+		    !(next & SURROGATE_LOW)) {
+			c = UNICODE_MAX + 1;
+			goto out;
+		}
+
+		c = PLANE_SIZE +
+		    ((c & SURROGATE_CHAR_MASK) << SURROGATE_CHAR_BITS) +
+		    (next & SURROGATE_CHAR_MASK);
+	}
+out:
+	*ret = c;
+	return str_i_idx - start_idx;
+}
+
+
+static int udf_name_conv_char(uint8_t *str_o, int str_o_max_len,
+			      int *str_o_idx,
+			      const uint8_t *str_i, int str_i_max_len,
+			      int *str_i_idx,
+			      int u_ch, int *needsCRC,
+			      int (*conv_f)(wchar_t, unsigned char *, int),
+			      int translate)
+{
+	unicode_t c;
+	int illChar = 0;
+	int len, gotch = 0;
+
+	while (!gotch && *str_i_idx < str_i_max_len) {
+		if (*str_o_idx >= str_o_max_len) {
+			*needsCRC = 1;
+			return gotch;
+		}
+
+		len = get_utf16_char(str_i, str_i_max_len, *str_i_idx, u_ch,
+				     &c);
+		/* These chars cannot be converted. Replace them. */
+		if (c == 0 || c > UNICODE_MAX || (conv_f && c > MAX_WCHAR_T) ||
+		    (translate && c == '/')) {
+			illChar = 1;
+			if (!translate)
+				gotch = 1;
+		} else if (illChar)
+			break;
+		else
+			gotch = 1;
+		*str_i_idx += len;
+	}
+	if (illChar) {
+		*needsCRC = 1;
+		c = ILLEGAL_CHAR_MARK;
+		gotch = 1;
+	}
+	if (gotch) {
+		if (conv_f) {
+			len = conv_f(c, &str_o[*str_o_idx],
+				     str_o_max_len - *str_o_idx);
+		} else {
+			len = utf32_to_utf8(c, &str_o[*str_o_idx],
+					    str_o_max_len - *str_o_idx);
+			if (len < 0)
+				len = -ENAMETOOLONG;
+		}
+		/* Valid character? */
+		if (len >= 0)
+			*str_o_idx += len;
+		else if (len == -ENAMETOOLONG) {
+			*needsCRC = 1;
+			gotch = 0;
+		} else {
+			str_o[(*str_o_idx)++] = ILLEGAL_CHAR_MARK;
+			*needsCRC = 1;
+		}
+	}
+	return gotch;
+}
+
+static int udf_name_from_CS0(struct super_block *sb,
+			     uint8_t *str_o, int str_max_len,
+			     const uint8_t *ocu, int ocu_len,
+			     int translate)
+{
+	uint32_t c;
+	uint8_t cmp_id;
+	int idx, len;
+	int u_ch;
+	int needsCRC = 0;
+	int ext_i_len, ext_max_len;
+	int str_o_len = 0;	/* Length of resulting output */
+	int ext_o_len = 0;	/* Extension output length */
+	int ext_crc_len = 0;	/* Extension output length if used with CRC */
+	int i_ext = -1;		/* Extension position in input buffer */
+	int o_crc = 0;		/* Rightmost possible output pos for CRC+ext */
+	unsigned short valueCRC;
+	uint8_t ext[EXT_SIZE * NLS_MAX_CHARSET_SIZE + 1];
+	uint8_t crc[CRC_LEN];
+	int (*conv_f)(wchar_t, unsigned char *, int);
+
+	if (str_max_len <= 0)
+		return 0;
+
+	if (ocu_len == 0) {
+		memset(str_o, 0, str_max_len);
+		return 0;
+	}
+
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+		conv_f = UDF_SB(sb)->s_nls_map->uni2char;
+	else
+		conv_f = NULL;
+
+	cmp_id = ocu[0];
+	if (cmp_id != 8 && cmp_id != 16) {
+		memset(str_o, 0, str_max_len);
+		pr_err("unknown compression code (%u)\n", cmp_id);
+		return -EINVAL;
+	}
+	u_ch = cmp_id >> 3;
+
+	ocu++;
+	ocu_len--;
+
+	if (ocu_len % u_ch) {
+		pr_err("incorrect filename length (%d)\n", ocu_len + 1);
+		return -EINVAL;
+	}
+
+	if (translate) {
+		/* Look for extension */
+		for (idx = ocu_len - u_ch, ext_i_len = 0;
+		     (idx >= 0) && (ext_i_len < EXT_SIZE);
+		     idx -= u_ch, ext_i_len++) {
+			c = ocu[idx];
+			if (u_ch > 1)
+				c = (c << 8) | ocu[idx + 1];
+
+			if (c == EXT_MARK) {
+				if (ext_i_len)
+					i_ext = idx;
+				break;
+			}
+		}
+		if (i_ext >= 0) {
+			/* Convert extension */
+			ext_max_len = min_t(int, sizeof(ext), str_max_len);
+			ext[ext_o_len++] = EXT_MARK;
+			idx = i_ext + u_ch;
+			while (udf_name_conv_char(ext, ext_max_len, &ext_o_len,
+						  ocu, ocu_len, &idx,
+						  u_ch, &needsCRC,
+						  conv_f, translate)) {
+				if ((ext_o_len + CRC_LEN) < str_max_len)
+					ext_crc_len = ext_o_len;
+			}
+		}
+	}
+
+	idx = 0;
+	while (1) {
+		if (translate && (idx == i_ext)) {
+			if (str_o_len > (str_max_len - ext_o_len))
+				needsCRC = 1;
+			break;
+		}
+
+		if (!udf_name_conv_char(str_o, str_max_len, &str_o_len,
+					ocu, ocu_len, &idx,
+					u_ch, &needsCRC, conv_f, translate))
+			break;
+
+		if (translate &&
+		    (str_o_len <= (str_max_len - ext_o_len - CRC_LEN)))
+			o_crc = str_o_len;
+	}
+
+	if (translate) {
+		if (str_o_len <= 2 && str_o[0] == '.' &&
+		    (str_o_len == 1 || str_o[1] == '.'))
+			needsCRC = 1;
+		if (needsCRC) {
+			str_o_len = o_crc;
+			valueCRC = crc_itu_t(0, ocu, ocu_len);
+			crc[0] = CRC_MARK;
+			crc[1] = hex_asc_upper_hi(valueCRC >> 8);
+			crc[2] = hex_asc_upper_lo(valueCRC >> 8);
+			crc[3] = hex_asc_upper_hi(valueCRC);
+			crc[4] = hex_asc_upper_lo(valueCRC);
+			len = min_t(int, CRC_LEN, str_max_len - str_o_len);
+			memcpy(&str_o[str_o_len], crc, len);
+			str_o_len += len;
+			ext_o_len = ext_crc_len;
+		}
+		if (ext_o_len > 0) {
+			memcpy(&str_o[str_o_len], ext, ext_o_len);
+			str_o_len += ext_o_len;
+		}
+	}
+
+	return str_o_len;
+}
+
+static int udf_name_to_CS0(struct super_block *sb,
+			   uint8_t *ocu, int ocu_max_len,
+			   const uint8_t *str_i, int str_len)
+{
+	int i, len;
+	unsigned int max_val;
+	int u_len, u_ch;
+	unicode_t uni_char;
+	int (*conv_f)(const unsigned char *, int, wchar_t *);
+
+	if (ocu_max_len <= 0)
+		return 0;
+
+	if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+		conv_f = UDF_SB(sb)->s_nls_map->char2uni;
+	else
+		conv_f = NULL;
+
+	memset(ocu, 0, ocu_max_len);
+	ocu[0] = 8;
+	max_val = 0xff;
+	u_ch = 1;
+
+try_again:
+	u_len = 1;
+	for (i = 0; i < str_len; i += len) {
+		/* Name didn't fit? */
+		if (u_len + u_ch > ocu_max_len)
+			return 0;
+		if (conv_f) {
+			wchar_t wchar;
+
+			len = conv_f(&str_i[i], str_len - i, &wchar);
+			if (len > 0)
+				uni_char = wchar;
+		} else {
+			len = utf8_to_utf32(&str_i[i], str_len - i,
+					    &uni_char);
+		}
+		/* Invalid character, deal with it */
+		if (len <= 0 || uni_char > UNICODE_MAX) {
+			len = 1;
+			uni_char = '?';
+		}
+
+		if (uni_char > max_val) {
+			unicode_t c;
+
+			if (max_val == 0xff) {
+				max_val = 0xffff;
+				ocu[0] = 0x10;
+				u_ch = 2;
+				goto try_again;
+			}
+			/*
+			 * Use UTF-16 encoding for chars outside we
+			 * cannot encode directly.
+			 */
+			if (u_len + 2 * u_ch > ocu_max_len)
+				return 0;
+
+			uni_char -= PLANE_SIZE;
+			c = SURROGATE_PAIR |
+			    ((uni_char >> SURROGATE_CHAR_BITS) &
+			     SURROGATE_CHAR_MASK);
+			ocu[u_len++] = (uint8_t)(c >> 8);
+			ocu[u_len++] = (uint8_t)(c & 0xff);
+			uni_char = SURROGATE_PAIR | SURROGATE_LOW |
+					(uni_char & SURROGATE_CHAR_MASK);
+		}
+
+		if (max_val == 0xffff)
+			ocu[u_len++] = (uint8_t)(uni_char >> 8);
+		ocu[u_len++] = (uint8_t)(uni_char & 0xff);
+	}
+
+	return u_len;
+}
+
+/*
+ * Convert CS0 dstring to output charset. Warning: This function may truncate
+ * input string if it is too long as it is used for informational strings only
+ * and it is better to truncate the string than to refuse mounting a media.
+ */
+int udf_dstrCS0toChar(struct super_block *sb, uint8_t *utf_o, int o_len,
+		      const uint8_t *ocu_i, int i_len)
+{
+	int s_len = 0;
+
+	if (i_len > 0) {
+		s_len = ocu_i[i_len - 1];
+		if (s_len >= i_len) {
+			pr_warn("incorrect dstring lengths (%d/%d),"
+				" truncating\n", s_len, i_len);
+			s_len = i_len - 1;
+			/* 2-byte encoding? Need to round properly... */
+			if (ocu_i[0] == 16)
+				s_len -= (s_len - 1) & 2;
+		}
+	}
+
+	return udf_name_from_CS0(sb, utf_o, o_len, ocu_i, s_len, 0);
+}
+
+int udf_get_filename(struct super_block *sb, const uint8_t *sname, int slen,
+		     uint8_t *dname, int dlen)
+{
+	int ret;
+
+	if (!slen)
+		return -EIO;
+
+	if (dlen <= 0)
+		return 0;
+
+	ret = udf_name_from_CS0(sb, dname, dlen, sname, slen, 1);
+	/* Zero length filename isn't valid... */
+	if (ret == 0)
+		ret = -EINVAL;
+	return ret;
+}
+
+int udf_put_filename(struct super_block *sb, const uint8_t *sname, int slen,
+		     uint8_t *dname, int dlen)
+{
+	return udf_name_to_CS0(sb, dname, dlen, sname, slen);
+}
+