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