diff -Nru linux/Documentation/Configure.help linux.new/Documentation/Configure.help --- linux/Documentation/Configure.help Fri Sep 7 11:18:50 2001 +++ linux.new/Documentation/Configure.help Fri Sep 7 11:32:15 2001 @@ -9538,6 +9538,26 @@ like lynx or netscape). Say Y here if you want to be able to read Joliet CDROMs under Linux. +UDF Filesystem support (EXPERIMENTAL) +CONFIG_UDF_FS + This is the new filesystem used by some CDROMS and DVD drivers. Say + Y if you intend to mount DVD discs or CDRWs written in packet mode, + or if written to by other UDF utilities, such as DirectCD. Please + read Documentation/filesystems/udf.txt + + This filesystem support is also available as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). The module is called udf.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + + If unsure, say N. + +UDF read-write support (FOR TESTING ONLY) +CONFIG_UDF_RW + Say Y if you want to test write support for UDF filesystems. + Due to lack of support for writing to CDR/CDRW's, this option + is only supported for Hard Discs, DVD-RAM, and loopback files. + fat fs support CONFIG_FAT_FS If you want to use one of the FAT-based filesystems (the MS-DOS, diff -Nru linux/Documentation/filesystems/00-INDEX linux.new/Documentation/filesystems/00-INDEX --- linux/Documentation/filesystems/00-INDEX Sun Mar 25 18:31:56 2001 +++ linux.new/Documentation/filesystems/00-INDEX Fri Sep 7 11:32:15 2001 @@ -22,6 +22,8 @@ - info on using filesystems with the SMB protocol (Windows 3.11 and NT) sysv-fs.txt - info on the SystemV/Coherent filesystem. +udf.txt + - info and mount options for the UDF filesystem. ufs.txt - info on the ufs filesystem. umsdos.txt diff -Nru linux/Documentation/filesystems/udf.txt linux.new/Documentation/filesystems/udf.txt --- linux/Documentation/filesystems/udf.txt Thu Jan 1 01:00:00 1970 +++ linux.new/Documentation/filesystems/udf.txt Fri Sep 7 11:32:15 2001 @@ -0,0 +1,60 @@ +* +* ./Documentation/filesystems/udf.txt +* +UDF Filesystem version 0.9.2.1 + +If you encounter problems with reading UDF discs using this driver, +please report them to linux_udf@hpesjro.fc.hp.com, which is the +developer's list. + +Write support requires a block driver which supports writing. The current +scsi and ide cdrom drivers do not support writing. + +------------------------------------------------------------------------------- +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) + strict Set strict conformance (unused) + +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. +------------------------------------------------------------------------------- + + +For more information see: + http://www.trylinux.com/projects/udf/index.html + +For the latest version and toolset see: + http://www.csc.calpoly.edu/~bfennema/udf.html + http://linux-udf.sourceforge.net/ + +Documentation on UDF and ECMA 167 is available FREE from: + http://www.osta.org/ + http://www.ecma.ch/ + +Ben Fennema diff -Nru linux/Documentation/ioctl-number.txt linux.new/Documentation/ioctl-number.txt --- linux/Documentation/ioctl-number.txt Sun Mar 25 18:37:29 2001 +++ linux.new/Documentation/ioctl-number.txt Fri Sep 7 11:32:15 2001 @@ -96,6 +96,8 @@ 'k' all asm-sparc/kbio.h, asm-sparc64/kbio.h 'l' 00-3F linux/tcfs_fs.h in development: +'l' 40-7F linux/udf_fs_i.h in development: + 'm' all linux/mtio.h conflict! 'm' all linux/soundcard.h conflict! 'n' all linux/ncp_fs.h diff -Nru linux/MAINTAINERS linux.new/MAINTAINERS --- linux/MAINTAINERS Fri Sep 7 11:18:22 2001 +++ linux.new/MAINTAINERS Fri Sep 7 11:32:15 2001 @@ -1042,6 +1042,15 @@ L: linux-scsi@vger.kernel.org S: Maintained +UDF FILESYSTEM +P: Ben Fennema +M: bfennema@falcon.csc.calpoly.edu +P: Dave Boynton +M: dave@trylinux.com +L: linux_udf@hootie.lvld.hp.com +W: http://www.trylinux.com/projects/udf/index.html +S: Maintained + UMSDOS FILESYSTEM P: Matija Nalis M: mnalis-umsdos@voyager.hr diff -Nru linux/fs/Config.in linux.new/fs/Config.in --- linux/fs/Config.in Fri Sep 7 11:18:14 2001 +++ linux.new/fs/Config.in Fri Sep 7 11:32:16 2001 @@ -31,6 +31,13 @@ # needed by nls/Config.in define_bool CONFIG_JOLIET n fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'UDF filesystem support (EXPERIMENTAL)' CONFIG_UDF_FS + if [ "$CONFIG_UDF_FS" != "n" ]; then + bool ' UDF read-write support (FOR TESTING ONLY)' CONFIG_UDF_RW + fi +fi + tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_FS diff -Nru linux.orig/fs/Makefile linux/fs/Makefile --- linux.orig/fs/Makefile Wed Nov 21 21:18:15 2001 +++ linux/fs/Makefile Wed Nov 21 21:27:00 2001 @@ -19,7 +19,7 @@ ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \ reiserfs \ hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd \ - nfsd nls devpts adfs qnx4 efs + nfsd nls devpts adfs qnx4 udf efs ifeq ($(CONFIG_QUOTA),y) O_OBJS += dquot.o @@ -232,6 +232,14 @@ endif endif +ifeq ($(CONFIG_UDF_FS),y) +SUB_DIRS += udf +else + ifeq ($(CONFIG_UDF_FS),m) + MOD_SUB_DIRS += udf + endif +endif + ifeq ($(CONFIG_AUTOFS_FS),y) SUB_DIRS += autofs else diff -Nru linux/fs/filesystems.c linux.new/fs/filesystems.c --- linux/fs/filesystems.c Fri Sep 7 11:17:42 2001 +++ linux.new/fs/filesystems.c Fri Sep 7 11:32:16 2001 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -163,6 +164,9 @@ init_qnx4_fs(); #endif +#ifdef CONFIG_UDF_FS + init_udf_fs(); +#endif #ifdef CONFIG_NLS init_nls(); #endif diff -Nru linux/fs/udf/Makefile linux.new/fs/udf/Makefile --- linux/fs/udf/Makefile Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/Makefile Fri Sep 7 11:32:16 2001 @@ -0,0 +1,16 @@ +# +# Makefile for the linux udf-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .o file). +# +# Note 2! The CFLAGS definitions are now in the main makefile.. + +O_TARGET := udf.o +O_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 +M_OBJS := $(O_TARGET) + +include $(TOPDIR)/Rules.make diff -Nru linux/fs/udf/balloc.c linux.new/fs/udf/balloc.c --- linux/fs/udf/balloc.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/balloc.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,1008 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 02/24/99 blf Created. + * + */ + +#include "udfdecl.h" +#include +#include +#include +#include + +#include + +#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)) + +extern inline int find_next_one_bit (void * addr, int size, int offset) +{ + unsigned long * p = ((unsigned long *) addr) + (offset / BITS_PER_LONG); + unsigned long 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; + 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), sb->s_blocksize); + 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; + + if (bitmap->s_block_bitmap[block_group]) + return 0; + else + 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(const struct inode * inode, + struct udf_bitmap *bitmap, lb_addr bloc, Uint32 offset, Uint32 count) +{ + struct buffer_head * bh = NULL; + unsigned long block; + unsigned long block_group; + unsigned long bit; + unsigned long i; + int bitmap_nr; + unsigned long overflow; + struct super_block * sb; + + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device"); + return; + } + + lock_super(sb); + 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 + { + DQUOT_FREE_BLOCK(sb, 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, 1); + 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), 1); + unlock_super(sb); + return; +} + +static int udf_bitmap_prealloc_blocks(const struct inode * inode, + struct udf_bitmap *bitmap, Uint16 partition, Uint32 first_block, + Uint32 block_count) +{ + int alloc_count = 0; + int bit, block, block_group, group_start; + int nr_groups, bitmap_nr; + struct buffer_head *bh; + struct super_block *sb; + + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device\n"); + return 0; + } + lock_super(sb); + + if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) + goto out; + +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(sb, 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(sb, inode, 1); + goto out; + } + block_count --; + alloc_count ++; + bit ++; + block ++; + } + mark_buffer_dirty(bh, 1); + 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), 1); + } + sb->s_dirt = 1; + unlock_super(sb); + return alloc_count; +} + +static int udf_bitmap_new_block(const struct inode * inode, + struct udf_bitmap *bitmap,Uint16 partition, Uint32 goal, int *err) +{ + int tmp, newbit, bit=0, block, block_group, group_start; + int end_goal, nr_groups, bitmap_nr, i; + struct buffer_head *bh = NULL; + struct super_block *sb; + char *ptr; + int newblock = 0; + + *err = -ENOSPC; + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device\n"); + return newblock; + } + lock_super(sb); + +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)) + { + unlock_super(sb); + 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) + { + unlock_super(sb); + 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 (DQUOT_ALLOC_BLOCK(sb, inode, 1)) + { + unlock_super(sb); + *err = -EDQUOT; + return 0; + } + + newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) - + (sizeof(struct SpaceBitmapDesc) << 3); + + tmp = udf_get_pblock(sb, newblock, partition, 0); + if (!udf_clear_bit(bit, bh->b_data)) + { + udf_debug("bit already cleared for block %d\n", bit); + goto repeat; + } + + mark_buffer_dirty(bh, 1); + if (!(bh = getblk(sb->s_dev, tmp, sb->s_blocksize))) + { + udf_debug("cannot get block %d\n", tmp); + unlock_super(sb); + return 0; + } + memset(bh->b_data, 0, sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + udf_release_data(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), 1); + } + sb->s_dirt = 1; + unlock_super(sb); + *err = 0; + return newblock; + +error_return: + *err = -EIO; + unlock_super(sb); + return 0; +} + +static void udf_table_free_blocks(const struct inode * inode, + struct inode * table, lb_addr bloc, Uint32 offset, Uint32 count) +{ + struct super_block * sb; + Uint32 start, end; + Uint32 nextoffset, oextoffset, elen; + lb_addr nbloc, obloc, eloc; + struct buffer_head *obh, *nbh; + char etype; + int i; + + udf_debug("ino=%ld, bloc=%d, offset=%d, count=%d\n", + inode->i_ino, bloc.logicalBlockNum, offset, count); + + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device"); + return; + } + + if (table == NULL) + return; + + lock_super(sb); + 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 */ + DQUOT_FREE_BLOCK(sb, 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), 1); + } + + start = bloc.logicalBlockNum + offset; + end = bloc.logicalBlockNum + offset + count - 1; + + oextoffset = nextoffset = sizeof(struct UnallocatedSpaceEntry); + elen = 0; + obloc = nbloc = UDF_I_LOCATION(table); + + obh = nbh = udf_tread(sb, udf_get_lb_pblock(sb, nbloc, 0), sb->s_blocksize); + nbh->b_count ++; + + 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 (memcmp(&nbloc, &obloc, sizeof(lb_addr))) + { + i = -1; + obloc = nbloc; + udf_release_data(obh); + 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 = (EXTENT_RECORDED_ALLOCATED << 30) | + (count << sb->s_blocksize_bits); + + if (UDF_I_ALLOCTYPE(table) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(table) == ICB_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), + sb->s_blocksize))) + { + 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); + 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 (memcmp(&UDF_I_LOCATION(table), &obloc, sizeof(lb_addr))) + { + 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); + } + } + udf_new_tag(nbh->b_data, TID_ALLOC_EXTENT_DESC, 2, 1, + nbloc.logicalBlockNum, sizeof(tag)); + switch (UDF_I_ALLOCTYPE(table)) + { + case ICB_FLAG_AD_SHORT: + { + sad = (short_ad *)sptr; + sad->extLength = cpu_to_le32( + EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + sb->s_blocksize); + sad->extPosition = cpu_to_le32(nbloc.logicalBlockNum); + break; + } + case ICB_FLAG_AD_LONG: + { + lad = (long_ad *)sptr; + lad->extLength = cpu_to_le32( + EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + sb->s_blocksize); + lad->extLocation = cpu_to_lelb(nbloc); + break; + } + } + udf_update_tag(obh->b_data, loffset); + mark_buffer_dirty(obh, 1); + } + + if (elen) /* It's possible that stealing the block emptied the extent */ + { + udf_write_aext(table, nbloc, &nextoffset, eloc, elen, nbh, 1); + + if (!memcmp(&UDF_I_LOCATION(table), &nbloc, sizeof(lb_addr))) + { + 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, 1); + } + } + } + + udf_release_data(nbh); + udf_release_data(obh); + +error_return: + sb->s_dirt = 1; + unlock_super(sb); + return; +} + +static int udf_table_prealloc_blocks(const struct inode * inode, + struct inode *table, Uint16 partition, Uint32 first_block, + Uint32 block_count) +{ + struct super_block *sb; + int alloc_count = 0; + Uint32 extoffset, elen, adsize; + lb_addr bloc, eloc; + struct buffer_head *bh; + char etype = -1; + + udf_debug("ino=%ld, partition=%d, first_block=%d, block_count=%d\n", + inode->i_ino, partition, first_block, block_count); + + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device\n"); + return 0; + } + + if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) + return 0; + + if (table == NULL) + return 0; + + if (UDF_I_ALLOCTYPE(table) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(table) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + return 0; + + lock_super(sb); + + extoffset = sizeof(struct UnallocatedSpaceEntry); + bloc = UDF_I_LOCATION(table); + + bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0), sb->s_blocksize); + 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 (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), 1); + } + sb->s_dirt = 1; + unlock_super(sb); + udf_debug("alloc_count=%d\n", alloc_count); + return alloc_count; +} + +static int udf_table_new_block(const struct inode * inode, + struct inode *table, Uint16 partition, Uint32 goal, int *err) +{ + struct super_block *sb; + Uint32 spread = 0xFFFFFFFF, nspread; + Uint32 newblock = 0, tmp, adsize; + Uint32 extoffset, goal_extoffset, elen, goal_elen = 0; + lb_addr bloc, goal_bloc, eloc, goal_eloc; + struct buffer_head *bh, *goal_bh; + char etype; + + udf_debug("ino=%ld, partition=%d, goal=%d\n", + inode->i_ino, partition, goal); + + *err = -ENOSPC; + sb = inode->i_sb; + if (!sb) + { + udf_debug("nonexistent device\n"); + return newblock; + } + + if (table == NULL) + return newblock; + + if (UDF_I_ALLOCTYPE(table) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(table) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + return newblock; + + lock_super(sb); + + 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 UnallocatedSpaceEntry); + bloc = UDF_I_LOCATION(table); + + goal_bh = bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0), sb->s_blocksize); + goal_bh->b_count ++; + + 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; + 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); + unlock_super(sb); + 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; + + tmp = udf_get_pblock(sb, newblock, partition, 0); + if (!(bh = getblk(sb->s_dev, tmp, sb->s_blocksize))) + { + udf_debug("cannot get block %d\n", tmp); + udf_release_data(bh); + unlock_super(sb); + return 0; + } + memset(bh->b_data, 0, sb->s_blocksize); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + udf_release_data(bh); + + 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), 1); + } + + sb->s_dirt = 1; + unlock_super(sb); + *err = 0; + return newblock; +} + +inline void udf_free_blocks(const struct inode * inode, lb_addr bloc, + Uint32 offset, Uint32 count) +{ + if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_UNALLOC_BITMAP) + { + return udf_bitmap_free_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_uspace.s_bitmap, + bloc, offset, count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_UNALLOC_TABLE) + { + return udf_table_free_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_uspace.s_table, + bloc, offset, count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_FREED_BITMAP) + { + return udf_bitmap_free_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_fspace.s_bitmap, + bloc, offset, count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, bloc.partitionReferenceNum) & UDF_PART_FLAG_FREED_TABLE) + { + return udf_table_free_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[bloc.partitionReferenceNum].s_fspace.s_table, + bloc, offset, count); + } + else + return; +} + +inline int udf_prealloc_blocks(const struct inode * inode, Uint16 partition, + Uint32 first_block, Uint32 block_count) +{ + if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) + { + return udf_bitmap_prealloc_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.s_bitmap, + partition, first_block, block_count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) + { + return udf_table_prealloc_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.s_table, + partition, first_block, block_count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_BITMAP) + { + return udf_bitmap_prealloc_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.s_bitmap, + partition, first_block, block_count); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_TABLE) + { + return udf_table_prealloc_blocks(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.s_table, + partition, first_block, block_count); + } + else + return 0; +} + +inline int udf_new_block(const struct inode * inode, Uint16 partition, + Uint32 goal, int *err) +{ + if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) + { + return udf_bitmap_new_block(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.s_bitmap, + partition, goal, err); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) + { + return udf_table_new_block(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_uspace.s_table, + partition, goal, err); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_BITMAP) + { + return udf_bitmap_new_block(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.s_bitmap, + partition, goal, err); + } + else if (UDF_SB_PARTFLAGS(inode->i_sb, partition) & UDF_PART_FLAG_FREED_TABLE) + { + return udf_table_new_block(inode, + UDF_SB_PARTMAPS(inode->i_sb)[partition].s_fspace.s_table, + partition, goal, err); + } + else + { + *err = -EIO; + return 0; + } +} diff -Nru linux/fs/udf/crc.c linux.new/fs/udf/crc.c --- linux/fs/udf/crc.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/crc.c Fri Sep 7 11:32:16 2001 @@ -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@hootie.lvld.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 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 + * CRC of the data block. + * + * HISTORY + * July 21, 1997 - Andrew E. Mileski + * Adapted from OSTA-UDF(tm) 1.50 standard. + */ +extern Uint16 +udf_crc(Uint8 *data, Uint32 size, Uint16 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 + +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 -Nru linux/fs/udf/dir.c linux.new/fs/udf/dir.c --- linux/fs/udf/dir.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/dir.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,283 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * + * HISTORY + * + * 10/05/98 dgb Split directory operations into it's own file + * Implemented directory reads via do_udf_readdir + * 10/06/98 Made directory operations work! + * 11/17/98 Rewrote directory to support ICB_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" + +#if defined(__linux__) && defined(__KERNEL__) +#include +#include "udf_i.h" +#include "udf_sb.h" +#include +#include +#include +#include +#include +#endif + +/* 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 */ + +static struct file_operations udf_dir_operations = { + NULL, /* lllseek */ + NULL, /* read */ + NULL, /* write */ + udf_readdir, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* flush */ + NULL, /* release */ + udf_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations udf_dir_inode_operations = { + &udf_dir_operations, + udf_create, /* create */ + udf_lookup, /* lookup */ + udf_link, /* link */ + udf_unlink, /* unlink */ + udf_symlink, /* symlink */ + udf_mkdir, /* mkdir */ + udf_rmdir, /* rmdir */ + udf_mknod, /* mknod */ + udf_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; + +/* + * 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 + * >=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; + + if (!dir) + return -EBADF; + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + if ( filp->f_pos == 0 ) + { + if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino)) + return 0; + } + + result = do_udf_readdir(dir, filp, filldir, dirent); + UPDATE_ATIME(dir); + 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; + int nf_pos = filp->f_pos; + int flen; + char fname[255]; + char *nameptr; + Uint16 liu; + Uint8 lfi; + int size = (udf_ext0_offset(dir) + dir->i_size) >> 2; + struct buffer_head * bh = NULL; + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + + if (nf_pos >= size) + return 1; + + 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 (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return 0; + } + + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + { + udf_release_data(bh); + return 0; + } + + while ( nf_pos < size ) + { + filp->f_pos = nf_pos; + + 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 1; + } + + 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 & FILE_DELETED) != 0 ) + { + if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) + continue; + } + + if ( (cfi.fileCharacteristics & FILE_HIDDEN) != 0 ) + { + if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) + continue; + } + + iblock = udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0); + + if (!lfi) /* parent directory */ + { + if (filldir(dirent, "..", 2, filp->f_pos, filp->f_dentry->d_parent->d_inode->i_ino)) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 1; + } + } + else + { + if ((flen = udf_get_filename(nameptr, fname, lfi))) + { + if (filldir(dirent, fname, flen, filp->f_pos, iblock)) + { + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + return 1; /* halt enum */ + } + } + else + { + udf_debug("size=%d, nf_pos=%d, liu=%d, lfi=%d\n", size, nf_pos, liu, lfi); + } + } + } /* end while */ + + filp->f_pos = nf_pos; + + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + udf_release_data(bh); + + if ( filp->f_pos >= size) + return 1; + else + return 0; +} diff -Nru linux/fs/udf/directory.c linux.new/fs/udf/directory.c --- linux/fs/udf/directory.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/directory.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,330 @@ +/* + * 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@hootie.lvld.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" + +#if defined(__linux__) && defined(__KERNEL__) + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + +#ifdef __KERNEL__ + +Uint8 * udf_filead_read(struct inode *dir, Uint8 *tmpad, Uint8 ad_size, + lb_addr fe_loc, int *pos, int *offset, struct buffer_head **bh, int *error) +{ + int loffset = *offset; + int block; + Uint8 *ad; + int remainder; + + *error = 0; + + ad = (Uint8 *)(*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, dir->i_sb->s_blocksize))) + return NULL; + } + else if (*offset > dir->i_sb->s_blocksize) + { + ad = tmpad; + + remainder = dir->i_sb->s_blocksize - loffset; + memcpy((Uint8 *)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, dir->i_sb->s_blocksize))) + return NULL; + + memcpy((Uint8 *)ad + remainder, (*bh)->b_data, ad_size - remainder); + *offset = ad_size - remainder; + } + return ad; +} + +struct FileIdentDesc * +udf_fileident_read(struct inode *dir, int *nf_pos, + struct udf_fileident_bh *fibh, + struct FileIdentDesc *cfi, + lb_addr *bloc, Uint32 *extoffset, + lb_addr *eloc, Uint32 *elen, + Uint32 *offset, struct buffer_head **bh) +{ + struct FileIdentDesc *fi; + int block; + + fibh->soffset = fibh->eoffset; + + if (fibh->eoffset == dir->i_sb->s_blocksize) + { + int lextoffset = *extoffset; + + if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != + EXTENT_RECORDED_ALLOCATED) + { + 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, dir->i_sb->s_blocksize))) + return NULL; + fibh->soffset = fibh->eoffset = 0; + } + 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 *)cfi, (Uint8 *)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) != + EXTENT_RECORDED_ALLOCATED) + { + 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, dir->i_sb->s_blocksize))) + return NULL; + + if (sizeof(struct FileIdentDesc) > - fibh->soffset) + { + int fi_len; + + memcpy((Uint8 *)cfi, (Uint8 *)fi, - fibh->soffset); + memcpy((Uint8 *)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 *)cfi, (Uint8 *)fi, sizeof(struct FileIdentDesc)); + } + } + return fi; +} +#endif + +struct FileIdentDesc * +udf_get_fileident(void * buffer, int bufsize, int * offset) +{ + struct FileIdentDesc *fi; + int lengthThisIdent; + Uint8 * ptr; + int padlen; + + if ( (!buffer) || (!offset) ) { +#ifdef __KERNEL__ + udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset); +#endif + return NULL; + } + + ptr = buffer; + + if ( (*offset > 0) && (*offset < bufsize) ) { + ptr += *offset; + } + fi=(struct FileIdentDesc *)ptr; + if (le16_to_cpu(fi->descTag.tagIdent) != TID_FILE_IDENT_DESC) + { +#ifdef __KERNEL__ + udf_debug("0x%x != TID_FILE_IDENT_DESC\n", + le16_to_cpu(fi->descTag.tagIdent)); + udf_debug("offset: %u sizeof: %lu bufsize: %u\n", + *offset, (unsigned long)sizeof(struct FileIdentDesc), bufsize); +#endif + 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; +} + +extent_ad * +udf_get_fileextent(void * buffer, int bufsize, int * offset) +{ + extent_ad * ext; + struct FileEntry *fe; + Uint8 * ptr; + + if ( (!buffer) || (!offset) ) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n"); +#endif + return NULL; + } + + fe = (struct FileEntry *)buffer; + + if ( le16_to_cpu(fe->descTag.tagIdent) != TID_FILE_ENTRY ) + { +#ifdef __KERNEL__ + udf_debug("0x%x != TID_FILE_ENTRY\n", + le16_to_cpu(fe->descTag.tagIdent)); +#endif + return NULL; + } + + ptr=(Uint8 *)(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; +} + +short_ad * +udf_get_fileshortad(void * buffer, int maxoffset, int *offset, int inc) +{ + short_ad * sa; + Uint8 * ptr; + + if ( (!buffer) || (!offset) ) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n"); +#endif + return NULL; + } + + ptr = (Uint8 *)buffer; + + if ( (*offset > 0) && (*offset < maxoffset) ) + ptr += *offset; + else + return NULL; + + if ((sa = (short_ad *)ptr)->extLength == 0) + return NULL; + else if (inc) + (*offset) += sizeof(short_ad); + return sa; +} + +long_ad * +udf_get_filelongad(void * buffer, int maxoffset, int * offset, int inc) +{ + long_ad * la; + Uint8 * ptr; + + if ( (!buffer) || !(offset) ) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n"); +#endif + return NULL; + } + + ptr = (Uint8 *)buffer; + + if ( (*offset > 0) && (*offset < maxoffset) ) + ptr += *offset; + else + return NULL; + + if ((la = (long_ad *)ptr)->extLength == 0) + return NULL; + else if (inc) + (*offset) += sizeof(long_ad); + return la; +} diff -Nru linux/fs/udf/file.c linux.new/fs/udf/file.c --- linux/fs/udf/file.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/file.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,549 @@ +/* + * 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@hootie.lvld.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-2000 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 + * ICB_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 +#include +#include +#include +#include /* memset */ +#include +#include + +#include "udf_i.h" +#include "udf_sb.h" + +#define NBUF 32 + +typedef void * poll_table; + +/* + * Make sure the offset never goes beyond the 32-bit mark.. + */ +static long long udf_file_llseek(struct file * file, long long offset, int origin) +{ + struct inode * inode = file->f_dentry->d_inode; + + switch (origin) + { + case 2: + { + offset += inode->i_size; + break; + } + case 1: + { + offset += file->f_pos; + break; + } + } +#if BITS_PER_LONG < 64 + if (((unsigned long long) offset >> 32) != 0) + { + return -EINVAL; + } +#endif + if (offset != file->f_pos) + { + file->f_pos = offset; + file->f_reada = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + file->f_version = ++event; +#else + file->f_version = ++global_event; +#endif + } + return offset; +} + +static inline void remove_suid(struct inode * inode) +{ + unsigned int mode; + + /* set S_IGID if S_IXGRP is set, and always set S_ISUID */ + mode = (inode->i_mode & S_IXGRP)*(S_ISGID/S_IXGRP) | S_ISUID; + + /* was any of the uid bits set? */ + mode &= inode->i_mode; + if (mode && !capable(CAP_FSETID)) + { + inode->i_mode &= ~mode; + mark_inode_dirty(inode); + } +} + +static ssize_t udf_file_write(struct file * filp, const char * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode = filp->f_dentry->d_inode; + off_t pos; + long block; + int offset; + int written, c; + struct buffer_head * bh, * bufferlist[NBUF]; + struct super_block * sb; + int err; + int i, buffercount, write_error, new_buffer; + unsigned long limit; + + /* POSIX: mtime/ctime may not change for 0 count */ + if (!count) + return 0; + /* This makes the bounds-checking arithmetic later on much more + * sane. */ + write_error = buffercount = 0; + if (!inode) + { + printk("udf_file_write: inode = NULL\n"); + return -EINVAL; + } + sb = inode->i_sb; + if (sb->s_flags & MS_RDONLY) + { + return -ENOSPC; + } + + if (!S_ISREG(inode->i_mode)) + { + udf_warning(sb, "udf_file_write", "mode = %07o", inode->i_mode); + return -EINVAL; + } + remove_suid(inode); + + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else + { + pos = *ppos; + if (pos != *ppos) + return -EINVAL; + } + +#if BITS_PER_LONG < 64 + /* If the fd's pos is already greater than or equal to the file + * descriptor's offset maximum, then we need to return EFBIG for + * any non-zero count (and we already tested for zero above). */ + if (((off_t) pos) >= 0x7FFFFFFFUL) + return -EFBIG; + + /* If we are about to overflow the maximum file size, we also + * need to return the error, but only if no bytes can be written + * successfully. */ + if (((off_t) pos + count) > 0x7FFFFFFFUL) + { + count = 0x7FFFFFFFL - pos; + if (((ssize_t) count) < 0) + return -EFBIG; + } +#endif + + limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) + { + if (((off_t) pos + count) >= limit) + { + count = limit - pos; + if (((ssize_t) count) <= 0) + { + send_sig(SIGXFSZ, current, 0); + return -EFBIG; + } + } + } + + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + 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) == ICB_FLAG_AD_IN_ICB) + { + udf_debug("udf_expand_file_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; + pos += udf_file_entry_alloc_offset(inode); + } + } + + if (filp->f_flags & O_SYNC) + ; /* Do something */ + block = pos >> sb->s_blocksize_bits; + offset = pos & (sb->s_blocksize - 1); + c = sb->s_blocksize - offset; + written = 0; + do + { + bh = udf_getblk(inode, block, 1, &err); + if (!bh) + { + if (!written) + written = err; + break; + } + if (c > count) + c = count; + + new_buffer = (!buffer_uptodate(bh) && !buffer_locked(bh) && + c == sb->s_blocksize); + + if (new_buffer) + { + + set_bit(BH_Lock, &bh->b_state); + c -= copy_from_user(bh->b_data + offset, buf, c); + if (c != sb->s_blocksize) + { + c = 0; + unlock_buffer(bh); + brelse(bh); + if (!written) + written = -EFAULT; + break; + } + mark_buffer_uptodate(bh, 1); + unlock_buffer(bh); + } + else + { + if (!buffer_uptodate(bh)) + { + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) + { + brelse(bh); + if (!written) + written = -EIO; + break; + } + } + c -= copy_from_user(bh->b_data + offset, buf, c); + } + if (!c) + { + brelse(bh); + if (!written) + written = -EFAULT; + break; + } + mark_buffer_dirty(bh, 0); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + update_vm_cache(inode, pos, bh->b_data + offset, c); +#else + update_vm_cache_conditional(inode, pos, bh->b_data + offset, c, + (unsigned long) buf); +#endif + pos += c; + written += c; + buf += c; + count -= c; + + if (filp->f_flags & O_SYNC) + bufferlist[buffercount++] = bh; + else + brelse(bh); + if (buffercount == NBUF) + { + ll_rw_block(WRITE, buffercount, bufferlist); + for (i=0; is_blocksize; + } while (count); + + if (buffercount) + { + ll_rw_block(WRITE, buffercount, bufferlist); + for (i=0; i inode->i_size) + inode->i_size = pos; + if (filp->f_flags & O_SYNC) + ; /* Do something */ + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME; + *ppos = pos; + mark_inode_dirty(inode); + return written; +} + +/* + * 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_bmap()], 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 + * 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 = -1; + struct buffer_head *bh = NULL; + Uint16 ident; + long_ad eaicb; + Uint8 *ea = NULL; + + if ( permission(inode, MAY_READ) != 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; + } + + /* first, do ioctls that don't need to udf_read */ + switch (cmd) + { + case UDF_GETVOLIDENT: + if ( (result == verify_area(VERIFY_WRITE, (char *)arg, 32)) == 0) + result = copy_to_user((char *)arg, UDF_SB_VOLIDENT(inode->i_sb), 32); + return result; + + } + + /* ok, we need to read the inode */ + bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident); + + if (!bh || (ident != TID_FILE_ENTRY && ident != TID_EXTENDED_FILE_ENTRY)) + { + udf_debug("bread failed (ino=%ld) or ident (%d) != TID_(EXTENDED_)FILE_ENTRY", + inode->i_ino, ident); + return -EFAULT; + } + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + struct FileEntry *fe; + + fe = (struct FileEntry *)bh->b_data; + eaicb = lela_to_cpu(fe->extendedAttrICB); + if (UDF_I_LENEATTR(inode)) + ea = fe->extendedAttr; + } + else + { + struct ExtendedFileEntry *efe; + + efe = (struct ExtendedFileEntry *)bh->b_data; + eaicb = lela_to_cpu(efe->extendedAttrICB); + if (UDF_I_LENEATTR(inode)) + ea = efe->extendedAttr; + } + + switch (cmd) + { + case UDF_GETEASIZE: + if ( (result = verify_area(VERIFY_WRITE, (char *)arg, 4)) == 0) + result = put_user(UDF_I_LENEATTR(inode), (int *)arg); + break; + + case UDF_GETEABLOCK: + if ( (result = verify_area(VERIFY_WRITE, (char *)arg, UDF_I_LENEATTR(inode))) == 0) + result = copy_to_user((char *)arg, ea, UDF_I_LENEATTR(inode)); + break; + + default: + udf_debug("ino=%ld, cmd=%d\n", inode->i_ino, cmd); + break; + } + + udf_release_data(bh); + 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) + udf_discard_prealloc(inode); + return 0; +} + +#if BITS_PER_LONG < 64 +/* + * udf_open_file + * + * PURPOSE + * Called when an inode is about to be open. + * + * DESCRIPTION + * Use this to disallow opening RW large files on 32 bit systems. + * + * HISTORY + * + */ +static int udf_open_file(struct inode * inode, struct file * filp) +{ + if (inode->i_size == (Uint32)-1 && (filp->f_mode & FMODE_WRITE)) + return -EFBIG; + return 0; +} +#endif + +static struct file_operations udf_file_operations = { + udf_file_llseek, /* llseek */ + generic_file_read, /* read */ + udf_file_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + udf_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ +#if BITS_PER_LONG == 64 + NULL, /* open */ +#else + udf_open_file, /* open */ +#endif + NULL, /* flush */ + udf_release_file, /* release */ + udf_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations udf_file_inode_operations = { + &udf_file_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + generic_readpage, /* readpage */ + NULL, /* writepage */ + udf_bmap, /* bmap */ + udf_truncate, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; + +struct inode_operations udf_file_inode_operations_adinicb = { + &udf_file_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + udf_readpage_adinicb, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + udf_truncate_adinicb, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; diff -Nru linux/fs/udf/fsync.c linux.new/fs/udf/fsync.c --- linux/fs/udf/fsync.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/fsync.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,115 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc + * + * HISTORY + * + * 05/22/99 blf Created. + */ + +#include "udfdecl.h" + +#include +#include +#include "udf_i.h" + +static int sync_block (struct inode * inode, Uint32 * block, int wait) +{ + struct buffer_head * bh; + + if (!*block) + return 0; + bh = get_hash_table (inode->i_dev, *block, inode->i_sb->s_blocksize); + if (!bh) + return 0; + if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { + brelse (bh); + return -1; + } + if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { + brelse (bh); + return 0; + } + ll_rw_block (WRITE, 1, &bh); + bh->b_count--; + return 0; +} + +static int sync_extent (struct inode * inode, lb_addr *loc, Uint32 *len, int wait) +{ + Uint32 i, block; + int rc, err = 0; + + for (i = 0; i < *len; i++) + { + block = udf_get_lb_pblock(inode->i_sb, *loc, i); + rc = sync_block (inode, &block, wait); + if (rc) + err = rc; + } + return err; +} + +static int sync_all_extents(struct inode * inode, int wait) +{ + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + int err = 0, etype; + struct buffer_head *bh = NULL; + + if ((etype = inode_bmap(inode, 0, &bloc, &extoffset, &eloc, &elen, &offset, &bh)) != -1) + { + err |= sync_extent(inode, &eloc, &elen, wait); + + while ((etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) + { + if (etype == EXTENT_RECORDED_ALLOCATED) + err |= sync_extent(inode, &eloc, &elen, wait); + } + } + udf_release_data(bh); + return err; +} + +/* + * File may be NULL when we are called. Perhaps we shouldn't + * even pass file to fsync ? + */ + +int udf_sync_file(struct file * file, struct dentry *dentry) +{ + int wait, err = 0; + struct inode *inode = dentry->d_inode; + + if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) + { + /* + * Don't sync fast links! or ICB_FLAG_AD_IN_ICB + */ + goto skip; + } + + for (wait=0; wait<=1; wait++) + { + err |= sync_all_extents (inode, wait); + } +skip: + err |= udf_sync_inode (inode); + return err ? -EIO : 0; +} diff -Nru linux/fs/udf/ialloc.c linux.new/fs/udf/ialloc.c --- linux/fs/udf/ialloc.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/ialloc.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,195 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * + * HISTORY + * + * 02/24/99 blf Created. + * + */ + +#include "udfdecl.h" +#include +#include +#include +#include +#include + +#include "udf_i.h" +#include "udf_sb.h" + +void udf_free_inode(struct inode * inode) +{ + struct super_block * sb = inode->i_sb; + int is_directory; + unsigned long ino; + + if (!inode->i_dev) + { + udf_debug("inode has no device\n"); + return; + } + if (inode->i_count > 1) + { + udf_debug("inode has count=%d\n", inode->i_count); + return; + } + if (inode->i_nlink) + { + udf_debug("inode has nlink=%d\n", inode->i_nlink); + return; + } + if (!sb) + { + udf_debug("inode on nonexistent device\n"); + return; + } + + ino = inode->i_ino; + + /* + * 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(sb, inode); + DQUOT_DROP(inode); + + lock_super(sb); + + is_directory = S_ISDIR(inode->i_mode); + + clear_inode(inode); + + if (UDF_SB_LVIDBH(sb)) + { + if (is_directory) + 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(UDF_SB_LVIDBH(sb), 1); + } + + unlock_super(sb); + + udf_free_blocks(inode, UDF_I_LOCATION(inode), 0, 1); +} + +struct inode * udf_new_inode (const struct inode *dir, int mode, int * err) +{ + struct super_block *sb; + struct inode * inode; + int block; + Uint32 start = UDF_I_LOCATION(dir).logicalBlockNum; + + inode = get_empty_inode(); + if (!inode) + { + *err = -ENOMEM; + return NULL; + } + sb = dir->i_sb; + inode->i_sb = sb; + inode->i_flags = 0; + *err = -ENOSPC; + + block = udf_new_block(dir, UDF_I_LOCATION(dir).partitionReferenceNum, + start, err); + if (*err) + { + iput(inode); + return NULL; + } + lock_super(sb); + + if (UDF_SB_LVIDBH(sb)) + { + struct LogicalVolHeaderDesc *lvhd; + Uint64 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), 1); + } + inode->i_mode = mode; + inode->i_sb = sb; + inode->i_nlink = 1; + inode->i_dev = sb->s_dev; + inode->i_uid = current->fsuid; + if (test_opt (sb, GRPID)) + inode->i_gid = dir->i_gid; + else 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; + inode->i_size = 0; + UDF_I_LENEATTR(inode) = 0; + UDF_I_LENALLOC(inode) = 0; + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) + { + UDF_I_EXTENDED_FE(inode) = 1; + UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE); + } + else + UDF_I_EXTENDED_FE(inode) = 0; + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_IN_ICB; + else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_SHORT; + else + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + UDF_I_UMTIME(inode) = UDF_I_UATIME(inode) = UDF_I_UCTIME(inode) = CURRENT_UTIME; + UDF_I_NEW_INODE(inode) = 1; + inode->i_op = NULL; + insert_inode_hash(inode); + mark_inode_dirty(inode); + + unlock_super(sb); + if (DQUOT_ALLOC_INODE(sb, inode)) + { + sb->dq_op->drop(inode); + inode->i_nlink = 0; + iput(inode); + *err = -EDQUOT; + return NULL; + } + + *err = 0; + return inode; +} diff -Nru linux/fs/udf/inode.c linux.new/fs/udf/inode.c --- linux/fs/udf/inode.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/inode.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,2022 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc + * + * HISTORY + * + * 10/04/98 dgb Added rudimentary directory functions + * 10/07/98 Fully working udf_bmap! It works! + * 11/25/98 bmap altered to better support extents + * 12/06/98 blf partition support in udf_iget, udf_bmap and udf_read_inode + * 12/12/98 rewrote udf_bmap 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_bmap (again) + * New funcs, inode_bmap, udf_next_aext + * 04/19/99 Support for writing device EA's for major/minor # + */ + +#include "udfdecl.h" +#include +#include + +#include "udf_i.h" +#include "udf_sb.h" + +#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, int *); +static void udf_split_extents(struct inode *, int *, int, int, + long_ad [EXTENT_MERGE_SIZE], int *); +static void udf_prealloc_extents(struct inode *, int, int, + long_ad [EXTENT_MERGE_SIZE], int *); +static void udf_merge_extents(struct inode *, + long_ad [EXTENT_MERGE_SIZE], int *); +static void udf_update_extents(struct inode *, + long_ad [EXTENT_MERGE_SIZE], int, int, + lb_addr, Uint32, struct buffer_head **); + +/* + * udf_put_inode + * + * PURPOSE + * + * DESCRIPTION + * This routine is called whenever the kernel no longer needs the inode. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * Called at each iput() + */ +void udf_put_inode(struct inode * inode) +{ + if (!(inode->i_sb->s_flags & MS_RDONLY)) + { + udf_discard_prealloc(inode); + /* write the root inode on put, if dirty */ + if (!inode->i_sb->s_root && inode->i_state & I_DIRTY) + udf_update_inode(inode, IS_SYNC(inode)); + } +} + +/* + * 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)) + return; + inode->i_size = 0; + if (inode->i_blocks) + { + if (inode->i_op && inode->i_op->truncate) + inode->i_op->truncate(inode); + else + udf_truncate(inode); + } + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + udf_truncate_adinicb(inode); + udf_update_inode(inode, IS_SYNC(inode)); + udf_free_inode(inode); +} + +void udf_discard_prealloc(struct inode * inode) +{ + if (inode->i_size && UDF_I_ALLOCTYPE(inode) != ICB_FLAG_AD_IN_ICB) + udf_trunc(inode); +} + +void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) +{ + int block, newblock; + struct buffer_head *sbh = NULL, *dbh = NULL; + lb_addr bloc, eloc; + Uint32 elen, extoffset; + + if (!UDF_I_LENALLOC(inode)) + { + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_SHORT; + else + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + mark_inode_dirty(inode); + inode->i_op = &udf_file_inode_operations; + return; + } + + /* alloc block, and copy data to it */ + block = udf_new_block(inode, + UDF_I_LOCATION(inode).partitionReferenceNum, + UDF_I_LOCATION(inode).logicalBlockNum, err); + + if (!(block)) + return; + newblock = udf_get_pblock(inode->i_sb, block, + UDF_I_LOCATION(inode).partitionReferenceNum, 0); + if (!newblock) + return; + sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + if (!sbh) + return; + dbh = udf_tread(inode->i_sb, newblock, inode->i_sb->s_blocksize); + if (!dbh) + return; + + memcpy(dbh->b_data, sbh->b_data + udf_file_entry_alloc_offset(inode), + UDF_I_LENALLOC(inode)); + + mark_buffer_dirty(dbh, 1); + udf_release_data(dbh); + + memset(sbh->b_data + udf_file_entry_alloc_offset(inode), + 0, 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) = ICB_FLAG_AD_SHORT; + else + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + bloc = UDF_I_LOCATION(inode); + eloc.logicalBlockNum = block; + eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + elen = newsize > inode->i_sb->s_blocksize ? + inode->i_sb->s_blocksize : newsize; + extoffset = udf_file_entry_alloc_offset(inode); + udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0); + /* UniqueID stuff */ + + inode->i_blocks = inode->i_sb->s_blocksize / 512; + mark_buffer_dirty(sbh, 1); + udf_release_data(sbh); + mark_inode_dirty(inode); + inode->i_version ++; + inode->i_op = &udf_file_inode_operations; +} + +struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) +{ + int newblock; + struct buffer_head *sbh = NULL, *dbh = NULL; + lb_addr bloc, eloc; + Uint32 elen, extoffset; + + struct udf_fileident_bh sfibh, dfibh; + int f_pos = udf_ext0_offset(inode) >> 2; + int size = (udf_ext0_offset(inode) + inode->i_size) >> 2; + struct FileIdentDesc cfi, *sfi, *dfi; + + if (!inode->i_size) + { + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_SHORT; + else + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + mark_inode_dirty(inode); + return NULL; + } + + /* alloc block, and copy data to it */ + *block = udf_new_block(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; + sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + if (!sbh) + return NULL; + dbh = udf_tread(inode->i_sb, newblock, inode->i_sb->s_blocksize); + if (!dbh) + return NULL; + + sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; + sfibh.sbh = sfibh.ebh = sbh; + dfibh.soffset = dfibh.eoffset = 0; + dfibh.sbh = dfibh.ebh = dbh; + while ( (f_pos < size) ) + { + sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL, NULL, NULL); + if (!sfi) + { + udf_release_data(sbh); + udf_release_data(dbh); + return NULL; + } + sfi->descTag.tagLocation = *block; + dfibh.soffset = dfibh.eoffset; + dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); + dfi = (struct FileIdentDesc *)(dbh->b_data + dfibh.soffset); + if (udf_write_fi(sfi, dfi, &dfibh, sfi->impUse, + sfi->fileIdent + sfi->lengthOfImpUse)) + { + udf_release_data(sbh); + udf_release_data(dbh); + return NULL; + } + } + mark_buffer_dirty(dbh, 1); + + memset(sbh->b_data + udf_file_entry_alloc_offset(inode), + 0, 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) = ICB_FLAG_AD_SHORT; + else + UDF_I_ALLOCTYPE(inode) = ICB_FLAG_AD_LONG; + bloc = UDF_I_LOCATION(inode); + eloc.logicalBlockNum = *block; + eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; + elen = inode->i_size; + extoffset = udf_file_entry_alloc_offset(inode); + udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0); + /* UniqueID stuff */ + + inode->i_blocks = inode->i_sb->s_blocksize / 512; + mark_buffer_dirty(sbh, 1); + udf_release_data(sbh); + mark_inode_dirty(inode); + inode->i_version ++; + return dbh; +} + +struct buffer_head * udf_getblk(struct inode * inode, long block, + int create, int * err) +{ + *err = -EIO; + if (block < 0) + { + udf_warning(inode->i_sb, "udf_getblk", "block < 0"); + return NULL; + } + + if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) + { + UDF_I_NEXT_ALLOC_BLOCK(inode) ++; + UDF_I_NEXT_ALLOC_GOAL(inode) ++; + } + + *err = -ENOSPC; + return inode_getblk(inode, block, create, err); +} + +static struct buffer_head * inode_getblk(struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head *pbh = NULL, *cbh = NULL, *result = NULL; + long_ad laarr[EXTENT_MERGE_SIZE]; + Uint32 pextoffset = 0, cextoffset = 0, nextoffset = 0; + int count = 0, startnum = 0, endnum = 0; + Uint32 elen = 0; + lb_addr eloc, pbloc = UDF_I_LOCATION(inode), cbloc = UDF_I_LOCATION(inode); + int c = 1; + int lbcount = 0, b_off = 0, offset = 0; + Uint32 newblocknum, newblock; + char etype; + int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum; + char lastblock = 0; + + pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode); + b_off = block << inode->i_sb->s_blocksize_bits; + pbloc = cbloc = 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); + pbh = cbh; + cbh->b_count ++; + pbloc = cbloc; + } + + lbcount += elen; + + pextoffset = cextoffset; + cextoffset = nextoffset; + + if ((etype = udf_next_aext(inode, &cbloc, &nextoffset, &eloc, &elen, &cbh, 1)) == -1) + break; + + c = !c; + + laarr[c].extLength = (etype << 30) | elen; + laarr[c].extLocation = eloc; + + if (etype != EXTENT_NOT_RECORDED_NOT_ALLOCATED) + pgoal = eloc.logicalBlockNum + + ((elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize); + + 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 == EXTENT_RECORDED_ALLOCATED) + { + if (elen & (inode->i_sb->s_blocksize - 1)) + { + elen = (EXTENT_RECORDED_ALLOCATED << 30) | + ((elen + inode->i_sb->s_blocksize - 1) & + ~(inode->i_sb->s_blocksize - 1)); + etype = udf_write_aext(inode, cbloc, &cextoffset, eloc, elen, cbh, 1); + } + udf_release_data(pbh); + udf_release_data(cbh); + newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset); + return getblk(inode->i_dev, newblock, inode->i_sb->s_blocksize); + } + + 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)); + } + c = !c; + laarr[c].extLength = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | + ((offset + 1) << inode->i_sb->s_blocksize_bits); + memset(&laarr[c].extLocation, 0x00, sizeof(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, &cbloc, &nextoffset, &eloc, &elen, &cbh, 0)) != -1) + { + laarr[c+1].extLength = (etype << 30) | elen; + laarr[c+1].extLocation = eloc; + count ++; + startnum ++; + endnum ++; + } + else + lastblock = 1; + } + udf_release_data(cbh); + + *err = -EFBIG; + if (!create && etype != EXTENT_NOT_RECORDED_ALLOCATED) + goto dont_create; + + { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) + { + limit >>= inode->i_sb->s_blocksize_bits; + if (block >= limit) + { + send_sig(SIGXFSZ, current, 0); +dont_create: + *err = -EFBIG; + return NULL; + } + } + } + + /* 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) == EXTENT_NOT_RECORDED_ALLOCATED) + 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, + UDF_I_LOCATION(inode).partitionReferenceNum, goal, err))) + { + udf_release_data(pbh); + *err = -ENOSPC; + return NULL; + } + } + + /* 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; + } + if ((result = getblk(inode->i_dev, newblock, inode->i_sb->s_blocksize))) + { + memset(result->b_data, 0x00, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } + UDF_I_NEXT_ALLOC_BLOCK(inode) = block; + UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum; + inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(inode) = CURRENT_UTIME; + inode->i_blocks += inode->i_sb->s_blocksize / 512; + 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, + long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +{ + if ((laarr[*c].extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED || + (laarr[*c].extLength >> 30) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + { + int curr = *c; + int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + int type = laarr[curr].extLength & ~UDF_EXTENT_LENGTH_MASK; + + 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) + { + laarr[curr].extLength = type | + (offset << inode->i_sb->s_blocksize_bits); + curr ++; + (*c) ++; + (*endnum) ++; + } + + laarr[curr].extLocation.logicalBlockNum = newblocknum; + if ((type >> 30) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + laarr[curr].extLocation.partitionReferenceNum = + UDF_I_LOCATION(inode).partitionReferenceNum; + laarr[curr].extLength = (EXTENT_RECORDED_ALLOCATED << 30) | + inode->i_sb->s_blocksize; + curr ++; + + if (blen != offset + 1) + { + if ((type >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) + laarr[curr].extLocation.logicalBlockNum += (offset + 1); + laarr[curr].extLength = type | + ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); + curr ++; + (*endnum) ++; + } + } +} + +static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, + long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +{ + int start, length = 0, currlength = 0, i; + + if (*endnum >= (c+1) && !lastblock) + return; + + if ((laarr[c+1].extLength >> 30) == EXTENT_NOT_RECORDED_ALLOCATED) + { + 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) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + 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, + 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 = (EXTENT_NOT_RECORDED_ALLOCATED << 30) | + (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[c+1].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) --; + } + } + } + } +} + +static void udf_merge_extents(struct inode *inode, + 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) == EXTENT_NOT_RECORDED_NOT_ALLOCATED) || + ((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 = (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) --; + } + } + } + } +} + +static void udf_update_extents(struct inode *inode, + long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum, + lb_addr pbloc, Uint32 pextoffset, struct buffer_head **pbh) +{ + int start = 0, i; + lb_addr tmploc; + Uint32 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; ii_blocks; + + bh = udf_getblk(inode, block, create, err); + if (!bh) + return NULL; + +#if 0 + if (create && + S_ISDIR(inode->i_mode) && + inode->i_blocks > prev_blocks) + { + int i; + struct buffer_head *tmp_bh = NULL; + + for (i=1; + i < UDF_DEFAULT_PREALLOC_DIR_BLOCKS; + i++) + { + tmp_bh = udf_getblk(inode, block+i, create, err); + if (!tmp_bh) + { + udf_release_data(bh); + return 0; + } + udf_release_data(tmp_bh); + } + } +#endif + + 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; +} + +/* + * udf_read_inode + * + * PURPOSE + * Read an inode. + * + * DESCRIPTION + * This routine is called by iget() [which is called by udf_iget()] + * (clean_inode() will have been called first) + * when an inode is first read into memory. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * 12/19/98 dgb Updated to fix size problems. + */ +void +udf_read_inode(struct inode *inode) +{ + memset(&UDF_I_LOCATION(inode), 0xFF, sizeof(lb_addr)); +} + +void +__udf_read_inode(struct inode *inode) +{ + struct buffer_head *bh = NULL; + struct FileEntry *fe; + Uint16 ident; + + /* + * Set defaults, but the inode is still incomplete! + * Note: get_new_inode() sets the following on a new inode: + * i_sb = sb + * i_dev = sb->s_dev; + * 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 != TID_FILE_ENTRY && ident != TID_EXTENDED_FILE_ENTRY && + ident != TID_UNALLOCATED_SPACE_ENTRY) + { + 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 == TID_INDIRECT_ENTRY) + { + if (ibh) + { + 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 == TID_FILE_ENTRY || + ident == TID_EXTENDED_FILE_ENTRY) + { + memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(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, alen; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + inode->i_version = ++event; +#else + inode->i_version = ++global_event; +#endif + UDF_I_NEW_INODE(inode) = 0; + + 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) & ICB_FLAG_ALLOC_MASK; + + if (fe->descTag.tagIdent == TID_EXTENDED_FILE_ENTRY) + UDF_I_EXTENDED_FE(inode) = 1; + else if (fe->descTag.tagIdent == TID_FILE_ENTRY) + UDF_I_EXTENDED_FE(inode) = 0; + else if (fe->descTag.tagIdent == TID_UNALLOCATED_SPACE_ENTRY) + { + UDF_I_LENALLOC(inode) = + le32_to_cpu( + ((struct UnallocatedSpaceEntry *)bh->b_data)->lengthAllocDescs); + return; + } + + inode->i_uid = udf_convert_uid(le32_to_cpu(fe->uid)); + if ( !inode->i_uid ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; + + inode->i_gid = udf_convert_gid(le32_to_cpu(fe->gid)); + if ( !inode->i_gid ) 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); +#if BITS_PER_LONG < 64 + if (le64_to_cpu(fe->informationLength) & 0xFFFFFFFF00000000ULL) + inode->i_size = (Uint32)-1; +#endif + + inode->i_mode = udf_convert_permissions(fe); + inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; + + UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; + UDF_I_NEXT_ALLOC_GOAL(inode) = 0; + + if (UDF_I_EXTENDED_FE(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->modificationTime)) ) + { + inode->i_mtime = convtime; + UDF_I_UMTIME(inode) = convtime_usec; + inode->i_ctime = convtime; + UDF_I_UCTIME(inode) = convtime_usec; + } + else + { + inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); + UDF_I_UMTIME(inode) = 0; + inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); + UDF_I_UCTIME(inode) = 0; + } + + if ( udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(fe->accessTime)) ) + { + inode->i_atime = convtime; + UDF_I_UATIME(inode) = convtime_usec; + } + else + { + inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); + UDF_I_UATIME(inode) = convtime_usec; + } + + 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); + alen = offset + UDF_I_LENALLOC(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->modificationTime)) ) + { + inode->i_mtime = convtime; + UDF_I_UMTIME(inode) = convtime_usec; + } + else + { + inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); + UDF_I_UMTIME(inode) = 0; + } + + if ( udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(efe->accessTime)) ) + { + inode->i_atime = convtime; + UDF_I_UATIME(inode) = convtime_usec; + } + else + { + inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); + UDF_I_UATIME(inode) = 0; + } + + if ( udf_stamp_to_time(&convtime, &convtime_usec, + lets_to_cpu(efe->createTime)) ) + { + inode->i_ctime = convtime; + UDF_I_UCTIME(inode) = convtime_usec; + } + else + { + inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); + UDF_I_UCTIME(inode) = 0; + } + + 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); + alen = offset + UDF_I_LENALLOC(inode); + } + + switch (fe->icbTag.fileType) + { + case FILE_TYPE_DIRECTORY: + { + inode->i_op = &udf_dir_inode_operations; + inode->i_mode |= S_IFDIR; + inode->i_nlink ++; + break; + } + case FILE_TYPE_REGULAR: + case FILE_TYPE_NONE: + { + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + inode->i_op = &udf_file_inode_operations_adinicb; + else + inode->i_op = &udf_file_inode_operations; + inode->i_mode |= S_IFREG; + break; + } + case FILE_TYPE_BLOCK: + { + inode->i_op = &blkdev_inode_operations; + inode->i_mode |= S_IFBLK; + break; + } + case FILE_TYPE_CHAR: + { + inode->i_op = &chrdev_inode_operations; + inode->i_mode |= S_IFCHR; + break; + } + case FILE_TYPE_FIFO: + { + init_fifo(inode); + break; + } + case FILE_TYPE_SYMLINK: + { + /* untested! */ + inode->i_op = &udf_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 buffer_head *tbh = NULL; + struct DeviceSpecificationExtendedAttr *dsea = + (struct DeviceSpecificationExtendedAttr *) + udf_get_extendedattr(inode, 12, 1, &tbh); + + if (dsea) + { + inode->i_rdev = to_kdev_t( + (le32_to_cpu(dsea->majorDeviceIdent)) << 8) | + (le32_to_cpu(dsea->minorDeviceIdent) & 0xFF); + /* Developer ID ??? */ + udf_release_data(tbh); + } + else + { + make_bad_inode(inode); + } + } +} + +static mode_t +udf_convert_permissions(struct FileEntry *fe) +{ + mode_t mode; + Uint32 permissions; + Uint32 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 & ICB_FLAG_SETUID) ? S_ISUID : 0) | + (( flags & ICB_FLAG_SETGID) ? S_ISGID : 0) | + (( flags & ICB_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. + */ + +void udf_write_inode(struct inode * inode) +{ + udf_update_inode(inode, 0); +} + +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 udfperms; + Uint16 icbflags; + Uint16 crclen; + int i; + timestamp cpu_time; + int err = 0; + + bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0), + inode->i_sb->s_blocksize); + if (!bh) + { + udf_debug("bread failure\n"); + return -EIO; + } + fe = (struct FileEntry *)bh->b_data; + efe = (struct ExtendedFileEntry *)bh->b_data; + if (UDF_I_NEW_INODE(inode) == 1) + { + if (UDF_I_EXTENDED_FE(inode) == 0) + memset(bh->b_data, 0x0, sizeof(struct FileEntry)); + else + memset(bh->b_data, 0x00, sizeof(struct ExtendedFileEntry)); + memset(bh->b_data + udf_file_entry_alloc_offset(inode) + + UDF_I_LENALLOC(inode), 0x0, inode->i_sb->s_blocksize - + udf_file_entry_alloc_offset(inode) - UDF_I_LENALLOC(inode)); + UDF_I_NEW_INODE(inode) = 0; + } + + if (fe->descTag.tagIdent == TID_UNALLOCATED_SPACE_ENTRY) + { + struct UnallocatedSpaceEntry *use = + (struct UnallocatedSpaceEntry *)bh->b_data; + + use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); + crclen = sizeof(struct UnallocatedSpaceEntry) + UDF_I_LENALLOC(inode) - + sizeof(tag); + 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 *)&(use->descTag))[i]; + + mark_buffer_dirty(bh, 1); + 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) & + (PERM_O_DELETE | PERM_O_CHATTR | + PERM_G_DELETE | PERM_G_CHATTR | + PERM_U_DELETE | 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)) + { + EntityID *eid; + struct buffer_head *tbh = NULL; + struct DeviceSpecificationExtendedAttr *dsea = + (struct DeviceSpecificationExtendedAttr *) + udf_get_extendedattr(inode, 12, 1, &tbh); + + if (!dsea) + { + dsea = (struct DeviceSpecificationExtendedAttr *) + udf_add_extendedattr(inode, + sizeof(struct DeviceSpecificationExtendedAttr) + + sizeof(EntityID), 12, 0x3, &tbh); + dsea->attrType = 12; + dsea->attrSubtype = 1; + dsea->attrLength = sizeof(struct DeviceSpecificationExtendedAttr) + + sizeof(EntityID); + dsea->impUseLength = sizeof(EntityID); + } + eid = (EntityID *)dsea->impUse; + memset(eid, 0, sizeof(EntityID)); + strcpy(eid->ident, UDF_ID_DEVELOPER); + eid->identSuffix[0] = UDF_OS_CLASS_UNIX; + eid->identSuffix[1] = UDF_OS_ID_LINUX; + dsea->majorDeviceIdent = kdev_t_to_nr(inode->i_rdev) >> 8; + dsea->minorDeviceIdent = kdev_t_to_nr(inode->i_rdev) & 0xFF; + mark_buffer_dirty(tbh, 1); + udf_release_data(tbh); + } + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + 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, UDF_I_UATIME(inode))) + fe->accessTime = cpu_to_lets(cpu_time); + if (udf_time_to_stamp(&cpu_time, inode->i_mtime, UDF_I_UMTIME(inode))) + fe->modificationTime = cpu_to_lets(cpu_time); + memset(&(fe->impIdent), 0, sizeof(EntityID)); + 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 = le16_to_cpu(TID_FILE_ENTRY); + crclen = sizeof(struct FileEntry); + } + else + { + efe->logicalBlocksRecorded = cpu_to_le64( + (inode->i_blocks + (2 << (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, UDF_I_UATIME(inode))) + efe->accessTime = cpu_to_lets(cpu_time); + if (udf_time_to_stamp(&cpu_time, inode->i_mtime, UDF_I_UMTIME(inode))) + efe->modificationTime = cpu_to_lets(cpu_time); + if (udf_time_to_stamp(&cpu_time, inode->i_ctime, UDF_I_UCTIME(inode))) + efe->createTime = cpu_to_lets(cpu_time); + memset(&(efe->impIdent), 0, sizeof(EntityID)); + 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 = le16_to_cpu(TID_EXTENDED_FILE_ENTRY); + 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 = FILE_TYPE_DIRECTORY; + else if (S_ISREG(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_REGULAR; + else if (S_ISLNK(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_SYMLINK; + else if (S_ISBLK(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_BLOCK; + else if (S_ISCHR(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_CHAR; + else if (S_ISFIFO(inode->i_mode)) + fe->icbTag.fileType = FILE_TYPE_FIFO; + + icbflags = UDF_I_ALLOCTYPE(inode) | + ((inode->i_mode & S_ISUID) ? ICB_FLAG_SETUID : 0) | + ((inode->i_mode & S_ISGID) ? ICB_FLAG_SETGID : 0) | + ((inode->i_mode & S_ISVTX) ? ICB_FLAG_STICKY : 0) | + (le16_to_cpu(fe->icbTag.flags) & + ~(ICB_FLAG_ALLOC_MASK | ICB_FLAG_SETUID | + ICB_FLAG_SETGID | ICB_FLAG_STICKY)); + + fe->icbTag.flags = cpu_to_le16(icbflags); + 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 *)&(fe->descTag))[i]; + + /* write the data blocks */ + mark_buffer_dirty(bh, 1); + if (do_sync) + { + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + { + printk("IO error syncing udf inode [%s:%08lx]\n", + bdevname(inode->i_dev), inode->i_ino); + err = -EIO; + } + } + udf_release_data(bh); + return err; +} + +/* + * udf_iget + * + * PURPOSE + * Get an inode. + * + * DESCRIPTION + * This routine replaces iget() and read_inode(). + * + * HISTORY + * October 3, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * 12/19/98 dgb Added semaphore and changed to be a wrapper of iget + */ +struct inode * +udf_iget(struct super_block *sb, lb_addr ino) +{ + struct inode *inode; + unsigned long block; + + block = udf_get_lb_pblock(sb, ino, 0); + + /* Get the inode */ + + inode = iget(sb, block); + /* calls udf_read_inode() ! */ + + if (!inode) + { + printk(KERN_ERR "udf: iget() failed\n"); + return NULL; + } + else if (is_bad_inode(inode)) + { + iput(inode); + return NULL; + } + else if (UDF_I_LOCATION(inode).logicalBlockNum == 0xFFFFFFFF && + UDF_I_LOCATION(inode).partitionReferenceNum == 0xFFFF) + { + memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(lb_addr)); + __udf_read_inode(inode); + } + + if ( ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum) ) + { + udf_debug("block=%d, partition=%d out of range\n", + ino.logicalBlockNum, ino.partitionReferenceNum); + return NULL; + } + + return inode; +} + +int udf_add_aext(struct inode *inode, lb_addr *bloc, int *extoffset, + lb_addr eloc, Uint32 elen, struct buffer_head **bh, int inc) +{ + int adsize; + short_ad *sad = NULL; + long_ad *lad = NULL; + struct AllocExtDesc *aed; + int ret; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, *bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + return -1; + } + } + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_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; + lb_addr obloc = *bloc; + + if (!(bloc->logicalBlockNum = udf_new_block(inode, + obloc.partitionReferenceNum, obloc.logicalBlockNum, &err))) + { + return -1; + } + if (!(nbh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, + *bloc, 0), inode->i_sb->s_blocksize))) + { + return -1; + } + aed = (struct AllocExtDesc *)(nbh->b_data); + aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); + if (*extoffset + adsize > inode->i_sb->s_blocksize) + { + loffset = *extoffset; + aed->lengthAllocDescs = cpu_to_le32(adsize); + sptr = (*bh)->b_data + *extoffset - 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 = (*bh)->b_data + *extoffset; + *extoffset = sizeof(struct AllocExtDesc); + + if (memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr))) + { + 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); + } + } + udf_new_tag(nbh->b_data, TID_ALLOC_EXTENT_DESC, 2, 1, + bloc->logicalBlockNum, sizeof(tag)); + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + sad = (short_ad *)sptr; + sad->extLength = cpu_to_le32( + EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + inode->i_sb->s_blocksize); + sad->extPosition = cpu_to_le32(bloc->logicalBlockNum); + break; + } + case ICB_FLAG_AD_LONG: + { + lad = (long_ad *)sptr; + lad->extLength = cpu_to_le32( + EXTENT_NEXT_EXTENT_ALLOCDECS << 30 | + inode->i_sb->s_blocksize); + lad->extLocation = cpu_to_lelb(*bloc); + break; + } + } + udf_update_tag((*bh)->b_data, loffset); + mark_buffer_dirty(*bh, 1); + udf_release_data(*bh); + *bh = nbh; + } + + ret = udf_write_aext(inode, *bloc, extoffset, eloc, elen, *bh, inc); + + if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr))) + { + 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); + udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize)); + mark_buffer_dirty(*bh, 1); + } + + return ret; +} + +int udf_write_aext(struct inode *inode, lb_addr bloc, int *extoffset, + lb_addr eloc, Uint32 elen, struct buffer_head *bh, int inc) +{ + int adsize; + short_ad *sad = NULL; + long_ad *lad = NULL; + + if (!(bh)) + { + if (!(bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, bloc, 0)); + return -1; + } + } + else + bh->b_count ++; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + return -1; + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + sad = (short_ad *)((bh)->b_data + *extoffset); + sad->extLength = cpu_to_le32(elen); + sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); + break; + } + case ICB_FLAG_AD_LONG: + { + lad = (long_ad *)((bh)->b_data + *extoffset); + lad->extLength = cpu_to_le32(elen); + lad->extLocation = cpu_to_lelb(eloc); + break; + } + } + + if (memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(bh)->b_data; + udf_update_tag((bh)->b_data, + le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct AllocExtDesc)); + } + else + mark_inode_dirty(inode); + + mark_buffer_dirty(bh, 1); + + if (inc) + *extoffset += adsize; + udf_release_data(bh); + return (elen >> 30); +} + +int udf_next_aext(struct inode *inode, lb_addr *bloc, int *extoffset, + lb_addr *eloc, Uint32 *elen, struct buffer_head **bh, int inc) +{ + Uint16 tagIdent; + int pos, alen; + Uint8 etype; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, *bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + return -1; + } + } + + tagIdent = ((tag *)(*bh)->b_data)->tagIdent; + + if (tagIdent == TID_FILE_ENTRY || tagIdent == TID_EXTENDED_FILE_ENTRY || + UDF_I_NEW_INODE(inode)) + { + pos = udf_file_entry_alloc_offset(inode); + alen = UDF_I_LENALLOC(inode) + pos; + } + else if (tagIdent == TID_UNALLOCATED_SPACE_ENTRY) + { + pos = sizeof(struct UnallocatedSpaceEntry); + alen = UDF_I_LENALLOC(inode) + pos; + } + else if (tagIdent == TID_ALLOC_EXTENT_DESC) + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(*bh)->b_data; + + pos = sizeof(struct AllocExtDesc); + alen = le32_to_cpu(aed->lengthAllocDescs) + pos; + } + else + return -1; + + if (!(*extoffset)) + *extoffset = pos; + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + short_ad *sad; + + if (!(sad = udf_get_fileshortad((*bh)->b_data, alen, extoffset, inc))) + return -1; + + if ((etype = le32_to_cpu(sad->extLength) >> 30) == EXTENT_NEXT_EXTENT_ALLOCDECS) + { + bloc->logicalBlockNum = le32_to_cpu(sad->extPosition); + *extoffset = 0; + udf_release_data(*bh); + *bh = NULL; + return udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, inc); + } + else + { + 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 ICB_FLAG_AD_LONG: + { + long_ad *lad; + + if (!(lad = udf_get_filelongad((*bh)->b_data, alen, extoffset, inc))) + return -1; + + if ((etype = le32_to_cpu(lad->extLength) >> 30) == EXTENT_NEXT_EXTENT_ALLOCDECS) + { + *bloc = lelb_to_cpu(lad->extLocation); + *extoffset = 0; + udf_release_data(*bh); + *bh = NULL; + return udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, inc); + } + else + { + *eloc = lelb_to_cpu(lad->extLocation); + *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; + } + break; + } + case ICB_FLAG_AD_IN_ICB: + { + if (UDF_I_LENALLOC(inode) == 0) + return -1; + etype = EXTENT_RECORDED_ALLOCATED; + *eloc = UDF_I_LOCATION(inode); + *elen = UDF_I_LENALLOC(inode); + break; + } + default: + { + udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); + return -1; + } + } + if (*elen) + return etype; + + udf_debug("Empty Extent, inode=%ld, alloctype=%d, eloc=%d, elen=%d, etype=%d, extoffset=%d\n", + inode->i_ino, UDF_I_ALLOCTYPE(inode), eloc->logicalBlockNum, *elen, etype, *extoffset); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + *extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + *extoffset -= sizeof(long_ad); + return -1; +} + +int udf_current_aext(struct inode *inode, lb_addr *bloc, int *extoffset, + lb_addr *eloc, Uint32 *elen, struct buffer_head **bh, int inc) +{ + int pos, alen; + Uint8 etype; + + if (!(*bh)) + { + if (!(*bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, *bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + return -1; + } + udf_debug("new bh, count=%d\n", (*bh)->b_count); + } + + if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr))) + { + if (!(UDF_I_EXTENDED_FE(inode))) + pos = sizeof(struct FileEntry) + UDF_I_LENEATTR(inode); + else + pos = sizeof(struct ExtendedFileEntry) + UDF_I_LENEATTR(inode); + alen = UDF_I_LENALLOC(inode) + pos; + } + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(*bh)->b_data; + + pos = sizeof(struct AllocExtDesc); + alen = le32_to_cpu(aed->lengthAllocDescs) + pos; + } + + if (!(*extoffset)) + *extoffset = pos; + + switch (UDF_I_ALLOCTYPE(inode)) + { + case ICB_FLAG_AD_SHORT: + { + short_ad *sad; + + if (!(sad = udf_get_fileshortad((*bh)->b_data, 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 ICB_FLAG_AD_LONG: + { + long_ad *lad; + + if (!(lad = udf_get_filelongad((*bh)->b_data, 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; + } + } + if (*elen) + return etype; + + udf_debug("Empty Extent!\n"); + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + *extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + *extoffset -= sizeof(long_ad); + return -1; +} + +int udf_insert_aext(struct inode *inode, lb_addr bloc, int extoffset, + lb_addr neloc, Uint32 nelen, struct buffer_head *bh) +{ + lb_addr oeloc; + Uint32 oelen; + int type; + + if (!bh) + { + if (!(bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, bloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, bloc, 0)); + return -1; + } + } + else + bh->b_count ++; + + while ((type = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1) + { + udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); + + neloc = oeloc; + nelen = (type << 30) | oelen; + } + udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1); + udf_release_data(bh); + return (nelen >> 30); +} + +int udf_delete_aext(struct inode *inode, lb_addr nbloc, int nextoffset, + lb_addr eloc, Uint32 elen, struct buffer_head *nbh) +{ + struct buffer_head *obh; + lb_addr obloc; + int oextoffset, adsize; + char type; + struct AllocExtDesc *aed; + + if (!(nbh)) + { + if (!(nbh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, nbloc, 0), + inode->i_sb->s_blocksize))) + { + udf_debug("reading block %d failed!\n", + udf_get_lb_pblock(inode->i_sb, nbloc, 0)); + return -1; + } + } + else + nbh->b_count ++; + nbh->b_count ++; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_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 ((type = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) + { + udf_write_aext(inode, obloc, &oextoffset, eloc, (type << 30) | elen, obh, 1); + if (memcmp(&nbloc, &obloc, sizeof(lb_addr))) + { + obloc = nbloc; + udf_release_data(obh); + nbh->b_count ++; + obh = nbh; + oextoffset = nextoffset - adsize; + } + } + memset(&eloc, 0x00, sizeof(lb_addr)); + elen = 0; + + if (memcmp(&nbloc, &obloc, sizeof(lb_addr))) + { + udf_free_blocks(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 (!memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr))) + { + 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)); + udf_update_tag((obh)->b_data, oextoffset - (2*adsize)); + mark_buffer_dirty(obh, 1); + } + } + else + { + udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); + if (!memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr))) + { + 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); + udf_update_tag((obh)->b_data, oextoffset - adsize); + mark_buffer_dirty(obh, 1); + } + } + + udf_release_data(nbh); + udf_release_data(obh); + return (elen >> 30); +} + +int inode_bmap(struct inode *inode, int block, lb_addr *bloc, Uint32 *extoffset, + lb_addr *eloc, Uint32 *elen, Uint32 *offset, struct buffer_head **bh) +{ + int etype, lbcount = 0; + + 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 = block - lbcount; + return -1; + } + lbcount += ((*elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits); + } while (lbcount <= block); + + *offset = block + ((*elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits) - lbcount; + + return etype; +} + +int udf_bmap(struct inode *inode, int block) +{ + lb_addr eloc, bloc; + Uint32 offset, extoffset, elen; + struct buffer_head *bh = NULL; + int ret; + + if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + ret = udf_get_lb_pblock(inode->i_sb, eloc, offset); + else + ret = 0; + + if (bh) + udf_release_data(bh); + + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV)) + return udf_fixed_to_variable(ret); + else + return ret; +} + +int udf_readpage_adinicb (struct file * file, struct page * page) +{ + struct inode * inode; + struct buffer_head *bh; + int block; + + inode = file->f_dentry->d_inode; + + memset((char *)page_address(page), 0, PAGE_SIZE); + block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); + bh = getblk (inode->i_dev, block, inode->i_sb->s_blocksize); + if (!buffer_uptodate(bh)) + { + ll_rw_block (READ, 1, &bh); + wait_on_buffer(bh); + } + memcpy((char *)page_address(page), bh->b_data + udf_ext0_offset(inode), + inode->i_size); + brelse(bh); + set_bit(PG_uptodate, &page->flags); + return 0; +} diff -Nru linux/fs/udf/lowlevel.c linux.new/fs/udf/lowlevel.c --- linux/fs/udf/lowlevel.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/lowlevel.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,390 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * + * HISTORY + * + * 03/26/99 blf Created. + */ + +#include "udfdecl.h" + +#include +#include +#include +#include + +typedef struct scsi_device Scsi_Device; +typedef struct scsi_cmnd Scsi_Cmnd; + +#include + +#include +#include "udf_sb.h" + +unsigned int +udf_get_last_session(struct super_block *sb) +{ + struct cdrom_multisession ms_info; + unsigned int vol_desc_start; + kdev_t dev = sb->s_dev; + struct inode inode_fake; + extern struct file_operations * get_blkfops(unsigned int); + int i; + + vol_desc_start=0; + if (get_blkfops(MAJOR(dev))->ioctl!=NULL) + { + /* Whoops. We must save the old FS, since otherwise + * we would destroy the kernels idea about FS on root + * mount in read_super... [chexum] + */ + mm_segment_t old_fs=get_fs(); + inode_fake.i_rdev=dev; + ms_info.addr_format=CDROM_LBA; + set_fs(KERNEL_DS); + i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + CDROMMULTISESSION, + (unsigned long) &ms_info); + set_fs(old_fs); + +#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); + } + } + else + { + udf_debug("Device doesn't know how to ioctl?\n"); + } + return vol_desc_start; +} + +#ifdef CDROM_LAST_WRITTEN + +static unsigned int +udf_get_last_written(kdev_t dev, struct inode *inode_fake) +{ + extern struct file_operations * get_blkfops(unsigned int); + unsigned long lastsector; + + if (!(get_blkfops(MAJOR(dev))->ioctl(inode_fake, + NULL, + CDROM_LAST_WRITTEN, + (unsigned long) &lastsector))) + { + return lastsector - 1; + } + else + return 0; +} + +#else + +static int +do_scsi(kdev_t dev, struct inode *inode_fake, Uint8 *command, int cmd_len, + Uint8 *buffer, Uint32 in_len, Uint32 out_len) +{ + extern struct file_operations * get_blkfops(unsigned int); + Uint32 *ip; + + ip = (Uint32 *)buffer; + ip[0] = in_len; + ip[1] = out_len; + memcpy(buffer + 8, command, cmd_len); + return get_blkfops(MAJOR(dev))->ioctl(inode_fake, + NULL, SCSI_IOCTL_SEND_COMMAND, (unsigned long)buffer); +} + +static unsigned int +udf_get_last_rti(kdev_t dev, struct inode *inode_fake) +{ + char buffer[128]; + int result = 0; + int *ip; + int track_no; + Uint32 trackstart, tracklength, freeblocks; + Uint8 cdb[10]; + unsigned long lastsector = 0; + int len; + + ip = (int *)(buffer + 8); + memset(cdb, 0, 10); + cdb[0] = 0x51; + cdb[8] = 32; + result = do_scsi(dev, inode_fake, cdb, 10, buffer, 0, 32); + if (!result) + { + track_no = buffer[14]; + udf_debug("Generic Read Disc Info worked; last track is %d. status=0x%x\n", + track_no, buffer[10] & 0x3); + memset(buffer, 0, 128); + cdb[0] = 0x52; + cdb[1] = 1; + cdb[4] = (track_no & 0xFF00) >> 8; + cdb[5] = track_no & 0xFF; + cdb[8] = 8; + result = do_scsi(dev, inode_fake, cdb, 10, buffer, 0, 8); + if (!result) + { + len = cdb[8] = ((buffer[8] << 8) | (buffer[9] & 0xFF)) + 2; + result = do_scsi(dev, inode_fake, cdb, 10, buffer, 0, len); + if (!result) + { + if (buffer[14] & 0x40) + { + cdb[4] = ((track_no - 1) & 0xFF00) >> 8; + cdb[5] = (track_no - 1) & 0xFF; + result = do_scsi(dev, inode_fake, cdb, 10, buffer, 0, len); + } + if (!result) + { + trackstart = be32_to_cpu(ip[2]); + tracklength = be32_to_cpu(ip[6]); + freeblocks = be32_to_cpu(ip[4]); + udf_debug("Start %d, length %d, freeblocks %d.\n", trackstart, tracklength, freeblocks); + if (buffer[14] & 0x20) + { + if (buffer[14] & 0x10) + { + udf_debug("Packet size is %d.\n", be32_to_cpu(ip[5])); + lastsector = trackstart + tracklength - 1; + } + else + { + udf_debug("Variable packet written track.\n"); + lastsector = trackstart + tracklength - 1; + if (freeblocks) + { + lastsector = lastsector - freeblocks - 7; + } + } + } + } + } + } + } + return lastsector; +} + +static unsigned int +udf_get_toc_entry(kdev_t dev, struct inode *inode_fake) +{ + extern struct file_operations * get_blkfops(unsigned int); + struct cdrom_tocentry toc; + int res, lastsector = 0; + + toc.cdte_format = CDROM_LBA; + toc.cdte_track = 0xAA; + + if (!(res = get_blkfops(MAJOR(dev))->ioctl(inode_fake, + NULL, + CDROMREADTOCENTRY, + (unsigned long) &toc))) + { + lastsector = toc.cdte_addr.lba - 1; + } + + return lastsector; +} + +static unsigned int +udf_get_capacity(kdev_t dev, struct inode *inode_fake) +{ + char buffer[128]; + int result = 0; + int *ip; + Uint8 cdb[10]; + unsigned long lastsector = 0; + + ip = (int *)(buffer + 8); + memset(cdb, 0, 10); + + cdb[0] = READ_CAPACITY; + result = do_scsi(dev, inode_fake, cdb, 10, buffer, 0, 8); + if (!result) + lastsector = be32_to_cpu(ip[0]); + + return lastsector; +} + +static int +is_mmc(kdev_t dev, struct inode *inode_fake) +{ + Uint8 buffer[142]; + int result = 0, n; + Uint8 cdb[6]; + Uint8 *data = &buffer[8]; + int len = 4; + + cdb[0] = MODE_SENSE; + cdb[2] = 0x2A; + cdb[4] = len; + cdb[1] = cdb[3] = cdb[5] = 0; + + memset(buffer, 0, 142); + result = do_scsi(dev, inode_fake, cdb, 6, buffer, 0, len); + if (!result) + { + len = cdb[4] = data[3] + 4 + 2; + result = do_scsi(dev, inode_fake, cdb, 6, buffer, 0, len); + if (!result) + { + n = data[3] + 4; + len = cdb[4] = n + 2 + data[n+1]; + result = do_scsi(dev, inode_fake, cdb, 6, buffer, 0, len); + if (!result && ((data[n] & 0x3F) == 0x2A)) + { + udf_debug("Page Code=0x%02x PS=0x%1x Page Length=0x%02x\n", + data[n] & 0x3F, (data[n] >> 7) & 0x01, data[n+1]); + udf_debug("DVD-RAM R/W(%c/%c) DVD-R R/W(%c/%c) DVD-ROM R(%c)\n", + data[n+2] & 0x20 ? 'Y' : 'N', data[n+3] & 0x20 ? 'Y' : 'N', + data[n+2] & 0x10 ? 'Y' : 'N', data[n+3] & 0x10 ? 'Y' : 'N', + data[n+2] & 0x08 ? 'Y' : 'N'); + udf_debug("CD-RW R/W(%c/%c) CD-R R/W(%c/%c) Fixed Packet (%c)\n", + data[n+2] & 0x02 ? 'Y' : 'N', data[n+3] & 0x02 ? 'Y' : 'N', + data[n+2] & 0x01 ? 'Y' : 'N', data[n+3] & 0x01 ? 'Y' : 'N', + data[n+2] & 0x04 ? 'Y' : 'N'); + udf_debug("Multi Session (%c) Mode 2 Form 2/1 (%c/%c) Digital Port (2)/(1) (%c/%c)\n", + data[n+4] & 0x40 ? 'Y' : 'N', data[n+4] & 0x20 ? 'Y' : 'N', + data[n+4] & 0x10 ? 'Y' : 'N', data[n+4] & 0x08 ? 'Y' : 'N', + data[n+4] & 0x04 ? 'Y' : 'N'); + udf_debug("Composite (%c) Audio Play (%c) Read Bar Code (%c) UPC (%c) ISRC (%c)\n", + data[n+4] & 0x02 ? 'Y' : 'N', data[n+4] & 0x01 ? 'Y' : 'N', + data[n+5] & 0x80 ? 'Y' : 'N', data[n+5] & 0x40 ? 'Y' : 'N', + data[n+5] & 0x20 ? 'Y' : 'N'); + udf_debug("C2 Pointers are supported (%c) R-W De-interleved & corrected (%c)\n", + data[n+5] & 0x10 ? 'Y' : 'N', data[n+5] & 0x80 ? 'Y' : 'N'); + udf_debug("R-W Supported (%c) CD-DA Stream is Accurate (%c) CD-DA Commands Supported (%c)\n", + data[n+5] & 0x04 ? 'Y' : 'N', data[n+5] & 0x02 ? 'Y' : 'N', + data[n+5] & 0x01 ? 'Y' : 'N'); + udf_debug("Loading Mechanism Type=0x%03x Eject (%c) Prevent Jumper (%c)\n", + (data[n+6] >> 5) & 0x07, data[n+6] & 0x08 ? 'Y' : 'N', + data[n+6] & 0x04 ? 'Y' : 'N'); + udf_debug("Lock State (%c) Lock(%c)\n", + data[n+6] & 0x02 ? 'Y' : 'N', data[n+6] & 0x01 ? 'Y' : 'N'); + udf_debug("P through W in Lead-In (%c) Side Change Capable (%c) S/W Slot Selection (%c)\n", + data[n+7] & 0x20 ? 'Y' : 'N', data[n+7] & 0x10 ? 'Y' : 'N', + data[n+7] & 0x08 ? 'Y' : 'N'); + udf_debug("Changer Supports Disc Present (%c) Seperate Channel Mute (%c) Seperate Volume Levels (%c)\n", + data[n+7] & 0x04 ? 'Y' : 'N', data[n+7] & 0x02 ? 'Y' : 'N', + data[n+7] & 0x01 ? 'Y' : 'N'); + udf_debug("Maximum Read Speed Supported (in kBps)=0x%04x (Obsolete)\n", + (data[n+8] << 8) | (data[n+9] & 0xFF)); + udf_debug("Number of Volume Levels Support=0x%04x\n", + (data[n+10] << 8) | (data[n+11] & 0xFF)); + udf_debug("Buffer Size supported by Drive (in KBytes)=0x%04x\n", + (data[n+12] << 8) | (data[n+13] & 0xFF)); + udf_debug("Current Read Speed Selected (in kBps)=0x%04x (Obsolete)\n", + (data[n+14] << 8) | (data[n+15] & 0xFF)); + udf_debug("Digital Out: Length=0x%01x LSBF (%c) RCK (%c) BCKF (%c)\n", + (data[n+17] >> 4) & 0x03, data[n+17] & 0x08 ? 'Y' : 'N', + data[n+17] & 0x04 ? 'Y' : 'N', data[n+17] & 0x02 ? 'Y' : 'N'); + udf_debug("Maximum Write Speed Supported (in kBps)=0x%04x (Obsolete)\n", + (data[n+18] << 8) | (data[n+19] & 0xFF)); + udf_debug("Current Write Speed Selected (in kBps)=0x%04x (Obsolete)\n", + (data[n+20] << 8) | (data[n+21] & 0xFF)); + udf_debug("Copy Management Revision Supported=%04x\n", + (data[n+22] << 8) | (data[n+23] & 0xFF)); + } + else + return 0; + } + } + return !result; +} + +#endif + +unsigned int +udf_get_last_block(struct super_block *sb) +{ + kdev_t dev = sb->s_dev; + struct inode inode_fake; + extern struct file_operations * get_blkfops(unsigned int); + int ret; + unsigned long lblock; + int accurate = 0; + + if (get_blkfops(MAJOR(dev))->ioctl!=NULL) + { + /* Whoops. We must save the old FS, since otherwise + * we would destroy the kernels idea about FS on root + * mount in read_super... [chexum] + */ + mm_segment_t old_fs=get_fs(); + inode_fake.i_rdev=dev; + set_fs(KERNEL_DS); + + lblock = 0; + ret = get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + BLKGETSIZE, + (unsigned long) &lblock); + + if (!ret && lblock != 0x7FFFFFFF) /* Hard Disk */ + { + udf_debug("BLKGETSIZE lblock=%ld\n", lblock); + lblock = ((512 * lblock) / sb->s_blocksize) - 1; + accurate = 1; + } + else /* CDROM */ + { +#ifdef CDROM_LAST_WRITTEN + if ((lblock = udf_get_last_written(dev, &inode_fake))) + { + udf_debug("last_written lblock=%ld\n", lblock); + accurate = 1; + } +#else + if (is_mmc(dev, &inode_fake) && + (lblock = udf_get_last_rti(dev, &inode_fake))) + { + udf_debug("LAST_RTI lblock=%ld\n", lblock); + } + else if ((lblock = udf_get_toc_entry(dev, &inode_fake))) + { + udf_debug("TOC_ENTRY lblock=%ld\n", lblock); + } + else if ((lblock = udf_get_capacity(dev, &inode_fake))) + { + udf_debug("READ_CAPACITY lblock=%ld\n", lblock); + } +#endif + } + set_fs(old_fs); + return lblock; + } + else + { + udf_debug("Device doesn't know how to ioctl?\n"); + } + return 0; +} diff -Nru linux/fs/udf/misc.c linux.new/fs/udf/misc.c --- linux/fs/udf/misc.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/misc.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,515 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc + * + * HISTORY + * + * 04/19/99 blf partial support for reading/writing specific EA's + */ + +#include "udfdecl.h" + +#if defined(__linux__) && defined(__KERNEL__) + +#include "udf_sb.h" +#include "udf_i.h" + +#include +#include +#include + +#else + +#include +#include +#include +#include + +int udf_blocksize=0; +int udf_errno=0; + +void +udf_setblocksize(int size) +{ + udf_blocksize=size; +} +#endif + +Uint32 +udf64_low32(Uint64 indat) +{ + return indat & 0x00000000FFFFFFFFULL; +} + +Uint32 +udf64_high32(Uint64 indat) +{ + return indat >> 32; +} + +uid_t udf_convert_uid(int uidin) +{ + if ( uidin == -1 ) + return 0; + if ( uidin > (64*1024U - 1) ) /* 16 bit UID */ + return 0; + return uidin; +} + +gid_t udf_convert_gid(int gidin) +{ + if ( gidin == -1 ) + return 0; + if ( gidin > (64*1024U - 1) ) /* 16 bit GID */ + return 0; + return gidin; +} + +#if defined(__linux__) && defined(__KERNEL__) + +extern struct buffer_head * +udf_tread(struct super_block *sb, int block, int size) +{ + if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) + return bread(sb->s_dev, udf_fixed_to_variable(block), size); + else + return bread(sb->s_dev, block, size); +} + +extern struct GenericAttrFormat * +udf_add_extendedattr(struct inode * inode, Uint32 size, Uint32 type, + Uint8 loc, struct buffer_head **bh) +{ + Uint8 *ea = NULL, *ad = NULL; + long_ad eaicb; + int offset; + + *bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + struct FileEntry *fe; + + fe = (struct FileEntry *)(*bh)->b_data; + eaicb = lela_to_cpu(fe->extendedAttrICB); + offset = sizeof(struct FileEntry); + } + else + { + struct ExtendedFileEntry *efe; + + efe = (struct ExtendedFileEntry *)(*bh)->b_data; + eaicb = lela_to_cpu(efe->extendedAttrICB); + offset = sizeof(struct ExtendedFileEntry); + } + + ea = &(*bh)->b_data[offset]; + if (UDF_I_LENEATTR(inode)) + offset += UDF_I_LENEATTR(inode); + else + size += sizeof(struct ExtendedAttrHeaderDesc); + + ad = &(*bh)->b_data[offset]; + if (UDF_I_LENALLOC(inode)) + offset += UDF_I_LENALLOC(inode); + + offset = inode->i_sb->s_blocksize - offset; + + /* 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) != TID_EXTENDED_ATTRE_HEADER_DESC || + le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) + { + udf_release_data(*bh); + return NULL; + } + } + else + { + size -= sizeof(struct ExtendedAttrHeaderDesc); + UDF_I_LENEATTR(inode) += sizeof(struct ExtendedAttrHeaderDesc); + eahd->descTag.tagIdent = cpu_to_le16(TID_EXTENDED_ATTRE_HEADER_DESC); + eahd->descTag.descVersion = cpu_to_le16(2); + eahd->descTag.tagSerialNum = cpu_to_le16(1); + 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 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 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 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 */ + UDF_I_LENEATTR(inode) += size; + return (struct GenericAttrFormat *)&ea[offset]; + } + if (loc & 0x02) + { + } + udf_release_data(*bh); + return NULL; +} + +extern struct GenericAttrFormat * +udf_get_extendedattr(struct inode * inode, Uint32 type, Uint8 subtype, + struct buffer_head **bh) +{ + struct GenericAttrFormat *gaf; + Uint8 *ea = NULL; + long_ad eaicb; + Uint32 offset; + + *bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (UDF_I_EXTENDED_FE(inode) == 0) + { + struct FileEntry *fe; + + fe = (struct FileEntry *)(*bh)->b_data; + eaicb = lela_to_cpu(fe->extendedAttrICB); + if (UDF_I_LENEATTR(inode)) + ea = fe->extendedAttr; + } + else + { + struct ExtendedFileEntry *efe; + + efe = (struct ExtendedFileEntry *)(*bh)->b_data; + eaicb = lela_to_cpu(efe->extendedAttrICB); + if (UDF_I_LENEATTR(inode)) + ea = efe->extendedAttr; + } + + if (UDF_I_LENEATTR(inode)) + { + struct ExtendedAttrHeaderDesc *eahd; + eahd = (struct ExtendedAttrHeaderDesc *)ea; + + /* check checksum/crc */ + if (le16_to_cpu(eahd->descTag.tagIdent) != TID_EXTENDED_ATTRE_HEADER_DESC || + le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) + { + udf_release_data(*bh); + 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 GenericAttrFormat *)&ea[offset]; + if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) + return gaf; + else + offset += le32_to_cpu(gaf->attrLength); + } + } + + udf_release_data(*bh); + if (eaicb.extLength) + { + /* TODO */ + } + return NULL; +} + +extern struct buffer_head * +udf_read_untagged(struct super_block *sb, Uint32 block, Uint32 offset) +{ + struct buffer_head *bh = NULL; + + /* Read the block */ + bh = udf_tread(sb, block+offset, sb->s_blocksize); + if (!bh) + { + printk(KERN_ERR "udf: udf_read_untagged(%p,%d,%d) failed\n", + sb, block, offset); + return NULL; + } + return bh; +} + +/* + * udf_read_tagged + * + * PURPOSE + * Read the first block of a tagged descriptor. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +extern struct buffer_head * +udf_read_tagged(struct super_block *sb, Uint32 block, Uint32 location, Uint16 *ident) +{ + tag *tag_p; + struct buffer_head *bh = NULL; + register Uint8 checksum; + register int i; + + /* Read the block */ + if (block == 0xFFFFFFFF) + return NULL; + + bh = udf_tread(sb, block, sb->s_blocksize); + if (!bh) + { + udf_debug("block=%d, location=%d: read failed\n", block, 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 %d, tag %d != %d\n", + block, le32_to_cpu(tag_p->tagLocation), location); + goto error_out; + } + + /* Verify the tag checksum */ + checksum = 0U; + for (i = 0; i < 4; i++) + checksum += (Uint8)(bh->b_data[i]); + for (i = 5; i < 16; i++) + checksum += (Uint8)(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, le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); + +error_out: + brelse(bh); + return NULL; +} + +extern struct buffer_head * +udf_read_ptagged(struct super_block *sb, lb_addr loc, Uint32 offset, Uint16 *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); +} + +#endif + +void udf_update_tag(char *data, int length) +{ + tag *tptr = (tag *)data; + int i; + + length -= sizeof(tag); + + tptr->tagChecksum = 0; + tptr->descCRCLength = le16_to_cpu(length); + tptr->descCRC = le16_to_cpu(udf_crc(data + sizeof(tag), length, 0)); + + for (i=0; i<16; i++) + if (i != 4) + tptr->tagChecksum += (Uint8)(data[i]); +} + +void udf_new_tag(char *data, Uint16 ident, Uint16 version, Uint16 snum, + Uint32 loc, int length) +{ + tag *tptr = (tag *)data; + tptr->tagIdent = le16_to_cpu(ident); + tptr->descVersion = le16_to_cpu(version); + tptr->tagSerialNum = le16_to_cpu(snum); + tptr->tagLocation = le32_to_cpu(loc); + udf_update_tag(data, length); +} + +#ifndef __KERNEL__ +/* + * udf_read_tagged_data + * + * PURPOSE + * Read the first block of a tagged descriptor. + * Usable from user-land. + * + * HISTORY + * 10/4/98 dgb: written + */ +int +udf_read_tagged_data(char *buffer, int size, int fd, int block, int offset) +{ + tag *tag_p; + register Uint8 checksum; + register int i; + unsigned long offs; + + if (!buffer) + { + udf_errno = 1; + return -1; + } + + if ( !udf_blocksize ) + { + udf_errno = 2; + return -1; + } + + if ( size < udf_blocksize ) + { + udf_errno=3; + return -1; + } + udf_errno=0; + + offs=(long)block * udf_blocksize; + if ( lseek(fd, offs, SEEK_SET) != offs ) { + udf_errno=4; + return -1; + } + + i=read(fd, buffer, udf_blocksize); + if ( i < udf_blocksize ) { + udf_errno=5; + return -1; + } + + tag_p = (tag *)(buffer); + + /* Verify the tag location */ + if ((block-offset) != tag_p->tagLocation) { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: location mismatch block %d, tag %d\n", + block, tag_p->tagLocation); +#else + udf_errno=6; +#endif + goto error_out; + } + + /* Verify the tag checksum */ + checksum = 0U; + for (i = 0; i < 4; i++) + checksum += (Uint8)(buffer[i]); + for (i = 5; i < 16; i++) + checksum += (Uint8)(buffer[i]); + if (checksum != tag_p->tagChecksum) { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: tag checksum failed\n"); +#else + udf_errno=7; +#endif + goto error_out; + } + + /* Verify the tag version */ + if (tag_p->descVersion != 0x0002U) { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: tag version 0x%04x != 0x0002U\n", + tag_p->descVersion); +#else + udf_errno=8; +#endif + goto error_out; + } + + /* Verify the descriptor CRC */ + if (tag_p->descCRC == udf_crc(buffer + 16, tag_p->descCRCLength, 0)) { + udf_errno=0; + return 0; + } +#ifdef __KERNEL__ + printk(KERN_ERR "udf: crc failure in udf_read_tagged\n"); +#else + udf_errno=9; +#endif + +error_out: + return -1; +} +#endif diff -Nru linux/fs/udf/namei.c linux.new/fs/udf/namei.c --- linux/fs/udf/namei.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/namei.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,1609 @@ +/* + * 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@hootie.lvld.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-2000 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" + +#if defined(__linux__) && defined(__KERNEL__) +#include +#include "udf_i.h" +#include "udf_sb.h" +#include +#include +#include +#include +#include +#include +#endif + +static inline int udf_match(int len, const char * const name, struct qstr *qs) +{ + if (len != qs->len) + return 0; + return !memcmp(name, qs->name, len); +} + +int udf_write_fi(struct FileIdentDesc *cfi, struct FileIdentDesc *sfi, + struct udf_fileident_bh *fibh, + Uint8 *impuse, Uint8 *fileident) +{ + Uint16 crclen = fibh->eoffset - fibh->soffset - sizeof(tag); + Uint16 crc; + Uint8 checksum = 0; + int i; + int offset; + Uint16 liu = le16_to_cpu(cfi->lengthOfImpUse); + Uint8 lfi = cfi->lengthFileIdent; + int padlen = fibh->eoffset - fibh->soffset - liu - lfi - + sizeof(struct FileIdentDesc); + + + offset = fibh->soffset + sizeof(struct FileIdentDesc); + + if (impuse) + { + if (offset + liu < 0) + memcpy((Uint8 *)sfi->impUse, impuse, liu); + else if (offset >= 0) + memcpy(fibh->ebh->b_data + offset, impuse, liu); + else + { + memcpy((Uint8 *)sfi->impUse, impuse, -offset); + memcpy(fibh->ebh->b_data, impuse - offset, liu + offset); + } + } + + offset += liu; + + if (fileident) + { + if (offset + lfi < 0) + memcpy((Uint8 *)sfi->fileIdent + liu, fileident, lfi); + else if (offset >= 0) + memcpy(fibh->ebh->b_data + offset, fileident, lfi); + else + { + memcpy((Uint8 *)sfi->fileIdent + liu, fileident, -offset); + memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); + } + } + + offset += lfi; + + if (offset + padlen < 0) + memset((Uint8 *)sfi->padding + liu + lfi, 0x00, padlen); + else if (offset >= 0) + memset(fibh->ebh->b_data + offset, 0x00, padlen); + else + { + memset((Uint8 *)sfi->padding + liu + lfi, 0x00, -offset); + memset(fibh->ebh->b_data, 0x00, padlen + offset); + } + + crc = udf_crc((Uint8 *)cfi + sizeof(tag), sizeof(struct FileIdentDesc) - + sizeof(tag), 0); + + if (fibh->sbh == fibh->ebh) + crc = udf_crc((Uint8 *)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 *)sfi->impUse, + -fibh->soffset - sizeof(struct FileIdentDesc), crc); + crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); + } + + cfi->descTag.descCRC = cpu_to_le32(crc); + cfi->descTag.descCRCLength = cpu_to_le16(crclen); + + for (i=0; i<16; i++) + if (i != 4) + checksum += ((Uint8 *)&cfi->descTag)[i]; + + cfi->descTag.tagChecksum = checksum; + if (sizeof(struct FileIdentDesc) <= -fibh->soffset) + memcpy((Uint8 *)sfi, (Uint8 *)cfi, sizeof(struct FileIdentDesc)); + else + { + memcpy((Uint8 *)sfi, (Uint8 *)cfi, -fibh->soffset); + memcpy(fibh->ebh->b_data, (Uint8 *)cfi - fibh->soffset, + sizeof(struct FileIdentDesc) + fibh->soffset); + } + + if (fibh->sbh != fibh->ebh) + mark_buffer_dirty(fibh->ebh, 1); + mark_buffer_dirty(fibh->sbh, 1); + 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; + int f_pos, block; + int flen; + char fname[255]; + char *nameptr; + Uint8 lfi; + Uint16 liu; + int size = (udf_ext0_offset(dir) + dir->i_size) >> 2; + lb_addr bloc, eloc; + Uint32 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 (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return NULL; + } + + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + { + udf_debug("udf_tread failed: block=%d\n", block); + 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 *)(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 & FILE_DELETED) != 0 ) + { + if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) + continue; + } + + if ( (cfi->fileCharacteristics & FILE_HIDDEN) != 0 ) + { + if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) + continue; + } + + if (!lfi) + continue; + + if ((flen = udf_get_filename(nameptr, fname, lfi))) + { + if (udf_match(flen, fname, &(dentry->d_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. + * + * POST-CONDITIONS + * Zero on success. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7) +int +#else +struct dentry * +#endif +udf_lookup(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = NULL; + struct FileIdentDesc cfi, *fi; + struct udf_fileident_bh fibh; + + if (dentry->d_name.len > UDF_NAME_LEN) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7) + return -ENAMETOOLONG; +#else + return ERR_PTR(-ENAMETOOLONG); +#endif + +#ifdef UDF_RECOVERY + /* temporary shorthand for specifying files by inode number */ + if (!strncmp(dentry->d_name.name, ".B=", 3) ) + { + lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; + inode = udf_iget(dir->i_sb, lb); + if (!inode) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7) + return -EACCES; +#else + return ERR_PTR(-EACCES); +#endif + } + 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 ) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7) + return -EACCES; +#else + return ERR_PTR(-EACCES); +#endif + } + d_add(dentry, inode); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7) + return 0; +#else + return NULL; +#endif +} + +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; + struct ustr unifilename; + char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; + int namelen; + int f_pos; + int flen; + char *nameptr; + int size = (udf_ext0_offset(dir) + dir->i_size) >> 2; + int nfidlen; + Uint8 lfi; + Uint16 liu; + int block; + lb_addr bloc, eloc; + Uint32 extoffset, elen, offset; + struct buffer_head *bh = NULL; + + if (!dir || !dir->i_nlink) + return NULL; + sb = dir->i_sb; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + if (dentry->d_name.len >= UDF_NAME_LEN) + { + *err = -ENAMETOOLONG; + return NULL; + } +#endif + + if (dentry->d_name.len) + { + if (!dentry->d_name.len) + { + *err = -EINVAL; + return NULL; + } + + if ( !(udf_char_to_ustr(&unifilename, dentry->d_name.name, dentry->d_name.len)) ) + { + *err = -ENAMETOOLONG; + return NULL; + } + + if ( !(namelen = udf_UTF8toCS0(name, &unifilename, UDF_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 (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + + if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + { + udf_release_data(bh); + *err = -EIO; + return NULL; + } + + block = UDF_I_LOCATION(dir).logicalBlockNum; + + 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 & FILE_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(cfi, fi, fibh, NULL, name)) + return fi; + else + { + *err = -EIO; + return NULL; + } + } + } + + if (!lfi) + continue; + + if ((flen = udf_get_filename(nameptr, fname, lfi))) + { + if (udf_match(flen, fname, &(dentry->d_name))) + { + if (fibh->sbh != fibh->ebh) + udf_release_data(fibh->ebh); + udf_release_data(fibh->sbh); + udf_release_data(bh); + *err = -EEXIST; + return NULL; + } + } + } + } + else + { + block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize); + fibh->soffset = fibh->eoffset = udf_file_entry_alloc_offset(dir); + } + else + { + fibh->sbh = fibh->ebh = NULL; + fibh->soffset = fibh->eoffset = sb->s_blocksize; + } + } + + f_pos += nfidlen; + + if (UDF_I_ALLOCTYPE(dir) == ICB_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) == ICB_FLAG_AD_SHORT) + extoffset += sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_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) != ICB_FLAG_AD_IN_ICB) + block = eloc.logicalBlockNum + ((elen - 1) >> + dir->i_sb->s_blocksize_bits); + else + block = UDF_I_LOCATION(dir).logicalBlockNum; + + 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) == + EXTENT_RECORDED_ALLOCATED) + { + 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)); + udf_new_tag((char *)cfi, TID_FILE_IDENT_DESC, 2, 1, block, sizeof(tag)); + cfi->fileVersionNum = cpu_to_le16(1); + cfi->lengthFileIdent = namelen; + cfi->lengthOfImpUse = cpu_to_le16(0); + if (!udf_write_fi(cfi, fi, fibh, NULL, name)) + { + udf_release_data(bh); + dir->i_size += nfidlen; + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + UDF_I_LENALLOC(dir) += nfidlen; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif + 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 FileIdentDesc *fi, + struct udf_fileident_bh *fibh, + struct FileIdentDesc *cfi) +{ + cfi->fileCharacteristics |= FILE_DELETED; + return udf_write_fi(cfi, fi, fibh, NULL, NULL); +} + +int udf_create(struct inode *dir, struct dentry *dentry, int mode) +{ + struct udf_fileident_bh fibh; + struct inode *inode; + struct FileIdentDesc cfi, *fi; + int err; + + inode = udf_new_inode(dir, mode, &err); + if (!inode) + { + udf_debug("udf_new_inode failure, err=%d\n", err); + return err; + } + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + inode->i_op = &udf_file_inode_operations_adinicb; + else + inode->i_op = &udf_file_inode_operations; + inode->i_mode = mode; + mark_inode_dirty(inode); + + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + { + udf_debug("udf_add_entry failure!\n"); + inode->i_nlink --; + mark_inode_dirty(inode); + iput(inode); + return err; + } + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + d_instantiate(dentry, inode); + return 0; +} + +int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev) +{ + struct inode * inode; + struct udf_fileident_bh fibh; + int err; + struct FileIdentDesc cfi, *fi; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + err = -ENAMETOOLONG; + if (dentry->d_name.len >= UDF_NAME_LEN) + goto out; +#endif + + err = -EIO; + inode = udf_new_inode(dir, mode, &err); + if (!inode) + goto out; + + inode->i_uid = current->fsuid; + inode->i_mode = mode; + inode->i_op = NULL; + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + { + udf_debug("udf_add_entry failure!\n"); + inode->i_nlink --; + mark_inode_dirty(inode); + iput(inode); + return err; + } + cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif + } + if (S_ISREG(inode->i_mode)) + { + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + inode->i_op = &udf_file_inode_operations_adinicb; + else + inode->i_op = &udf_file_inode_operations; + } + else if (S_ISCHR(inode->i_mode)) + { + inode->i_op = &chrdev_inode_operations; + } + else if (S_ISBLK(inode->i_mode)) + { + inode->i_op = &blkdev_inode_operations; + } + else if (S_ISFIFO(inode->i_mode)) + { + init_fifo(inode); + } + if (S_ISBLK(mode) || S_ISCHR(mode)) + inode->i_rdev = to_kdev_t(rdev); + 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: + return err; +} + +int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) +{ + struct inode * inode; + struct udf_fileident_bh fibh; + int err; + struct FileIdentDesc cfi, *fi; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + err = -ENAMETOOLONG; + if (dentry->d_name.len >= UDF_NAME_LEN) + goto out; +#endif + + err = -EMLINK; + if (dir->i_nlink >= (256<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; + 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)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL); + cfi.fileCharacteristics = FILE_DIRECTORY | FILE_PARENT; + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + udf_release_data(fibh.sbh); + inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); + 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))) + { + udf_debug("udf_add_entry failure!\n"); + 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)); + *(Uint32 *)((struct ADImpUse *)cfi.icb.impUse)->impUse = + cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + cfi.fileCharacteristics |= FILE_DIRECTORY; + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif + 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: + return err; +} + +static int empty_dir(struct inode *dir) +{ + struct FileIdentDesc *fi, cfi; + struct udf_fileident_bh fibh; + int f_pos; + int size = (udf_ext0_offset(dir) + dir->i_size) >> 2; + int block; + lb_addr bloc, eloc; + Uint32 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 (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), + &bloc, &extoffset, &eloc, &elen, &offset, &bh) == EXTENT_RECORDED_ALLOCATED) + { + block = udf_get_lb_pblock(dir->i_sb, eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) + { + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_SHORT) + extoffset -= sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_LONG) + extoffset -= sizeof(long_ad); + } + else + offset = 0; + } + else + { + udf_release_data(bh); + return 0; + } + + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize))) + 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 & FILE_DELETED) == 0) + { + 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; +} + +int udf_rmdir(struct inode * dir, struct dentry * dentry) +{ + int retval; + struct inode * inode; + struct udf_fileident_bh fibh; + struct FileIdentDesc *fi, cfi; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + retval = -ENAMETOOLONG; + if (dentry->d_name.len >= UDF_NAME_LEN) + goto out; +#endif + + retval = -ENOENT; + fi = udf_find_entry(dir, dentry, &fibh, &cfi); + if (!fi) + goto out; + + inode = dentry->d_inode; + DQUOT_INIT(inode); + + retval = -EIO; + if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0) != inode->i_ino) + goto end_rmdir; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + if (!empty_dir(inode)) + retval = -ENOTEMPTY; + else if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0) != + inode->i_ino) + { + retval = -ENOENT; + } + else + { + retval = udf_delete_entry(fi, &fibh, &cfi); + dir->i_version = ++event; + } +#else + retval = -ENOTEMPTY; + if (!empty_dir(inode)) + goto end_rmdir; + retval = udf_delete_entry(fi, &fibh, &cfi); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif +#endif + 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); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + inode->i_version = ++event; +#else + inode->i_version = ++global_event; +#endif + 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_TIME; + UDF_I_UCTIME(inode) = UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; + mark_inode_dirty(dir); + d_delete(dentry); + +end_rmdir: + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); +out: + return retval; +} + +int udf_unlink(struct inode * dir, struct dentry * dentry) +{ + int retval; + struct inode * inode; + struct udf_fileident_bh fibh; + struct FileIdentDesc *fi; + struct FileIdentDesc cfi; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + retval = -ENAMETOOLONG; + if (dentry->d_name.len >= UDF_NAME_LEN) + goto out; +#endif + + retval = -ENOENT; + fi = udf_find_entry(dir, dentry, &fibh, &cfi); + if (!fi) + goto out; + + inode = dentry->d_inode; + DQUOT_INIT(inode); + + retval = -EIO; + + if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 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(fi, &fibh, &cfi); + if (retval) + goto end_unlink; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME; + mark_inode_dirty(dir); + inode->i_nlink--; + mark_inode_dirty(inode); + inode->i_ctime = dir->i_ctime; + retval = 0; + d_delete(dentry); /* This also frees the inode */ + +end_unlink: + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); +out: + return retval; +} + +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; + + if (!(inode = udf_new_inode(dir, S_IFLNK, &err))) + goto out; + + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &udf_symlink_inode_operations; + + if (UDF_I_ALLOCTYPE(inode) != ICB_FLAG_AD_IN_ICB) + { + struct buffer_head *bh = NULL; + lb_addr bloc, eloc; + Uint32 elen, extoffset; + + block = udf_new_block(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; + extoffset = udf_file_entry_alloc_offset(inode); + udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 0); + udf_release_data(bh); + + inode->i_blocks = inode->i_sb->s_blocksize / 512; + block = udf_get_pblock(inode->i_sb, block, + UDF_I_LOCATION(inode).partitionReferenceNum, 0); + } + else + block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0); + + bh = udf_tread(inode->i_sb, block, inode->i_sb->s_blocksize); + ea = bh->b_data + udf_ext0_offset(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 (pc->componentIdent[0] == '.') + { + if (pc->lengthComponentIdent == 1) + pc->componentType = 4; + else if (pc->lengthComponentIdent == 2 && pc->componentIdent[1] == '.') + pc->componentType = 3; + } + + if (pc->componentType == 5) + { + if (elen + sizeof(struct PathComponent) + symname - compstart > eoffset) + goto out_no_entry; + else + pc->lengthComponentIdent = symname - compstart; + + memcpy(pc->componentIdent, compstart, pc->lengthComponentIdent); + } + + 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) == ICB_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 uniqueID; + lvhd = (struct LogicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); + uniqueID = le64_to_cpu(lvhd->uniqueID); + *(Uint32 *)((struct ADImpUse *)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), 1); + } + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + d_instantiate(dentry, inode); + err = 0; + +out: + return err; + +out_no_entry: + inode->i_nlink--; + mark_inode_dirty(inode); + iput(inode); + goto out; +} + +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; + int err; + struct FileIdentDesc cfi, *fi; + + if (S_ISDIR(inode->i_mode)) + return -EPERM; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + return -EPERM; +#endif + + if (inode->i_nlink >= (256<i_nlink))-1) + return -EMLINK; + + if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + 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 uniqueID; + lvhd = (struct LogicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); + uniqueID = le64_to_cpu(lvhd->uniqueID); + *(Uint32 *)((struct ADImpUse *)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), 1); + } + udf_write_fi(&cfi, fi, &fibh, NULL, NULL); + if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(dir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + dir->i_version = ++event; +#else + dir->i_version = ++global_event; +#endif + } + if (fibh.sbh != fibh.ebh) + udf_release_data(fibh.ebh); + udf_release_data(fibh.sbh); + inode->i_nlink ++; + inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(inode) = CURRENT_UTIME; + mark_inode_dirty(inode); + inode->i_count ++; + d_instantiate(dentry, inode); + return 0; +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) + +/* + * rename uses retrying to avoid race-conditions: at least they should be + * minimal. + * it tries to allocate all the blocks, then sanity-checks, and if the sanity- + * checks fail, it tries to restart itself again. Very practical - no changes + * are done until we know everything works ok.. and then all the changes can be + * done in one fell swoop when we have claimed all the buffers needed. + * + * Anybody can rename anything with this: the permission checks are left to the + * higher-level routines. + */ +static int do_udf_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode * old_inode, * new_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; + + if (old_dentry->d_name.len > UDF_NAME_LEN) + goto end_rename; + + old_inode = old_dentry->d_inode; + ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); + if (!ofi || udf_get_lb_pblock(old_dir->i_sb, lelb_to_cpu(ocfi.icb.extLocation), 0) != + old_inode->i_ino) + { + goto end_rename; + } + + new_inode = new_dentry->d_inode; + 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; + } + else + { + DQUOT_INIT(new_inode); + } + } + retval = 0; + if (new_inode == old_inode) + goto end_rename; + if (S_ISDIR(old_inode->i_mode)) + { + Uint32 offset = udf_ext0_offset(old_inode); + retval = -EINVAL; + if (is_subdir(new_dentry, old_dentry)) + goto end_rename; + + if (new_inode) + { + /* Prune any children before testing for busy */ + if (new_dentry->d_count > 1) + shrink_dcache_parent(new_dentry); + retval = -EBUSY; + if (new_dentry->d_count > 1) + goto end_rename; + retval = -ENOTEMPTY; + if (!empty_dir(new_inode)) + goto end_rename; + } + 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; + if (udf_get_lb_pblock(old_inode->i_sb, cpu_to_lelb(dir_fi->icb.extLocation), 0) != + old_dir->i_ino) + { + goto end_rename; + } + retval = -EMLINK; + if (!new_inode && new_dir->i_nlink >= (256<i_nlink))-1) + goto end_rename; + } + if (!nfi) + { + nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); + if (!nfi) + goto end_rename; + } + new_dir->i_version = ++event; + + /* + * Like most other Unix systems, set the ctime for inodes on a + * rename. + */ + old_inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(old_inode) = CURRENT_UTIME; + 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(&ncfi, nfi, &nfibh, NULL, NULL); + + udf_delete_entry(ofi, &ofibh, &ocfi); + + old_dir->i_version = ++event; + if (new_inode) + { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(new_inode) = CURRENT_UTIME; + mark_inode_dirty(new_inode); + } + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(old_dir) = UDF_I_UMTIME(old_dir) = CURRENT_UTIME; + mark_inode_dirty(old_dir); + + if (dir_bh) + { + dir_fi->icb.extLocation = lelb_to_cpu(UDF_I_LOCATION(new_dir)); + udf_update_tag((char *)dir_fi, sizeof(struct FileIdentDesc) + + cpu_to_le16(dir_fi->lengthOfImpUse)); + if (UDF_I_ALLOCTYPE(old_inode) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(old_inode); + old_inode->i_version = ++event; + } + else + mark_buffer_dirty(dir_bh, 1); + 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); + } + } + + /* Update the dcache */ + d_move(old_dentry, new_dentry); + retval = 0; + +end_rename: + udf_release_data(dir_bh); + if (ofi) + { + if (ofibh.sbh != ofibh.ebh) + udf_release_data(ofibh.ebh); + udf_release_data(ofibh.sbh); + } + if (nfi) + { + if (nfibh.sbh != nfibh.ebh) + udf_release_data(nfibh.ebh); + udf_release_data(nfibh.sbh); + } + return retval; +} + +/* Ok, rename also locks out other renames, as they can change the parent of + * a directory, and we don't want any races. Other races are checked for by + * "do_rename()", which restarts if there are inconsistencies. + * + * Note that there is no race between different filesystems: it's only within + * the same device that races occur: many renames can happen at once, as long + * as they are on different partitions. + * + * In the udf file system, we use a lock flag stored in the memory + * super-block. This way, we really lock other renames only if they occur + * on the same file system + */ + +int udf_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int result; + + while (UDF_SB_RENAME_LOCK(old_dir->i_sb)) + sleep_on(&UDF_SB_RENAME_WAIT(old_dir->i_sb)); + UDF_SB_RENAME_LOCK(old_dir->i_sb) = 1; + result = do_udf_rename(old_dir, old_dentry, new_dir, new_dentry); + UDF_SB_RENAME_LOCK(old_dir->i_sb) = 0; + wake_up(&UDF_SB_RENAME_WAIT(old_dir->i_sb)); + return result; +} + +#else + +/* Anybody can rename anything with this: the permission checks are left to the + * higher-level routines. + */ +int udf_rename (struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry) +{ + struct inode * old_inode, * new_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; + + old_inode = old_dentry->d_inode; + ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); + if (!ofi || udf_get_lb_pblock(old_dir->i_sb, lelb_to_cpu(ocfi.icb.extLocation), 0) != + old_inode->i_ino) + { + goto end_rename; + } + + new_inode = new_dentry->d_inode; + 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; + } + else + { + DQUOT_INIT(new_inode); + } + } + if (S_ISDIR(old_inode->i_mode)) + { + Uint32 offset = udf_ext0_offset(old_inode); + + if (new_inode) + { + retval = -ENOTEMPTY; + if (!empty_dir(new_inode)) + goto end_rename; + } + retval = -EIO; + 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; + if (udf_get_lb_pblock(old_inode->i_sb, cpu_to_lelb(dir_fi->icb.extLocation), 0) != + old_dir->i_ino) + { + goto end_rename; + } + retval = -EMLINK; + if (!new_inode && new_dir->i_nlink >= (256<i_nlink))-1) + goto end_rename; + } + if (!nfi) + { + nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); + if (!nfi) + goto end_rename; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + new_dir->i_version = ++event; +#else + new_dir->i_version = ++global_event; +#endif + + /* + * ok, that's it + */ + ncfi.fileVersionNum = ocfi.fileVersionNum; + ncfi.fileCharacteristics = ocfi.fileCharacteristics; + memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad)); + udf_write_fi(&ncfi, nfi, &nfibh, NULL, NULL); + + udf_delete_entry(ofi, &ofibh, &ocfi); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + old_dir->i_version = ++event; +#else + old_dir->i_version = ++global_event; +#endif + if (new_inode) + { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + UDF_I_UCTIME(new_inode) = CURRENT_UTIME; + mark_inode_dirty(new_inode); + } + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; + UDF_I_UCTIME(old_dir) = UDF_I_UMTIME(old_dir) = CURRENT_UTIME; + mark_inode_dirty(old_dir); + + if (dir_bh) + { + dir_fi->icb.extLocation = lelb_to_cpu(UDF_I_LOCATION(new_dir)); + udf_update_tag((char *)dir_fi, sizeof(struct FileIdentDesc) + + cpu_to_le16(dir_fi->lengthOfImpUse)); + if (UDF_I_ALLOCTYPE(old_inode) == ICB_FLAG_AD_IN_ICB) + { + mark_inode_dirty(old_inode); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,14) + old_inode->i_version = ++event; +#else + old_inode->i_version = ++global_event; +#endif + } + else + mark_buffer_dirty(dir_bh, 1); + 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); + } + } + + retval = 0; + +end_rename: + udf_release_data(dir_bh); + if (ofi) + { + if (ofibh.sbh != ofibh.ebh) + udf_release_data(ofibh.ebh); + udf_release_data(ofibh.sbh); + } + if (nfi) + { + if (nfibh.sbh != nfibh.ebh) + udf_release_data(nfibh.ebh); + udf_release_data(nfibh.sbh); + } + return retval; +} +#endif diff -Nru linux/fs/udf/partition.c linux.new/fs/udf/partition.c --- linux/fs/udf/partition.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/partition.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,233 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * + * HISTORY + * + * 12/06/98 blf Created file. + * + */ + +#include "udfdecl.h" +#include "udf_sb.h" +#include "udf_i.h" + +#include +#include +#include +#include + +inline Uint32 udf_get_pblock(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 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 udf_get_pblock_virt15(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + struct buffer_head *bh = NULL; + Uint32 newblock; + Uint32 index; + Uint32 loc; + + index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(Uint32); + + 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))); + index = block % (sb->s_blocksize / sizeof(Uint32)); + } + else + { + newblock = 0; + index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(Uint32) + block; + } + + loc = udf_bmap(UDF_SB_VAT(sb), newblock); + + if (!(bh = bread(sb->s_dev, loc, sb->s_blocksize))) + { + 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(((Uint32 *)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 udf_get_pblock_virt20(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + return udf_get_pblock_virt15(sb, block, partition, offset); +} + +Uint32 udf_get_pblock_spar15(struct super_block *sb, Uint32 block, Uint16 partition, Uint32 offset) +{ + Uint32 packet = (block + offset) >> UDF_SB_TYPESPAR(sb,partition).s_spar_pshift; + Uint32 index = 0; + + if (UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize == 8) + index = UDF_SB_TYPESPAR(sb,partition).s_spar_remap.s_spar_remap8[packet]; + else if (UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize == 16) + index = UDF_SB_TYPESPAR(sb,partition).s_spar_remap.s_spar_remap16[packet]; + else if (UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize == 32) + index = UDF_SB_TYPESPAR(sb,partition).s_spar_remap.s_spar_remap32[packet]; + + if (index == ((1 << UDF_SB_TYPESPAR(sb,partition).s_spar_indexsize)-1)) + return UDF_SB_PARTROOT(sb,partition) + block + offset; + + packet = UDF_SB_TYPESPAR(sb,partition).s_spar_map[index]; + return packet + ((block + offset) & ((1 << UDF_SB_TYPESPAR(sb,partition).s_spar_pshift)-1)); +} + +void udf_fill_spartable(struct super_block *sb, struct udf_sparing_data *sdata, int partlen) +{ + Uint16 ident; + Uint32 spartable; + int i; + struct buffer_head *bh; + struct SparingTable *st; + + for (i=0; i<4; i++) + { + if (!(spartable = sdata->s_spar_loc[i])) + continue; + + bh = udf_read_tagged(sb, spartable, spartable, &ident); + + if (!bh) + { + sdata->s_spar_loc[i] = 0; + continue; + } + + if (ident == 0) + { + st = (struct SparingTable *)bh->b_data; + if (!strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) + { + SparingEntry *se; + Uint16 rtl = le16_to_cpu(st->reallocationTableLen); + int index; + + if (!sdata->s_spar_map) + { + int num = 1, mapsize; + sdata->s_spar_indexsize = 8; + while (rtl*sizeof(Uint32) >= (1 << sdata->s_spar_indexsize)) + { + num ++; + sdata->s_spar_indexsize <<= 1; + } + mapsize = (rtl * sizeof(Uint32)) + + ((partlen/(1 << sdata->s_spar_pshift)) * sizeof(Uint8) * num); + sdata->s_spar_map = kmalloc(mapsize, GFP_KERNEL); + sdata->s_spar_remap.s_spar_remap32 = &sdata->s_spar_map[rtl]; + memset(sdata->s_spar_map, 0xFF, mapsize); + } + + index = sizeof(struct SparingTable); + for (i=0; i sb->s_blocksize) + { + udf_release_data(bh); + bh = udf_tread(sb, ++spartable, sb->s_blocksize); + if (!bh) + { + sdata->s_spar_loc[i] = 0; + continue; + } + index = 0; + } + se = (SparingEntry *)&(bh->b_data[index]); + index += sizeof(SparingEntry); + + if (sdata->s_spar_map[i] == 0xFFFFFFFF) + sdata->s_spar_map[i] = le32_to_cpu(se->mappedLocation); + else if (sdata->s_spar_map[i] != le32_to_cpu(se->mappedLocation)) + { + udf_debug("Found conflicting Sparing Data (%d vs %d for entry %d)\n", + sdata->s_spar_map[i], le32_to_cpu(se->mappedLocation), i); + } + + if (le32_to_cpu(se->origLocation) < 0xFFFFFFF0) + { + int packet = le32_to_cpu(se->origLocation) >> sdata->s_spar_pshift; + if (sdata->s_spar_indexsize == 8) + { + if (sdata->s_spar_remap.s_spar_remap8[packet] == 0xFF) + sdata->s_spar_remap.s_spar_remap8[packet] = i; + else if (sdata->s_spar_remap.s_spar_remap8[packet] != i) + { + udf_debug("Found conflicting Sparing Data (%d vs %d)\n", + sdata->s_spar_remap.s_spar_remap8[packet], i); + } + } + else if (sdata->s_spar_indexsize == 16) + { + if (sdata->s_spar_remap.s_spar_remap16[packet] == 0xFFFF) + sdata->s_spar_remap.s_spar_remap16[packet] = i; + else if (sdata->s_spar_remap.s_spar_remap16[packet] != i) + { + udf_debug("Found conflicting Sparing Data (%d vs %d)\n", + sdata->s_spar_remap.s_spar_remap16[packet], i); + } + } + else if (sdata->s_spar_indexsize == 32) + { + if (sdata->s_spar_remap.s_spar_remap32[packet] == 0xFFFFFFFF) + sdata->s_spar_remap.s_spar_remap32[packet] = i; + else if (sdata->s_spar_remap.s_spar_remap32[packet] != i) + { + udf_debug("Found conflicting Sparing Data (%d vs %d)\n", + sdata->s_spar_remap.s_spar_remap32[packet], i); + } + } + } + } + } + } + udf_release_data(bh); + } +} diff -Nru linux/fs/udf/super.c linux.new/fs/udf/super.c --- linux/fs/udf/super.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/super.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,1765 @@ +/* + * 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@hootie.lvld.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-2000 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "udf_sb.h" +#include "udf_i.h" + +#include +#include + +#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 struct super_block *udf_read_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 *, lb_addr *); +static int udf_load_logicalvol(struct super_block *, struct buffer_head *, lb_addr *); +static void udf_load_logicalvolint(struct super_block *, extent_ad); +static int udf_find_anchor(struct super_block *, int, int); +static int udf_find_fileset(struct super_block *, lb_addr *, lb_addr *); +static void udf_load_pvoldesc(struct super_block *, struct buffer_head *); +static void udf_load_fileset(struct super_block *, struct buffer_head *, 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 statfs *, int); + +/* UDF filesystem type */ +static struct file_system_type udf_fstype = { + "udf", /* name */ + FS_REQUIRES_DEV, /* fs_flags */ + udf_read_super, /* read_super */ + NULL /* next */ +}; + +/* Superblock operations */ +static struct super_operations udf_sb_ops = +{ + udf_read_inode, /* read_inode */ + udf_write_inode, /* write_inode */ + udf_put_inode, /* put_inode */ + udf_delete_inode, /* delete_inode */ + NULL, /* notify_change */ + udf_put_super, /* put_super */ + udf_write_super, /* write_super */ + udf_statfs, /* statfs */ + udf_remount_fs, /* remount_fs */ + NULL, /* clear_inode */ + NULL, /* umount_begin */ +}; + +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; +}; + +#if defined(MODULE) + +/* + * cleanup_module + * + * PURPOSE + * Unregister the UDF filesystem type. + * + * DESCRIPTION + * Clean-up before the module is unloaded. + * This routine only applies when compiled as a module. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int +cleanup_module(void) +{ + printk(KERN_NOTICE "udf: unregistering filesystem\n"); + return unregister_filesystem(&udf_fstype); +} + +/* + * init_module / init_udf_fs + * + * PURPOSE + * Register the UDF filesystem type. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int init_module(void) +#else /* if !defined(MODULE) */ +__initfunc(int init_udf_fs(void)) +#endif +{ + printk(KERN_NOTICE "udf: registering filesystem\n"); + { + struct super_block sb; + int size; + + size = sizeof(struct super_block) + + (long)&sb.u - (long)&sb; + if ( size < sizeof(struct udf_sb_info) ) + { + printk(KERN_ERR "udf: Danger! Kernel was compiled without enough room for udf_sb_info\n"); + printk(KERN_ERR "udf: Kernel has room for %u bytes, udf needs %lu\n", + size, (unsigned long)sizeof(struct udf_sb_info)); + return 0; + } + } + return register_filesystem(&udf_fstype); +} + +/* + * 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) + * strict Set strict conformance (unused) + * + * 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 + * 0 Mount options parsed okay. + * -1 Error parsing mount options. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ + +static int +udf_parse_options(char *options, struct udf_options *uopt) +{ + char *opt, *val; + + uopt->novrs = 0; + uopt->blocksize = 512; + uopt->partition = 0xFFFF; + uopt->session = 0xFFFFFFFF; + uopt->lastblock = 0xFFFFFFFF; + uopt->anchor = 0xFFFFFFFF; + uopt->volume = 0xFFFFFFFF; + uopt->rootdir = 0xFFFFFFFF; + uopt->fileset = 0xFFFFFFFF; + + if (!options) + return 1; + + for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) + { + /* Make "opt=val" into two strings */ + val = strchr(opt, '='); + if (val) + *(val++) = 0; + if (!strcmp(opt, "novrs") && !val) + uopt->novrs = 1; + else if (!strcmp(opt, "bs") && val) + uopt->blocksize = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "unhide") && !val) + uopt->flags |= (1 << UDF_FLAG_UNHIDE); + else if (!strcmp(opt, "undelete") && !val) + uopt->flags |= (1 << UDF_FLAG_UNDELETE); + else if (!strcmp(opt, "noadinicb") && !val) + uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); + else if (!strcmp(opt, "adinicb") && !val) + uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); + else if (!strcmp(opt, "shortad") && !val) + uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); + else if (!strcmp(opt, "longad") && !val) + uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); + else if (!strcmp(opt, "gid") && val) + uopt->gid = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "umask") && val) + uopt->umask = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "strict") && !val) + uopt->flags |= (1 << UDF_FLAG_STRICT); + else if (!strcmp(opt, "uid") && val) + uopt->uid = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "session") && val) + uopt->session = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "lastblock") && val) + uopt->lastblock = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "anchor") && val) + uopt->anchor = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "volume") && val) + uopt->volume = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "partition") && val) + uopt->partition = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "fileset") && val) + uopt->fileset = simple_strtoul(val, NULL, 0); + else if (!strcmp(opt, "rootdir") && val) + uopt->rootdir = simple_strtoul(val, NULL, 0); + else if (val) + { + printk(KERN_ERR "udf: bad mount option \"%s=%s\"\n", + opt, val); + return 0; + } + else + { + printk(KERN_ERR "udf: bad mount option \"%s\"\n", + opt); + return 0; + } + } + return 1; +} + +void +udf_write_super(struct super_block *sb) +{ + if (!(sb->s_flags & MS_RDONLY)) + udf_open_lvid(sb); + sb->s_dirt = 0; +} + +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 CONFIG_UDF_RW != 1 + *flags |= MS_RDONLY; +#endif + + 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. + * 0 Blocksize is valid. + * 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) +{ + /* Use specified block size if specified */ + if (!(sb->s_blocksize = get_hardblocksize(sb->s_dev))) + sb->s_blocksize = 2048; + if (bsize > sb->s_blocksize) + sb->s_blocksize = bsize; + + /* Block size must be an even multiple of 512 */ + switch (sb->s_blocksize) { + case 512: sb->s_blocksize_bits = 9; break; + case 1024: sb->s_blocksize_bits = 10; break; + case 2048: sb->s_blocksize_bits = 11; break; + case 4096: sb->s_blocksize_bits = 12; break; + case 8192: sb->s_blocksize_bits = 13; break; + default: + { + udf_debug("Bad block size (%ld)\n", sb->s_blocksize); + printk(KERN_ERR "udf: bad block size (%ld)\n", sb->s_blocksize); + return 0; + } + } + + /* Set the block size */ + set_blocksize(sb->s_dev, sb->s_blocksize); + return sb->s_blocksize; +} + +static int +udf_vrs(struct super_block *sb, int silent) +{ + struct VolStructDesc *vsd = NULL; + int sector = 32768; + 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 sector; + + 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 += 2048) + { + /* Read a block */ + bh = udf_tread(sb, sector >> sb->s_blocksize_bits, sb->s_blocksize); + 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, STD_ID_CD001, 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, STD_ID_BEA01, STD_ID_LEN)) + { + } + else if (!strncmp(vsd->stdIdent, STD_ID_TEA01, STD_ID_LEN)) + { + udf_release_data(bh); + break; + } + else if (!strncmp(vsd->stdIdent, STD_ID_NSR02, STD_ID_LEN)) + { + nsr02 = sector; + } + else if (!strncmp(vsd->stdIdent, STD_ID_NSR03, 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 + * 1 if not found, 0 if ok + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +static int +udf_find_anchor(struct super_block *sb, int useranchor, int lastblock) +{ + int varlastblock = udf_variable_to_fixed(lastblock); + int last[] = { lastblock, lastblock - 2, + lastblock - 150, lastblock - 152, + varlastblock, varlastblock - 2, + varlastblock - 150, varlastblock - 152 }; + struct buffer_head *bh = NULL; + Uint16 ident; + Uint32 location; + int i; + + UDF_SB_ANCHOR(sb)[0] = 0; + UDF_SB_ANCHOR(sb)[1] = 0; + UDF_SB_ANCHOR(sb)[2] = 0; + UDF_SB_ANCHOR(sb)[3] = 256 + UDF_SB_SESSION(sb); + + 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 && is_dev, last[i], sb->s_blocksize))) + { + 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 == TID_ANCHOR_VOL_DESC_PTR) + { + if (location == last[i] - UDF_SB_SESSION(sb)) + { + lastblock = UDF_SB_ANCHOR(sb)[0] = last[i]; + UDF_SB_ANCHOR(sb)[1] = last[i] - 256; + } + 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_ANCHOR(sb)[1] = lastblock - 256; + } + else + udf_debug("Anchor found at block %d, location mismatch %d.\n", + last[i], location); + } + else if (ident == TID_FILE_ENTRY || ident == TID_EXTENDED_FILE_ENTRY) + { + lastblock = last[i]; + UDF_SB_ANCHOR(sb)[2] = 512 + UDF_SB_SESSION(sb); + } + else + { + if (!(bh = bread(sb->s_dev, last[i] - 256, sb->s_blocksize))) + { + 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 == TID_ANCHOR_VOL_DESC_PTR && + location == last[i] - 256 - UDF_SB_SESSION(sb)) + { + lastblock = last[i]; + UDF_SB_ANCHOR(sb)[1] = last[i] - 256; + } + else + { + if (!(bh = bread(sb->s_dev, last[i] - 312 - UDF_SB_SESSION(sb), + sb->s_blocksize))) + { + 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 == TID_ANCHOR_VOL_DESC_PTR && + 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 = bread(sb->s_dev, 312 + UDF_SB_SESSION(sb), sb->s_blocksize))) + { + ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); + location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); + udf_release_data(bh); + + if (ident == TID_ANCHOR_VOL_DESC_PTR && location == 256) + UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); + } + } + + for (i=0; ilogicalBlockNum != 0xFFFFFFFF || + fileset->partitionReferenceNum != 0xFFFF) + { + bh = udf_read_ptagged(sb, *fileset, 0, &ident); + + if (!bh) + return 1; + else if (ident != TID_FILE_SET_DESC) + { + udf_release_data(bh); + return 1; + } + + } + + if (!bh) /* Search backwards through the partitions */ + { + 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 TID_SPACE_BITMAP_DESC: + { + 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 TID_FILE_SET_DESC: + { + *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)) ) + { + 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) = recording; + } + + 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, 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; ipartitionNumber)); + 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) + UDF_SB_SESSION(sb); + + if (UDF_SB_PARTTYPE(sb,i) == UDF_SPARABLE_MAP15) + udf_fill_spartable(sb, &UDF_SB_TYPESPAR(sb,i), UDF_SB_PARTLEN(sb,i)); + + if (!strcmp(p->partitionContents.ident, PARTITION_CONTENTS_NSR02) || + !strcmp(p->partitionContents.ident, PARTITION_CONTENTS_NSR03)) + { + struct PartitionHeaderDesc *phd; + + phd = (struct PartitionHeaderDesc *)(p->partitionContentsUse); + if (phd->unallocatedSpaceTable.extLength) + { + lb_addr loc = { i, phd->unallocatedSpaceTable.extPosition }; + + 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("unallocatedSpaceTable (part %d) @ %ld\n", + i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino); + } + if (phd->unallocatedSpaceBitmap.extLength) + { + UDF_SB_ALLOC_BITMAP(sb, i, s_uspace); + UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength = + le32_to_cpu(phd->unallocatedSpaceBitmap.extLength); + UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition = + le32_to_cpu(phd->unallocatedSpaceBitmap.extPosition); + UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP; + udf_debug("unallocatedSpaceBitmap (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) + { + lb_addr loc = { i, phd->freedSpaceTable.extPosition }; + + 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); + 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, lb_addr *fileset) +{ + struct LogicalVolDesc *lvd; + int i, j, offset; + Uint8 type; + + lvd = (struct LogicalVolDesc *)bh->b_data; + + UDF_SB_NUMPARTS(sb) = le32_to_cpu(lvd->numPartitionMaps); + UDF_SB_ALLOC_PARTMAPS(sb, UDF_SB_NUMPARTS(sb)); + + for (i=0,offset=0; + imapTableLength); + 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(((Uint16 *)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(((Uint16 *)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))) + { + int plen; + + struct SparablePartitionMap *spm = (struct SparablePartitionMap *)&(lvd->partitionMaps[offset]); + UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; + plen = le16_to_cpu(spm->packetLength); + UDF_SB_TYPESPAR(sb,i).s_spar_pshift = 0; + while (plen >>= 1) + UDF_SB_TYPESPAR(sb,i).s_spar_pshift ++; + for (j=0; jnumSparingTables; j++) + UDF_SB_TYPESPAR(sb,i).s_spar_loc[j] = le32_to_cpu(spm->locSparingTable[j]); + 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, extent_ad loc) +{ + struct buffer_head *bh = NULL; + Uint16 ident; + + while ((bh = udf_read_tagged(sb, loc.extLocation, loc.extLocation, &ident)) && + ident == TID_LOGICAL_VOL_INTEGRITY_DESC && loc.extLength > 0) + { + 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, lb_addr *fileset) +{ + struct buffer_head *bh = NULL; + struct udf_vds_record vds[VDS_POS_LENGTH]; + struct GenericDesc *gd; + int done=0; + int i,j; + Uint32 vdsn; + Uint16 ident; + + 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 GenericDesc *)bh->b_data; + vdsn = le32_to_cpu(gd->volDescSeqNum); + switch (ident) + { + case TID_PRIMARY_VOL_DESC: /* 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 TID_VOL_DESC_PTR: /* 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; + } + break; + case TID_IMP_USE_VOL_DESC: /* 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 TID_PARTITION_DESC: /* ISO 13346 3/10.5 */ + if (!vds[VDS_POS_PARTITION_DESC].block) + vds[VDS_POS_PARTITION_DESC].block = block; + break; + case TID_LOGICAL_VOL_DESC: /* 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 TID_UNALLOC_SPACE_DESC: /* 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 TID_TERMINATING_DESC: /* ISO 13346 3/10.9 */ + vds[VDS_POS_TERMINATING_DESC].block = block; + done = 1; + break; + } + udf_release_data(bh); + } + for (i=0; ib_data; + if (ident == TID_PARTITION_DESC) + 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"); + return 0; + } + else + return !block; +} + +static int +udf_load_partition(struct super_block *sb, lb_addr *fileset) +{ + struct AnchorVolDescPtr *anchor; + Uint16 ident; + struct buffer_head *bh; + long main_s, main_e, reserve_s, reserve_e; + int i, j; + + if (!sb) + return 1; + + for (i=0; ib_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; ii_size - 36) >> 2; + } + else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) + { + struct buffer_head *bh = NULL; + Uint32 pos; + + pos = udf_bmap(UDF_SB_VAT(sb), 0); + bh = bread(sb->s_dev, pos, sb->s_blocksize); + 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; + 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, CURRENT_UTIME)) + UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); + UDF_SB_LVID(sb)->integrityType = 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 *)&(UDF_SB_LVID(sb)->descTag))[i]; + + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } +} + +static void udf_close_lvid(struct super_block *sb) +{ + if (UDF_SB_LVIDBH(sb) && + UDF_SB_LVID(sb)->integrityType == INTEGRITY_TYPE_OPEN) + { + int i; + 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, CURRENT_UTIME)) + 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 = 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 *)&(UDF_SB_LVID(sb)->descTag))[i]; + + mark_buffer_dirty(UDF_SB_LVIDBH(sb), 1); + } +} + +/* + * 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 struct super_block * +udf_read_super(struct super_block *sb, void *options, int silent) +{ + struct inode *inode=NULL; + struct udf_options uopt; + lb_addr rootdir, fileset; + + uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB); + uopt.uid = 0; + uopt.gid = 0; + uopt.umask = 0; + + /* Lock the module in memory (if applicable) */ + MOD_INC_USE_COUNT; + + lock_super(sb); + UDF_SB_ALLOC(sb); /* kmalloc, if needed */ + memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info)); + +#if CONFIG_UDF_RW != 1 + sb->s_flags |= MS_RDONLY; +#endif + + if (!udf_parse_options((char *)options, &uopt)) + goto error_out; + + 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; + + /* 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)); + + if ( uopt.lastblock == 0xFFFFFFFF ) + UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); + else + UDF_SB_LASTBLOCK(sb) = uopt.lastblock; + + UDF_SB_LASTBLOCK(sb) = udf_find_anchor(sb, uopt.anchor, UDF_SB_LASTBLOCK(sb)); + + udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb)); + + if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */ + { + printk("UDF-fs: No VRS found\n"); + goto error_out; + } + + /* 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; + + if (udf_load_partition(sb, &fileset)) + { + printk("UDF-fs: No partition found (1)\n"); + goto error_out; + } + + if ( UDF_SB_LVIDBH(sb) ) + { + Uint16 minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev); + Uint16 minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); + /* Uint16 maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */ + + if (minUDFReadRev > UDF_MAX_READ_VERSION) + { + printk("UDF-fs: minUDFReadRev=%x (max is %x)\n", + UDF_SB_LVIDIU(sb)->minUDFReadRev, UDF_MAX_READ_VERSION); + goto error_out; + } + else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) + { + sb->s_flags |= MS_RDONLY; + } + + 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) + { + timestamp ts; + udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb), 0); + 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); + unlock_super(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, NULL); + if (!sb->s_root) + { + printk("UDF-fs: Couldn't allocate root dentry\n"); + iput(inode); + goto error_out; + } + + return sb; + +error_out: + sb->s_dev = NODEV; + 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) + kfree(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) + kfree(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap); + } + if (!(sb->s_flags & MS_RDONLY)) + udf_close_lvid(sb); + udf_release_data(UDF_SB_LVIDBH(sb)); + UDF_SB_FREE(sb); + unlock_super(sb); + MOD_DEC_USE_COUNT; + return NULL; +} + +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", + bdevname(sb->s_dev), 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", + bdevname(sb->s_dev), 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) + { + for (i=0; is_flags & MS_RDONLY)) + udf_close_lvid(sb); + udf_release_data(UDF_SB_LVIDBH(sb)); + UDF_SB_FREE(sb); + + MOD_DEC_USE_COUNT; +} + +/* + * 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 statfs *buf, int bufsize) +{ + int size; + struct statfs tmp; + int rc; + + size = (bufsize < sizeof(tmp)) ? bufsize: sizeof(tmp); + + memset(&tmp, 0, sizeof(tmp)); + tmp.f_type = UDF_SUPER_MAGIC; + tmp.f_bsize = sb->s_blocksize; + tmp.f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); + tmp.f_bfree = udf_count_free(sb); + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = (UDF_SB_LVIDBH(sb) ? + (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + + le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + tmp.f_bfree; + tmp.f_ffree = tmp.f_bfree; + /* __kernel_fsid_t f_fsid */ + tmp.f_namelen = UDF_NAME_LEN; + + rc= copy_to_user(buf, &tmp, size) ? -EFAULT: 0; + return rc; +} + +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; + lb_addr loc; + Uint32 bytes; + Uint8 value; + Uint8 *ptr; + Uint16 ident; + struct SpaceBitmapDesc *bm; + + 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"); + return 0; + } + else if (ident != TID_SPACE_BITMAP_DESC) + { + udf_release_data(bh); + printk(KERN_ERR "udf: udf_count_free failed\n"); + return 0; + } + + bm = (struct SpaceBitmapDesc *)bh->b_data; + bytes = bm->numOfBytes; + index = sizeof(struct SpaceBitmapDesc); /* offset in first block only */ + ptr = (Uint8 *)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, sb->s_blocksize); + if (!bh) + { + udf_debug("read failed\n"); + return accum; + } + index = 0; + ptr = (Uint8 *)bh->b_data; + } + } + udf_release_data(bh); + return accum; +} + +static unsigned int +udf_count_free_table(struct super_block *sb, struct inode * table) +{ + unsigned int accum = 0; + Uint32 extoffset, elen; + lb_addr bloc, eloc; + char etype; + struct buffer_head *bh = NULL; + + bloc = UDF_I_LOCATION(table); + extoffset = sizeof(struct UnallocatedSpaceEntry); + + 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); + return accum; +} + +static unsigned int +udf_count_free(struct super_block *sb) +{ + unsigned int accum = 0; + + 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); + } + if (accum) + return accum; + + 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; + } + } + return accum; +} diff -Nru linux/fs/udf/symlink.c linux.new/fs/udf/symlink.c --- linux/fs/udf/symlink.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/symlink.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,181 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 04/16/99 blf Created. + * + */ + +#include "udfdecl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "udf_i.h" + +static void udf_pc_to_char(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: + memcpy(p, pc->componentIdent, pc->lengthComponentIdent); + p += pc->lengthComponentIdent; + *p++ = '/'; + } + elen += sizeof(struct PathComponent) + pc->lengthComponentIdent; + } + if (p > to+1) + p[-1] = '\0'; + else + p[0] = '\0'; +} + +static struct dentry * udf_follow_link(struct dentry * dentry, + struct dentry * base, unsigned int follow) +{ + struct inode *inode = dentry->d_inode; + struct buffer_head *bh = NULL; + char *symlink, *tmpbuf; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data + udf_file_entry_alloc_offset(inode); + } + else + { + bh = bread(inode->i_dev, udf_bmap(inode, 0), inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data; + } + if ((tmpbuf = (char *)kmalloc(inode->i_size, GFP_KERNEL))) + { + udf_pc_to_char(symlink, inode->i_size, tmpbuf); + base = lookup_dentry(tmpbuf, base, follow); + kfree(tmpbuf); + return base; + } + else + return ERR_PTR(-ENOMEM); +} + +static int udf_readlink(struct dentry * dentry, char * buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct buffer_head *bh = NULL; + char *symlink, *tmpbuf; + int len; + + if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB) + { + bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data + udf_file_entry_alloc_offset(inode); + } + else + { + bh = bread(inode->i_dev, udf_bmap(inode, 0), inode->i_sb->s_blocksize); + + if (!bh) + return 0; + + symlink = bh->b_data; + } + + if ((tmpbuf = (char *)kmalloc(inode->i_size, GFP_KERNEL))) + { + udf_pc_to_char(symlink, inode->i_size, tmpbuf); + if ((len = strlen(tmpbuf)) > buflen) + len = buflen; + if (copy_to_user(buffer, tmpbuf, len)) + len = -EFAULT; + kfree(tmpbuf); + } + else + len = -ENOMEM; + + UPDATE_ATIME(inode); + if (bh) + udf_release_data(bh); + return len; +} + +/* + * symlinks can't do much... + */ +struct inode_operations udf_symlink_inode_operations = { + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + udf_readlink, /* readlink */ + udf_follow_link,/* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; diff -Nru linux/fs/udf/truncate.c linux.new/fs/udf/truncate.c --- linux/fs/udf/truncate.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/truncate.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,288 @@ +/* + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999 Stelias Computing Inc + * + * HISTORY + * + * 02/24/99 blf Created. + * + */ + +#include "udfdecl.h" +#include +#include +#include + +#include "udf_i.h" +#include "udf_sb.h" + +static void extent_trunc(struct inode * inode, lb_addr bloc, int extoffset, + lb_addr eloc, Uint8 etype, Uint32 elen, struct buffer_head *bh, Uint32 nelen) +{ + lb_addr neloc = { 0, 0 }; + int blocks = inode->i_sb->s_blocksize / 512; + 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) + { + 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 == EXTENT_RECORDED_ALLOCATED) + { + inode->i_blocks -= (blocks * (last_block - first_block)); + mark_inode_dirty(inode); + } + if (etype != EXTENT_NOT_RECORDED_NOT_ALLOCATED) + udf_free_blocks(inode, eloc, first_block, last_block - first_block); + } + } +} + +void udf_trunc(struct inode * inode) +{ + lb_addr bloc, eloc, neloc = { 0, 0 }; + Uint32 extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc; + int 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) == ICB_FLAG_AD_SHORT) + adsize = sizeof(short_ad); + else if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_LONG) + adsize = sizeof(long_ad); + else + adsize = 0; + + etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh); + offset = (offset << inode->i_sb->s_blocksize_bits) | + (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 (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + 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 == EXTENT_NEXT_EXTENT_ALLOCDECS) + { + udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); + extoffset = 0; + if (lelen) + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode)); + else + memset(bh->b_data, 0x00, sizeof(struct AllocExtDesc)); + udf_free_blocks(inode, bloc, 0, lelen); + } + else + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + { + UDF_I_LENALLOC(inode) = lenalloc; + mark_inode_dirty(inode); + } + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(bh->b_data); + aed->lengthAllocDescs = cpu_to_le32(lenalloc); + udf_update_tag(bh->b_data, lenalloc + + sizeof(struct AllocExtDesc)); + mark_buffer_dirty(bh, 1); + } + } + + udf_release_data(bh); + bh = NULL; + + bloc = eloc; + 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 (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode)); + else + memset(bh->b_data, 0x00, sizeof(struct AllocExtDesc)); + udf_free_blocks(inode, bloc, 0, lelen); + } + else + { + if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr))) + { + UDF_I_LENALLOC(inode) = lenalloc; + mark_inode_dirty(inode); + } + else + { + struct AllocExtDesc *aed = (struct AllocExtDesc *)(bh->b_data); + aed->lengthAllocDescs = cpu_to_le32(lenalloc); + udf_update_tag(bh->b_data, lenalloc + + sizeof(struct AllocExtDesc)); + mark_buffer_dirty(bh, 1); + } + } + } + else if (inode->i_size) + { + if (offset) + { + extoffset -= adsize; + etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1); + + if (etype == EXTENT_NOT_RECORDED_NOT_ALLOCATED) + { + extoffset -= adsize; + elen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | (elen + offset); + udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0); + } + else if (etype == EXTENT_NOT_RECORDED_ALLOCATED) + { + lb_addr neloc = { 0, 0 }; + extoffset -= adsize; + nelen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | + ((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 = (EXTENT_RECORDED_ALLOCATED << 30) | + ((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(lb_addr)); + elen = (EXTENT_NOT_RECORDED_NOT_ALLOCATED << 30) | offset; + udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); + } + } + } + + udf_release_data(bh); +} + +void udf_truncate(struct inode * inode) +{ + int offset; + + 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; + + udf_trunc(inode); + + offset = (inode->i_size & (inode->i_sb->s_blocksize - 1)); + if (offset) + { + struct buffer_head *bh; + bh = bread(inode->i_dev, + udf_bmap(inode, inode->i_size >> inode->i_sb->s_blocksize_bits), + inode->i_sb->s_blocksize); + if (bh) + { + memset(bh->b_data + offset, 0x00, inode->i_sb->s_blocksize - offset); + mark_buffer_dirty(bh, 0); + udf_release_data(bh); + } + } + + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} + +void udf_truncate_adinicb(struct inode * inode) +{ + int offset; + struct buffer_head *bh; + 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; + + 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) == ICB_FLAG_AD_IN_ICB) + { + inode->i_size = UDF_I_LENALLOC(inode); + return; + } + else + return udf_truncate(inode); + } + else + UDF_I_LENALLOC(inode) = inode->i_size; + + offset = (inode->i_size & (inode->i_sb->s_blocksize - 1)) + + udf_file_entry_alloc_offset(inode); + + if ((bh = udf_tread(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0), + inode->i_sb->s_blocksize))) + { + memset(bh->b_data + offset, 0x00, inode->i_sb->s_blocksize - offset); + mark_buffer_dirty(bh, 0); + udf_release_data(bh); + } + + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +} diff -Nru linux/fs/udf/udf_i.h linux.new/fs/udf/udf_i.h --- linux/fs/udf/udf_i.h Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/udf_i.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,29 @@ +#ifndef __LINUX_UDF_I_H +#define __LINUX_UDF_I_H + +#ifndef CONFIG_UDF_FS_EXT + +#define UDF_I(X) (&((X)->u.udf_i)) + +#else +/* we're not compiled in, so we can't expect our stuff in */ +#define UDF_I(X) ( (struct udf_inode_info *) &((X)->u.pipe_i)) + /* god, this is slimy. stealing another filesystem's union area. */ + /* for the record, pipe_i is 9 ints long, we're using 9 */ +#endif + +#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_UNIQUE(X) ( UDF_I(X)->i_unique ) +#define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type ) +#define UDF_I_EXTENDED_FE(X)( UDF_I(X)->i_extended_fe ) +#define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat_4096 ) +#define UDF_I_NEW_INODE(X) ( UDF_I(X)->i_new_inode ) +#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_UATIME(X) ( UDF_I(X)->i_uatime ) +#define UDF_I_UMTIME(X) ( UDF_I(X)->i_umtime ) +#define UDF_I_UCTIME(X) ( UDF_I(X)->i_uctime ) + +#endif /* !defined(_LINUX_UDF_I_H) */ diff -Nru linux/fs/udf/udf_sb.h linux.new/fs/udf/udf_sb.h --- linux/fs/udf/udf_sb.h Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/udf_sb.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,125 @@ +#ifndef __LINUX_UDF_SB_H +#define __LINUX_UDF_SB_H + +/* Since UDF 1.50 is ISO 13346 based... */ +#define UDF_SUPER_MAGIC 0x15013346 + +#define UDF_MAX_READ_VERSION 0x0200 +#define UDF_MAX_WRITE_VERSION 0x0200 + +#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_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 + +/* following is set only when compiling outside kernel tree */ +#ifndef CONFIG_UDF_FS_EXT + +#define UDF_SB_ALLOC(X) +#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(X) (&((X)->u.udf_sb)) + +#else + /* else we kmalloc a page to hold our stuff */ +#define UDF_SB_ALLOC(X)\ + (UDF_SB(X) = kmalloc(sizeof(struct udf_sb_info), GFP_KERNEL)) +#define UDF_SB_FREE(X)\ +{\ + if (UDF_SB(X))\ + {\ + if (UDF_SB_PARTMAPS(X))\ + kfree(UDF_SB_PARTMAPS(X));\ + kfree(UDF_SB(X));\ + UDF_SB(X) = NULL;\ + }\ +} + +#define UDF_SB(X) ((struct udf_sb_info *) ((X)->u.generic_sbp)) +#endif + +#define UDF_SB_ALLOC_PARTMAPS(X,Y)\ +{\ + UDF_SB_NUMPARTS(X) = Y;\ + UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ + memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * 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));\ + UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(sizeof(struct udf_bitmap) +\ + sizeof(struct buffer_head *) * nr_groups,\ + GFP_KERNEL);\ + memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00,\ + sizeof(struct udf_bitmap) + sizeof(struct buffer_head *) * nr_groups);\ + 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;\ +} + + +#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[UDF_SB_LVID(X)->numOfPartitions * 2 * sizeof(Uint32)/sizeof(Uint8)]) ) + +#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 ) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,6) +#define UDF_SB_RENAME_LOCK(X) ( UDF_SB(X)->s_rename_lock ) +#define UDF_SB_RENAME_WAIT(X) ( UDF_SB(X)->s_rename_wait ) +#endif + +#endif /* __LINUX_UDF_SB_H */ diff -Nru linux/fs/udf/udfdecl.h linux.new/fs/udf/udfdecl.h --- linux/fs/udf/udfdecl.h Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/udfdecl.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,244 @@ +#ifndef __UDF_DECL_H +#define __UDF_DECL_H + +#include +#include +#include "udfend.h" + +#include + +#ifdef __KERNEL__ + +#include +#include +#include + +#ifndef LINUX_VERSION_CODE +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,70) +#error "The UDF Module Current Requires Kernel Version 2.1.70 or greater" +#endif + +#if !defined(CONFIG_UDF_FS) && !defined(CONFIG_UDF_FS_MODULE) +#define CONFIG_UDF_FS_MODULE +#include +#include +#endif + +#define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) ) +#define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) ) + +#define CURRENT_UTIME (xtime.tv_usec) + +#define udf_file_entry_alloc_offset(inode)\ + ((UDF_I_EXTENDED_FE(inode) ?\ + sizeof(struct ExtendedFileEntry) :\ + sizeof(struct FileEntry)) + UDF_I_LENEATTR(inode)) + +#define udf_ext0_offset(inode)\ + (UDF_I_ALLOCTYPE(inode) == ICB_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)) + +#else + +#include + +#endif /* __KERNEL__ */ + + + +#ifdef __KERNEL__ + +struct dentry; +struct inode; +struct task_struct; +struct buffer_head; +struct super_block; + +extern struct inode_operations udf_dir_inode_operations; +extern struct inode_operations udf_file_inode_operations; +extern struct inode_operations udf_file_inode_operations_adinicb; +extern struct inode_operations udf_symlink_inode_operations; + +struct udf_fileident_bh +{ + struct buffer_head *sbh; + struct buffer_head *ebh; + int soffset; + int eoffset; +}; + +#endif /* __KERNEL__ */ + +struct udf_directory_record +{ + Uint32 d_parent; + Uint32 d_inode; + Uint32 d_name[255]; +}; + + +struct udf_vds_record +{ + Uint32 block; + Uint32 volDescSeqNum; +}; + +struct ktm +{ + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_isdst; +}; + +struct ustr +{ + Uint8 u_cmpID; + Uint8 u_name[UDF_NAME_LEN]; + Uint8 u_len; + Uint8 padding; + unsigned long u_hash; +}; + +#ifdef __KERNEL__ + +/* 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 FileIdentDesc *, struct FileIdentDesc *, struct udf_fileident_bh *, Uint8 *, Uint8 *); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,7) +extern int udf_lookup(struct inode *, struct dentry *); +#else +extern struct dentry * udf_lookup(struct inode *, struct dentry *); +#endif +extern int udf_create(struct inode *, struct dentry *, int); +extern int udf_mknod(struct inode *, struct dentry *, int, int); +extern int udf_mkdir(struct inode *, struct dentry *, int); +extern int udf_rmdir(struct inode *, struct dentry *); +extern int udf_unlink(struct inode *, struct dentry *); +extern int udf_symlink(struct inode *, struct dentry *, const char *); +extern int udf_link(struct dentry *, struct inode *, struct dentry *); +extern int udf_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); + +/* file.c */ +extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long); + +/* inode.c */ +extern struct inode *udf_iget(struct super_block *, 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_getblk(struct inode *, long, int, int *); +extern struct buffer_head * udf_bread(struct inode *, int, int, int *); +extern void udf_read_inode(struct inode *); +extern void udf_put_inode(struct inode *); +extern void udf_delete_inode(struct inode *); +extern void udf_write_inode(struct inode *); +extern int inode_bmap(struct inode *, int, lb_addr *, Uint32 *, lb_addr *, Uint32 *, Uint32 *, struct buffer_head **); +extern int udf_bmap(struct inode *, int); +extern int udf_readpage_adinicb(struct file *, struct page *); +extern int udf_add_aext(struct inode *, lb_addr *, int *, lb_addr, Uint32, struct buffer_head **, int); +extern int udf_write_aext(struct inode *, lb_addr, int *, lb_addr, Uint32, struct buffer_head *, int); +extern int udf_insert_aext(struct inode *, lb_addr, int, lb_addr, Uint32, struct buffer_head *); +extern int udf_delete_aext(struct inode *, lb_addr, int, lb_addr, Uint32, struct buffer_head *); +extern int udf_next_aext(struct inode *, lb_addr *, int *, lb_addr *, Uint32 *, struct buffer_head **, int); +extern int udf_current_aext(struct inode *, lb_addr *, int *, lb_addr *, Uint32 *, struct buffer_head **, int); +extern void udf_discard_prealloc(struct inode *); + +/* misc.c */ +extern int udf_read_tagged_data(char *, int size, int fd, int block, int partref); +extern struct buffer_head *udf_tread(struct super_block *, int, int); +extern struct GenericAttrFormat *udf_add_extendedattr(struct inode *, Uint32, Uint32, Uint8, struct buffer_head **); +extern struct GenericAttrFormat *udf_get_extendedattr(struct inode *, Uint32, Uint8, struct buffer_head **); +extern struct buffer_head *udf_read_tagged(struct super_block *, Uint32, Uint32, Uint16 *); +extern struct buffer_head *udf_read_ptagged(struct super_block *, lb_addr, Uint32, Uint16 *); +extern struct buffer_head *udf_read_untagged(struct super_block *, Uint32, Uint32); +extern void udf_release_data(struct buffer_head *); + +/* lowlevel.c */ +extern unsigned int udf_get_last_session(struct super_block *); +extern unsigned int udf_get_last_block(struct super_block *); + +/* partition.c */ +extern Uint32 udf_get_pblock(struct super_block *, Uint32, Uint16, Uint32); +extern Uint32 udf_get_pblock_virt15(struct super_block *, Uint32, Uint16, Uint32); +extern Uint32 udf_get_pblock_virt20(struct super_block *, Uint32, Uint16, Uint32); +extern Uint32 udf_get_pblock_spar15(struct super_block *, Uint32, Uint16, Uint32); +extern void udf_fill_spartable(struct super_block *, struct udf_sparing_data *, int); + +/* unicode.c */ +extern int udf_get_filename(Uint8 *, Uint8 *, int); + +/* ialloc.c */ +extern void udf_free_inode(struct inode *); +extern struct inode * udf_new_inode (const struct inode *, int, int *); + +/* truncate.c */ +extern void udf_trunc(struct inode *); +extern void udf_truncate(struct inode *); +extern void udf_truncate_adinicb(struct inode *); + +/* balloc.c */ +extern inline void udf_free_blocks(const struct inode *, lb_addr, Uint32, Uint32); +extern inline int udf_prealloc_blocks(const struct inode *, Uint16, Uint32, Uint32); +extern inline int udf_new_block(const struct inode *, Uint16, Uint32, int *); + +/* fsync.c */ +extern int udf_sync_file(struct file *, struct dentry *); +extern int udf_sync_file_adinicb(struct file *, struct dentry *); + +/* directory.c */ +extern Uint8 * udf_filead_read(struct inode *, Uint8 *, Uint8, lb_addr, int *, int *, struct buffer_head **, int *); +extern struct FileIdentDesc * udf_fileident_read(struct inode *, int *, struct udf_fileident_bh *, struct FileIdentDesc *, lb_addr *, Uint32 *, lb_addr *, Uint32 *, Uint32 *, struct buffer_head **); + +#endif /* __KERNEL__ */ + +/* Miscellaneous UDF Prototypes */ + +/* unicode.c */ +extern int udf_ustr_to_dchars(Uint8 *, const struct ustr *, int); +extern int udf_ustr_to_char(Uint8 *, const struct ustr *, int); +extern int udf_ustr_to_dstring(dstring *, const struct ustr *, int); +extern int udf_dchars_to_ustr(struct ustr *, const Uint8 *, int); +extern int udf_char_to_ustr(struct ustr *, const Uint8 *, int); +extern int udf_dstring_to_ustr(struct ustr *, const dstring *, int); +extern int udf_translate_to_linux(Uint8 *, Uint8 *, int, Uint8 *, int); +extern int udf_build_ustr(struct ustr *, dstring *, int); +extern int udf_build_ustr_exact(struct ustr *, dstring *, int); +extern int udf_CS0toUTF8(struct ustr *, struct ustr *); +extern int udf_UTF8toCS0(dstring *, struct ustr *, int); + +/* crc.c */ +extern Uint16 udf_crc(Uint8 *, Uint32, Uint16); + +/* misc.c */ +extern uid_t udf_convert_uid(int); +extern gid_t udf_convert_gid(int); +extern Uint32 udf64_low32(Uint64); +extern Uint32 udf64_high32(Uint64); +extern void udf_update_tag(char *, int); +extern void udf_new_tag(char *, Uint16, Uint16, Uint16, Uint32, int); + +/* udftime.c */ +extern time_t *udf_stamp_to_time(time_t *, long *, timestamp); +extern timestamp *udf_time_to_stamp(timestamp *, time_t, long); +extern time_t udf_converttime (struct ktm *); + +/* directory.c */ +extern struct FileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset); +extern extent_ad * udf_get_fileextent(void * buffer, int bufsize, int * offset); +extern long_ad * udf_get_filelongad(void * buffer, int bufsize, int * offset, int); +extern short_ad * udf_get_fileshortad(void * buffer, int bufsize, int * offset, int); +extern Uint8 * udf_get_filead(struct FileEntry *, Uint8 *, int, int, int, int *); + +#endif /* __UDF_DECL_H */ diff -Nru linux/fs/udf/udfend.h linux.new/fs/udf/udfend.h --- linux/fs/udf/udfend.h Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/udfend.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,132 @@ +#ifndef __UDF_ENDIAN_H +#define __UDF_ENDIAN_H + +#ifndef __KERNEL__ + +#include + +#if __BYTE_ORDER == 0 + +#error "__BYTE_ORDER must be defined" + +#elif __BYTE_ORDER == __BIG_ENDIAN + +#define le16_to_cpu(x) \ + ((Uint16)((((Uint16)(x) & 0x00FFU) << 8) | \ + (((Uint16)(x) & 0xFF00U) >> 8))) + +#define le32_to_cpu(x) \ + ((Uint32)((((Uint32)(x) & 0x000000FFU) << 24) | \ + (((Uint32)(x) & 0x0000FF00U) << 8) | \ + (((Uint32)(x) & 0x00FF0000U) >> 8) | \ + (((Uint32)(x) & 0xFF000000U) >> 24))) + +#define le64_to_cpu(x) \ + ((Uint64)((((Uint64)(x) & 0x00000000000000FFULL) << 56) | \ + (((Uint64)(x) & 0x000000000000FF00ULL) << 40) | \ + (((Uint64)(x) & 0x0000000000FF0000ULL) << 24) | \ + (((Uint64)(x) & 0x00000000FF000000ULL) << 8) | \ + (((Uint64)(x) & 0x000000FF00000000ULL) >> 8) | \ + (((Uint64)(x) & 0x0000FF0000000000ULL) >> 24) | \ + (((Uint64)(x) & 0x00FF000000000000ULL) >> 40) | \ + (((Uint64)(x) & 0xFF00000000000000ULL) >> 56))) + +#define cpu_to_le16(x) (le16_to_cpu(x)) +#define cpu_to_le32(x) (le32_to_cpu(x)) +#define cpu_to_le64(x) (le64_to_cpu(x)) + +#else /* __BYTE_ORDER == __LITTLE_ENDIAN */ + +#define le16_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le64_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) + +#endif /* __BYTE_ORDER == 0 */ + +#include + +#else /* __KERNEL__ */ + +#include +#include + +#endif /* ! __KERNEL__ */ + +static inline lb_addr lelb_to_cpu(lb_addr in) +{ + 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(lb_addr in) +{ + lb_addr out; + out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum); + out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum); + return out; +} + +static inline timestamp lets_to_cpu(timestamp in) +{ + 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 long_ad lela_to_cpu(long_ad in) +{ + 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(long_ad in) +{ + long_ad out; + out.extLength = cpu_to_le32(in.extLength); + out.extLocation = cpu_to_lelb(in.extLocation); + return out; +} + +static inline extent_ad leea_to_cpu(extent_ad in) +{ + 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(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 -Nru linux/fs/udf/udftime.c linux.new/fs/udf/udftime.c --- linux/fs/udf/udftime.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/udftime.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,186 @@ +/* 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 + */ + +#if defined(__linux__) && defined(__KERNEL__) +#include +#include +#else +#include +#include +#include +#endif + +#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) + +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) +}; + +#ifdef __KERNEL__ +extern struct timezone sys_tz; +#endif + +#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, timestamp src) +{ + int yday; + Uint8 type = src.typeAndTimezone >> 12; + Sint16 offset; + + if (type == 1) + { + offset = src.typeAndTimezone << 4; + /* sign extent offset */ + offset = (offset >> 4); + } + 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; +} + + +timestamp * +udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec) +{ + long int days, rem, y; + const unsigned short int *ip; + Sint16 offset; +#ifndef __KERNEL__ + struct timeval tv; + struct timezone sys_tz; + + gettimeofday(&tv, &sys_tz); +#endif + offset = -sys_tz.tz_minuteswest; + + if (!dest) + return NULL; + + dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF); + + tv_sec += offset * 60; + days = tv_sec / SECS_PER_DAY; + rem = 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 = tv_usec / 10000; + dest->hundredsOfMicroseconds = (tv_usec - dest->centiseconds * 10000) / 100; + dest->microseconds = (tv_usec - dest->centiseconds * 10000 - + dest->hundredsOfMicroseconds * 100); + return dest; +} + +/* EOF */ diff -Nru linux/fs/udf/unicode.c linux.new/fs/udf/unicode.c --- linux/fs/udf/unicode.c Thu Jan 1 01:00:00 1970 +++ linux.new/fs/udf/unicode.c Fri Sep 7 11:32:16 2001 @@ -0,0 +1,452 @@ +/* + * 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@hootie.lvld.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. + */ + + +#ifdef __KERNEL__ +#include +#include /* for memset */ +#include +#else +#include +#endif + +#include "udfdecl.h" + +int udf_ustr_to_dchars(Uint8 *dest, const struct ustr *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (src->u_len > strlen) ) + return 0; + memcpy(dest+1, src->u_name, src->u_len); + dest[0] = src->u_cmpID; + return src->u_len + 1; +} + +int udf_ustr_to_char(Uint8 *dest, const struct ustr *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (src->u_len >= strlen) ) + return 0; + memcpy(dest, src->u_name, src->u_len); + return src->u_len; +} + +int udf_ustr_to_dstring(dstring *dest, const struct ustr *src, int dlength) +{ + if ( udf_ustr_to_dchars(dest, src, dlength-1) ) + { + dest[dlength-1] = src->u_len + 1; + return dlength; + } + else + return 0; +} + +int udf_dchars_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN) ) + return 0; + memset(dest, 0, sizeof(struct ustr)); + memcpy(dest->u_name, src+1, strlen-1); + dest->u_cmpID = src[0]; + dest->u_len = strlen-1; + return strlen-1; +} + +int udf_char_to_ustr(struct ustr *dest, const Uint8 *src, int strlen) +{ + if ( (!dest) || (!src) || (!strlen) || (strlen >= UDF_NAME_LEN) ) + return 0; + memset(dest, 0, sizeof(struct ustr)); + memcpy(dest->u_name, src, strlen); + dest->u_cmpID = 0x08; + dest->u_len = strlen; + return strlen; +} + + +int udf_dstring_to_ustr(struct ustr *dest, const dstring *src, int dlength) +{ + if ( dlength && udf_dchars_to_ustr(dest, src, src[dlength-1]) ) + return dlength; + else + return 0; +} + +/* + * 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 + */ +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_udf8 + * + * 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 + * 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 *ocu; + Uint32 c; + Uint8 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)) + { +#ifdef __KERNEL__ + printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); +#endif + 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)c; + else if (c < 0x800U) + { + utf_o->u_name[utf_o->u_len++] = (Uint8)(0xc0 | (c >> 6)); + utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | (c & 0x3f)); + } + else + { + utf_o->u_name[utf_o->u_len++] = (Uint8)(0xe0 | (c >> 12)); + utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | ((c >> 6) & 0x3f)); + utf_o->u_name[utf_o->u_len++] = (Uint8)(0x80 | (c & 0x3f)); + } + } + utf_o->u_cmpID=8; + utf_o->u_hash=0L; + utf_o->padding=0; + + 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 + * Zero on success. + * + * HISTORY + * November 12, 1997 - Andrew E. Mileski + * Written, tested, and released. + */ +int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length) +{ + unsigned c, i, max_val, utf_char; + int utf_cnt; + int u_len = 0; + + memset(ocu, 0, sizeof(dstring) * length); + ocu[0] = 8; + max_val = 0xffU; + +try_again: + utf_char = 0U; + utf_cnt = 0U; + for (i = 0U; i < utf->u_len; i++) + { + c = (Uint8)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)0x10U; + goto try_again; + } + goto error_out; + } + + if (max_val == 0xffffU) + { + ocu[++u_len] = (Uint8)(utf_char >> 8); + } + ocu[++u_len] = (Uint8)(utf_char & 0xffU); + } + + + if (utf_cnt) + { +error_out: +#ifdef __KERNEL__ + printk(KERN_ERR "udf: bad UTF-8 character\n"); +#endif + return 0; + } + + ocu[length - 1] = (Uint8)u_len + 1; + return u_len + 1; +} + +#ifdef __KERNEL__ +int udf_get_filename(Uint8 *sname, Uint8 *dname, int flen) +{ + struct ustr filename, unifilename; + int len; + + if (udf_build_ustr_exact(&unifilename, sname, flen)) + { + return 0; + } + + if (!udf_CS0toUTF8(&filename, &unifilename) ) + { + udf_debug("Failed in udf_get_filename: sname = %s\n", sname); + 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; +} +#endif + +#define ILLEGAL_CHAR_MARK '_' +#define EXT_MARK '.' +#define CRC_MARK '#' +#define EXT_SIZE 5 + +int udf_translate_to_linux(Uint8 *newName, Uint8 *udfName, int udfLen, Uint8 *fidName, int fidNameLen) +{ + int index, newIndex = 0, needsCRC = 0; + int extIndex = 0, newExtIndex = 0, hasExt = 0; + unsigned short valueCRC; + Uint8 curr; + const Uint8 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 ext[EXT_SIZE]; + int localExtIndex = 0; + + if (hasExt) + { + int maxFilenameLen; + for(index = 0; index 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; +} diff -Nru linux/include/linux/fs.h linux.new/include/linux/fs.h --- linux/include/linux/fs.h Fri Sep 7 11:17:46 2001 +++ linux.new/include/linux/fs.h Fri Sep 7 11:37:56 2001 @@ -286,6 +286,7 @@ #include #include #include +#include #include #include #include @@ -403,6 +404,7 @@ struct hfs_inode_info hfs_i; struct adfs_inode_info adfs_i; struct qnx4_inode_info qnx4_i; + struct udf_inode_info udf_i; struct reiserfs_inode_info reiserfs_i; struct usbdev_inode_info usbdev_i; struct socket socket_i; @@ -530,6 +532,7 @@ #include #include #include +#include #include #include #include @@ -576,7 +579,8 @@ struct hfs_sb_info hfs_sb; struct adfs_sb_info adfs_sb; struct qnx4_sb_info qnx4_sb; - struct reiserfs_sb_info reiserfs_sb; + struct udf_sb_info udf_sb; + struct reiserfs_sb_info reiserfs_sb; struct usbdev_sb_info usbdevfs_sb; struct beos_sb_info beos_sb; void *generic_sbp; diff -Nru linux/include/linux/udf_167.h linux.new/include/linux/udf_167.h --- linux/include/linux/udf_167.h Thu Jan 1 01:00:00 1970 +++ linux.new/include/linux/udf_167.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,786 @@ +#if !defined(_LINUX_UDF_167_H) +#define _LINUX_UDF_167_H +/* + * udf_167.h + * + * DESCRIPTION + * Definitions from the ECMA 167 standard. + * http://www.ecma.ch/ + * + * These abbreviations are used to keep the symbols short: + * Alloc Allocation + * App Application + * Attr Attribute + * Char Characters + * Desc Descriptor + * Descs Descriptors + * Ext Extent + * Ident Identifier + * Imp Implementation + * Lvl Level + * Max Maximum + * Num Number + * Ptr Pointer + * Seq Sequence + * Std Standard + * Struct Structure + * Vol Volume + * The symbols are otherwise identical to the standard, and the + * sections of the standard to refer to are indicated. + * + * 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@hootie.lvld.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. + * + * HISTORY + * July 12, 1997 - Andrew E. Mileski + * Adapted from the ECMA-167 standard. + * + * 10/2/98 dgb Adaptation + * 10/4/98 Changes by HJA Sandkuyl + * 10/7/98 Changed FILE_EXISTENCE to FILE_HIDDEN, per UDF 2.0 spec + * 11/26/98 Modifed some entries for UDF 1.5/2.0 + * 11/26/98 bf Fixed typos, non-linux types, more structures + * 12/5/98 dgb Adjusted structure and content of include files. + */ + +#ifdef __KERNEL__ +#include +#define Uint8 __u8 +#define Sint8 __s8 +#define Uint16 __u16 +#define Sint16 __s16 +#define Uint32 __u32 +#define Sint32 __s32 +#define Uint64 __u64 +#define Sint64 __s64 +typedef Uint8 dstring; +#else +#define Uint8 unsigned char +#define Sint8 char +#define Uint16 unsigned short +#define Sint16 short +#define Uint32 unsigned int +#define Sint32 int +#define Uint64 unsigned long long +#define Sint64 long long +typedef Uint8 dstring; +#endif + +/* make sure all structures are packed! */ +#pragma pack(1) + +/* CS0 Charspec (ECMA 167 1/7.2.1) */ +typedef struct { + Uint8 charSetType; + Uint8 charSetInfo[63]; +} charspec; + +/* Timestamp (ECMA 167 1/7.3) */ +typedef struct { + Uint16 typeAndTimezone; + Uint16 year; + Uint8 month; + Uint8 day; + Uint8 hour; + Uint8 minute; + Uint8 second; + Uint8 centiseconds; + Uint8 hundredsOfMicroseconds; + Uint8 microseconds; +} timestamp; + +/* Timestamp types (ECMA 167 1/7.3.1) */ +#define TIMESTAMP_TYPE_CUT 0x0000U +#define TIMESTAMP_TYPE_LOCAL 0x0001U +#define TIMESTAMP_TYPE_AGREEMENT 0x0002U + +/* Entity Identifier (ECMA 167 1/7.4) */ +typedef struct { + Uint8 flags; + Uint8 ident[23]; + Uint8 identSuffix[8]; +} EntityID; +#define regid EntityID + +/* Entity identifier flags (ECMA 167 1/7.4.1) */ +#define ENTITYID_FLAGS_DIRTY 0x01U +#define ENTITYID_FLAGS_PROTECTED 0x02U + +/* Volume Structure Descriptor (ECMA 167 2/9.1) */ +#define STD_ID_LEN 5 +struct VolStructDesc { + Uint8 structType; + Uint8 stdIdent[STD_ID_LEN]; + Uint8 structVersion; + Uint8 structData[2041]; +}; + +/* Std structure identifiers (ECMA 167 2/9.1.2) */ +#define STD_ID_BEA01 "BEA01" +#define STD_ID_BOOT2 "BOOT2" +#define STD_ID_CD001 "CD001" +#define STD_ID_CDW02 "CDW02" +#define STD_ID_NSR02 "NSR02" +#define STD_ID_NSR03 "NSR03" +#define STD_ID_TEA01 "TEA01" + +/* Beginning Extended Area Descriptor (ECMA 167 2/9.2) */ +struct BeginningExtendedAreaDesc { + Uint8 structType; + Uint8 stdIdent[STD_ID_LEN]; + Uint8 structVersion; + Uint8 structData[2041]; +}; + +/* Terminating Extended Area Descriptor (ECMA 167 2/9.3) */ +struct TerminatingExtendedAreaDesc { + Uint8 structType; + Uint8 stdIdent[STD_ID_LEN]; + Uint8 structVersion; + Uint8 structData[2041]; +}; + +/* Boot Descriptor (ECMA 167 2/9.4) */ +struct BootDesc { + Uint8 structType; + Uint8 stdIdent[STD_ID_LEN]; + Uint8 structVersion; + Uint8 reserved1; + EntityID architectureType; + EntityID bootIdent; + Uint32 bootExtLocation; + Uint32 bootExtLength; + Uint64 loadAddress; + Uint64 startAddress; + timestamp descCreationDateAndTime; + Uint16 flags; + Uint8 reserved2[32]; + Uint8 bootUse[1906]; +}; + +/* Boot flags (ECMA 167 2/9.4.12) */ +#define BOOT_FLAGS_ERASE 1 + +/* Extent Descriptor (ECMA 167 3/7.1) */ +typedef struct { + Uint32 extLength; + Uint32 extLocation; +} extent_ad; + +/* Descriptor Tag (ECMA 167 3/7.2) */ +typedef struct { + Uint16 tagIdent; + Uint16 descVersion; + Uint8 tagChecksum; + Uint8 reserved; + Uint16 tagSerialNum; + Uint16 descCRC; + Uint16 descCRCLength; + Uint32 tagLocation; +} tag; + +/* Tag Identifiers (ECMA 167 3/7.2.1) */ +#define TID_UNUSED_DESC 0x0000U +#define TID_PRIMARY_VOL_DESC 0x0001U +#define TID_ANCHOR_VOL_DESC_PTR 0x0002U +#define TID_VOL_DESC_PTR 0x0003U +#define TID_IMP_USE_VOL_DESC 0x0004U +#define TID_PARTITION_DESC 0x0005U +#define TID_LOGICAL_VOL_DESC 0x0006U +#define TID_UNALLOC_SPACE_DESC 0x0007U +#define TID_TERMINATING_DESC 0x0008U +#define TID_LOGICAL_VOL_INTEGRITY_DESC 0x0009U + +/* Tag Identifiers (ECMA 167 4/7.2.1) */ +#define TID_FILE_SET_DESC 0x0100U +#define TID_FILE_IDENT_DESC 0x0101U +#define TID_ALLOC_EXTENT_DESC 0x0102U +#define TID_INDIRECT_ENTRY 0x0103U +#define TID_TERMINAL_ENTRY 0x0104U +#define TID_FILE_ENTRY 0x0105U +#define TID_EXTENDED_ATTRE_HEADER_DESC 0x0106U +#define TID_UNALLOCATED_SPACE_ENTRY 0x0107U +#define TID_SPACE_BITMAP_DESC 0x0108U +#define TID_PARTITION_INTEGRITY_ENTRY 0x0109U +#define TID_EXTENDED_FILE_ENTRY 0x010AU + +/* NSR Descriptor (ECMA 167 3/9.1) */ +struct NSRDesc { + Uint8 structType; + Uint8 stdIdent[STD_ID_LEN]; + Uint8 structVersion; + Uint8 reserved; + Uint8 structData[2040]; +}; + +/* Primary Volume Descriptor (ECMA 167 3/10.1) */ +struct PrimaryVolDesc { + tag descTag; + Uint32 volDescSeqNum; + Uint32 primaryVolDescNum; + dstring volIdent[32]; + Uint16 volSeqNum; + Uint16 maxVolSeqNum; + Uint16 interchangeLvl; + Uint16 maxInterchangeLvl; + Uint32 charSetList; + Uint32 maxCharSetList; + dstring volSetIdent[128]; + charspec descCharSet; + charspec explanatoryCharSet; + extent_ad volAbstract; + extent_ad volCopyright; + EntityID appIdent; + timestamp recordingDateAndTime; + EntityID impIdent; + Uint8 impUse[64]; + Uint32 predecessorVolDescSeqLocation; + Uint16 flags; + Uint8 reserved[22]; +}; + +/* Primary volume descriptor flags (ECMA 167 3/10.1.21) */ +#define VOL_SET_IDENT 1 + +/* Anchor Volume Descriptor Pointer (ECMA 167 3/10.2) */ +struct AnchorVolDescPtr { + tag descTag; + extent_ad mainVolDescSeqExt; + extent_ad reserveVolDescSeqExt; + Uint8 reserved[480]; +}; + +/* Volume Descriptor Pointer (ECMA 167 3/10.3) */ +struct VolDescPtr { + tag descTag; + Uint32 volDescSeqNum; + extent_ad nextVolDescSeqExt; + Uint8 reserved[484]; +}; + +/* Implementation Use Volume Descriptor (ECMA 167 3/10.4) */ +struct ImpUseVolDesc { + tag descTag; + Uint32 volDescSeqNum; + EntityID impIdent; + Uint8 impUse[460]; +}; + +/* Partition Descriptor (ECMA 167 3/10.5) */ +struct PartitionDesc { + tag descTag; + Uint32 volDescSeqNum; + Uint16 partitionFlags; + Uint16 partitionNumber; + EntityID partitionContents; + Uint8 partitionContentsUse[128]; + Uint32 accessType; + Uint32 partitionStartingLocation; + Uint32 partitionLength; + EntityID impIdent; + Uint8 impUse[128]; + Uint8 reserved[156]; +}; + +/* Partition Flags (ECMA 167 3/10.5.3) */ +#define PARTITION_FLAGS_ALLOC 1 + +/* Partition Contents (ECMA 167 3/10.5.5) */ +#define PARTITION_CONTENTS_FDC01 "+FDC01" +#define PARTITION_CONTENTS_CD001 "+CD001" +#define PARTITION_CONTENTS_CDW02 "+CDW02" +#define PARTITION_CONTENTS_NSR02 "+NSR02" +#define PARTITION_CONTENTS_NSR03 "+NSR03" + +/* Partition Access Types (ECMA 167 3/10.5.7) */ +#define PARTITION_ACCESS_NONE 0 +#define PARTITION_ACCESS_R 1 +#define PARTITION_ACCESS_WO 2 +#define PARTITION_ACCESS_RW 3 +#define PARTITION_ACCESS_OW 4 + +/* Logical Volume Descriptor (ECMA 167 3/10.6) */ +struct LogicalVolDesc { + tag descTag; + Uint32 volDescSeqNum; + charspec descCharSet; + dstring logicalVolIdent[128]; + Uint32 logicalBlockSize; + EntityID domainIdent; + Uint8 logicalVolContentsUse[16]; /* used to find fileset */ + Uint32 mapTableLength; + Uint32 numPartitionMaps; + EntityID impIdent; + Uint8 impUse[128]; + extent_ad integritySeqExt; + Uint8 partitionMaps[0]; +}; + +/* Generic Partition Map (ECMA 167 3/10.7.1) */ +struct GenericPartitionMap { + Uint8 partitionMapType; + Uint8 partitionMapLength; + Uint8 partitionMapping[0]; +}; + +/* Partition Map Type (ECMA 167 3/10.7.1.1) */ +#define PARTITION_MAP_TYPE_NONE 0 +#define PARTITION_MAP_TYPE_1 1 +#define PARTITION_MAP_TYPE_2 2 + +/* Type 1 Partition Map (ECMA 167 3/10.7.2) */ +struct GenericPartitionMap1 { + Uint8 partitionMapType; + Uint8 partitionMapLength; + Uint16 volSeqNum; + Uint16 partitionNum; +}; + +/* Type 2 Partition Map (ECMA 167 3/10.7.3) */ +struct GenericPartitionMap2 { + Uint8 partitionMapType; /* 2 */ + Uint8 partitionMapLength; + Uint8 partitionIdent[62]; +}; + +/* Unallocated Space Descriptor (ECMA 167 3/10.8) */ +struct UnallocatedSpaceDesc { + tag descTag; + Uint32 volDescSeqNum; + Uint32 numAllocDescs; + extent_ad allocDescs[0]; +}; + +/* Terminating Descriptor (ECMA 3/10.9) */ +struct TerminatingDesc { + tag descTag; + Uint8 reserved[496]; +}; + +struct GenericDesc +{ + tag descTag; + Uint32 volDescSeqNum; +}; + +/* Logical Volume Integrity Descriptor (ECMA 167 3/10.10) */ +struct LogicalVolIntegrityDesc { + tag descTag; + timestamp recordingDateAndTime; + Uint32 integrityType; + extent_ad nextIntegrityExt; + Uint8 logicalVolContentsUse[32]; + Uint32 numOfPartitions; + Uint32 lengthOfImpUse; + Uint32 freeSpaceTable[0]; + Uint32 sizeTable[0]; + Uint8 impUse[0]; +}; + +/* Integrity Types (ECMA 167 3/10.10.3) */ +#define INTEGRITY_TYPE_OPEN 0 +#define INTEGRITY_TYPE_CLOSE 1 + +/* Recorded Address (ECMA 167 4/7.1) */ +typedef struct { + Uint32 logicalBlockNum; + Uint16 partitionReferenceNum; +} lb_addr; + +/* Extent interpretation (ECMA 167 4/14.14.1.1) */ +#define EXTENT_RECORDED_ALLOCATED 0x00 +#define EXTENT_NOT_RECORDED_ALLOCATED 0x01 +#define EXTENT_NOT_RECORDED_NOT_ALLOCATED 0x02 +#define EXTENT_NEXT_EXTENT_ALLOCDECS 0x03 + +/* Long Allocation Descriptor (ECMA 167 4/14.14.2) */ +typedef struct { + Uint32 extLength; + lb_addr extLocation; + Uint8 impUse[6]; +} long_ad; + /* upper 2 bits of extLength indicate type */ + +/* File Set Descriptor (ECMA 167 4/14.1) */ +struct FileSetDesc { + tag descTag; + timestamp recordingDateAndTime; + Uint16 interchangeLvl; + Uint16 maxInterchangeLvl; + Uint32 charSetList; + Uint32 maxCharSetList; + Uint32 fileSetNum; + Uint32 fileSetDescNum; + charspec logicalVolIdentCharSet; + dstring logicalVolIdent[128]; + charspec fileSetCharSet; + dstring fileSetIdent[32]; + dstring copyrightFileIdent[32]; + dstring abstractFileIdent[32]; + long_ad rootDirectoryICB; + EntityID domainIdent; + long_ad nextExt; + long_ad streamDirectoryICB; + Uint8 reserved[32]; +}; + +/* Short Allocation Descriptor (ECMA 167 4/14.14.1) */ +typedef struct { + Uint32 extLength; + Uint32 extPosition; +} short_ad; + +/* Partition Header Descriptor (ECMA 167 4/14.3) */ +struct PartitionHeaderDesc { + short_ad unallocatedSpaceTable; + short_ad unallocatedSpaceBitmap; + short_ad partitionIntegrityTable; + short_ad freedSpaceTable; + short_ad freedSpaceBitmap; + Uint8 reserved[88]; +}; + +/* File Identifier Descriptor (ECMA 167 4/14.4) */ +struct FileIdentDesc +{ + tag descTag; + Uint16 fileVersionNum; /* 1 */ + Uint8 fileCharacteristics; + Uint8 lengthFileIdent; + long_ad icb; + Uint16 lengthOfImpUse; + Uint8 impUse[0]; + Uint8 fileIdent[0]; + Uint8 padding[0]; +}; + +/* File Characteristics (ECMA 167 4/14.4.3) */ +#define FILE_HIDDEN 1 +#define FILE_DIRECTORY 2 +#define FILE_DELETED 4 +#define FILE_PARENT 8 +#define FILE_METADATA 0x10 /* UDF 2.0 */ + +/* Allocation Ext Descriptor (ECMA 167 4/14.5) */ +struct AllocExtDesc +{ + tag descTag; + Uint32 previousAllocExtLocation; + Uint32 lengthAllocDescs; +}; + +/* ICB Tag (ECMA 167 4/14.6) */ +typedef struct { + Uint32 priorRecordedNumDirectEntries; + Uint16 strategyType; + Uint16 strategyParameter; + Uint16 numEntries; + Uint8 reserved; + Uint8 fileType; + lb_addr parentICBLocation; + Uint16 flags; +} icbtag; + +/* ICB File Type (ECMA 167 4/14.6.6) */ +#define FILE_TYPE_NONE 0x00U +#define FILE_TYPE_UNALLOC 0x01U +#define FILE_TYPE_INTEGRITY 0x02U +#define FILE_TYPE_INDIRECT 0x03U +#define FILE_TYPE_DIRECTORY 0x04U +#define FILE_TYPE_REGULAR 0x05U +#define FILE_TYPE_BLOCK 0x06U +#define FILE_TYPE_CHAR 0x07U +#define FILE_TYPE_EXTENDED 0x08U +#define FILE_TYPE_FIFO 0x09U +#define FILE_TYPE_SOCKET 0x0aU +#define FILE_TYPE_TERMINAL 0x0bU +#define FILE_TYPE_SYMLINK 0x0cU +#define FILE_TYPE_STREAMDIR 0x0dU /* ECMA 167 4/13 */ + +/* ICB Flags (ECMA 167 4/14.6.8) */ +#define ICB_FLAG_ALLOC_MASK 0x0007U +#define ICB_FLAG_SORTED 0x0008U +#define ICB_FLAG_NONRELOCATABLE 0x0010U +#define ICB_FLAG_ARCHIVE 0x0020U +#define ICB_FLAG_SETUID 0x0040U +#define ICB_FLAG_SETGID 0x0080U +#define ICB_FLAG_STICKY 0x0100U +#define ICB_FLAG_CONTIGUOUS 0x0200U +#define ICB_FLAG_SYSTEM 0x0400U +#define ICB_FLAG_TRANSFORMED 0x0800U +#define ICB_FLAG_MULTIVERSIONS 0x1000U + +/* ICB Flags Allocation type(ECMA 167 4/14.6.8) */ +#define ICB_FLAG_AD_SHORT 0 +#define ICB_FLAG_AD_LONG 1 +#define ICB_FLAG_AD_EXTENDED 2 +#define ICB_FLAG_AD_IN_ICB 3 + +/* Indirect Entry (ECMA 167 4/14.7) */ +struct IndirectEntry { + tag descTag; + icbtag icbTag; + long_ad indirectICB; +}; + +/* Terminal Entry (ECMA 167 4/14.8) */ +struct TerminalEntry { + tag descTag; + icbtag icbTag; +}; + +/* File Entry (ECMA 167 4/14.9) */ +struct FileEntry { + tag descTag; + icbtag icbTag; + Uint32 uid; + Uint32 gid; + Uint32 permissions; + Uint16 fileLinkCount; + Uint8 recordFormat; + Uint8 recordDisplayAttr; + Uint32 recordLength; + Uint64 informationLength; + Uint64 logicalBlocksRecorded; + timestamp accessTime; + timestamp modificationTime; + timestamp attrTime; + Uint32 checkpoint; + long_ad extendedAttrICB; + EntityID impIdent; + Uint64 uniqueID; /* 0= root, 16- (2^32-1) */ + Uint32 lengthExtendedAttr; + Uint32 lengthAllocDescs; + Uint8 extendedAttr[0]; + Uint8 allocDescs[0]; +}; + +/* File Permissions (ECMA 167 4/14.9.5) */ +#define PERM_O_EXEC 0x00000001U +#define PERM_O_WRITE 0x00000002U +#define PERM_O_READ 0x00000004U +#define PERM_O_CHATTR 0x00000008U +#define PERM_O_DELETE 0x00000010U +#define PERM_G_EXEC 0x00000020U +#define PERM_G_WRITE 0x00000040U +#define PERM_G_READ 0x00000080U +#define PERM_G_CHATTR 0x00000100U +#define PERM_G_DELETE 0x00000200U +#define PERM_U_EXEC 0x00000400U +#define PERM_U_WRITE 0x00000800U +#define PERM_U_READ 0x00001000U +#define PERM_U_CHATTR 0x00002000U +#define PERM_U_DELETE 0x00004000U + +/* File Record Format (ECMA 167 4/14.9.7) */ +#define RECORD_FMT_NONE 0 +#define RECORD_FMT_FIXED_PAD 1 +#define RECORD_FMT_FIXED 2 +#define RECORD_FMT_VARIABLE8 3 +#define RECORD_FMT_VARIABLE16 4 +#define RECORD_FMT_VARIABLE16_MSB 5 +#define RECORD_FMT_VARIABLE32 6 +#define RECORD_FMT_PRINT 7 +#define RECORD_FMT_LF 8 +#define RECORD_FMT_CR 9 +#define RECORD_FMT_CRLF 10 +#define RECORD_FMT_LFCR 10 + +/* Extended Attribute Header Descriptor (ECMA 167 4/14.10.1) */ +struct ExtendedAttrHeaderDesc { + tag descTag; + Uint32 impAttrLocation; + Uint32 appAttrLocation; +}; + +/* Generic Attribute Format (ECMA 4/14.10.2) */ +struct GenericAttrFormat { + Uint32 attrType; + Uint8 attrSubtype; + Uint8 reserved[3]; + Uint32 attrLength; + Uint8 attrData[0]; +}; + +/* Character Set Attribute Format (ECMA 4/14.10.3) */ +struct CharSetAttrFormat { + Uint32 attrType; /* 1 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint32 escapeSeqLength; + Uint8 charSetType; + Uint8 escapeSeq[0]; +}; + +/* Alternate Permissions (ECMA 167 4/14.10.4) */ +struct AlternatePermissionsExtendedAttr { + Uint32 attrType; /* 3 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint16 ownerIdent; + Uint16 groupIdent; + Uint16 permission; +}; + +/* File Times Extended Attribute (ECMA 167 4/14.10.5) */ +struct FileTimesExtendedAttr { + Uint32 attrType; /* 5 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint32 dataLength; + Uint32 fileTimeExistence; + Uint8 fileTimes; +}; + +/* FileTimeExistence (ECMA 167 4/14.10.5.6) */ +#define FTE_CREATION 0 +#define FTE_DELETION 2 +#define FTE_EFFECTIVE 3 +#define FTE_BACKUP 5 + +/* Information Times Extended Attribute (ECMA 167 4/14.10.6) */ +struct InfoTimesExtendedAttr { + Uint32 attrType; /* 6 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint32 dataLength; + Uint32 infoTimeExistence; + Uint8 infoTimes[0]; +}; + +/* Device Specification Extended Attribute (ECMA 167 4/14.10.7) */ +struct DeviceSpecificationExtendedAttr { + Uint32 attrType; /* 12 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint32 impUseLength; + Uint32 majorDeviceIdent; + Uint32 minorDeviceIdent; + Uint8 impUse[0]; +}; + +/* Implementation Use Extended Attr (ECMA 167 4/14.10.8) */ +struct ImpUseExtendedAttr { + Uint32 attrType; /* 2048 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint32 impUseLength; + EntityID impIdent; + Uint8 impUse[0]; +}; + +/* Application Use Extended Attribute (ECMA 167 4/14.10.9) */ +struct AppUseExtendedAttr { + Uint32 attrType; /* 65536 */ + Uint8 attrSubtype; /* 1 */ + Uint8 reserved[3]; + Uint32 attrLength; + Uint32 appUseLength; + EntityID appIdent; + Uint8 appUse[0]; +}; + +#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 167 4/14.11) */ +struct UnallocatedSpaceEntry { + tag descTag; + icbtag icbTag; + Uint32 lengthAllocDescs; + Uint8 allocDescs[0]; +}; + +/* Space Bitmap Descriptor (ECMA 167 4/14.12) */ +struct SpaceBitmapDesc { + tag descTag; + Uint32 numOfBits; + Uint32 numOfBytes; + Uint8 bitmap[0]; +}; + +/* Partition Integrity Entry (ECMA 167 4/14.13) */ +struct PartitionIntegrityEntry { + tag descTag; + icbtag icbTag; + timestamp recordingDateAndTime; + Uint8 integrityType; + Uint8 reserved[175]; + EntityID impIdent; + Uint8 impUse[256]; +}; + +/* Extended Allocation Descriptor (ECMA 167 4/14.14.3) */ +typedef struct { /* ECMA 167 4/14.14.3 */ + Uint32 extLength; + Uint32 recordedLength; + Uint32 informationLength; + lb_addr extLocation; +} ext_ad; + +/* Logical Volume Header Descriptor (ECMA 167 4/14.5) */ +struct LogicalVolHeaderDesc { + Uint64 uniqueID; + Uint8 reserved[24]; +}; + +/* Path Component (ECMA 167 4/14.16.1) */ +struct PathComponent { + Uint8 componentType; + Uint8 lengthComponentIdent; + Uint16 componentFileVersionNum; + dstring componentIdent[0]; +}; + +/* File Entry (ECMA 167 4/14.17) */ +struct ExtendedFileEntry { + tag descTag; + icbtag icbTag; + Uint32 uid; + Uint32 gid; + Uint32 permissions; + Uint16 fileLinkCount; + Uint8 recordFormat; + Uint8 recordDisplayAttr; + Uint32 recordLength; + Uint64 informationLength; + Uint64 objectSize; + Uint64 logicalBlocksRecorded; + timestamp accessTime; + timestamp modificationTime; + timestamp createTime; + timestamp attrTime; + Uint32 checkpoint; + Uint32 reserved; + long_ad extendedAttrICB; + long_ad streamDirectoryICB; + EntityID impIdent; + Uint64 uniqueID; + Uint32 lengthExtendedAttr; + Uint32 lengthAllocDescs; + Uint8 extendedAttr[0]; + Uint8 allocDescs[0]; +}; +#pragma pack() + +#endif /* !defined(_LINUX_UDF_167_H) */ diff -Nru linux/include/linux/udf_fs.h linux.new/include/linux/udf_fs.h --- linux/include/linux/udf_fs.h Thu Jan 1 01:00:00 1970 +++ linux.new/include/linux/udf_fs.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,72 @@ +/* + * udf_fs.h + * + * PURPOSE + * Included by fs/filesystems.c + * + * 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@hootie.lvld.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-2000 Ben Fennema + * (C) 1999-2000 Stelias Computing Inc + * + * HISTORY + * + */ + +#if !defined(_LINUX_UDF_FS_H) +#define _LINUX_UDF_FS_H + + +#define UDF_PREALLOCATE +#define UDF_DEFAULT_PREALLOC_BLOCKS 8 + +#define UDFFS_DATE "2000/05/10" +#define UDFFS_VERSION "0.9.2.1" + +#define UDFFS_DEBUG + +#ifdef UDFFS_DEBUG +#define udf_debug(f, a...) \ + { \ + printk (KERN_DEBUG "UDF-fs DEBUG (%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (## f, ## a); \ + } +#else +#define udf_debug(f, a...) /**/ +#endif + +#define udf_info(f, a...) \ + printk (KERN_INFO "UDF-fs INFO " ## f, ## a); + +#ifdef __KERNEL__ + +#ifndef LINUX_VERSION_CODE +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +extern int init_udf_fs(void); +#endif /* 2.2.XX */ + +#endif /* __KERNEL__ */ + +#endif /* !defined(_LINUX_UDF_FS_H) */ diff -Nru linux/include/linux/udf_fs_i.h linux.new/include/linux/udf_fs_i.h --- linux/include/linux/udf_fs_i.h Thu Jan 1 01:00:00 1970 +++ linux.new/include/linux/udf_fs_i.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,58 @@ +/* + * udf_fs_i.h + * + * This file is intended for the Linux kernel/module. + * + * 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@hootie.lvld.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. + */ + +#if !defined(_LINUX_UDF_FS_I_H) +#define _LINUX_UDF_FS_I_H + +#ifdef __KERNEL__ + +#ifndef _LINUX_UDF_167_H +typedef struct +{ + __u32 logicalBlockNum; + __u16 partitionReferenceNum; +} lb_addr; +#endif + +struct udf_inode_info +{ + long i_uatime; + long i_umtime; + long i_uctime; + /* Physical address of inode */ + lb_addr i_location; + __u64 i_unique; + __u32 i_lenEAttr; + __u32 i_lenAlloc; + __u32 i_next_alloc_block; + __u32 i_next_alloc_goal; + unsigned i_alloc_type : 3; + unsigned i_extended_fe : 1; + unsigned i_strat_4096 : 1; + unsigned i_new_inode : 1; + unsigned reserved : 26; +}; + +#endif + +/* exported IOCTLs, we have 'l', 0x40-0x7f */ + +#define UDF_GETEASIZE _IOR('l', 0x40, int) +#define UDF_GETEABLOCK _IOR('l', 0x41, void *) +#define UDF_GETVOLIDENT _IOR('l', 0x42, void *) + +#endif /* !defined(_LINUX_UDF_FS_I_H) */ diff -Nru linux/include/linux/udf_fs_sb.h linux.new/include/linux/udf_fs_sb.h --- linux/include/linux/udf_fs_sb.h Thu Jan 1 01:00:00 1970 +++ linux.new/include/linux/udf_fs_sb.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,124 @@ +/* + * udf_fs_sb.h + * + * This include file is for the Linux kernel/module. + * + * 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@hootie.lvld.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. + */ + +#if !defined(_LINUX_UDF_FS_SB_H) +#define _LINUX_UDF_FS_SB_H + +#pragma pack(1) + +#define UDF_MAX_BLOCK_LOADED 8 + +#define UDF_TYPE1_MAP15 0x1511U +#define UDF_VIRTUAL_MAP15 0x1512U +#define UDF_VIRTUAL_MAP20 0x2012U +#define UDF_SPARABLE_MAP15 0x1522U + +struct udf_sparing_data +{ + __u32 s_spar_loc[4]; + __u8 s_spar_pshift; + __u8 s_spar_indexsize; + __u32 *s_spar_map; + union + { + __u8 *s_spar_remap8; + __u16 *s_spar_remap16; + __u32 *s_spar_remap32; + } s_spar_remap; +}; + +struct udf_virtual_data +{ + __u32 s_num_entries; + __u16 s_start_offset; +}; + +struct udf_bitmap +{ + __u32 s_extLength; + __u32 s_extPosition; + __u16 s_nr_groups; + struct buffer_head **s_block_bitmap; +}; + +struct udf_part_map +{ + union + { + struct udf_bitmap *s_bitmap; + struct inode *s_table; + } s_uspace; + union + { + struct udf_bitmap *s_bitmap; + struct inode *s_table; + } s_fspace; + __u32 s_partition_root; + __u32 s_partition_len; + __u16 s_partition_type; + __u16 s_partition_num; + union + { + struct udf_sparing_data s_sparing; + struct udf_virtual_data s_virtual; + } s_type_specific; + __u32 (*s_partition_func)(struct super_block *, __u32, __u16, __u32); + __u16 s_volumeseqnum; + __u16 s_partition_flags; +}; + +#pragma pack() + +struct udf_sb_info +{ + struct udf_part_map *s_partmaps; + __u8 s_volident[32]; + + /* Overall info */ + __u16 s_partitions; + __u16 s_partition; + + /* Sector headers */ + __u32 s_session; + __u32 s_anchor[4]; + __u32 s_lastblock; + + struct buffer_head *s_lvidbh; + + /* Default permissions */ + mode_t s_umask; + gid_t s_gid; + uid_t s_uid; + + /* Root Info */ + time_t s_recordtime; + + /* Fileset Info */ + __u16 s_serialnum; + + /* highest UDF revision we have recorded to this media */ + __u16 s_udfrev; + + /* Miscellaneous flags */ + __u32 s_flags; + + /* VAT inode */ + struct inode *s_vat; + +}; + +#endif /* !defined(_LINUX_UDF_FS_SB_H) */ diff -Nru linux/include/linux/udf_udf.h linux.new/include/linux/udf_udf.h --- linux/include/linux/udf_udf.h Thu Jan 1 01:00:00 1970 +++ linux.new/include/linux/udf_udf.h Fri Sep 7 11:32:16 2001 @@ -0,0 +1,227 @@ +#if !defined(_LINUX_UDF_UDF_H) +#define _LINUX_UDF_UDF_H +/* + * udf_udf.h + * + * PURPOSE + * OSTA-UDF(tm) format specification [based on ECMA 167 standard]. + * http://www.osta.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@hootie.lvld.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. + * + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. + * + * 10/2/98 dgb changed UDF_ID_DEVELOPER + * 11/26/98 bf changed UDF_ID_DEVELOPER, + * 12/5/98 dgb updated include file hierarchy, more UDF definitions + */ + +/* based on ECMA 167 structure definitions */ +#include + +#pragma pack(1) + +/* -------- Basic types and constants ----------- */ +/* UDF character set (UDF 1.50 2.1.2) */ +#define UDF_CHAR_SET_TYPE 0 +#define UDF_CHAR_SET_INFO "OSTA Compressed Unicode" + +#define UDF_ID_DEVELOPER "*Linux UDFFS" + +/* UDF 1.02 2.2.6.4 */ +struct LogicalVolIntegrityDescImpUse +{ + EntityID impIdent; + Uint32 numFiles; + Uint32 numDirs; + Uint16 minUDFReadRev; + Uint16 minUDFWriteRev; + Uint16 maxUDFWriteRev; +}; + +/* UDF 1.02 2.2.7.2 */ +/* LVInformation may be present in ImpUseVolDesc.impUse */ +struct ImpUseVolDescImpUse +{ + charspec LVICharset; + dstring logicalVolIdent[128]; + dstring LVInfo1[36]; + dstring LVInfo2[36]; + dstring LVInfo3[36]; + EntityID impIdent; + Uint8 impUse[128]; +}; + +struct UdfPartitionMap2 +{ + Uint8 partitionMapType; + Uint8 partitionMapLength; + Uint8 reserved1[2]; + EntityID partIdent; + Uint16 volSeqNum; + Uint16 partitionNum; + Uint8 reserved2[24]; +}; + +/* UDF 1.5 2.2.8 */ +struct VirtualPartitionMap +{ + Uint8 partitionMapType; /* 2 */ + Uint8 partitionMapLength; /* 64 */ + Uint8 reserved1[2]; /* #00 */ + EntityID partIdent; + Uint16 volSeqNum; + Uint16 partitionNum; + Uint8 reserved2[24]; /* #00 */ +}; + +/* UDF 1.5 2.2.9 */ +struct SparablePartitionMap +{ + Uint8 partitionMapType; /* 2 */ + Uint8 partitionMapLength; /* 64 */ + Uint8 reserved1[2]; /* #00 */ + EntityID partIdent; /* Flags = 0 */ + /* Id = UDF_ID_SPARABLE */ + /* IdSuf = 2.1.5.3 */ + Uint16 volSeqNum; + Uint16 partitionNum; + Uint16 packetLength; /* 32 */ + Uint8 numSparingTables; + Uint8 reserved2[1]; /* #00 */ + Uint32 sizeSparingTable; + Uint32 locSparingTable[4]; +}; + +/* DVD Copyright Management Info, see UDF 1.02 3.3.4.5.1.2 */ +/* when ImpUseExtendedAttr.impIdent= "*UDF DVD CGMS Info" */ +struct DVDCopyrightImpUse { + Uint16 headerChecksum; + Uint8 CGMSInfo; + Uint8 dataType; + Uint8 protectionSystemInfo[4]; +}; + +/* the impUse of long_ad used in AllocDescs - UDF 1.02 2.3.10.1 */ +struct ADImpUse +{ + Uint16 flags; + Uint8 impUse[4]; +}; + +/* UDF 1.02 2.3.10.1 */ +#define UDF_EXTENT_LENGTH_MASK 0x3FFFFFFF +#define UDF_EXTENT_FLAG_MASK 0xc0000000 +#define UDF_EXTENT_FLAG_ERASED 0x40000000 + +/* + * Important! VirtualAllocationTables are + * very different between 1.5 and 2.0! + */ + +/* ----------- 1.5 ------------- */ +/* UDF 1.5 2.2.10 */ +#define FILE_TYPE_VAT15 0x0U + +/* UDF 1.5 2.2.10 - VAT layout: */ +struct VirutalAllocationTable15 { + Uint32 VirtualSector[0]; + EntityID ident; + Uint32 previousVATICB; + }; +/* where number of VirtualSector's is (VATSize-36)/4 */ + +/* ----------- 2.0 ------------- */ +/* UDF 2.0 2.2.10 */ +#define FILE_TYPE_VAT20 0xf8U + +/* UDF 2.0 2.2.10 (different from 1.5!) */ +struct VirtualAllocationTable20 { + Uint16 lengthHeader; + Uint16 lengthImpUse; + dstring logicalVolIdent[128]; + Uint32 previousVatICBLoc; + Uint32 numFIDSFiles; + Uint32 numFIDSDirectories; /* non-parent */ + Uint16 minReadRevision; + Uint16 minWriteRevision; + Uint16 maxWriteRevision; + Uint16 reserved; + Uint8 impUse[0]; + Uint32 vatEntry[0]; +}; + +/* Sparing maps, see UDF 1.5 2.2.11 */ +typedef struct { + Uint32 origLocation; + Uint32 mappedLocation; +} SparingEntry; + +/* sparing maps, see UDF 2.0 2.2.11 */ +struct SparingTable { + tag descTag; + EntityID sparingIdent; /* *UDF Sparing Table */ + Uint16 reallocationTableLen; + Uint16 reserved; /* #00 */ + Uint32 sequenceNum; + SparingEntry mapEntry[0]; +}; + +/* Entity Identifiers (UDF 1.50 6.1) */ +#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" + +/* Operating System Identifiers (UDF 1.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_WIN95 0x05U +#define UDF_OS_CLASS_WINNT 0x06U +#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_UNIX 0x00U +#define UDF_OS_ID_WIN95 0x00U +#define UDF_OS_ID_WINNT 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_NAME_PAD 4 +#define UDF_NAME_LEN 255 +#define UDF_PATH_LEN 1023 + +#pragma pack() + +#endif /* !defined(_LINUX_UDF_FMT_H) */