diff options
Diffstat (limited to 'fs/udf')
| -rw-r--r-- | fs/udf/Kconfig | 18 | ||||
| -rw-r--r-- | fs/udf/Makefile | 4 | ||||
| -rw-r--r-- | fs/udf/balloc.c | 1098 | ||||
| -rw-r--r-- | fs/udf/crc.c | 178 | ||||
| -rw-r--r-- | fs/udf/dir.c | 277 | ||||
| -rw-r--r-- | fs/udf/directory.c | 308 | ||||
| -rw-r--r-- | fs/udf/ecma_167.h | 584 | ||||
| -rw-r--r-- | fs/udf/file.c | 316 | ||||
| -rw-r--r-- | fs/udf/fsync.c | 56 | ||||
| -rw-r--r-- | fs/udf/ialloc.c | 163 | ||||
| -rw-r--r-- | fs/udf/inode.c | 2816 | ||||
| -rw-r--r-- | fs/udf/lowlevel.c | 38 | ||||
| -rw-r--r-- | fs/udf/misc.c | 248 | ||||
| -rw-r--r-- | fs/udf/namei.c | 1467 | ||||
| -rw-r--r-- | fs/udf/osta_udf.h | 95 | ||||
| -rw-r--r-- | fs/udf/partition.c | 353 | ||||
| -rw-r--r-- | fs/udf/super.c | 3087 | ||||
| -rw-r--r-- | fs/udf/symlink.c | 96 | ||||
| -rw-r--r-- | fs/udf/truncate.c | 411 | ||||
| -rw-r--r-- | fs/udf/udf_i.h | 76 | ||||
| -rw-r--r-- | fs/udf/udf_sb.h | 235 | ||||
| -rw-r--r-- | fs/udf/udfdecl.h | 241 | ||||
| -rw-r--r-- | fs/udf/udfend.h | 56 | ||||
| -rw-r--r-- | fs/udf/udftime.c | 128 | ||||
| -rw-r--r-- | fs/udf/unicode.c | 377 |
25 files changed, 6563 insertions, 6163 deletions
diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig new file mode 100644 index 00000000000..0e0e99bd6bc --- /dev/null +++ b/fs/udf/Kconfig @@ -0,0 +1,18 @@ +config UDF_FS + tristate "UDF file system support" + select CRC_ITU_T + help + This is the new file system used on some CD-ROMs and DVDs. Say Y if + you intend to mount DVD discs or CDRW's written in packet mode, or + if written to by other UDF utilities, such as DirectCD. + Please read <file:Documentation/filesystems/udf.txt>. + + To compile this file system support as a module, choose M here: the + module will be called udf. + + If unsure, say N. + +config UDF_NLS + bool + default y + depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y) diff --git a/fs/udf/Makefile b/fs/udf/Makefile index be845e7540e..eb880f66c23 100644 --- a/fs/udf/Makefile +++ b/fs/udf/Makefile @@ -5,5 +5,5 @@ obj-$(CONFIG_UDF_FS) += udf.o udf-objs := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \ - partition.o super.o truncate.o symlink.o fsync.o \ - crc.o directory.o misc.o udftime.o unicode.o + partition.o super.o truncate.o symlink.o \ + directory.o misc.o udftime.o unicode.o diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index b9ded26b10a..1ba2baaf436 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -4,11 +4,6 @@ * PURPOSE * Block allocation handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -26,109 +21,62 @@ #include "udfdecl.h" -#include <linux/quotaops.h> #include <linux/buffer_head.h> #include <linux/bitops.h> #include "udf_i.h" #include "udf_sb.h" -#define udf_clear_bit(nr,addr) ext2_clear_bit(nr,addr) -#define udf_set_bit(nr,addr) ext2_set_bit(nr,addr) -#define udf_test_bit(nr, addr) ext2_test_bit(nr, addr) -#define udf_find_first_one_bit(addr, size) find_first_one_bit(addr, size) -#define udf_find_next_one_bit(addr, size, offset) find_next_one_bit(addr, size, offset) - -#define leBPL_to_cpup(x) leNUM_to_cpup(BITS_PER_LONG, x) -#define leNUM_to_cpup(x,y) xleNUM_to_cpup(x,y) -#define xleNUM_to_cpup(x,y) (le ## x ## _to_cpup(y)) -#define uintBPL_t uint(BITS_PER_LONG) -#define uint(x) xuint(x) -#define xuint(x) __le ## x +#define udf_clear_bit __test_and_clear_bit_le +#define udf_set_bit __test_and_set_bit_le +#define udf_test_bit test_bit_le +#define udf_find_next_one_bit find_next_bit_le -extern inline int find_next_one_bit (void * addr, int size, int offset) -{ - uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG); - int result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset &= (BITS_PER_LONG-1); - if (offset) - { - tmp = leBPL_to_cpup(p++); - tmp &= ~0UL << offset; - if (size < BITS_PER_LONG) - goto found_first; - if (tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG-1)) - { - if ((tmp = leBPL_to_cpup(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = leBPL_to_cpup(p); -found_first: - tmp &= ~0UL >> (BITS_PER_LONG-size); -found_middle: - return result + ffz(~tmp); -} - -#define find_first_one_bit(addr, size)\ - find_next_one_bit((addr), (size), 0) - -static int read_block_bitmap(struct super_block * sb, - struct udf_bitmap *bitmap, unsigned int block, unsigned long bitmap_nr) +static int read_block_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap, unsigned int block, + unsigned long bitmap_nr) { struct buffer_head *bh = NULL; int retval = 0; - kernel_lb_addr loc; + struct kernel_lb_addr loc; loc.logicalBlockNum = bitmap->s_extPosition; - loc.partitionReferenceNum = UDF_SB_PARTITION(sb); + loc.partitionReferenceNum = UDF_SB(sb)->s_partition; - bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block)); + bh = udf_tread(sb, udf_get_lb_pblock(sb, &loc, block)); if (!bh) - { retval = -EIO; - } + bitmap->s_block_bitmap[bitmap_nr] = bh; return retval; } -static int __load_block_bitmap(struct super_block * sb, - struct udf_bitmap *bitmap, unsigned int block_group) +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 (block_group >= nr_groups) { + udf_debug("block_group (%d) > nr_groups (%d)\n", + block_group, nr_groups); } - if (bitmap->s_block_bitmap[block_group]) + if (bitmap->s_block_bitmap[block_group]) { return block_group; - else - { - retval = read_block_bitmap(sb, bitmap, block_group, 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) +static inline int load_block_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap, + unsigned int block_group) { int slot; @@ -143,13 +91,28 @@ static inline int load_block_bitmap(struct super_block * sb, return slot; } -static void udf_bitmap_free_blocks(struct super_block * sb, - struct inode * inode, - struct udf_bitmap *bitmap, - kernel_lb_addr bloc, uint32_t offset, uint32_t count) +static void udf_add_free_space(struct super_block *sb, u16 partition, u32 cnt) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + struct logicalVolIntegrityDesc *lvid; + + if (!sbi->s_lvid_bh) + return; + + lvid = (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data; + le32_add_cpu(&lvid->freeSpaceTable[partition], cnt); + udf_updated_lvid(sb); +} + +static void udf_bitmap_free_blocks(struct super_block *sb, + struct udf_bitmap *bitmap, + struct kernel_lb_addr *bloc, + uint32_t offset, + uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; + struct udf_part_map *partmap; unsigned long block; unsigned long block_group; unsigned long bit; @@ -157,149 +120,122 @@ static void udf_bitmap_free_blocks(struct super_block * sb, int bitmap_nr; unsigned long overflow; - down(&sbi->s_alloc_sem); - if (bloc.logicalBlockNum < 0 || - (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) - { + mutex_lock(&sbi->s_alloc_mutex); + partmap = &sbi->s_partmaps[bloc->partitionReferenceNum]; + if (bloc->logicalBlockNum + count < count || + (bloc->logicalBlockNum + count) > partmap->s_partition_len) { udf_debug("%d < %d || %d + %d > %d\n", - bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, - UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); + bloc->logicalBlockNum, 0, + bloc->logicalBlockNum, count, + partmap->s_partition_len); 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); + block = bloc->logicalBlockNum + offset + + (sizeof(struct spaceBitmapDesc) << 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; + do { + overflow = 0; + block_group = block >> (sb->s_blocksize_bits + 3); + bit = block % (sb->s_blocksize << 3); - 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]); + /* + * 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; } - else - { - if (inode) - DQUOT_FREE_BLOCK(inode, 1); - if (UDF_SB_LVIDBH(sb)) - { - UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1); + 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]); } } - } - mark_buffer_dirty(bh); - if (overflow) - { - block += count; - count = overflow; - goto do_more; - } + udf_add_free_space(sb, sbi->s_partition, count); + mark_buffer_dirty(bh); + if (overflow) { + block += count; + count = overflow; + } + } while (overflow); + error_return: - sb->s_dirt = 1; - if (UDF_SB_LVIDBH(sb)) - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - up(&sbi->s_alloc_sem); - return; + mutex_unlock(&sbi->s_alloc_mutex); } -static int udf_bitmap_prealloc_blocks(struct super_block * sb, - struct inode * inode, - struct udf_bitmap *bitmap, uint16_t partition, uint32_t first_block, - uint32_t block_count) +static int udf_bitmap_prealloc_blocks(struct super_block *sb, + struct udf_bitmap *bitmap, + uint16_t partition, uint32_t first_block, + uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; int bit, block, block_group, group_start; int nr_groups, bitmap_nr; struct buffer_head *bh; + __u32 part_len; - down(&sbi->s_alloc_sem); - if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) + mutex_lock(&sbi->s_alloc_mutex); + part_len = sbi->s_partmaps[partition].s_partition_len; + if (first_block >= part_len) goto out; - if (first_block + block_count > UDF_SB_PARTLEN(sb, partition)) - block_count = UDF_SB_PARTLEN(sb, partition) - first_block; + if (first_block + block_count > part_len) + block_count = part_len - first_block; -repeat: - nr_groups = (UDF_SB_PARTLEN(sb, partition) + - (sizeof(struct spaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8); - block = first_block + (sizeof(struct spaceBitmapDesc) << 3); - block_group = block >> (sb->s_blocksize_bits + 3); - group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); + do { + nr_groups = udf_compute_nr_groups(sb, partition); + block = first_block + (sizeof(struct spaceBitmapDesc) << 3); + block_group = block >> (sb->s_blocksize_bits + 3); + group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc); - bitmap_nr = load_block_bitmap(sb, bitmap, block_group); - if (bitmap_nr < 0) - goto out; - bh = bitmap->s_block_bitmap[bitmap_nr]; + 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); + bit = block % (sb->s_blocksize << 3); - while (bit < (sb->s_blocksize << 3) && block_count > 0) - { - if (!udf_test_bit(bit, bh->b_data)) - goto out; - else if (DQUOT_PREALLOC_BLOCK(inode, 1)) - goto out; - else if (!udf_clear_bit(bit, bh->b_data)) - { - udf_debug("bit already cleared for block %d\n", bit); - DQUOT_FREE_BLOCK(inode, 1); - goto out; + while (bit < (sb->s_blocksize << 3) && block_count > 0) { + if (!udf_clear_bit(bit, bh->b_data)) + goto out; + block_count--; + alloc_count++; + bit++; + block++; } - block_count --; - alloc_count ++; - bit ++; - block ++; - } - mark_buffer_dirty(bh); - if (block_count > 0) - goto repeat; + mark_buffer_dirty(bh); + } while (block_count > 0); + out: - if (UDF_SB_LVIDBH(sb)) - { - UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - } - sb->s_dirt = 1; - up(&sbi->s_alloc_sem); + udf_add_free_space(sb, partition, -alloc_count); + mutex_unlock(&sbi->s_alloc_mutex); return alloc_count; } -static int udf_bitmap_new_block(struct super_block * sb, - struct inode * inode, - struct udf_bitmap *bitmap, uint16_t partition, uint32_t goal, int *err) +static int udf_bitmap_new_block(struct super_block *sb, + struct udf_bitmap *bitmap, uint16_t partition, + uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); - int newbit, bit=0, block, block_group, group_start; + int newbit, bit = 0, block, block_group, group_start; int end_goal, nr_groups, bitmap_nr, i; struct buffer_head *bh = NULL; char *ptr; int newblock = 0; *err = -ENOSPC; - down(&sbi->s_alloc_sem); + mutex_lock(&sbi->s_alloc_mutex); repeat: - if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) + if (goal >= sbi->s_partmaps[partition].s_partition_len) goal = 0; nr_groups = bitmap->s_nr_groups; @@ -311,38 +247,37 @@ repeat: 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); + ptr = memscan((char *)bh->b_data + group_start, 0xFF, + sb->s_blocksize - group_start); - if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) - { + 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)); + + 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) - { + 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) - { + + 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 ++; + 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); @@ -351,473 +286,424 @@ repeat: 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) - { + 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); + } else { + bit = udf_find_next_one_bit(bh->b_data, + sb->s_blocksize << 3, + group_start << 3); if (bit < sb->s_blocksize << 3) break; } } - if (i >= (nr_groups*2)) - { - up(&sbi->s_alloc_sem); + if (i >= (nr_groups * 2)) { + mutex_unlock(&sbi->s_alloc_mutex); return newblock; } if (bit < sb->s_blocksize << 3) goto search_back; else - bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3); - if (bit >= sb->s_blocksize << 3) - { - up(&sbi->s_alloc_sem); + bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, + group_start << 3); + if (bit >= sb->s_blocksize << 3) { + mutex_unlock(&sbi->s_alloc_mutex); return 0; } search_back: - for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--); - -got_block: - - /* - * Check quota for allocation of this block. - */ - if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) - { - up(&sbi->s_alloc_sem); - *err = -EDQUOT; - return 0; + i = 0; + while (i < 7 && bit > (group_start << 3) && + udf_test_bit(bit - 1, bh->b_data)) { + ++i; + --bit; } +got_block: newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) - (sizeof(struct spaceBitmapDesc) << 3); - if (!udf_clear_bit(bit, bh->b_data)) - { + if (!udf_clear_bit(bit, bh->b_data)) { udf_debug("bit already cleared for block %d\n", bit); goto repeat; } mark_buffer_dirty(bh); - if (UDF_SB_LVIDBH(sb)) - { - UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1); - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - } - sb->s_dirt = 1; - up(&sbi->s_alloc_sem); + udf_add_free_space(sb, partition, -1); + mutex_unlock(&sbi->s_alloc_mutex); *err = 0; return newblock; error_return: *err = -EIO; - up(&sbi->s_alloc_sem); + mutex_unlock(&sbi->s_alloc_mutex); return 0; } -static void udf_table_free_blocks(struct super_block * sb, - struct inode * inode, - struct inode * table, - kernel_lb_addr bloc, uint32_t offset, uint32_t count) +static void udf_table_free_blocks(struct super_block *sb, + struct inode *table, + struct kernel_lb_addr *bloc, + uint32_t offset, + uint32_t count) { struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *partmap; uint32_t start, end; - uint32_t nextoffset, oextoffset, elen; - kernel_lb_addr nbloc, obloc, eloc; - struct buffer_head *obh, *nbh; + uint32_t elen; + struct kernel_lb_addr eloc; + struct extent_position oepos, epos; int8_t etype; int i; + struct udf_inode_info *iinfo; - down(&sbi->s_alloc_sem); - if (bloc.logicalBlockNum < 0 || - (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)) - { + mutex_lock(&sbi->s_alloc_mutex); + partmap = &sbi->s_partmaps[bloc->partitionReferenceNum]; + if (bloc->logicalBlockNum + count < count || + (bloc->logicalBlockNum + count) > partmap->s_partition_len) { udf_debug("%d < %d || %d + %d > %d\n", - bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count, - UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum)); + bloc->logicalBlockNum, 0, + bloc->logicalBlockNum, count, + partmap->s_partition_len); goto error_return; } - /* We do this up front - There are some error conditions that could occure, - but.. oh well */ - if (inode) - DQUOT_FREE_BLOCK(inode, count); - if (UDF_SB_LVIDBH(sb)) - { - UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+count); - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - } + iinfo = UDF_I(table); + udf_add_free_space(sb, sbi->s_partition, count); - start = bloc.logicalBlockNum + offset; - end = bloc.logicalBlockNum + offset + count - 1; + start = bloc->logicalBlockNum + offset; + end = bloc->logicalBlockNum + offset + count - 1; - oextoffset = nextoffset = sizeof(struct unallocSpaceEntry); + epos.offset = oepos.offset = sizeof(struct unallocSpaceEntry); elen = 0; - obloc = nbloc = UDF_I_LOCATION(table); - - obh = nbh = NULL; - - while (count && (etype = - udf_next_aext(table, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) - { - if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) == - start)) - { - if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits)) - { - count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); - start += ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits); - elen = (etype << 30) | (0x40000000 - sb->s_blocksize); - } - else - { + epos.block = oepos.block = iinfo->i_location; + epos.bh = oepos.bh = NULL; + + while (count && + (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { + if (((eloc.logicalBlockNum + + (elen >> sb->s_blocksize_bits)) == start)) { + if ((0x3FFFFFFF - elen) < + (count << sb->s_blocksize_bits)) { + uint32_t tmp = ((0x3FFFFFFF - elen) >> + sb->s_blocksize_bits); + count -= tmp; + start += tmp; elen = (etype << 30) | - (elen + (count << sb->s_blocksize_bits)); + (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 - { + udf_write_aext(table, &oepos, &eloc, elen, 1); + } else if (eloc.logicalBlockNum == (end + 1)) { + if ((0x3FFFFFFF - elen) < + (count << sb->s_blocksize_bits)) { + uint32_t tmp = ((0x3FFFFFFF - elen) >> + sb->s_blocksize_bits); + count -= tmp; + end -= tmp; + eloc.logicalBlockNum -= tmp; + elen = (etype << 30) | + (0x40000000 - sb->s_blocksize); + } else { eloc.logicalBlockNum = start; elen = (etype << 30) | - (elen + (count << sb->s_blocksize_bits)); + (elen + + (count << sb->s_blocksize_bits)); end -= count; count = 0; } - udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1); + udf_write_aext(table, &oepos, &eloc, elen, 1); } - if (nbh != obh) - { + if (epos.bh != oepos.bh) { i = -1; - obloc = nbloc; - udf_release_data(obh); - atomic_inc(&nbh->b_count); - obh = nbh; - oextoffset = 0; + oepos.block = epos.block; + brelse(oepos.bh); + get_bh(epos.bh); + oepos.bh = epos.bh; + oepos.offset = 0; + } else { + oepos.offset = epos.offset; } - 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. - */ + 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 short_ad *sad = NULL; + struct long_ad *lad = NULL; struct allocExtDesc *aed; eloc.logicalBlockNum = start; elen = EXT_RECORDED_ALLOCATED | (count << sb->s_blocksize_bits); - if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); - else - { - udf_release_data(obh); - udf_release_data(nbh); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); + else { + brelse(oepos.bh); + brelse(epos.bh); goto error_return; } - if (nextoffset + (2 * adsize) > sb->s_blocksize) - { - char *sptr, *dptr; + if (epos.offset + (2 * adsize) > sb->s_blocksize) { + unsigned char *sptr, *dptr; int loffset; - - udf_release_data(obh); - obh = nbh; - obloc = nbloc; - oextoffset = nextoffset; + + brelse(oepos.bh); + oepos = epos; /* Steal a block from the extent being free'd */ - nbloc.logicalBlockNum = eloc.logicalBlockNum; - eloc.logicalBlockNum ++; + epos.block.logicalBlockNum = eloc.logicalBlockNum; + eloc.logicalBlockNum++; elen -= sb->s_blocksize; - if (!(nbh = udf_tread(sb, - udf_get_lb_pblock(sb, nbloc, 0)))) - { - udf_release_data(obh); + epos.bh = udf_tread(sb, + udf_get_lb_pblock(sb, &epos.block, 0)); + if (!epos.bh) { + brelse(oepos.bh); 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 = (struct allocExtDesc *)(epos.bh->b_data); + aed->previousAllocExtLocation = + cpu_to_le32(oepos.block.logicalBlockNum); + if (epos.offset + adsize > sb->s_blocksize) { + loffset = epos.offset; aed->lengthAllocDescs = cpu_to_le32(adsize); - if (obh) - sptr = UDF_I_DATA(inode) + nextoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode) - adsize; - else - sptr = obh->b_data + nextoffset - adsize; - dptr = nbh->b_data + sizeof(struct allocExtDesc); + sptr = iinfo->i_ext.i_data + epos.offset + - adsize; + dptr = epos.bh->b_data + + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); - nextoffset = sizeof(struct allocExtDesc) + adsize; - } - else - { - loffset = nextoffset + adsize; + epos.offset = sizeof(struct allocExtDesc) + + adsize; + } else { + loffset = epos.offset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); - sptr = (obh)->b_data + nextoffset; - nextoffset = sizeof(struct allocExtDesc); - - if (obh) - { - aed = (struct allocExtDesc *)(obh)->b_data; - aed->lengthAllocDescs = - cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); - } - else - { - UDF_I_LENALLOC(table) += adsize; + if (oepos.bh) { + sptr = oepos.bh->b_data + epos.offset; + aed = (struct allocExtDesc *) + oepos.bh->b_data; + le32_add_cpu(&aed->lengthAllocDescs, + adsize); + } else { + sptr = iinfo->i_ext.i_data + + epos.offset; + iinfo->i_lenAlloc += adsize; mark_inode_dirty(table); } + epos.offset = sizeof(struct allocExtDesc); } - if (UDF_SB_UDFREV(sb) >= 0x0200) - udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, - nbloc.logicalBlockNum, sizeof(tag)); + if (sbi->s_udfrev >= 0x0200) + udf_new_tag(epos.bh->b_data, TAG_IDENT_AED, + 3, 1, epos.block.logicalBlockNum, + sizeof(struct tag)); else - udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, - nbloc.logicalBlockNum, sizeof(tag)); - switch (UDF_I_ALLOCTYPE(table)) - { - case ICBTAG_FLAG_AD_SHORT: - { - sad = (short_ad *)sptr; - sad->extLength = cpu_to_le32( - EXT_NEXT_EXTENT_ALLOCDECS | - sb->s_blocksize); - sad->extPosition = cpu_to_le32(nbloc.logicalBlockNum); - break; - } - case ICBTAG_FLAG_AD_LONG: - { - lad = (long_ad *)sptr; - lad->extLength = cpu_to_le32( - EXT_NEXT_EXTENT_ALLOCDECS | - sb->s_blocksize); - lad->extLocation = cpu_to_lelb(nbloc); - break; - } - } - if (obh) - { - udf_update_tag(obh->b_data, loffset); - mark_buffer_dirty(obh); + udf_new_tag(epos.bh->b_data, TAG_IDENT_AED, + 2, 1, epos.block.logicalBlockNum, + sizeof(struct tag)); + + switch (iinfo->i_alloc_type) { + case ICBTAG_FLAG_AD_SHORT: + sad = (struct short_ad *)sptr; + sad->extLength = cpu_to_le32( + EXT_NEXT_EXTENT_ALLOCDECS | + sb->s_blocksize); + sad->extPosition = + cpu_to_le32(epos.block.logicalBlockNum); + break; + case ICBTAG_FLAG_AD_LONG: + lad = (struct long_ad *)sptr; + lad->extLength = cpu_to_le32( + EXT_NEXT_EXTENT_ALLOCDECS | + sb->s_blocksize); + lad->extLocation = + cpu_to_lelb(epos.block); + break; } - else + if (oepos.bh) { + udf_update_tag(oepos.bh->b_data, loffset); + mark_buffer_dirty(oepos.bh); + } else { mark_inode_dirty(table); + } } - if (elen) /* It's possible that stealing the block emptied the extent */ - { - udf_write_aext(table, nbloc, &nextoffset, eloc, elen, nbh, 1); + /* It's possible that stealing the block emptied the extent */ + if (elen) { + udf_write_aext(table, &epos, &eloc, elen, 1); - if (!nbh) - { - UDF_I_LENALLOC(table) += adsize; + if (!epos.bh) { + iinfo->i_lenAlloc += 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); + } else { + aed = (struct allocExtDesc *)epos.bh->b_data; + le32_add_cpu(&aed->lengthAllocDescs, adsize); + udf_update_tag(epos.bh->b_data, epos.offset); + mark_buffer_dirty(epos.bh); } } } - udf_release_data(nbh); - udf_release_data(obh); + brelse(epos.bh); + brelse(oepos.bh); error_return: - sb->s_dirt = 1; - up(&sbi->s_alloc_sem); + mutex_unlock(&sbi->s_alloc_mutex); return; } -static int udf_table_prealloc_blocks(struct super_block * sb, - struct inode * inode, - struct inode *table, uint16_t partition, uint32_t first_block, - uint32_t block_count) +static int udf_table_prealloc_blocks(struct super_block *sb, + struct inode *table, uint16_t partition, + uint32_t first_block, uint32_t block_count) { struct udf_sb_info *sbi = UDF_SB(sb); int alloc_count = 0; - uint32_t extoffset, elen, adsize; - kernel_lb_addr bloc, eloc; - struct buffer_head *bh; + uint32_t elen, adsize; + struct kernel_lb_addr eloc; + struct extent_position epos; int8_t etype = -1; + struct udf_inode_info *iinfo; - if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition)) + if (first_block >= sbi->s_partmaps[partition].s_partition_len) return 0; - if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); + iinfo = UDF_I(table); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); else return 0; - down(&sbi->s_alloc_sem); - extoffset = sizeof(struct unallocSpaceEntry); - bloc = UDF_I_LOCATION(table); - - bh = NULL; + mutex_lock(&sbi->s_alloc_mutex); + epos.offset = sizeof(struct unallocSpaceEntry); + epos.block = iinfo->i_location; + epos.bh = NULL; eloc.logicalBlockNum = 0xFFFFFFFF; - while (first_block != eloc.logicalBlockNum && (etype = - udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) - { + while (first_block != eloc.logicalBlockNum && + (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { udf_debug("eloc=%d, elen=%d, first_block=%d\n", - eloc.logicalBlockNum, elen, first_block); + eloc.logicalBlockNum, elen, first_block); ; /* empty loop body */ } - if (first_block == eloc.logicalBlockNum) - { - extoffset -= adsize; + if (first_block == eloc.logicalBlockNum) { + epos.offset -= adsize; alloc_count = (elen >> sb->s_blocksize_bits); - if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count)) - alloc_count = 0; - else if (alloc_count > block_count) - { + 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 + udf_write_aext(table, &epos, &eloc, + (etype << 30) | elen, 1); + } else + udf_delete_aext(table, epos, eloc, + (etype << 30) | elen); + } else { alloc_count = 0; + } - udf_release_data(bh); + brelse(epos.bh); - if (alloc_count && UDF_SB_LVIDBH(sb)) - { - UDF_SB_LVID(sb)->freeSpaceTable[partition] = - cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count); - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - sb->s_dirt = 1; - } - up(&sbi->s_alloc_sem); + if (alloc_count) + udf_add_free_space(sb, partition, -alloc_count); + mutex_unlock(&sbi->s_alloc_mutex); return alloc_count; } -static int udf_table_new_block(struct super_block * sb, - struct inode * inode, - struct inode *table, uint16_t partition, uint32_t goal, int *err) +static int udf_table_new_block(struct super_block *sb, + struct inode *table, uint16_t partition, + uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF; uint32_t newblock = 0, adsize; - uint32_t extoffset, goal_extoffset, elen, goal_elen = 0; - kernel_lb_addr bloc, goal_bloc, eloc, goal_eloc; - struct buffer_head *bh, *goal_bh; + uint32_t elen, goal_elen = 0; + struct kernel_lb_addr eloc, uninitialized_var(goal_eloc); + struct extent_position epos, goal_epos; int8_t etype; + struct udf_inode_info *iinfo = UDF_I(table); *err = -ENOSPC; - if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); else return newblock; - down(&sbi->s_alloc_sem); - if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition)) + mutex_lock(&sbi->s_alloc_mutex); + if (goal >= sbi->s_partmaps[partition].s_partition_len) goal = 0; - /* We search for the closest matching block to goal. If we find a exact hit, - we stop. Otherwise we keep going till we run out of extents. - We store the buffer_head, bloc, and extoffset of the current closest - match and use that when we are done. - */ - - extoffset = sizeof(struct unallocSpaceEntry); - bloc = UDF_I_LOCATION(table); - - goal_bh = bh = NULL; - - while (spread && (etype = - udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) - { - if (goal >= eloc.logicalBlockNum) - { - if (goal < eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) + /* We search for the closest matching block to goal. If we find + a exact hit, we stop. Otherwise we keep going till we run out + of extents. We store the buffer_head, bloc, and extoffset + of the current closest match and use that when we are done. + */ + epos.offset = sizeof(struct unallocSpaceEntry); + epos.block = iinfo->i_location; + epos.bh = goal_epos.bh = NULL; + + while (spread && + (etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { + if (goal >= eloc.logicalBlockNum) { + if (goal < eloc.logicalBlockNum + + (elen >> sb->s_blocksize_bits)) nspread = 0; else nspread = goal - eloc.logicalBlockNum - (elen >> sb->s_blocksize_bits); - } - else + } else { nspread = eloc.logicalBlockNum - goal; + } - if (nspread < spread) - { + if (nspread < spread) { spread = nspread; - if (goal_bh != bh) - { - udf_release_data(goal_bh); - goal_bh = bh; - atomic_inc(&goal_bh->b_count); + if (goal_epos.bh != epos.bh) { + brelse(goal_epos.bh); + goal_epos.bh = epos.bh; + get_bh(goal_epos.bh); } - goal_bloc = bloc; - goal_extoffset = extoffset - adsize; + goal_epos.block = epos.block; + goal_epos.offset = epos.offset - adsize; goal_eloc = eloc; goal_elen = (etype << 30) | elen; } } - udf_release_data(bh); + brelse(epos.bh); - if (spread == 0xFFFFFFFF) - { - udf_release_data(goal_bh); - up(&sbi->s_alloc_sem); + if (spread == 0xFFFFFFFF) { + brelse(goal_epos.bh); + mutex_unlock(&sbi->s_alloc_mutex); return 0; } @@ -827,133 +713,113 @@ static int udf_table_new_block(struct super_block * sb, /* This works, but very poorly.... */ newblock = goal_eloc.logicalBlockNum; - goal_eloc.logicalBlockNum ++; + goal_eloc.logicalBlockNum++; goal_elen -= sb->s_blocksize; - if (inode && DQUOT_ALLOC_BLOCK(inode, 1)) - { - udf_release_data(goal_bh); - up(&sbi->s_alloc_sem); - *err = -EDQUOT; - return 0; - } - if (goal_elen) - udf_write_aext(table, goal_bloc, &goal_extoffset, goal_eloc, goal_elen, goal_bh, 1); + udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 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)); - } + udf_delete_aext(table, goal_epos, goal_eloc, goal_elen); + brelse(goal_epos.bh); + + udf_add_free_space(sb, partition, -1); - sb->s_dirt = 1; - up(&sbi->s_alloc_sem); + mutex_unlock(&sbi->s_alloc_mutex); *err = 0; return newblock; } -inline void udf_free_blocks(struct super_block * sb, - struct inode * inode, - kernel_lb_addr bloc, uint32_t offset, uint32_t count) +void udf_free_blocks(struct super_block *sb, struct inode *inode, + struct kernel_lb_addr *bloc, uint32_t offset, + uint32_t count) { - uint16_t partition = bloc.partitionReferenceNum; - - if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) - { - return udf_bitmap_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, - bloc, offset, count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) - { - return udf_table_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, - bloc, offset, count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) - { - return udf_bitmap_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, - bloc, offset, count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) - { - return udf_table_free_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, - bloc, offset, count); + uint16_t partition = bloc->partitionReferenceNum; + struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; + + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) { + udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap, + bloc, offset, count); + } else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) { + udf_table_free_blocks(sb, map->s_uspace.s_table, + bloc, offset, count); + } else if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) { + udf_bitmap_free_blocks(sb, map->s_fspace.s_bitmap, + bloc, offset, count); + } else if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) { + udf_table_free_blocks(sb, map->s_fspace.s_table, + bloc, offset, count); + } + + if (inode) { + inode_sub_bytes(inode, + ((sector_t)count) << sb->s_blocksize_bits); } - else - return; } -inline int udf_prealloc_blocks(struct super_block * sb, - struct inode * inode, - uint16_t partition, uint32_t first_block, uint32_t block_count) +inline int udf_prealloc_blocks(struct super_block *sb, + struct inode *inode, + uint16_t partition, uint32_t first_block, + uint32_t block_count) { - if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) - { - return udf_bitmap_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, - partition, first_block, block_count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) - { - return udf_table_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, - partition, first_block, block_count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) - { - return udf_bitmap_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, - partition, first_block, block_count); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) - { - return udf_table_prealloc_blocks(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, - partition, first_block, block_count); - } + struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; + sector_t allocated; + + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) + allocated = udf_bitmap_prealloc_blocks(sb, + map->s_uspace.s_bitmap, + partition, first_block, + block_count); + else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) + allocated = udf_table_prealloc_blocks(sb, + map->s_uspace.s_table, + partition, first_block, + block_count); + else if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) + allocated = udf_bitmap_prealloc_blocks(sb, + map->s_fspace.s_bitmap, + partition, first_block, + block_count); + else if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) + allocated = udf_table_prealloc_blocks(sb, + map->s_fspace.s_table, + partition, first_block, + block_count); else return 0; + + if (inode && allocated > 0) + inode_add_bytes(inode, allocated << sb->s_blocksize_bits); + return allocated; } -inline int udf_new_block(struct super_block * sb, - struct inode * inode, - uint16_t partition, uint32_t goal, int *err) +inline int udf_new_block(struct super_block *sb, + struct inode *inode, + uint16_t partition, uint32_t goal, int *err) { - if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP) - { - return udf_bitmap_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap, - partition, goal, err); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE) - { - return udf_table_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table, - partition, goal, err); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP) - { - return udf_bitmap_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap, - partition, goal, err); - } - else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE) - { - return udf_table_new_block(sb, inode, - UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table, - partition, goal, err); - } - else - { + struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; + int block; + + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) + block = udf_bitmap_new_block(sb, + map->s_uspace.s_bitmap, + partition, goal, err); + else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) + block = udf_table_new_block(sb, + map->s_uspace.s_table, + partition, goal, err); + else if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) + block = udf_bitmap_new_block(sb, + map->s_fspace.s_bitmap, + partition, goal, err); + else if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) + block = udf_table_new_block(sb, + map->s_fspace.s_table, + partition, goal, err); + else { *err = -EIO; return 0; } + if (inode && block) + inode_add_bytes(inode, sb->s_blocksize); + return block; } diff --git a/fs/udf/crc.c b/fs/udf/crc.c deleted file mode 100644 index d95c6e38a45..00000000000 --- a/fs/udf/crc.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * crc.c - * - * PURPOSE - * Routines to generate, calculate, and test a 16-bit CRC. - * - * DESCRIPTION - * The CRC code was devised by Don P. Mitchell of AT&T Bell Laboratories - * and Ned W. Rhodes of Software Systems Group. It has been published in - * "Design and Validation of Computer Protocols", Prentice Hall, - * Englewood Cliffs, NJ, 1991, Chapter 3, ISBN 0-13-539925-4. - * - * Copyright is held by AT&T. - * - * AT&T gives permission for the free use of the CRC source code. - * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * - * COPYRIGHT - * This file is distributed under the terms of the GNU General Public - * License (GPL). Copies of the GPL can be obtained from: - * ftp://prep.ai.mit.edu/pub/gnu/GPL - * Each contributing author retains all rights to their own work. - */ - -#include "udfdecl.h" - -static uint16_t crc_table[256] = { - 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50a5U, 0x60c6U, 0x70e7U, - 0x8108U, 0x9129U, 0xa14aU, 0xb16bU, 0xc18cU, 0xd1adU, 0xe1ceU, 0xf1efU, - 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52b5U, 0x4294U, 0x72f7U, 0x62d6U, - 0x9339U, 0x8318U, 0xb37bU, 0xa35aU, 0xd3bdU, 0xc39cU, 0xf3ffU, 0xe3deU, - 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64e6U, 0x74c7U, 0x44a4U, 0x5485U, - 0xa56aU, 0xb54bU, 0x8528U, 0x9509U, 0xe5eeU, 0xf5cfU, 0xc5acU, 0xd58dU, - 0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76d7U, 0x66f6U, 0x5695U, 0x46b4U, - 0xb75bU, 0xa77aU, 0x9719U, 0x8738U, 0xf7dfU, 0xe7feU, 0xd79dU, 0xc7bcU, - 0x48c4U, 0x58e5U, 0x6886U, 0x78a7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U, - 0xc9ccU, 0xd9edU, 0xe98eU, 0xf9afU, 0x8948U, 0x9969U, 0xa90aU, 0xb92bU, - 0x5af5U, 0x4ad4U, 0x7ab7U, 0x6a96U, 0x1a71U, 0x0a50U, 0x3a33U, 0x2a12U, - 0xdbfdU, 0xcbdcU, 0xfbbfU, 0xeb9eU, 0x9b79U, 0x8b58U, 0xbb3bU, 0xab1aU, - 0x6ca6U, 0x7c87U, 0x4ce4U, 0x5cc5U, 0x2c22U, 0x3c03U, 0x0c60U, 0x1c41U, - 0xedaeU, 0xfd8fU, 0xcdecU, 0xddcdU, 0xad2aU, 0xbd0bU, 0x8d68U, 0x9d49U, - 0x7e97U, 0x6eb6U, 0x5ed5U, 0x4ef4U, 0x3e13U, 0x2e32U, 0x1e51U, 0x0e70U, - 0xff9fU, 0xefbeU, 0xdfddU, 0xcffcU, 0xbf1bU, 0xaf3aU, 0x9f59U, 0x8f78U, - 0x9188U, 0x81a9U, 0xb1caU, 0xa1ebU, 0xd10cU, 0xc12dU, 0xf14eU, 0xe16fU, - 0x1080U, 0x00a1U, 0x30c2U, 0x20e3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U, - 0x83b9U, 0x9398U, 0xa3fbU, 0xb3daU, 0xc33dU, 0xd31cU, 0xe37fU, 0xf35eU, - 0x02b1U, 0x1290U, 0x22f3U, 0x32d2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U, - 0xb5eaU, 0xa5cbU, 0x95a8U, 0x8589U, 0xf56eU, 0xe54fU, 0xd52cU, 0xc50dU, - 0x34e2U, 0x24c3U, 0x14a0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U, - 0xa7dbU, 0xb7faU, 0x8799U, 0x97b8U, 0xe75fU, 0xf77eU, 0xc71dU, 0xd73cU, - 0x26d3U, 0x36f2U, 0x0691U, 0x16b0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U, - 0xd94cU, 0xc96dU, 0xf90eU, 0xe92fU, 0x99c8U, 0x89e9U, 0xb98aU, 0xa9abU, - 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18c0U, 0x08e1U, 0x3882U, 0x28a3U, - 0xcb7dU, 0xdb5cU, 0xeb3fU, 0xfb1eU, 0x8bf9U, 0x9bd8U, 0xabbbU, 0xbb9aU, - 0x4a75U, 0x5a54U, 0x6a37U, 0x7a16U, 0x0af1U, 0x1ad0U, 0x2ab3U, 0x3a92U, - 0xfd2eU, 0xed0fU, 0xdd6cU, 0xcd4dU, 0xbdaaU, 0xad8bU, 0x9de8U, 0x8dc9U, - 0x7c26U, 0x6c07U, 0x5c64U, 0x4c45U, 0x3ca2U, 0x2c83U, 0x1ce0U, 0x0cc1U, - 0xef1fU, 0xff3eU, 0xcf5dU, 0xdf7cU, 0xaf9bU, 0xbfbaU, 0x8fd9U, 0x9ff8U, - 0x6e17U, 0x7e36U, 0x4e55U, 0x5e74U, 0x2e93U, 0x3eb2U, 0x0ed1U, 0x1ef0U -}; - -/* - * udf_crc - * - * PURPOSE - * Calculate a 16-bit CRC checksum using ITU-T V.41 polynomial. - * - * DESCRIPTION - * The OSTA-UDF(tm) 1.50 standard states that using CRCs is mandatory. - * The polynomial used is: x^16 + x^12 + x^15 + 1 - * - * PRE-CONDITIONS - * data Pointer to the data block. - * size Size of the data block. - * - * POST-CONDITIONS - * <return> CRC of the data block. - * - * HISTORY - * July 21, 1997 - Andrew E. Mileski - * Adapted from OSTA-UDF(tm) 1.50 standard. - */ -uint16_t -udf_crc(uint8_t *data, uint32_t size, uint16_t crc) -{ - while (size--) - crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8); - - return crc; -} - -/****************************************************************************/ -#if defined(TEST) - -/* - * PURPOSE - * Test udf_crc() - * - * HISTORY - * July 21, 1997 - Andrew E. Mileski - * Adapted from OSTA-UDF(tm) 1.50 standard. - */ - -unsigned char bytes[] = { 0x70U, 0x6AU, 0x77U }; - -int main(void) -{ - unsigned short x; - - x = udf_crc16(bytes, sizeof bytes); - printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U); - - return 0; -} - -#endif /* defined(TEST) */ - -/****************************************************************************/ -#if defined(GENERATE) - -/* - * PURPOSE - * Generate a table for fast 16-bit CRC calculations (any polynomial). - * - * DESCRIPTION - * The ITU-T V.41 polynomial is 010041. - * - * HISTORY - * July 21, 1997 - Andrew E. Mileski - * Adapted from OSTA-UDF(tm) 1.50 standard. - */ - -#include <stdio.h> - -int main(int argc, char **argv) -{ - unsigned long crc, poly; - int n, i; - - /* Get the polynomial */ - sscanf(argv[1], "%lo", &poly); - if (poly & 0xffff0000U){ - fprintf(stderr, "polynomial is too large\en"); - exit(1); - } - - printf("/* CRC 0%o */\n", poly); - - /* Create a table */ - printf("static unsigned short crc_table[256] = {\n"); - for (n = 0; n < 256; n++){ - if (n % 8 == 0) - printf("\t"); - crc = n << 8; - for (i = 0; i < 8; i++){ - if(crc & 0x8000U) - crc = (crc << 1) ^ poly; - else - crc <<= 1; - crc &= 0xFFFFU; - } - if (n == 255) - printf("0x%04xU ", crc); - else - printf("0x%04xU, ", crc); - if(n % 8 == 7) - printf("\n"); - } - printf("};\n"); - - return 0; -} - -#endif /* defined(GENERATE) */ diff --git a/fs/udf/dir.c b/fs/udf/dir.c index 82440b73114..a012c51caff 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -4,11 +4,6 @@ * PURPOSE * Directory handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -35,234 +30,170 @@ #include <linux/errno.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/buffer_head.h> #include "udf_i.h" #include "udf_sb.h" -/* Prototypes for file operations */ -static int udf_readdir(struct file *, void *, filldir_t); -static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *); - -/* readdir and lookup functions */ - -struct file_operations udf_dir_operations = { - .read = generic_read_dir, - .readdir = udf_readdir, - .ioctl = udf_ioctl, - .fsync = udf_fsync_file, -}; -/* - * udf_readdir - * - * PURPOSE - * Read a directory entry. - * - * DESCRIPTION - * Optional - sys_getdents() will return -ENOTDIR if this routine is not - * available. - * - * Refer to sys_getdents() in fs/readdir.c - * sys_getdents() -> . - * - * PRE-CONDITIONS - * filp Pointer to directory file. - * buf Pointer to directory entry buffer. - * filldir Pointer to filldir function. - * - * POST-CONDITIONS - * <return> >=0 on success. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ - -int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) +static int udf_readdir(struct file *file, struct dir_context *ctx) { - struct inode *dir = filp->f_dentry->d_inode; - int result; - - lock_kernel(); - - if ( filp->f_pos == 0 ) - { - if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0) - { - unlock_kernel(); - return 0; - } - filp->f_pos ++; - } - - result = do_udf_readdir(dir, filp, filldir, dirent); - unlock_kernel(); - return result; -} - -static int -do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent) -{ - struct udf_fileident_bh fibh; - struct fileIdentDesc *fi=NULL; + struct inode *dir = file_inode(file); + struct udf_inode_info *iinfo = UDF_I(dir); + struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL}; + struct fileIdentDesc *fi = NULL; struct fileIdentDesc cfi; int block, iblock; - loff_t nf_pos = filp->f_pos - 1; + loff_t nf_pos; int flen; - char fname[UDF_NAME_LEN]; - char *nameptr; + unsigned char *fname = NULL; + unsigned char *nameptr; uint16_t liu; uint8_t lfi; - loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; - struct buffer_head * bh = NULL, * tmp, * bha[16]; - kernel_lb_addr bloc, eloc; - uint32_t extoffset, elen, offset; - int i, num; - unsigned int dt_type; - + loff_t size = udf_ext0_offset(dir) + dir->i_size; + struct buffer_head *tmp, *bha[16]; + struct kernel_lb_addr eloc; + uint32_t elen; + sector_t offset; + int i, num, ret = 0; + struct extent_position epos = { NULL, 0, {0, 0} }; + + if (ctx->pos == 0) { + if (!dir_emit_dot(file, ctx)) + return 0; + ctx->pos = 1; + } + nf_pos = (ctx->pos - 1) << 2; if (nf_pos >= size) - return 0; + goto out; - if (nf_pos == 0) - nf_pos = (udf_ext0_offset(dir) >> 2); + fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); + if (!fname) { + ret = -ENOMEM; + goto out; + } - fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - fibh.sbh = fibh.ebh = NULL; - else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2), - &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) - { - offset >>= dir->i_sb->s_blocksize_bits; - block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) - extoffset -= sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) - extoffset -= sizeof(long_ad); + if (nf_pos == 0) + nf_pos = udf_ext0_offset(dir); + + fibh.soffset = fibh.eoffset = nf_pos & (dir->i_sb->s_blocksize - 1); + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + if (inode_bmap(dir, nf_pos >> dir->i_sb->s_blocksize_bits, + &epos, &eloc, &elen, &offset) + != (EXT_RECORDED_ALLOCATED >> 30)) { + ret = -ENOENT; + goto out; } - else + block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (iinfo->i_alloc_type == + ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + } else { offset = 0; + } - if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) - { - udf_release_data(bh); - return -EIO; + if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) { + ret = -EIO; + goto out; } - - if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) - { + + if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) { i = 16 >> (dir->i_sb->s_blocksize_bits - 9); - if (i+offset > (elen >> dir->i_sb->s_blocksize_bits)) - i = (elen >> dir->i_sb->s_blocksize_bits)-offset; - for (num=0; i>0; i--) - { - block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i); + if (i + offset > (elen >> dir->i_sb->s_blocksize_bits)) + i = (elen >> dir->i_sb->s_blocksize_bits) - offset; + for (num = 0; i > 0; i--) { + block = udf_get_lb_pblock(dir->i_sb, &eloc, offset + i); tmp = udf_tgetblk(dir->i_sb, block); if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } - if (num) - { + if (num) { ll_rw_block(READA, num, bha); - for (i=0; i<num; i++) + for (i = 0; i < num; i++) brelse(bha[i]); } } } - else - { - udf_release_data(bh); - return -ENOENT; - } - while ( nf_pos < size ) - { - filp->f_pos = nf_pos + 1; + while (nf_pos < size) { + struct kernel_lb_addr tloc; - fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); + ctx->pos = (nf_pos >> 2) + 1; + fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc, + &elen, &offset); if (!fi) - { - if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - udf_release_data(bh); - return 0; - } + goto out; liu = le16_to_cpu(cfi.lengthOfImpUse); lfi = cfi.lengthFileIdent; - if (fibh.sbh == fibh.ebh) + if (fibh.sbh == fibh.ebh) { nameptr = fi->fileIdent + liu; - else - { + } else { int poffset; /* Unpaded ending offset */ poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi; - if (poffset >= lfi) + if (poffset >= lfi) { nameptr = (char *)(fibh.ebh->b_data + poffset - lfi); - else - { + } else { nameptr = fname; - memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); - memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset); + memcpy(nameptr, fi->fileIdent + liu, + lfi - poffset); + memcpy(nameptr + lfi - poffset, + fibh.ebh->b_data, poffset); } } - if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) + if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE)) continue; } - - if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) + + if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE)) continue; } - if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT ) - { - iblock = parent_ino(filp->f_dentry); - flen = 2; - memcpy(fname, "..", flen); - dt_type = DT_DIR; + if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) { + if (!dir_emit_dotdot(file, ctx)) + goto out; + continue; } - else - { - kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation); - iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0); - flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); - dt_type = DT_UNKNOWN; - } + flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); + if (!flen) + continue; - if (flen) - { - if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0) - { - if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - udf_release_data(bh); - return 0; - } - } + tloc = lelb_to_cpu(cfi.icb.extLocation); + iblock = udf_get_lb_pblock(dir->i_sb, &tloc, 0); + if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN)) + goto out; } /* end while */ - filp->f_pos = nf_pos + 1; + ctx->pos = (nf_pos >> 2) + 1; +out: if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - udf_release_data(bh); + brelse(fibh.ebh); + brelse(fibh.sbh); + brelse(epos.bh); + kfree(fname); - return 0; + return ret; } + +/* readdir and lookup functions */ +const struct file_operations udf_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = udf_readdir, + .unlocked_ioctl = udf_ioctl, + .fsync = generic_file_fsync, +}; diff --git a/fs/udf/directory.c b/fs/udf/directory.c index 9a61ecc5451..3e44f575fb9 100644 --- a/fs/udf/directory.c +++ b/fs/udf/directory.c @@ -4,11 +4,6 @@ * PURPOSE * Directory related functions * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -23,233 +18,170 @@ #include <linux/string.h> #include <linux/buffer_head.h> -#if 0 -static uint8_t * -udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size, - kernel_lb_addr fe_loc, int *pos, int *offset, - struct buffer_head **bh, int *error) -{ - int loffset = *offset; - int block; - uint8_t *ad; - int remainder; - - *error = 0; - - ad = (uint8_t *)(*bh)->b_data + *offset; - *offset += ad_size; - - if (!ad) - { - udf_release_data(*bh); - *error = 1; - return NULL; - } - - if (*offset == dir->i_sb->s_blocksize) - { - udf_release_data(*bh); - block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); - if (!block) - return NULL; - if (!(*bh = udf_tread(dir->i_sb, block))) - return NULL; - } - else if (*offset > dir->i_sb->s_blocksize) - { - ad = tmpad; - - remainder = dir->i_sb->s_blocksize - loffset; - memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder); - - udf_release_data(*bh); - block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos); - if (!block) - return NULL; - if (!((*bh) = udf_tread(dir->i_sb, block))) - return NULL; - - memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder); - *offset = ad_size - remainder; - } - return ad; -} -#endif - -struct fileIdentDesc * -udf_fileident_read(struct inode *dir, loff_t *nf_pos, - struct udf_fileident_bh *fibh, - struct fileIdentDesc *cfi, - kernel_lb_addr *bloc, uint32_t *extoffset, - kernel_lb_addr *eloc, uint32_t *elen, - uint32_t *offset, struct buffer_head **bh) +struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos, + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi, + struct extent_position *epos, + struct kernel_lb_addr *eloc, uint32_t *elen, + sector_t *offset) { struct fileIdentDesc *fi; int i, num, block; - struct buffer_head * tmp, * bha[16]; + struct buffer_head *tmp, *bha[16]; + struct udf_inode_info *iinfo = UDF_I(dir); fibh->soffset = fibh->eoffset; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { - fi = udf_get_fileident(UDF_I_DATA(dir) - - (UDF_I_EFE(dir) ? - sizeof(struct extendedFileEntry) : - sizeof(struct fileEntry)), - dir->i_sb->s_blocksize, &(fibh->eoffset)); - + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + fi = udf_get_fileident(iinfo->i_ext.i_data - + (iinfo->i_efe ? + sizeof(struct extendedFileEntry) : + sizeof(struct fileEntry)), + dir->i_sb->s_blocksize, + &(fibh->eoffset)); if (!fi) return NULL; - *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); + *nf_pos += fibh->eoffset - fibh->soffset; - memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); + memcpy((uint8_t *)cfi, (uint8_t *)fi, + sizeof(struct fileIdentDesc)); return fi; } - if (fibh->eoffset == dir->i_sb->s_blocksize) - { - int lextoffset = *extoffset; + if (fibh->eoffset == dir->i_sb->s_blocksize) { + int lextoffset = epos->offset; + unsigned char blocksize_bits = dir->i_sb->s_blocksize_bits; - if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != - (EXT_RECORDED_ALLOCATED >> 30)) - { + if (udf_next_aext(dir, epos, eloc, elen, 1) != + (EXT_RECORDED_ALLOCATED >> 30)) return NULL; - } - block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); + block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); - (*offset) ++; + (*offset)++; - if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) + if ((*offset << blocksize_bits) >= *elen) *offset = 0; else - *extoffset = lextoffset; + epos->offset = lextoffset; - udf_release_data(fibh->sbh); - if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) + brelse(fibh->sbh); + fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); + if (!fibh->sbh) return NULL; fibh->soffset = fibh->eoffset = 0; - if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1))) - { - i = 16 >> (dir->i_sb->s_blocksize_bits - 9); - if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits)) - i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset; - for (num=0; i>0; i--) - { - block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i); + if (!(*offset & ((16 >> (blocksize_bits - 9)) - 1))) { + i = 16 >> (blocksize_bits - 9); + if (i + *offset > (*elen >> blocksize_bits)) + i = (*elen >> blocksize_bits)-*offset; + for (num = 0; i > 0; i--) { + block = udf_get_lb_pblock(dir->i_sb, eloc, + *offset + i); tmp = udf_tgetblk(dir->i_sb, block); - if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp)) + if (tmp && !buffer_uptodate(tmp) && + !buffer_locked(tmp)) bha[num++] = tmp; else brelse(tmp); } - if (num) - { + if (num) { ll_rw_block(READA, num, bha); - for (i=0; i<num; i++) + for (i = 0; i < num; i++) brelse(bha[i]); } } - } - else if (fibh->sbh != fibh->ebh) - { - udf_release_data(fibh->sbh); + } else if (fibh->sbh != fibh->ebh) { + brelse(fibh->sbh); fibh->sbh = fibh->ebh; } fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize, - &(fibh->eoffset)); + &(fibh->eoffset)); if (!fi) return NULL; - *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2); + *nf_pos += fibh->eoffset - fibh->soffset; - if (fibh->eoffset <= dir->i_sb->s_blocksize) - { - memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); - } - else if (fibh->eoffset > dir->i_sb->s_blocksize) - { - int lextoffset = *extoffset; + if (fibh->eoffset <= dir->i_sb->s_blocksize) { + memcpy((uint8_t *)cfi, (uint8_t *)fi, + sizeof(struct fileIdentDesc)); + } else if (fibh->eoffset > dir->i_sb->s_blocksize) { + int lextoffset = epos->offset; - if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) != - (EXT_RECORDED_ALLOCATED >> 30)) - { + if (udf_next_aext(dir, epos, eloc, elen, 1) != + (EXT_RECORDED_ALLOCATED >> 30)) return NULL; - } - block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset); + block = udf_get_lb_pblock(dir->i_sb, eloc, *offset); - (*offset) ++; + (*offset)++; if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen) *offset = 0; else - *extoffset = lextoffset; + epos->offset = lextoffset; fibh->soffset -= dir->i_sb->s_blocksize; fibh->eoffset -= dir->i_sb->s_blocksize; - if (!(fibh->ebh = udf_tread(dir->i_sb, block))) + fibh->ebh = udf_tread(dir->i_sb, block); + if (!fibh->ebh) return NULL; - if (sizeof(struct fileIdentDesc) > - fibh->soffset) - { + if (sizeof(struct fileIdentDesc) > -fibh->soffset) { int fi_len; - memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset); - memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data, - sizeof(struct fileIdentDesc) + fibh->soffset); + memcpy((uint8_t *)cfi, (uint8_t *)fi, -fibh->soffset); + memcpy((uint8_t *)cfi - fibh->soffset, + fibh->ebh->b_data, + sizeof(struct fileIdentDesc) + fibh->soffset); - fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent + - le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; + fi_len = (sizeof(struct fileIdentDesc) + + cfi->lengthFileIdent + + le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3; - *nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2); + *nf_pos += fi_len - (fibh->eoffset - fibh->soffset); fibh->eoffset = fibh->soffset + fi_len; - } - else - { - memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc)); + } else { + memcpy((uint8_t *)cfi, (uint8_t *)fi, + sizeof(struct fileIdentDesc)); } } return fi; } -struct fileIdentDesc * -udf_get_fileident(void * buffer, int bufsize, int * offset) +struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset) { struct fileIdentDesc *fi; int lengthThisIdent; - uint8_t * ptr; + uint8_t *ptr; int padlen; - if ( (!buffer) || (!offset) ) { - udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset); + if ((!buffer) || (!offset)) { + udf_debug("invalidparms, buffer=%p, offset=%p\n", + buffer, offset); return NULL; } ptr = buffer; - if ( (*offset > 0) && (*offset < bufsize) ) { + if ((*offset > 0) && (*offset < bufsize)) ptr += *offset; - } - fi=(struct fileIdentDesc *)ptr; - if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID) - { + fi = (struct fileIdentDesc *)ptr; + if (fi->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) { udf_debug("0x%x != TAG_IDENT_FID\n", - le16_to_cpu(fi->descTag.tagIdent)); + le16_to_cpu(fi->descTag.tagIdent)); udf_debug("offset: %u sizeof: %lu bufsize: %u\n", - *offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize); + *offset, (unsigned long)sizeof(struct fileIdentDesc), + bufsize); return NULL; } - if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize ) - { + if ((*offset + sizeof(struct fileIdentDesc)) > bufsize) lengthThisIdent = sizeof(struct fileIdentDesc); - } else lengthThisIdent = sizeof(struct fileIdentDesc) + fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse); @@ -263,81 +195,47 @@ udf_get_fileident(void * buffer, int bufsize, int * offset) return fi; } -#if 0 -static extent_ad * -udf_get_fileextent(void * buffer, int bufsize, int * offset) +struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset, + int inc) { - extent_ad * ext; - struct fileEntry *fe; - uint8_t * ptr; - - if ( (!buffer) || (!offset) ) - { - printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n"); - return NULL; - } - - fe = (struct fileEntry *)buffer; + struct short_ad *sa; - if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE ) - { - udf_debug("0x%x != TAG_IDENT_FE\n", - le16_to_cpu(fe->descTag.tagIdent)); + if ((!ptr) || (!offset)) { + pr_err("%s: invalidparms\n", __func__); return NULL; } - ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr); - - if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) ) - { - ptr += *offset; - } - - ext = (extent_ad *)ptr; - - *offset = *offset + sizeof(extent_ad); - return ext; -} -#endif - -short_ad * -udf_get_fileshortad(uint8_t *ptr, int maxoffset, int *offset, int inc) -{ - short_ad *sa; - - if ( (!ptr) || (!offset) ) - { - printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n"); + if ((*offset + sizeof(struct short_ad)) > maxoffset) return NULL; + else { + sa = (struct short_ad *)ptr; + if (sa->extLength == 0) + return NULL; } - if ( (*offset < 0) || ((*offset + sizeof(short_ad)) > maxoffset) ) - return NULL; - else if ((sa = (short_ad *)ptr)->extLength == 0) - return NULL; - if (inc) - *offset += sizeof(short_ad); + *offset += sizeof(struct short_ad); return sa; } -long_ad * -udf_get_filelongad(uint8_t *ptr, int maxoffset, int * offset, int inc) +struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc) { - long_ad *la; + struct long_ad *la; - if ( (!ptr) || (!offset) ) - { - printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n"); + if ((!ptr) || (!offset)) { + pr_err("%s: invalidparms\n", __func__); return NULL; } - if ( (*offset < 0) || ((*offset + sizeof(long_ad)) > maxoffset) ) - return NULL; - else if ((la = (long_ad *)ptr)->extLength == 0) + if ((*offset + sizeof(struct long_ad)) > maxoffset) return NULL; + else { + la = (struct long_ad *)ptr; + if (la->extLength == 0) + return NULL; + } if (inc) - *offset += sizeof(long_ad); + *offset += sizeof(struct long_ad); return la; } diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h index f81f2ebbf50..4792b771aa8 100644 --- a/fs/udf/ecma_167.h +++ b/fs/udf/ecma_167.h @@ -38,11 +38,10 @@ #define _ECMA_167_H 1 /* Character set specification (ECMA 167r3 1/7.2.1) */ -typedef struct -{ +struct charspec { uint8_t charSetType; uint8_t charSetInfo[63]; -} __attribute__ ((packed)) charspec; +} __attribute__ ((packed)); /* Character Set Type (ECMA 167r3 1/7.2.1.1) */ #define CHARSPEC_TYPE_CS0 0x00 /* (1/7.2.2) */ @@ -58,8 +57,7 @@ typedef struct typedef uint8_t dstring; /* Timestamp (ECMA 167r3 1/7.3) */ -typedef struct -{ +struct timestamp { __le16 typeAndTimezone; __le16 year; uint8_t month; @@ -70,21 +68,7 @@ typedef struct uint8_t centiseconds; uint8_t hundredsOfMicroseconds; uint8_t microseconds; -} __attribute__ ((packed)) timestamp; - -typedef struct -{ - uint16_t typeAndTimezone; - int16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; - uint8_t centiseconds; - uint8_t hundredsOfMicroseconds; - uint8_t microseconds; -} __attribute__ ((packed)) kernel_timestamp; +} __attribute__ ((packed)); /* Type and Time Zone (ECMA 167r3 1/7.3.1) */ #define TIMESTAMP_TYPE_MASK 0xF000 @@ -94,12 +78,11 @@ typedef struct #define TIMESTAMP_TIMEZONE_MASK 0x0FFF /* Entity identifier (ECMA 167r3 1/7.4) */ -typedef struct -{ +struct regid { uint8_t flags; uint8_t ident[23]; uint8_t identSuffix[8]; -} __attribute__ ((packed)) regid; +} __attribute__ ((packed)); /* Flags (ECMA 167r3 1/7.4.1) */ #define ENTITYID_FLAGS_DIRTY 0x00 @@ -107,8 +90,7 @@ typedef struct /* Volume Structure Descriptor (ECMA 167r3 2/9.1) */ #define VSD_STD_ID_LEN 5 -struct volStructDesc -{ +struct volStructDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -127,8 +109,7 @@ struct volStructDesc #define VSD_STD_ID_TEA01 "TEA01" /* (2/9.3) */ /* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */ -struct beginningExtendedAreaDesc -{ +struct beginningExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -136,8 +117,7 @@ struct beginningExtendedAreaDesc } __attribute__ ((packed)); /* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */ -struct terminatingExtendedAreaDesc -{ +struct terminatingExtendedAreaDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; @@ -145,43 +125,39 @@ struct terminatingExtendedAreaDesc } __attribute__ ((packed)); /* Boot Descriptor (ECMA 167r3 2/9.4) */ -struct bootDesc -{ - uint8_t structType; - uint8_t stdIdent[VSD_STD_ID_LEN]; - uint8_t structVersion; - uint8_t reserved1; - regid archType; - regid bootIdent; - __le32 bootExtLocation; - __le32 bootExtLength; - __le64 loadAddress; - __le64 startAddress; - timestamp descCreationDateAndTime; - __le16 flags; - uint8_t reserved2[32]; - uint8_t bootUse[1906]; +struct bootDesc { + uint8_t structType; + uint8_t stdIdent[VSD_STD_ID_LEN]; + uint8_t structVersion; + uint8_t reserved1; + struct regid archType; + struct regid bootIdent; + __le32 bootExtLocation; + __le32 bootExtLength; + __le64 loadAddress; + __le64 startAddress; + struct timestamp descCreationDateAndTime; + __le16 flags; + uint8_t reserved2[32]; + uint8_t bootUse[1906]; } __attribute__ ((packed)); /* Flags (ECMA 167r3 2/9.4.12) */ #define BOOT_FLAGS_ERASE 0x01 /* Extent Descriptor (ECMA 167r3 3/7.1) */ -typedef struct -{ +struct extent_ad { __le32 extLength; __le32 extLocation; -} __attribute__ ((packed)) extent_ad; +} __attribute__ ((packed)); -typedef struct -{ +struct kernel_extent_ad { uint32_t extLength; uint32_t extLocation; -} kernel_extent_ad; +}; /* Descriptor Tag (ECMA 167r3 3/7.2) */ -typedef struct -{ +struct tag { __le16 tagIdent; __le16 descVersion; uint8_t tagChecksum; @@ -190,7 +166,7 @@ typedef struct __le16 descCRC; __le16 descCRCLength; __le32 tagLocation; -} __attribute__ ((packed)) tag; +} __attribute__ ((packed)); /* Tag Identifier (ECMA 167r3 3/7.2.1) */ #define TAG_IDENT_PVD 0x0001 @@ -204,87 +180,81 @@ typedef struct #define TAG_IDENT_LVID 0x0009 /* NSR Descriptor (ECMA 167r3 3/9.1) */ -struct NSRDesc -{ +struct NSRDesc { uint8_t structType; uint8_t stdIdent[VSD_STD_ID_LEN]; uint8_t structVersion; uint8_t reserved; uint8_t structData[2040]; } __attribute__ ((packed)); - + /* Primary Volume Descriptor (ECMA 167r3 3/10.1) */ -struct primaryVolDesc -{ - tag descTag; - __le32 volDescSeqNum; - __le32 primaryVolDescNum; - dstring volIdent[32]; - __le16 volSeqNum; - __le16 maxVolSeqNum; - __le16 interchangeLvl; - __le16 maxInterchangeLvl; - __le32 charSetList; - __le32 maxCharSetList; - dstring volSetIdent[128]; - charspec descCharSet; - charspec explanatoryCharSet; - extent_ad volAbstract; - extent_ad volCopyright; - regid appIdent; - timestamp recordingDateAndTime; - regid impIdent; - uint8_t impUse[64]; - __le32 predecessorVolDescSeqLocation; - __le16 flags; - uint8_t reserved[22]; +struct primaryVolDesc { + struct tag descTag; + __le32 volDescSeqNum; + __le32 primaryVolDescNum; + dstring volIdent[32]; + __le16 volSeqNum; + __le16 maxVolSeqNum; + __le16 interchangeLvl; + __le16 maxInterchangeLvl; + __le32 charSetList; + __le32 maxCharSetList; + dstring volSetIdent[128]; + struct charspec descCharSet; + struct charspec explanatoryCharSet; + struct extent_ad volAbstract; + struct extent_ad volCopyright; + struct regid appIdent; + struct timestamp recordingDateAndTime; + struct regid impIdent; + uint8_t impUse[64]; + __le32 predecessorVolDescSeqLocation; + __le16 flags; + uint8_t reserved[22]; } __attribute__ ((packed)); /* Flags (ECMA 167r3 3/10.1.21) */ #define PVD_FLAGS_VSID_COMMON 0x0001 /* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */ -struct anchorVolDescPtr -{ - tag descTag; - extent_ad mainVolDescSeqExt; - extent_ad reserveVolDescSeqExt; - uint8_t reserved[480]; +struct anchorVolDescPtr { + struct tag descTag; + struct extent_ad mainVolDescSeqExt; + struct extent_ad reserveVolDescSeqExt; + uint8_t reserved[480]; } __attribute__ ((packed)); /* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */ -struct volDescPtr -{ - tag descTag; - __le32 volDescSeqNum; - extent_ad nextVolDescSeqExt; - uint8_t reserved[484]; +struct volDescPtr { + struct tag descTag; + __le32 volDescSeqNum; + struct extent_ad nextVolDescSeqExt; + uint8_t reserved[484]; } __attribute__ ((packed)); /* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */ -struct impUseVolDesc -{ - tag descTag; +struct impUseVolDesc { + struct tag descTag; __le32 volDescSeqNum; - regid impIdent; + struct regid impIdent; uint8_t impUse[460]; } __attribute__ ((packed)); /* Partition Descriptor (ECMA 167r3 3/10.5) */ -struct partitionDesc -{ - tag descTag; - __le32 volDescSeqNum; - __le16 partitionFlags; - __le16 partitionNumber; - regid partitionContents; - uint8_t partitionContentsUse[128]; - __le32 accessType; - __le32 partitionStartingLocation; - __le32 partitionLength; - regid impIdent; - uint8_t impUse[128]; - uint8_t reserved[156]; +struct partitionDesc { + struct tag descTag; + __le32 volDescSeqNum; + __le16 partitionFlags; + __le16 partitionNumber; + struct regid partitionContents; + uint8_t partitionContentsUse[128]; + __le32 accessType; + __le32 partitionStartingLocation; + __le32 partitionLength; + struct regid impIdent; + uint8_t impUse[128]; + uint8_t reserved[156]; } __attribute__ ((packed)); /* Partition Flags (ECMA 167r3 3/10.5.3) */ @@ -307,26 +277,24 @@ struct partitionDesc #define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004 /* Logical Volume Descriptor (ECMA 167r3 3/10.6) */ -struct logicalVolDesc -{ - tag descTag; - __le32 volDescSeqNum; - charspec descCharSet; - dstring logicalVolIdent[128]; - __le32 logicalBlockSize; - regid domainIdent; - uint8_t logicalVolContentsUse[16]; - __le32 mapTableLength; - __le32 numPartitionMaps; - regid impIdent; - uint8_t impUse[128]; - extent_ad integritySeqExt; - uint8_t partitionMaps[0]; +struct logicalVolDesc { + struct tag descTag; + __le32 volDescSeqNum; + struct charspec descCharSet; + dstring logicalVolIdent[128]; + __le32 logicalBlockSize; + struct regid domainIdent; + uint8_t logicalVolContentsUse[16]; + __le32 mapTableLength; + __le32 numPartitionMaps; + struct regid impIdent; + uint8_t impUse[128]; + struct extent_ad integritySeqExt; + uint8_t partitionMaps[0]; } __attribute__ ((packed)); /* Generic Partition Map (ECMA 167r3 3/10.7.1) */ -struct genericPartitionMap -{ +struct genericPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t partitionMapping[0]; @@ -338,8 +306,7 @@ struct genericPartitionMap #define GP_PARTITION_MAP_TYPE_2 0x02 /* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */ -struct genericPartitionMap1 -{ +struct genericPartitionMap1 { uint8_t partitionMapType; uint8_t partitionMapLength; __le16 volSeqNum; @@ -347,42 +314,38 @@ struct genericPartitionMap1 } __attribute__ ((packed)); /* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */ -struct genericPartitionMap2 -{ +struct genericPartitionMap2 { uint8_t partitionMapType; - uint8_t partitionMapLength; + uint8_t partitionMapLength; uint8_t partitionIdent[62]; } __attribute__ ((packed)); /* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */ -struct unallocSpaceDesc -{ - tag descTag; - __le32 volDescSeqNum; - __le32 numAllocDescs; - extent_ad allocDescs[0]; +struct unallocSpaceDesc { + struct tag descTag; + __le32 volDescSeqNum; + __le32 numAllocDescs; + struct extent_ad allocDescs[0]; } __attribute__ ((packed)); /* Terminating Descriptor (ECMA 167r3 3/10.9) */ -struct terminatingDesc -{ - tag descTag; +struct terminatingDesc { + struct tag descTag; uint8_t reserved[496]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */ -struct logicalVolIntegrityDesc -{ - tag descTag; - timestamp recordingDateAndTime; - __le32 integrityType; - extent_ad nextIntegrityExt; - uint8_t logicalVolContentsUse[32]; - __le32 numOfPartitions; - __le32 lengthOfImpUse; - __le32 freeSpaceTable[0]; - __le32 sizeTable[0]; - uint8_t impUse[0]; +struct logicalVolIntegrityDesc { + struct tag descTag; + struct timestamp recordingDateAndTime; + __le32 integrityType; + struct extent_ad nextIntegrityExt; + uint8_t logicalVolContentsUse[32]; + __le32 numOfPartitions; + __le32 lengthOfImpUse; + __le32 freeSpaceTable[0]; + __le32 sizeTable[0]; + uint8_t impUse[0]; } __attribute__ ((packed)); /* Integrity Type (ECMA 167r3 3/10.10.3) */ @@ -390,57 +353,50 @@ struct logicalVolIntegrityDesc #define LVID_INTEGRITY_TYPE_CLOSE 0x00000001 /* Recorded Address (ECMA 167r3 4/7.1) */ -typedef struct -{ +struct lb_addr { __le32 logicalBlockNum; __le16 partitionReferenceNum; -} __attribute__ ((packed)) lb_addr; +} __attribute__ ((packed)); /* ... and its in-core analog */ -typedef struct -{ +struct kernel_lb_addr { uint32_t logicalBlockNum; uint16_t partitionReferenceNum; -} kernel_lb_addr; +}; /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ -typedef struct -{ +struct short_ad { __le32 extLength; __le32 extPosition; -} __attribute__ ((packed)) short_ad; +} __attribute__ ((packed)); /* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */ -typedef struct -{ +struct long_ad { __le32 extLength; - lb_addr extLocation; + struct lb_addr extLocation; uint8_t impUse[6]; -} __attribute__ ((packed)) long_ad; +} __attribute__ ((packed)); -typedef struct -{ - uint32_t extLength; - kernel_lb_addr extLocation; - uint8_t impUse[6]; -} kernel_long_ad; +struct kernel_long_ad { + uint32_t extLength; + struct kernel_lb_addr extLocation; + uint8_t impUse[6]; +}; /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ -typedef struct -{ +struct ext_ad { __le32 extLength; __le32 recordedLength; __le32 informationLength; - lb_addr extLocation; -} __attribute__ ((packed)) ext_ad; + struct lb_addr extLocation; +} __attribute__ ((packed)); -typedef struct -{ - uint32_t extLength; - uint32_t recordedLength; - uint32_t informationLength; - kernel_lb_addr extLocation; -} kernel_ext_ad; +struct kernel_ext_ad { + uint32_t extLength; + uint32_t recordedLength; + uint32_t informationLength; + struct kernel_lb_addr extLocation; +}; /* Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */ @@ -458,48 +414,45 @@ typedef struct #define TAG_IDENT_EFE 0x010A /* File Set Descriptor (ECMA 167r3 4/14.1) */ -struct fileSetDesc -{ - tag descTag; - timestamp recordingDateAndTime; - __le16 interchangeLvl; - __le16 maxInterchangeLvl; - __le32 charSetList; - __le32 maxCharSetList; - __le32 fileSetNum; - __le32 fileSetDescNum; - charspec logicalVolIdentCharSet; - dstring logicalVolIdent[128]; - charspec fileSetCharSet; - dstring fileSetIdent[32]; - dstring copyrightFileIdent[32]; - dstring abstractFileIdent[32]; - long_ad rootDirectoryICB; - regid domainIdent; - long_ad nextExt; - long_ad streamDirectoryICB; - uint8_t reserved[32]; +struct fileSetDesc { + struct tag descTag; + struct timestamp recordingDateAndTime; + __le16 interchangeLvl; + __le16 maxInterchangeLvl; + __le32 charSetList; + __le32 maxCharSetList; + __le32 fileSetNum; + __le32 fileSetDescNum; + struct charspec logicalVolIdentCharSet; + dstring logicalVolIdent[128]; + struct charspec fileSetCharSet; + dstring fileSetIdent[32]; + dstring copyrightFileIdent[32]; + dstring abstractFileIdent[32]; + struct long_ad rootDirectoryICB; + struct regid domainIdent; + struct long_ad nextExt; + struct long_ad streamDirectoryICB; + uint8_t reserved[32]; } __attribute__ ((packed)); /* Partition Header Descriptor (ECMA 167r3 4/14.3) */ -struct partitionHeaderDesc -{ - short_ad unallocSpaceTable; - short_ad unallocSpaceBitmap; - short_ad partitionIntegrityTable; - short_ad freedSpaceTable; - short_ad freedSpaceBitmap; +struct partitionHeaderDesc { + struct short_ad unallocSpaceTable; + struct short_ad unallocSpaceBitmap; + struct short_ad partitionIntegrityTable; + struct short_ad freedSpaceTable; + struct short_ad freedSpaceBitmap; uint8_t reserved[88]; } __attribute__ ((packed)); /* File Identifier Descriptor (ECMA 167r3 4/14.4) */ -struct fileIdentDesc -{ - tag descTag; +struct fileIdentDesc { + struct tag descTag; __le16 fileVersionNum; uint8_t fileCharacteristics; uint8_t lengthFileIdent; - long_ad icb; + struct long_ad icb; __le16 lengthOfImpUse; uint8_t impUse[0]; uint8_t fileIdent[0]; @@ -514,25 +467,23 @@ struct fileIdentDesc #define FID_FILE_CHAR_METADATA 0x10 /* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */ -struct allocExtDesc -{ - tag descTag; +struct allocExtDesc { + struct tag descTag; __le32 previousAllocExtLocation; __le32 lengthAllocDescs; } __attribute__ ((packed)); /* ICB Tag (ECMA 167r3 4/14.6) */ -typedef struct -{ +struct icbtag { __le32 priorRecordedNumDirectEntries; __le16 strategyType; __le16 strategyParameter; __le16 numEntries; uint8_t reserved; uint8_t fileType; - lb_addr parentICBLocation; + struct lb_addr parentICBLocation; __le16 flags; -} __attribute__ ((packed)) icbtag; +} __attribute__ ((packed)); /* Strategy Type (ECMA 167r3 4/14.6.2) */ #define ICBTAG_STRATEGY_TYPE_UNDEF 0x0000 @@ -576,45 +527,42 @@ typedef struct #define ICBTAG_FLAG_STREAM 0x2000 /* Indirect Entry (ECMA 167r3 4/14.7) */ -struct indirectEntry -{ - tag descTag; - icbtag icbTag; - long_ad indirectICB; +struct indirectEntry { + struct tag descTag; + struct icbtag icbTag; + struct long_ad indirectICB; } __attribute__ ((packed)); /* Terminal Entry (ECMA 167r3 4/14.8) */ -struct terminalEntry -{ - tag descTag; - icbtag icbTag; +struct terminalEntry { + struct tag descTag; + struct icbtag icbTag; } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.9) */ -struct fileEntry -{ - tag descTag; - icbtag icbTag; - __le32 uid; - __le32 gid; - __le32 permissions; - __le16 fileLinkCount; - uint8_t recordFormat; - uint8_t recordDisplayAttr; - __le32 recordLength; - __le64 informationLength; - __le64 logicalBlocksRecorded; - timestamp accessTime; - timestamp modificationTime; - timestamp attrTime; - __le32 checkpoint; - long_ad extendedAttrICB; - regid impIdent; - __le64 uniqueID; - __le32 lengthExtendedAttr; - __le32 lengthAllocDescs; - uint8_t extendedAttr[0]; - uint8_t allocDescs[0]; +struct fileEntry { + struct tag descTag; + struct icbtag icbTag; + __le32 uid; + __le32 gid; + __le32 permissions; + __le16 fileLinkCount; + uint8_t recordFormat; + uint8_t recordDisplayAttr; + __le32 recordLength; + __le64 informationLength; + __le64 logicalBlocksRecorded; + struct timestamp accessTime; + struct timestamp modificationTime; + struct timestamp attrTime; + __le32 checkpoint; + struct long_ad extendedAttrICB; + struct regid impIdent; + __le64 uniqueID; + __le32 lengthExtendedAttr; + __le32 lengthAllocDescs; + uint8_t extendedAttr[0]; + uint8_t allocDescs[0]; } __attribute__ ((packed)); /* Permissions (ECMA 167r3 4/14.9.5) */ @@ -655,16 +603,14 @@ struct fileEntry #define FE_RECORD_DISPLAY_ATTR_3 0x03 /* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */ -struct extendedAttrHeaderDesc -{ - tag descTag; +struct extendedAttrHeaderDesc { + struct tag descTag; __le32 impAttrLocation; __le32 appAttrLocation; } __attribute__ ((packed)); /* Generic Format (ECMA 167r3 4/14.10.2) */ -struct genericFormat -{ +struct genericFormat { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -673,8 +619,7 @@ struct genericFormat } __attribute__ ((packed)); /* Character Set Information (ECMA 167r3 4/14.10.3) */ -struct charSetInfo -{ +struct charSetInfo { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -685,8 +630,7 @@ struct charSetInfo } __attribute__ ((packed)); /* Alternate Permissions (ECMA 167r3 4/14.10.4) */ -struct altPerms -{ +struct altPerms { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -697,8 +641,7 @@ struct altPerms } __attribute__ ((packed)); /* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */ -struct fileTimesExtAttr -{ +struct fileTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -715,8 +658,7 @@ struct fileTimesExtAttr #define FTE_BACKUP 0x00000002 /* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */ -struct infoTimesExtAttr -{ +struct infoTimesExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -727,8 +669,7 @@ struct infoTimesExtAttr } __attribute__ ((packed)); /* Device Specification (ECMA 167r3 4/14.10.7) */ -struct deviceSpec -{ +struct deviceSpec { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; @@ -740,26 +681,24 @@ struct deviceSpec } __attribute__ ((packed)); /* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */ -struct impUseExtAttr -{ +struct impUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 impUseLength; - regid impIdent; + struct regid impIdent; uint8_t impUse[0]; } __attribute__ ((packed)); /* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */ -struct appUseExtAttr -{ +struct appUseExtAttr { __le32 attrType; uint8_t attrSubtype; uint8_t reserved[3]; __le32 attrLength; __le32 appUseLength; - regid appIdent; + struct regid appIdent; uint8_t appUse[0]; } __attribute__ ((packed)); @@ -771,35 +710,31 @@ struct appUseExtAttr #define EXTATTR_IMP_USE 2048 #define EXTATTR_APP_USE 65536 - /* Unallocated Space Entry (ECMA 167r3 4/14.11) */ -struct unallocSpaceEntry -{ - tag descTag; - icbtag icbTag; +struct unallocSpaceEntry { + struct tag descTag; + struct icbtag icbTag; __le32 lengthAllocDescs; uint8_t allocDescs[0]; } __attribute__ ((packed)); /* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */ -struct spaceBitmapDesc -{ - tag descTag; +struct spaceBitmapDesc { + struct tag descTag; __le32 numOfBits; __le32 numOfBytes; uint8_t bitmap[0]; } __attribute__ ((packed)); /* Partition Integrity Entry (ECMA 167r3 4/14.13) */ -struct partitionIntegrityEntry -{ - tag descTag; - icbtag icbTag; - timestamp recordingDateAndTime; - uint8_t integrityType; - uint8_t reserved[175]; - regid impIdent; - uint8_t impUse[256]; +struct partitionIntegrityEntry { + struct tag descTag; + struct icbtag icbTag; + struct timestamp recordingDateAndTime; + uint8_t integrityType; + uint8_t reserved[175]; + struct regid impIdent; + uint8_t impUse[256]; } __attribute__ ((packed)); /* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */ @@ -815,15 +750,13 @@ struct partitionIntegrityEntry /* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */ /* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */ -struct logicalVolHeaderDesc -{ +struct logicalVolHeaderDesc { __le64 uniqueID; uint8_t reserved[24]; } __attribute__ ((packed)); /* Path Component (ECMA 167r3 4/14.16.1) */ -struct pathComponent -{ +struct pathComponent { uint8_t componentType; uint8_t lengthComponentIdent; __le16 componentFileVersionNum; @@ -831,34 +764,33 @@ struct pathComponent } __attribute__ ((packed)); /* File Entry (ECMA 167r3 4/14.17) */ -struct extendedFileEntry -{ - tag descTag; - icbtag icbTag; - __le32 uid; - __le32 gid; - __le32 permissions; - __le16 fileLinkCount; - uint8_t recordFormat; - uint8_t recordDisplayAttr; - __le32 recordLength; - __le64 informationLength; - __le64 objectSize; - __le64 logicalBlocksRecorded; - timestamp accessTime; - timestamp modificationTime; - timestamp createTime; - timestamp attrTime; - __le32 checkpoint; - __le32 reserved; - long_ad extendedAttrICB; - long_ad streamDirectoryICB; - regid impIdent; - __le64 uniqueID; - __le32 lengthExtendedAttr; - __le32 lengthAllocDescs; - uint8_t extendedAttr[0]; - uint8_t allocDescs[0]; +struct extendedFileEntry { + struct tag descTag; + struct icbtag icbTag; + __le32 uid; + __le32 gid; + __le32 permissions; + __le16 fileLinkCount; + uint8_t recordFormat; + uint8_t recordDisplayAttr; + __le32 recordLength; + __le64 informationLength; + __le64 objectSize; + __le64 logicalBlocksRecorded; + struct timestamp accessTime; + struct timestamp modificationTime; + struct timestamp createTime; + struct timestamp attrTime; + __le32 checkpoint; + __le32 reserved; + struct long_ad extendedAttrICB; + struct long_ad streamDirectoryICB; + struct regid impIdent; + __le64 uniqueID; + __le32 lengthExtendedAttr; + __le32 lengthAllocDescs; + uint8_t extendedAttr[0]; + uint8_t allocDescs[0]; } __attribute__ ((packed)); #endif /* _ECMA_167_H */ diff --git a/fs/udf/file.c b/fs/udf/file.c index bb40d63f328..d80738fdf42 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -4,11 +4,6 @@ * PURPOSE * File handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -32,237 +27,264 @@ #include "udfdecl.h" #include <linux/fs.h> -#include <linux/udf_fs.h> #include <asm/uaccess.h> #include <linux/kernel.h> #include <linux/string.h> /* memset */ +#include <linux/capability.h> #include <linux/errno.h> -#include <linux/smp_lock.h> #include <linux/pagemap.h> #include <linux/buffer_head.h> +#include <linux/aio.h> #include "udf_i.h" #include "udf_sb.h" -static int udf_adinicb_readpage(struct file *file, struct page * page) +static void __udf_adinicb_readpage(struct page *page) { struct inode *inode = page->mapping->host; char *kaddr; - - BUG_ON(!PageLocked(page)); + struct udf_inode_info *iinfo = UDF_I(inode); kaddr = kmap(page); - memset(kaddr, 0, PAGE_CACHE_SIZE); - memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), inode->i_size); + memcpy(kaddr, iinfo->i_ext.i_data + iinfo->i_lenEAttr, inode->i_size); + memset(kaddr + inode->i_size, 0, PAGE_CACHE_SIZE - inode->i_size); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); +} + +static int udf_adinicb_readpage(struct file *file, struct page *page) +{ + BUG_ON(!PageLocked(page)); + __udf_adinicb_readpage(page); unlock_page(page); + return 0; } -static int udf_adinicb_writepage(struct page *page, struct writeback_control *wbc) +static int udf_adinicb_writepage(struct page *page, + struct writeback_control *wbc) { struct inode *inode = page->mapping->host; char *kaddr; + struct udf_inode_info *iinfo = UDF_I(inode); BUG_ON(!PageLocked(page)); kaddr = kmap(page); - memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), kaddr, inode->i_size); + memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr, kaddr, inode->i_size); mark_inode_dirty(inode); SetPageUptodate(page); kunmap(page); unlock_page(page); + return 0; } -static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to) +static int udf_adinicb_write_begin(struct file *file, + struct address_space *mapping, loff_t pos, + unsigned len, unsigned flags, struct page **pagep, + void **fsdata) { - kmap(page); + struct page *page; + + if (WARN_ON_ONCE(pos >= PAGE_CACHE_SIZE)) + return -EIO; + page = grab_cache_page_write_begin(mapping, 0, flags); + if (!page) + return -ENOMEM; + *pagep = page; + + if (!PageUptodate(page) && len != PAGE_CACHE_SIZE) + __udf_adinicb_readpage(page); return 0; } -static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) +static int udf_adinicb_write_end(struct file *file, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) { - struct inode *inode = page->mapping->host; - char *kaddr = page_address(page); + struct inode *inode = mapping->host; + unsigned offset = pos & (PAGE_CACHE_SIZE - 1); + char *kaddr; + struct udf_inode_info *iinfo = UDF_I(inode); - memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, - kaddr + offset, to - offset); - mark_inode_dirty(inode); - SetPageUptodate(page); - kunmap(page); - /* only one page here */ - if (to > inode->i_size) - inode->i_size = to; + kaddr = kmap_atomic(page); + memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr + offset, + kaddr + offset, copied); + kunmap_atomic(kaddr); + + return simple_write_end(file, mapping, pos, len, copied, page, fsdata); +} + +static ssize_t udf_adinicb_direct_IO(int rw, struct kiocb *iocb, + struct iov_iter *iter, + loff_t offset) +{ + /* Fallback to buffered I/O. */ return 0; } -struct address_space_operations udf_adinicb_aops = { - .readpage = udf_adinicb_readpage, - .writepage = udf_adinicb_writepage, - .sync_page = block_sync_page, - .prepare_write = udf_adinicb_prepare_write, - .commit_write = udf_adinicb_commit_write, +const struct address_space_operations udf_adinicb_aops = { + .readpage = udf_adinicb_readpage, + .writepage = udf_adinicb_writepage, + .write_begin = udf_adinicb_write_begin, + .write_end = udf_adinicb_write_end, + .direct_IO = udf_adinicb_direct_IO, }; -static ssize_t udf_file_write(struct file * file, const char __user * buf, - size_t count, loff_t *ppos) +static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { ssize_t retval; - struct inode *inode = file->f_dentry->d_inode; + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); int err, pos; + size_t count = iocb->ki_nbytes; + struct udf_inode_info *iinfo = UDF_I(inode); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { + mutex_lock(&inode->i_mutex); + down_write(&iinfo->i_data_sem); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { if (file->f_flags & O_APPEND) pos = inode->i_size; else - pos = *ppos; - - if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + - pos + count)) - { - udf_expand_file_adinicb(inode, pos + count, &err); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { + pos = iocb->ki_pos; + + if (inode->i_sb->s_blocksize < + (udf_file_entry_alloc_offset(inode) + + pos + count)) { + err = udf_expand_file_adinicb(inode); + if (err) { + mutex_unlock(&inode->i_mutex); udf_debug("udf_expand_adinicb: err=%d\n", err); return err; } - } - else - { + } else { if (pos + count > inode->i_size) - UDF_I_LENALLOC(inode) = pos + count; + iinfo->i_lenAlloc = pos + count; else - UDF_I_LENALLOC(inode) = inode->i_size; + iinfo->i_lenAlloc = inode->i_size; + up_write(&iinfo->i_data_sem); } - } + } else + up_write(&iinfo->i_data_sem); - retval = generic_file_write(file, buf, count, ppos); + retval = __generic_file_write_iter(iocb, from); + mutex_unlock(&inode->i_mutex); + + if (retval > 0) { + ssize_t err; - if (retval > 0) mark_inode_dirty(inode); + err = generic_write_sync(file, iocb->ki_pos - retval, retval); + if (err < 0) + retval = err; + } + return retval; } -/* - * udf_ioctl - * - * PURPOSE - * Issue an ioctl. - * - * DESCRIPTION - * Optional - sys_ioctl() will return -ENOTTY if this routine is not - * available, and the ioctl cannot be handled without filesystem help. - * - * sys_ioctl() handles these ioctls that apply only to regular files: - * FIBMAP [requires udf_block_map()], FIGETBSZ, FIONREAD - * These ioctls are also handled by sys_ioctl(): - * FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC - * All other ioctls are passed to the filesystem. - * - * Refer to sys_ioctl() in fs/ioctl.c - * sys_ioctl() -> . - * - * PRE-CONDITIONS - * inode Pointer to inode that ioctl was issued on. - * filp Pointer to file that ioctl was issued on. - * cmd The ioctl command. - * arg The ioctl argument [can be interpreted as a - * user-space pointer if desired]. - * - * POST-CONDITIONS - * <return> Success (>=0) or an error code (<=0) that - * sys_ioctl() will return. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) +long udf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + struct inode *inode = file_inode(filp); + long old_block, new_block; int result = -EINVAL; - if ( permission(inode, MAY_READ, NULL) != 0 ) - { - udf_debug("no permission to access inode %lu\n", - inode->i_ino); - return -EPERM; + if (inode_permission(inode, MAY_READ) != 0) { + udf_debug("no permission to access inode %lu\n", inode->i_ino); + result = -EPERM; + goto out; } - if ( !arg ) - { + if (!arg) { udf_debug("invalid argument to udf_ioctl\n"); - return -EINVAL; + result = -EINVAL; + goto out; } - switch (cmd) - { - case UDF_GETVOLIDENT: - return copy_to_user((char __user *)arg, - UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0; - case UDF_RELOCATE_BLOCKS: - { - long old, new; - - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (get_user(old, (long __user *)arg)) return -EFAULT; - if ((result = udf_relocate_blocks(inode->i_sb, - old, &new)) == 0) - result = put_user(new, (long __user *)arg); - - return result; + switch (cmd) { + case UDF_GETVOLIDENT: + if (copy_to_user((char __user *)arg, + UDF_SB(inode->i_sb)->s_volume_ident, 32)) + result = -EFAULT; + else + result = 0; + goto out; + case UDF_RELOCATE_BLOCKS: + if (!capable(CAP_SYS_ADMIN)) { + result = -EPERM; + goto out; + } + if (get_user(old_block, (long __user *)arg)) { + result = -EFAULT; + goto out; } - case UDF_GETEASIZE: - result = put_user(UDF_I_LENEATTR(inode), (int __user *)arg); - break; - - case UDF_GETEABLOCK: - result = copy_to_user((char __user *)arg, UDF_I_DATA(inode), - UDF_I_LENEATTR(inode)) ? -EFAULT : 0; - break; + result = udf_relocate_blocks(inode->i_sb, + old_block, &new_block); + if (result == 0) + result = put_user(new_block, (long __user *)arg); + goto out; + case UDF_GETEASIZE: + result = put_user(UDF_I(inode)->i_lenEAttr, (int __user *)arg); + goto out; + case UDF_GETEABLOCK: + result = copy_to_user((char __user *)arg, + UDF_I(inode)->i_ext.i_data, + UDF_I(inode)->i_lenEAttr) ? -EFAULT : 0; + goto out; } +out: 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) +static int udf_release_file(struct inode *inode, struct file *filp) { - if (filp->f_mode & FMODE_WRITE) - { - lock_kernel(); + if (filp->f_mode & FMODE_WRITE) { + down_write(&UDF_I(inode)->i_data_sem); udf_discard_prealloc(inode); - unlock_kernel(); + udf_truncate_tail_extent(inode); + up_write(&UDF_I(inode)->i_data_sem); } return 0; } -struct file_operations udf_file_operations = { - .read = generic_file_read, - .ioctl = udf_ioctl, +const struct file_operations udf_file_operations = { + .read = new_sync_read, + .read_iter = generic_file_read_iter, + .unlocked_ioctl = udf_ioctl, .open = generic_file_open, .mmap = generic_file_mmap, - .write = udf_file_write, + .write = new_sync_write, + .write_iter = udf_file_write_iter, .release = udf_release_file, - .fsync = udf_fsync_file, - .sendfile = generic_file_sendfile, + .fsync = generic_file_fsync, + .splice_read = generic_file_splice_read, + .llseek = generic_file_llseek, }; -struct inode_operations udf_file_inode_operations = { - .truncate = udf_truncate, +static int udf_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = udf_setsize(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; +} + +const struct inode_operations udf_file_inode_operations = { + .setattr = udf_setattr, }; diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c deleted file mode 100644 index 2dde6b888c2..00000000000 --- a/fs/udf/fsync.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * fsync.c - * - * PURPOSE - * Fsync handling routines for the OSTA-UDF(tm) filesystem. - * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * - * COPYRIGHT - * This file is distributed under the terms of the GNU General Public - * License (GPL). Copies of the GPL can be obtained from: - * ftp://prep.ai.mit.edu/pub/gnu/GPL - * Each contributing author retains all rights to their own work. - * - * (C) 1999-2001 Ben Fennema - * (C) 1999-2000 Stelias Computing Inc - * - * HISTORY - * - * 05/22/99 blf Created. - */ - -#include "udfdecl.h" - -#include <linux/fs.h> -#include <linux/smp_lock.h> - -static int udf_fsync_inode(struct inode *, int); - -/* - * File may be NULL when we are called. Perhaps we shouldn't - * even pass file to fsync ? - */ - -int udf_fsync_file(struct file * file, struct dentry *dentry, int datasync) -{ - struct inode *inode = dentry->d_inode; - return udf_fsync_inode(inode, datasync); -} - -static int udf_fsync_inode(struct inode *inode, int datasync) -{ - int err; - - err = sync_mapping_buffers(inode->i_mapping); - if (!(inode->i_state & I_DIRTY)) - return err; - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) - return err; - - err |= udf_sync_inode (inode); - return err ? -EIO : 0; -} diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index a7e5d40f1eb..6eaf5edf1ea 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c @@ -4,11 +4,6 @@ * PURPOSE * Inode allocation handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -25,145 +20,111 @@ #include "udfdecl.h" #include <linux/fs.h> -#include <linux/quotaops.h> -#include <linux/udf_fs.h> #include <linux/sched.h> #include <linux/slab.h> #include "udf_i.h" #include "udf_sb.h" -void udf_free_inode(struct inode * inode) +void udf_free_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); + struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb); - /* - * Note: we must free any quota before locking the superblock, - * as writing the quota to disk may need the lock as well. - */ - DQUOT_FREE_INODE(inode); - DQUOT_DROP(inode); - - clear_inode(inode); - - down(&sbi->s_alloc_sem); - if (sbi->s_lvidbh) { + if (lvidiu) { + mutex_lock(&sbi->s_alloc_mutex); if (S_ISDIR(inode->i_mode)) - UDF_SB_LVIDIU(sb)->numDirs = - cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) - 1); + le32_add_cpu(&lvidiu->numDirs, -1); else - UDF_SB_LVIDIU(sb)->numFiles = - cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1); - - mark_buffer_dirty(sbi->s_lvidbh); + le32_add_cpu(&lvidiu->numFiles, -1); + udf_updated_lvid(sb); + mutex_unlock(&sbi->s_alloc_mutex); } - up(&sbi->s_alloc_sem); - udf_free_blocks(sb, NULL, UDF_I_LOCATION(inode), 0, 1); + udf_free_blocks(sb, NULL, &UDF_I(inode)->i_location, 0, 1); } -struct inode * udf_new_inode (struct inode *dir, int mode, int * err) +struct inode *udf_new_inode(struct inode *dir, umode_t mode, int *err) { struct super_block *sb = dir->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); - struct inode * inode; + struct inode *inode; int block; - uint32_t start = UDF_I_LOCATION(dir).logicalBlockNum; + uint32_t start = UDF_I(dir)->i_location.logicalBlockNum; + struct udf_inode_info *iinfo; + struct udf_inode_info *dinfo = UDF_I(dir); + struct logicalVolIntegrityDescImpUse *lvidiu; inode = new_inode(sb); - if (!inode) - { + if (!inode) { *err = -ENOMEM; return NULL; } *err = -ENOSPC; - block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum, - start, err); - if (*err) - { + iinfo = UDF_I(inode); + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) { + iinfo->i_efe = 1; + if (UDF_VERS_USE_EXTENDED_FE > sbi->s_udfrev) + sbi->s_udfrev = UDF_VERS_USE_EXTENDED_FE; + iinfo->i_ext.i_data = kzalloc(inode->i_sb->s_blocksize - + sizeof(struct extendedFileEntry), + GFP_KERNEL); + } else { + iinfo->i_efe = 0; + iinfo->i_ext.i_data = kzalloc(inode->i_sb->s_blocksize - + sizeof(struct fileEntry), + GFP_KERNEL); + } + if (!iinfo->i_ext.i_data) { iput(inode); + *err = -ENOMEM; return NULL; } - down(&sbi->s_alloc_sem); - UDF_I_UNIQUE(inode) = 0; - UDF_I_LENEXTENTS(inode) = 0; - UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; - UDF_I_NEXT_ALLOC_GOAL(inode) = 0; - UDF_I_STRAT4096(inode) = 0; - if (UDF_SB_LVIDBH(sb)) - { - struct logicalVolHeaderDesc *lvhd; - uint64_t uniqueID; - lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse); - if (S_ISDIR(mode)) - UDF_SB_LVIDIU(sb)->numDirs = - cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) + 1); - else - UDF_SB_LVIDIU(sb)->numFiles = - cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + 1); - UDF_I_UNIQUE(inode) = uniqueID = le64_to_cpu(lvhd->uniqueID); - if (!(++uniqueID & 0x00000000FFFFFFFFUL)) - uniqueID += 16; - lvhd->uniqueID = cpu_to_le64(uniqueID); - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); + block = udf_new_block(dir->i_sb, NULL, + dinfo->i_location.partitionReferenceNum, + start, err); + if (*err) { + iput(inode); + return NULL; } - inode->i_mode = mode; - inode->i_uid = current->fsuid; - if (dir->i_mode & S_ISGID) - { - inode->i_gid = dir->i_gid; + + lvidiu = udf_sb_lvidiu(sb); + if (lvidiu) { + iinfo->i_unique = lvid_get_unique_id(sb); + mutex_lock(&sbi->s_alloc_mutex); if (S_ISDIR(mode)) - mode |= S_ISGID; + le32_add_cpu(&lvidiu->numDirs, 1); + else + le32_add_cpu(&lvidiu->numFiles, 1); + udf_updated_lvid(sb); + mutex_unlock(&sbi->s_alloc_mutex); } - 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_init_owner(inode, dir, mode); + + iinfo->i_location.logicalBlockNum = block; + iinfo->i_location.partitionReferenceNum = + dinfo->i_location.partitionReferenceNum; + inode->i_ino = udf_get_lb_pblock(sb, &iinfo->i_location, 0); inode->i_blocks = 0; - UDF_I_LENEATTR(inode) = 0; - UDF_I_LENALLOC(inode) = 0; - UDF_I_USE(inode) = 0; - if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) - { - UDF_I_EFE(inode) = 1; - UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE); - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); - memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); - } - else - { - UDF_I_EFE(inode) = 0; - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); - memset(UDF_I_DATA(inode), 0x00, inode->i_sb->s_blocksize - sizeof(struct fileEntry)); - } + iinfo->i_lenEAttr = 0; + iinfo->i_lenAlloc = 0; + iinfo->i_use = 0; + iinfo->i_checkpoint = 1; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB)) - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; + iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; + iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; else - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; + iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; inode->i_mtime = inode->i_atime = inode->i_ctime = - UDF_I_CRTIME(inode) = current_fs_time(inode->i_sb); + iinfo->i_crtime = current_fs_time(inode->i_sb); insert_inode_hash(inode); mark_inode_dirty(inode); - up(&sbi->s_alloc_sem); - - if (DQUOT_ALLOC_INODE(inode)) - { - DQUOT_DROP(inode); - inode->i_flags |= S_NOQUOTA; - inode->i_nlink = 0; - iput(inode); - *err = -EDQUOT; - return NULL; - } *err = 0; return inode; diff --git a/fs/udf/inode.c b/fs/udf/inode.c index b83890beaaa..236cd48184c 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -4,11 +4,6 @@ * PURPOSE * Inode handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -24,7 +19,8 @@ * 10/04/98 dgb Added rudimentary directory functions * 10/07/98 Fully working udf_block_map! It works! * 11/25/98 bmap altered to better support extents - * 12/06/98 blf partition support in udf_iget, udf_block_map and udf_read_inode + * 12/06/98 blf partition support in udf_iget, udf_block_map + * and udf_read_inode * 12/12/98 rewrote udf_block_map to handle next extents and descs across * block boundaries (which is not actually allowed) * 12/20/98 added support for strategy 4096 @@ -35,12 +31,14 @@ #include "udfdecl.h" #include <linux/mm.h> -#include <linux/smp_lock.h> #include <linux/module.h> #include <linux/pagemap.h> #include <linux/buffer_head.h> #include <linux/writeback.h> #include <linux/slab.h> +#include <linux/crc-itu-t.h> +#include <linux/mpage.h> +#include <linux/aio.h> #include "udf_i.h" #include "udf_sb.h" @@ -51,70 +49,137 @@ MODULE_LICENSE("GPL"); #define EXTENT_MERGE_SIZE 5 -static mode_t udf_convert_permissions(struct fileEntry *); +static umode_t udf_convert_permissions(struct fileEntry *); static int udf_update_inode(struct inode *, int); static void udf_fill_inode(struct inode *, struct buffer_head *); -static struct buffer_head *inode_getblk(struct inode *, long, int *, - long *, int *); -static int8_t udf_insert_aext(struct inode *, kernel_lb_addr, int, - kernel_lb_addr, uint32_t, struct buffer_head *); +static int udf_sync_inode(struct inode *inode); +static int udf_alloc_i_data(struct inode *inode, size_t size); +static sector_t inode_getblk(struct inode *, sector_t, int *, int *); +static int8_t udf_insert_aext(struct inode *, struct extent_position, + struct kernel_lb_addr, uint32_t); static void udf_split_extents(struct inode *, int *, int, int, - kernel_long_ad [EXTENT_MERGE_SIZE], int *); + struct kernel_long_ad[EXTENT_MERGE_SIZE], int *); static void udf_prealloc_extents(struct inode *, int, int, - kernel_long_ad [EXTENT_MERGE_SIZE], int *); + struct kernel_long_ad[EXTENT_MERGE_SIZE], int *); static void udf_merge_extents(struct inode *, - kernel_long_ad [EXTENT_MERGE_SIZE], int *); + struct kernel_long_ad[EXTENT_MERGE_SIZE], int *); static void udf_update_extents(struct inode *, - kernel_long_ad [EXTENT_MERGE_SIZE], int, int, - kernel_lb_addr, uint32_t, struct buffer_head **); + struct kernel_long_ad[EXTENT_MERGE_SIZE], int, int, + struct extent_position *); static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); -/* - * udf_delete_inode - * - * PURPOSE - * Clean-up before the specified inode is destroyed. - * - * DESCRIPTION - * This routine is called when the kernel destroys an inode structure - * ie. when iput() finds i_count == 0. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - * - * Called at the last iput() if i_nlink is zero. - */ -void udf_delete_inode(struct inode * inode) +static void __udf_clear_extent_cache(struct inode *inode) { - truncate_inode_pages(&inode->i_data, 0); + struct udf_inode_info *iinfo = UDF_I(inode); - if (is_bad_inode(inode)) - goto no_delete; + if (iinfo->cached_extent.lstart != -1) { + brelse(iinfo->cached_extent.epos.bh); + iinfo->cached_extent.lstart = -1; + } +} - inode->i_size = 0; - udf_truncate(inode); - lock_kernel(); +/* Invalidate extent cache */ +static void udf_clear_extent_cache(struct inode *inode) +{ + struct udf_inode_info *iinfo = UDF_I(inode); - udf_update_inode(inode, IS_SYNC(inode)); - udf_free_inode(inode); + spin_lock(&iinfo->i_extent_cache_lock); + __udf_clear_extent_cache(inode); + spin_unlock(&iinfo->i_extent_cache_lock); +} - unlock_kernel(); - return; -no_delete: - clear_inode(inode); +/* Return contents of extent cache */ +static int udf_read_extent_cache(struct inode *inode, loff_t bcount, + loff_t *lbcount, struct extent_position *pos) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + int ret = 0; + + spin_lock(&iinfo->i_extent_cache_lock); + if ((iinfo->cached_extent.lstart <= bcount) && + (iinfo->cached_extent.lstart != -1)) { + /* Cache hit */ + *lbcount = iinfo->cached_extent.lstart; + memcpy(pos, &iinfo->cached_extent.epos, + sizeof(struct extent_position)); + if (pos->bh) + get_bh(pos->bh); + ret = 1; + } + spin_unlock(&iinfo->i_extent_cache_lock); + return ret; } -void udf_clear_inode(struct inode *inode) +/* Add extent to extent cache */ +static void udf_update_extent_cache(struct inode *inode, loff_t estart, + struct extent_position *pos, int next_epos) { - if (!(inode->i_sb->s_flags & MS_RDONLY)) { - lock_kernel(); - udf_discard_prealloc(inode); - unlock_kernel(); + struct udf_inode_info *iinfo = UDF_I(inode); + + spin_lock(&iinfo->i_extent_cache_lock); + /* Invalidate previously cached extent */ + __udf_clear_extent_cache(inode); + if (pos->bh) + get_bh(pos->bh); + memcpy(&iinfo->cached_extent.epos, pos, + sizeof(struct extent_position)); + iinfo->cached_extent.lstart = estart; + if (next_epos) + switch (iinfo->i_alloc_type) { + case ICBTAG_FLAG_AD_SHORT: + iinfo->cached_extent.epos.offset -= + sizeof(struct short_ad); + break; + case ICBTAG_FLAG_AD_LONG: + iinfo->cached_extent.epos.offset -= + sizeof(struct long_ad); + } + spin_unlock(&iinfo->i_extent_cache_lock); +} + +void udf_evict_inode(struct inode *inode) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + int want_delete = 0; + + if (!inode->i_nlink && !is_bad_inode(inode)) { + want_delete = 1; + udf_setsize(inode, 0); + udf_update_inode(inode, IS_SYNC(inode)); + } + truncate_inode_pages_final(&inode->i_data); + invalidate_inode_buffers(inode); + clear_inode(inode); + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB && + inode->i_size != iinfo->i_lenExtents) { + udf_warn(inode->i_sb, "Inode %lu (mode %o) has inode size %llu different from extent length %llu. Filesystem need not be standards compliant.\n", + inode->i_ino, inode->i_mode, + (unsigned long long)inode->i_size, + (unsigned long long)iinfo->i_lenExtents); + } + kfree(iinfo->i_ext.i_data); + iinfo->i_ext.i_data = NULL; + udf_clear_extent_cache(inode); + if (want_delete) { + udf_free_inode(inode); } +} - kfree(UDF_I_DATA(inode)); - UDF_I_DATA(inode) = NULL; +static void udf_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + struct udf_inode_info *iinfo = UDF_I(inode); + loff_t isize = inode->i_size; + + if (to > isize) { + truncate_pagecache(inode, isize); + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + down_write(&iinfo->i_data_sem); + udf_clear_extent_cache(inode); + udf_truncate_extents(inode); + up_write(&iinfo->i_data_sem); + } + } } static int udf_writepage(struct page *page, struct writeback_control *wbc) @@ -122,114 +187,182 @@ static int udf_writepage(struct page *page, struct writeback_control *wbc) return block_write_full_page(page, udf_get_block, wbc); } +static int udf_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, udf_get_block); +} + static int udf_readpage(struct file *file, struct page *page) { - return block_read_full_page(page, udf_get_block); + return mpage_readpage(page, udf_get_block); } -static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +static int udf_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) { - return block_prepare_write(page, from, to, udf_get_block); + return mpage_readpages(mapping, pages, nr_pages, udf_get_block); +} + +static int udf_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, udf_get_block); + if (unlikely(ret)) + udf_write_failed(mapping, pos + len); + return ret; +} + +static ssize_t udf_direct_IO(int rw, struct kiocb *iocb, + struct iov_iter *iter, + loff_t offset) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_iter_count(iter); + ssize_t ret; + + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, udf_get_block); + if (unlikely(ret < 0 && (rw & WRITE))) + udf_write_failed(mapping, offset + count); + return ret; } static sector_t udf_bmap(struct address_space *mapping, sector_t block) { - return generic_block_bmap(mapping,block,udf_get_block); + return generic_block_bmap(mapping, block, udf_get_block); } -struct address_space_operations udf_aops = { - .readpage = udf_readpage, - .writepage = udf_writepage, - .sync_page = block_sync_page, - .prepare_write = udf_prepare_write, - .commit_write = generic_commit_write, - .bmap = udf_bmap, +const struct address_space_operations udf_aops = { + .readpage = udf_readpage, + .readpages = udf_readpages, + .writepage = udf_writepage, + .writepages = udf_writepages, + .write_begin = udf_write_begin, + .write_end = generic_write_end, + .direct_IO = udf_direct_IO, + .bmap = udf_bmap, }; -void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err) +/* + * Expand file stored in ICB to a normal one-block-file + * + * This function requires i_data_sem for writing and releases it. + * This function requires i_mutex held + */ +int udf_expand_file_adinicb(struct inode *inode) { struct page *page; char *kaddr; + struct udf_inode_info *iinfo = UDF_I(inode); + int err; struct writeback_control udf_wbc = { .sync_mode = WB_SYNC_NONE, .nr_to_write = 1, }; - /* from now on we have normal address_space methods */ - inode->i_data.a_ops = &udf_aops; - - if (!UDF_I_LENALLOC(inode)) - { + WARN_ON_ONCE(!mutex_is_locked(&inode->i_mutex)); + if (!iinfo->i_lenAlloc) { if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; + iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; else - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; + iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; + /* from now on we have normal address_space methods */ + inode->i_data.a_ops = &udf_aops; + up_write(&iinfo->i_data_sem); mark_inode_dirty(inode); - return; + return 0; } + /* + * Release i_data_sem so that we can lock a page - page lock ranks + * above i_data_sem. i_mutex still protects us against file changes. + */ + up_write(&iinfo->i_data_sem); - page = grab_cache_page(inode->i_mapping, 0); - BUG_ON(!PageLocked(page)); + page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS); + if (!page) + return -ENOMEM; - if (!PageUptodate(page)) - { + if (!PageUptodate(page)) { kaddr = kmap(page); - memset(kaddr + UDF_I_LENALLOC(inode), 0x00, - PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode)); - memcpy(kaddr, UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), - UDF_I_LENALLOC(inode)); + memset(kaddr + iinfo->i_lenAlloc, 0x00, + PAGE_CACHE_SIZE - iinfo->i_lenAlloc); + memcpy(kaddr, iinfo->i_ext.i_data + iinfo->i_lenEAttr, + iinfo->i_lenAlloc); flush_dcache_page(page); SetPageUptodate(page); kunmap(page); } - memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0x00, - UDF_I_LENALLOC(inode)); - UDF_I_LENALLOC(inode) = 0; + down_write(&iinfo->i_data_sem); + memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr, 0x00, + iinfo->i_lenAlloc); + iinfo->i_lenAlloc = 0; if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT; + iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; else - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG; - - inode->i_data.a_ops->writepage(page, &udf_wbc); + iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; + /* from now on we have normal address_space methods */ + inode->i_data.a_ops = &udf_aops; + up_write(&iinfo->i_data_sem); + err = inode->i_data.a_ops->writepage(page, &udf_wbc); + if (err) { + /* Restore everything back so that we don't lose data... */ + lock_page(page); + kaddr = kmap(page); + down_write(&iinfo->i_data_sem); + memcpy(iinfo->i_ext.i_data + iinfo->i_lenEAttr, kaddr, + inode->i_size); + kunmap(page); + unlock_page(page); + iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; + inode->i_data.a_ops = &udf_adinicb_aops; + up_write(&iinfo->i_data_sem); + } page_cache_release(page); - mark_inode_dirty(inode); + + return err; } -struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err) +struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, int *block, + int *err) { int newblock; - struct buffer_head *sbh = NULL, *dbh = NULL; - kernel_lb_addr bloc, eloc; - uint32_t elen, extoffset; + struct buffer_head *dbh = NULL; + struct kernel_lb_addr eloc; uint8_t alloctype; + struct extent_position epos; struct udf_fileident_bh sfibh, dfibh; - loff_t f_pos = udf_ext0_offset(inode) >> 2; - int size = (udf_ext0_offset(inode) + inode->i_size) >> 2; + loff_t f_pos = udf_ext0_offset(inode); + int size = udf_ext0_offset(inode) + inode->i_size; struct fileIdentDesc cfi, *sfi, *dfi; + struct udf_inode_info *iinfo = UDF_I(inode); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) alloctype = ICBTAG_FLAG_AD_SHORT; else alloctype = ICBTAG_FLAG_AD_LONG; - if (!inode->i_size) - { - UDF_I_ALLOCTYPE(inode) = alloctype; + if (!inode->i_size) { + iinfo->i_alloc_type = alloctype; mark_inode_dirty(inode); return NULL; } /* alloc block, and copy data to it */ *block = udf_new_block(inode->i_sb, inode, - UDF_I_LOCATION(inode).partitionReferenceNum, - UDF_I_LOCATION(inode).logicalBlockNum, err); - + iinfo->i_location.partitionReferenceNum, + iinfo->i_location.logicalBlockNum, err); if (!(*block)) return NULL; newblock = udf_get_pblock(inode->i_sb, *block, - UDF_I_LOCATION(inode).partitionReferenceNum, 0); + iinfo->i_location.partitionReferenceNum, + 0); if (!newblock) return NULL; dbh = udf_tgetblk(inode->i_sb, newblock); @@ -241,58 +374,60 @@ struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int unlock_buffer(dbh); mark_buffer_dirty_inode(dbh, inode); - sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2; - sbh = sfibh.sbh = sfibh.ebh = NULL; + sfibh.soffset = sfibh.eoffset = + f_pos & (inode->i_sb->s_blocksize - 1); + sfibh.sbh = sfibh.ebh = NULL; dfibh.soffset = dfibh.eoffset = 0; dfibh.sbh = dfibh.ebh = dbh; - while ( (f_pos < size) ) - { - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; - sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL, NULL, NULL); - if (!sfi) - { - udf_release_data(dbh); + while (f_pos < size) { + iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; + sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, + NULL, NULL, NULL); + if (!sfi) { + brelse(dbh); return NULL; } - UDF_I_ALLOCTYPE(inode) = alloctype; + iinfo->i_alloc_type = alloctype; sfi->descTag.tagLocation = cpu_to_le32(*block); dfibh.soffset = dfibh.eoffset; dfibh.eoffset += (sfibh.eoffset - sfibh.soffset); dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset); if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse, - sfi->fileIdent + le16_to_cpu(sfi->lengthOfImpUse))) - { - UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB; - udf_release_data(dbh); + sfi->fileIdent + + le16_to_cpu(sfi->lengthOfImpUse))) { + iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; + brelse(dbh); return NULL; } } mark_buffer_dirty_inode(dbh, inode); - memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode), 0, UDF_I_LENALLOC(inode)); - UDF_I_LENALLOC(inode) = 0; - bloc = UDF_I_LOCATION(inode); + memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr, 0, + iinfo->i_lenAlloc); + iinfo->i_lenAlloc = 0; eloc.logicalBlockNum = *block; - eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; - elen = inode->i_size; - UDF_I_LENEXTENTS(inode) = elen; - extoffset = udf_file_entry_alloc_offset(inode); - udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0); + eloc.partitionReferenceNum = + iinfo->i_location.partitionReferenceNum; + iinfo->i_lenExtents = inode->i_size; + epos.bh = NULL; + epos.block = iinfo->i_location; + epos.offset = udf_file_entry_alloc_offset(inode); + udf_add_aext(inode, &epos, &eloc, inode->i_size, 0); /* UniqueID stuff */ - udf_release_data(sbh); + brelse(epos.bh); mark_inode_dirty(inode); return dbh; } -static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create) +static int udf_get_block(struct inode *inode, sector_t block, + struct buffer_head *bh_result, int create) { int err, new; - struct buffer_head *bh; - unsigned long phys; + sector_t phys = 0; + struct udf_inode_info *iinfo; - if (!create) - { + if (!create) { phys = udf_block_map(inode, block); if (phys) map_bh(bh_result, inode->i_sb, phys); @@ -301,55 +436,40 @@ static int udf_get_block(struct inode *inode, sector_t block, struct buffer_head err = -EIO; new = 0; - bh = NULL; + iinfo = UDF_I(inode); - lock_kernel(); - - if (block < 0) - goto abort_negative; - - if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1) - { - UDF_I_NEXT_ALLOC_BLOCK(inode) ++; - UDF_I_NEXT_ALLOC_GOAL(inode) ++; + down_write(&iinfo->i_data_sem); + if (block == iinfo->i_next_alloc_block + 1) { + iinfo->i_next_alloc_block++; + iinfo->i_next_alloc_goal++; } - err = 0; - - bh = inode_getblk(inode, block, &err, &phys, &new); - if (bh) - BUG(); - if (err) - goto abort; + udf_clear_extent_cache(inode); + phys = inode_getblk(inode, block, &err, &new); if (!phys) - BUG(); + goto abort; if (new) set_buffer_new(bh_result); map_bh(bh_result, inode->i_sb, phys); + abort: - unlock_kernel(); + up_write(&iinfo->i_data_sem); return err; - -abort_negative: - udf_warning(inode->i_sb, "udf_get_block", "block < 0"); - goto abort; } -static struct buffer_head * -udf_getblk(struct inode *inode, long block, int create, int *err) +static struct buffer_head *udf_getblk(struct inode *inode, long block, + int create, int *err) { + struct buffer_head *bh; struct buffer_head dummy; dummy.b_state = 0; dummy.b_blocknr = -1000; *err = udf_get_block(inode, block, &dummy, create); - if (!*err && buffer_mapped(&dummy)) - { - struct buffer_head *bh; + if (!*err && buffer_mapped(&dummy)) { bh = sb_getblk(inode->i_sb, dummy.b_blocknr); - if (buffer_new(&dummy)) - { + if (buffer_new(&dummy)) { lock_buffer(bh); memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(bh); @@ -358,56 +478,235 @@ udf_getblk(struct inode *inode, long block, int create, int *err) } return bh; } + return NULL; } -static struct buffer_head * inode_getblk(struct inode * inode, long block, - int *err, long *phys, int *new) +/* Extend the file by 'blocks' blocks, return the number of extents added */ +static int udf_do_extend_file(struct inode *inode, + struct extent_position *last_pos, + struct kernel_long_ad *last_ext, + sector_t blocks) +{ + sector_t add; + int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK); + struct super_block *sb = inode->i_sb; + struct kernel_lb_addr prealloc_loc = {}; + int prealloc_len = 0; + struct udf_inode_info *iinfo; + int err; + + /* The previous extent is fake and we should not extend by anything + * - there's nothing to do... */ + if (!blocks && fake) + return 0; + + iinfo = UDF_I(inode); + /* Round the last extent up to a multiple of block size */ + if (last_ext->extLength & (sb->s_blocksize - 1)) { + last_ext->extLength = + (last_ext->extLength & UDF_EXTENT_FLAG_MASK) | + (((last_ext->extLength & UDF_EXTENT_LENGTH_MASK) + + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1)); + iinfo->i_lenExtents = + (iinfo->i_lenExtents + sb->s_blocksize - 1) & + ~(sb->s_blocksize - 1); + } + + /* Last extent are just preallocated blocks? */ + if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == + EXT_NOT_RECORDED_ALLOCATED) { + /* Save the extent so that we can reattach it to the end */ + prealloc_loc = last_ext->extLocation; + prealloc_len = last_ext->extLength; + /* Mark the extent as a hole */ + last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | + (last_ext->extLength & UDF_EXTENT_LENGTH_MASK); + last_ext->extLocation.logicalBlockNum = 0; + last_ext->extLocation.partitionReferenceNum = 0; + } + + /* Can we merge with the previous extent? */ + if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == + EXT_NOT_RECORDED_NOT_ALLOCATED) { + add = ((1 << 30) - sb->s_blocksize - + (last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >> + sb->s_blocksize_bits; + if (add > blocks) + add = blocks; + blocks -= add; + last_ext->extLength += add << sb->s_blocksize_bits; + } + + if (fake) { + udf_add_aext(inode, last_pos, &last_ext->extLocation, + last_ext->extLength, 1); + count++; + } else + udf_write_aext(inode, last_pos, &last_ext->extLocation, + last_ext->extLength, 1); + + /* Managed to do everything necessary? */ + if (!blocks) + goto out; + + /* All further extents will be NOT_RECORDED_NOT_ALLOCATED */ + last_ext->extLocation.logicalBlockNum = 0; + last_ext->extLocation.partitionReferenceNum = 0; + add = (1 << (30-sb->s_blocksize_bits)) - 1; + last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | + (add << sb->s_blocksize_bits); + + /* Create enough extents to cover the whole hole */ + while (blocks > add) { + blocks -= add; + err = udf_add_aext(inode, last_pos, &last_ext->extLocation, + last_ext->extLength, 1); + if (err) + return err; + count++; + } + if (blocks) { + last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | + (blocks << sb->s_blocksize_bits); + err = udf_add_aext(inode, last_pos, &last_ext->extLocation, + last_ext->extLength, 1); + if (err) + return err; + count++; + } + +out: + /* Do we have some preallocated blocks saved? */ + if (prealloc_len) { + err = udf_add_aext(inode, last_pos, &prealloc_loc, + prealloc_len, 1); + if (err) + return err; + last_ext->extLocation = prealloc_loc; + last_ext->extLength = prealloc_len; + count++; + } + + /* last_pos should point to the last written extent... */ + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + last_pos->offset -= sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + last_pos->offset -= sizeof(struct long_ad); + else + return -EIO; + + return count; +} + +static int udf_extend_file(struct inode *inode, loff_t newsize) { - struct buffer_head *pbh = NULL, *cbh = NULL, *nbh = NULL, *result = NULL; - kernel_long_ad laarr[EXTENT_MERGE_SIZE]; - uint32_t pextoffset = 0, cextoffset = 0, nextoffset = 0; + + struct extent_position epos; + struct kernel_lb_addr eloc; + uint32_t elen; + int8_t etype; + struct super_block *sb = inode->i_sb; + sector_t first_block = newsize >> sb->s_blocksize_bits, offset; + int adsize; + struct udf_inode_info *iinfo = UDF_I(inode); + struct kernel_long_ad extent; + int err; + + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); + else + BUG(); + + etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); + + /* File has extent covering the new size (could happen when extending + * inside a block)? */ + if (etype != -1) + return 0; + if (newsize & (sb->s_blocksize - 1)) + offset++; + /* Extended file just to the boundary of the last file block? */ + if (offset == 0) + return 0; + + /* Truncate is extending the file by 'offset' blocks */ + if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || + (epos.bh && epos.offset == sizeof(struct allocExtDesc))) { + /* File has no extents at all or has empty last + * indirect extent! Create a fake extent... */ + extent.extLocation.logicalBlockNum = 0; + extent.extLocation.partitionReferenceNum = 0; + extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED; + } else { + epos.offset -= adsize; + etype = udf_next_aext(inode, &epos, &extent.extLocation, + &extent.extLength, 0); + extent.extLength |= etype << 30; + } + err = udf_do_extend_file(inode, &epos, &extent, offset); + if (err < 0) + goto out; + err = 0; + iinfo->i_lenExtents = newsize; +out: + brelse(epos.bh); + return err; +} + +static sector_t inode_getblk(struct inode *inode, sector_t block, + int *err, int *new) +{ + struct kernel_long_ad laarr[EXTENT_MERGE_SIZE]; + struct extent_position prev_epos, cur_epos, next_epos; int count = 0, startnum = 0, endnum = 0; - uint32_t elen = 0; - kernel_lb_addr eloc, pbloc, cbloc, nbloc; + uint32_t elen = 0, tmpelen; + struct kernel_lb_addr eloc, tmpeloc; int c = 1; - uint64_t lbcount = 0, b_off = 0; - uint32_t newblocknum, newblock, offset = 0; + loff_t lbcount = 0, b_off = 0; + uint32_t newblocknum, newblock; + sector_t offset = 0; int8_t etype; - int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum; - char lastblock = 0; + struct udf_inode_info *iinfo = UDF_I(inode); + int goal = 0, pgoal = iinfo->i_location.logicalBlockNum; + int lastblock = 0; + bool isBeyondEOF; - pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode); - b_off = (uint64_t)block << inode->i_sb->s_blocksize_bits; - pbloc = cbloc = nbloc = UDF_I_LOCATION(inode); + *err = 0; + *new = 0; + prev_epos.offset = udf_file_entry_alloc_offset(inode); + prev_epos.block = iinfo->i_location; + prev_epos.bh = NULL; + cur_epos = next_epos = prev_epos; + b_off = (loff_t)block << inode->i_sb->s_blocksize_bits; /* find the extent which contains the block we are looking for. - alternate between laarr[0] and laarr[1] for locations of the - current extent, and the previous extent */ - do - { - if (pbh != cbh) - { - udf_release_data(pbh); - atomic_inc(&cbh->b_count); - pbh = cbh; + alternate between laarr[0] and laarr[1] for locations of the + current extent, and the previous extent */ + do { + if (prev_epos.bh != cur_epos.bh) { + brelse(prev_epos.bh); + get_bh(cur_epos.bh); + prev_epos.bh = cur_epos.bh; } - if (cbh != nbh) - { - udf_release_data(cbh); - atomic_inc(&nbh->b_count); - cbh = nbh; + if (cur_epos.bh != next_epos.bh) { + brelse(cur_epos.bh); + get_bh(next_epos.bh); + cur_epos.bh = next_epos.bh; } lbcount += elen; - pbloc = cbloc; - cbloc = nbloc; + prev_epos.block = cur_epos.block; + cur_epos.block = next_epos.block; - pextoffset = cextoffset; - cextoffset = nextoffset; + prev_epos.offset = cur_epos.offset; + cur_epos.offset = next_epos.offset; - if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) == -1) + etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 1); + if (etype == -1) break; c = !c; @@ -418,428 +717,475 @@ static struct buffer_head * inode_getblk(struct inode * inode, long block, if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) pgoal = eloc.logicalBlockNum + ((elen + inode->i_sb->s_blocksize - 1) >> - inode->i_sb->s_blocksize_bits); + inode->i_sb->s_blocksize_bits); - count ++; + count++; } while (lbcount + elen <= b_off); b_off -= lbcount; offset = b_off >> inode->i_sb->s_blocksize_bits; + /* + * Move prev_epos and cur_epos into indirect extent if we are at + * the pointer to it + */ + udf_next_aext(inode, &prev_epos, &tmpeloc, &tmpelen, 0); + udf_next_aext(inode, &cur_epos, &tmpeloc, &tmpelen, 0); /* if the extent is allocated and recorded, return the block - if the extent is not a multiple of the blocksize, round up */ + if the extent is not a multiple of the blocksize, round up */ - if (etype == (EXT_RECORDED_ALLOCATED >> 30)) - { - if (elen & (inode->i_sb->s_blocksize - 1)) - { + if (etype == (EXT_RECORDED_ALLOCATED >> 30)) { + if (elen & (inode->i_sb->s_blocksize - 1)) { elen = EXT_RECORDED_ALLOCATED | ((elen + inode->i_sb->s_blocksize - 1) & - ~(inode->i_sb->s_blocksize - 1)); - etype = udf_write_aext(inode, nbloc, &cextoffset, eloc, elen, nbh, 1); - } - udf_release_data(pbh); - udf_release_data(cbh); - udf_release_data(nbh); - newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset); - *phys = newblock; - return NULL; + ~(inode->i_sb->s_blocksize - 1)); + udf_write_aext(inode, &cur_epos, &eloc, elen, 1); + } + brelse(prev_epos.bh); + brelse(cur_epos.bh); + brelse(next_epos.bh); + newblock = udf_get_lb_pblock(inode->i_sb, &eloc, offset); + return newblock; } - if (etype == -1) - { - endnum = startnum = ((count > 1) ? 1 : count); - if (laarr[c].extLength & (inode->i_sb->s_blocksize - 1)) - { - laarr[c].extLength = - (laarr[c].extLength & UDF_EXTENT_FLAG_MASK) | - (((laarr[c].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & - ~(inode->i_sb->s_blocksize - 1)); - UDF_I_LENEXTENTS(inode) = - (UDF_I_LENEXTENTS(inode) + inode->i_sb->s_blocksize - 1) & - ~(inode->i_sb->s_blocksize - 1); - } - c = !c; - laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | - ((offset + 1) << inode->i_sb->s_blocksize_bits); - memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr)); - count ++; - endnum ++; + /* Are we beyond EOF? */ + if (etype == -1) { + int ret; + isBeyondEOF = 1; + if (count) { + if (c) + laarr[0] = laarr[1]; + startnum = 1; + } else { + /* Create a fake extent when there's not one */ + memset(&laarr[0].extLocation, 0x00, + sizeof(struct kernel_lb_addr)); + laarr[0].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED; + /* Will udf_do_extend_file() create real extent from + a fake one? */ + startnum = (offset > 0); + } + /* Create extents for the hole between EOF and offset */ + ret = udf_do_extend_file(inode, &prev_epos, laarr, offset); + if (ret < 0) { + brelse(prev_epos.bh); + brelse(cur_epos.bh); + brelse(next_epos.bh); + *err = ret; + return 0; + } + c = 0; + offset = 0; + count += ret; + /* We are not covered by a preallocated extent? */ + if ((laarr[0].extLength & UDF_EXTENT_FLAG_MASK) != + EXT_NOT_RECORDED_ALLOCATED) { + /* Is there any real extent? - otherwise we overwrite + * the fake one... */ + if (count) + c = !c; + laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | + inode->i_sb->s_blocksize; + memset(&laarr[c].extLocation, 0x00, + sizeof(struct kernel_lb_addr)); + count++; + } + endnum = c + 1; lastblock = 1; - } - else + } else { + isBeyondEOF = 0; endnum = startnum = ((count > 2) ? 2 : count); - /* if the current extent is in position 0, swap it with the previous */ - if (!c && count != 1) - { - laarr[2] = laarr[0]; - laarr[0] = laarr[1]; - laarr[1] = laarr[2]; - c = 1; - } - - /* if the current block is located in a extent, read the next extent */ - if (etype != -1) - { - if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 0)) != -1) - { - laarr[c+1].extLength = (etype << 30) | elen; - laarr[c+1].extLocation = eloc; - count ++; - startnum ++; - endnum ++; - } - else + /* if the current extent is in position 0, + swap it with the previous */ + if (!c && count != 1) { + laarr[2] = laarr[0]; + laarr[0] = laarr[1]; + laarr[1] = laarr[2]; + c = 1; + } + + /* if the current block is located in an extent, + read the next extent */ + etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 0); + if (etype != -1) { + laarr[c + 1].extLength = (etype << 30) | elen; + laarr[c + 1].extLocation = eloc; + count++; + startnum++; + endnum++; + } else lastblock = 1; } - udf_release_data(cbh); - udf_release_data(nbh); /* if the current extent is not recorded but allocated, get the - block in the extent corresponding to the requested block */ + * block in the extent corresponding to the requested block */ if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) newblocknum = laarr[c].extLocation.logicalBlockNum + offset; - else /* otherwise, allocate a new block */ - { - if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block) - goal = UDF_I_NEXT_ALLOC_GOAL(inode); - - if (!goal) - { - if (!(goal = pgoal)) - goal = UDF_I_LOCATION(inode).logicalBlockNum + 1; - } - - if (!(newblocknum = udf_new_block(inode->i_sb, inode, - UDF_I_LOCATION(inode).partitionReferenceNum, goal, err))) - { - udf_release_data(pbh); + else { /* otherwise, allocate a new block */ + if (iinfo->i_next_alloc_block == block) + goal = iinfo->i_next_alloc_goal; + + if (!goal) { + if (!(goal = pgoal)) /* XXX: what was intended here? */ + goal = iinfo->i_location.logicalBlockNum + 1; + } + + newblocknum = udf_new_block(inode->i_sb, inode, + iinfo->i_location.partitionReferenceNum, + goal, err); + if (!newblocknum) { + brelse(prev_epos.bh); + brelse(cur_epos.bh); + brelse(next_epos.bh); *err = -ENOSPC; - return NULL; + return 0; } - UDF_I_LENEXTENTS(inode) += inode->i_sb->s_blocksize; + if (isBeyondEOF) + iinfo->i_lenExtents += inode->i_sb->s_blocksize; } - /* if the extent the requsted block is located in contains multiple blocks, - split the extent into at most three extents. blocks prior to requested - block, requested block, and blocks after requested block */ + /* 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); + /* We preallocate blocks only for regular files. It also makes sense + * for directories but there's a problem when to drop the + * preallocation. We might use some delayed work for that but I feel + * it's overengineering for a filesystem like UDF. */ + if (S_ISREG(inode->i_mode)) + udf_prealloc_extents(inode, c, lastblock, laarr, &endnum); #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; + * of extents is greater than the old number, and deleting extents if + * the new number of extents is less than the old number */ + udf_update_extents(inode, laarr, startnum, endnum, &prev_epos); + + brelse(prev_epos.bh); + brelse(cur_epos.bh); + brelse(next_epos.bh); + + newblock = udf_get_pblock(inode->i_sb, newblocknum, + iinfo->i_location.partitionReferenceNum, 0); + if (!newblock) { + *err = -EIO; + return 0; } - *phys = newblock; - *err = 0; *new = 1; - UDF_I_NEXT_ALLOC_BLOCK(inode) = block; - UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum; + iinfo->i_next_alloc_block = block; + iinfo->i_next_alloc_goal = newblocknum; inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) udf_sync_inode(inode); else mark_inode_dirty(inode); - return result; + + return newblock; } -static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) +static void udf_split_extents(struct inode *inode, int *c, int offset, + int newblocknum, + struct kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int *endnum) { + unsigned long blocksize = inode->i_sb->s_blocksize; + unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; + if ((laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30) || - (laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) - { + (laarr[*c].extLength >> 30) == + (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { int curr = *c; int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + blocksize - 1) >> blocksize_bits; int8_t etype = (laarr[curr].extLength >> 30); if (blen == 1) ; - else if (!offset || blen == offset + 1) - { - laarr[curr+2] = laarr[curr+1]; - laarr[curr+1] = laarr[curr]; - } - else - { - laarr[curr+3] = laarr[curr+1]; - laarr[curr+2] = laarr[curr+1] = laarr[curr]; - } - - if (offset) - { - if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset); - laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | - (offset << inode->i_sb->s_blocksize_bits); + else if (!offset || blen == offset + 1) { + laarr[curr + 2] = laarr[curr + 1]; + laarr[curr + 1] = laarr[curr]; + } else { + laarr[curr + 3] = laarr[curr + 1]; + laarr[curr + 2] = laarr[curr + 1] = laarr[curr]; + } + + if (offset) { + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + udf_free_blocks(inode->i_sb, inode, + &laarr[curr].extLocation, + 0, offset); + laarr[curr].extLength = + EXT_NOT_RECORDED_NOT_ALLOCATED | + (offset << blocksize_bits); laarr[curr].extLocation.logicalBlockNum = 0; - laarr[curr].extLocation.partitionReferenceNum = 0; - } - else + laarr[curr].extLocation. + partitionReferenceNum = 0; + } else laarr[curr].extLength = (etype << 30) | - (offset << inode->i_sb->s_blocksize_bits); - curr ++; - (*c) ++; - (*endnum) ++; + (offset << blocksize_bits); + curr++; + (*c)++; + (*endnum)++; } - + laarr[curr].extLocation.logicalBlockNum = newblocknum; if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) laarr[curr].extLocation.partitionReferenceNum = - UDF_I_LOCATION(inode).partitionReferenceNum; + UDF_I(inode)->i_location.partitionReferenceNum; laarr[curr].extLength = EXT_RECORDED_ALLOCATED | - inode->i_sb->s_blocksize; - curr ++; + blocksize; + curr++; - if (blen != offset + 1) - { + if (blen != offset + 1) { if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - laarr[curr].extLocation.logicalBlockNum += (offset + 1); + laarr[curr].extLocation.logicalBlockNum += + offset + 1; laarr[curr].extLength = (etype << 30) | - ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits); - curr ++; - (*endnum) ++; + ((blen - (offset + 1)) << blocksize_bits); + curr++; + (*endnum)++; } } } static void udf_prealloc_extents(struct inode *inode, int c, int lastblock, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) + struct kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int *endnum) { int start, length = 0, currlength = 0, i; - if (*endnum >= (c+1)) - { + if (*endnum >= (c + 1)) { if (!lastblock) return; else start = c; - } - else - { - if ((laarr[c+1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - start = c+1; - length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); - } - else + } else { + if ((laarr[c + 1].extLength >> 30) == + (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + start = c + 1; + length = currlength = + (((laarr[c + 1].extLength & + UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits); + } else start = c; } - for (i=start+1; i<=*endnum; i++) - { - if (i == *endnum) - { + for (i = start + 1; i <= *endnum; i++) { + if (i == *endnum) { if (lastblock) length += UDF_DEFAULT_PREALLOC_BLOCKS; - } - else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) - length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); - else + } else if ((laarr[i].extLength >> 30) == + (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) { + length += (((laarr[i].extLength & + UDF_EXTENT_LENGTH_MASK) + + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits); + } else break; } - if (length) - { + 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); + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits); int numalloc = udf_prealloc_blocks(inode->i_sb, inode, - laarr[start].extLocation.partitionReferenceNum, - next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length : - UDF_DEFAULT_PREALLOC_BLOCKS) - currlength); - - if (numalloc) - { - if (start == (c+1)) + laarr[start].extLocation.partitionReferenceNum, + next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? + length : UDF_DEFAULT_PREALLOC_BLOCKS) - + currlength); + if (numalloc) { + if (start == (c + 1)) laarr[start].extLength += - (numalloc << inode->i_sb->s_blocksize_bits); - else - { - memmove(&laarr[c+2], &laarr[c+1], - sizeof(long_ad) * (*endnum - (c+1))); - (*endnum) ++; - laarr[c+1].extLocation.logicalBlockNum = next; - laarr[c+1].extLocation.partitionReferenceNum = - laarr[c].extLocation.partitionReferenceNum; - laarr[c+1].extLength = EXT_NOT_RECORDED_ALLOCATED | - (numalloc << inode->i_sb->s_blocksize_bits); - start = c+1; + (numalloc << + inode->i_sb->s_blocksize_bits); + else { + memmove(&laarr[c + 2], &laarr[c + 1], + sizeof(struct long_ad) * (*endnum - (c + 1))); + (*endnum)++; + laarr[c + 1].extLocation.logicalBlockNum = next; + laarr[c + 1].extLocation.partitionReferenceNum = + laarr[c].extLocation. + partitionReferenceNum; + laarr[c + 1].extLength = + EXT_NOT_RECORDED_ALLOCATED | + (numalloc << + inode->i_sb->s_blocksize_bits); + start = c + 1; } - for (i=start+1; numalloc && i<*endnum; i++) - { - int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + 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) - { + if (elen > numalloc) { laarr[i].extLength -= - (numalloc << inode->i_sb->s_blocksize_bits); + (numalloc << + inode->i_sb->s_blocksize_bits); numalloc = 0; - } - else - { + } else { numalloc -= elen; - if (*endnum > (i+1)) - memmove(&laarr[i], &laarr[i+1], - sizeof(long_ad) * (*endnum - (i+1))); - i --; - (*endnum) --; + if (*endnum > (i + 1)) + memmove(&laarr[i], + &laarr[i + 1], + sizeof(struct long_ad) * + (*endnum - (i + 1))); + i--; + (*endnum)--; } } - UDF_I_LENEXTENTS(inode) += numalloc << inode->i_sb->s_blocksize_bits; + UDF_I(inode)->i_lenExtents += + numalloc << inode->i_sb->s_blocksize_bits; } } } static void udf_merge_extents(struct inode *inode, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int *endnum) + struct kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int *endnum) { int i; - - for (i=0; i<(*endnum-1); i++) - { - if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30)) - { - if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) || - ((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) == - (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits))) - { - if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) - { - laarr[i+1].extLength = (laarr[i+1].extLength - - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); - laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + - (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; - laarr[i+1].extLocation.logicalBlockNum = - laarr[i].extLocation.logicalBlockNum + - ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >> - inode->i_sb->s_blocksize_bits); - } - else - { - laarr[i].extLength = laarr[i+1].extLength + - (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); - if (*endnum > (i+2)) - memmove(&laarr[i+1], &laarr[i+2], - sizeof(long_ad) * (*endnum - (i+2))); - i --; - (*endnum) --; - } - } - } - else if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) && - ((laarr[i+1].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) - { - udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, - ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); - laarr[i].extLocation.logicalBlockNum = 0; - laarr[i].extLocation.partitionReferenceNum = 0; - - if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) - { - laarr[i+1].extLength = (laarr[i+1].extLength - - (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1); - laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_FLAG_MASK) + - (UDF_EXTENT_LENGTH_MASK + 1) - inode->i_sb->s_blocksize; + unsigned long blocksize = inode->i_sb->s_blocksize; + unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; + + for (i = 0; i < (*endnum - 1); i++) { + struct kernel_long_ad *li /*l[i]*/ = &laarr[i]; + struct kernel_long_ad *lip1 /*l[i plus 1]*/ = &laarr[i + 1]; + + if (((li->extLength >> 30) == (lip1->extLength >> 30)) && + (((li->extLength >> 30) == + (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) || + ((lip1->extLocation.logicalBlockNum - + li->extLocation.logicalBlockNum) == + (((li->extLength & UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) >> blocksize_bits)))) { + + if (((li->extLength & UDF_EXTENT_LENGTH_MASK) + + (lip1->extLength & UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { + lip1->extLength = (lip1->extLength - + (li->extLength & + UDF_EXTENT_LENGTH_MASK) + + UDF_EXTENT_LENGTH_MASK) & + ~(blocksize - 1); + li->extLength = (li->extLength & + UDF_EXTENT_FLAG_MASK) + + (UDF_EXTENT_LENGTH_MASK + 1) - + blocksize; + lip1->extLocation.logicalBlockNum = + li->extLocation.logicalBlockNum + + ((li->extLength & + UDF_EXTENT_LENGTH_MASK) >> + blocksize_bits); + } else { + li->extLength = lip1->extLength + + (((li->extLength & + UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) & ~(blocksize - 1)); + if (*endnum > (i + 2)) + memmove(&laarr[i + 1], &laarr[i + 2], + sizeof(struct long_ad) * + (*endnum - (i + 2))); + i--; + (*endnum)--; } - else - { - laarr[i].extLength = laarr[i+1].extLength + - (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1)); - if (*endnum > (i+2)) - memmove(&laarr[i+1], &laarr[i+2], - sizeof(long_ad) * (*endnum - (i+2))); - i --; - (*endnum) --; + } else if (((li->extLength >> 30) == + (EXT_NOT_RECORDED_ALLOCATED >> 30)) && + ((lip1->extLength >> 30) == + (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))) { + udf_free_blocks(inode->i_sb, inode, &li->extLocation, 0, + ((li->extLength & + UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) >> blocksize_bits); + li->extLocation.logicalBlockNum = 0; + li->extLocation.partitionReferenceNum = 0; + + if (((li->extLength & UDF_EXTENT_LENGTH_MASK) + + (lip1->extLength & UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK) { + lip1->extLength = (lip1->extLength - + (li->extLength & + UDF_EXTENT_LENGTH_MASK) + + UDF_EXTENT_LENGTH_MASK) & + ~(blocksize - 1); + li->extLength = (li->extLength & + UDF_EXTENT_FLAG_MASK) + + (UDF_EXTENT_LENGTH_MASK + 1) - + blocksize; + } else { + li->extLength = lip1->extLength + + (((li->extLength & + UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) & ~(blocksize - 1)); + if (*endnum > (i + 2)) + memmove(&laarr[i + 1], &laarr[i + 2], + sizeof(struct long_ad) * + (*endnum - (i + 2))); + i--; + (*endnum)--; } - } - else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - udf_free_blocks(inode->i_sb, inode, laarr[i].extLocation, 0, - ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) + - inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits); - laarr[i].extLocation.logicalBlockNum = 0; - laarr[i].extLocation.partitionReferenceNum = 0; - laarr[i].extLength = (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) | - EXT_NOT_RECORDED_NOT_ALLOCATED; + } else if ((li->extLength >> 30) == + (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + udf_free_blocks(inode->i_sb, inode, + &li->extLocation, 0, + ((li->extLength & + UDF_EXTENT_LENGTH_MASK) + + blocksize - 1) >> blocksize_bits); + li->extLocation.logicalBlockNum = 0; + li->extLocation.partitionReferenceNum = 0; + li->extLength = (li->extLength & + UDF_EXTENT_LENGTH_MASK) | + EXT_NOT_RECORDED_NOT_ALLOCATED; } } } static void udf_update_extents(struct inode *inode, - kernel_long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum, - kernel_lb_addr pbloc, uint32_t pextoffset, struct buffer_head **pbh) + struct kernel_long_ad laarr[EXTENT_MERGE_SIZE], + int startnum, int endnum, + struct extent_position *epos) { int start = 0, i; - kernel_lb_addr tmploc; + struct kernel_lb_addr tmploc; uint32_t tmplen; - if (startnum > endnum) - { - for (i=0; i<(startnum-endnum); i++) - { - udf_delete_aext(inode, pbloc, pextoffset, laarr[i].extLocation, - laarr[i].extLength, *pbh); - } - } - else if (startnum < endnum) - { - for (i=0; i<(endnum-startnum); i++) - { - udf_insert_aext(inode, pbloc, pextoffset, laarr[i].extLocation, - laarr[i].extLength, *pbh); - udf_next_aext(inode, &pbloc, &pextoffset, &laarr[i].extLocation, - &laarr[i].extLength, pbh, 1); - start ++; + if (startnum > endnum) { + for (i = 0; i < (startnum - endnum); i++) + udf_delete_aext(inode, *epos, laarr[i].extLocation, + laarr[i].extLength); + } else if (startnum < endnum) { + for (i = 0; i < (endnum - startnum); i++) { + udf_insert_aext(inode, *epos, laarr[i].extLocation, + laarr[i].extLength); + udf_next_aext(inode, epos, &laarr[i].extLocation, + &laarr[i].extLength, 1); + start++; } } - for (i=start; i<endnum; i++) - { - udf_next_aext(inode, &pbloc, &pextoffset, &tmploc, &tmplen, pbh, 0); - udf_write_aext(inode, pbloc, &pextoffset, laarr[i].extLocation, - laarr[i].extLength, *pbh, 1); + for (i = start; i < endnum; i++) { + udf_next_aext(inode, epos, &tmploc, &tmplen, 0); + udf_write_aext(inode, epos, &laarr[i].extLocation, + laarr[i].extLength, 1); } } -struct buffer_head * udf_bread(struct inode * inode, int block, - int create, int * err) +struct buffer_head *udf_bread(struct inode *inode, int block, + int create, int *err) { - struct buffer_head * bh = NULL; + struct buffer_head *bh = NULL; bh = udf_getblk(inode, block, create, err); if (!bh) @@ -847,69 +1193,90 @@ struct buffer_head * udf_bread(struct inode * inode, int block, if (buffer_uptodate(bh)) return bh; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); if (buffer_uptodate(bh)) return bh; + brelse(bh); *err = -EIO; return NULL; } -void udf_truncate(struct inode * inode) +int udf_setsize(struct inode *inode, loff_t newsize) { - int offset; int err; + struct udf_inode_info *iinfo; + int bsize = 1 << inode->i_blkbits; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; + S_ISLNK(inode->i_mode))) + return -EINVAL; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; - - lock_kernel(); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { - if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) + - inode->i_size)) - { - udf_expand_file_adinicb(inode, inode->i_size, &err); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - { - inode->i_size = UDF_I_LENALLOC(inode); - unlock_kernel(); - return; + return -EPERM; + + iinfo = UDF_I(inode); + if (newsize > inode->i_size) { + down_write(&iinfo->i_data_sem); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + if (bsize < + (udf_file_entry_alloc_offset(inode) + newsize)) { + err = udf_expand_file_adinicb(inode); + if (err) + return err; + down_write(&iinfo->i_data_sem); + } else { + iinfo->i_lenAlloc = newsize; + goto set_size; } - else - udf_truncate_extents(inode); } - else - { - offset = inode->i_size & (inode->i_sb->s_blocksize - 1); - memset(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset, 0x00, inode->i_sb->s_blocksize - offset - udf_file_entry_alloc_offset(inode)); - UDF_I_LENALLOC(inode) = inode->i_size; - } - } - else - { - block_truncate_page(inode->i_mapping, inode->i_size, udf_get_block); + err = udf_extend_file(inode, newsize); + if (err) { + up_write(&iinfo->i_data_sem); + return err; + } +set_size: + truncate_setsize(inode, newsize); + up_write(&iinfo->i_data_sem); + } else { + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + down_write(&iinfo->i_data_sem); + udf_clear_extent_cache(inode); + memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr + newsize, + 0x00, bsize - newsize - + udf_file_entry_alloc_offset(inode)); + iinfo->i_lenAlloc = newsize; + truncate_setsize(inode, newsize); + up_write(&iinfo->i_data_sem); + goto update_time; + } + err = block_truncate_page(inode->i_mapping, newsize, + udf_get_block); + if (err) + return err; + down_write(&iinfo->i_data_sem); + udf_clear_extent_cache(inode); + truncate_setsize(inode, newsize); udf_truncate_extents(inode); - } - + up_write(&iinfo->i_data_sem); + } +update_time: inode->i_mtime = inode->i_ctime = current_fs_time(inode->i_sb); if (IS_SYNC(inode)) - udf_sync_inode (inode); + udf_sync_inode(inode); else mark_inode_dirty(inode); - unlock_kernel(); + return 0; } -static void -__udf_read_inode(struct inode *inode) +static void __udf_read_inode(struct inode *inode) { struct buffer_head *bh = NULL; struct fileEntry *fe; uint16_t ident; + struct udf_inode_info *iinfo = UDF_I(inode); /* * Set defaults, but the inode is still incomplete! @@ -923,437 +1290,378 @@ __udf_read_inode(struct inode *inode) * 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); + bh = udf_read_ptagged(inode->i_sb, &iinfo->i_location, 0, &ident); + if (!bh) { + udf_err(inode->i_sb, "(ino %ld) failed !bh\n", inode->i_ino); make_bad_inode(inode); return; } if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE && - ident != TAG_IDENT_USE) - { - printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n", + ident != TAG_IDENT_USE) { + udf_err(inode->i_sb, "(ino %ld) failed ident=%d\n", inode->i_ino, ident); - udf_release_data(bh); + brelse(bh); make_bad_inode(inode); return; } fe = (struct fileEntry *)bh->b_data; - if (le16_to_cpu(fe->icbTag.strategyType) == 4096) - { - struct buffer_head *ibh = NULL, *nbh = NULL; - struct indirectEntry *ie; - - ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident); - if (ident == TAG_IDENT_IE) - { - if (ibh) - { - kernel_lb_addr loc; - ie = (struct indirectEntry *)ibh->b_data; - - loc = lelb_to_cpu(ie->indirectICB.extLocation); - - if (ie->indirectICB.extLength && - (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident))) - { - if (ident == TAG_IDENT_FE || - ident == TAG_IDENT_EFE) - { - memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(kernel_lb_addr)); - udf_release_data(bh); - udf_release_data(ibh); - udf_release_data(nbh); - __udf_read_inode(inode); - return; - } - else - { - udf_release_data(nbh); - udf_release_data(ibh); - } + if (fe->icbTag.strategyType == cpu_to_le16(4096)) { + struct buffer_head *ibh; + + ibh = udf_read_ptagged(inode->i_sb, &iinfo->i_location, 1, + &ident); + if (ident == TAG_IDENT_IE && ibh) { + struct buffer_head *nbh = NULL; + struct kernel_lb_addr loc; + struct indirectEntry *ie; + + ie = (struct indirectEntry *)ibh->b_data; + loc = lelb_to_cpu(ie->indirectICB.extLocation); + + if (ie->indirectICB.extLength && + (nbh = udf_read_ptagged(inode->i_sb, &loc, 0, + &ident))) { + if (ident == TAG_IDENT_FE || + ident == TAG_IDENT_EFE) { + memcpy(&iinfo->i_location, + &loc, + sizeof(struct kernel_lb_addr)); + brelse(bh); + brelse(ibh); + brelse(nbh); + __udf_read_inode(inode); + return; } - else - udf_release_data(ibh); + brelse(nbh); } } - else - udf_release_data(ibh); - } - else if (le16_to_cpu(fe->icbTag.strategyType) != 4) - { - printk(KERN_ERR "udf: unsupported strategy type: %d\n", + brelse(ibh); + } else if (fe->icbTag.strategyType != cpu_to_le16(4)) { + udf_err(inode->i_sb, "unsupported strategy type: %d\n", le16_to_cpu(fe->icbTag.strategyType)); - udf_release_data(bh); + brelse(bh); make_bad_inode(inode); return; } udf_fill_inode(inode, bh); - udf_release_data(bh); + + brelse(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; + struct udf_sb_info *sbi = UDF_SB(inode->i_sb); + struct udf_inode_info *iinfo = UDF_I(inode); + unsigned int link_count; fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; - if (le16_to_cpu(fe->icbTag.strategyType) == 4) - UDF_I_STRAT4096(inode) = 0; - else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */ - UDF_I_STRAT4096(inode) = 1; - - UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK; - UDF_I_UNIQUE(inode) = 0; - UDF_I_LENEATTR(inode) = 0; - UDF_I_LENEXTENTS(inode) = 0; - UDF_I_LENALLOC(inode) = 0; - UDF_I_NEXT_ALLOC_BLOCK(inode) = 0; - UDF_I_NEXT_ALLOC_GOAL(inode) = 0; - if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE) - { - UDF_I_EFE(inode) = 1; - UDF_I_USE(inode) = 0; - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry), GFP_KERNEL); - memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct extendedFileEntry), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); - } - else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE) - { - UDF_I_EFE(inode) = 0; - UDF_I_USE(inode) = 0; - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct fileEntry), GFP_KERNEL); - memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct fileEntry), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); - } - else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) - { - UDF_I_EFE(inode) = 0; - UDF_I_USE(inode) = 1; - UDF_I_LENALLOC(inode) = - le32_to_cpu( - ((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs); - UDF_I_DATA(inode) = kmalloc(inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry), GFP_KERNEL); - memcpy(UDF_I_DATA(inode), bh->b_data + sizeof(struct unallocSpaceEntry), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); + if (fe->icbTag.strategyType == cpu_to_le16(4)) + iinfo->i_strat4096 = 0; + else /* if (fe->icbTag.strategyType == cpu_to_le16(4096)) */ + iinfo->i_strat4096 = 1; + + iinfo->i_alloc_type = le16_to_cpu(fe->icbTag.flags) & + ICBTAG_FLAG_AD_MASK; + iinfo->i_unique = 0; + iinfo->i_lenEAttr = 0; + iinfo->i_lenExtents = 0; + iinfo->i_lenAlloc = 0; + iinfo->i_next_alloc_block = 0; + iinfo->i_next_alloc_goal = 0; + if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) { + iinfo->i_efe = 1; + iinfo->i_use = 0; + if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + sizeof(struct extendedFileEntry))) { + make_bad_inode(inode); + return; + } + memcpy(iinfo->i_ext.i_data, + bh->b_data + sizeof(struct extendedFileEntry), + inode->i_sb->s_blocksize - + sizeof(struct extendedFileEntry)); + } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) { + iinfo->i_efe = 0; + iinfo->i_use = 0; + if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + sizeof(struct fileEntry))) { + make_bad_inode(inode); + return; + } + memcpy(iinfo->i_ext.i_data, + bh->b_data + sizeof(struct fileEntry), + inode->i_sb->s_blocksize - sizeof(struct fileEntry)); + } else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) { + iinfo->i_efe = 0; + iinfo->i_use = 1; + iinfo->i_lenAlloc = le32_to_cpu( + ((struct unallocSpaceEntry *)bh->b_data)-> + lengthAllocDescs); + if (udf_alloc_i_data(inode, inode->i_sb->s_blocksize - + sizeof(struct unallocSpaceEntry))) { + make_bad_inode(inode); + return; + } + memcpy(iinfo->i_ext.i_data, + bh->b_data + sizeof(struct unallocSpaceEntry), + inode->i_sb->s_blocksize - + sizeof(struct unallocSpaceEntry)); return; } - inode->i_uid = le32_to_cpu(fe->uid); - if ( inode->i_uid == -1 ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid; + read_lock(&sbi->s_cred_lock); + i_uid_write(inode, le32_to_cpu(fe->uid)); + if (!uid_valid(inode->i_uid) || + UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_IGNORE) || + UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_SET)) + inode->i_uid = UDF_SB(inode->i_sb)->s_uid; + + i_gid_write(inode, le32_to_cpu(fe->gid)); + if (!gid_valid(inode->i_gid) || + UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_IGNORE) || + UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_SET)) + inode->i_gid = UDF_SB(inode->i_sb)->s_gid; + + if (fe->icbTag.fileType != ICBTAG_FILE_TYPE_DIRECTORY && + sbi->s_fmode != UDF_INVALID_MODE) + inode->i_mode = sbi->s_fmode; + else if (fe->icbTag.fileType == ICBTAG_FILE_TYPE_DIRECTORY && + sbi->s_dmode != UDF_INVALID_MODE) + inode->i_mode = sbi->s_dmode; + else + inode->i_mode = udf_convert_permissions(fe); + inode->i_mode &= ~sbi->s_umask; + read_unlock(&sbi->s_cred_lock); - inode->i_gid = le32_to_cpu(fe->gid); - if ( inode->i_gid == -1 ) inode->i_gid = UDF_SB(inode->i_sb)->s_gid; + link_count = le16_to_cpu(fe->fileLinkCount); + if (!link_count) + link_count = 1; + set_nlink(inode, link_count); - inode->i_nlink = le16_to_cpu(fe->fileLinkCount); - if (!inode->i_nlink) - inode->i_nlink = 1; - inode->i_size = le64_to_cpu(fe->informationLength); - UDF_I_LENEXTENTS(inode) = inode->i_size; - - inode->i_mode = udf_convert_permissions(fe); - inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask; + iinfo->i_lenExtents = inode->i_size; - if (UDF_I_EFE(inode) == 0) - { + if (iinfo->i_efe == 0) { inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) << (inode->i_sb->s_blocksize_bits - 9); - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(fe->accessTime)) ) - { - inode->i_atime.tv_sec = convtime; - inode->i_atime.tv_nsec = convtime_usec * 1000; - } - else - { - inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&inode->i_atime, fe->accessTime)) + inode->i_atime = sbi->s_record_time; - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(fe->modificationTime)) ) - { - inode->i_mtime.tv_sec = convtime; - inode->i_mtime.tv_nsec = convtime_usec * 1000; - } - else - { - inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&inode->i_mtime, + fe->modificationTime)) + inode->i_mtime = sbi->s_record_time; - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(fe->attrTime)) ) - { - inode->i_ctime.tv_sec = convtime; - inode->i_ctime.tv_nsec = convtime_usec * 1000; - } - else - { - inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&inode->i_ctime, fe->attrTime)) + inode->i_ctime = sbi->s_record_time; - UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID); - UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr); - UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs); - offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode); - } - else - { - inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << - (inode->i_sb->s_blocksize_bits - 9); + iinfo->i_unique = le64_to_cpu(fe->uniqueID); + iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr); + iinfo->i_lenAlloc = le32_to_cpu(fe->lengthAllocDescs); + iinfo->i_checkpoint = le32_to_cpu(fe->checkpoint); + } else { + inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) << + (inode->i_sb->s_blocksize_bits - 9); - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->accessTime)) ) - { - inode->i_atime.tv_sec = convtime; - inode->i_atime.tv_nsec = convtime_usec * 1000; - } - else - { - inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&inode->i_atime, efe->accessTime)) + inode->i_atime = sbi->s_record_time; - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->modificationTime)) ) - { - inode->i_mtime.tv_sec = convtime; - inode->i_mtime.tv_nsec = convtime_usec * 1000; - } - else - { - inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&inode->i_mtime, + efe->modificationTime)) + inode->i_mtime = sbi->s_record_time; - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->createTime)) ) - { - UDF_I_CRTIME(inode).tv_sec = convtime; - UDF_I_CRTIME(inode).tv_nsec = convtime_usec * 1000; - } - else - { - UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime)) + iinfo->i_crtime = sbi->s_record_time; - if ( udf_stamp_to_time(&convtime, &convtime_usec, - lets_to_cpu(efe->attrTime)) ) - { - inode->i_ctime.tv_sec = convtime; - inode->i_ctime.tv_nsec = convtime_usec * 1000; - } - else - { - inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb); - } + if (!udf_disk_stamp_to_time(&inode->i_ctime, efe->attrTime)) + inode->i_ctime = sbi->s_record_time; - 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); + iinfo->i_unique = le64_to_cpu(efe->uniqueID); + iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr); + iinfo->i_lenAlloc = le32_to_cpu(efe->lengthAllocDescs); + iinfo->i_checkpoint = le32_to_cpu(efe->checkpoint); } - switch (fe->icbTag.fileType) - { - case ICBTAG_FILE_TYPE_DIRECTORY: - { - inode->i_op = &udf_dir_inode_operations; - inode->i_fop = &udf_dir_operations; - inode->i_mode |= S_IFDIR; - inode->i_nlink ++; - break; - } - case ICBTAG_FILE_TYPE_REALTIME: - case ICBTAG_FILE_TYPE_REGULAR: - case ICBTAG_FILE_TYPE_UNDEF: - { - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - inode->i_data.a_ops = &udf_adinicb_aops; - else - inode->i_data.a_ops = &udf_aops; - inode->i_op = &udf_file_inode_operations; - inode->i_fop = &udf_file_operations; - inode->i_mode |= S_IFREG; - break; - } - case ICBTAG_FILE_TYPE_BLOCK: - { - inode->i_mode |= S_IFBLK; - break; - } - case ICBTAG_FILE_TYPE_CHAR: - { - inode->i_mode |= S_IFCHR; - break; - } - case ICBTAG_FILE_TYPE_FIFO: - { - init_special_inode(inode, inode->i_mode | S_IFIFO, 0); - break; - } - case ICBTAG_FILE_TYPE_SOCKET: - { - init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); - break; - } - case ICBTAG_FILE_TYPE_SYMLINK: - { - inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &page_symlink_inode_operations; - inode->i_mode = S_IFLNK|S_IRWXUGO; - break; - } - default: - { - printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n", - inode->i_ino, fe->icbTag.fileType); - make_bad_inode(inode); - return; - } + switch (fe->icbTag.fileType) { + case ICBTAG_FILE_TYPE_DIRECTORY: + inode->i_op = &udf_dir_inode_operations; + inode->i_fop = &udf_dir_operations; + inode->i_mode |= S_IFDIR; + inc_nlink(inode); + break; + case ICBTAG_FILE_TYPE_REALTIME: + case ICBTAG_FILE_TYPE_REGULAR: + case ICBTAG_FILE_TYPE_UNDEF: + case ICBTAG_FILE_TYPE_VAT20: + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + inode->i_data.a_ops = &udf_adinicb_aops; + else + inode->i_data.a_ops = &udf_aops; + inode->i_op = &udf_file_inode_operations; + inode->i_fop = &udf_file_operations; + inode->i_mode |= S_IFREG; + break; + case ICBTAG_FILE_TYPE_BLOCK: + inode->i_mode |= S_IFBLK; + break; + case ICBTAG_FILE_TYPE_CHAR: + inode->i_mode |= S_IFCHR; + break; + case ICBTAG_FILE_TYPE_FIFO: + init_special_inode(inode, inode->i_mode | S_IFIFO, 0); + break; + case ICBTAG_FILE_TYPE_SOCKET: + init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); + break; + case ICBTAG_FILE_TYPE_SYMLINK: + inode->i_data.a_ops = &udf_symlink_aops; + inode->i_op = &udf_symlink_inode_operations; + inode->i_mode = S_IFLNK | S_IRWXUGO; + break; + case ICBTAG_FILE_TYPE_MAIN: + udf_debug("METADATA FILE-----\n"); + break; + case ICBTAG_FILE_TYPE_MIRROR: + udf_debug("METADATA MIRROR FILE-----\n"); + break; + case ICBTAG_FILE_TYPE_BITMAP: + udf_debug("METADATA BITMAP FILE-----\n"); + break; + default: + udf_err(inode->i_sb, "(ino %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)) - { + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { struct deviceSpec *dsea = - (struct deviceSpec *) - udf_get_extendedattr(inode, 12, 1); - - if (dsea) - { - init_special_inode(inode, inode->i_mode, MKDEV( - le32_to_cpu(dsea->majorDeviceIdent), - le32_to_cpu(dsea->minorDeviceIdent))); + (struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); + if (dsea) { + init_special_inode(inode, inode->i_mode, + MKDEV(le32_to_cpu(dsea->majorDeviceIdent), + le32_to_cpu(dsea->minorDeviceIdent))); /* Developer ID ??? */ - } - else - { + } else make_bad_inode(inode); - } } } -static mode_t -udf_convert_permissions(struct fileEntry *fe) +static int udf_alloc_i_data(struct inode *inode, size_t size) { - mode_t mode; + struct udf_inode_info *iinfo = UDF_I(inode); + iinfo->i_ext.i_data = kmalloc(size, GFP_KERNEL); + + if (!iinfo->i_ext.i_data) { + udf_err(inode->i_sb, "(ino %ld) no free memory\n", + inode->i_ino); + return -ENOMEM; + } + + return 0; +} + +static umode_t udf_convert_permissions(struct fileEntry *fe) +{ + umode_t mode; uint32_t permissions; uint32_t flags; permissions = le32_to_cpu(fe->permissions); flags = le16_to_cpu(fe->icbTag.flags); - mode = (( permissions ) & S_IRWXO) | - (( permissions >> 2 ) & S_IRWXG) | - (( permissions >> 4 ) & S_IRWXU) | - (( flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) | - (( flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) | - (( flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0); + mode = ((permissions) & S_IRWXO) | + ((permissions >> 2) & S_IRWXG) | + ((permissions >> 4) & S_IRWXU) | + ((flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) | + ((flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) | + ((flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0); return mode; } -/* - * udf_write_inode - * - * PURPOSE - * Write out the specified inode. - * - * DESCRIPTION - * This routine is called whenever an inode is synced. - * Currently this routine is just a placeholder. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ - -int udf_write_inode(struct inode * inode, int sync) +int udf_write_inode(struct inode *inode, struct writeback_control *wbc) { - int ret; - lock_kernel(); - ret = udf_update_inode(inode, sync); - unlock_kernel(); - return ret; + return udf_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL); } -int udf_sync_inode(struct inode * inode) +static int udf_sync_inode(struct inode *inode) { return udf_update_inode(inode, 1); } -static int -udf_update_inode(struct inode *inode, int do_sync) +static int udf_update_inode(struct inode *inode, int do_sync) { struct buffer_head *bh = NULL; struct fileEntry *fe; struct extendedFileEntry *efe; + uint64_t lb_recorded; uint32_t udfperms; uint16_t icbflags; uint16_t crclen; - int i; - kernel_timestamp cpu_time; int err = 0; - - bh = udf_tread(inode->i_sb, - udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0)); - - if (!bh) - { - udf_debug("bread failure\n"); - return -EIO; + struct udf_sb_info *sbi = UDF_SB(inode->i_sb); + unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; + struct udf_inode_info *iinfo = UDF_I(inode); + + bh = udf_tgetblk(inode->i_sb, + udf_get_lb_pblock(inode->i_sb, &iinfo->i_location, 0)); + if (!bh) { + udf_debug("getblk failure\n"); + return -ENOMEM; } - memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); - + lock_buffer(bh); + memset(bh->b_data, 0, inode->i_sb->s_blocksize); fe = (struct fileEntry *)bh->b_data; efe = (struct extendedFileEntry *)bh->b_data; - if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE) - { + if (iinfo->i_use) { struct unallocSpaceEntry *use = (struct unallocSpaceEntry *)bh->b_data; - use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); - memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct unallocSpaceEntry)); - crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) - - sizeof(tag); - use->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); + use->lengthAllocDescs = cpu_to_le32(iinfo->i_lenAlloc); + memcpy(bh->b_data + sizeof(struct unallocSpaceEntry), + iinfo->i_ext.i_data, inode->i_sb->s_blocksize - + sizeof(struct unallocSpaceEntry)); + use->descTag.tagIdent = cpu_to_le16(TAG_IDENT_USE); + use->descTag.tagLocation = + cpu_to_le32(iinfo->i_location.logicalBlockNum); + crclen = sizeof(struct unallocSpaceEntry) + + iinfo->i_lenAlloc - sizeof(struct 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_t *)&(use->descTag))[i]; + use->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)use + + sizeof(struct tag), + crclen)); + use->descTag.tagChecksum = udf_tag_checksum(&use->descTag); - mark_buffer_dirty(bh); - udf_release_data(bh); - return err; + goto out; } - if (inode->i_uid != UDF_SB(inode->i_sb)->s_uid) - fe->uid = cpu_to_le32(inode->i_uid); + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_FORGET)) + fe->uid = cpu_to_le32(-1); + else + fe->uid = cpu_to_le32(i_uid_read(inode)); - if (inode->i_gid != UDF_SB(inode->i_sb)->s_gid) - fe->gid = cpu_to_le32(inode->i_gid); + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_FORGET)) + fe->gid = cpu_to_le32(-1); + else + fe->gid = cpu_to_le32(i_gid_read(inode)); - udfperms = ((inode->i_mode & S_IRWXO) ) | - ((inode->i_mode & S_IRWXG) << 2) | - ((inode->i_mode & S_IRWXU) << 4); + udfperms = ((inode->i_mode & S_IRWXO)) | + ((inode->i_mode & S_IRWXG) << 2) | + ((inode->i_mode & S_IRWXU) << 4); - udfperms |= (le32_to_cpu(fe->permissions) & - (FE_PERM_O_DELETE | FE_PERM_O_CHATTR | - FE_PERM_G_DELETE | FE_PERM_G_CHATTR | - FE_PERM_U_DELETE | FE_PERM_U_CHATTR)); + udfperms |= (le32_to_cpu(fe->permissions) & + (FE_PERM_O_DELETE | FE_PERM_O_CHATTR | + FE_PERM_G_DELETE | FE_PERM_G_CHATTR | + FE_PERM_U_DELETE | FE_PERM_U_CHATTR)); fe->permissions = cpu_to_le32(udfperms); if (S_ISDIR(inode->i_mode)) @@ -1363,27 +1671,24 @@ udf_update_inode(struct inode *inode, int do_sync) fe->informationLength = cpu_to_le64(inode->i_size); - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - { - regid *eid; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + struct regid *eid; struct deviceSpec *dsea = - (struct deviceSpec *) - udf_get_extendedattr(inode, 12, 1); - - if (!dsea) - { + (struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); + if (!dsea) { dsea = (struct deviceSpec *) udf_add_extendedattr(inode, - sizeof(struct deviceSpec) + - sizeof(regid), 12, 0x3); + sizeof(struct deviceSpec) + + sizeof(struct regid), 12, 0x3); dsea->attrType = cpu_to_le32(12); dsea->attrSubtype = 1; - dsea->attrLength = cpu_to_le32(sizeof(struct deviceSpec) + - sizeof(regid)); - dsea->impUseLength = cpu_to_le32(sizeof(regid)); + dsea->attrLength = cpu_to_le32( + sizeof(struct deviceSpec) + + sizeof(struct regid)); + dsea->impUseLength = cpu_to_le32(sizeof(struct regid)); } - eid = (regid *)dsea->impUse; - memset(eid, 0, sizeof(regid)); + eid = (struct regid *)dsea->impUse; + memset(eid, 0, sizeof(struct regid)); strcpy(eid->ident, UDF_ID_DEVELOPER); eid->identSuffix[0] = UDF_OS_CLASS_UNIX; eid->identSuffix[1] = UDF_OS_ID_LINUX; @@ -1391,83 +1696,76 @@ udf_update_inode(struct inode *inode, int do_sync) dsea->minorDeviceIdent = cpu_to_le32(iminor(inode)); } - if (UDF_I_EFE(inode) == 0) - { - memcpy(bh->b_data + sizeof(struct fileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct fileEntry)); - fe->logicalBlocksRecorded = cpu_to_le64( - (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> - (inode->i_sb->s_blocksize_bits - 9)); - - if (udf_time_to_stamp(&cpu_time, inode->i_atime)) - fe->accessTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) - fe->modificationTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) - fe->attrTime = cpu_to_lets(cpu_time); - memset(&(fe->impIdent), 0, sizeof(regid)); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + lb_recorded = 0; /* No extents => no blocks! */ + else + lb_recorded = + (inode->i_blocks + (1 << (blocksize_bits - 9)) - 1) >> + (blocksize_bits - 9); + + if (iinfo->i_efe == 0) { + memcpy(bh->b_data + sizeof(struct fileEntry), + iinfo->i_ext.i_data, + inode->i_sb->s_blocksize - sizeof(struct fileEntry)); + fe->logicalBlocksRecorded = cpu_to_le64(lb_recorded); + + udf_time_to_disk_stamp(&fe->accessTime, inode->i_atime); + udf_time_to_disk_stamp(&fe->modificationTime, inode->i_mtime); + udf_time_to_disk_stamp(&fe->attrTime, inode->i_ctime); + memset(&(fe->impIdent), 0, sizeof(struct regid)); strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER); fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; fe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - fe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); - fe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); - fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); + fe->uniqueID = cpu_to_le64(iinfo->i_unique); + fe->lengthExtendedAttr = cpu_to_le32(iinfo->i_lenEAttr); + fe->lengthAllocDescs = cpu_to_le32(iinfo->i_lenAlloc); + fe->checkpoint = cpu_to_le32(iinfo->i_checkpoint); fe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_FE); crclen = sizeof(struct fileEntry); - } - else - { - memcpy(bh->b_data + sizeof(struct extendedFileEntry), UDF_I_DATA(inode), inode->i_sb->s_blocksize - sizeof(struct extendedFileEntry)); + } else { + memcpy(bh->b_data + sizeof(struct extendedFileEntry), + iinfo->i_ext.i_data, + inode->i_sb->s_blocksize - + sizeof(struct extendedFileEntry)); efe->objectSize = cpu_to_le64(inode->i_size); - efe->logicalBlocksRecorded = cpu_to_le64( - (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >> - (inode->i_sb->s_blocksize_bits - 9)); - - if (UDF_I_CRTIME(inode).tv_sec > inode->i_atime.tv_sec || - (UDF_I_CRTIME(inode).tv_sec == inode->i_atime.tv_sec && - UDF_I_CRTIME(inode).tv_nsec > inode->i_atime.tv_nsec)) - { - UDF_I_CRTIME(inode) = inode->i_atime; - } - if (UDF_I_CRTIME(inode).tv_sec > inode->i_mtime.tv_sec || - (UDF_I_CRTIME(inode).tv_sec == inode->i_mtime.tv_sec && - UDF_I_CRTIME(inode).tv_nsec > inode->i_mtime.tv_nsec)) - { - UDF_I_CRTIME(inode) = inode->i_mtime; - } - if (UDF_I_CRTIME(inode).tv_sec > inode->i_ctime.tv_sec || - (UDF_I_CRTIME(inode).tv_sec == inode->i_ctime.tv_sec && - UDF_I_CRTIME(inode).tv_nsec > inode->i_ctime.tv_nsec)) - { - UDF_I_CRTIME(inode) = inode->i_ctime; - } + efe->logicalBlocksRecorded = cpu_to_le64(lb_recorded); + + if (iinfo->i_crtime.tv_sec > inode->i_atime.tv_sec || + (iinfo->i_crtime.tv_sec == inode->i_atime.tv_sec && + iinfo->i_crtime.tv_nsec > inode->i_atime.tv_nsec)) + iinfo->i_crtime = inode->i_atime; + + if (iinfo->i_crtime.tv_sec > inode->i_mtime.tv_sec || + (iinfo->i_crtime.tv_sec == inode->i_mtime.tv_sec && + iinfo->i_crtime.tv_nsec > inode->i_mtime.tv_nsec)) + iinfo->i_crtime = inode->i_mtime; - if (udf_time_to_stamp(&cpu_time, inode->i_atime)) - efe->accessTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_mtime)) - efe->modificationTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode))) - efe->createTime = cpu_to_lets(cpu_time); - if (udf_time_to_stamp(&cpu_time, inode->i_ctime)) - efe->attrTime = cpu_to_lets(cpu_time); + if (iinfo->i_crtime.tv_sec > inode->i_ctime.tv_sec || + (iinfo->i_crtime.tv_sec == inode->i_ctime.tv_sec && + iinfo->i_crtime.tv_nsec > inode->i_ctime.tv_nsec)) + iinfo->i_crtime = inode->i_ctime; - memset(&(efe->impIdent), 0, sizeof(regid)); + udf_time_to_disk_stamp(&efe->accessTime, inode->i_atime); + udf_time_to_disk_stamp(&efe->modificationTime, inode->i_mtime); + udf_time_to_disk_stamp(&efe->createTime, iinfo->i_crtime); + udf_time_to_disk_stamp(&efe->attrTime, inode->i_ctime); + + memset(&(efe->impIdent), 0, sizeof(struct regid)); strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER); efe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; efe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - efe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode)); - efe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode)); - efe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode)); + efe->uniqueID = cpu_to_le64(iinfo->i_unique); + efe->lengthExtendedAttr = cpu_to_le32(iinfo->i_lenEAttr); + efe->lengthAllocDescs = cpu_to_le32(iinfo->i_lenAlloc); + efe->checkpoint = cpu_to_le32(iinfo->i_checkpoint); efe->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EFE); crclen = sizeof(struct extendedFileEntry); } - if (UDF_I_STRAT4096(inode)) - { + if (iinfo->i_strat4096) { fe->icbTag.strategyType = cpu_to_le16(4096); fe->icbTag.strategyParameter = cpu_to_le16(1); fe->icbTag.numEntries = cpu_to_le16(2); - } - else - { + } else { fe->icbTag.strategyType = cpu_to_le16(4); fe->icbTag.numEntries = cpu_to_le16(1); } @@ -1487,7 +1785,7 @@ udf_update_inode(struct inode *inode, int do_sync) else if (S_ISSOCK(inode->i_mode)) fe->icbTag.fileType = ICBTAG_FILE_TYPE_SOCKET; - icbflags = UDF_I_ALLOCTYPE(inode) | + icbflags = iinfo->i_alloc_type | ((inode->i_mode & S_ISUID) ? ICBTAG_FLAG_SETUID : 0) | ((inode->i_mode & S_ISGID) ? ICBTAG_FLAG_SETGID : 0) | ((inode->i_mode & S_ISVTX) ? ICBTAG_FLAG_STICKY : 0) | @@ -1496,39 +1794,39 @@ udf_update_inode(struct inode *inode, int do_sync) ICBTAG_FLAG_SETGID | ICBTAG_FLAG_STICKY)); fe->icbTag.flags = cpu_to_le16(icbflags); - if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) + if (sbi->s_udfrev >= 0x0200) fe->descTag.descVersion = cpu_to_le16(3); else fe->descTag.descVersion = cpu_to_le16(2); - fe->descTag.tagSerialNum = cpu_to_le16(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.tagSerialNum = cpu_to_le16(sbi->s_serial_number); + fe->descTag.tagLocation = cpu_to_le32( + iinfo->i_location.logicalBlockNum); + crclen += iinfo->i_lenEAttr + iinfo->i_lenAlloc - sizeof(struct tag); fe->descTag.descCRCLength = cpu_to_le16(crclen); - fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0)); + fe->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)fe + sizeof(struct tag), + crclen)); + fe->descTag.tagChecksum = udf_tag_checksum(&fe->descTag); - fe->descTag.tagChecksum = 0; - for (i=0; i<16; i++) - if (i != 4) - fe->descTag.tagChecksum += ((uint8_t *)&(fe->descTag))[i]; +out: + set_buffer_uptodate(bh); + unlock_buffer(bh); /* write the data blocks */ mark_buffer_dirty(bh); - if (do_sync) - { + if (do_sync) { sync_dirty_buffer(bh); - if (buffer_req(bh) && !buffer_uptodate(bh)) - { - printk("IO error syncing udf inode [%s:%08lx]\n", - inode->i_sb->s_id, inode->i_ino); + if (buffer_write_io_error(bh)) { + udf_warn(inode->i_sb, "IO error syncing udf inode [%08lx]\n", + inode->i_ino); err = -EIO; } } - udf_release_data(bh); + brelse(bh); + return err; } -struct inode * -udf_iget(struct super_block *sb, kernel_lb_addr ino) +struct inode *udf_iget(struct super_block *sb, struct kernel_lb_addr *ino) { unsigned long block = udf_get_lb_pblock(sb, ino, 0); struct inode *inode = iget_locked(sb, block); @@ -1537,7 +1835,7 @@ udf_iget(struct super_block *sb, kernel_lb_addr ino) return NULL; if (inode->i_state & I_NEW) { - memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(kernel_lb_addr)); + memcpy(&UDF_I(inode)->i_location, ino, sizeof(struct kernel_lb_addr)); __udf_read_inode(inode); unlock_new_inode(inode); } @@ -1545,9 +1843,10 @@ udf_iget(struct super_block *sb, kernel_lb_addr ino) if (is_bad_inode(inode)) goto out_iput; - if (ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum)) { + if (ino->logicalBlockNum >= UDF_SB(sb)-> + s_partmaps[ino->partitionReferenceNum].s_partition_len) { udf_debug("block=%d, partition=%d out of range\n", - ino.logicalBlockNum, ino.partitionReferenceNum); + ino->logicalBlockNum, ino->partitionReferenceNum); make_bad_inode(inode); goto out_iput; } @@ -1559,45 +1858,46 @@ udf_iget(struct super_block *sb, kernel_lb_addr ino) return NULL; } -int8_t udf_add_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, - kernel_lb_addr eloc, uint32_t elen, struct buffer_head **bh, int inc) +int udf_add_aext(struct inode *inode, struct extent_position *epos, + struct kernel_lb_addr *eloc, uint32_t elen, int inc) { int adsize; - short_ad *sad = NULL; - long_ad *lad = NULL; + struct short_ad *sad = NULL; + struct long_ad *lad = NULL; struct allocExtDesc *aed; - int8_t etype; uint8_t *ptr; + struct udf_inode_info *iinfo = UDF_I(inode); - if (!*bh) - ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); + if (!epos->bh) + ptr = iinfo->i_ext.i_data + epos->offset - + udf_file_entry_alloc_offset(inode) + + iinfo->i_lenEAttr; else - ptr = (*bh)->b_data + *extoffset; + ptr = epos->bh->b_data + epos->offset; - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); else - return -1; + return -EIO; - if (*extoffset + (2 * adsize) > inode->i_sb->s_blocksize) - { - char *sptr, *dptr; + if (epos->offset + (2 * adsize) > inode->i_sb->s_blocksize) { + unsigned char *sptr, *dptr; struct buffer_head *nbh; int err, loffset; - kernel_lb_addr obloc = *bloc; - - if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, NULL, - obloc.partitionReferenceNum, obloc.logicalBlockNum, &err))) - { - return -1; - } - if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb, - *bloc, 0)))) - { - return -1; - } + struct kernel_lb_addr obloc = epos->block; + + epos->block.logicalBlockNum = udf_new_block(inode->i_sb, NULL, + obloc.partitionReferenceNum, + obloc.logicalBlockNum, &err); + if (!epos->block.logicalBlockNum) + return -ENOSPC; + nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb, + &epos->block, + 0)); + if (!nbh) + return -EIO; lock_buffer(nbh); memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(nbh); @@ -1606,170 +1906,154 @@ int8_t udf_add_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, aed = (struct allocExtDesc *)(nbh->b_data); if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) - aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum); - if (*extoffset + adsize > inode->i_sb->s_blocksize) - { - loffset = *extoffset; + aed->previousAllocExtLocation = + cpu_to_le32(obloc.logicalBlockNum); + if (epos->offset + adsize > inode->i_sb->s_blocksize) { + loffset = epos->offset; aed->lengthAllocDescs = cpu_to_le32(adsize); sptr = ptr - adsize; dptr = nbh->b_data + sizeof(struct allocExtDesc); memcpy(dptr, sptr, adsize); - *extoffset = sizeof(struct allocExtDesc) + adsize; - } - else - { - loffset = *extoffset + adsize; + epos->offset = sizeof(struct allocExtDesc) + adsize; + } else { + loffset = epos->offset + adsize; aed->lengthAllocDescs = cpu_to_le32(0); sptr = ptr; - *extoffset = sizeof(struct allocExtDesc); + epos->offset = sizeof(struct allocExtDesc); - if (*bh) - { - aed = (struct allocExtDesc *)(*bh)->b_data; - aed->lengthAllocDescs = - cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); - } - else - { - UDF_I_LENALLOC(inode) += adsize; + if (epos->bh) { + aed = (struct allocExtDesc *)epos->bh->b_data; + le32_add_cpu(&aed->lengthAllocDescs, adsize); + } else { + iinfo->i_lenAlloc += adsize; mark_inode_dirty(inode); } } - if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) + if (UDF_SB(inode->i_sb)->s_udfrev >= 0x0200) udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1, - bloc->logicalBlockNum, sizeof(tag)); + epos->block.logicalBlockNum, sizeof(struct tag)); else udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1, - bloc->logicalBlockNum, sizeof(tag)); - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICBTAG_FLAG_AD_SHORT: - { - sad = (short_ad *)sptr; - sad->extLength = cpu_to_le32( - EXT_NEXT_EXTENT_ALLOCDECS | - inode->i_sb->s_blocksize); - sad->extPosition = cpu_to_le32(bloc->logicalBlockNum); - break; - } - case ICBTAG_FLAG_AD_LONG: - { - lad = (long_ad *)sptr; - lad->extLength = cpu_to_le32( - EXT_NEXT_EXTENT_ALLOCDECS | - inode->i_sb->s_blocksize); - lad->extLocation = cpu_to_lelb(*bloc); - memset(lad->impUse, 0x00, sizeof(lad->impUse)); - break; - } + epos->block.logicalBlockNum, sizeof(struct tag)); + switch (iinfo->i_alloc_type) { + case ICBTAG_FLAG_AD_SHORT: + sad = (struct short_ad *)sptr; + sad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS | + inode->i_sb->s_blocksize); + sad->extPosition = + cpu_to_le32(epos->block.logicalBlockNum); + break; + case ICBTAG_FLAG_AD_LONG: + lad = (struct long_ad *)sptr; + lad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS | + inode->i_sb->s_blocksize); + lad->extLocation = cpu_to_lelb(epos->block); + memset(lad->impUse, 0x00, sizeof(lad->impUse)); + break; } - if (*bh) - { - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag((*bh)->b_data, loffset); + if (epos->bh) { + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) + udf_update_tag(epos->bh->b_data, loffset); else - udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(*bh, inode); - udf_release_data(*bh); - } - else + udf_update_tag(epos->bh->b_data, + sizeof(struct allocExtDesc)); + mark_buffer_dirty_inode(epos->bh, inode); + brelse(epos->bh); + } else { mark_inode_dirty(inode); - *bh = nbh; + } + epos->bh = nbh; } - etype = udf_write_aext(inode, *bloc, extoffset, eloc, elen, *bh, inc); + udf_write_aext(inode, epos, eloc, elen, inc); - if (!*bh) - { - UDF_I_LENALLOC(inode) += adsize; + if (!epos->bh) { + iinfo->i_lenAlloc += adsize; mark_inode_dirty(inode); - } - else - { - aed = (struct allocExtDesc *)(*bh)->b_data; - aed->lengthAllocDescs = - cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize)); + } else { + aed = (struct allocExtDesc *)epos->bh->b_data; + le32_add_cpu(&aed->lengthAllocDescs, adsize); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) + udf_update_tag(epos->bh->b_data, + epos->offset + (inc ? 0 : adsize)); else - udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(*bh, inode); + udf_update_tag(epos->bh->b_data, + sizeof(struct allocExtDesc)); + mark_buffer_dirty_inode(epos->bh, inode); } - return etype; + return 0; } -int8_t udf_write_aext(struct inode *inode, kernel_lb_addr bloc, int *extoffset, - kernel_lb_addr eloc, uint32_t elen, struct buffer_head *bh, int inc) +void udf_write_aext(struct inode *inode, struct extent_position *epos, + struct kernel_lb_addr *eloc, uint32_t elen, int inc) { int adsize; uint8_t *ptr; - - if (!bh) - ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); + struct short_ad *sad; + struct long_ad *lad; + struct udf_inode_info *iinfo = UDF_I(inode); + + if (!epos->bh) + ptr = iinfo->i_ext.i_data + epos->offset - + udf_file_entry_alloc_offset(inode) + + iinfo->i_lenEAttr; else - { - ptr = bh->b_data + *extoffset; - atomic_inc(&bh->b_count); - } - - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICBTAG_FLAG_AD_SHORT: - { - short_ad *sad = (short_ad *)ptr; - sad->extLength = cpu_to_le32(elen); - sad->extPosition = cpu_to_le32(eloc.logicalBlockNum); - adsize = sizeof(short_ad); - break; - } - case ICBTAG_FLAG_AD_LONG: - { - long_ad *lad = (long_ad *)ptr; - lad->extLength = cpu_to_le32(elen); - lad->extLocation = cpu_to_lelb(eloc); - memset(lad->impUse, 0x00, sizeof(lad->impUse)); - adsize = sizeof(long_ad); - break; - } - default: - return -1; + ptr = epos->bh->b_data + epos->offset; + + switch (iinfo->i_alloc_type) { + case ICBTAG_FLAG_AD_SHORT: + sad = (struct short_ad *)ptr; + sad->extLength = cpu_to_le32(elen); + sad->extPosition = cpu_to_le32(eloc->logicalBlockNum); + adsize = sizeof(struct short_ad); + break; + case ICBTAG_FLAG_AD_LONG: + lad = (struct long_ad *)ptr; + lad->extLength = cpu_to_le32(elen); + lad->extLocation = cpu_to_lelb(*eloc); + memset(lad->impUse, 0x00, sizeof(lad->impUse)); + adsize = sizeof(struct long_ad); + break; + default: + return; } - if (bh) - { - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - { - struct allocExtDesc *aed = (struct allocExtDesc *)(bh)->b_data; - udf_update_tag((bh)->b_data, - le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc)); - } - mark_buffer_dirty_inode(bh, inode); - udf_release_data(bh); - } - else + if (epos->bh) { + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) { + struct allocExtDesc *aed = + (struct allocExtDesc *)epos->bh->b_data; + udf_update_tag(epos->bh->b_data, + le32_to_cpu(aed->lengthAllocDescs) + + sizeof(struct allocExtDesc)); + } + mark_buffer_dirty_inode(epos->bh, inode); + } else { mark_inode_dirty(inode); + } if (inc) - *extoffset += adsize; - return (elen >> 30); + epos->offset += adsize; } -int8_t udf_next_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, - kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc) +int8_t udf_next_aext(struct inode *inode, struct extent_position *epos, + struct kernel_lb_addr *eloc, uint32_t *elen, int inc) { int8_t etype; - while ((etype = udf_current_aext(inode, bloc, extoffset, eloc, elen, bh, inc)) == - (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) - { - *bloc = *eloc; - *extoffset = sizeof(struct allocExtDesc); - udf_release_data(*bh); - if (!(*bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, *bloc, 0)))) - { - udf_debug("reading block %d failed!\n", - udf_get_lb_pblock(inode->i_sb, *bloc, 0)); + while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) == + (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { + int block; + epos->block = *eloc; + epos->offset = sizeof(struct allocExtDesc); + brelse(epos->bh); + block = udf_get_lb_pblock(inode->i_sb, &epos->block, 0); + epos->bh = udf_tread(inode->i_sb, block); + if (!epos->bh) { + udf_debug("reading block %d failed!\n", block); return -1; } } @@ -1777,233 +2061,215 @@ int8_t udf_next_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, return etype; } -int8_t udf_current_aext(struct inode *inode, kernel_lb_addr *bloc, int *extoffset, - kernel_lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc) +int8_t udf_current_aext(struct inode *inode, struct extent_position *epos, + struct kernel_lb_addr *eloc, uint32_t *elen, int inc) { int alen; int8_t etype; uint8_t *ptr; - - if (!*bh) - { - if (!(*extoffset)) - *extoffset = udf_file_entry_alloc_offset(inode); - ptr = UDF_I_DATA(inode) + *extoffset - udf_file_entry_alloc_offset(inode) + UDF_I_LENEATTR(inode); - alen = udf_file_entry_alloc_offset(inode) + UDF_I_LENALLOC(inode); + struct short_ad *sad; + struct long_ad *lad; + struct udf_inode_info *iinfo = UDF_I(inode); + + if (!epos->bh) { + if (!epos->offset) + epos->offset = udf_file_entry_alloc_offset(inode); + ptr = iinfo->i_ext.i_data + epos->offset - + udf_file_entry_alloc_offset(inode) + + iinfo->i_lenEAttr; + alen = udf_file_entry_alloc_offset(inode) + + iinfo->i_lenAlloc; + } else { + if (!epos->offset) + epos->offset = sizeof(struct allocExtDesc); + ptr = epos->bh->b_data + epos->offset; + alen = sizeof(struct allocExtDesc) + + le32_to_cpu(((struct allocExtDesc *)epos->bh->b_data)-> + lengthAllocDescs); } - else - { - if (!(*extoffset)) - *extoffset = sizeof(struct allocExtDesc); - ptr = (*bh)->b_data + *extoffset; - alen = sizeof(struct allocExtDesc) + le32_to_cpu(((struct allocExtDesc *)(*bh)->b_data)->lengthAllocDescs); - } - - switch (UDF_I_ALLOCTYPE(inode)) - { - case ICBTAG_FLAG_AD_SHORT: - { - short_ad *sad; - if (!(sad = udf_get_fileshortad(ptr, alen, extoffset, inc))) - return -1; - - etype = le32_to_cpu(sad->extLength) >> 30; - eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); - eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; - *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; - break; - } - case ICBTAG_FLAG_AD_LONG: - { - long_ad *lad; - - if (!(lad = udf_get_filelongad(ptr, alen, extoffset, inc))) - return -1; - - etype = le32_to_cpu(lad->extLength) >> 30; - *eloc = lelb_to_cpu(lad->extLocation); - *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; - break; - } - default: - { - udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode)); + switch (iinfo->i_alloc_type) { + case ICBTAG_FLAG_AD_SHORT: + sad = udf_get_fileshortad(ptr, alen, &epos->offset, inc); + if (!sad) return -1; - } + etype = le32_to_cpu(sad->extLength) >> 30; + eloc->logicalBlockNum = le32_to_cpu(sad->extPosition); + eloc->partitionReferenceNum = + iinfo->i_location.partitionReferenceNum; + *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK; + break; + case ICBTAG_FLAG_AD_LONG: + lad = udf_get_filelongad(ptr, alen, &epos->offset, inc); + if (!lad) + return -1; + etype = le32_to_cpu(lad->extLength) >> 30; + *eloc = lelb_to_cpu(lad->extLocation); + *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK; + break; + default: + udf_debug("alloc_type = %d unsupported\n", iinfo->i_alloc_type); + return -1; } return etype; } -static int8_t -udf_insert_aext(struct inode *inode, kernel_lb_addr bloc, int extoffset, - kernel_lb_addr neloc, uint32_t nelen, struct buffer_head *bh) +static int8_t udf_insert_aext(struct inode *inode, struct extent_position epos, + struct kernel_lb_addr neloc, uint32_t nelen) { - kernel_lb_addr oeloc; + struct kernel_lb_addr oeloc; uint32_t oelen; int8_t etype; - if (bh) - atomic_inc(&bh->b_count); - - while ((etype = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1) - { - udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); + if (epos.bh) + get_bh(epos.bh); + while ((etype = udf_next_aext(inode, &epos, &oeloc, &oelen, 0)) != -1) { + udf_write_aext(inode, &epos, &neloc, nelen, 1); neloc = oeloc; nelen = (etype << 30) | oelen; } - udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1); - udf_release_data(bh); + udf_add_aext(inode, &epos, &neloc, nelen, 1); + brelse(epos.bh); + return (nelen >> 30); } -int8_t udf_delete_aext(struct inode *inode, kernel_lb_addr nbloc, int nextoffset, - kernel_lb_addr eloc, uint32_t elen, struct buffer_head *nbh) +int8_t udf_delete_aext(struct inode *inode, struct extent_position epos, + struct kernel_lb_addr eloc, uint32_t elen) { - struct buffer_head *obh; - kernel_lb_addr obloc; - int oextoffset, adsize; + struct extent_position oepos; + int adsize; int8_t etype; struct allocExtDesc *aed; + struct udf_inode_info *iinfo; - if (nbh) - { - atomic_inc(&nbh->b_count); - atomic_inc(&nbh->b_count); + if (epos.bh) { + get_bh(epos.bh); + get_bh(epos.bh); } - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); + iinfo = UDF_I(inode); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); else adsize = 0; - obh = nbh; - obloc = nbloc; - oextoffset = nextoffset; - - if (udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1) == -1) + oepos = epos; + if (udf_next_aext(inode, &epos, &eloc, &elen, 1) == -1) return -1; - while ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1) - { - udf_write_aext(inode, obloc, &oextoffset, eloc, (etype << 30) | elen, obh, 1); - if (obh != nbh) - { - obloc = nbloc; - udf_release_data(obh); - atomic_inc(&nbh->b_count); - obh = nbh; - oextoffset = nextoffset - adsize; + while ((etype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { + udf_write_aext(inode, &oepos, &eloc, (etype << 30) | elen, 1); + if (oepos.bh != epos.bh) { + oepos.block = epos.block; + brelse(oepos.bh); + get_bh(epos.bh); + oepos.bh = epos.bh; + oepos.offset = epos.offset - adsize; } } - memset(&eloc, 0x00, sizeof(kernel_lb_addr)); + memset(&eloc, 0x00, sizeof(struct kernel_lb_addr)); elen = 0; - if (nbh != obh) - { - udf_free_blocks(inode->i_sb, inode, nbloc, 0, 1); - udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); - udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); - if (!obh) - { - UDF_I_LENALLOC(inode) -= (adsize * 2); + if (epos.bh != oepos.bh) { + udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1); + udf_write_aext(inode, &oepos, &eloc, elen, 1); + udf_write_aext(inode, &oepos, &eloc, elen, 1); + if (!oepos.bh) { + iinfo->i_lenAlloc -= (adsize * 2); mark_inode_dirty(inode); - } - else - { - aed = (struct allocExtDesc *)(obh)->b_data; - aed->lengthAllocDescs = - cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize)); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag((obh)->b_data, oextoffset - (2*adsize)); + } else { + aed = (struct allocExtDesc *)oepos.bh->b_data; + le32_add_cpu(&aed->lengthAllocDescs, -(2 * adsize)); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) + udf_update_tag(oepos.bh->b_data, + oepos.offset - (2 * adsize)); else - udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(obh, inode); - } - } - else - { - udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1); - if (!obh) - { - UDF_I_LENALLOC(inode) -= adsize; + udf_update_tag(oepos.bh->b_data, + sizeof(struct allocExtDesc)); + mark_buffer_dirty_inode(oepos.bh, inode); + } + } else { + udf_write_aext(inode, &oepos, &eloc, elen, 1); + if (!oepos.bh) { + iinfo->i_lenAlloc -= adsize; mark_inode_dirty(inode); - } - else - { - aed = (struct allocExtDesc *)(obh)->b_data; - aed->lengthAllocDescs = - cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag((obh)->b_data, oextoffset - adsize); + } else { + aed = (struct allocExtDesc *)oepos.bh->b_data; + le32_add_cpu(&aed->lengthAllocDescs, -adsize); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) + udf_update_tag(oepos.bh->b_data, + epos.offset - adsize); else - udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(obh, inode); + udf_update_tag(oepos.bh->b_data, + sizeof(struct allocExtDesc)); + mark_buffer_dirty_inode(oepos.bh, inode); } } - - udf_release_data(nbh); - udf_release_data(obh); + + brelse(epos.bh); + brelse(oepos.bh); + return (elen >> 30); } -int8_t inode_bmap(struct inode *inode, int block, kernel_lb_addr *bloc, uint32_t *extoffset, - kernel_lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh) +int8_t inode_bmap(struct inode *inode, sector_t block, + struct extent_position *pos, struct kernel_lb_addr *eloc, + uint32_t *elen, sector_t *offset) { - uint64_t lbcount = 0, bcount = (uint64_t)block << inode->i_sb->s_blocksize_bits; + unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; + loff_t lbcount = 0, bcount = + (loff_t) block << blocksize_bits; int8_t etype; + struct udf_inode_info *iinfo; - if (block < 0) - { - printk(KERN_ERR "udf: inode_bmap: block < 0\n"); - return -1; + iinfo = UDF_I(inode); + if (!udf_read_extent_cache(inode, bcount, &lbcount, pos)) { + pos->offset = 0; + pos->block = iinfo->i_location; + pos->bh = NULL; } - if (!inode) - { - printk(KERN_ERR "udf: inode_bmap: NULL inode\n"); - return -1; - } - - *extoffset = 0; *elen = 0; - *bloc = UDF_I_LOCATION(inode); - - do - { - if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1) - { - *offset = bcount - lbcount; - UDF_I_LENEXTENTS(inode) = lbcount; + do { + etype = udf_next_aext(inode, pos, eloc, elen, 1); + if (etype == -1) { + *offset = (bcount - lbcount) >> blocksize_bits; + iinfo->i_lenExtents = lbcount; return -1; } lbcount += *elen; } while (lbcount <= bcount); - - *offset = bcount + *elen - lbcount; + /* update extent cache */ + udf_update_extent_cache(inode, lbcount - *elen, pos, 1); + *offset = (bcount + *elen - lbcount) >> blocksize_bits; return etype; } -long udf_block_map(struct inode *inode, long block) +long udf_block_map(struct inode *inode, sector_t block) { - kernel_lb_addr eloc, bloc; - uint32_t offset, extoffset, elen; - struct buffer_head *bh = NULL; + struct kernel_lb_addr eloc; + uint32_t elen; + sector_t offset; + struct extent_position epos = {}; int ret; - lock_kernel(); + down_read(&UDF_I(inode)->i_data_sem); - if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) - ret = udf_get_lb_pblock(inode->i_sb, eloc, offset >> inode->i_sb->s_blocksize_bits); + if (inode_bmap(inode, block, &epos, &eloc, &elen, &offset) == + (EXT_RECORDED_ALLOCATED >> 30)) + ret = udf_get_lb_pblock(inode->i_sb, &eloc, offset); else ret = 0; - unlock_kernel(); - udf_release_data(bh); + up_read(&UDF_I(inode)->i_data_sem); + brelse(epos.bh); if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV)) return udf_fixed_to_variable(ret); diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c index 2da5087dfe0..6583fe9b064 100644 --- a/fs/udf/lowlevel.c +++ b/fs/udf/lowlevel.c @@ -4,11 +4,6 @@ * PURPOSE * Low Level Device Routines for the UDF filesystem * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -28,46 +23,41 @@ #include <linux/cdrom.h> #include <asm/uaccess.h> -#include <linux/udf_fs.h> #include "udf_sb.h" -unsigned int -udf_get_last_session(struct super_block *sb) +unsigned int udf_get_last_session(struct super_block *sb) { struct cdrom_multisession ms_info; unsigned int vol_desc_start; struct block_device *bdev = sb->s_bdev; int i; - vol_desc_start=0; - ms_info.addr_format=CDROM_LBA; - i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info); - -#define WE_OBEY_THE_WRITTEN_STANDARDS 1 + vol_desc_start = 0; + ms_info.addr_format = CDROM_LBA; + i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long)&ms_info); - if (i == 0) - { + 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 + ms_info.xa_flag ? "yes" : "no", ms_info.addr.lba); if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */ -#endif vol_desc_start = ms_info.addr.lba; - } - else - { + } else { udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i); } return vol_desc_start; } -unsigned long -udf_get_last_block(struct super_block *sb) +unsigned long udf_get_last_block(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; unsigned long lblock = 0; - if (ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock)) + /* + * ioctl failed or returned obviously bogus value? + * Try using the device size... + */ + if (ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock) || + lblock == 0) lblock = bdev->bd_inode->i_size >> sb->s_blocksize_bits; if (lblock) diff --git a/fs/udf/misc.c b/fs/udf/misc.c index fd321f9ace8..c175b4dabc1 100644 --- a/fs/udf/misc.c +++ b/fs/udf/misc.c @@ -4,11 +4,6 @@ * PURPOSE * Miscellaneous routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -28,14 +23,13 @@ #include <linux/fs.h> #include <linux/string.h> -#include <linux/udf_fs.h> #include <linux/buffer_head.h> +#include <linux/crc-itu-t.h> #include "udf_i.h" #include "udf_sb.h" -struct buffer_head * -udf_tgetblk(struct super_block *sb, int block) +struct buffer_head *udf_tgetblk(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_getblk(sb, udf_fixed_to_variable(block)); @@ -43,8 +37,7 @@ udf_tgetblk(struct super_block *sb, int block) return sb_getblk(sb, block); } -struct buffer_head * -udf_tread(struct super_block *sb, int block) +struct buffer_head *udf_tread(struct super_block *sb, int block) { if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV)) return sb_bread(sb, udf_fixed_to_variable(block)); @@ -52,132 +45,130 @@ udf_tread(struct super_block *sb, int block) return sb_bread(sb, block); } -struct genericFormat * -udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type, - uint8_t loc) +struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size, + uint32_t type, uint8_t loc) { uint8_t *ea = NULL, *ad = NULL; int offset; uint16_t crclen; - int i; + struct udf_inode_info *iinfo = UDF_I(inode); - ea = UDF_I_DATA(inode); - if (UDF_I_LENEATTR(inode)) - ad = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); - else - { + ea = iinfo->i_ext.i_data; + if (iinfo->i_lenEAttr) { + ad = iinfo->i_ext.i_data + iinfo->i_lenEAttr; + } else { ad = ea; size += sizeof(struct extendedAttrHeaderDesc); } offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) - - UDF_I_LENALLOC(inode); + iinfo->i_lenAlloc; /* TODO - Check for FreeEASpace */ - if (loc & 0x01 && offset >= size) - { + 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 (iinfo->i_lenAlloc) + memmove(&ad[size], ad, iinfo->i_lenAlloc); - if (UDF_I_LENEATTR(inode)) - { + if (iinfo->i_lenEAttr) { /* check checksum/crc */ - if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || - le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) - { + if (eahd->descTag.tagIdent != + cpu_to_le16(TAG_IDENT_EAHD) || + le32_to_cpu(eahd->descTag.tagLocation) != + iinfo->i_location.logicalBlockNum) return NULL; - } - } - else - { + } else { + struct udf_sb_info *sbi = UDF_SB(inode->i_sb); + size -= sizeof(struct extendedAttrHeaderDesc); - UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc); + iinfo->i_lenEAttr += + sizeof(struct extendedAttrHeaderDesc); eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD); - if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200) + if (sbi->s_udfrev >= 0x0200) eahd->descTag.descVersion = cpu_to_le16(3); else eahd->descTag.descVersion = cpu_to_le16(2); - eahd->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb)); - eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum); + eahd->descTag.tagSerialNum = + cpu_to_le16(sbi->s_serial_number); + eahd->descTag.tagLocation = cpu_to_le32( + iinfo->i_location.logicalBlockNum); eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF); eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF); } - offset = UDF_I_LENEATTR(inode); - if (type < 2048) - { - if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) - { - uint32_t aal = le32_to_cpu(eahd->appAttrLocation); + offset = iinfo->i_lenEAttr; + if (type < 2048) { + if (le32_to_cpu(eahd->appAttrLocation) < + iinfo->i_lenEAttr) { + uint32_t aal = + le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; - eahd->appAttrLocation = cpu_to_le32(aal + size); + eahd->appAttrLocation = + cpu_to_le32(aal + size); } - if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode)) - { - uint32_t ial = le32_to_cpu(eahd->impAttrLocation); + if (le32_to_cpu(eahd->impAttrLocation) < + iinfo->i_lenEAttr) { + uint32_t ial = + le32_to_cpu(eahd->impAttrLocation); memmove(&ea[offset - ial + size], &ea[ial], offset - ial); offset -= ial; - eahd->impAttrLocation = cpu_to_le32(ial + size); + eahd->impAttrLocation = + cpu_to_le32(ial + size); } - } - else if (type < 65536) - { - if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode)) - { - uint32_t aal = le32_to_cpu(eahd->appAttrLocation); + } else if (type < 65536) { + if (le32_to_cpu(eahd->appAttrLocation) < + iinfo->i_lenEAttr) { + uint32_t aal = + le32_to_cpu(eahd->appAttrLocation); memmove(&ea[offset - aal + size], &ea[aal], offset - aal); offset -= aal; - eahd->appAttrLocation = cpu_to_le32(aal + size); + eahd->appAttrLocation = + cpu_to_le32(aal + size); } } /* rewrite CRC + checksum of eahd */ - crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(tag); + crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(struct tag); eahd->descTag.descCRCLength = cpu_to_le16(crclen); - eahd->descTag.descCRC = cpu_to_le16(udf_crc((char *)eahd + sizeof(tag), crclen, 0)); - eahd->descTag.tagChecksum = 0; - for (i=0; i<16; i++) - if (i != 4) - eahd->descTag.tagChecksum += ((uint8_t *)&(eahd->descTag))[i]; - UDF_I_LENEATTR(inode) += size; + eahd->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)eahd + + sizeof(struct tag), crclen)); + eahd->descTag.tagChecksum = udf_tag_checksum(&eahd->descTag); + iinfo->i_lenEAttr += size; return (struct genericFormat *)&ea[offset]; } if (loc & 0x02) - { - } + ; + return NULL; } -struct genericFormat * -udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) +struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type, + uint8_t subtype) { struct genericFormat *gaf; uint8_t *ea = NULL; uint32_t offset; + struct udf_inode_info *iinfo = UDF_I(inode); - ea = UDF_I_DATA(inode); + ea = iinfo->i_ext.i_data; - if (UDF_I_LENEATTR(inode)) - { + if (iinfo->i_lenEAttr) { struct extendedAttrHeaderDesc *eahd; eahd = (struct extendedAttrHeaderDesc *)ea; /* check checksum/crc */ - if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD || - le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum) - { + if (eahd->descTag.tagIdent != + cpu_to_le16(TAG_IDENT_EAHD) || + le32_to_cpu(eahd->descTag.tagLocation) != + iinfo->i_location.logicalBlockNum) return NULL; - } - + if (type < 2048) offset = sizeof(struct extendedAttrHeaderDesc); else if (type < 65536) @@ -185,15 +176,16 @@ udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) else offset = le32_to_cpu(eahd->appAttrLocation); - while (offset < UDF_I_LENEATTR(inode)) - { + while (offset < iinfo->i_lenEAttr) { gaf = (struct genericFormat *)&ea[offset]; - if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype) + if (le32_to_cpu(gaf->attrType) == type && + gaf->attrSubtype == subtype) return gaf; else offset += le32_to_cpu(gaf->attrLength); } } + return NULL; } @@ -207,107 +199,101 @@ udf_get_extendedattr(struct inode *inode, uint32_t type, uint8_t subtype) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -struct buffer_head * -udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident) +struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block, + uint32_t location, uint16_t *ident) { - tag *tag_p; + struct tag *tag_p; struct buffer_head *bh = NULL; - register uint8_t checksum; - register int i; + u8 checksum; /* Read the block */ if (block == 0xFFFFFFFF) return NULL; - bh = udf_tread(sb, block + UDF_SB_SESSION(sb)); - if (!bh) - { - udf_debug("block=%d, location=%d: read failed\n", block + UDF_SB_SESSION(sb), location); + bh = udf_tread(sb, block); + if (!bh) { + udf_err(sb, "read failed, block=%u, location=%d\n", + block, location); return NULL; } - tag_p = (tag *)(bh->b_data); + tag_p = (struct tag *)(bh->b_data); *ident = le16_to_cpu(tag_p->tagIdent); - if ( location != le32_to_cpu(tag_p->tagLocation) ) - { + if (location != le32_to_cpu(tag_p->tagLocation)) { udf_debug("location mismatch block %u, tag %u != %u\n", - block + UDF_SB_SESSION(sb), le32_to_cpu(tag_p->tagLocation), location); + 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_t)(bh->b_data[i]); - for (i = 5; i < 16; i++) - checksum += (uint8_t)(bh->b_data[i]); + checksum = udf_tag_checksum(tag_p); if (checksum != tag_p->tagChecksum) { - printk(KERN_ERR "udf: tag checksum failed block %d\n", block); + udf_err(sb, "tag checksum failed, block %u: 0x%02x != 0x%02x\n", + block, checksum, tag_p->tagChecksum); goto error_out; } /* Verify the tag version */ - if (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", + if (tag_p->descVersion != cpu_to_le16(0x0002U) && + tag_p->descVersion != cpu_to_le16(0x0003U)) { + udf_err(sb, "tag version 0x%04x != 0x0002 || 0x0003, block %u\n", le16_to_cpu(tag_p->descVersion), block); goto error_out; } /* Verify the descriptor CRC */ - if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize || - le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag), - le16_to_cpu(tag_p->descCRCLength), 0)) - { + if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize || + le16_to_cpu(tag_p->descCRC) == crc_itu_t(0, + bh->b_data + sizeof(struct tag), + le16_to_cpu(tag_p->descCRCLength))) return bh; - } - udf_debug("Crc failure block %d: crc = %d, crclen = %d\n", - block + UDF_SB_SESSION(sb), le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength)); + 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; } -struct buffer_head * -udf_read_ptagged(struct super_block *sb, kernel_lb_addr loc, uint32_t offset, uint16_t *ident) +struct buffer_head *udf_read_ptagged(struct super_block *sb, + struct kernel_lb_addr *loc, + uint32_t offset, uint16_t *ident) { return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset), - loc.logicalBlockNum + offset, ident); -} - -void udf_release_data(struct buffer_head *bh) -{ - if (bh) - brelse(bh); + loc->logicalBlockNum + offset, ident); } void udf_update_tag(char *data, int length) { - tag *tptr = (tag *)data; - int i; - - length -= sizeof(tag); + struct tag *tptr = (struct tag *)data; + length -= sizeof(struct tag); - tptr->tagChecksum = 0; tptr->descCRCLength = cpu_to_le16(length); - tptr->descCRC = cpu_to_le16(udf_crc(data + sizeof(tag), length, 0)); - - for (i=0; i<16; i++) - if (i != 4) - tptr->tagChecksum += (uint8_t)(data[i]); + tptr->descCRC = cpu_to_le16(crc_itu_t(0, data + sizeof(struct tag), length)); + tptr->tagChecksum = udf_tag_checksum(tptr); } void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum, - uint32_t loc, int length) + uint32_t loc, int length) { - tag *tptr = (tag *)data; + struct tag *tptr = (struct tag *)data; tptr->tagIdent = cpu_to_le16(ident); tptr->descVersion = cpu_to_le16(version); tptr->tagSerialNum = cpu_to_le16(snum); tptr->tagLocation = cpu_to_le32(loc); udf_update_tag(data, length); } + +u8 udf_tag_checksum(const struct tag *t) +{ + u8 *data = (u8 *)t; + u8 checksum = 0; + int i; + for (i = 0; i < sizeof(struct tag); ++i) + if (i != 4) /* position of checksum */ + checksum += data[i]; + return checksum; +} diff --git a/fs/udf/namei.c b/fs/udf/namei.c index ac191ed7df0..9737cba1357 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -4,11 +4,6 @@ * PURPOSE * Inode name handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -32,25 +27,26 @@ #include <linux/errno.h> #include <linux/mm.h> #include <linux/slab.h> -#include <linux/quotaops.h> -#include <linux/smp_lock.h> #include <linux/buffer_head.h> +#include <linux/sched.h> +#include <linux/crc-itu-t.h> +#include <linux/exportfs.h> -static inline int udf_match(int len1, const char *name1, int len2, const char *name2) +static inline int udf_match(int len1, const unsigned char *name1, int len2, + const unsigned char *name2) { if (len1 != len2) return 0; + return !memcmp(name1, name2, len1); } int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, - struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, - uint8_t *impuse, uint8_t *fileident) + struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh, + uint8_t *impuse, uint8_t *fileident) { - uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag); + uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(struct tag); uint16_t crc; - uint8_t checksum = 0; - int i; int offset; uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse); uint8_t lfi = cfi->lengthFileIdent; @@ -58,88 +54,84 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, sizeof(struct fileIdentDesc); int adinicb = 0; - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) + if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) adinicb = 1; offset = fibh->soffset + sizeof(struct fileIdentDesc); - if (impuse) - { - if (adinicb || (offset + liu < 0)) + if (impuse) { + if (adinicb || (offset + liu < 0)) { memcpy((uint8_t *)sfi->impUse, impuse, liu); - else if (offset >= 0) + } else if (offset >= 0) { memcpy(fibh->ebh->b_data + offset, impuse, liu); - else - { + } else { memcpy((uint8_t *)sfi->impUse, impuse, -offset); - memcpy(fibh->ebh->b_data, impuse - offset, liu + offset); + memcpy(fibh->ebh->b_data, impuse - offset, + liu + offset); } } offset += liu; - if (fileident) - { - if (adinicb || (offset + lfi < 0)) + if (fileident) { + if (adinicb || (offset + lfi < 0)) { memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi); - else if (offset >= 0) + } else if (offset >= 0) { memcpy(fibh->ebh->b_data + offset, fileident, lfi); - else - { - memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset); - memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset); + } else { + memcpy((uint8_t *)sfi->fileIdent + liu, fileident, + -offset); + memcpy(fibh->ebh->b_data, fileident - offset, + lfi + offset); } } offset += lfi; - if (adinicb || (offset + padlen < 0)) + if (adinicb || (offset + padlen < 0)) { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen); - else if (offset >= 0) + } else if (offset >= 0) { memset(fibh->ebh->b_data + offset, 0x00, padlen); - else - { + } else { memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset); memset(fibh->ebh->b_data, 0x00, padlen + offset); } - crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) - - sizeof(tag), 0); - - if (fibh->sbh == fibh->ebh) - crc = udf_crc((uint8_t *)sfi->impUse, - crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); - else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) - crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset, - crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc); - else - { - crc = udf_crc((uint8_t *)sfi->impUse, - -fibh->soffset - sizeof(struct fileIdentDesc), crc); - crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc); + crc = crc_itu_t(0, (uint8_t *)cfi + sizeof(struct tag), + sizeof(struct fileIdentDesc) - sizeof(struct tag)); + + if (fibh->sbh == fibh->ebh) { + crc = crc_itu_t(crc, (uint8_t *)sfi->impUse, + crclen + sizeof(struct tag) - + sizeof(struct fileIdentDesc)); + } else if (sizeof(struct fileIdentDesc) >= -fibh->soffset) { + crc = crc_itu_t(crc, fibh->ebh->b_data + + sizeof(struct fileIdentDesc) + + fibh->soffset, + crclen + sizeof(struct tag) - + sizeof(struct fileIdentDesc)); + } else { + crc = crc_itu_t(crc, (uint8_t *)sfi->impUse, + -fibh->soffset - sizeof(struct fileIdentDesc)); + crc = crc_itu_t(crc, fibh->ebh->b_data, fibh->eoffset); } cfi->descTag.descCRC = cpu_to_le16(crc); cfi->descTag.descCRCLength = cpu_to_le16(crclen); + cfi->descTag.tagChecksum = udf_tag_checksum(&cfi->descTag); - for (i=0; i<16; i++) - if (i != 4) - checksum += ((uint8_t *)&cfi->descTag)[i]; - - cfi->descTag.tagChecksum = checksum; - if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) - memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc)); - else - { + if (adinicb || (sizeof(struct fileIdentDesc) <= -fibh->soffset)) { + memcpy((uint8_t *)sfi, (uint8_t *)cfi, + sizeof(struct fileIdentDesc)); + } else { memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset); memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset, - sizeof(struct fileIdentDesc) + fibh->soffset); + sizeof(struct fileIdentDesc) + fibh->soffset); } - if (adinicb) + if (adinicb) { mark_inode_dirty(inode); - else - { + } else { if (fibh->sbh != fibh->ebh) mark_buffer_dirty_inode(fibh->ebh, inode); mark_buffer_dirty_inode(fibh->sbh, inode); @@ -147,635 +139,584 @@ int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi, return 0; } -static struct fileIdentDesc * -udf_find_entry(struct inode *dir, struct dentry *dentry, - struct udf_fileident_bh *fibh, - struct fileIdentDesc *cfi) +static struct fileIdentDesc *udf_find_entry(struct inode *dir, + const struct qstr *child, + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi) { - struct fileIdentDesc *fi=NULL; + struct fileIdentDesc *fi = NULL; loff_t f_pos; int block, flen; - char fname[UDF_NAME_LEN]; - char *nameptr; + unsigned char *fname = NULL; + unsigned char *nameptr; uint8_t lfi; uint16_t liu; loff_t size; - kernel_lb_addr bloc, eloc; - uint32_t extoffset, elen, offset; - struct buffer_head *bh = NULL; - - size = (udf_ext0_offset(dir) + dir->i_size) >> 2; - f_pos = (udf_ext0_offset(dir) >> 2); - - fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - fibh->sbh = fibh->ebh = NULL; - else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), - &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) - { - offset >>= dir->i_sb->s_blocksize_bits; - block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) - extoffset -= sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) - extoffset -= sizeof(long_ad); - } - else + struct kernel_lb_addr eloc; + uint32_t elen; + sector_t offset; + struct extent_position epos = {}; + struct udf_inode_info *dinfo = UDF_I(dir); + int isdotdot = child->len == 2 && + child->name[0] == '.' && child->name[1] == '.'; + + size = udf_ext0_offset(dir) + dir->i_size; + f_pos = udf_ext0_offset(dir); + + fibh->sbh = fibh->ebh = NULL; + fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1); + if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos, + &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) + goto out_err; + block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + } else offset = 0; - if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) - { - udf_release_data(bh); - return NULL; - } - } - else - { - udf_release_data(bh); - return NULL; + fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); + if (!fibh->sbh) + goto out_err; } - while ( (f_pos < size) ) - { - fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); + fname = kmalloc(UDF_NAME_LEN, GFP_NOFS); + if (!fname) + goto out_err; + while (f_pos < size) { + fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, + &elen, &offset); if (!fi) - { - if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - udf_release_data(bh); - return NULL; - } + goto out_err; liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; - if (fibh->sbh == fibh->ebh) - { + if (fibh->sbh == fibh->ebh) { nameptr = fi->fileIdent + liu; - } - else - { + } else { int poffset; /* Unpaded ending offset */ - poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; + poffset = fibh->soffset + sizeof(struct fileIdentDesc) + + liu + lfi; if (poffset >= lfi) - nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi); - else - { + nameptr = (uint8_t *)(fibh->ebh->b_data + + poffset - lfi); + else { nameptr = fname; - memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); - memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); + memcpy(nameptr, fi->fileIdent + liu, + lfi - poffset); + memcpy(nameptr + lfi - poffset, + fibh->ebh->b_data, poffset); } } - if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) ) + if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE)) continue; } - - if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 ) - { - if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) ) + + if ((cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) { + if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE)) continue; } + if ((cfi->fileCharacteristics & FID_FILE_CHAR_PARENT) && + isdotdot) + goto out_ok; + if (!lfi) continue; - if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi))) - { - if (udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) - { - udf_release_data(bh); - return fi; - } - } + flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi); + if (flen && udf_match(flen, fname, child->len, child->name)) + goto out_ok; } + +out_err: + fi = NULL; if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - udf_release_data(bh); - return NULL; -} + brelse(fibh->ebh); + brelse(fibh->sbh); +out_ok: + brelse(epos.bh); + kfree(fname); -/* - * udf_lookup - * - * PURPOSE - * Look-up the inode for a given name. - * - * DESCRIPTION - * Required - lookup_dentry() will return -ENOTDIR if this routine is not - * available for a directory. The filesystem is useless if this routine is - * not available for at least the filesystem's root directory. - * - * This routine is passed an incomplete dentry - it must be completed by - * calling d_add(dentry, inode). If the name does not exist, then the - * specified inode must be set to null. An error should only be returned - * when the lookup fails for a reason other than the name not existing. - * Note that the directory inode semaphore is held during the call. - * - * Refer to lookup_dentry() in fs/namei.c - * lookup_dentry() -> lookup() -> real_lookup() -> . - * - * PRE-CONDITIONS - * dir Pointer to inode of parent directory. - * dentry Pointer to dentry to complete. - * nd Pointer to lookup nameidata - * - * POST-CONDITIONS - * <return> Zero on success. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ + return fi; +} -static struct dentry * -udf_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) { struct inode *inode = NULL; - struct fileIdentDesc cfi, *fi; + struct fileIdentDesc cfi; struct udf_fileident_bh fibh; - if (dentry->d_name.len > UDF_NAME_LEN-2) + if (dentry->d_name.len > UDF_NAME_LEN - 2) return ERR_PTR(-ENAMETOOLONG); - lock_kernel(); #ifdef UDF_RECOVERY /* temporary shorthand for specifying files by inode number */ - if (!strncmp(dentry->d_name.name, ".B=", 3) ) - { - kernel_lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) }; + if (!strncmp(dentry->d_name.name, ".B=", 3)) { + struct kernel_lb_addr lb = { + .logicalBlockNum = 0, + .partitionReferenceNum = + simple_strtoul(dentry->d_name.name + 3, + NULL, 0), + }; inode = udf_iget(dir->i_sb, lb); - if (!inode) - { - unlock_kernel(); + if (!inode) { return ERR_PTR(-EACCES); } - } - else + } else #endif /* UDF_RECOVERY */ - if ((fi = udf_find_entry(dir, dentry, &fibh, &cfi))) - { + if (udf_find_entry(dir, &dentry->d_name, &fibh, &cfi)) { + struct kernel_lb_addr loc; + if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); + brelse(fibh.ebh); + brelse(fibh.sbh); - inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation)); - if ( !inode ) - { - unlock_kernel(); + loc = lelb_to_cpu(cfi.icb.extLocation); + inode = udf_iget(dir->i_sb, &loc); + if (!inode) { return ERR_PTR(-EACCES); } } - unlock_kernel(); - d_add(dentry, inode); - return NULL; + + return d_splice_alias(inode, dentry); } -static struct fileIdentDesc * -udf_add_entry(struct inode *dir, struct dentry *dentry, - struct udf_fileident_bh *fibh, - struct fileIdentDesc *cfi, int *err) +static struct fileIdentDesc *udf_add_entry(struct inode *dir, + struct dentry *dentry, + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi, int *err) { - struct super_block *sb; - struct fileIdentDesc *fi=NULL; - char name[UDF_NAME_LEN], fname[UDF_NAME_LEN]; + struct super_block *sb = dir->i_sb; + struct fileIdentDesc *fi = NULL; + unsigned char *name = NULL; int namelen; loff_t f_pos; - int flen; - char *nameptr; - loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; + loff_t size = udf_ext0_offset(dir) + dir->i_size; int nfidlen; uint8_t lfi; uint16_t liu; int block; - kernel_lb_addr bloc, eloc; - uint32_t extoffset, elen, offset; - struct buffer_head *bh = NULL; - - sb = dir->i_sb; + struct kernel_lb_addr eloc; + uint32_t elen = 0; + sector_t offset; + struct extent_position epos = {}; + struct udf_inode_info *dinfo; + + fibh->sbh = fibh->ebh = NULL; + name = kmalloc(UDF_NAME_LEN, GFP_NOFS); + if (!name) { + *err = -ENOMEM; + goto out_err; + } - if (dentry) - { - if (!dentry->d_name.len) - { + if (dentry) { + if (!dentry->d_name.len) { *err = -EINVAL; - return NULL; + goto out_err; } - - if ( !(namelen = udf_put_filename(sb, dentry->d_name.name, name, dentry->d_name.len))) - { + namelen = udf_put_filename(sb, dentry->d_name.name, name, + dentry->d_name.len); + if (!namelen) { *err = -ENAMETOOLONG; - return NULL; + goto out_err; } - } - else + } else { namelen = 0; + } nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3; - f_pos = (udf_ext0_offset(dir) >> 2); - - fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - fibh->sbh = fibh->ebh = NULL; - else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), - &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) - { - offset >>= dir->i_sb->s_blocksize_bits; - block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) - extoffset -= sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) - extoffset -= sizeof(long_ad); + f_pos = udf_ext0_offset(dir); + + fibh->soffset = fibh->eoffset = f_pos & (dir->i_sb->s_blocksize - 1); + dinfo = UDF_I(dir); + if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, &epos, + &eloc, &elen, &offset) != (EXT_RECORDED_ALLOCATED >> 30)) { + block = udf_get_lb_pblock(dir->i_sb, + &dinfo->i_location, 0); + fibh->soffset = fibh->eoffset = sb->s_blocksize; + goto add; } - else + block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + } else offset = 0; - if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block))) - { - udf_release_data(bh); + fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block); + if (!fibh->sbh) { *err = -EIO; - return NULL; + goto out_err; } - block = UDF_I_LOCATION(dir).logicalBlockNum; - - } - else - { - block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0); - fibh->sbh = fibh->ebh = NULL; - fibh->soffset = fibh->eoffset = sb->s_blocksize; - goto add; + block = dinfo->i_location.logicalBlockNum; } - while ( (f_pos < size) ) - { - fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); + while (f_pos < size) { + fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &epos, &eloc, + &elen, &offset); - if (!fi) - { - if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - udf_release_data(bh); + if (!fi) { *err = -EIO; - return NULL; + goto out_err; } liu = le16_to_cpu(cfi->lengthOfImpUse); lfi = cfi->lengthFileIdent; - if (fibh->sbh == fibh->ebh) - nameptr = fi->fileIdent + liu; - else - { - int poffset; /* Unpaded ending offset */ - - poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi; - - if (poffset >= lfi) - nameptr = (char *)(fibh->ebh->b_data + poffset - lfi); - else - { - nameptr = fname; - memcpy(nameptr, fi->fileIdent + liu, lfi - poffset); - memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset); - } - } - - if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 ) - { - if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen) - { - udf_release_data(bh); + if ((cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) { + if (((sizeof(struct fileIdentDesc) + + liu + lfi + 3) & ~3) == nfidlen) { cfi->descTag.tagSerialNum = cpu_to_le16(1); cfi->fileVersionNum = cpu_to_le16(1); cfi->fileCharacteristics = 0; cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); - if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) - return fi; - else - { + if (!udf_write_fi(dir, cfi, fi, fibh, NULL, + name)) + goto out_ok; + else { *err = -EIO; - return NULL; + goto out_err; } } } - - if (!lfi || !dentry) - continue; - - if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) && - udf_match(flen, fname, dentry->d_name.len, dentry->d_name.name)) - { - if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - udf_release_data(bh); - *err = -EEXIST; - return NULL; - } } add: f_pos += nfidlen; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB && - sb->s_blocksize - fibh->eoffset < nfidlen) - { - udf_release_data(bh); - bh = NULL; + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && + sb->s_blocksize - fibh->eoffset < nfidlen) { + brelse(epos.bh); + epos.bh = NULL; fibh->soffset -= udf_ext0_offset(dir); fibh->eoffset -= udf_ext0_offset(dir); - f_pos -= (udf_ext0_offset(dir) >> 2); + f_pos -= udf_ext0_offset(dir); if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); - if (!(fibh->sbh = fibh->ebh = udf_expand_dir_adinicb(dir, &block, err))) - return NULL; - bloc = UDF_I_LOCATION(dir); - eloc.logicalBlockNum = block; - eloc.partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum; - elen = dir->i_sb->s_blocksize; - extoffset = udf_file_entry_alloc_offset(dir); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) - extoffset += sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) - extoffset += sizeof(long_ad); + brelse(fibh->ebh); + brelse(fibh->sbh); + fibh->sbh = fibh->ebh = + udf_expand_dir_adinicb(dir, &block, err); + if (!fibh->sbh) + goto out_err; + epos.block = dinfo->i_location; + epos.offset = udf_file_entry_alloc_offset(dir); + /* Load extent udf_expand_dir_adinicb() has created */ + udf_current_aext(dir, &epos, &eloc, &elen, 1); } - if (sb->s_blocksize - fibh->eoffset >= nfidlen) - { + /* Entry fits into current block? */ + if (sb->s_blocksize - fibh->eoffset >= nfidlen) { fibh->soffset = fibh->eoffset; fibh->eoffset += nfidlen; - if (fibh->sbh != fibh->ebh) - { - udf_release_data(fibh->sbh); + if (fibh->sbh != fibh->ebh) { + brelse(fibh->sbh); fibh->sbh = fibh->ebh; } - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { - block = UDF_I_LOCATION(dir).logicalBlockNum; - fi = (struct fileIdentDesc *)(UDF_I_DATA(dir) + fibh->soffset - udf_ext0_offset(dir) + UDF_I_LENEATTR(dir)); - } - else - { - block = eloc.logicalBlockNum + ((elen - 1) >> - dir->i_sb->s_blocksize_bits); - fi = (struct fileIdentDesc *)(fibh->sbh->b_data + fibh->soffset); + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + block = dinfo->i_location.logicalBlockNum; + fi = (struct fileIdentDesc *) + (dinfo->i_ext.i_data + + fibh->soffset - + udf_ext0_offset(dir) + + dinfo->i_lenEAttr); + } else { + block = eloc.logicalBlockNum + + ((elen - 1) >> + dir->i_sb->s_blocksize_bits); + fi = (struct fileIdentDesc *) + (fibh->sbh->b_data + fibh->soffset); } - } - else - { + } else { + /* Round up last extent in the file */ + elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1); + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + udf_write_aext(dir, &epos, &eloc, elen, 1); + dinfo->i_lenExtents = (dinfo->i_lenExtents + sb->s_blocksize + - 1) & ~(sb->s_blocksize - 1); + fibh->soffset = fibh->eoffset - sb->s_blocksize; fibh->eoffset += nfidlen - sb->s_blocksize; - if (fibh->sbh != fibh->ebh) - { - udf_release_data(fibh->sbh); + if (fibh->sbh != fibh->ebh) { + brelse(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) == + dir->i_sb->s_blocksize_bits); + fibh->ebh = udf_bread(dir, + f_pos >> dir->i_sb->s_blocksize_bits, 1, err); + if (!fibh->ebh) + goto out_err; + /* Extents could have been merged, invalidate our position */ + brelse(epos.bh); + epos.bh = NULL; + epos.block = dinfo->i_location; + epos.offset = udf_file_entry_alloc_offset(dir); + + if (!fibh->soffset) { + /* Find the freshly allocated block */ + while (udf_next_aext(dir, &epos, &eloc, &elen, 1) == (EXT_RECORDED_ALLOCATED >> 30)) - { - block = eloc.logicalBlockNum + ((elen - 1) >> + ; + block = eloc.logicalBlockNum + ((elen - 1) >> dir->i_sb->s_blocksize_bits); - } - else - block ++; - - udf_release_data(fibh->sbh); + brelse(fibh->sbh); fibh->sbh = fibh->ebh; fi = (struct fileIdentDesc *)(fibh->sbh->b_data); - } - else - { + } else { fi = (struct fileIdentDesc *) - (fibh->sbh->b_data + sb->s_blocksize + fibh->soffset); + (fibh->sbh->b_data + sb->s_blocksize + + fibh->soffset); } } memset(cfi, 0, sizeof(struct fileIdentDesc)); - if (UDF_SB_UDFREV(sb) >= 0x0200) - udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, sizeof(tag)); + if (UDF_SB(sb)->s_udfrev >= 0x0200) + udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, + sizeof(struct tag)); else - udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, sizeof(tag)); + udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, + sizeof(struct tag)); cfi->fileVersionNum = cpu_to_le16(1); cfi->lengthFileIdent = namelen; cfi->lengthOfImpUse = cpu_to_le16(0); - if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) - { - udf_release_data(bh); + if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name)) { dir->i_size += nfidlen; - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - UDF_I_LENALLOC(dir) += nfidlen; + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + dinfo->i_lenAlloc += nfidlen; + else { + /* Find the last extent and truncate it to proper size */ + while (udf_next_aext(dir, &epos, &eloc, &elen, 1) == + (EXT_RECORDED_ALLOCATED >> 30)) + ; + elen -= dinfo->i_lenExtents - dir->i_size; + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + udf_write_aext(dir, &epos, &eloc, elen, 1); + dinfo->i_lenExtents = dir->i_size; + } + mark_inode_dirty(dir); - return fi; - } - else - { - udf_release_data(bh); - if (fibh->sbh != fibh->ebh) - udf_release_data(fibh->ebh); - udf_release_data(fibh->sbh); + goto out_ok; + } else { *err = -EIO; - return NULL; + goto out_err; } + +out_err: + fi = NULL; + if (fibh->sbh != fibh->ebh) + brelse(fibh->ebh); + brelse(fibh->sbh); +out_ok: + brelse(epos.bh); + kfree(name); + return fi; } static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi, - struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi) + struct udf_fileident_bh *fibh, + struct fileIdentDesc *cfi) { cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED; + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT)) - memset(&(cfi->icb), 0x00, sizeof(long_ad)); + memset(&(cfi->icb), 0x00, sizeof(struct long_ad)); + return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL); } -static int udf_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) +static int udf_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) { struct udf_fileident_bh fibh; struct inode *inode; struct fileIdentDesc cfi, *fi; int err; + struct udf_inode_info *iinfo; - lock_kernel(); inode = udf_new_inode(dir, mode, &err); - if (!inode) - { - unlock_kernel(); + if (!inode) { return err; } - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) + iinfo = UDF_I(inode); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) inode->i_data.a_ops = &udf_adinicb_aops; else inode->i_data.a_ops = &udf_aops; inode->i_op = &udf_file_inode_operations; inode->i_fop = &udf_file_operations; - inode->i_mode = mode; mark_inode_dirty(inode); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { - inode->i_nlink --; - mark_inode_dirty(inode); + fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); + if (!fi) { + inode_dec_link_count(inode); iput(inode); - unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); - cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = - cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) mark_inode_dirty(dir); - } if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - unlock_kernel(); + brelse(fibh.ebh); + brelse(fibh.sbh); d_instantiate(dentry, inode); + return 0; } -static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev) +static int udf_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) { - struct inode * inode; + struct inode *inode; + struct udf_inode_info *iinfo; + int err; + + inode = udf_new_inode(dir, mode, &err); + if (!inode) + return err; + + iinfo = UDF_I(inode); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + inode->i_data.a_ops = &udf_adinicb_aops; + else + inode->i_data.a_ops = &udf_aops; + inode->i_op = &udf_file_inode_operations; + inode->i_fop = &udf_file_operations; + mark_inode_dirty(inode); + + d_tmpfile(dentry, inode); + return 0; +} + +static int udf_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t rdev) +{ + struct inode *inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; + struct udf_inode_info *iinfo; if (!old_valid_dev(rdev)) return -EINVAL; - lock_kernel(); err = -EIO; inode = udf_new_inode(dir, mode, &err); if (!inode) goto out; - inode->i_uid = current->fsuid; + iinfo = UDF_I(inode); init_special_inode(inode, mode, rdev); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { - inode->i_nlink --; - mark_inode_dirty(inode); + fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); + if (!fi) { + inode_dec_link_count(inode); iput(inode); - unlock_kernel(); return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); - cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); + cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = - cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) mark_inode_dirty(dir); - } mark_inode_dirty(inode); if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); + brelse(fibh.ebh); + brelse(fibh.sbh); d_instantiate(dentry, inode); err = 0; + out: - unlock_kernel(); return err; } -static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode) +static int udf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { - struct inode * inode; + struct inode *inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; - - lock_kernel(); - err = -EMLINK; - if (dir->i_nlink >= (256<<sizeof(dir->i_nlink))-1) - goto out; + struct udf_inode_info *dinfo = UDF_I(dir); + struct udf_inode_info *iinfo; err = -EIO; - inode = udf_new_inode(dir, S_IFDIR, &err); + inode = udf_new_inode(dir, S_IFDIR | mode, &err); if (!inode) goto out; + iinfo = UDF_I(inode); inode->i_op = &udf_dir_inode_operations; inode->i_fop = &udf_dir_operations; - if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err))) - { - inode->i_nlink--; - mark_inode_dirty(inode); + fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err); + if (!fi) { + inode_dec_link_count(inode); iput(inode); goto out; } - inode->i_nlink = 2; + set_nlink(inode, 2); cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); - cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(dir)); + cfi.icb.extLocation = cpu_to_lelb(dinfo->i_location); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = - cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL); - cfi.fileCharacteristics = FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT; + cpu_to_le32(dinfo->i_unique & 0x00000000FFFFFFFFUL); + cfi.fileCharacteristics = + FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT; udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL); - udf_release_data(fibh.sbh); - inode->i_mode = S_IFDIR | mode; - if (dir->i_mode & S_ISGID) - inode->i_mode |= S_ISGID; + brelse(fibh.sbh); mark_inode_dirty(inode); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { - inode->i_nlink = 0; + fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); + if (!fi) { + clear_nlink(inode); 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)); + cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = - cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL); + cpu_to_le32(iinfo->i_unique & 0x00000000FFFFFFFFUL); cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY; udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - dir->i_nlink++; + inc_nlink(dir); mark_inode_dirty(dir); d_instantiate(dentry, inode); if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); + brelse(fibh.ebh); + brelse(fibh.sbh); err = 0; + out: - unlock_kernel(); return err; } @@ -784,92 +725,86 @@ static int empty_dir(struct inode *dir) struct fileIdentDesc *fi, cfi; struct udf_fileident_bh fibh; loff_t f_pos; - loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2; + loff_t size = udf_ext0_offset(dir) + dir->i_size; int block; - kernel_lb_addr bloc, eloc; - uint32_t extoffset, elen, offset; - struct buffer_head *bh = NULL; - - f_pos = (udf_ext0_offset(dir) >> 2); + struct kernel_lb_addr eloc; + uint32_t elen; + sector_t offset; + struct extent_position epos = {}; + struct udf_inode_info *dinfo = UDF_I(dir); - fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2; + f_pos = udf_ext0_offset(dir); + fibh.soffset = fibh.eoffset = f_pos & (dir->i_sb->s_blocksize - 1); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) fibh.sbh = fibh.ebh = NULL; - else if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), - &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30)) - { - offset >>= dir->i_sb->s_blocksize_bits; - block = udf_get_lb_pblock(dir->i_sb, eloc, offset); - if ((++offset << dir->i_sb->s_blocksize_bits) < elen) - { - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT) - extoffset -= sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG) - extoffset -= sizeof(long_ad); - } - else + else if (inode_bmap(dir, f_pos >> dir->i_sb->s_blocksize_bits, + &epos, &eloc, &elen, &offset) == + (EXT_RECORDED_ALLOCATED >> 30)) { + block = udf_get_lb_pblock(dir->i_sb, &eloc, offset); + if ((++offset << dir->i_sb->s_blocksize_bits) < elen) { + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + } else offset = 0; - if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) - { - udf_release_data(bh); + fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block); + if (!fibh.sbh) { + brelse(epos.bh); return 0; } - } - else - { - udf_release_data(bh); + } else { + brelse(epos.bh); return 0; } - - while ( (f_pos < size) ) - { - fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh); - - if (!fi) - { + while (f_pos < size) { + fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &epos, &eloc, + &elen, &offset); + if (!fi) { if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - udf_release_data(bh); + brelse(fibh.ebh); + brelse(fibh.sbh); + brelse(epos.bh); return 0; } - if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) - { + if (cfi.lengthFileIdent && + (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0) { if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - udf_release_data(bh); + brelse(fibh.ebh); + brelse(fibh.sbh); + brelse(epos.bh); return 0; } } + if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - udf_release_data(bh); + brelse(fibh.ebh); + brelse(fibh.sbh); + brelse(epos.bh); + return 1; } -static int udf_rmdir(struct inode * dir, struct dentry * dentry) +static int udf_rmdir(struct inode *dir, struct dentry *dentry) { int retval; - struct inode * inode = dentry->d_inode; + struct inode *inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi, cfi; - kernel_lb_addr tloc; + struct kernel_lb_addr tloc; retval = -ENOENT; - lock_kernel(); - fi = udf_find_entry(dir, dentry, &fibh, &cfi); + fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); if (!fi) goto out; retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); - if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) + if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino) goto end_rmdir; retval = -ENOTEMPTY; if (!empty_dir(inode)) @@ -878,177 +813,180 @@ static int udf_rmdir(struct inode * dir, struct dentry * dentry) if (retval) goto end_rmdir; if (inode->i_nlink != 2) - udf_warning(inode->i_sb, "udf_rmdir", - "empty directory has nlink != 2 (%d)", - inode->i_nlink); - inode->i_nlink = 0; + udf_warn(inode->i_sb, "empty directory has nlink != 2 (%d)\n", + inode->i_nlink); + clear_nlink(inode); inode->i_size = 0; - mark_inode_dirty(inode); - dir->i_nlink --; - inode->i_ctime = dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); + inode_dec_link_count(dir); + inode->i_ctime = dir->i_ctime = dir->i_mtime = + current_fs_time(dir->i_sb); mark_inode_dirty(dir); end_rmdir: if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); + brelse(fibh.ebh); + brelse(fibh.sbh); + out: - unlock_kernel(); return retval; } -static int udf_unlink(struct inode * dir, struct dentry * dentry) +static int udf_unlink(struct inode *dir, struct dentry *dentry) { int retval; - struct inode * inode = dentry->d_inode; + struct inode *inode = dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc *fi; struct fileIdentDesc cfi; - kernel_lb_addr tloc; + struct kernel_lb_addr tloc; retval = -ENOENT; - lock_kernel(); - fi = udf_find_entry(dir, dentry, &fibh, &cfi); + fi = udf_find_entry(dir, &dentry->d_name, &fibh, &cfi); if (!fi) goto out; retval = -EIO; tloc = lelb_to_cpu(cfi.icb.extLocation); - if (udf_get_lb_pblock(dir->i_sb, tloc, 0) != inode->i_ino) + if (udf_get_lb_pblock(dir->i_sb, &tloc, 0) != inode->i_ino) goto end_unlink; - if (!inode->i_nlink) - { + if (!inode->i_nlink) { udf_debug("Deleting nonexistent file (%lu), %d\n", - inode->i_ino, inode->i_nlink); - inode->i_nlink = 1; + inode->i_ino, inode->i_nlink); + set_nlink(inode, 1); } retval = udf_delete_entry(dir, fi, &fibh, &cfi); if (retval) goto end_unlink; dir->i_ctime = dir->i_mtime = current_fs_time(dir->i_sb); mark_inode_dirty(dir); - inode->i_nlink--; - mark_inode_dirty(inode); + inode_dec_link_count(inode); inode->i_ctime = dir->i_ctime; retval = 0; end_unlink: if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); + brelse(fibh.ebh); + brelse(fibh.sbh); + out: - unlock_kernel(); return retval; } -static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname) +static int udf_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) { - struct inode * inode; + struct inode *inode; struct pathComponent *pc; - char *compstart; + const char *compstart; struct udf_fileident_bh fibh; - struct buffer_head *bh = NULL; + struct extent_position epos = {}; int eoffset, elen = 0; struct fileIdentDesc *fi; struct fileIdentDesc cfi; - char *ea; + uint8_t *ea; int err; int block; - char name[UDF_NAME_LEN]; + unsigned char *name = NULL; int namelen; + struct udf_inode_info *iinfo; + struct super_block *sb = dir->i_sb; - lock_kernel(); - if (!(inode = udf_new_inode(dir, S_IFLNK, &err))) + inode = udf_new_inode(dir, S_IFLNK | S_IRWXUGO, &err); + if (!inode) goto out; - inode->i_mode = S_IFLNK | S_IRWXUGO; + iinfo = UDF_I(inode); + down_write(&iinfo->i_data_sem); + name = kmalloc(UDF_NAME_LEN, GFP_NOFS); + if (!name) { + err = -ENOMEM; + goto out_no_entry; + } + inode->i_data.a_ops = &udf_symlink_aops; - inode->i_op = &page_symlink_inode_operations; + inode->i_op = &udf_symlink_inode_operations; - if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB) - { - struct buffer_head *bh = NULL; - kernel_lb_addr bloc, eloc; - uint32_t elen, extoffset; + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + struct kernel_lb_addr eloc; + uint32_t bsize; - block = udf_new_block(inode->i_sb, inode, - UDF_I_LOCATION(inode).partitionReferenceNum, - UDF_I_LOCATION(inode).logicalBlockNum, &err); + block = udf_new_block(sb, inode, + iinfo->i_location.partitionReferenceNum, + iinfo->i_location.logicalBlockNum, &err); if (!block) goto out_no_entry; - bloc = UDF_I_LOCATION(inode); + epos.block = iinfo->i_location; + epos.offset = udf_file_entry_alloc_offset(inode); + epos.bh = NULL; eloc.logicalBlockNum = block; - eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum; - elen = inode->i_sb->s_blocksize; - UDF_I_LENEXTENTS(inode) = elen; - extoffset = udf_file_entry_alloc_offset(inode); - udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 0); - udf_release_data(bh); - - block = udf_get_pblock(inode->i_sb, block, - UDF_I_LOCATION(inode).partitionReferenceNum, 0); - bh = udf_tread(inode->i_sb, block); - lock_buffer(bh); - memset(bh->b_data, 0x00, inode->i_sb->s_blocksize); - set_buffer_uptodate(bh); - unlock_buffer(bh); - mark_buffer_dirty_inode(bh, inode); - ea = bh->b_data + udf_ext0_offset(inode); - } - else - ea = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); - - eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode); + eloc.partitionReferenceNum = + iinfo->i_location.partitionReferenceNum; + bsize = sb->s_blocksize; + iinfo->i_lenExtents = bsize; + udf_add_aext(inode, &epos, &eloc, bsize, 0); + brelse(epos.bh); + + block = udf_get_pblock(sb, block, + iinfo->i_location.partitionReferenceNum, + 0); + epos.bh = udf_tgetblk(sb, block); + lock_buffer(epos.bh); + memset(epos.bh->b_data, 0x00, bsize); + set_buffer_uptodate(epos.bh); + unlock_buffer(epos.bh); + mark_buffer_dirty_inode(epos.bh, inode); + ea = epos.bh->b_data + udf_ext0_offset(inode); + } else + ea = iinfo->i_ext.i_data + iinfo->i_lenEAttr; + + eoffset = sb->s_blocksize - udf_ext0_offset(inode); pc = (struct pathComponent *)ea; - if (*symname == '/') - { - do - { + 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) - { + while (*symname) { if (elen + sizeof(struct pathComponent) > eoffset) goto out_no_entry; pc = (struct pathComponent *)(ea + elen); - compstart = (char *)symname; + compstart = symname; - do - { + do { symname++; } while (*symname && *symname != '/'); pc->componentType = 5; pc->lengthComponentIdent = 0; pc->componentFileVersionNum = 0; - if (compstart[0] == '.') - { - if ((symname-compstart) == 1) + if (compstart[0] == '.') { + if ((symname - compstart) == 1) pc->componentType = 4; - else if ((symname-compstart) == 2 && compstart[1] == '.') + else if ((symname - compstart) == 2 && + compstart[1] == '.') pc->componentType = 3; } - if (pc->componentType == 5) - { - if ( !(namelen = udf_put_filename(inode->i_sb, compstart, name, symname-compstart))) + if (pc->componentType == 5) { + namelen = udf_put_filename(sb, compstart, name, + symname - compstart); + if (!namelen) goto out_no_entry; - if (elen + sizeof(struct pathComponent) + namelen > eoffset) + if (elen + sizeof(struct pathComponent) + namelen > + eoffset) goto out_no_entry; else pc->lengthComponentIdent = namelen; @@ -1058,189 +996,154 @@ static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; - if (*symname) - { - do - { + if (*symname) { + do { symname++; } while (*symname == '/'); } } - udf_release_data(bh); + brelse(epos.bh); inode->i_size = elen; - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - UDF_I_LENALLOC(inode) = inode->i_size; + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + iinfo->i_lenAlloc = inode->i_size; + else + udf_truncate_tail_extent(inode); mark_inode_dirty(inode); + up_write(&iinfo->i_data_sem); - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) + fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); + if (!fi) goto out_no_entry; - cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); - cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); - if (UDF_SB_LVIDBH(inode->i_sb)) - { - struct logicalVolHeaderDesc *lvhd; - uint64_t uniqueID; - lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); - uniqueID = le64_to_cpu(lvhd->uniqueID); + cfi.icb.extLength = cpu_to_le32(sb->s_blocksize); + cfi.icb.extLocation = cpu_to_lelb(iinfo->i_location); + if (UDF_SB(inode->i_sb)->s_lvid_bh) { *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = - cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); - if (!(++uniqueID & 0x00000000FFFFFFFFUL)) - uniqueID += 16; - lvhd->uniqueID = cpu_to_le64(uniqueID); - mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); + cpu_to_le32(lvid_get_unique_id(sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) mark_inode_dirty(dir); - } if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); + brelse(fibh.ebh); + brelse(fibh.sbh); d_instantiate(dentry, inode); err = 0; out: - unlock_kernel(); + kfree(name); return err; out_no_entry: - inode->i_nlink--; - mark_inode_dirty(inode); + up_write(&iinfo->i_data_sem); + inode_dec_link_count(inode); iput(inode); goto out; } -static int udf_link(struct dentry * old_dentry, struct inode * dir, - struct dentry *dentry) +static int udf_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; struct udf_fileident_bh fibh; struct fileIdentDesc cfi, *fi; int err; - lock_kernel(); - if (inode->i_nlink >= (256<<sizeof(inode->i_nlink))-1) - { - unlock_kernel(); - return -EMLINK; - } - - if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err))) - { - unlock_kernel(); + fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); + if (!fi) { return err; } cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize); - cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode)); - if (UDF_SB_LVIDBH(inode->i_sb)) - { - struct logicalVolHeaderDesc *lvhd; - uint64_t uniqueID; - lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse); - uniqueID = le64_to_cpu(lvhd->uniqueID); + cfi.icb.extLocation = cpu_to_lelb(UDF_I(inode)->i_location); + if (UDF_SB(inode->i_sb)->s_lvid_bh) { *(__le32 *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse = - cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL); - if (!(++uniqueID & 0x00000000FFFFFFFFUL)) - uniqueID += 16; - lvhd->uniqueID = cpu_to_le64(uniqueID); - mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb)); + cpu_to_le32(lvid_get_unique_id(inode->i_sb)); } udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL); - if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB) - { + if (UDF_I(dir)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) mark_inode_dirty(dir); - } + if (fibh.sbh != fibh.ebh) - udf_release_data(fibh.ebh); - udf_release_data(fibh.sbh); - inode->i_nlink ++; + brelse(fibh.ebh); + brelse(fibh.sbh); + inc_nlink(inode); inode->i_ctime = current_fs_time(inode->i_sb); mark_inode_dirty(inode); - atomic_inc(&inode->i_count); + ihold(inode); d_instantiate(dentry, inode); - unlock_kernel(); + return 0; } /* Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, - struct inode * new_dir, struct dentry * new_dentry) +static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { - struct inode * old_inode = old_dentry->d_inode; - struct inode * new_inode = new_dentry->d_inode; + struct inode *old_inode = old_dentry->d_inode; + struct inode *new_inode = new_dentry->d_inode; struct udf_fileident_bh ofibh, nfibh; - struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi; + struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL; + struct fileIdentDesc ocfi, ncfi; struct buffer_head *dir_bh = NULL; int retval = -ENOENT; - kernel_lb_addr tloc; + struct kernel_lb_addr tloc; + struct udf_inode_info *old_iinfo = UDF_I(old_inode); - lock_kernel(); - if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi))) - { + ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); + if (ofi) { if (ofibh.sbh != ofibh.ebh) - udf_release_data(ofibh.ebh); - udf_release_data(ofibh.sbh); + brelse(ofibh.ebh); + brelse(ofibh.sbh); } tloc = lelb_to_cpu(ocfi.icb.extLocation); - if (!ofi || udf_get_lb_pblock(old_dir->i_sb, tloc, 0) - != old_inode->i_ino) + if (!ofi || udf_get_lb_pblock(old_dir->i_sb, &tloc, 0) + != old_inode->i_ino) goto end_rename; - nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi); - if (nfi) - { - if (!new_inode) - { + nfi = udf_find_entry(new_dir, &new_dentry->d_name, &nfibh, &ncfi); + if (nfi) { + if (!new_inode) { if (nfibh.sbh != nfibh.ebh) - udf_release_data(nfibh.ebh); - udf_release_data(nfibh.sbh); + brelse(nfibh.ebh); + brelse(nfibh.sbh); nfi = NULL; } } - if (S_ISDIR(old_inode->i_mode)) - { - uint32_t offset = udf_ext0_offset(old_inode); + if (S_ISDIR(old_inode->i_mode)) { + int offset = udf_ext0_offset(old_inode); - if (new_inode) - { + if (new_inode) { retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; } retval = -EIO; - if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) - { - dir_fi = udf_get_fileident(UDF_I_DATA(old_inode) - - (UDF_I_EFE(old_inode) ? - sizeof(struct extendedFileEntry) : - sizeof(struct fileEntry)), - old_inode->i_sb->s_blocksize, &offset); - } - else - { + if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + dir_fi = udf_get_fileident( + old_iinfo->i_ext.i_data - + (old_iinfo->i_efe ? + sizeof(struct extendedFileEntry) : + sizeof(struct fileEntry)), + old_inode->i_sb->s_blocksize, &offset); + } else { dir_bh = udf_bread(old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; - dir_fi = udf_get_fileident(dir_bh->b_data, old_inode->i_sb->s_blocksize, &offset); + dir_fi = udf_get_fileident(dir_bh->b_data, + old_inode->i_sb->s_blocksize, &offset); } if (!dir_fi) goto end_rename; tloc = lelb_to_cpu(dir_fi->icb.extLocation); - if (udf_get_lb_pblock(old_inode->i_sb, tloc, 0) - != old_dir->i_ino) - goto end_rename; - - retval = -EMLINK; - if (!new_inode && new_dir->i_nlink >= (256<<sizeof(new_dir->i_nlink))-1) + if (udf_get_lb_pblock(old_inode->i_sb, &tloc, 0) != + old_dir->i_ino) goto end_rename; } - if (!nfi) - { - nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval); + if (!nfi) { + nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, + &retval); if (!nfi) goto end_rename; } @@ -1257,69 +1160,171 @@ static int udf_rename (struct inode * old_dir, struct dentry * old_dentry, */ ncfi.fileVersionNum = ocfi.fileVersionNum; ncfi.fileCharacteristics = ocfi.fileCharacteristics; - memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad)); + memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(struct long_ad)); udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL); /* The old fid may have moved - find it again */ - ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi); + ofi = udf_find_entry(old_dir, &old_dentry->d_name, &ofibh, &ocfi); udf_delete_entry(old_dir, ofi, &ofibh, &ocfi); - if (new_inode) - { - new_inode->i_nlink--; + if (new_inode) { new_inode->i_ctime = current_fs_time(new_inode->i_sb); - mark_inode_dirty(new_inode); + inode_dec_link_count(new_inode); } old_dir->i_ctime = old_dir->i_mtime = current_fs_time(old_dir->i_sb); mark_inode_dirty(old_dir); - if (dir_fi) - { - dir_fi->icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(new_dir)); - udf_update_tag((char *)dir_fi, (sizeof(struct fileIdentDesc) + - le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3); - if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB) - { + if (dir_fi) { + dir_fi->icb.extLocation = cpu_to_lelb(UDF_I(new_dir)->i_location); + udf_update_tag((char *)dir_fi, + (sizeof(struct fileIdentDesc) + + le16_to_cpu(dir_fi->lengthOfImpUse) + 3) & ~3); + if (old_iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) mark_inode_dirty(old_inode); - } else mark_buffer_dirty_inode(dir_bh, old_inode); - old_dir->i_nlink --; - mark_inode_dirty(old_dir); + + inode_dec_link_count(old_dir); if (new_inode) - { - new_inode->i_nlink --; - mark_inode_dirty(new_inode); - } - else - { - new_dir->i_nlink ++; + inode_dec_link_count(new_inode); + else { + inc_nlink(new_dir); mark_inode_dirty(new_dir); } } - if (ofi) - { + if (ofi) { if (ofibh.sbh != ofibh.ebh) - udf_release_data(ofibh.ebh); - udf_release_data(ofibh.sbh); + brelse(ofibh.ebh); + brelse(ofibh.sbh); } retval = 0; end_rename: - udf_release_data(dir_bh); - if (nfi) - { + brelse(dir_bh); + if (nfi) { if (nfibh.sbh != nfibh.ebh) - udf_release_data(nfibh.ebh); - udf_release_data(nfibh.sbh); + brelse(nfibh.ebh); + brelse(nfibh.sbh); } - unlock_kernel(); + return retval; } -struct inode_operations udf_dir_inode_operations = { +static struct dentry *udf_get_parent(struct dentry *child) +{ + struct kernel_lb_addr tloc; + struct inode *inode = NULL; + struct qstr dotdot = QSTR_INIT("..", 2); + struct fileIdentDesc cfi; + struct udf_fileident_bh fibh; + + if (!udf_find_entry(child->d_inode, &dotdot, &fibh, &cfi)) + goto out_unlock; + + if (fibh.sbh != fibh.ebh) + brelse(fibh.ebh); + brelse(fibh.sbh); + + tloc = lelb_to_cpu(cfi.icb.extLocation); + inode = udf_iget(child->d_inode->i_sb, &tloc); + if (!inode) + goto out_unlock; + + return d_obtain_alias(inode); +out_unlock: + return ERR_PTR(-EACCES); +} + + +static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block, + u16 partref, __u32 generation) +{ + struct inode *inode; + struct kernel_lb_addr loc; + + if (block == 0) + return ERR_PTR(-ESTALE); + + loc.logicalBlockNum = block; + loc.partitionReferenceNum = partref; + inode = udf_iget(sb, &loc); + + if (inode == NULL) + return ERR_PTR(-ENOMEM); + + if (generation && inode->i_generation != generation) { + iput(inode); + return ERR_PTR(-ESTALE); + } + return d_obtain_alias(inode); +} + +static struct dentry *udf_fh_to_dentry(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + if ((fh_len != 3 && fh_len != 5) || + (fh_type != FILEID_UDF_WITH_PARENT && + fh_type != FILEID_UDF_WITHOUT_PARENT)) + return NULL; + + return udf_nfs_get_inode(sb, fid->udf.block, fid->udf.partref, + fid->udf.generation); +} + +static struct dentry *udf_fh_to_parent(struct super_block *sb, + struct fid *fid, int fh_len, int fh_type) +{ + if (fh_len != 5 || fh_type != FILEID_UDF_WITH_PARENT) + return NULL; + + return udf_nfs_get_inode(sb, fid->udf.parent_block, + fid->udf.parent_partref, + fid->udf.parent_generation); +} +static int udf_encode_fh(struct inode *inode, __u32 *fh, int *lenp, + struct inode *parent) +{ + int len = *lenp; + struct kernel_lb_addr location = UDF_I(inode)->i_location; + struct fid *fid = (struct fid *)fh; + int type = FILEID_UDF_WITHOUT_PARENT; + + if (parent && (len < 5)) { + *lenp = 5; + return FILEID_INVALID; + } else if (len < 3) { + *lenp = 3; + return FILEID_INVALID; + } + + *lenp = 3; + fid->udf.block = location.logicalBlockNum; + fid->udf.partref = location.partitionReferenceNum; + fid->udf.parent_partref = 0; + fid->udf.generation = inode->i_generation; + + if (parent) { + location = UDF_I(parent)->i_location; + fid->udf.parent_block = location.logicalBlockNum; + fid->udf.parent_partref = location.partitionReferenceNum; + fid->udf.parent_generation = inode->i_generation; + *lenp = 5; + type = FILEID_UDF_WITH_PARENT; + } + + return type; +} + +const struct export_operations udf_export_ops = { + .encode_fh = udf_encode_fh, + .fh_to_dentry = udf_fh_to_dentry, + .fh_to_parent = udf_fh_to_parent, + .get_parent = udf_get_parent, +}; + +const struct inode_operations udf_dir_inode_operations = { .lookup = udf_lookup, .create = udf_create, .link = udf_link, @@ -1329,4 +1334,10 @@ struct inode_operations udf_dir_inode_operations = { .rmdir = udf_rmdir, .mknod = udf_mknod, .rename = udf_rename, + .tmpfile = udf_tmpfile, +}; +const struct inode_operations udf_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = page_follow_link_light, + .put_link = page_put_link, }; diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h index e82aae65269..fbff74654df 100644 --- a/fs/udf/osta_udf.h +++ b/fs/udf/osta_udf.h @@ -65,31 +65,27 @@ #define IS_DF_HARD_WRITE_PROTECT 0x01 #define IS_DF_SOFT_WRITE_PROTECT 0x02 -struct UDFIdentSuffix -{ +struct UDFIdentSuffix { __le16 UDFRevision; uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[4]; } __attribute__ ((packed)); -struct impIdentSuffix -{ +struct impIdentSuffix { uint8_t OSClass; uint8_t OSIdentifier; uint8_t reserved[6]; } __attribute__ ((packed)); -struct appIdentSuffix -{ +struct appIdentSuffix { uint8_t impUse[8]; } __attribute__ ((packed)); /* Logical Volume Integrity Descriptor (UDF 2.50 2.2.6) */ /* Implementation Use (UDF 2.50 2.2.6.4) */ -struct logicalVolIntegrityDescImpUse -{ - regid impIdent; +struct logicalVolIntegrityDescImpUse { + struct regid impIdent; __le32 numFiles; __le32 numDirs; __le16 minUDFReadRev; @@ -100,62 +96,57 @@ struct logicalVolIntegrityDescImpUse /* Implementation Use Volume Descriptor (UDF 2.50 2.2.7) */ /* Implementation Use (UDF 2.50 2.2.7.2) */ -struct impUseVolDescImpUse -{ - charspec LVICharset; +struct impUseVolDescImpUse { + struct charspec LVICharset; dstring logicalVolIdent[128]; dstring LVInfo1[36]; dstring LVInfo2[36]; dstring LVInfo3[36]; - regid impIdent; + struct regid impIdent; uint8_t impUse[128]; } __attribute__ ((packed)); -struct udfPartitionMap2 -{ +struct udfPartitionMap2 { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; - regid partIdent; + struct regid partIdent; __le16 volSeqNum; __le16 partitionNum; } __attribute__ ((packed)); /* Virtual Partition Map (UDF 2.50 2.2.8) */ -struct virtualPartitionMap -{ +struct virtualPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; - regid partIdent; + struct regid partIdent; __le16 volSeqNum; __le16 partitionNum; uint8_t reserved2[24]; } __attribute__ ((packed)); /* Sparable Partition Map (UDF 2.50 2.2.9) */ -struct sparablePartitionMap -{ - uint8_t partitionMapType; - uint8_t partitionMapLength; - uint8_t reserved1[2]; - regid partIdent; - __le16 volSeqNum; - __le16 partitionNum; - __le16 packetLength; - uint8_t numSparingTables; - uint8_t reserved2[1]; - __le32 sizeSparingTable; - __le32 locSparingTable[4]; +struct sparablePartitionMap { + uint8_t partitionMapType; + uint8_t partitionMapLength; + uint8_t reserved1[2]; + struct regid partIdent; + __le16 volSeqNum; + __le16 partitionNum; + __le16 packetLength; + uint8_t numSparingTables; + uint8_t reserved2[1]; + __le32 sizeSparingTable; + __le32 locSparingTable[4]; } __attribute__ ((packed)); /* Metadata Partition Map (UDF 2.4.0 2.2.10) */ -struct metadataPartitionMap -{ +struct metadataPartitionMap { uint8_t partitionMapType; uint8_t partitionMapLength; uint8_t reserved1[2]; - regid partIdent; + struct regid partIdent; __le16 volSeqNum; __le16 partitionNum; __le32 metadataFileLoc; @@ -168,18 +159,16 @@ struct metadataPartitionMap } __attribute__ ((packed)); /* Virtual Allocation Table (UDF 1.5 2.2.10) */ -struct virtualAllocationTable15 -{ +struct virtualAllocationTable15 { __le32 VirtualSector[0]; - regid vatIdent; + struct regid vatIdent; __le32 previousVATICBLoc; -} __attribute__ ((packed)); +} __attribute__ ((packed)); #define ICBTAG_FILE_TYPE_VAT15 0x00U /* Virtual Allocation Table (UDF 2.50 2.2.11) */ -struct virtualAllocationTable20 -{ +struct virtualAllocationTable20 { __le16 lengthHeader; __le16 lengthImpUse; dstring logicalVolIdent[128]; @@ -197,16 +186,14 @@ struct virtualAllocationTable20 #define ICBTAG_FILE_TYPE_VAT20 0xF8U /* Sparing Table (UDF 2.50 2.2.12) */ -struct sparingEntry -{ +struct sparingEntry { __le32 origLocation; __le32 mappedLocation; } __attribute__ ((packed)); -struct sparingTable -{ - tag descTag; - regid sparingIdent; +struct sparingTable { + struct tag descTag; + struct regid sparingIdent; __le16 reallocationTableLen; __le16 reserved; __le32 sequenceNum; @@ -219,9 +206,8 @@ struct sparingTable #define ICBTAG_FILE_TYPE_MIRROR 0xFB #define ICBTAG_FILE_TYPE_BITMAP 0xFC -/* struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */ -struct allocDescImpUse -{ +/* struct struct long_ad ICB - ADImpUse (UDF 2.50 2.2.4.3) */ +struct allocDescImpUse { __le16 flags; uint8_t impUse[4]; } __attribute__ ((packed)); @@ -233,15 +219,13 @@ struct allocDescImpUse /* Implementation Use Extended Attribute (UDF 2.50 3.3.4.5) */ /* FreeEASpace (UDF 2.50 3.3.4.5.1.1) */ -struct freeEaSpace -{ +struct freeEaSpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); /* DVD Copyright Management Information (UDF 2.50 3.3.4.5.1.2) */ -struct DVDCopyrightImpUse -{ +struct DVDCopyrightImpUse { __le16 headerChecksum; uint8_t CGMSInfo; uint8_t dataType; @@ -250,8 +234,7 @@ struct DVDCopyrightImpUse /* Application Use Extended Attribute (UDF 2.50 3.3.4.6) */ /* FreeAppEASpace (UDF 2.50 3.3.4.6.1) */ -struct freeAppEASpace -{ +struct freeAppEASpace { __le16 headerChecksum; uint8_t freeEASpace[0]; } __attribute__ ((packed)); diff --git a/fs/udf/partition.c b/fs/udf/partition.c index 4d36f264be0..d6caf01a209 100644 --- a/fs/udf/partition.c +++ b/fs/udf/partition.c @@ -4,11 +4,6 @@ * PURPOSE * Partition handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -19,7 +14,7 @@ * * HISTORY * - * 12/06/98 blf Created file. + * 12/06/98 blf Created file. * */ @@ -29,110 +24,130 @@ #include <linux/fs.h> #include <linux/string.h> -#include <linux/udf_fs.h> -#include <linux/slab.h> #include <linux/buffer_head.h> +#include <linux/mutex.h> -inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) { - if (partition >= UDF_SB_NUMPARTS(sb)) - { + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map; + if (partition >= sbi->s_partitions) { udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n", - block, partition, offset); + block, partition, offset); return 0xFFFFFFFF; } - if (UDF_SB_PARTFUNC(sb, partition)) - return UDF_SB_PARTFUNC(sb, partition)(sb, block, partition, offset); + map = &sbi->s_partmaps[partition]; + if (map->s_partition_func) + return map->s_partition_func(sb, block, partition, offset); else - return UDF_SB_PARTROOT(sb, partition) + block + offset; + return map->s_partition_root + block + offset; } -uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) { struct buffer_head *bh = NULL; uint32_t newblock; uint32_t index; uint32_t loc; + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map; + struct udf_virtual_data *vdata; + struct udf_inode_info *iinfo = UDF_I(sbi->s_vat_inode); - index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(uint32_t); + map = &sbi->s_partmaps[partition]; + vdata = &map->s_type_specific.s_virtual; - if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries) - { + if (block > vdata->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); + block, vdata->s_num_entries); return 0xFFFFFFFF; } - if (block >= index) - { + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + loc = le32_to_cpu(((__le32 *)(iinfo->i_ext.i_data + + vdata->s_start_offset))[block]); + goto translate; + } + index = (sb->s_blocksize - vdata->s_start_offset) / sizeof(uint32_t); + if (block >= index) { block -= index; newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t))); index = block % (sb->s_blocksize / sizeof(uint32_t)); - } - else - { + } else { newblock = 0; - index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(uint32_t) + block; + index = vdata->s_start_offset / sizeof(uint32_t) + block; } - loc = udf_block_map(UDF_SB_VAT(sb), newblock); + loc = udf_block_map(sbi->s_vat_inode, newblock); - if (!(bh = sb_bread(sb, loc))) - { + bh = sb_bread(sb, loc); + if (!bh) { udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n", - sb, block, partition, loc, index); + sb, block, partition, loc, index); return 0xFFFFFFFF; } loc = le32_to_cpu(((__le32 *)bh->b_data)[index]); - udf_release_data(bh); + brelse(bh); - if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition) - { +translate: + if (iinfo->i_location.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); + return udf_get_pblock(sb, loc, + iinfo->i_location.partitionReferenceNum, + offset); } -inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) { return udf_get_pblock_virt15(sb, block, partition, offset); } -uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset) +uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) { int i; struct sparingTable *st = NULL; - uint32_t packet = (block + offset) & ~(UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1); + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map; + uint32_t packet; + struct udf_sparing_data *sdata; + + map = &sbi->s_partmaps[partition]; + sdata = &map->s_type_specific.s_sparing; + packet = (block + offset) & ~(sdata->s_packet_len - 1); - for (i=0; i<4; i++) - { - if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL) - { - st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,partition).s_spar_map[i]->b_data; + for (i = 0; i < 4; i++) { + if (sdata->s_spar_map[i] != NULL) { + st = (struct sparingTable *) + sdata->s_spar_map[i]->b_data; break; } } - if (st) - { - for (i=0; i<le16_to_cpu(st->reallocationTableLen); i++) - { - if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0) + if (st) { + for (i = 0; i < le16_to_cpu(st->reallocationTableLen); i++) { + struct sparingEntry *entry = &st->mapEntry[i]; + u32 origLoc = le32_to_cpu(entry->origLocation); + if (origLoc >= 0xFFFFFFF0) break; - else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet) - { - return le32_to_cpu(st->mapEntry[i].mappedLocation) + - ((block + offset) & (UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1)); - } - else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet) + else if (origLoc == packet) + return le32_to_cpu(entry->mappedLocation) + + ((block + offset) & + (sdata->s_packet_len - 1)); + else if (origLoc > packet) break; } } - return UDF_SB_PARTROOT(sb,partition) + block + offset; + + return map->s_partition_root + block + offset; } int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) @@ -142,85 +157,183 @@ int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block) struct sparingEntry mapEntry; uint32_t packet; int i, j, k, l; + struct udf_sb_info *sbi = UDF_SB(sb); + u16 reallocationTableLen; + struct buffer_head *bh; + int ret = 0; + + mutex_lock(&sbi->s_alloc_mutex); + for (i = 0; i < sbi->s_partitions; i++) { + struct udf_part_map *map = &sbi->s_partmaps[i]; + if (old_block > map->s_partition_root && + old_block < map->s_partition_root + map->s_partition_len) { + sdata = &map->s_type_specific.s_sparing; + packet = (old_block - map->s_partition_root) & + ~(sdata->s_packet_len - 1); - for (i=0; i<UDF_SB_NUMPARTS(sb); i++) - { - if (old_block > UDF_SB_PARTROOT(sb,i) && - old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i)) - { - sdata = &UDF_SB_TYPESPAR(sb,i); - packet = (old_block - UDF_SB_PARTROOT(sb,i)) & ~(sdata->s_packet_len - 1); - - for (j=0; j<4; j++) - { - if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) - { - st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; + for (j = 0; j < 4; j++) + if (sdata->s_spar_map[j] != NULL) { + st = (struct sparingTable *) + sdata->s_spar_map[j]->b_data; break; } + + if (!st) { + ret = 1; + goto out; } - if (!st) - return 1; - - for (k=0; k<le16_to_cpu(st->reallocationTableLen); k++) - { - if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF) - { - for (; j<4; j++) - { - if (sdata->s_spar_map[j]) - { - st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; - st->mapEntry[k].origLocation = cpu_to_le32(packet); - udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); - mark_buffer_dirty(sdata->s_spar_map[j]); - } + reallocationTableLen = + le16_to_cpu(st->reallocationTableLen); + for (k = 0; k < reallocationTableLen; k++) { + struct sparingEntry *entry = &st->mapEntry[k]; + u32 origLoc = le32_to_cpu(entry->origLocation); + + if (origLoc == 0xFFFFFFFF) { + for (; j < 4; j++) { + int len; + bh = sdata->s_spar_map[j]; + if (!bh) + continue; + + st = (struct sparingTable *) + bh->b_data; + entry->origLocation = + cpu_to_le32(packet); + len = + sizeof(struct sparingTable) + + reallocationTableLen * + sizeof(struct sparingEntry); + udf_update_tag((char *)st, len); + mark_buffer_dirty(bh); } - *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + - ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); - return 0; - } - else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet) - { - *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + - ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); - return 0; - } - else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet) + *new_block = le32_to_cpu( + entry->mappedLocation) + + ((old_block - + map->s_partition_root) & + (sdata->s_packet_len - 1)); + ret = 0; + goto out; + } else if (origLoc == packet) { + *new_block = le32_to_cpu( + entry->mappedLocation) + + ((old_block - + map->s_partition_root) & + (sdata->s_packet_len - 1)); + ret = 0; + goto out; + } else if (origLoc > packet) break; } - for (l=k; l<le16_to_cpu(st->reallocationTableLen); l++) - { - if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF) - { - for (; j<4; j++) - { - if (sdata->s_spar_map[j]) - { - st = (struct sparingTable *)sdata->s_spar_map[j]->b_data; - mapEntry = st->mapEntry[l]; - mapEntry.origLocation = cpu_to_le32(packet); - memmove(&st->mapEntry[k+1], &st->mapEntry[k], (l-k)*sizeof(struct sparingEntry)); - st->mapEntry[k] = mapEntry; - udf_update_tag((char *)st, sizeof(struct sparingTable) + le16_to_cpu(st->reallocationTableLen) * sizeof(struct sparingEntry)); - mark_buffer_dirty(sdata->s_spar_map[j]); - } - } - *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) + - ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1)); - return 0; + + for (l = k; l < reallocationTableLen; l++) { + struct sparingEntry *entry = &st->mapEntry[l]; + u32 origLoc = le32_to_cpu(entry->origLocation); + + if (origLoc != 0xFFFFFFFF) + continue; + + for (; j < 4; j++) { + bh = sdata->s_spar_map[j]; + if (!bh) + continue; + + st = (struct sparingTable *)bh->b_data; + mapEntry = st->mapEntry[l]; + mapEntry.origLocation = + cpu_to_le32(packet); + memmove(&st->mapEntry[k + 1], + &st->mapEntry[k], + (l - k) * + sizeof(struct sparingEntry)); + st->mapEntry[k] = mapEntry; + udf_update_tag((char *)st, + sizeof(struct sparingTable) + + reallocationTableLen * + sizeof(struct sparingEntry)); + mark_buffer_dirty(bh); } + *new_block = + le32_to_cpu( + st->mapEntry[k].mappedLocation) + + ((old_block - map->s_partition_root) & + (sdata->s_packet_len - 1)); + ret = 0; + goto out; } - return 1; - } + + ret = 1; + goto out; + } /* if old_block */ } - if (i == UDF_SB_NUMPARTS(sb)) - { + + if (i == sbi->s_partitions) { /* outside of partitions */ /* for now, fail =) */ - return 1; + ret = 1; + } + +out: + mutex_unlock(&sbi->s_alloc_mutex); + return ret; +} + +static uint32_t udf_try_read_meta(struct inode *inode, uint32_t block, + uint16_t partition, uint32_t offset) +{ + struct super_block *sb = inode->i_sb; + struct udf_part_map *map; + struct kernel_lb_addr eloc; + uint32_t elen; + sector_t ext_offset; + struct extent_position epos = {}; + uint32_t phyblock; + + if (inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset) != + (EXT_RECORDED_ALLOCATED >> 30)) + phyblock = 0xFFFFFFFF; + else { + map = &UDF_SB(sb)->s_partmaps[partition]; + /* map to sparable/physical partition desc */ + phyblock = udf_get_pblock(sb, eloc.logicalBlockNum, + map->s_partition_num, ext_offset + offset); + } + + brelse(epos.bh); + return phyblock; +} + +uint32_t udf_get_pblock_meta25(struct super_block *sb, uint32_t block, + uint16_t partition, uint32_t offset) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map; + struct udf_meta_data *mdata; + uint32_t retblk; + struct inode *inode; + + udf_debug("READING from METADATA\n"); + + map = &sbi->s_partmaps[partition]; + mdata = &map->s_type_specific.s_metadata; + inode = mdata->s_metadata_fe ? : mdata->s_mirror_fe; + + /* We shouldn't mount such media... */ + BUG_ON(!inode); + retblk = udf_try_read_meta(inode, block, partition, offset); + if (retblk == 0xFFFFFFFF && mdata->s_metadata_fe) { + udf_warn(sb, "error reading from METADATA, trying to read from MIRROR\n"); + if (!(mdata->s_flags & MF_MIRROR_FE_LOADED)) { + mdata->s_mirror_fe = udf_find_metadata_inode_efe(sb, + mdata->s_mirror_file_loc, map->s_partition_num); + mdata->s_flags |= MF_MIRROR_FE_LOADED; + } + + inode = mdata->s_mirror_fe; + if (!inode) + return 0xFFFFFFFF; + retblk = udf_try_read_meta(inode, block, partition, offset); } - return 0; + return retblk; } diff --git a/fs/udf/super.c b/fs/udf/super.c index 15bd4f24c5b..3286db047a4 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -14,11 +14,6 @@ * http://www.ecma.ch/ * http://www.iso.org/ * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -38,14 +33,13 @@ * 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/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 "udfdecl.h" -#include <linux/config.h> #include <linux/blkdev.h> #include <linux/slab.h> #include <linux/kernel.h> @@ -54,13 +48,17 @@ #include <linux/stat.h> #include <linux/cdrom.h> #include <linux/nls.h> -#include <linux/smp_lock.h> #include <linux/buffer_head.h> #include <linux/vfs.h> #include <linux/vmalloc.h> +#include <linux/errno.h> +#include <linux/mount.h> +#include <linux/seq_file.h> +#include <linux/bitmap.h> +#include <linux/crc-itu-t.h> +#include <linux/log2.h> #include <asm/byteorder.h> -#include <linux/udf_fs.h> #include "udf_sb.h" #include "udf_i.h" @@ -76,103 +74,143 @@ #define VDS_POS_TERMINATING_DESC 6 #define VDS_POS_LENGTH 7 -static char error_buf[1024]; +#define UDF_DEFAULT_BLOCKSIZE 2048 + +#define VSD_FIRST_SECTOR_OFFSET 32768 +#define VSD_MAX_SECTOR_OFFSET 0x800000 + +enum { UDF_MAX_LINKS = 0xffff }; /* These are the "meat" - everything else is stuffing */ static int udf_fill_super(struct super_block *, void *, int); static void udf_put_super(struct super_block *); -static void udf_write_super(struct super_block *); +static int udf_sync_fs(struct super_block *, int); static int udf_remount_fs(struct super_block *, int *, char *); -static int udf_check_valid(struct super_block *, int, int); -static int udf_vrs(struct super_block *sb, int silent); -static int udf_load_partition(struct super_block *, kernel_lb_addr *); -static int udf_load_logicalvol(struct super_block *, struct buffer_head *, kernel_lb_addr *); -static void udf_load_logicalvolint(struct super_block *, kernel_extent_ad); -static void udf_find_anchor(struct super_block *); -static int udf_find_fileset(struct super_block *, kernel_lb_addr *, kernel_lb_addr *); -static void udf_load_pvoldesc(struct super_block *, struct buffer_head *); -static void udf_load_fileset(struct super_block *, struct buffer_head *, kernel_lb_addr *); -static void udf_load_partdesc(struct super_block *, struct buffer_head *); +static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad); +static int udf_find_fileset(struct super_block *, struct kernel_lb_addr *, + struct kernel_lb_addr *); +static void udf_load_fileset(struct super_block *, struct buffer_head *, + struct kernel_lb_addr *); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); -static int udf_statfs(struct super_block *, struct kstatfs *); +static int udf_statfs(struct dentry *, struct kstatfs *); +static int udf_show_options(struct seq_file *, struct dentry *); + +struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) +{ + struct logicalVolIntegrityDesc *lvid; + unsigned int partnum; + unsigned int offset; + + if (!UDF_SB(sb)->s_lvid_bh) + return NULL; + lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data; + partnum = le32_to_cpu(lvid->numOfPartitions); + if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) - + offsetof(struct logicalVolIntegrityDesc, impUse)) / + (2 * sizeof(uint32_t)) < partnum) { + udf_err(sb, "Logical volume integrity descriptor corrupted " + "(numOfPartitions = %u)!\n", partnum); + return NULL; + } + /* The offset is to skip freeSpaceTable and sizeTable arrays */ + offset = partnum * 2 * sizeof(uint32_t); + return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]); +} /* UDF filesystem type */ -static struct super_block *udf_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static struct dentry *udf_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) { - return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super); + return mount_bdev(fs_type, flags, dev_name, data, udf_fill_super); } static struct file_system_type udf_fstype = { .owner = THIS_MODULE, .name = "udf", - .get_sb = udf_get_sb, + .mount = udf_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, }; +MODULE_ALIAS_FS("udf"); -static kmem_cache_t * udf_inode_cachep; +static struct kmem_cache *udf_inode_cachep; static struct inode *udf_alloc_inode(struct super_block *sb) { struct udf_inode_info *ei; - ei = (struct udf_inode_info *)kmem_cache_alloc(udf_inode_cachep, SLAB_KERNEL); + ei = kmem_cache_alloc(udf_inode_cachep, GFP_KERNEL); if (!ei) return NULL; + + ei->i_unique = 0; + ei->i_lenExtents = 0; + ei->i_next_alloc_block = 0; + ei->i_next_alloc_goal = 0; + ei->i_strat4096 = 0; + init_rwsem(&ei->i_data_sem); + ei->cached_extent.lstart = -1; + spin_lock_init(&ei->i_extent_cache_lock); + return &ei->vfs_inode; } -static void udf_destroy_inode(struct inode *inode) +static void udf_i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } -static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +static void udf_destroy_inode(struct inode *inode) { - struct udf_inode_info *ei = (struct udf_inode_info *) foo; + call_rcu(&inode->i_rcu, udf_i_callback); +} - if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) - { - ei->i_ext.i_data = NULL; - inode_init_once(&ei->vfs_inode); - } +static void init_once(void *foo) +{ + struct udf_inode_info *ei = (struct udf_inode_info *)foo; + + ei->i_ext.i_data = NULL; + inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), - 0, SLAB_RECLAIM_ACCOUNT, - init_once, NULL); - if (udf_inode_cachep == NULL) + 0, (SLAB_RECLAIM_ACCOUNT | + SLAB_MEM_SPREAD), + init_once); + if (!udf_inode_cachep) return -ENOMEM; return 0; } static void destroy_inodecache(void) { - if (kmem_cache_destroy(udf_inode_cachep)) - printk(KERN_INFO "udf_inode_cache: not all structures were freed\n"); + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(udf_inode_cachep); } /* Superblock operations */ -static struct super_operations udf_sb_ops = { - .alloc_inode = udf_alloc_inode, - .destroy_inode = udf_destroy_inode, - .write_inode = udf_write_inode, - .delete_inode = udf_delete_inode, - .clear_inode = udf_clear_inode, - .put_super = udf_put_super, - .write_super = udf_write_super, - .statfs = udf_statfs, - .remount_fs = udf_remount_fs, +static const struct super_operations udf_sb_ops = { + .alloc_inode = udf_alloc_inode, + .destroy_inode = udf_destroy_inode, + .write_inode = udf_write_inode, + .evict_inode = udf_evict_inode, + .put_super = udf_put_super, + .sync_fs = udf_sync_fs, + .statfs = udf_statfs, + .remount_fs = udf_remount_fs, + .show_options = udf_show_options, }; -struct udf_options -{ +struct udf_options { unsigned char novrs; unsigned int blocksize; unsigned int session; @@ -183,24 +221,30 @@ struct udf_options unsigned int fileset; unsigned int rootdir; unsigned int flags; - mode_t umask; - gid_t gid; - uid_t uid; + umode_t umask; + kgid_t gid; + kuid_t uid; + umode_t fmode; + umode_t dmode; struct nls_table *nls_map; }; static int __init init_udf_fs(void) { int err; + err = init_inodecache(); if (err) goto out1; err = register_filesystem(&udf_fstype); if (err) goto out; + return 0; + out: destroy_inodecache(); + out1: return err; } @@ -214,6 +258,134 @@ static void __exit exit_udf_fs(void) module_init(init_udf_fs) module_exit(exit_udf_fs) +static int udf_sb_alloc_partition_maps(struct super_block *sb, u32 count) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + + sbi->s_partmaps = kcalloc(count, sizeof(struct udf_part_map), + GFP_KERNEL); + if (!sbi->s_partmaps) { + udf_err(sb, "Unable to allocate space for %d partition maps\n", + count); + sbi->s_partitions = 0; + return -ENOMEM; + } + + sbi->s_partitions = count; + return 0; +} + +static void udf_sb_free_bitmap(struct udf_bitmap *bitmap) +{ + int i; + int nr_groups = bitmap->s_nr_groups; + int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * + nr_groups); + + for (i = 0; i < nr_groups; i++) + if (bitmap->s_block_bitmap[i]) + brelse(bitmap->s_block_bitmap[i]); + + if (size <= PAGE_SIZE) + kfree(bitmap); + else + vfree(bitmap); +} + +static void udf_free_partition(struct udf_part_map *map) +{ + int i; + struct udf_meta_data *mdata; + + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) + iput(map->s_uspace.s_table); + if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) + iput(map->s_fspace.s_table); + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) + udf_sb_free_bitmap(map->s_uspace.s_bitmap); + if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) + udf_sb_free_bitmap(map->s_fspace.s_bitmap); + if (map->s_partition_type == UDF_SPARABLE_MAP15) + for (i = 0; i < 4; i++) + brelse(map->s_type_specific.s_sparing.s_spar_map[i]); + else if (map->s_partition_type == UDF_METADATA_MAP25) { + mdata = &map->s_type_specific.s_metadata; + iput(mdata->s_metadata_fe); + mdata->s_metadata_fe = NULL; + + iput(mdata->s_mirror_fe); + mdata->s_mirror_fe = NULL; + + iput(mdata->s_bitmap_fe); + mdata->s_bitmap_fe = NULL; + } +} + +static void udf_sb_free_partitions(struct super_block *sb) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + int i; + if (sbi->s_partmaps == NULL) + return; + for (i = 0; i < sbi->s_partitions; i++) + udf_free_partition(&sbi->s_partmaps[i]); + kfree(sbi->s_partmaps); + sbi->s_partmaps = NULL; +} + +static int udf_show_options(struct seq_file *seq, struct dentry *root) +{ + struct super_block *sb = root->d_sb; + struct udf_sb_info *sbi = UDF_SB(sb); + + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT)) + seq_puts(seq, ",nostrict"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_BLOCKSIZE_SET)) + seq_printf(seq, ",bs=%lu", sb->s_blocksize); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE)) + seq_puts(seq, ",unhide"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE)) + seq_puts(seq, ",undelete"); + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_USE_AD_IN_ICB)) + seq_puts(seq, ",noadinicb"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_USE_SHORT_AD)) + seq_puts(seq, ",shortad"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_FORGET)) + seq_puts(seq, ",uid=forget"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_IGNORE)) + seq_puts(seq, ",uid=ignore"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_FORGET)) + seq_puts(seq, ",gid=forget"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_IGNORE)) + seq_puts(seq, ",gid=ignore"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET)) + seq_printf(seq, ",uid=%u", from_kuid(&init_user_ns, sbi->s_uid)); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET)) + seq_printf(seq, ",gid=%u", from_kgid(&init_user_ns, sbi->s_gid)); + if (sbi->s_umask != 0) + seq_printf(seq, ",umask=%ho", sbi->s_umask); + if (sbi->s_fmode != UDF_INVALID_MODE) + seq_printf(seq, ",mode=%ho", sbi->s_fmode); + if (sbi->s_dmode != UDF_INVALID_MODE) + seq_printf(seq, ",dmode=%ho", sbi->s_dmode); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_SESSION_SET)) + seq_printf(seq, ",session=%u", sbi->s_session); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_LASTBLOCK_SET)) + seq_printf(seq, ",lastblock=%u", sbi->s_last_block); + if (sbi->s_anchor != 0) + seq_printf(seq, ",anchor=%u", sbi->s_anchor); + /* + * volume, partition, fileset and rootdir seem to be ignored + * currently + */ + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) + seq_puts(seq, ",utf8"); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP) && sbi->s_nls_map) + seq_printf(seq, ",iocharset=%s", sbi->s_nls_map->charset); + + return 0; +} + /* * udf_parse_options * @@ -225,6 +397,8 @@ module_exit(exit_udf_fs) * * gid= Set the default group. * umask= Set the default umask. + * mode= Set the default file permissions. + * dmode= Set the default directory permissions. * uid= Set the default user. * bs= Set the block size. * unhide Show otherwise hidden files. @@ -238,7 +412,7 @@ module_exit(exit_udf_fs) * * The remaining are for debugging and disaster recovery: * - * novrs Skip volume sequence recognition + * novrs Skip volume sequence recognition * * The following expect a offset from 0. * @@ -274,42 +448,48 @@ enum { Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, Opt_rootdir, Opt_utf8, Opt_iocharset, - Opt_err + Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore, + Opt_fmode, Opt_dmode }; -static match_table_t tokens = { - {Opt_novrs, "novrs"}, - {Opt_nostrict, "nostrict"}, - {Opt_bs, "bs=%u"}, - {Opt_unhide, "unhide"}, - {Opt_undelete, "undelete"}, - {Opt_noadinicb, "noadinicb"}, - {Opt_adinicb, "adinicb"}, - {Opt_shortad, "shortad"}, - {Opt_longad, "longad"}, - {Opt_gid, "gid=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_umask, "umask=%o"}, - {Opt_session, "session=%u"}, - {Opt_lastblock, "lastblock=%u"}, - {Opt_anchor, "anchor=%u"}, - {Opt_volume, "volume=%u"}, - {Opt_partition, "partition=%u"}, - {Opt_fileset, "fileset=%u"}, - {Opt_rootdir, "rootdir=%u"}, - {Opt_utf8, "utf8"}, - {Opt_iocharset, "iocharset=%s"}, - {Opt_err, NULL} +static const match_table_t tokens = { + {Opt_novrs, "novrs"}, + {Opt_nostrict, "nostrict"}, + {Opt_bs, "bs=%u"}, + {Opt_unhide, "unhide"}, + {Opt_undelete, "undelete"}, + {Opt_noadinicb, "noadinicb"}, + {Opt_adinicb, "adinicb"}, + {Opt_shortad, "shortad"}, + {Opt_longad, "longad"}, + {Opt_uforget, "uid=forget"}, + {Opt_uignore, "uid=ignore"}, + {Opt_gforget, "gid=forget"}, + {Opt_gignore, "gid=ignore"}, + {Opt_gid, "gid=%u"}, + {Opt_uid, "uid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_session, "session=%u"}, + {Opt_lastblock, "lastblock=%u"}, + {Opt_anchor, "anchor=%u"}, + {Opt_volume, "volume=%u"}, + {Opt_partition, "partition=%u"}, + {Opt_fileset, "fileset=%u"}, + {Opt_rootdir, "rootdir=%u"}, + {Opt_utf8, "utf8"}, + {Opt_iocharset, "iocharset=%s"}, + {Opt_fmode, "mode=%o"}, + {Opt_dmode, "dmode=%o"}, + {Opt_err, NULL} }; -static int -udf_parse_options(char *options, struct udf_options *uopt) +static int udf_parse_options(char *options, struct udf_options *uopt, + bool remount) { char *p; int option; uopt->novrs = 0; - uopt->blocksize = 2048; uopt->partition = 0xFFFF; uopt->session = 0xFFFFFFFF; uopt->lastblock = 0; @@ -322,221 +502,227 @@ udf_parse_options(char *options, struct udf_options *uopt) if (!options) return 1; - while ((p = strsep(&options, ",")) != NULL) - { + while ((p = strsep(&options, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; + unsigned n; if (!*p) continue; token = match_token(p, tokens, args); - switch (token) - { - case Opt_novrs: - uopt->novrs = 1; - case Opt_bs: - if (match_int(&args[0], &option)) - return 0; - uopt->blocksize = option; - break; - case Opt_unhide: - uopt->flags |= (1 << UDF_FLAG_UNHIDE); - break; - case Opt_undelete: - uopt->flags |= (1 << UDF_FLAG_UNDELETE); - break; - case Opt_noadinicb: - uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_adinicb: - uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_shortad: - uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_longad: - uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_gid: - if (match_int(args, &option)) - return 0; - uopt->gid = option; - break; - case Opt_uid: - if (match_int(args, &option)) - return 0; - uopt->uid = option; - break; - case Opt_umask: - if (match_octal(args, &option)) - return 0; - uopt->umask = option; - break; - case Opt_nostrict: - uopt->flags &= ~(1 << UDF_FLAG_STRICT); - break; - case Opt_session: - if (match_int(args, &option)) - return 0; - uopt->session = option; - break; - case Opt_lastblock: - if (match_int(args, &option)) - return 0; - uopt->lastblock = option; - break; - case Opt_anchor: - if (match_int(args, &option)) - return 0; - uopt->anchor = option; - break; - case Opt_volume: - if (match_int(args, &option)) - return 0; - uopt->volume = option; - break; - case Opt_partition: - if (match_int(args, &option)) - return 0; - uopt->partition = option; - break; - case Opt_fileset: - if (match_int(args, &option)) - return 0; - uopt->fileset = option; - break; - case Opt_rootdir: - if (match_int(args, &option)) - return 0; - uopt->rootdir = option; - break; - case Opt_utf8: - uopt->flags |= (1 << UDF_FLAG_UTF8); - break; + switch (token) { + case Opt_novrs: + uopt->novrs = 1; + break; + case Opt_bs: + if (match_int(&args[0], &option)) + return 0; + n = option; + if (n != 512 && n != 1024 && n != 2048 && n != 4096) + return 0; + uopt->blocksize = n; + uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET); + break; + case Opt_unhide: + uopt->flags |= (1 << UDF_FLAG_UNHIDE); + break; + case Opt_undelete: + uopt->flags |= (1 << UDF_FLAG_UNDELETE); + break; + case Opt_noadinicb: + uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); + break; + case Opt_adinicb: + uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); + break; + case Opt_shortad: + uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_longad: + uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_gid: + if (match_int(args, &option)) + return 0; + uopt->gid = make_kgid(current_user_ns(), option); + if (!gid_valid(uopt->gid)) + return 0; + uopt->flags |= (1 << UDF_FLAG_GID_SET); + break; + case Opt_uid: + if (match_int(args, &option)) + return 0; + uopt->uid = make_kuid(current_user_ns(), option); + if (!uid_valid(uopt->uid)) + return 0; + uopt->flags |= (1 << UDF_FLAG_UID_SET); + break; + case Opt_umask: + if (match_octal(args, &option)) + return 0; + uopt->umask = option; + break; + case Opt_nostrict: + uopt->flags &= ~(1 << UDF_FLAG_STRICT); + break; + case Opt_session: + if (match_int(args, &option)) + return 0; + uopt->session = option; + if (!remount) + uopt->flags |= (1 << UDF_FLAG_SESSION_SET); + break; + case Opt_lastblock: + if (match_int(args, &option)) + return 0; + uopt->lastblock = option; + if (!remount) + uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET); + break; + case Opt_anchor: + if (match_int(args, &option)) + return 0; + uopt->anchor = option; + break; + case Opt_volume: + if (match_int(args, &option)) + return 0; + uopt->volume = option; + break; + case Opt_partition: + if (match_int(args, &option)) + return 0; + uopt->partition = option; + break; + case Opt_fileset: + if (match_int(args, &option)) + return 0; + uopt->fileset = option; + break; + case Opt_rootdir: + if (match_int(args, &option)) + return 0; + uopt->rootdir = option; + break; + case Opt_utf8: + uopt->flags |= (1 << UDF_FLAG_UTF8); + break; #ifdef CONFIG_UDF_NLS - case Opt_iocharset: - uopt->nls_map = load_nls(args[0].from); - uopt->flags |= (1 << UDF_FLAG_NLS_MAP); - break; + case Opt_iocharset: + uopt->nls_map = load_nls(args[0].from); + uopt->flags |= (1 << UDF_FLAG_NLS_MAP); + break; #endif - default: - printk(KERN_ERR "udf: bad mount option \"%s\" " - "or missing value\n", p); + case Opt_uignore: + uopt->flags |= (1 << UDF_FLAG_UID_IGNORE); + break; + case Opt_uforget: + uopt->flags |= (1 << UDF_FLAG_UID_FORGET); + break; + case Opt_gignore: + uopt->flags |= (1 << UDF_FLAG_GID_IGNORE); + break; + case Opt_gforget: + uopt->flags |= (1 << UDF_FLAG_GID_FORGET); + break; + case Opt_fmode: + if (match_octal(args, &option)) + return 0; + uopt->fmode = option & 0777; + break; + case Opt_dmode: + if (match_octal(args, &option)) + return 0; + uopt->dmode = option & 0777; + break; + default: + pr_err("bad mount option \"%s\" or missing value\n", p); return 0; } } return 1; } -void -udf_write_super(struct super_block *sb) -{ - lock_kernel(); - if (!(sb->s_flags & MS_RDONLY)) - udf_open_lvid(sb); - sb->s_dirt = 0; - unlock_kernel(); -} - -static int -udf_remount_fs(struct super_block *sb, int *flags, char *options) +static int udf_remount_fs(struct super_block *sb, int *flags, char *options) { struct udf_options uopt; + struct udf_sb_info *sbi = UDF_SB(sb); + int error = 0; + struct logicalVolIntegrityDescImpUse *lvidiu = udf_sb_lvidiu(sb); + + sync_filesystem(sb); + if (lvidiu) { + int write_rev = le16_to_cpu(lvidiu->minUDFWriteRev); + if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY)) + return -EACCES; + } - 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 ; + uopt.flags = sbi->s_flags; + uopt.uid = sbi->s_uid; + uopt.gid = sbi->s_gid; + uopt.umask = sbi->s_umask; + uopt.fmode = sbi->s_fmode; + uopt.dmode = sbi->s_dmode; - if ( !udf_parse_options(options, &uopt) ) + if (!udf_parse_options(options, &uopt, true)) return -EINVAL; - UDF_SB(sb)->s_flags = uopt.flags; - UDF_SB(sb)->s_uid = uopt.uid; - UDF_SB(sb)->s_gid = uopt.gid; - UDF_SB(sb)->s_umask = uopt.umask; - - if (UDF_SB_LVIDBH(sb)) { - int write_rev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); - if (write_rev > UDF_MAX_WRITE_VERSION) - *flags |= MS_RDONLY; - } + write_lock(&sbi->s_cred_lock); + sbi->s_flags = uopt.flags; + sbi->s_uid = uopt.uid; + sbi->s_gid = uopt.gid; + sbi->s_umask = uopt.umask; + sbi->s_fmode = uopt.fmode; + sbi->s_dmode = uopt.dmode; + write_unlock(&sbi->s_cred_lock); if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) - return 0; + goto out_unlock; + if (*flags & MS_RDONLY) udf_close_lvid(sb); else udf_open_lvid(sb); - return 0; +out_unlock: + return error; } -/* - * udf_set_blocksize - * - * PURPOSE - * Set the block size to be used in all transfers. - * - * DESCRIPTION - * To allow room for a DMA transfer, it is best to guess big when unsure. - * This routine picks 2048 bytes as the blocksize when guessing. This - * should be adequate until devices with larger block sizes become common. - * - * Note that the Linux kernel can currently only deal with blocksizes of - * 512, 1024, 2048, 4096, and 8192 bytes. - * - * PRE-CONDITIONS - * sb Pointer to _locked_ superblock. - * - * POST-CONDITIONS - * sb->s_blocksize Blocksize. - * sb->s_blocksize_bits log2 of blocksize. - * <return> 0 Blocksize is valid. - * <return> 1 Blocksize is invalid. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static int -udf_set_blocksize(struct super_block *sb, int bsize) -{ - if (!sb_min_blocksize(sb, bsize)) { - udf_debug("Bad block size (%d)\n", bsize); - printk(KERN_ERR "udf: bad block size (%d)\n", bsize); - return 0; - } - return sb->s_blocksize; -} - -static int -udf_vrs(struct super_block *sb, int silent) +/* Check Volume Structure Descriptors (ECMA 167 2/9.1) */ +/* We also check any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */ +static loff_t udf_check_vsd(struct super_block *sb) { struct volStructDesc *vsd = NULL; - int sector = 32768; + loff_t sector = VSD_FIRST_SECTOR_OFFSET; int sectorsize; struct buffer_head *bh = NULL; - int iso9660=0; - int nsr02=0; - int nsr03=0; - - /* Block size must be a multiple of 512 */ - if (sb->s_blocksize & 511) - return 0; + int nsr02 = 0; + int nsr03 = 0; + struct udf_sb_info *sbi; + sbi = UDF_SB(sb); if (sb->s_blocksize < sizeof(struct volStructDesc)) sectorsize = sizeof(struct volStructDesc); else sectorsize = sb->s_blocksize; - sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits); + sector += (sbi->s_session << sb->s_blocksize_bits); udf_debug("Starting at sector %u (%ld byte sectors)\n", - (sector >> sb->s_blocksize_bits), sb->s_blocksize); - /* Process the sequence (if applicable) */ - for (;!nsr02 && !nsr03; sector += sectorsize) - { + (unsigned int)(sector >> sb->s_blocksize_bits), + sb->s_blocksize); + /* Process the sequence (if applicable). The hard limit on the sector + * offset is arbitrary, hopefully large enough so that all valid UDF + * filesystems will be recognised. There is no mention of an upper + * bound to the size of the volume recognition area in the standard. + * The limit will prevent the code to read all the sectors of a + * specially crafted image (like a bluray disc full of CD001 sectors), + * potentially causing minutes or even hours of uninterruptible I/O + * activity. This actually happened with uninitialised SSD partitions + * (all 0xFF) before the check for the limit and all valid IDs were + * added */ + for (; !nsr02 && !nsr03 && sector < VSD_MAX_SECTOR_OFFSET; + sector += sectorsize) { /* Read a block */ bh = udf_tread(sb, sector >> sb->s_blocksize_bits); if (!bh) @@ -544,362 +730,308 @@ udf_vrs(struct super_block *sb, int silent) /* Look for ISO descriptors */ vsd = (struct volStructDesc *)(bh->b_data + - (sector & (sb->s_blocksize - 1))); + (sector & (sb->s_blocksize - 1))); - if (vsd->stdIdent[0] == 0) - { - udf_release_data(bh); - break; - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) - { - iso9660 = sector; - switch (vsd->structType) - { - case 0: - udf_debug("ISO9660 Boot Record found\n"); - break; - case 1: - udf_debug("ISO9660 Primary Volume Descriptor found\n"); - break; - case 2: - udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); - break; - case 3: - udf_debug("ISO9660 Volume Partition Descriptor found\n"); - break; - case 255: - udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); - break; - default: - udf_debug("ISO9660 VRS (%u) found\n", vsd->structType); - break; + if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, + VSD_STD_ID_LEN)) { + switch (vsd->structType) { + case 0: + udf_debug("ISO9660 Boot Record found\n"); + break; + case 1: + udf_debug("ISO9660 Primary Volume Descriptor found\n"); + break; + case 2: + udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); + break; + case 3: + udf_debug("ISO9660 Volume Partition Descriptor found\n"); + break; + case 255: + udf_debug("ISO9660 Volume Descriptor Set Terminator found\n"); + break; + default: + udf_debug("ISO9660 VRS (%u) found\n", + vsd->structType); + break; } - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN)) - { - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN)) - { - udf_release_data(bh); + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, + VSD_STD_ID_LEN)) + ; /* nothing */ + else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, + VSD_STD_ID_LEN)) { + brelse(bh); break; - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN)) - { + } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, + VSD_STD_ID_LEN)) nsr02 = sector; - } - else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN)) - { + else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, + VSD_STD_ID_LEN)) nsr03 = sector; + else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BOOT2, + VSD_STD_ID_LEN)) + ; /* nothing */ + else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CDW02, + VSD_STD_ID_LEN)) + ; /* nothing */ + else { + /* invalid id : end of volume recognition area */ + brelse(bh); + break; } - udf_release_data(bh); + brelse(bh); } if (nsr03) return nsr03; else if (nsr02) return nsr02; - else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768) + else if (!bh && sector - (sbi->s_session << sb->s_blocksize_bits) == + VSD_FIRST_SECTOR_OFFSET) return -1; else return 0; } -/* - * udf_find_anchor - * - * PURPOSE - * Find an anchor volume descriptor. - * - * PRE-CONDITIONS - * sb Pointer to _locked_ superblock. - * lastblock Last block on media. - * - * POST-CONDITIONS - * <return> 1 if not found, 0 if ok - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static void -udf_find_anchor(struct super_block *sb) -{ - int lastblock = UDF_SB_LASTBLOCK(sb); - struct buffer_head *bh = NULL; - uint16_t ident; - uint32_t location; - int i; - - if (lastblock) - { - int varlastblock = udf_variable_to_fixed(lastblock); - int last[] = { lastblock, lastblock - 2, - lastblock - 150, lastblock - 152, - varlastblock, varlastblock - 2, - varlastblock - 150, varlastblock - 152 }; - - lastblock = 0; - - /* Search for an anchor volume descriptor pointer */ - - /* according to spec, anchor is in either: - * block 256 - * lastblock-256 - * lastblock - * however, if the disc isn't closed, it could be 512 */ - - for (i=0; (!lastblock && i<sizeof(last)/sizeof(int)); i++) - { - if (last[i] < 0 || !(bh = sb_bread(sb, last[i]))) - { - ident = location = 0; - } - else - { - ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); - location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); - udf_release_data(bh); - } - - if (ident == TAG_IDENT_AVDP) - { - if (location == last[i] - UDF_SB_SESSION(sb)) - { - lastblock = UDF_SB_ANCHOR(sb)[0] = last[i] - UDF_SB_SESSION(sb); - UDF_SB_ANCHOR(sb)[1] = last[i] - 256 - UDF_SB_SESSION(sb); - } - else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb)) - { - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb); - UDF_SB_ANCHOR(sb)[1] = lastblock - 256 - UDF_SB_SESSION(sb); - } - else - udf_debug("Anchor found at block %d, location mismatch %d.\n", - last[i], location); - } - else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE) - { - lastblock = last[i]; - UDF_SB_ANCHOR(sb)[3] = 512; - } - else - { - if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256))) - { - ident = location = 0; - } - else - { - ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); - location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); - udf_release_data(bh); - } - - if (ident == TAG_IDENT_AVDP && - location == last[i] - 256 - UDF_SB_SESSION(sb)) - { - lastblock = last[i]; - UDF_SB_ANCHOR(sb)[1] = last[i] - 256; - } - else - { - if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb)))) - { - ident = location = 0; - } - else - { - ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); - location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); - udf_release_data(bh); - } - - if (ident == TAG_IDENT_AVDP && - location == udf_variable_to_fixed(last[i]) - 256) - { - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - lastblock = udf_variable_to_fixed(last[i]); - UDF_SB_ANCHOR(sb)[1] = lastblock - 256; - } - } - } - } - } - - if (!lastblock) - { - /* We havn't found the lastblock. check 312 */ - if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb)))) - { - ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent); - location = le32_to_cpu(((tag *)bh->b_data)->tagLocation); - udf_release_data(bh); - - if (ident == TAG_IDENT_AVDP && location == 256) - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - } - } - - for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++) - { - if (UDF_SB_ANCHOR(sb)[i]) - { - if (!(bh = udf_read_tagged(sb, - UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) - { - UDF_SB_ANCHOR(sb)[i] = 0; - } - else - { - udf_release_data(bh); - if ((ident != TAG_IDENT_AVDP) && (i || - (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE))) - { - UDF_SB_ANCHOR(sb)[i] = 0; - } - } - } - } - - UDF_SB_LASTBLOCK(sb) = lastblock; -} - -static int -udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, kernel_lb_addr *root) +static int udf_find_fileset(struct super_block *sb, + struct kernel_lb_addr *fileset, + struct kernel_lb_addr *root) { struct buffer_head *bh = NULL; long lastblock; uint16_t ident; + struct udf_sb_info *sbi; if (fileset->logicalBlockNum != 0xFFFFFFFF || - fileset->partitionReferenceNum != 0xFFFF) - { - bh = udf_read_ptagged(sb, *fileset, 0, &ident); + fileset->partitionReferenceNum != 0xFFFF) { + bh = udf_read_ptagged(sb, fileset, 0, &ident); - if (!bh) + if (!bh) { return 1; - else if (ident != TAG_IDENT_FSD) - { - udf_release_data(bh); + } else if (ident != TAG_IDENT_FSD) { + brelse(bh); return 1; } - + } - if (!bh) /* Search backwards through the partitions */ - { - kernel_lb_addr newfileset; + sbi = UDF_SB(sb); + if (!bh) { + /* Search backwards through the partitions */ + struct kernel_lb_addr newfileset; +/* --> cvg: FIXME - is it reasonable? */ 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); + + for (newfileset.partitionReferenceNum = sbi->s_partitions - 1; + (newfileset.partitionReferenceNum != 0xFFFF && + fileset->logicalBlockNum == 0xFFFFFFFF && + fileset->partitionReferenceNum == 0xFFFF); + newfileset.partitionReferenceNum--) { + lastblock = sbi->s_partmaps + [newfileset.partitionReferenceNum] + .s_partition_len; newfileset.logicalBlockNum = 0; - do - { - bh = udf_read_ptagged(sb, newfileset, 0, &ident); - if (!bh) - { - newfileset.logicalBlockNum ++; + do { + bh = udf_read_ptagged(sb, &newfileset, 0, + &ident); + if (!bh) { + newfileset.logicalBlockNum++; continue; } - switch (ident) + switch (ident) { + case TAG_IDENT_SBD: { - case TAG_IDENT_SBD: - { - struct spaceBitmapDesc *sp; - sp = (struct spaceBitmapDesc *)bh->b_data; - newfileset.logicalBlockNum += 1 + - ((le32_to_cpu(sp->numOfBytes) + sizeof(struct spaceBitmapDesc) - 1) - >> sb->s_blocksize_bits); - udf_release_data(bh); - break; - } - case TAG_IDENT_FSD: - { - *fileset = newfileset; - break; - } - default: - { - newfileset.logicalBlockNum ++; - udf_release_data(bh); - bh = NULL; - break; - } + 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); + brelse(bh); + break; } - } - while (newfileset.logicalBlockNum < lastblock && - fileset->logicalBlockNum == 0xFFFFFFFF && - fileset->partitionReferenceNum == 0xFFFF); + case TAG_IDENT_FSD: + *fileset = newfileset; + break; + default: + newfileset.logicalBlockNum++; + brelse(bh); + bh = NULL; + break; + } + } while (newfileset.logicalBlockNum < lastblock && + fileset->logicalBlockNum == 0xFFFFFFFF && + fileset->partitionReferenceNum == 0xFFFF); } } if ((fileset->logicalBlockNum != 0xFFFFFFFF || - fileset->partitionReferenceNum != 0xFFFF) && bh) - { + fileset->partitionReferenceNum != 0xFFFF) && bh) { udf_debug("Fileset at block=%d, partition=%d\n", - fileset->logicalBlockNum, fileset->partitionReferenceNum); + fileset->logicalBlockNum, + fileset->partitionReferenceNum); - UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum; + sbi->s_partition = fileset->partitionReferenceNum; udf_load_fileset(sb, bh, root); - udf_release_data(bh); + brelse(bh); return 0; } return 1; } -static void -udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) +/* + * Load primary Volume Descriptor Sequence + * + * Return <0 on error, 0 on success. -EAGAIN is special meaning next sequence + * should be tried. + */ +static int udf_load_pvoldesc(struct super_block *sb, sector_t block) { struct primaryVolDesc *pvoldesc; - time_t recording; - long recording_usec; - struct ustr instr; - struct ustr outstr; + struct ustr *instr, *outstr; + struct buffer_head *bh; + uint16_t ident; + int ret = -ENOMEM; + + instr = kmalloc(sizeof(struct ustr), GFP_NOFS); + if (!instr) + return -ENOMEM; + + outstr = kmalloc(sizeof(struct ustr), GFP_NOFS); + if (!outstr) + goto out1; + + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) { + ret = -EAGAIN; + goto out2; + } + + if (ident != TAG_IDENT_PVD) { + ret = -EIO; + goto out_bh; + } pvoldesc = (struct primaryVolDesc *)bh->b_data; - if ( udf_stamp_to_time(&recording, &recording_usec, - lets_to_cpu(pvoldesc->recordingDateAndTime)) ) - { - kernel_timestamp ts; - ts = lets_to_cpu(pvoldesc->recordingDateAndTime); - udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n", - recording, recording_usec, - ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone); - UDF_SB_RECORDTIME(sb).tv_sec = recording; - UDF_SB_RECORDTIME(sb).tv_nsec = recording_usec * 1000; - } - - if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) ) - { - if (udf_CS0toUTF8(&outstr, &instr)) - { - strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name, - outstr.u_len > 31 ? 31 : outstr.u_len); - udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb)); + if (udf_disk_stamp_to_time(&UDF_SB(sb)->s_record_time, + pvoldesc->recordingDateAndTime)) { +#ifdef UDFFS_DEBUG + struct timestamp *ts = &pvoldesc->recordingDateAndTime; + udf_debug("recording time %04u/%02u/%02u %02u:%02u (%x)\n", + le16_to_cpu(ts->year), ts->month, ts->day, ts->hour, + ts->minute, le16_to_cpu(ts->typeAndTimezone)); +#endif + } + + if (!udf_build_ustr(instr, pvoldesc->volIdent, 32)) + if (udf_CS0toUTF8(outstr, instr)) { + strncpy(UDF_SB(sb)->s_volume_ident, outstr->u_name, + outstr->u_len > 31 ? 31 : outstr->u_len); + udf_debug("volIdent[] = '%s'\n", + UDF_SB(sb)->s_volume_ident); } + + if (!udf_build_ustr(instr, pvoldesc->volSetIdent, 128)) + if (udf_CS0toUTF8(outstr, instr)) + udf_debug("volSetIdent[] = '%s'\n", outstr->u_name); + + ret = 0; +out_bh: + brelse(bh); +out2: + kfree(outstr); +out1: + kfree(instr); + return ret; +} + +struct inode *udf_find_metadata_inode_efe(struct super_block *sb, + u32 meta_file_loc, u32 partition_num) +{ + struct kernel_lb_addr addr; + struct inode *metadata_fe; + + addr.logicalBlockNum = meta_file_loc; + addr.partitionReferenceNum = partition_num; + + metadata_fe = udf_iget(sb, &addr); + + if (metadata_fe == NULL) + udf_warn(sb, "metadata inode efe not found\n"); + else if (UDF_I(metadata_fe)->i_alloc_type != ICBTAG_FLAG_AD_SHORT) { + udf_warn(sb, "metadata inode efe does not have short allocation descriptors!\n"); + iput(metadata_fe); + metadata_fe = NULL; } - if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) ) - { - if (udf_CS0toUTF8(&outstr, &instr)) - udf_debug("volSetIdent[] = '%s'\n", outstr.u_name); + return metadata_fe; +} + +static int udf_load_metadata_files(struct super_block *sb, int partition) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map; + struct udf_meta_data *mdata; + struct kernel_lb_addr addr; + + map = &sbi->s_partmaps[partition]; + mdata = &map->s_type_specific.s_metadata; + + /* metadata address */ + udf_debug("Metadata file location: block = %d part = %d\n", + mdata->s_meta_file_loc, map->s_partition_num); + + mdata->s_metadata_fe = udf_find_metadata_inode_efe(sb, + mdata->s_meta_file_loc, map->s_partition_num); + + if (mdata->s_metadata_fe == NULL) { + /* mirror file entry */ + udf_debug("Mirror metadata file location: block = %d part = %d\n", + mdata->s_mirror_file_loc, map->s_partition_num); + + mdata->s_mirror_fe = udf_find_metadata_inode_efe(sb, + mdata->s_mirror_file_loc, map->s_partition_num); + + if (mdata->s_mirror_fe == NULL) { + udf_err(sb, "Both metadata and mirror metadata inode efe can not found\n"); + return -EIO; + } } + + /* + * bitmap file entry + * Note: + * Load only if bitmap file location differs from 0xFFFFFFFF (DCN-5102) + */ + if (mdata->s_bitmap_file_loc != 0xFFFFFFFF) { + addr.logicalBlockNum = mdata->s_bitmap_file_loc; + addr.partitionReferenceNum = map->s_partition_num; + + udf_debug("Bitmap file location: block = %d part = %d\n", + addr.logicalBlockNum, addr.partitionReferenceNum); + + mdata->s_bitmap_fe = udf_iget(sb, &addr); + if (mdata->s_bitmap_fe == NULL) { + if (sb->s_flags & MS_RDONLY) + udf_warn(sb, "bitmap inode efe not found but it's ok since the disc is mounted read-only\n"); + else { + udf_err(sb, "bitmap inode efe not found and attempted read-write mount\n"); + return -EIO; + } + } + } + + udf_debug("udf_load_metadata_files Ok\n"); + return 0; } -static void -udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr *root) +static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, + struct kernel_lb_addr *root) { struct fileSetDesc *fset; @@ -907,357 +1039,670 @@ udf_load_fileset(struct super_block *sb, struct buffer_head *bh, kernel_lb_addr *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation); - UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum); + UDF_SB(sb)->s_serial_number = le16_to_cpu(fset->descTag.tagSerialNum); + + udf_debug("Rootdir at block=%d, partition=%d\n", + root->logicalBlockNum, root->partitionReferenceNum); +} + +int udf_compute_nr_groups(struct super_block *sb, u32 partition) +{ + struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; + return DIV_ROUND_UP(map->s_partition_len + + (sizeof(struct spaceBitmapDesc) << 3), + sb->s_blocksize * 8); +} + +static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index) +{ + struct udf_bitmap *bitmap; + int nr_groups; + int size; + + nr_groups = udf_compute_nr_groups(sb, index); + size = sizeof(struct udf_bitmap) + + (sizeof(struct buffer_head *) * nr_groups); - udf_debug("Rootdir at block=%d, partition=%d\n", - root->logicalBlockNum, root->partitionReferenceNum); + if (size <= PAGE_SIZE) + bitmap = kzalloc(size, GFP_KERNEL); + else + bitmap = vzalloc(size); /* TODO: get rid of vzalloc */ + + if (bitmap == NULL) + return NULL; + + bitmap->s_nr_groups = nr_groups; + return bitmap; +} + +static int udf_fill_partdesc_info(struct super_block *sb, + struct partitionDesc *p, int p_index) +{ + struct udf_part_map *map; + struct udf_sb_info *sbi = UDF_SB(sb); + struct partitionHeaderDesc *phd; + + map = &sbi->s_partmaps[p_index]; + + map->s_partition_len = le32_to_cpu(p->partitionLength); /* blocks */ + map->s_partition_root = le32_to_cpu(p->partitionStartingLocation); + + if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_READ_ONLY)) + map->s_partition_flags |= UDF_PART_FLAG_READ_ONLY; + if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_WRITE_ONCE)) + map->s_partition_flags |= UDF_PART_FLAG_WRITE_ONCE; + if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_REWRITABLE)) + map->s_partition_flags |= UDF_PART_FLAG_REWRITABLE; + if (p->accessType == cpu_to_le32(PD_ACCESS_TYPE_OVERWRITABLE)) + map->s_partition_flags |= UDF_PART_FLAG_OVERWRITABLE; + + udf_debug("Partition (%d type %x) starts at physical %d, block length %d\n", + p_index, map->s_partition_type, + map->s_partition_root, map->s_partition_len); + + if (strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) && + strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03)) + return 0; + + phd = (struct partitionHeaderDesc *)p->partitionContentsUse; + if (phd->unallocSpaceTable.extLength) { + struct kernel_lb_addr loc = { + .logicalBlockNum = le32_to_cpu( + phd->unallocSpaceTable.extPosition), + .partitionReferenceNum = p_index, + }; + + map->s_uspace.s_table = udf_iget(sb, &loc); + if (!map->s_uspace.s_table) { + udf_debug("cannot load unallocSpaceTable (part %d)\n", + p_index); + return -EIO; + } + map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_TABLE; + udf_debug("unallocSpaceTable (part %d) @ %ld\n", + p_index, map->s_uspace.s_table->i_ino); + } + + if (phd->unallocSpaceBitmap.extLength) { + struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index); + if (!bitmap) + return -ENOMEM; + map->s_uspace.s_bitmap = bitmap; + bitmap->s_extPosition = le32_to_cpu( + phd->unallocSpaceBitmap.extPosition); + map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_BITMAP; + udf_debug("unallocSpaceBitmap (part %d) @ %d\n", + p_index, bitmap->s_extPosition); + } + + if (phd->partitionIntegrityTable.extLength) + udf_debug("partitionIntegrityTable (part %d)\n", p_index); + + if (phd->freedSpaceTable.extLength) { + struct kernel_lb_addr loc = { + .logicalBlockNum = le32_to_cpu( + phd->freedSpaceTable.extPosition), + .partitionReferenceNum = p_index, + }; + + map->s_fspace.s_table = udf_iget(sb, &loc); + if (!map->s_fspace.s_table) { + udf_debug("cannot load freedSpaceTable (part %d)\n", + p_index); + return -EIO; + } + + map->s_partition_flags |= UDF_PART_FLAG_FREED_TABLE; + udf_debug("freedSpaceTable (part %d) @ %ld\n", + p_index, map->s_fspace.s_table->i_ino); + } + + if (phd->freedSpaceBitmap.extLength) { + struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index); + if (!bitmap) + return -ENOMEM; + map->s_fspace.s_bitmap = bitmap; + bitmap->s_extPosition = le32_to_cpu( + phd->freedSpaceBitmap.extPosition); + map->s_partition_flags |= UDF_PART_FLAG_FREED_BITMAP; + udf_debug("freedSpaceBitmap (part %d) @ %d\n", + p_index, bitmap->s_extPosition); + } + return 0; +} + +static void udf_find_vat_block(struct super_block *sb, int p_index, + int type1_index, sector_t start_block) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map = &sbi->s_partmaps[p_index]; + sector_t vat_block; + struct kernel_lb_addr ino; + + /* + * VAT file entry is in the last recorded block. Some broken disks have + * it a few blocks before so try a bit harder... + */ + ino.partitionReferenceNum = type1_index; + for (vat_block = start_block; + vat_block >= map->s_partition_root && + vat_block >= start_block - 3 && + !sbi->s_vat_inode; vat_block--) { + ino.logicalBlockNum = vat_block - map->s_partition_root; + sbi->s_vat_inode = udf_iget(sb, &ino); + } } -static void -udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) +static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) { + struct udf_sb_info *sbi = UDF_SB(sb); + struct udf_part_map *map = &sbi->s_partmaps[p_index]; + struct buffer_head *bh = NULL; + struct udf_inode_info *vati; + uint32_t pos; + struct virtualAllocationTable20 *vat20; + sector_t blocks = sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits; + + udf_find_vat_block(sb, p_index, type1_index, sbi->s_last_block); + if (!sbi->s_vat_inode && + sbi->s_last_block != blocks - 1) { + pr_notice("Failed to read VAT inode from the last recorded block (%lu), retrying with the last block of the device (%lu).\n", + (unsigned long)sbi->s_last_block, + (unsigned long)blocks - 1); + udf_find_vat_block(sb, p_index, type1_index, blocks - 1); + } + if (!sbi->s_vat_inode) + return -EIO; + + if (map->s_partition_type == UDF_VIRTUAL_MAP15) { + map->s_type_specific.s_virtual.s_start_offset = 0; + map->s_type_specific.s_virtual.s_num_entries = + (sbi->s_vat_inode->i_size - 36) >> 2; + } else if (map->s_partition_type == UDF_VIRTUAL_MAP20) { + vati = UDF_I(sbi->s_vat_inode); + if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { + pos = udf_block_map(sbi->s_vat_inode, 0); + bh = sb_bread(sb, pos); + if (!bh) + return -EIO; + vat20 = (struct virtualAllocationTable20 *)bh->b_data; + } else { + vat20 = (struct virtualAllocationTable20 *) + vati->i_ext.i_data; + } + + map->s_type_specific.s_virtual.s_start_offset = + le16_to_cpu(vat20->lengthHeader); + map->s_type_specific.s_virtual.s_num_entries = + (sbi->s_vat_inode->i_size - + map->s_type_specific.s_virtual. + s_start_offset) >> 2; + brelse(bh); + } + return 0; +} + +/* + * Load partition descriptor block + * + * Returns <0 on error, 0 on success, -EAGAIN is special - try next descriptor + * sequence. + */ +static int udf_load_partdesc(struct super_block *sb, sector_t block) +{ + struct buffer_head *bh; struct partitionDesc *p; - int i; + struct udf_part_map *map; + struct udf_sb_info *sbi = UDF_SB(sb); + int i, type1_idx; + uint16_t partitionNumber; + uint16_t ident; + int ret; + + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + return -EAGAIN; + if (ident != TAG_IDENT_PD) { + ret = 0; + goto out_bh; + } p = (struct partitionDesc *)bh->b_data; + partitionNumber = le16_to_cpu(p->partitionNumber); + + /* First scan for TYPE1, SPARABLE and METADATA partitions */ + for (i = 0; i < sbi->s_partitions; i++) { + map = &sbi->s_partmaps[i]; + udf_debug("Searching map: (%d == %d)\n", + map->s_partition_num, partitionNumber); + if (map->s_partition_num == partitionNumber && + (map->s_partition_type == UDF_TYPE1_MAP15 || + map->s_partition_type == UDF_SPARABLE_MAP15)) + break; + } - for (i=0; i<UDF_SB_NUMPARTS(sb); i++) - { - udf_debug("Searching map: (%d == %d)\n", - UDF_SB_PARTMAPS(sb)[i].s_partition_num, le16_to_cpu(p->partitionNumber)); - if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber)) - { - UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */ - UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation); - if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_READ_ONLY) - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_READ_ONLY; - if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_WRITE_ONCE) - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_WRITE_ONCE; - if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_REWRITABLE) - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_REWRITABLE; - if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_OVERWRITABLE) - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_OVERWRITABLE; - - if (!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) || - !strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03)) - { - struct partitionHeaderDesc *phd; - - phd = (struct partitionHeaderDesc *)(p->partitionContentsUse); - if (phd->unallocSpaceTable.extLength) - { - kernel_lb_addr loc = { le32_to_cpu(phd->unallocSpaceTable.extPosition), i }; + if (i >= sbi->s_partitions) { + udf_debug("Partition (%d) not found in partition map\n", + partitionNumber); + ret = 0; + goto out_bh; + } - UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table = - udf_iget(sb, loc); - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_TABLE; - udf_debug("unallocSpaceTable (part %d) @ %ld\n", - i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino); - } - if (phd->unallocSpaceBitmap.extLength) - { - UDF_SB_ALLOC_BITMAP(sb, i, s_uspace); - if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL) - { - UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength = - le32_to_cpu(phd->unallocSpaceBitmap.extLength); - UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition = - le32_to_cpu(phd->unallocSpaceBitmap.extPosition); - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP; - udf_debug("unallocSpaceBitmap (part %d) @ %d\n", - i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition); - } - } - if (phd->partitionIntegrityTable.extLength) - udf_debug("partitionIntegrityTable (part %d)\n", i); - if (phd->freedSpaceTable.extLength) - { - kernel_lb_addr loc = { le32_to_cpu(phd->freedSpaceTable.extPosition), i }; + ret = udf_fill_partdesc_info(sb, p, i); + if (ret < 0) + goto out_bh; + + /* + * Now rescan for VIRTUAL or METADATA partitions when SPARABLE and + * PHYSICAL partitions are already set up + */ + type1_idx = i; +#ifdef UDFFS_DEBUG + map = NULL; /* supress 'maybe used uninitialized' warning */ +#endif + for (i = 0; i < sbi->s_partitions; i++) { + map = &sbi->s_partmaps[i]; - UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table = - udf_iget(sb, loc); - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_TABLE; - udf_debug("freedSpaceTable (part %d) @ %ld\n", - i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino); - } - if (phd->freedSpaceBitmap.extLength) - { - UDF_SB_ALLOC_BITMAP(sb, i, s_fspace); - if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL) - { - UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extLength = - le32_to_cpu(phd->freedSpaceBitmap.extLength); - UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition = - le32_to_cpu(phd->freedSpaceBitmap.extPosition); - UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP; - udf_debug("freedSpaceBitmap (part %d) @ %d\n", - i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition); - } - } - } + if (map->s_partition_num == partitionNumber && + (map->s_partition_type == UDF_VIRTUAL_MAP15 || + map->s_partition_type == UDF_VIRTUAL_MAP20 || + map->s_partition_type == UDF_METADATA_MAP25)) break; + } + + if (i >= sbi->s_partitions) { + ret = 0; + goto out_bh; + } + + ret = udf_fill_partdesc_info(sb, p, i); + if (ret < 0) + goto out_bh; + + if (map->s_partition_type == UDF_METADATA_MAP25) { + ret = udf_load_metadata_files(sb, i); + if (ret < 0) { + udf_err(sb, "error loading MetaData partition map %d\n", + i); + goto out_bh; + } + } else { + /* + * If we have a partition with virtual map, we don't handle + * writing to it (we overwrite blocks instead of relocating + * them). + */ + if (!(sb->s_flags & MS_RDONLY)) { + ret = -EACCES; + goto out_bh; } + ret = udf_load_vat(sb, i, type1_idx); + if (ret < 0) + goto out_bh; + } + ret = 0; +out_bh: + /* In case loading failed, we handle cleanup in udf_fill_super */ + brelse(bh); + return ret; +} + +static int udf_load_sparable_map(struct super_block *sb, + struct udf_part_map *map, + struct sparablePartitionMap *spm) +{ + uint32_t loc; + uint16_t ident; + struct sparingTable *st; + struct udf_sparing_data *sdata = &map->s_type_specific.s_sparing; + int i; + struct buffer_head *bh; + + map->s_partition_type = UDF_SPARABLE_MAP15; + sdata->s_packet_len = le16_to_cpu(spm->packetLength); + if (!is_power_of_2(sdata->s_packet_len)) { + udf_err(sb, "error loading logical volume descriptor: " + "Invalid packet length %u\n", + (unsigned)sdata->s_packet_len); + return -EIO; } - if (i == UDF_SB_NUMPARTS(sb)) - { - udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber)); + if (spm->numSparingTables > 4) { + udf_err(sb, "error loading logical volume descriptor: " + "Too many sparing tables (%d)\n", + (int)spm->numSparingTables); + return -EIO; } - 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)); + + for (i = 0; i < spm->numSparingTables; i++) { + loc = le32_to_cpu(spm->locSparingTable[i]); + bh = udf_read_tagged(sb, loc, loc, &ident); + if (!bh) + continue; + + st = (struct sparingTable *)bh->b_data; + if (ident != 0 || + strncmp(st->sparingIdent.ident, UDF_ID_SPARING, + strlen(UDF_ID_SPARING)) || + sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > + sb->s_blocksize) { + brelse(bh); + continue; + } + + sdata->s_spar_map[i] = bh; } + map->s_partition_func = udf_get_pblock_spar15; + return 0; } -static int -udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, kernel_lb_addr *fileset) +static int udf_load_logicalvol(struct super_block *sb, sector_t block, + struct kernel_lb_addr *fileset) { struct logicalVolDesc *lvd; - int i, j, offset; + int i, offset; uint8_t type; + struct udf_sb_info *sbi = UDF_SB(sb); + struct genericPartitionMap *gpm; + uint16_t ident; + struct buffer_head *bh; + unsigned int table_len; + int ret; + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + return -EAGAIN; + BUG_ON(ident != TAG_IDENT_LVD); lvd = (struct logicalVolDesc *)bh->b_data; + table_len = le32_to_cpu(lvd->mapTableLength); + if (table_len > sb->s_blocksize - sizeof(*lvd)) { + udf_err(sb, "error loading logical volume descriptor: " + "Partition table too long (%u > %lu)\n", table_len, + sb->s_blocksize - sizeof(*lvd)); + ret = -EIO; + goto out_bh; + } - UDF_SB_ALLOC_PARTMAPS(sb, le32_to_cpu(lvd->numPartitionMaps)); - - for (i=0,offset=0; - i<UDF_SB_NUMPARTS(sb) && offset<le32_to_cpu(lvd->mapTableLength); - i++,offset+=((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength) - { - type = ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType; - if (type == 1) - { - struct genericPartitionMap1 *gpm1 = (struct genericPartitionMap1 *)&(lvd->partitionMaps[offset]); - UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15; - UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum); - UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum); - UDF_SB_PARTFUNC(sb,i) = NULL; - } - else if (type == 2) - { - struct udfPartitionMap2 *upm2 = (struct udfPartitionMap2 *)&(lvd->partitionMaps[offset]); - if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) - { - if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) - { - UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; - UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15; - } - else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) - { - UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; - UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20; + ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); + if (ret) + goto out_bh; + + for (i = 0, offset = 0; + i < sbi->s_partitions && offset < table_len; + i++, offset += gpm->partitionMapLength) { + struct udf_part_map *map = &sbi->s_partmaps[i]; + gpm = (struct genericPartitionMap *) + &(lvd->partitionMaps[offset]); + type = gpm->partitionMapType; + if (type == 1) { + struct genericPartitionMap1 *gpm1 = + (struct genericPartitionMap1 *)gpm; + map->s_partition_type = UDF_TYPE1_MAP15; + map->s_volumeseqnum = le16_to_cpu(gpm1->volSeqNum); + map->s_partition_num = le16_to_cpu(gpm1->partitionNum); + map->s_partition_func = NULL; + } else if (type == 2) { + struct udfPartitionMap2 *upm2 = + (struct udfPartitionMap2 *)gpm; + if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, + strlen(UDF_ID_VIRTUAL))) { + u16 suf = + le16_to_cpu(((__le16 *)upm2->partIdent. + identSuffix)[0]); + if (suf < 0x0200) { + map->s_partition_type = + UDF_VIRTUAL_MAP15; + map->s_partition_func = + udf_get_pblock_virt15; + } else { + map->s_partition_type = + UDF_VIRTUAL_MAP20; + map->s_partition_func = + udf_get_pblock_virt20; } - } - else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) - { - uint32_t loc; - uint16_t ident; - struct sparingTable *st; - struct sparablePartitionMap *spm = (struct sparablePartitionMap *)&(lvd->partitionMaps[offset]); - - UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; - UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength); - for (j=0; j<spm->numSparingTables; j++) - { - loc = le32_to_cpu(spm->locSparingTable[j]); - UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = - udf_read_tagged(sb, loc, loc, &ident); - if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) - { - st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data; - if (ident != 0 || - strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) - { - udf_release_data(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]); - UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL; - } - } - } - UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15; - } - else - { - udf_debug("Unknown ident: %s\n", upm2->partIdent.ident); + } else if (!strncmp(upm2->partIdent.ident, + UDF_ID_SPARABLE, + strlen(UDF_ID_SPARABLE))) { + ret = udf_load_sparable_map(sb, map, + (struct sparablePartitionMap *)gpm); + if (ret < 0) + goto out_bh; + } else if (!strncmp(upm2->partIdent.ident, + UDF_ID_METADATA, + strlen(UDF_ID_METADATA))) { + struct udf_meta_data *mdata = + &map->s_type_specific.s_metadata; + struct metadataPartitionMap *mdm = + (struct metadataPartitionMap *) + &(lvd->partitionMaps[offset]); + udf_debug("Parsing Logical vol part %d type %d id=%s\n", + i, type, UDF_ID_METADATA); + + map->s_partition_type = UDF_METADATA_MAP25; + map->s_partition_func = udf_get_pblock_meta25; + + mdata->s_meta_file_loc = + le32_to_cpu(mdm->metadataFileLoc); + mdata->s_mirror_file_loc = + le32_to_cpu(mdm->metadataMirrorFileLoc); + mdata->s_bitmap_file_loc = + le32_to_cpu(mdm->metadataBitmapFileLoc); + mdata->s_alloc_unit_size = + le32_to_cpu(mdm->allocUnitSize); + mdata->s_align_unit_size = + le16_to_cpu(mdm->alignUnitSize); + if (mdm->flags & 0x01) + mdata->s_flags |= MF_DUPLICATE_MD; + + udf_debug("Metadata Ident suffix=0x%x\n", + le16_to_cpu(*(__le16 *) + mdm->partIdent.identSuffix)); + udf_debug("Metadata part num=%d\n", + le16_to_cpu(mdm->partitionNum)); + udf_debug("Metadata part alloc unit size=%d\n", + le32_to_cpu(mdm->allocUnitSize)); + udf_debug("Metadata file loc=%d\n", + le32_to_cpu(mdm->metadataFileLoc)); + udf_debug("Mirror file loc=%d\n", + le32_to_cpu(mdm->metadataMirrorFileLoc)); + udf_debug("Bitmap file loc=%d\n", + le32_to_cpu(mdm->metadataBitmapFileLoc)); + udf_debug("Flags: %d %d\n", + mdata->s_flags, mdm->flags); + } 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); + map->s_volumeseqnum = le16_to_cpu(upm2->volSeqNum); + map->s_partition_num = 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)); + i, map->s_partition_num, type, map->s_volumeseqnum); } - if (fileset) - { - long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]); + if (fileset) { + struct long_ad *la = (struct long_ad *)&(lvd->logicalVolContentsUse[0]); *fileset = lelb_to_cpu(la->extLocation); udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n", - fileset->logicalBlockNum, - fileset->partitionReferenceNum); + fileset->logicalBlockNum, + fileset->partitionReferenceNum); } if (lvd->integritySeqExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt)); - return 0; + ret = 0; +out_bh: + brelse(bh); + return ret; } /* * udf_load_logicalvolint * */ -static void -udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) +static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ad loc) { struct buffer_head *bh = NULL; uint16_t ident; + struct udf_sb_info *sbi = UDF_SB(sb); + struct logicalVolIntegrityDesc *lvid; while (loc.extLength > 0 && - (bh = udf_read_tagged(sb, loc.extLocation, - loc.extLocation, &ident)) && - ident == TAG_IDENT_LVID) - { - UDF_SB_LVIDBH(sb) = bh; - - if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength) - udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt)); - - if (UDF_SB_LVIDBH(sb) != bh) - udf_release_data(bh); + (bh = udf_read_tagged(sb, loc.extLocation, + loc.extLocation, &ident)) && + ident == TAG_IDENT_LVID) { + sbi->s_lvid_bh = bh; + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; + + if (lvid->nextIntegrityExt.extLength) + udf_load_logicalvolint(sb, + leea_to_cpu(lvid->nextIntegrityExt)); + + if (sbi->s_lvid_bh != bh) + brelse(bh); loc.extLength -= sb->s_blocksize; - loc.extLocation ++; + loc.extLocation++; } - if (UDF_SB_LVIDBH(sb) != bh) - udf_release_data(bh); + if (sbi->s_lvid_bh != bh) + brelse(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. + * Process a main/reserve volume descriptor sequence. + * @block First block of first extent of the sequence. + * @lastblock Lastblock of first extent of the sequence. + * @fileset There we store extent containing root fileset * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. + * Returns <0 on error, 0 on success. -EAGAIN is special - try next descriptor + * sequence */ -static int -udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_lb_addr *fileset) +static noinline int udf_process_sequence( + struct super_block *sb, + sector_t block, sector_t lastblock, + struct kernel_lb_addr *fileset) { struct buffer_head *bh = NULL; struct udf_vds_record vds[VDS_POS_LENGTH]; + struct udf_vds_record *curr; struct generic_desc *gd; struct volDescPtr *vdp; - int done=0; - int i,j; + int done = 0; uint32_t vdsn; uint16_t ident; long next_s = 0, next_e = 0; + int ret; memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); - /* Read the main descriptor sequence */ - for (;(!done && block <= lastblock); block++) - { + /* + * Read the main descriptor sequence and find which descriptors + * are in it. + */ + for (; (!done && block <= lastblock); block++) { bh = udf_read_tagged(sb, block, block, &ident); - if (!bh) - break; + if (!bh) { + udf_err(sb, + "Block %llu of volume descriptor sequence is corrupted or we could not read it\n", + (unsigned long long)block); + return -EAGAIN; + } /* Process each descriptor (ISO 13346 3/8.3-8.4) */ gd = (struct generic_desc *)bh->b_data; vdsn = le32_to_cpu(gd->volDescSeqNum); - switch (ident) - { - case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */ - if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum) - { - vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_PRIMARY_VOL_DESC].block = block; - } - break; - case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ - if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum) - { - vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn; - vds[VDS_POS_VOL_DESC_PTR].block = block; - - vdp = (struct volDescPtr *)bh->b_data; - next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation); - next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength); - next_e = next_e >> sb->s_blocksize_bits; - next_e += next_s; - } - break; - case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ - if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum) - { - vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_IMP_USE_VOL_DESC].block = block; - } - break; - case TAG_IDENT_PD: /* ISO 13346 3/10.5 */ - if (!vds[VDS_POS_PARTITION_DESC].block) - vds[VDS_POS_PARTITION_DESC].block = block; - break; - case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */ - if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum) - { - vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_LOGICAL_VOL_DESC].block = block; - } - break; - case TAG_IDENT_USD: /* ISO 13346 3/10.8 */ - if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum) - { - vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn; - vds[VDS_POS_UNALLOC_SPACE_DESC].block = block; - } - break; - case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ - vds[VDS_POS_TERMINATING_DESC].block = block; - if (next_e) - { - block = next_s; - lastblock = next_e; - next_s = next_e = 0; - } - else - done = 1; - break; - } - udf_release_data(bh); - } - for (i=0; i<VDS_POS_LENGTH; i++) - { - if (vds[i].block) - { - bh = udf_read_tagged(sb, vds[i].block, vds[i].block, &ident); - - if (i == VDS_POS_PRIMARY_VOL_DESC) - udf_load_pvoldesc(sb, bh); - else if (i == VDS_POS_LOGICAL_VOL_DESC) - udf_load_logicalvol(sb, bh, fileset); - else if (i == VDS_POS_PARTITION_DESC) - { - struct buffer_head *bh2 = NULL; - udf_load_partdesc(sb, bh); - for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++) - { - bh2 = udf_read_tagged(sb, j, j, &ident); - gd = (struct generic_desc *)bh2->b_data; - if (ident == TAG_IDENT_PD) - udf_load_partdesc(sb, bh2); - udf_release_data(bh2); - } + switch (ident) { + case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */ + curr = &vds[VDS_POS_PRIMARY_VOL_DESC]; + if (vdsn >= curr->volDescSeqNum) { + curr->volDescSeqNum = vdsn; + curr->block = block; + } + break; + case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */ + curr = &vds[VDS_POS_VOL_DESC_PTR]; + if (vdsn >= curr->volDescSeqNum) { + curr->volDescSeqNum = vdsn; + curr->block = block; + + vdp = (struct volDescPtr *)bh->b_data; + next_s = le32_to_cpu( + vdp->nextVolDescSeqExt.extLocation); + next_e = le32_to_cpu( + vdp->nextVolDescSeqExt.extLength); + next_e = next_e >> sb->s_blocksize_bits; + next_e += next_s; + } + break; + case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */ + curr = &vds[VDS_POS_IMP_USE_VOL_DESC]; + if (vdsn >= curr->volDescSeqNum) { + curr->volDescSeqNum = vdsn; + curr->block = block; + } + break; + case TAG_IDENT_PD: /* ISO 13346 3/10.5 */ + curr = &vds[VDS_POS_PARTITION_DESC]; + if (!curr->block) + curr->block = block; + break; + case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */ + curr = &vds[VDS_POS_LOGICAL_VOL_DESC]; + if (vdsn >= curr->volDescSeqNum) { + curr->volDescSeqNum = vdsn; + curr->block = block; + } + break; + case TAG_IDENT_USD: /* ISO 13346 3/10.8 */ + curr = &vds[VDS_POS_UNALLOC_SPACE_DESC]; + if (vdsn >= curr->volDescSeqNum) { + curr->volDescSeqNum = vdsn; + curr->block = block; } - udf_release_data(bh); + break; + case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ + vds[VDS_POS_TERMINATING_DESC].block = block; + if (next_e) { + block = next_s; + lastblock = next_e; + next_s = next_e = 0; + } else + done = 1; + break; + } + brelse(bh); + } + /* + * Now read interesting descriptors again and process them + * in a suitable order + */ + if (!vds[VDS_POS_PRIMARY_VOL_DESC].block) { + udf_err(sb, "Primary Volume Descriptor not found!\n"); + return -EAGAIN; + } + ret = udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block); + if (ret < 0) + return ret; + + if (vds[VDS_POS_LOGICAL_VOL_DESC].block) { + ret = udf_load_logicalvol(sb, + vds[VDS_POS_LOGICAL_VOL_DESC].block, + fileset); + if (ret < 0) + return ret; + } + + if (vds[VDS_POS_PARTITION_DESC].block) { + /* + * We rescan the whole descriptor sequence to find + * partition descriptor blocks and process them. + */ + for (block = vds[VDS_POS_PARTITION_DESC].block; + block < vds[VDS_POS_TERMINATING_DESC].block; + block++) { + ret = udf_load_partdesc(sb, block); + if (ret < 0) + return ret; } } @@ -1265,260 +1710,374 @@ udf_process_sequence(struct super_block *sb, long block, long lastblock, kernel_ } /* - * udf_check_valid() + * Load Volume Descriptor Sequence described by anchor in bh + * + * Returns <0 on error, 0 on success */ -static int -udf_check_valid(struct super_block *sb, int novrs, int silent) +static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh, + struct kernel_lb_addr *fileset) { - long block; - - if (novrs) - { - udf_debug("Validity check skipped because of novrs option\n"); - return 0; - } - /* Check that it is NSR02 compliant */ - /* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */ - else if ((block = udf_vrs(sb, silent)) == -1) - { - udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n"); - if (!UDF_SB_LASTBLOCK(sb)) - UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); - return 0; + struct anchorVolDescPtr *anchor; + sector_t main_s, main_e, reserve_s, reserve_e; + int ret; + + anchor = (struct anchorVolDescPtr *)bh->b_data; + + /* Locate the main sequence */ + main_s = le32_to_cpu(anchor->mainVolDescSeqExt.extLocation); + main_e = le32_to_cpu(anchor->mainVolDescSeqExt.extLength); + main_e = main_e >> sb->s_blocksize_bits; + main_e += main_s; + + /* 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; + + /* Process the main & reserve sequences */ + /* responsible for finding the PartitionDesc(s) */ + ret = udf_process_sequence(sb, main_s, main_e, fileset); + if (ret != -EAGAIN) + return ret; + udf_sb_free_partitions(sb); + ret = udf_process_sequence(sb, reserve_s, reserve_e, fileset); + if (ret < 0) { + udf_sb_free_partitions(sb); + /* No sequence was OK, return -EIO */ + if (ret == -EAGAIN) + ret = -EIO; } - else - return !block; + return ret; } -static int -udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) +/* + * Check whether there is an anchor block in the given block and + * load Volume Descriptor Sequence if so. + * + * Returns <0 on error, 0 on success, -EAGAIN is special - try next anchor + * block + */ +static int udf_check_anchor_block(struct super_block *sb, sector_t block, + struct kernel_lb_addr *fileset) { - struct anchorVolDescPtr *anchor; - uint16_t ident; struct buffer_head *bh; - long main_s, main_e, reserve_s, reserve_e; - int i, j; + uint16_t ident; + int ret; - if (!sb) - return 1; + if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV) && + udf_fixed_to_variable(block) >= + sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits) + return -EAGAIN; - for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++) - { - if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb, - UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident))) - { - anchor = (struct anchorVolDescPtr *)bh->b_data; - - /* Locate the main sequence */ - main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation ); - main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength ); - main_e = main_e >> sb->s_blocksize_bits; - main_e += main_s; - - /* Locate the reserve sequence */ - reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation); - reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength); - reserve_e = reserve_e >> sb->s_blocksize_bits; - reserve_e += reserve_s; - - udf_release_data(bh); - - /* Process the main & reserve sequences */ - /* responsible for finding the PartitionDesc(s) */ - if (!(udf_process_sequence(sb, main_s, main_e, fileset) && - udf_process_sequence(sb, reserve_s, reserve_e, fileset))) - { - break; - } - } + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + return -EAGAIN; + if (ident != TAG_IDENT_AVDP) { + brelse(bh); + return -EAGAIN; } + ret = udf_load_sequence(sb, bh, fileset); + brelse(bh); + return ret; +} - if (i == sizeof(UDF_SB_ANCHOR(sb))/sizeof(int)) - { - udf_debug("No Anchor block found\n"); - return 1; +/* + * Search for an anchor volume descriptor pointer. + * + * Returns < 0 on error, 0 on success. -EAGAIN is special - try next set + * of anchors. + */ +static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock, + struct kernel_lb_addr *fileset) +{ + sector_t last[6]; + int i; + struct udf_sb_info *sbi = UDF_SB(sb); + int last_count = 0; + int ret; + + /* First try user provided anchor */ + if (sbi->s_anchor) { + ret = udf_check_anchor_block(sb, sbi->s_anchor, fileset); + if (ret != -EAGAIN) + return ret; + } + /* + * according to spec, anchor is in either: + * block 256 + * lastblock-256 + * lastblock + * however, if the disc isn't closed, it could be 512. + */ + ret = udf_check_anchor_block(sb, sbi->s_session + 256, fileset); + if (ret != -EAGAIN) + return ret; + /* + * The trouble is which block is the last one. Drives often misreport + * this so we try various possibilities. + */ + last[last_count++] = *lastblock; + if (*lastblock >= 1) + last[last_count++] = *lastblock - 1; + last[last_count++] = *lastblock + 1; + if (*lastblock >= 2) + last[last_count++] = *lastblock - 2; + if (*lastblock >= 150) + last[last_count++] = *lastblock - 150; + if (*lastblock >= 152) + last[last_count++] = *lastblock - 152; + + for (i = 0; i < last_count; i++) { + if (last[i] >= sb->s_bdev->bd_inode->i_size >> + sb->s_blocksize_bits) + continue; + ret = udf_check_anchor_block(sb, last[i], fileset); + if (ret != -EAGAIN) { + if (!ret) + *lastblock = last[i]; + return ret; + } + if (last[i] < 256) + continue; + ret = udf_check_anchor_block(sb, last[i] - 256, fileset); + if (ret != -EAGAIN) { + if (!ret) + *lastblock = last[i]; + return ret; + } } - else - udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]); - - for (i=0; i<UDF_SB_NUMPARTS(sb); i++) - { - switch UDF_SB_PARTTYPE(sb, i) - { - case UDF_VIRTUAL_MAP15: - case UDF_VIRTUAL_MAP20: - { - kernel_lb_addr ino; - - if (!UDF_SB_LASTBLOCK(sb)) - { - UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); - udf_find_anchor(sb); - } - if (!UDF_SB_LASTBLOCK(sb)) - { - udf_debug("Unable to determine Lastblock (For Virtual Partition)\n"); - return 1; - } + /* Finally try block 512 in case media is open */ + return udf_check_anchor_block(sb, sbi->s_session + 512, fileset); +} - for (j=0; j<UDF_SB_NUMPARTS(sb); j++) - { - if (j != i && - UDF_SB_PARTVSN(sb,i) == UDF_SB_PARTVSN(sb,j) && - UDF_SB_PARTNUM(sb,i) == UDF_SB_PARTNUM(sb,j)) - { - ino.partitionReferenceNum = j; - ino.logicalBlockNum = UDF_SB_LASTBLOCK(sb) - - UDF_SB_PARTROOT(sb,j); - break; - } - } +/* + * Find an anchor volume descriptor and load Volume Descriptor Sequence from + * area specified by it. The function expects sbi->s_lastblock to be the last + * block on the media. + * + * Return <0 on error, 0 if anchor found. -EAGAIN is special meaning anchor + * was not found. + */ +static int udf_find_anchor(struct super_block *sb, + struct kernel_lb_addr *fileset) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + sector_t lastblock = sbi->s_last_block; + int ret; - if (j == UDF_SB_NUMPARTS(sb)) - return 1; + ret = udf_scan_anchors(sb, &lastblock, fileset); + if (ret != -EAGAIN) + goto out; - if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino))) - return 1; + /* No anchor found? Try VARCONV conversion of block numbers */ + UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); + lastblock = udf_variable_to_fixed(sbi->s_last_block); + /* Firstly, we try to not convert number of the last block */ + ret = udf_scan_anchors(sb, &lastblock, fileset); + if (ret != -EAGAIN) + goto out; - if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP15) - { - UDF_SB_TYPEVIRT(sb,i).s_start_offset = udf_ext0_offset(UDF_SB_VAT(sb)); - UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - 36) >> 2; - } - else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20) - { - struct buffer_head *bh = NULL; - uint32_t pos; - - pos = udf_block_map(UDF_SB_VAT(sb), 0); - bh = sb_bread(sb, pos); - UDF_SB_TYPEVIRT(sb,i).s_start_offset = - le16_to_cpu(((struct virtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) + - udf_ext0_offset(UDF_SB_VAT(sb)); - UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - - UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2; - udf_release_data(bh); - } - UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0); - UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum); - } + lastblock = sbi->s_last_block; + /* Secondly, we try with converted number of the last block */ + ret = udf_scan_anchors(sb, &lastblock, fileset); + if (ret < 0) { + /* VARCONV didn't help. Clear it. */ + UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV); + } +out: + if (ret == 0) + sbi->s_last_block = lastblock; + return ret; +} + +/* + * Check Volume Structure Descriptor, find Anchor block and load Volume + * Descriptor Sequence. + * + * Returns < 0 on error, 0 on success. -EAGAIN is special meaning anchor + * block was not found. + */ +static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, + int silent, struct kernel_lb_addr *fileset) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + loff_t nsr_off; + int ret; + + if (!sb_set_blocksize(sb, uopt->blocksize)) { + if (!silent) + udf_warn(sb, "Bad block size\n"); + return -EINVAL; + } + sbi->s_last_block = uopt->lastblock; + if (!uopt->novrs) { + /* Check that it is NSR02 compliant */ + nsr_off = udf_check_vsd(sb); + if (!nsr_off) { + if (!silent) + udf_warn(sb, "No VRS found\n"); + return 0; } + if (nsr_off == -1) + udf_debug("Failed to read sector at offset %d. " + "Assuming open disc. Skipping validity " + "check\n", VSD_FIRST_SECTOR_OFFSET); + if (!sbi->s_last_block) + sbi->s_last_block = udf_get_last_block(sb); + } else { + udf_debug("Validity check skipped because of novrs option\n"); + } + + /* Look for anchor block and load Volume Descriptor Sequence */ + sbi->s_anchor = uopt->anchor; + ret = udf_find_anchor(sb, fileset); + if (ret < 0) { + if (!silent && ret == -EAGAIN) + udf_warn(sb, "No anchor found\n"); + return ret; } return 0; } static void udf_open_lvid(struct super_block *sb) { - if (UDF_SB_LVIDBH(sb)) - { - int i; - kernel_timestamp cpu_time; + struct udf_sb_info *sbi = UDF_SB(sb); + struct buffer_head *bh = sbi->s_lvid_bh; + struct logicalVolIntegrityDesc *lvid; + struct logicalVolIntegrityDescImpUse *lvidiu; - UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; - UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) - UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); - UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN; - - UDF_SB_LVID(sb)->descTag.descCRC = - cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), - le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); + if (!bh) + return; + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; + lvidiu = udf_sb_lvidiu(sb); + if (!lvidiu) + return; + + mutex_lock(&sbi->s_alloc_mutex); + lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; + lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; + udf_time_to_disk_stamp(&lvid->recordingDateAndTime, + CURRENT_TIME); + lvid->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_OPEN); + + lvid->descTag.descCRC = cpu_to_le16( + crc_itu_t(0, (char *)lvid + sizeof(struct tag), + le16_to_cpu(lvid->descTag.descCRCLength))); + + lvid->descTag.tagChecksum = udf_tag_checksum(&lvid->descTag); + mark_buffer_dirty(bh); + sbi->s_lvid_dirty = 0; + mutex_unlock(&sbi->s_alloc_mutex); + /* Make opening of filesystem visible on the media immediately */ + sync_dirty_buffer(bh); +} - UDF_SB_LVID(sb)->descTag.tagChecksum = 0; - for (i=0; i<16; i++) - if (i != 4) - UDF_SB_LVID(sb)->descTag.tagChecksum += - ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; +static void udf_close_lvid(struct super_block *sb) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + struct buffer_head *bh = sbi->s_lvid_bh; + struct logicalVolIntegrityDesc *lvid; + struct logicalVolIntegrityDescImpUse *lvidiu; - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - } + if (!bh) + return; + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; + lvidiu = udf_sb_lvidiu(sb); + if (!lvidiu) + return; + + mutex_lock(&sbi->s_alloc_mutex); + lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; + lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; + udf_time_to_disk_stamp(&lvid->recordingDateAndTime, CURRENT_TIME); + if (UDF_MAX_WRITE_VERSION > le16_to_cpu(lvidiu->maxUDFWriteRev)) + lvidiu->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION); + if (sbi->s_udfrev > le16_to_cpu(lvidiu->minUDFReadRev)) + lvidiu->minUDFReadRev = cpu_to_le16(sbi->s_udfrev); + if (sbi->s_udfrev > le16_to_cpu(lvidiu->minUDFWriteRev)) + lvidiu->minUDFWriteRev = cpu_to_le16(sbi->s_udfrev); + lvid->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE); + + lvid->descTag.descCRC = cpu_to_le16( + crc_itu_t(0, (char *)lvid + sizeof(struct tag), + le16_to_cpu(lvid->descTag.descCRCLength))); + + lvid->descTag.tagChecksum = udf_tag_checksum(&lvid->descTag); + /* + * We set buffer uptodate unconditionally here to avoid spurious + * warnings from mark_buffer_dirty() when previous EIO has marked + * the buffer as !uptodate + */ + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + sbi->s_lvid_dirty = 0; + mutex_unlock(&sbi->s_alloc_mutex); + /* Make closing of filesystem visible on the media immediately */ + sync_dirty_buffer(bh); } -static void udf_close_lvid(struct super_block *sb) +u64 lvid_get_unique_id(struct super_block *sb) { - if (UDF_SB_LVIDBH(sb) && - UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN) - { - int i; - kernel_timestamp cpu_time; - - UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; - UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) - UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time); - if (UDF_MAX_WRITE_VERSION > le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev)) - UDF_SB_LVIDIU(sb)->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION); - if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev)) - UDF_SB_LVIDIU(sb)->minUDFReadRev = cpu_to_le16(UDF_SB_UDFREV(sb)); - if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev)) - UDF_SB_LVIDIU(sb)->minUDFWriteRev = cpu_to_le16(UDF_SB_UDFREV(sb)); - UDF_SB_LVID(sb)->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE); - - UDF_SB_LVID(sb)->descTag.descCRC = - cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), - le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0)); - - UDF_SB_LVID(sb)->descTag.tagChecksum = 0; - for (i=0; i<16; i++) - if (i != 4) - UDF_SB_LVID(sb)->descTag.tagChecksum += - ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i]; - - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - } + struct buffer_head *bh; + struct udf_sb_info *sbi = UDF_SB(sb); + struct logicalVolIntegrityDesc *lvid; + struct logicalVolHeaderDesc *lvhd; + u64 uniqueID; + u64 ret; + + bh = sbi->s_lvid_bh; + if (!bh) + return 0; + + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; + lvhd = (struct logicalVolHeaderDesc *)lvid->logicalVolContentsUse; + + mutex_lock(&sbi->s_alloc_mutex); + ret = uniqueID = le64_to_cpu(lvhd->uniqueID); + if (!(++uniqueID & 0xFFFFFFFF)) + uniqueID += 16; + lvhd->uniqueID = cpu_to_le64(uniqueID); + mutex_unlock(&sbi->s_alloc_mutex); + mark_buffer_dirty(bh); + + return ret; } -/* - * udf_read_super - * - * PURPOSE - * Complete the specified super block. - * - * PRE-CONDITIONS - * sb Pointer to superblock to complete - never NULL. - * sb->s_dev Device to read suberblock from. - * options Pointer to mount options. - * silent Silent flag. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ static int udf_fill_super(struct super_block *sb, void *options, int silent) { - int i; - struct inode *inode=NULL; + int ret = -EINVAL; + struct inode *inode = NULL; struct udf_options uopt; - kernel_lb_addr rootdir, fileset; + struct kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); - uopt.uid = -1; - uopt.gid = -1; + uopt.uid = INVALID_UID; + uopt.gid = INVALID_GID; uopt.umask = 0; + uopt.fmode = UDF_INVALID_MODE; + uopt.dmode = UDF_INVALID_MODE; - sbi = kmalloc(sizeof(struct udf_sb_info), GFP_KERNEL); + sbi = kzalloc(sizeof(struct udf_sb_info), GFP_KERNEL); if (!sbi) return -ENOMEM; + sb->s_fs_info = sbi; - memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info)); - init_MUTEX(&sbi->s_alloc_sem); + mutex_init(&sbi->s_alloc_mutex); - if (!udf_parse_options((char *)options, &uopt)) + if (!udf_parse_options((char *)options, &uopt, false)) goto error_out; if (uopt.flags & (1 << UDF_FLAG_UTF8) && - uopt.flags & (1 << UDF_FLAG_NLS_MAP)) - { - udf_error(sb, "udf_read_super", - "utf8 cannot be combined with iocharset\n"); + uopt.flags & (1 << UDF_FLAG_NLS_MAP)) { + udf_err(sb, "utf8 cannot be combined with iocharset\n"); goto error_out; } #ifdef CONFIG_UDF_NLS - if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) - { + if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map) { uopt.nls_map = load_nls_default(); if (!uopt.nls_map) uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP); @@ -1532,70 +2091,79 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) fileset.logicalBlockNum = 0xFFFFFFFF; fileset.partitionReferenceNum = 0xFFFF; - UDF_SB(sb)->s_flags = uopt.flags; - UDF_SB(sb)->s_uid = uopt.uid; - UDF_SB(sb)->s_gid = uopt.gid; - UDF_SB(sb)->s_umask = uopt.umask; - UDF_SB(sb)->s_nls_map = uopt.nls_map; - - /* Set the block size for all transfers */ - if (!udf_set_blocksize(sb, uopt.blocksize)) - goto error_out; - - if ( uopt.session == 0xFFFFFFFF ) - UDF_SB_SESSION(sb) = udf_get_last_session(sb); + sbi->s_flags = uopt.flags; + sbi->s_uid = uopt.uid; + sbi->s_gid = uopt.gid; + sbi->s_umask = uopt.umask; + sbi->s_fmode = uopt.fmode; + sbi->s_dmode = uopt.dmode; + sbi->s_nls_map = uopt.nls_map; + rwlock_init(&sbi->s_cred_lock); + + if (uopt.session == 0xFFFFFFFF) + sbi->s_session = udf_get_last_session(sb); else - UDF_SB_SESSION(sb) = uopt.session; - - udf_debug("Multi-session=%d\n", UDF_SB_SESSION(sb)); - - UDF_SB_LASTBLOCK(sb) = uopt.lastblock; - UDF_SB_ANCHOR(sb)[0] = UDF_SB_ANCHOR(sb)[1] = 0; - UDF_SB_ANCHOR(sb)[2] = uopt.anchor; - UDF_SB_ANCHOR(sb)[3] = 256; - - if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */ - { - printk("UDF-fs: No VRS found\n"); - goto error_out; - } + sbi->s_session = uopt.session; - udf_find_anchor(sb); + udf_debug("Multi-session=%d\n", sbi->s_session); /* Fill in the rest of the superblock */ sb->s_op = &udf_sb_ops; - sb->dq_op = NULL; - sb->s_dirt = 0; + sb->s_export_op = &udf_export_ops; + sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; - if (udf_load_partition(sb, &fileset)) - { - printk("UDF-fs: No partition found (1)\n"); + if (uopt.flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) { + ret = udf_load_vrs(sb, &uopt, silent, &fileset); + } else { + uopt.blocksize = bdev_logical_block_size(sb->s_bdev); + ret = udf_load_vrs(sb, &uopt, silent, &fileset); + if (ret == -EAGAIN && uopt.blocksize != UDF_DEFAULT_BLOCKSIZE) { + if (!silent) + pr_notice("Rescanning with blocksize %d\n", + UDF_DEFAULT_BLOCKSIZE); + brelse(sbi->s_lvid_bh); + sbi->s_lvid_bh = NULL; + uopt.blocksize = UDF_DEFAULT_BLOCKSIZE; + ret = udf_load_vrs(sb, &uopt, silent, &fileset); + } + } + if (ret < 0) { + if (ret == -EAGAIN) { + udf_warn(sb, "No partition found (1)\n"); + ret = -EINVAL; + } goto error_out; } - udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb)); + udf_debug("Lastblock=%d\n", sbi->s_last_block); - if ( UDF_SB_LVIDBH(sb) ) - { - uint16_t minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev); - uint16_t minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev); - /* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */ + if (sbi->s_lvid_bh) { + struct logicalVolIntegrityDescImpUse *lvidiu = + udf_sb_lvidiu(sb); + uint16_t minUDFReadRev; + uint16_t minUDFWriteRev; - if (minUDFReadRev > UDF_MAX_READ_VERSION) - { - printk("UDF-fs: minUDFReadRev=%x (max is %x)\n", - le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev), - UDF_MAX_READ_VERSION); + if (!lvidiu) { + ret = -EINVAL; goto error_out; } - else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) - { - sb->s_flags |= MS_RDONLY; + minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev); + minUDFWriteRev = le16_to_cpu(lvidiu->minUDFWriteRev); + if (minUDFReadRev > UDF_MAX_READ_VERSION) { + udf_err(sb, "minUDFReadRev=%x (max is %x)\n", + minUDFReadRev, + UDF_MAX_READ_VERSION); + ret = -EINVAL; + goto error_out; + } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION && + !(sb->s_flags & MS_RDONLY)) { + ret = -EACCES; + goto error_out; } - UDF_SB_UDFREV(sb) = minUDFWriteRev; + sbi->s_udfrev = minUDFWriteRev; if (minUDFReadRev >= UDF_VERS_USE_EXTENDED_FE) UDF_SET_FLAG(sb, UDF_FLAG_USE_EXTENDED_FE); @@ -1603,26 +2171,32 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) UDF_SET_FLAG(sb, UDF_FLAG_USE_STREAMS); } - if ( !UDF_SB_NUMPARTS(sb) ) - { - printk("UDF-fs: No partition found (2)\n"); + if (!sbi->s_partitions) { + udf_warn(sb, "No partition found (2)\n"); + ret = -EINVAL; + goto error_out; + } + + if (sbi->s_partmaps[sbi->s_partition].s_partition_flags & + UDF_PART_FLAG_READ_ONLY && + !(sb->s_flags & MS_RDONLY)) { + ret = -EACCES; goto error_out; } - if ( udf_find_fileset(sb, &fileset, &rootdir) ) - { - printk("UDF-fs: No fileset found\n"); + if (udf_find_fileset(sb, &fileset, &rootdir)) { + udf_warn(sb, "No fileset found\n"); + ret = -EINVAL; goto error_out; } - if (!silent) - { - kernel_timestamp ts; - udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb)); - udf_info("UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", - UDFFS_VERSION, UDFFS_DATE, - UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute, - ts.typeAndTimezone); + if (!silent) { + struct timestamp ts; + udf_time_to_disk_stamp(&ts, sbi->s_record_time); + udf_info("Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n", + sbi->s_volume_ident, + le16_to_cpu(ts.year), ts.month, ts.day, + ts.hour, ts.minute, le16_to_cpu(ts.typeAndTimezone)); } if (!(sb->s_flags & MS_RDONLY)) udf_open_lvid(sb); @@ -1630,198 +2204,159 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) /* 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); + inode = udf_iget(sb, &rootdir); + if (!inode) { + udf_err(sb, "Error in udf_iget, block=%d, partition=%d\n", + rootdir.logicalBlockNum, rootdir.partitionReferenceNum); + ret = -EIO; goto error_out; } /* Allocate a dentry for the root inode */ - sb->s_root = d_alloc_root(inode); - if (!sb->s_root) - { - printk("UDF-fs: Couldn't allocate root dentry\n"); - iput(inode); + sb->s_root = d_make_root(inode); + if (!sb->s_root) { + udf_err(sb, "Couldn't allocate root dentry\n"); + ret = -ENOMEM; goto error_out; } sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_max_links = UDF_MAX_LINKS; return 0; error_out: - if (UDF_SB_VAT(sb)) - iput(UDF_SB_VAT(sb)); - if (UDF_SB_NUMPARTS(sb)) - { - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) - iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) - iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); - if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) - { - for (i=0; i<4; i++) - udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); - } - } + if (sbi->s_vat_inode) + iput(sbi->s_vat_inode); #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - unload_nls(UDF_SB(sb)->s_nls_map); + unload_nls(sbi->s_nls_map); #endif if (!(sb->s_flags & MS_RDONLY)) udf_close_lvid(sb); - udf_release_data(UDF_SB_LVIDBH(sb)); - UDF_SB_FREE(sb); + brelse(sbi->s_lvid_bh); + udf_sb_free_partitions(sb); kfree(sbi); sb->s_fs_info = NULL; - return -EINVAL; + + return ret; } -void udf_error(struct super_block *sb, const char *function, - const char *fmt, ...) +void _udf_err(struct super_block *sb, const char *function, + const char *fmt, ...) { + struct va_format vaf; 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); + + vaf.fmt = fmt; + vaf.va = &args; + + pr_err("error (device %s): %s: %pV", sb->s_id, function, &vaf); + va_end(args); - printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n", - sb->s_id, function, error_buf); } -void udf_warning(struct super_block *sb, const char *function, - const char *fmt, ...) +void _udf_warn(struct super_block *sb, const char *function, + const char *fmt, ...) { + struct va_format vaf; va_list args; - va_start (args, fmt); - vsprintf(error_buf, fmt, args); + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + pr_warn("warning (device %s): %s: %pV", sb->s_id, function, &vaf); + va_end(args); - printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n", - sb->s_id, function, error_buf); } -/* - * udf_put_super - * - * PURPOSE - * Prepare for destruction of the superblock. - * - * DESCRIPTION - * Called before the filesystem is unmounted. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static void -udf_put_super(struct super_block *sb) +static void udf_put_super(struct super_block *sb) { - int i; + struct udf_sb_info *sbi; - if (UDF_SB_VAT(sb)) - iput(UDF_SB_VAT(sb)); - if (UDF_SB_NUMPARTS(sb)) - { - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) - iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) - iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace); - if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) - UDF_SB_FREE_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace); - if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15) - { - for (i=0; i<4; i++) - udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]); - } - } + sbi = UDF_SB(sb); + + if (sbi->s_vat_inode) + iput(sbi->s_vat_inode); #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - unload_nls(UDF_SB(sb)->s_nls_map); + unload_nls(sbi->s_nls_map); #endif if (!(sb->s_flags & MS_RDONLY)) udf_close_lvid(sb); - udf_release_data(UDF_SB_LVIDBH(sb)); - UDF_SB_FREE(sb); + brelse(sbi->s_lvid_bh); + udf_sb_free_partitions(sb); kfree(sb->s_fs_info); sb->s_fs_info = NULL; } -/* - * udf_stat_fs - * - * PURPOSE - * Return info about the filesystem. - * - * DESCRIPTION - * Called by sys_statfs() - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static int -udf_statfs(struct super_block *sb, struct kstatfs *buf) +static int udf_sync_fs(struct super_block *sb, int wait) +{ + struct udf_sb_info *sbi = UDF_SB(sb); + + mutex_lock(&sbi->s_alloc_mutex); + if (sbi->s_lvid_dirty) { + /* + * Blockdevice will be synced later so we don't have to submit + * the buffer for IO + */ + mark_buffer_dirty(sbi->s_lvid_bh); + sbi->s_lvid_dirty = 0; + } + mutex_unlock(&sbi->s_alloc_mutex); + + return 0; +} + +static int udf_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; + struct udf_sb_info *sbi = UDF_SB(sb); + struct logicalVolIntegrityDescImpUse *lvidiu; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + + lvidiu = udf_sb_lvidiu(sb); buf->f_type = UDF_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); + buf->f_blocks = sbi->s_partmaps[sbi->s_partition].s_partition_len; buf->f_bfree = udf_count_free(sb); buf->f_bavail = buf->f_bfree; - buf->f_files = (UDF_SB_LVIDBH(sb) ? - (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + - le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree; + buf->f_files = (lvidiu != NULL ? (le32_to_cpu(lvidiu->numFiles) + + le32_to_cpu(lvidiu->numDirs)) : 0) + + buf->f_bfree; buf->f_ffree = buf->f_bfree; - /* __kernel_fsid_t f_fsid */ - buf->f_namelen = UDF_NAME_LEN-2; + buf->f_namelen = UDF_NAME_LEN - 2; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); return 0; } -static unsigned char udf_bitmap_lookup[16] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 -}; - -static unsigned int -udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) +static unsigned int udf_count_free_bitmap(struct super_block *sb, + struct udf_bitmap *bitmap) { struct buffer_head *bh = NULL; unsigned int accum = 0; int index; int block = 0, newblock; - kernel_lb_addr loc; + struct kernel_lb_addr loc; uint32_t bytes; - uint8_t value; uint8_t *ptr; uint16_t ident; struct spaceBitmapDesc *bm; - lock_kernel(); - loc.logicalBlockNum = bitmap->s_extPosition; - loc.partitionReferenceNum = UDF_SB_PARTITION(sb); - bh = udf_read_ptagged(sb, loc, 0, &ident); + loc.partitionReferenceNum = UDF_SB(sb)->s_partition; + bh = udf_read_ptagged(sb, &loc, 0, &ident); - if (!bh) - { - printk(KERN_ERR "udf: udf_count_free failed\n"); + if (!bh) { + udf_err(sb, "udf_count_free failed\n"); goto out; - } - else if (ident != TAG_IDENT_SBD) - { - udf_release_data(bh); - printk(KERN_ERR "udf: udf_count_free failed\n"); + } else if (ident != TAG_IDENT_SBD) { + brelse(bh); + udf_err(sb, "udf_count_free failed\n"); goto out; } @@ -1830,23 +2365,16 @@ udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) index = sizeof(struct spaceBitmapDesc); /* offset in first block only */ ptr = (uint8_t *)bh->b_data; - while ( bytes > 0 ) - { - while ((bytes > 0) && (index < sb->s_blocksize)) - { - value = ptr[index]; - accum += udf_bitmap_lookup[ value & 0x0f ]; - accum += udf_bitmap_lookup[ value >> 4 ]; - index++; - bytes--; - } - if ( bytes ) - { - udf_release_data(bh); - newblock = udf_get_lb_pblock(sb, loc, ++block); + while (bytes > 0) { + u32 cur_bytes = min_t(u32, bytes, sb->s_blocksize - index); + accum += bitmap_weight((const unsigned long *)(ptr + index), + cur_bytes * 8); + bytes -= cur_bytes; + if (bytes) { + brelse(bh); + newblock = udf_get_lb_pblock(sb, &loc, ++block); bh = udf_tread(sb, newblock); - if (!bh) - { + if (!bh) { udf_debug("read failed\n"); goto out; } @@ -1854,50 +2382,48 @@ udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap) ptr = (uint8_t *)bh->b_data; } } - udf_release_data(bh); - + brelse(bh); out: - unlock_kernel(); - return accum; } -static unsigned int -udf_count_free_table(struct super_block *sb, struct inode * table) +static unsigned int udf_count_free_table(struct super_block *sb, + struct inode *table) { unsigned int accum = 0; - uint32_t extoffset, elen; - kernel_lb_addr bloc, eloc; + uint32_t elen; + struct kernel_lb_addr eloc; int8_t etype; - struct buffer_head *bh = NULL; + struct extent_position epos; - lock_kernel(); + mutex_lock(&UDF_SB(sb)->s_alloc_mutex); + epos.block = UDF_I(table)->i_location; + epos.offset = sizeof(struct unallocSpaceEntry); + epos.bh = NULL; - bloc = UDF_I_LOCATION(table); - extoffset = sizeof(struct unallocSpaceEntry); - - while ((etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) - { + while ((etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) accum += (elen >> table->i_sb->s_blocksize_bits); - } - udf_release_data(bh); - unlock_kernel(); + brelse(epos.bh); + mutex_unlock(&UDF_SB(sb)->s_alloc_mutex); return accum; } - -static unsigned int -udf_count_free(struct super_block *sb) + +static unsigned int udf_count_free(struct super_block *sb) { unsigned int accum = 0; - - if (UDF_SB_LVIDBH(sb)) - { - if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb)) - { - accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]); - + struct udf_sb_info *sbi; + struct udf_part_map *map; + + sbi = UDF_SB(sb); + if (sbi->s_lvid_bh) { + struct logicalVolIntegrityDesc *lvid = + (struct logicalVolIntegrityDesc *) + sbi->s_lvid_bh->b_data; + if (le32_to_cpu(lvid->numOfPartitions) > sbi->s_partition) { + accum = le32_to_cpu( + lvid->freeSpaceTable[sbi->s_partition]); if (accum == 0xFFFFFFFF) accum = 0; } @@ -1906,28 +2432,25 @@ udf_count_free(struct super_block *sb) if (accum) return accum; - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP) - { + map = &sbi->s_partmaps[sbi->s_partition]; + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) { accum += udf_count_free_bitmap(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap); + map->s_uspace.s_bitmap); } - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP) - { + if (map->s_partition_flags & UDF_PART_FLAG_FREED_BITMAP) { accum += udf_count_free_bitmap(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap); + map->s_fspace.s_bitmap); } if (accum) return accum; - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE) - { + if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) { accum += udf_count_free_table(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table); + map->s_uspace.s_table); } - if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE) - { + if (map->s_partition_flags & UDF_PART_FLAG_FREED_TABLE) { accum += udf_count_free_table(sb, - UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table); + map->s_fspace.s_table); } return accum; diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 43f3051ef75..d7c6dbe4194 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -4,11 +4,6 @@ * PURPOSE * Symlink handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -16,7 +11,7 @@ * Each contributing author retains all rights to their own work. * * (C) 1998-2001 Ben Fennema - * (C) 1999 Stelias Computing Inc + * (C) 1999 Stelias Computing Inc * * HISTORY * @@ -28,51 +23,53 @@ #include <asm/uaccess.h> #include <linux/errno.h> #include <linux/fs.h> -#include <linux/udf_fs.h> #include <linux/time.h> #include <linux/mm.h> #include <linux/stat.h> -#include <linux/slab.h> #include <linux/pagemap.h> -#include <linux/smp_lock.h> #include <linux/buffer_head.h> #include "udf_i.h" -static void udf_pc_to_char(struct super_block *sb, char *from, int fromlen, char *to) +static void udf_pc_to_char(struct super_block *sb, unsigned char *from, + int fromlen, unsigned char *to) { struct pathComponent *pc; int elen = 0; - char *p = to; + unsigned char *p = to; - while (elen < fromlen) - { + while (elen < fromlen) { pc = (struct pathComponent *)(from + elen); - switch (pc->componentType) - { - case 1: - if (pc->lengthComponentIdent == 0) - { - p = to; - *p++ = '/'; - } - break; - case 3: - memcpy(p, "../", 3); - p += 3; - break; - case 4: - memcpy(p, "./", 2); - p += 2; - /* that would be . - just ignore */ - break; - case 5: - p += udf_get_filename(sb, pc->componentIdent, p, pc->lengthComponentIdent); - *p++ = '/'; + switch (pc->componentType) { + case 1: + /* + * Symlink points to some place which should be agreed + * upon between originator and receiver of the media. Ignore. + */ + if (pc->lengthComponentIdent > 0) break; + /* Fall through */ + case 2: + p = to; + *p++ = '/'; + break; + case 3: + memcpy(p, "../", 3); + p += 3; + break; + case 4: + memcpy(p, "./", 2); + p += 2; + /* that would be . - just ignore */ + break; + case 5: + p += udf_get_filename(sb, pc->componentIdent, p, + pc->lengthComponentIdent); + *p++ = '/'; + break; } elen += sizeof(struct pathComponent) + pc->lengthComponentIdent; } - if (p > to+1) + if (p > to + 1) p[-1] = '\0'; else p[0] = '\0'; @@ -82,16 +79,20 @@ static int udf_symlink_filler(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; struct buffer_head *bh = NULL; - char *symlink; + unsigned char *symlink; int err = -EIO; - char *p = kmap(page); + unsigned char *p = kmap(page); + struct udf_inode_info *iinfo; + uint32_t pos; - lock_kernel(); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB) - symlink = UDF_I_DATA(inode) + UDF_I_LENEATTR(inode); - else - { - bh = sb_bread(inode->i_sb, udf_block_map(inode, 0)); + iinfo = UDF_I(inode); + pos = udf_block_map(inode, 0); + + down_read(&iinfo->i_data_sem); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { + symlink = iinfo->i_ext.i_data + iinfo->i_lenEAttr; + } else { + bh = sb_bread(inode->i_sb, pos); if (!bh) goto out; @@ -100,15 +101,16 @@ static int udf_symlink_filler(struct file *file, struct page *page) } udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p); - udf_release_data(bh); + brelse(bh); - unlock_kernel(); + up_read(&iinfo->i_data_sem); SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; + out: - unlock_kernel(); + up_read(&iinfo->i_data_sem); SetPageError(page); kunmap(page); unlock_page(page); @@ -118,6 +120,6 @@ out: /* * symlinks can't do much... */ -struct address_space_operations udf_symlink_aops = { +const struct address_space_operations udf_symlink_aops = { .readpage = udf_symlink_filler, }; diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 7dc8a5572ca..8a9657d7f7c 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -4,11 +4,6 @@ * PURPOSE * Truncate handling routines for the OSTA-UDF(tm) filesystem. * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -27,258 +22,266 @@ #include "udfdecl.h" #include <linux/fs.h> #include <linux/mm.h> -#include <linux/udf_fs.h> #include <linux/buffer_head.h> #include "udf_i.h" #include "udf_sb.h" -static void extent_trunc(struct inode * inode, kernel_lb_addr bloc, int extoffset, - kernel_lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen) +static void extent_trunc(struct inode *inode, struct extent_position *epos, + struct kernel_lb_addr *eloc, int8_t etype, uint32_t elen, + uint32_t nelen) { - kernel_lb_addr neloc = { 0, 0 }; - int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; - int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; + struct kernel_lb_addr neloc = {}; + int last_block = (elen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits; + int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> + inode->i_sb->s_blocksize_bits; - if (nelen) - { - if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block); + if (nelen) { + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + udf_free_blocks(inode->i_sb, inode, eloc, 0, + last_block); etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30); - } - else - neloc = eloc; + } else + neloc = *eloc; nelen = (etype << 30) | nelen; } - if (elen != nelen) - { - udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); - if (last_block - first_block > 0) - { + if (elen != nelen) { + udf_write_aext(inode, epos, &neloc, nelen, 0); + if (last_block - first_block > 0) { if (etype == (EXT_RECORDED_ALLOCATED >> 30)) mark_inode_dirty(inode); if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) - udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block); + udf_free_blocks(inode->i_sb, inode, eloc, + first_block, + last_block - first_block); } } } -void udf_discard_prealloc(struct inode * inode) +/* + * Truncate the last extent to match i_size. This function assumes + * that preallocation extent is already truncated. + */ +void udf_truncate_tail_extent(struct inode *inode) { - kernel_lb_addr bloc, eloc; - uint32_t extoffset = 0, elen, nelen; + struct extent_position epos = {}; + struct kernel_lb_addr eloc; + uint32_t elen, nelen; uint64_t lbcount = 0; int8_t etype = -1, netype; - struct buffer_head *bh = NULL; int adsize; + struct udf_inode_info *iinfo = UDF_I(inode); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || - inode->i_size == UDF_I_LENEXTENTS(inode)) - { + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB || + inode->i_size == iinfo->i_lenExtents) + return; + /* Are we going to delete the file anyway? */ + if (inode->i_nlink == 0) return; + + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); + else + BUG(); + + /* Find the last extent in the file */ + while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { + etype = netype; + lbcount += elen; + if (lbcount > inode->i_size) { + if (lbcount - inode->i_size >= inode->i_sb->s_blocksize) + udf_warn(inode->i_sb, + "Too long extent after EOF in inode %u: i_size: %lld lbcount: %lld extent %u+%u\n", + (unsigned)inode->i_ino, + (long long)inode->i_size, + (long long)lbcount, + (unsigned)eloc.logicalBlockNum, + (unsigned)elen); + nelen = elen - (lbcount - inode->i_size); + epos.offset -= adsize; + extent_trunc(inode, &epos, &eloc, etype, elen, nelen); + epos.offset += adsize; + if (udf_next_aext(inode, &epos, &eloc, &elen, 1) != -1) + udf_err(inode->i_sb, + "Extent after EOF in inode %u\n", + (unsigned)inode->i_ino); + break; + } } + /* This inode entry is in-memory only and thus we don't have to mark + * the inode dirty */ + iinfo->i_lenExtents = inode->i_size; + brelse(epos.bh); +} + +void udf_discard_prealloc(struct inode *inode) +{ + struct extent_position epos = { NULL, 0, {0, 0} }; + struct kernel_lb_addr eloc; + uint32_t elen; + uint64_t lbcount = 0; + int8_t etype = -1, netype; + int adsize; + struct udf_inode_info *iinfo = UDF_I(inode); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB || + inode->i_size == iinfo->i_lenExtents) + return; + + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); else adsize = 0; - bloc = UDF_I_LOCATION(inode); + epos.block = iinfo->i_location; - while ((netype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1) - { + /* Find the last extent in the file */ + while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { etype = netype; lbcount += elen; - if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize) - { - nelen = elen - (lbcount - inode->i_size); - extent_trunc(inode, bloc, extoffset-adsize, eloc, etype, elen, bh, nelen); - lbcount = inode->i_size; - } } - if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - extoffset -= adsize; + if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { + epos.offset -= adsize; lbcount -= elen; - extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); - if (!bh) - { - UDF_I_LENALLOC(inode) = extoffset - udf_file_entry_alloc_offset(inode); + extent_trunc(inode, &epos, &eloc, etype, elen, 0); + if (!epos.bh) { + iinfo->i_lenAlloc = + epos.offset - + udf_file_entry_alloc_offset(inode); mark_inode_dirty(inode); - } - else - { - struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); - aed->lengthAllocDescs = cpu_to_le32(extoffset - sizeof(struct allocExtDesc)); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag(bh->b_data, extoffset); + } else { + struct allocExtDesc *aed = + (struct allocExtDesc *)(epos.bh->b_data); + aed->lengthAllocDescs = + cpu_to_le32(epos.offset - + sizeof(struct allocExtDesc)); + if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || + UDF_SB(inode->i_sb)->s_udfrev >= 0x0201) + udf_update_tag(epos.bh->b_data, epos.offset); else - udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(bh, inode); + udf_update_tag(epos.bh->b_data, + sizeof(struct allocExtDesc)); + mark_buffer_dirty_inode(epos.bh, inode); } } - UDF_I_LENEXTENTS(inode) = lbcount; + /* This inode entry is in-memory only and thus we don't have to mark + * the inode dirty */ + iinfo->i_lenExtents = lbcount; + brelse(epos.bh); +} + +static void udf_update_alloc_ext_desc(struct inode *inode, + struct extent_position *epos, + u32 lenalloc) +{ + struct super_block *sb = inode->i_sb; + struct udf_sb_info *sbi = UDF_SB(sb); - udf_release_data(bh); + struct allocExtDesc *aed = (struct allocExtDesc *) (epos->bh->b_data); + int len = sizeof(struct allocExtDesc); + + aed->lengthAllocDescs = cpu_to_le32(lenalloc); + if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || sbi->s_udfrev >= 0x0201) + len += lenalloc; + + udf_update_tag(epos->bh->b_data, len); + mark_buffer_dirty_inode(epos->bh, inode); } -void udf_truncate_extents(struct inode * inode) +/* + * Truncate extents of inode to inode->i_size. This function can be used only + * for making file shorter. For making file longer, udf_extend_file() has to + * be used. + */ +void udf_truncate_extents(struct inode *inode) { - kernel_lb_addr bloc, eloc, neloc = { 0, 0 }; - uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc; + struct extent_position epos; + struct kernel_lb_addr eloc, neloc = {}; + uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc; int8_t etype; - int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits; - struct buffer_head *bh = NULL; + struct super_block *sb = inode->i_sb; + sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset; + loff_t byte_offset; int adsize; + struct udf_inode_info *iinfo = UDF_I(inode); - if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) - adsize = sizeof(short_ad); - else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) - adsize = sizeof(long_ad); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + adsize = sizeof(struct short_ad); + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + adsize = sizeof(struct long_ad); else - adsize = 0; - - etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh); - offset += (inode->i_size & (inode->i_sb->s_blocksize - 1)); - if (etype != -1) - { - extoffset -= adsize; - extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset); - extoffset += adsize; + BUG(); - if (offset) - lenalloc = extoffset; - else - lenalloc = extoffset - adsize; - - if (!bh) - lenalloc -= udf_file_entry_alloc_offset(inode); - else - lenalloc -= sizeof(struct allocExtDesc); - - while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1) - { - if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) - { - udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0); - extoffset = 0; - if (lelen) - { - if (!bh) - BUG(); - else - memset(bh->b_data, 0x00, sizeof(struct allocExtDesc)); - udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen); - } - else - { - if (!bh) - { - UDF_I_LENALLOC(inode) = lenalloc; - mark_inode_dirty(inode); - } - else - { - struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); - aed->lengthAllocDescs = cpu_to_le32(lenalloc); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag(bh->b_data, lenalloc + - sizeof(struct allocExtDesc)); - else - udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(bh, inode); - } - } + etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); + byte_offset = (offset << sb->s_blocksize_bits) + + (inode->i_size & (sb->s_blocksize - 1)); + if (etype == -1) { + /* We should extend the file? */ + WARN_ON(byte_offset); + return; + } + epos.offset -= adsize; + extent_trunc(inode, &epos, &eloc, etype, elen, byte_offset); + epos.offset += adsize; + if (byte_offset) + lenalloc = epos.offset; + else + lenalloc = epos.offset - adsize; - udf_release_data(bh); - extoffset = sizeof(struct allocExtDesc); - bloc = eloc; - bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, bloc, 0)); - if (elen) - lelen = (elen + inode->i_sb->s_blocksize - 1) >> - inode->i_sb->s_blocksize_bits; - else - lelen = 1; - } - else - { - extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0); - extoffset += adsize; - } - } + if (!epos.bh) + lenalloc -= udf_file_entry_alloc_offset(inode); + else + lenalloc -= sizeof(struct allocExtDesc); - if (lelen) - { - if (!bh) - BUG(); - else - memset(bh->b_data, 0x00, sizeof(struct allocExtDesc)); - udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen); - } - else - { - if (!bh) - { - UDF_I_LENALLOC(inode) = lenalloc; + while ((etype = udf_current_aext(inode, &epos, &eloc, + &elen, 0)) != -1) { + if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) { + udf_write_aext(inode, &epos, &neloc, nelen, 0); + if (indirect_ext_len) { + /* We managed to free all extents in the + * indirect extent - free it too */ + BUG_ON(!epos.bh); + udf_free_blocks(sb, NULL, &epos.block, + 0, indirect_ext_len); + } else if (!epos.bh) { + iinfo->i_lenAlloc = lenalloc; mark_inode_dirty(inode); - } + } else + udf_update_alloc_ext_desc(inode, + &epos, lenalloc); + brelse(epos.bh); + epos.offset = sizeof(struct allocExtDesc); + epos.block = eloc; + epos.bh = udf_tread(sb, + udf_get_lb_pblock(sb, &eloc, 0)); + if (elen) + indirect_ext_len = + (elen + sb->s_blocksize - 1) >> + sb->s_blocksize_bits; else - { - struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data); - aed->lengthAllocDescs = cpu_to_le32(lenalloc); - if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) - udf_update_tag(bh->b_data, lenalloc + - sizeof(struct allocExtDesc)); - else - udf_update_tag(bh->b_data, sizeof(struct allocExtDesc)); - mark_buffer_dirty_inode(bh, inode); - } + indirect_ext_len = 1; + } else { + extent_trunc(inode, &epos, &eloc, etype, elen, 0); + epos.offset += adsize; } } - else if (inode->i_size) - { - if (offset) - { - extoffset -= adsize; - etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1); - if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) - { - extoffset -= adsize; - elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset); - udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0); - } - else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) - { - kernel_lb_addr neloc = { 0, 0 }; - extoffset -= adsize; - nelen = EXT_NOT_RECORDED_NOT_ALLOCATED | - ((elen + offset + inode->i_sb->s_blocksize - 1) & - ~(inode->i_sb->s_blocksize - 1)); - udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1); - udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1); - } - else - { - if (elen & (inode->i_sb->s_blocksize - 1)) - { - extoffset -= adsize; - elen = EXT_RECORDED_ALLOCATED | - ((elen + inode->i_sb->s_blocksize - 1) & - ~(inode->i_sb->s_blocksize - 1)); - udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1); - } - memset(&eloc, 0x00, sizeof(kernel_lb_addr)); - elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset; - udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1); - } - } - } - UDF_I_LENEXTENTS(inode) = inode->i_size; - udf_release_data(bh); + if (indirect_ext_len) { + BUG_ON(!epos.bh); + udf_free_blocks(sb, NULL, &epos.block, 0, indirect_ext_len); + } else if (!epos.bh) { + iinfo->i_lenAlloc = lenalloc; + mark_inode_dirty(inode); + } else + udf_update_alloc_ext_desc(inode, &epos, lenalloc); + iinfo->i_lenExtents = inode->i_size; + + brelse(epos.bh); } diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index d7dbe6f3ba0..b5cd8ed2aa1 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h @@ -1,26 +1,62 @@ -#ifndef __LINUX_UDF_I_H -#define __LINUX_UDF_I_H +#ifndef _UDF_I_H +#define _UDF_I_H + +struct extent_position { + struct buffer_head *bh; + uint32_t offset; + struct kernel_lb_addr block; +}; + +struct udf_ext_cache { + /* Extent position */ + struct extent_position epos; + /* Start logical offset in bytes */ + loff_t lstart; +}; + +/* + * The i_data_sem and i_mutex serve for protection of allocation information + * of a regular files and symlinks. This includes all extents belonging to + * the file/symlink, a fact whether data are in-inode or in external data + * blocks, preallocation, goal block information... When extents are read, + * i_mutex or i_data_sem must be held (for reading is enough in case of + * i_data_sem). When extents are changed, i_data_sem must be held for writing + * and also i_mutex must be held. + * + * For directories i_mutex is used for all the necessary protection. + */ + +struct udf_inode_info { + struct timespec i_crtime; + /* Physical address of inode */ + struct kernel_lb_addr i_location; + __u64 i_unique; + __u32 i_lenEAttr; + __u32 i_lenAlloc; + __u64 i_lenExtents; + __u32 i_next_alloc_block; + __u32 i_next_alloc_goal; + __u32 i_checkpoint; + unsigned i_alloc_type : 3; + unsigned i_efe : 1; /* extendedFileEntry */ + unsigned i_use : 1; /* unallocSpaceEntry */ + unsigned i_strat4096 : 1; + unsigned reserved : 26; + union { + struct short_ad *i_sad; + struct long_ad *i_lad; + __u8 *i_data; + } i_ext; + struct rw_semaphore i_data_sem; + struct udf_ext_cache cached_extent; + /* Spinlock for protecting extent cache */ + spinlock_t i_extent_cache_lock; + struct inode vfs_inode; +}; -#include <linux/udf_fs_i.h> static inline struct udf_inode_info *UDF_I(struct inode *inode) { return list_entry(inode, struct udf_inode_info, vfs_inode); } -#define UDF_I_LOCATION(X) ( UDF_I(X)->i_location ) -#define UDF_I_LENEATTR(X) ( UDF_I(X)->i_lenEAttr ) -#define UDF_I_LENALLOC(X) ( UDF_I(X)->i_lenAlloc ) -#define UDF_I_LENEXTENTS(X) ( UDF_I(X)->i_lenExtents ) -#define UDF_I_UNIQUE(X) ( UDF_I(X)->i_unique ) -#define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type ) -#define UDF_I_EFE(X) ( UDF_I(X)->i_efe ) -#define UDF_I_USE(X) ( UDF_I(X)->i_use ) -#define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat4096 ) -#define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block ) -#define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal ) -#define UDF_I_CRTIME(X) ( UDF_I(X)->i_crtime ) -#define UDF_I_SAD(X) ( UDF_I(X)->i_ext.i_sad ) -#define UDF_I_LAD(X) ( UDF_I(X)->i_ext.i_lad ) -#define UDF_I_DATA(X) ( UDF_I(X)->i_ext.i_data ) - -#endif /* !defined(_LINUX_UDF_I_H) */ +#endif /* _UDF_I_H) */ diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h index 0e54922daa0..1f32c7bd9f5 100644 --- a/fs/udf/udf_sb.h +++ b/fs/udf/udf_sb.h @@ -1,10 +1,13 @@ #ifndef __LINUX_UDF_SB_H #define __LINUX_UDF_SB_H +#include <linux/mutex.h> +#include <linux/bitops.h> + /* Since UDF 2.01 is ISO 13346 based... */ #define UDF_SUPER_MAGIC 0x15013346 -#define UDF_MAX_READ_VERSION 0x0201 +#define UDF_MAX_READ_VERSION 0x0250 #define UDF_MAX_WRITE_VERSION 0x0201 #define UDF_FLAG_USE_EXTENDED_FE 0 @@ -20,6 +23,15 @@ #define UDF_FLAG_VARCONV 8 #define UDF_FLAG_NLS_MAP 9 #define UDF_FLAG_UTF8 10 +#define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */ +#define UDF_FLAG_UID_IGNORE 12 /* use sb uid instead of on disk uid */ +#define UDF_FLAG_GID_FORGET 13 +#define UDF_FLAG_GID_IGNORE 14 +#define UDF_FLAG_UID_SET 15 +#define UDF_FLAG_GID_SET 16 +#define UDF_FLAG_SESSION_SET 17 +#define UDF_FLAG_LASTBLOCK_SET 18 +#define UDF_FLAG_BLOCKSIZE_SET 19 #define UDF_PART_FLAG_UNALLOC_BITMAP 0x0001 #define UDF_PART_FLAG_UNALLOC_TABLE 0x0002 @@ -30,110 +42,143 @@ #define UDF_PART_FLAG_REWRITABLE 0x0040 #define UDF_PART_FLAG_OVERWRITABLE 0x0080 +#define UDF_MAX_BLOCK_LOADED 8 + +#define UDF_TYPE1_MAP15 0x1511U +#define UDF_VIRTUAL_MAP15 0x1512U +#define UDF_VIRTUAL_MAP20 0x2012U +#define UDF_SPARABLE_MAP15 0x1522U +#define UDF_METADATA_MAP25 0x2511U + +#define UDF_INVALID_MODE ((umode_t)-1) + +#pragma pack(1) /* XXX(hch): Why? This file just defines in-core structures */ + +#define MF_DUPLICATE_MD 0x01 +#define MF_MIRROR_FE_LOADED 0x02 + +struct udf_meta_data { + __u32 s_meta_file_loc; + __u32 s_mirror_file_loc; + __u32 s_bitmap_file_loc; + __u32 s_alloc_unit_size; + __u16 s_align_unit_size; + int s_flags; + struct inode *s_metadata_fe; + struct inode *s_mirror_fe; + struct inode *s_bitmap_fe; +}; + +struct udf_sparing_data { + __u16 s_packet_len; + struct buffer_head *s_spar_map[4]; +}; + +struct udf_virtual_data { + __u32 s_num_entries; + __u16 s_start_offset; +}; + +struct udf_bitmap { + __u32 s_extPosition; + int s_nr_groups; + struct buffer_head *s_block_bitmap[0]; +}; + +struct udf_part_map { + union { + struct udf_bitmap *s_bitmap; + struct inode *s_table; + } s_uspace; + union { + struct udf_bitmap *s_bitmap; + struct inode *s_table; + } s_fspace; + __u32 s_partition_root; + __u32 s_partition_len; + __u16 s_partition_type; + __u16 s_partition_num; + union { + struct udf_sparing_data s_sparing; + struct udf_virtual_data s_virtual; + struct udf_meta_data s_metadata; + } s_type_specific; + __u32 (*s_partition_func)(struct super_block *, __u32, __u16, __u32); + __u16 s_volumeseqnum; + __u16 s_partition_flags; +}; + +#pragma pack() + +struct udf_sb_info { + struct udf_part_map *s_partmaps; + __u8 s_volume_ident[32]; + + /* Overall info */ + __u16 s_partitions; + __u16 s_partition; + + /* Sector headers */ + __s32 s_session; + __u32 s_anchor; + __u32 s_last_block; + + struct buffer_head *s_lvid_bh; + + /* Default permissions */ + umode_t s_umask; + kgid_t s_gid; + kuid_t s_uid; + umode_t s_fmode; + umode_t s_dmode; + /* Lock protecting consistency of above permission settings */ + rwlock_t s_cred_lock; + + /* Root Info */ + struct timespec s_record_time; + + /* Fileset Info */ + __u16 s_serial_number; + + /* highest UDF revision we have recorded to this media */ + __u16 s_udfrev; + + /* Miscellaneous flags */ + unsigned long s_flags; + + /* Encoding info */ + struct nls_table *s_nls_map; + + /* VAT inode */ + struct inode *s_vat_inode; + + struct mutex s_alloc_mutex; + /* Protected by s_alloc_mutex */ + unsigned int s_lvid_dirty; +}; + static inline struct udf_sb_info *UDF_SB(struct super_block *sb) { return sb->s_fs_info; } -#define UDF_SB_FREE(X)\ -{\ - if (UDF_SB(X))\ - {\ - if (UDF_SB_PARTMAPS(X))\ - kfree(UDF_SB_PARTMAPS(X));\ - UDF_SB_PARTMAPS(X) = NULL;\ - }\ -} +struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb); -#define UDF_SB_ALLOC_PARTMAPS(X,Y)\ -{\ - UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\ - if (UDF_SB_PARTMAPS(X) != NULL)\ - {\ - UDF_SB_NUMPARTS(X) = Y;\ - memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\ - }\ - else\ - {\ - UDF_SB_NUMPARTS(X) = 0;\ - udf_error(X, __FUNCTION__, "Unable to allocate space for %d partition maps", Y);\ - }\ -} +int udf_compute_nr_groups(struct super_block *sb, u32 partition); -#define UDF_SB_ALLOC_BITMAP(X,Y,Z)\ -{\ - int nr_groups = ((UDF_SB_PARTLEN((X),(Y)) + (sizeof(struct spaceBitmapDesc) << 3) +\ - ((X)->s_blocksize * 8) - 1) / ((X)->s_blocksize * 8));\ - int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ - if (size <= PAGE_SIZE)\ - UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(size, GFP_KERNEL);\ - else\ - UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = vmalloc(size);\ - if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\ - {\ - memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00, size);\ - UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\ - (struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\ - UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\ - }\ - else\ - {\ - udf_error(X, __FUNCTION__, "Unable to allocate space for bitmap and %d buffer_head pointers", nr_groups);\ - }\ +static inline int UDF_QUERY_FLAG(struct super_block *sb, int flag) +{ + return test_bit(flag, &UDF_SB(sb)->s_flags); } -#define UDF_SB_FREE_BITMAP(X,Y,Z)\ -{\ - int i;\ - int nr_groups = UDF_SB_BITMAP_NR_GROUPS(X,Y,Z);\ - int size = sizeof(struct udf_bitmap) + (sizeof(struct buffer_head *) * nr_groups);\ - for (i=0; i<nr_groups; i++)\ - {\ - if (UDF_SB_BITMAP(X,Y,Z,i))\ - udf_release_data(UDF_SB_BITMAP(X,Y,Z,i));\ - }\ - if (size <= PAGE_SIZE)\ - kfree(UDF_SB_PARTMAPS(X)[Y].Z.s_bitmap);\ - else\ - vfree(UDF_SB_PARTMAPS(X)[Y].Z.s_bitmap);\ +static inline void UDF_SET_FLAG(struct super_block *sb, int flag) +{ + set_bit(flag, &UDF_SB(sb)->s_flags); } -#define UDF_QUERY_FLAG(X,Y) ( UDF_SB(X)->s_flags & ( 1 << (Y) ) ) -#define UDF_SET_FLAG(X,Y) ( UDF_SB(X)->s_flags |= ( 1 << (Y) ) ) -#define UDF_CLEAR_FLAG(X,Y) ( UDF_SB(X)->s_flags &= ~( 1 << (Y) ) ) - -#define UDF_UPDATE_UDFREV(X,Y) ( ((Y) > UDF_SB_UDFREV(X)) ? UDF_SB_UDFREV(X) = (Y) : UDF_SB_UDFREV(X) ) - -#define UDF_SB_PARTMAPS(X) ( UDF_SB(X)->s_partmaps ) -#define UDF_SB_PARTTYPE(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_type ) -#define UDF_SB_PARTROOT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_root ) -#define UDF_SB_PARTLEN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_len ) -#define UDF_SB_PARTVSN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_volumeseqnum ) -#define UDF_SB_PARTNUM(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_num ) -#define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing ) -#define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual ) -#define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func ) -#define UDF_SB_PARTFLAGS(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags ) -#define UDF_SB_BITMAP(X,Y,Z,I) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap[I] ) -#define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups ) - -#define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident ) -#define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions ) -#define UDF_SB_PARTITION(X) ( UDF_SB(X)->s_partition ) -#define UDF_SB_SESSION(X) ( UDF_SB(X)->s_session ) -#define UDF_SB_ANCHOR(X) ( UDF_SB(X)->s_anchor ) -#define UDF_SB_LASTBLOCK(X) ( UDF_SB(X)->s_lastblock ) -#define UDF_SB_LVIDBH(X) ( UDF_SB(X)->s_lvidbh ) -#define UDF_SB_LVID(X) ( (struct logicalVolIntegrityDesc *)UDF_SB_LVIDBH(X)->b_data ) -#define UDF_SB_LVIDIU(X) ( (struct logicalVolIntegrityDescImpUse *)&(UDF_SB_LVID(X)->impUse[le32_to_cpu(UDF_SB_LVID(X)->numOfPartitions) * 2 * sizeof(uint32_t)/sizeof(uint8_t)]) ) - -#define UDF_SB_UMASK(X) ( UDF_SB(X)->s_umask ) -#define UDF_SB_GID(X) ( UDF_SB(X)->s_gid ) -#define UDF_SB_UID(X) ( UDF_SB(X)->s_uid ) -#define UDF_SB_RECORDTIME(X) ( UDF_SB(X)->s_recordtime ) -#define UDF_SB_SERIALNUM(X) ( UDF_SB(X)->s_serialnum ) -#define UDF_SB_UDFREV(X) ( UDF_SB(X)->s_udfrev ) -#define UDF_SB_FLAGS(X) ( UDF_SB(X)->s_flags ) -#define UDF_SB_VAT(X) ( UDF_SB(X)->s_vat ) +static inline void UDF_CLEAR_FLAG(struct super_block *sb, int flag) +{ + clear_bit(flag, &UDF_SB(sb)->s_flags); +} #endif /* __LINUX_UDF_SB_H */ diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 1d5800e0cbe..be7dabbbcb4 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -1,18 +1,46 @@ #ifndef __UDF_DECL_H #define __UDF_DECL_H -#include <linux/udf_fs.h> +#define pr_fmt(fmt) "UDF-fs: " fmt + #include "ecma_167.h" #include "osta_udf.h" #include <linux/fs.h> -#include <linux/config.h> #include <linux/types.h> -#include <linux/udf_fs_i.h> -#include <linux/udf_fs_sb.h> #include <linux/buffer_head.h> +#include <linux/udf_fs_i.h> +#include "udf_sb.h" #include "udfend.h" +#include "udf_i.h" + +#define UDF_PREALLOCATE +#define UDF_DEFAULT_PREALLOC_BLOCKS 8 + +extern __printf(3, 4) void _udf_err(struct super_block *sb, + const char *function, const char *fmt, ...); +#define udf_err(sb, fmt, ...) \ + _udf_err(sb, __func__, fmt, ##__VA_ARGS__) + +extern __printf(3, 4) void _udf_warn(struct super_block *sb, + const char *function, const char *fmt, ...); +#define udf_warn(sb, fmt, ...) \ + _udf_warn(sb, __func__, fmt, ##__VA_ARGS__) + +#define udf_info(fmt, ...) \ + pr_info("INFO " fmt, ##__VA_ARGS__) + +#undef UDFFS_DEBUG + +#ifdef UDFFS_DEBUG +#define udf_debug(fmt, ...) \ + printk(KERN_DEBUG pr_fmt("%s:%d:%s: " fmt), \ + __FILE__, __LINE__, __func__, ##__VA_ARGS__) +#else +#define udf_debug(fmt, ...) \ + no_printk(fmt, ##__VA_ARGS__) +#endif #define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) ) #define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) ) @@ -24,18 +52,27 @@ #define UDF_NAME_LEN 256 #define UDF_PATH_LEN 1023 -#define udf_file_entry_alloc_offset(inode)\ - (UDF_I_USE(inode) ?\ - sizeof(struct unallocSpaceEntry) :\ - ((UDF_I_EFE(inode) ?\ - sizeof(struct extendedFileEntry) :\ - sizeof(struct fileEntry)) + UDF_I_LENEATTR(inode))) - -#define udf_ext0_offset(inode)\ - (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ?\ - udf_file_entry_alloc_offset(inode) : 0) +static inline size_t udf_file_entry_alloc_offset(struct inode *inode) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + if (iinfo->i_use) + return sizeof(struct unallocSpaceEntry); + else if (iinfo->i_efe) + return sizeof(struct extendedFileEntry) + iinfo->i_lenEAttr; + else + return sizeof(struct fileEntry) + iinfo->i_lenEAttr; +} + +static inline size_t udf_ext0_offset(struct inode *inode) +{ + if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + return udf_file_entry_alloc_offset(inode); + else + return 0; +} -#define udf_get_lb_pblock(sb,loc,offset) udf_get_pblock((sb), (loc).logicalBlockNum, (loc).partitionReferenceNum, (offset)) +/* computes tag checksum */ +u8 udf_tag_checksum(const struct tag *t); struct dentry; struct inode; @@ -43,78 +80,98 @@ struct task_struct; struct buffer_head; struct super_block; -extern struct inode_operations udf_dir_inode_operations; -extern struct file_operations udf_dir_operations; -extern struct inode_operations udf_file_inode_operations; -extern struct file_operations udf_file_operations; -extern struct address_space_operations udf_aops; -extern struct address_space_operations udf_adinicb_aops; -extern struct address_space_operations udf_symlink_aops; - -struct udf_fileident_bh -{ +extern const struct export_operations udf_export_ops; +extern const struct inode_operations udf_dir_inode_operations; +extern const struct file_operations udf_dir_operations; +extern const struct inode_operations udf_file_inode_operations; +extern const struct file_operations udf_file_operations; +extern const struct inode_operations udf_symlink_inode_operations; +extern const struct address_space_operations udf_aops; +extern const struct address_space_operations udf_adinicb_aops; +extern const struct address_space_operations udf_symlink_aops; + +struct udf_fileident_bh { struct buffer_head *sbh; struct buffer_head *ebh; int soffset; int eoffset; }; -struct udf_vds_record -{ +struct udf_vds_record { uint32_t block; uint32_t volDescSeqNum; }; -struct generic_desc -{ - tag descTag; +struct generic_desc { + struct tag descTag; __le32 volDescSeqNum; }; -struct ustr -{ +struct ustr { uint8_t u_cmpID; - uint8_t u_name[UDF_NAME_LEN-2]; + uint8_t u_name[UDF_NAME_LEN - 2]; uint8_t u_len; }; + /* super.c */ -extern void udf_error(struct super_block *, const char *, const char *, ...); -extern void udf_warning(struct super_block *, const char *, const char *, ...); + +static inline void udf_updated_lvid(struct super_block *sb) +{ + struct buffer_head *bh = UDF_SB(sb)->s_lvid_bh; + + BUG_ON(!bh); + WARN_ON_ONCE(((struct logicalVolIntegrityDesc *) + bh->b_data)->integrityType != + cpu_to_le32(LVID_INTEGRITY_TYPE_OPEN)); + UDF_SB(sb)->s_lvid_dirty = 1; +} +extern u64 lvid_get_unique_id(struct super_block *sb); +struct inode *udf_find_metadata_inode_efe(struct super_block *sb, + u32 meta_file_loc, u32 partition_num); /* namei.c */ -extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, struct fileIdentDesc *, struct udf_fileident_bh *, uint8_t *, uint8_t *); +extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, + struct fileIdentDesc *, struct udf_fileident_bh *, + uint8_t *, uint8_t *); /* file.c */ -extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long); - +extern long udf_ioctl(struct file *, unsigned int, unsigned long); /* inode.c */ -extern struct inode *udf_iget(struct super_block *, kernel_lb_addr); -extern int udf_sync_inode(struct inode *); -extern void udf_expand_file_adinicb(struct inode *, int, int *); -extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *); -extern struct buffer_head * udf_bread(struct inode *, int, int, int *); -extern void udf_truncate(struct inode *); +extern struct inode *udf_iget(struct super_block *, struct kernel_lb_addr *); +extern int udf_expand_file_adinicb(struct inode *); +extern struct buffer_head *udf_expand_dir_adinicb(struct inode *, int *, int *); +extern struct buffer_head *udf_bread(struct inode *, int, int, int *); +extern int udf_setsize(struct inode *, loff_t); extern void udf_read_inode(struct inode *); -extern void udf_delete_inode(struct inode *); -extern void udf_clear_inode(struct inode *); -extern int udf_write_inode(struct inode *, int); -extern long udf_block_map(struct inode *, long); -extern int8_t inode_bmap(struct inode *, int, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **); -extern int8_t udf_add_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr, uint32_t, struct buffer_head **, int); -extern int8_t udf_write_aext(struct inode *, kernel_lb_addr, int *, kernel_lb_addr, uint32_t, struct buffer_head *, int); -extern int8_t udf_delete_aext(struct inode *, kernel_lb_addr, int, kernel_lb_addr, uint32_t, struct buffer_head *); -extern int8_t udf_next_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int); -extern int8_t udf_current_aext(struct inode *, kernel_lb_addr *, int *, kernel_lb_addr *, uint32_t *, struct buffer_head **, int); +extern void udf_evict_inode(struct inode *); +extern int udf_write_inode(struct inode *, struct writeback_control *wbc); +extern long udf_block_map(struct inode *, sector_t); +extern int8_t inode_bmap(struct inode *, sector_t, struct extent_position *, + struct kernel_lb_addr *, uint32_t *, sector_t *); +extern int udf_add_aext(struct inode *, struct extent_position *, + struct kernel_lb_addr *, uint32_t, int); +extern void udf_write_aext(struct inode *, struct extent_position *, + struct kernel_lb_addr *, uint32_t, int); +extern int8_t udf_delete_aext(struct inode *, struct extent_position, + struct kernel_lb_addr, uint32_t); +extern int8_t udf_next_aext(struct inode *, struct extent_position *, + struct kernel_lb_addr *, uint32_t *, int); +extern int8_t udf_current_aext(struct inode *, struct extent_position *, + struct kernel_lb_addr *, uint32_t *, int); /* misc.c */ extern struct buffer_head *udf_tgetblk(struct super_block *, int); extern struct buffer_head *udf_tread(struct super_block *, int); -extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t); -extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t); -extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *); -extern struct buffer_head *udf_read_ptagged(struct super_block *, kernel_lb_addr, uint32_t, uint16_t *); -extern void udf_release_data(struct buffer_head *); +extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, + uint32_t, uint8_t); +extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, + uint8_t); +extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, + uint32_t, uint16_t *); +extern struct buffer_head *udf_read_ptagged(struct super_block *, + struct kernel_lb_addr *, uint32_t, + uint16_t *); extern void udf_update_tag(char *, int); extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int); @@ -123,45 +180,65 @@ extern unsigned int udf_get_last_session(struct super_block *); extern unsigned long udf_get_last_block(struct super_block *); /* partition.c */ -extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, uint32_t); -extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, uint32_t); -extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, uint32_t); -extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, uint32_t); +extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, + uint32_t); +extern uint32_t udf_get_pblock_meta25(struct super_block *, uint32_t, uint16_t, + uint32_t); extern int udf_relocate_blocks(struct super_block *, long, long *); +static inline uint32_t +udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc, + uint32_t offset) +{ + return udf_get_pblock(sb, loc->logicalBlockNum, + loc->partitionReferenceNum, offset); +} + /* unicode.c */ extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int); -extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, int); +extern int udf_put_filename(struct super_block *, const uint8_t *, uint8_t *, + int); extern int udf_build_ustr(struct ustr *, dstring *, int); -extern int udf_CS0toUTF8(struct ustr *, struct ustr *); +extern int udf_CS0toUTF8(struct ustr *, const struct ustr *); /* ialloc.c */ extern void udf_free_inode(struct inode *); -extern struct inode * udf_new_inode (struct inode *, int, int *); +extern struct inode *udf_new_inode(struct inode *, umode_t, int *); /* truncate.c */ +extern void udf_truncate_tail_extent(struct inode *); extern void udf_discard_prealloc(struct inode *); extern void udf_truncate_extents(struct inode *); /* balloc.c */ -extern void udf_free_blocks(struct super_block *, struct inode *, kernel_lb_addr, uint32_t, uint32_t); -extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, uint32_t, uint32_t); -extern int udf_new_block(struct super_block *, struct inode *, uint16_t, uint32_t, int *); - -/* fsync.c */ -extern int udf_fsync_file(struct file *, struct dentry *, int); +extern void udf_free_blocks(struct super_block *, struct inode *, + struct kernel_lb_addr *, uint32_t, uint32_t); +extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, + uint32_t, uint32_t); +extern int udf_new_block(struct super_block *, struct inode *, uint16_t, + uint32_t, int *); /* directory.c */ -extern struct fileIdentDesc * udf_fileident_read(struct inode *, loff_t *, struct udf_fileident_bh *, struct fileIdentDesc *, kernel_lb_addr *, uint32_t *, kernel_lb_addr *, uint32_t *, uint32_t *, struct buffer_head **); -extern struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset); -extern long_ad * udf_get_filelongad(uint8_t *, int, int *, int); -extern short_ad * udf_get_fileshortad(uint8_t *, int, int *, int); - -/* crc.c */ -extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t); +extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *, + struct udf_fileident_bh *, + struct fileIdentDesc *, + struct extent_position *, + struct kernel_lb_addr *, uint32_t *, + sector_t *); +extern struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, + int *offset); +extern struct long_ad *udf_get_filelongad(uint8_t *, int, uint32_t *, int); +extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int); /* udftime.c */ -extern time_t *udf_stamp_to_time(time_t *, long *, kernel_timestamp); -extern kernel_timestamp *udf_time_to_stamp(kernel_timestamp *, struct timespec); +extern struct timespec *udf_disk_stamp_to_time(struct timespec *dest, + struct timestamp src); +extern struct timestamp *udf_time_to_disk_stamp(struct timestamp *dest, struct timespec src); -#endif /* __UDF_DECL_H */ +#endif /* __UDF_DECL_H */ diff --git a/fs/udf/udfend.h b/fs/udf/udfend.h index 17d37887956..6a9f3a9cc42 100644 --- a/fs/udf/udfend.h +++ b/fs/udf/udfend.h @@ -4,77 +4,73 @@ #include <asm/byteorder.h> #include <linux/string.h> -static inline kernel_lb_addr lelb_to_cpu(lb_addr in) +static inline struct kernel_lb_addr lelb_to_cpu(struct lb_addr in) { - kernel_lb_addr out; + struct kernel_lb_addr out; + out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum); out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum); + return out; } -static inline lb_addr cpu_to_lelb(kernel_lb_addr in) +static inline struct lb_addr cpu_to_lelb(struct kernel_lb_addr in) { - lb_addr out; + struct lb_addr out; + out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum); out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum); - return out; -} -static inline kernel_timestamp lets_to_cpu(timestamp in) -{ - kernel_timestamp out; - memcpy(&out, &in, sizeof(timestamp)); - out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone); - out.year = le16_to_cpu(in.year); return out; } -static inline short_ad lesa_to_cpu(short_ad in) +static inline struct short_ad lesa_to_cpu(struct short_ad in) { - short_ad out; + struct 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) +static inline struct short_ad cpu_to_lesa(struct short_ad in) { - short_ad out; + struct short_ad out; + out.extLength = cpu_to_le32(in.extLength); out.extPosition = cpu_to_le32(in.extPosition); + return out; } -static inline kernel_long_ad lela_to_cpu(long_ad in) +static inline struct kernel_long_ad lela_to_cpu(struct long_ad in) { - kernel_long_ad out; + struct kernel_long_ad out; + out.extLength = le32_to_cpu(in.extLength); out.extLocation = lelb_to_cpu(in.extLocation); + return out; } -static inline long_ad cpu_to_lela(kernel_long_ad in) +static inline struct long_ad cpu_to_lela(struct kernel_long_ad in) { - long_ad out; + struct long_ad out; + out.extLength = cpu_to_le32(in.extLength); out.extLocation = cpu_to_lelb(in.extLocation); + return out; } -static inline kernel_extent_ad leea_to_cpu(extent_ad in) +static inline struct kernel_extent_ad leea_to_cpu(struct extent_ad in) { - kernel_extent_ad out; + struct kernel_extent_ad out; + out.extLength = le32_to_cpu(in.extLength); out.extLocation = le32_to_cpu(in.extLocation); - return out; -} -static inline timestamp cpu_to_lets(kernel_timestamp in) -{ - timestamp out; - memcpy(&out, &in, sizeof(timestamp)); - out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone); - out.year = cpu_to_le16(in.year); return out; } diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c index 85d8dbe843f..1f11483eba6 100644 --- a/fs/udf/udftime.c +++ b/fs/udf/udftime.c @@ -18,23 +18,26 @@ 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 + * 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. + * 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 + * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm */ +#include "udfdecl.h" + #include <linux/types.h> #include <linux/kernel.h> -#include "udfdecl.h" #define EPOCH_YEAR 1970 @@ -46,37 +49,36 @@ #endif /* How many days come before each month (0-12). */ -static const unsigned short int __mon_yday[2][13] = -{ +static const unsigned short int __mon_yday[2][13] = { /* Normal years. */ - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + {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 } + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} }; #define MAX_YEAR_SECONDS 69 -#define SPD 0x15180 /*3600*24*/ -#define SPY(y,l,s) (SPD * (365*y+l)+s) - -static time_t year_seconds[MAX_YEAR_SECONDS]= { -/*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0), -/*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0), -/*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0), -/*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0), -/*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0), -/*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0), -/*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0), -/*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0), -/*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0), -/*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0), -/*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0), -/*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0), -/*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0), -/*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0), -/*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0), -/*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0), -/*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0), -/*2038*/ SPY(68,17,0) +#define SPD 0x15180 /*3600*24 */ +#define SPY(y, l, s) (SPD * (365 * y + l) + s) + +static time_t year_seconds[MAX_YEAR_SECONDS] = { +/*1970*/ SPY(0, 0, 0), SPY(1, 0, 0), SPY(2, 0, 0), SPY(3, 1, 0), +/*1974*/ SPY(4, 1, 0), SPY(5, 1, 0), SPY(6, 1, 0), SPY(7, 2, 0), +/*1978*/ SPY(8, 2, 0), SPY(9, 2, 0), SPY(10, 2, 0), SPY(11, 3, 0), +/*1982*/ SPY(12, 3, 0), SPY(13, 3, 0), SPY(14, 3, 0), SPY(15, 4, 0), +/*1986*/ SPY(16, 4, 0), SPY(17, 4, 0), SPY(18, 4, 0), SPY(19, 5, 0), +/*1990*/ SPY(20, 5, 0), SPY(21, 5, 0), SPY(22, 5, 0), SPY(23, 6, 0), +/*1994*/ SPY(24, 6, 0), SPY(25, 6, 0), SPY(26, 6, 0), SPY(27, 7, 0), +/*1998*/ SPY(28, 7, 0), SPY(29, 7, 0), SPY(30, 7, 0), SPY(31, 8, 0), +/*2002*/ SPY(32, 8, 0), SPY(33, 8, 0), SPY(34, 8, 0), SPY(35, 9, 0), +/*2006*/ SPY(36, 9, 0), SPY(37, 9, 0), SPY(38, 9, 0), SPY(39, 10, 0), +/*2010*/ SPY(40, 10, 0), SPY(41, 10, 0), SPY(42, 10, 0), SPY(43, 11, 0), +/*2014*/ SPY(44, 11, 0), SPY(45, 11, 0), SPY(46, 11, 0), SPY(47, 12, 0), +/*2018*/ SPY(48, 12, 0), SPY(49, 12, 0), SPY(50, 12, 0), SPY(51, 13, 0), +/*2022*/ SPY(52, 13, 0), SPY(53, 13, 0), SPY(54, 13, 0), SPY(55, 14, 0), +/*2026*/ SPY(56, 14, 0), SPY(57, 14, 0), SPY(58, 14, 0), SPY(59, 15, 0), +/*2030*/ SPY(60, 15, 0), SPY(61, 15, 0), SPY(62, 15, 0), SPY(63, 16, 0), +/*2034*/ SPY(64, 16, 0), SPY(65, 16, 0), SPY(66, 16, 0), SPY(67, 17, 0), +/*2038*/ SPY(68, 17, 0) }; extern struct timezone sys_tz; @@ -84,44 +86,40 @@ extern struct timezone sys_tz; #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) -time_t * -udf_stamp_to_time(time_t *dest, long *dest_usec, kernel_timestamp src) +struct timespec * +udf_disk_stamp_to_time(struct timespec *dest, struct timestamp src) { int yday; - uint8_t type = src.typeAndTimezone >> 12; + u16 typeAndTimezone = le16_to_cpu(src.typeAndTimezone); + u16 year = le16_to_cpu(src.year); + uint8_t type = typeAndTimezone >> 12; int16_t offset; - if (type == 1) - { - offset = src.typeAndTimezone << 4; + if (type == 1) { + offset = typeAndTimezone << 4; /* sign extent offset */ offset = (offset >> 4); if (offset == -2047) /* unspecified offset */ offset = 0; - } - else + } else offset = 0; - if ((src.year < EPOCH_YEAR) || - (src.year >= EPOCH_YEAR+MAX_YEAR_SECONDS)) - { - *dest = -1; - *dest_usec = -1; + if ((year < EPOCH_YEAR) || + (year >= EPOCH_YEAR + MAX_YEAR_SECONDS)) { return NULL; } - *dest = year_seconds[src.year - EPOCH_YEAR]; - *dest -= offset * 60; + dest->tv_sec = year_seconds[year - EPOCH_YEAR]; + dest->tv_sec -= 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; + yday = ((__mon_yday[__isleap(year)][src.month - 1]) + src.day - 1); + dest->tv_sec += (((yday * 24) + src.hour) * 60 + src.minute) * 60 + src.second; + dest->tv_nsec = 1000 * (src.centiseconds * 10000 + + src.hundredsOfMicroseconds * 100 + src.microseconds); return dest; } - -kernel_timestamp * -udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) +struct timestamp * +udf_time_to_disk_stamp(struct timestamp *dest, struct timespec ts) { long int days, rem, y; const unsigned short int *ip; @@ -132,7 +130,7 @@ udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) if (!dest) return NULL; - dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF); + dest->typeAndTimezone = cpu_to_le16(0x1000 | (offset & 0x0FFF)); ts.tv_sec += offset * 60; days = ts.tv_sec / SECS_PER_DAY; @@ -143,31 +141,31 @@ udf_time_to_stamp(kernel_timestamp *dest, struct timespec ts) dest->second = rem % 60; y = 1970; -#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0)) +#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)) - { + 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)); + + LEAPS_THRU_END_OF(yg - 1) + - LEAPS_THRU_END_OF(y - 1)); y = yg; } - dest->year = y; + dest->year = cpu_to_le16(y); ip = __mon_yday[__isleap(y)]; - for (y = 11; days < (long int) ip[y]; --y) + for (y = 11; days < (long int)ip[y]; --y) continue; days -= ip[y]; dest->month = y + 1; dest->day = days + 1; dest->centiseconds = ts.tv_nsec / 10000000; - dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000) / 100; + dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 - + dest->centiseconds * 10000) / 100; dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 - - dest->hundredsOfMicroseconds * 100); + dest->hundredsOfMicroseconds * 100); return dest; } diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c index 5a80efd8deb..44b815e57f9 100644 --- a/fs/udf/unicode.c +++ b/fs/udf/unicode.c @@ -11,11 +11,6 @@ * UTF-8 is explained in the IETF RFC XXXX. * ftp://ftp.internic.net/rfc/rfcxxxx.txt * - * CONTACTS - * E-mail regarding any portion of the Linux UDF file system should be - * directed to the development team's mailing list (run by majordomo): - * linux_udf@hpesjro.fc.hp.com - * * COPYRIGHT * This file is distributed under the terms of the GNU General Public * License (GPL). Copies of the GPL can be obtained from: @@ -28,7 +23,8 @@ #include <linux/kernel.h> #include <linux/string.h> /* for memset */ #include <linux/nls.h> -#include <linux/udf_fs.h> +#include <linux/crc-itu-t.h> +#include <linux/slab.h> #include "udf_sb.h" @@ -36,12 +32,14 @@ static int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int); static int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen) { - if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN-2) ) + if ((!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN - 2)) return 0; + memset(dest, 0, sizeof(struct ustr)); memcpy(dest->u_name, src, strlen); dest->u_cmpID = 0x08; dest->u_len = strlen; + return strlen; } @@ -52,14 +50,17 @@ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) { int usesize; - if ( (!dest) || (!ptr) || (!size) ) + if (!dest || !ptr || !size) return -1; + BUG_ON(size < 2); + + usesize = min_t(size_t, ptr[size - 1], sizeof(dest->u_name)); + usesize = min(usesize, size - 2); + dest->u_cmpID = ptr[0]; + dest->u_len = usesize; + memcpy(dest->u_name, ptr + 1, usesize); + memset(dest->u_name + usesize, 0, sizeof(dest->u_name) - usesize); - 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; } @@ -68,13 +69,14 @@ int udf_build_ustr(struct ustr *dest, dstring *ptr, int size) */ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) { - if ( (!dest) || (!ptr) || (!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); + dest->u_cmpID = ptr[0]; + dest->u_len = exactsize - 1; + memcpy(dest->u_name, ptr + 1, exactsize - 1); + return 0; } @@ -84,9 +86,6 @@ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) * 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 @@ -100,57 +99,54 @@ static int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize) * November 12, 1997 - Andrew E. Mileski * Written, tested, and released. */ -int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i) +int udf_CS0toUTF8(struct ustr *utf_o, const struct ustr *ocu_i) { - uint8_t *ocu; - uint32_t c; + const uint8_t *ocu; uint8_t cmp_id, ocu_len; int i; - ocu = ocu_i->u_name; - ocu_len = ocu_i->u_len; - cmp_id = ocu_i->u_cmpID; - utf_o->u_len = 0; - - if (ocu_len == 0) - { + if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); - utf_o->u_cmpID = 0; - utf_o->u_len = 0; return 0; } - if ((cmp_id != 8) && (cmp_id != 16)) - { - printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); + cmp_id = ocu_i->u_cmpID; + if (cmp_id != 8 && cmp_id != 16) { + memset(utf_o, 0, sizeof(struct ustr)); + pr_err("unknown compression code (%d) stri=%s\n", + cmp_id, ocu_i->u_name); return 0; } - for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) - { + ocu = ocu_i->u_name; + utf_o->u_len = 0; + for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) { /* Expand OSTA compressed Unicode to Unicode */ - c = ocu[i++]; + uint32_t c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; /* Compress Unicode to UTF-8 */ if (c < 0x80U) utf_o->u_name[utf_o->u_len++] = (uint8_t)c; - else if (c < 0x800U) - { - utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6)); - utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); - } - else - { - utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12)); - utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f)); - utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f)); + else if (c < 0x800U) { + utf_o->u_name[utf_o->u_len++] = + (uint8_t)(0xc0 | (c >> 6)); + utf_o->u_name[utf_o->u_len++] = + (uint8_t)(0x80 | (c & 0x3f)); + } else { + utf_o->u_name[utf_o->u_len++] = + (uint8_t)(0xe0 | (c >> 12)); + utf_o->u_name[utf_o->u_len++] = + (uint8_t)(0x80 | + ((c >> 6) & 0x3f)); + utf_o->u_name[utf_o->u_len++] = + (uint8_t)(0x80 | (c & 0x3f)); } } - utf_o->u_cmpID=8; + utf_o->u_cmpID = 8; return utf_o->u_len; } @@ -191,61 +187,46 @@ try_again: u_len = 0U; utf_char = 0U; utf_cnt = 0U; - for (i = 0U; i < utf->u_len; i++) - { + for (i = 0U; i < utf->u_len; i++) { c = (uint8_t)utf->u_name[i]; /* Complete a multi-byte UTF-8 character */ - if (utf_cnt) - { + if (utf_cnt) { utf_char = (utf_char << 6) | (c & 0x3fU); if (--utf_cnt) continue; - } - else - { + } else { /* Check for a multi-byte UTF-8 character */ - if (c & 0x80U) - { + if (c & 0x80U) { /* Start a multi-byte UTF-8 character */ - if ((c & 0xe0U) == 0xc0U) - { + if ((c & 0xe0U) == 0xc0U) { utf_char = c & 0x1fU; utf_cnt = 1; - } - else if ((c & 0xf0U) == 0xe0U) - { + } else if ((c & 0xf0U) == 0xe0U) { utf_char = c & 0x0fU; utf_cnt = 2; - } - else if ((c & 0xf8U) == 0xf0U) - { + } else if ((c & 0xf8U) == 0xf0U) { utf_char = c & 0x07U; utf_cnt = 3; - } - else if ((c & 0xfcU) == 0xf8U) - { + } else if ((c & 0xfcU) == 0xf8U) { utf_char = c & 0x03U; utf_cnt = 4; - } - else if ((c & 0xfeU) == 0xfcU) - { + } else if ((c & 0xfeU) == 0xfcU) { utf_char = c & 0x01U; utf_cnt = 5; - } - else + } else { goto error_out; + } continue; - } else + } 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 ) - { + if (utf_char > max_val) { + if (max_val == 0xffU) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; @@ -254,69 +235,69 @@ try_again: } if (max_val == 0xffffU) - { ocu[++u_len] = (uint8_t)(utf_char >> 8); - } ocu[++u_len] = (uint8_t)(utf_char & 0xffU); } - - if (utf_cnt) - { + if (utf_cnt) { error_out: ocu[++u_len] = '?'; - printk(KERN_DEBUG "udf: bad UTF-8 character\n"); + printk(KERN_DEBUG pr_fmt("bad UTF-8 character\n")); } ocu[length - 1] = (uint8_t)u_len + 1; + return u_len + 1; } -static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr *ocu_i) +static int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, + const struct ustr *ocu_i) { - uint8_t *ocu; - uint32_t c; + const uint8_t *ocu; uint8_t cmp_id, ocu_len; - int i; + int i, len; - 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) - { + if (ocu_len == 0) { memset(utf_o, 0, sizeof(struct ustr)); - utf_o->u_cmpID = 0; - utf_o->u_len = 0; return 0; } - if ((cmp_id != 8) && (cmp_id != 16)) - { - printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name); + cmp_id = ocu_i->u_cmpID; + if (cmp_id != 8 && cmp_id != 16) { + memset(utf_o, 0, sizeof(struct ustr)); + pr_err("unknown compression code (%d) stri=%s\n", + cmp_id, ocu_i->u_name); return 0; } - for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;) - { + ocu = ocu_i->u_name; + utf_o->u_len = 0; + for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN - 3));) { /* Expand OSTA compressed Unicode to Unicode */ - c = ocu[i++]; + uint32_t c = ocu[i++]; if (cmp_id == 16) c = (c << 8) | ocu[i++]; - utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len], - UDF_NAME_LEN - utf_o->u_len); + len = nls->uni2char(c, &utf_o->u_name[utf_o->u_len], + UDF_NAME_LEN - utf_o->u_len); + /* Valid character? */ + if (len >= 0) + utf_o->u_len += len; + else + utf_o->u_name[utf_o->u_len++] = '?'; } - utf_o->u_cmpID=8; + utf_o->u_cmpID = 8; return utf_o->u_len; } -static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length) +static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, + int length) { - unsigned len, i, max_val; + int len; + unsigned i, max_val; uint16_t uni_char; int u_len; @@ -326,19 +307,22 @@ static int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, i try_again: u_len = 0U; - for (i = 0U; i < uni->u_len; i++) - { - len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char); - if (len <= 0) + for (i = 0U; i < uni->u_len; i++) { + len = nls->char2uni(&uni->u_name[i], uni->u_len - i, &uni_char); + if (!len) continue; + /* Invalid character, deal with it */ + if (len < 0) { + len = 1; + uni_char = '?'; + } - if (uni_char > max_val) - { + if (uni_char > max_val) { max_val = 0xffffU; ocu[0] = (uint8_t)0x10U; goto try_again; } - + if (max_val == 0xffffU) ocu[++u_len] = (uint8_t)(uni_char >> 8); ocu[++u_len] = (uint8_t)(uni_char & 0xffU); @@ -349,112 +333,108 @@ try_again: return u_len + 1; } -int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, int flen) +int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, + int flen) { - struct ustr filename, unifilename; - int len; + struct ustr *filename, *unifilename; + int len = 0; - if (udf_build_ustr_exact(&unifilename, sname, flen)) - { + filename = kmalloc(sizeof(struct ustr), GFP_NOFS); + if (!filename) return 0; - } - if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) - { - if (!udf_CS0toUTF8(&filename, &unifilename) ) - { - udf_debug("Failed in udf_get_filename: sname = %s\n", sname); - return 0; + unifilename = kmalloc(sizeof(struct ustr), GFP_NOFS); + if (!unifilename) + goto out1; + + if (udf_build_ustr_exact(unifilename, sname, flen)) + goto out2; + + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { + if (!udf_CS0toUTF8(filename, unifilename)) { + udf_debug("Failed in udf_get_filename: sname = %s\n", + sname); + goto out2; } - } - else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - { - if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename) ) - { - udf_debug("Failed in udf_get_filename: sname = %s\n", sname); - return 0; + } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { + if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, filename, + unifilename)) { + udf_debug("Failed in udf_get_filename: sname = %s\n", + sname); + goto out2; } - } - else - return 0; - - if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len, - unifilename.u_name, unifilename.u_len))) - { - return len; - } - return 0; + } else + goto out2; + + len = udf_translate_to_linux(dname, filename->u_name, filename->u_len, + unifilename->u_name, unifilename->u_len); +out2: + kfree(unifilename); +out1: + kfree(filename); + return len; } -int udf_put_filename(struct super_block *sb, const uint8_t *sname, uint8_t *dname, int flen) +int udf_put_filename(struct super_block *sb, const uint8_t *sname, + uint8_t *dname, int flen) { struct ustr unifilename; int namelen; - if ( !(udf_char_to_ustr(&unifilename, sname, flen)) ) - { + if (!udf_char_to_ustr(&unifilename, sname, flen)) return 0; - } - if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) - { - if ( !(namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN)) ) - { + if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8)) { + namelen = udf_UTF8toCS0(dname, &unifilename, UDF_NAME_LEN); + if (!namelen) return 0; - } - } - else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) - { - if ( !(namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, &unifilename, UDF_NAME_LEN)) ) - { + } else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) { + namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, dname, + &unifilename, UDF_NAME_LEN); + if (!namelen) return 0; - } - } - else + } else return 0; return namelen; } #define ILLEGAL_CHAR_MARK '_' -#define EXT_MARK '.' -#define CRC_MARK '#' -#define EXT_SIZE 5 +#define EXT_MARK '.' +#define CRC_MARK '#' +#define EXT_SIZE 5 -static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen) +static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, + int udfLen, uint8_t *fidName, + int fidNameLen) { - int index, newIndex = 0, needsCRC = 0; + int index, newIndex = 0, needsCRC = 0; int extIndex = 0, newExtIndex = 0, hasExt = 0; unsigned short valueCRC; uint8_t curr; const uint8_t hexChar[] = "0123456789ABCDEF"; - if (udfName[0] == '.' && (udfLen == 1 || - (udfLen == 2 && udfName[1] == '.'))) - { + if (udfName[0] == '.' && + (udfLen == 1 || (udfLen == 2 && udfName[1] == '.'))) { needsCRC = 1; newIndex = udfLen; memcpy(newName, udfName, udfLen); - } - else - { - for (index = 0; index < udfLen; index++) - { + } else { + for (index = 0; index < udfLen; index++) { curr = udfName[index]; - if (curr == '/' || curr == 0) - { + if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; - while (index+1 < udfLen && (udfName[index+1] == '/' || - udfName[index+1] == 0)) + while (index + 1 < udfLen && + (udfName[index + 1] == '/' || + udfName[index + 1] == 0)) index++; } - if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE) - { + if (curr == EXT_MARK && + (udfLen - index - 1) <= EXT_SIZE) { if (udfLen == index + 1) hasExt = 0; - else - { + else { hasExt = 1; extIndex = index; newExtIndex = newIndex; @@ -466,26 +446,24 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen needsCRC = 1; } } - if (needsCRC) - { + if (needsCRC) { uint8_t ext[EXT_SIZE]; int localExtIndex = 0; - if (hasExt) - { + if (hasExt) { int maxFilenameLen; - for(index = 0; index<EXT_SIZE && extIndex + index +1 < udfLen; - index++ ) - { + for (index = 0; + index < EXT_SIZE && extIndex + index + 1 < udfLen; + index++) { curr = udfName[extIndex + index + 1]; - if (curr == '/' || curr == 0) - { + if (curr == '/' || curr == 0) { needsCRC = 1; curr = ILLEGAL_CHAR_MARK; - while(extIndex + index + 2 < udfLen && (index + 1 < EXT_SIZE - && (udfName[extIndex + index + 2] == '/' || - udfName[extIndex + index + 2] == 0))) + while (extIndex + index + 2 < udfLen && + (index + 1 < EXT_SIZE && + (udfName[extIndex + index + 2] == '/' || + udfName[extIndex + index + 2] == 0))) index++; } ext[localExtIndex++] = curr; @@ -495,22 +473,21 @@ static int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen newIndex = maxFilenameLen; else newIndex = newExtIndex; - } - else if (newIndex > 250) + } else if (newIndex > 250) newIndex = 250; newName[newIndex++] = CRC_MARK; - valueCRC = udf_crc(fidName, fidNameLen, 0); + valueCRC = crc_itu_t(0, fidName, fidNameLen); 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) - { + if (hasExt) { newName[newIndex++] = EXT_MARK; - for (index = 0;index < localExtIndex ;index++ ) + for (index = 0; index < localExtIndex; index++) newName[newIndex++] = ext[index]; } } + return newIndex; } |
