diff options
Diffstat (limited to 'fs/udf/super.c')
| -rw-r--r-- | fs/udf/super.c | 2397 |
1 files changed, 1503 insertions, 894 deletions
diff --git a/fs/udf/super.c b/fs/udf/super.c index c68a6e730b9..3286db047a4 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -33,8 +33,8 @@ * 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) */ @@ -48,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" @@ -70,53 +74,73 @@ #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_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 *, - kernel_lb_addr *); -static int udf_load_partdesc(struct super_block *, struct buffer_head *); + struct kernel_lb_addr *); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); static int udf_statfs(struct dentry *, struct kstatfs *); +static int udf_show_options(struct seq_file *, struct dentry *); + +struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) +{ + struct logicalVolIntegrityDesc *lvid; + unsigned int partnum; + unsigned int offset; + + if (!UDF_SB(sb)->s_lvid_bh) + return NULL; + lvid = (struct logicalVolIntegrityDesc *)UDF_SB(sb)->s_lvid_bh->b_data; + partnum = le32_to_cpu(lvid->numOfPartitions); + if ((sb->s_blocksize - sizeof(struct logicalVolIntegrityDescImpUse) - + offsetof(struct logicalVolIntegrityDesc, impUse)) / + (2 * sizeof(uint32_t)) < partnum) { + udf_err(sb, "Logical volume integrity descriptor corrupted " + "(numOfPartitions = %u)!\n", partnum); + return NULL; + } + /* The offset is to skip freeSpaceTable and sizeTable arrays */ + offset = partnum * 2 * sizeof(uint32_t); + return (struct logicalVolIntegrityDescImpUse *)&(lvid->impUse[offset]); +} /* UDF filesystem type */ -static int udf_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, - struct vfsmount *mnt) +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, mnt); + 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 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, GFP_KERNEL); + ei = kmem_cache_alloc(udf_inode_cachep, GFP_KERNEL); if (!ei) return NULL; @@ -125,16 +149,25 @@ static struct inode *udf_alloc_inode(struct super_block *sb) 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, struct kmem_cache *cachep, unsigned long flags) +static void udf_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, udf_i_callback); +} + +static void init_once(void *foo) { struct udf_inode_info *ei = (struct udf_inode_info *)foo; @@ -142,7 +175,7 @@ static void init_once(void *foo, struct kmem_cache *cachep, unsigned long flags) 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), @@ -156,6 +189,11 @@ static int init_inodecache(void) static void destroy_inodecache(void) { + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); kmem_cache_destroy(udf_inode_cachep); } @@ -164,12 +202,12 @@ static const 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, + .evict_inode = udf_evict_inode, .put_super = udf_put_super, - .write_super = udf_write_super, + .sync_fs = udf_sync_fs, .statfs = udf_statfs, .remount_fs = udf_remount_fs, + .show_options = udf_show_options, }; struct udf_options { @@ -183,9 +221,11 @@ 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; }; @@ -218,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 * @@ -229,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. @@ -278,10 +448,11 @@ 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_uforget, Opt_uignore, Opt_gforget, Opt_gignore + Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore, + Opt_fmode, Opt_dmode }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_novrs, "novrs"}, {Opt_nostrict, "nostrict"}, {Opt_bs, "bs=%u"}, @@ -307,16 +478,18 @@ static match_table_t tokens = { {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; @@ -332,6 +505,7 @@ static int udf_parse_options(char *options, struct udf_options *uopt) while ((p = strsep(&options, ",")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; + unsigned n; if (!*p) continue; @@ -339,10 +513,15 @@ static int udf_parse_options(char *options, struct udf_options *uopt) switch (token) { case Opt_novrs: uopt->novrs = 1; + break; case Opt_bs: if (match_int(&args[0], &option)) return 0; - uopt->blocksize = option; + 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); @@ -365,13 +544,17 @@ static int udf_parse_options(char *options, struct udf_options *uopt) case Opt_gid: if (match_int(args, &option)) return 0; - uopt->gid = option; + 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 = option; + 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: @@ -386,11 +569,15 @@ static int udf_parse_options(char *options, struct udf_options *uopt) 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)) @@ -438,122 +625,104 @@ static int udf_parse_options(char *options, struct udf_options *uopt) 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: - printk(KERN_ERR "udf: bad mount option \"%s\" " - "or missing value\n", p); + 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) { 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; + struct udf_sb_info *sbi; - /* Block size must be a multiple of 512 */ - if (sb->s_blocksize & 511) - return 0; - + 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) @@ -561,46 +730,54 @@ static int 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) { - brelse(bh); - break; - } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) { - iso9660 = sector; + 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"); + udf_debug("ISO9660 Primary Volume Descriptor found\n"); break; case 2: - udf_debug - ("ISO9660 Supplementary Volume Descriptor found\n"); + udf_debug("ISO9660 Supplementary Volume Descriptor found\n"); break; case 3: - udf_debug - ("ISO9660 Volume Partition Descriptor found\n"); + udf_debug("ISO9660 Volume Partition Descriptor found\n"); break; case 255: - udf_debug - ("ISO9660 Volume Descriptor Set Terminator found\n"); + 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)) { + } 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; } brelse(bh); } @@ -609,151 +786,25 @@ static int udf_vrs(struct super_block *sb, int silent) 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 < ARRAY_SIZE(last); 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); - brelse(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); - brelse(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); - brelse(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); - brelse(bh); - - if (ident == TAG_IDENT_AVDP && location == 256) - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - } - } - - for (i = 0; i < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); 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 { - brelse(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); + bh = udf_read_ptagged(sb, fileset, 0, &ident); if (!bh) { return 1; @@ -764,22 +815,27 @@ static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, ker } - 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; + for (newfileset.partitionReferenceNum = sbi->s_partitions - 1; (newfileset.partitionReferenceNum != 0xFFFF && fileset->logicalBlockNum == 0xFFFFFFFF && fileset->partitionReferenceNum == 0xFFFF); newfileset.partitionReferenceNum--) { - lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum); + lastblock = sbi->s_partmaps + [newfileset.partitionReferenceNum] + .s_partition_len; newfileset.logicalBlockNum = 0; do { - bh = udf_read_ptagged(sb, newfileset, 0, &ident); + bh = udf_read_ptagged(sb, &newfileset, 0, + &ident); if (!bh) { newfileset.logicalBlockNum++; continue; @@ -789,11 +845,12 @@ static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, ker case TAG_IDENT_SBD: { struct spaceBitmapDesc *sp; - sp = (struct spaceBitmapDesc *)bh->b_data; + sp = (struct spaceBitmapDesc *) + bh->b_data; newfileset.logicalBlockNum += 1 + ((le32_to_cpu(sp->numOfBytes) + - sizeof(struct spaceBitmapDesc) - 1) - >> sb->s_blocksize_bits); + sizeof(struct spaceBitmapDesc) + - 1) >> sb->s_blocksize_bits); brelse(bh); break; } @@ -818,7 +875,7 @@ static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, ker fileset->logicalBlockNum, fileset->partitionReferenceNum); - UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum; + sbi->s_partition = fileset->partitionReferenceNum; udf_load_fileset(sb, bh, root); brelse(bh); return 0; @@ -826,44 +883,155 @@ static int udf_find_fileset(struct super_block *sb, kernel_lb_addr *fileset, ker 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; + } + + 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; } } - if (!udf_build_ustr(&instr, pvoldesc->volSetIdent, 128)) { - if (udf_CS0toUTF8(&outstr, &instr)) - udf_debug("volSetIdent[] = '%s'\n", outstr.u_name); + /* + * 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) + struct kernel_lb_addr *root) { struct fileSetDesc *fset; @@ -871,180 +1039,489 @@ static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, *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); } -static int udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) +int udf_compute_nr_groups(struct super_block *sb, u32 partition) +{ + struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition]; + return DIV_ROUND_UP(map->s_partition_len + + (sizeof(struct spaceBitmapDesc) << 3), + sb->s_blocksize * 8); +} + +static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index) +{ + struct udf_bitmap *bitmap; + int nr_groups; + int size; + + nr_groups = udf_compute_nr_groups(sb, index); + size = sizeof(struct udf_bitmap) + + (sizeof(struct buffer_head *) * nr_groups); + + if (size <= PAGE_SIZE) + bitmap = kzalloc(size, GFP_KERNEL); + else + bitmap = vzalloc(size); /* TODO: get rid of vzalloc */ + + if (bitmap == 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 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); - for (i = 0; i < UDF_SB_NUMPARTS(sb); i++) { + /* 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", - 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 = { - .logicalBlockNum = le32_to_cpu(phd->unallocSpaceTable.extPosition), - .partitionReferenceNum = i, - }; - - UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table = - udf_iget(sb, loc); - if (!UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table) { - udf_debug("cannot load unallocSpaceTable (part %d)\n", - i); - return 1; - } - 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 = { - .logicalBlockNum = le32_to_cpu(phd->freedSpaceTable.extPosition), - .partitionReferenceNum = i, - }; - - UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table = - udf_iget(sb, loc); - if (!UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table) { - udf_debug("cannot load freedSpaceTable (part %d)\n", - i); - return 1; - } - 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); - } - } - } + map->s_partition_num, partitionNumber); + if (map->s_partition_num == partitionNumber && + (map->s_partition_type == UDF_TYPE1_MAP15 || + map->s_partition_type == UDF_SPARABLE_MAP15)) break; - } } - if (i == UDF_SB_NUMPARTS(sb)) { + + if (i >= sbi->s_partitions) { udf_debug("Partition (%d) not found in partition map\n", - le16_to_cpu(p->partitionNumber)); + partitionNumber); + ret = 0; + goto out_bh; + } + + ret = udf_fill_partdesc_info(sb, p, i); + if (ret < 0) + goto out_bh; + + /* + * Now rescan for VIRTUAL or METADATA partitions when SPARABLE and + * PHYSICAL partitions are already set up + */ + type1_idx = i; +#ifdef UDFFS_DEBUG + map = NULL; /* supress 'maybe used uninitialized' warning */ +#endif + for (i = 0; i < sbi->s_partitions; i++) { + map = &sbi->s_partmaps[i]; + + if (map->s_partition_num == partitionNumber && + (map->s_partition_type == UDF_VIRTUAL_MAP15 || + map->s_partition_type == UDF_VIRTUAL_MAP20 || + map->s_partition_type == UDF_METADATA_MAP25)) + break; + } + + if (i >= sbi->s_partitions) { + ret = 0; + goto out_bh; + } + + ret = udf_fill_partdesc_info(sb, p, i); + if (ret < 0) + goto out_bh; + + if (map->s_partition_type == UDF_METADATA_MAP25) { + ret = udf_load_metadata_files(sb, i); + if (ret < 0) { + udf_err(sb, "error loading MetaData partition map %d\n", + i); + goto out_bh; + } } 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)); + /* + * 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 (spm->numSparingTables > 4) { + udf_err(sb, "error loading logical volume descriptor: " + "Too many sparing tables (%d)\n", + (int)spm->numSparingTables); + return -EIO; } + + for (i = 0; i < spm->numSparingTables; i++) { + loc = le32_to_cpu(spm->locSparingTable[i]); + bh = udf_read_tagged(sb, loc, loc, &ident); + if (!bh) + continue; + + st = (struct sparingTable *)bh->b_data; + if (ident != 0 || + strncmp(st->sparingIdent.ident, UDF_ID_SPARING, + strlen(UDF_ID_SPARING)) || + sizeof(*st) + le16_to_cpu(st->reallocationTableLen) > + sb->s_blocksize) { + brelse(bh); + continue; + } + + sdata->s_spar_map[i] = bh; + } + map->s_partition_func = udf_get_pblock_spar15; return 0; } -static int udf_load_logicalvol(struct super_block *sb, 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)); + ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); + if (ret) + goto out_bh; 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; + 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 *)&(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; + 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 *)&(lvd->partitionMaps[offset]); - if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL))) { - if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0150) { - UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15; - UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15; - } else if (le16_to_cpu(((__le16 *)upm2->partIdent.identSuffix)[0]) == 0x0200) { - UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20; - UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20; - } - } else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { - uint32_t loc; - uint16_t ident; - struct sparingTable *st; - struct sparablePartitionMap *spm = (struct sparablePartitionMap *)&(lvd->partitionMaps[offset]); - - UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15; - UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength); - for (j = 0; j < spm->numSparingTables; j++) { - loc = le32_to_cpu(spm->locSparingTable[j]); - UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = - udf_read_tagged(sb, loc, loc, &ident); - if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL) { - st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data; - if (ident != 0 || - strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING))) { - brelse(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]); - UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL; - } - } + 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; } - UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15; + } 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); + 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]); + 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", @@ -1053,116 +1530,134 @@ static int udf_load_logicalvol(struct super_block *sb, struct buffer_head *bh, } 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; + sbi->s_lvid_bh = bh; + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; - if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength) - udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt)); + if (lvid->nextIntegrityExt.extLength) + udf_load_logicalvolint(sb, + leea_to_cpu(lvid->nextIntegrityExt)); - if (UDF_SB_LVIDBH(sb) != bh) + if (sbi->s_lvid_bh != bh) brelse(bh); loc.extLength -= sb->s_blocksize; loc.extLocation++; } - if (UDF_SB_LVIDBH(sb) != 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; 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 */ + /* + * 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; + 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 */ - 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; + 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_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; + 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 */ - if (!vds[VDS_POS_PARTITION_DESC].block) - vds[VDS_POS_PARTITION_DESC].block = block; + curr = &vds[VDS_POS_PARTITION_DESC]; + if (!curr->block) + curr->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; + 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 */ - 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; + curr = &vds[VDS_POS_UNALLOC_SPACE_DESC]; + if (vdsn >= curr->volDescSeqNum) { + curr->volDescSeqNum = vdsn; + curr->block = block; } break; case TAG_IDENT_TD: /* ISO 13346 3/10.9 */ @@ -1171,40 +1666,43 @@ static int udf_process_sequence(struct super_block *sb, long block, long lastblo block = next_s; lastblock = next_e; next_s = next_e = 0; - } else { + } else done = 1; - } break; } brelse(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; - if (udf_load_partdesc(sb, bh)) { - brelse(bh); - return 1; - } - 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) - if (udf_load_partdesc(sb, bh2)) { - brelse(bh); - brelse(bh2); - return 1; - } - brelse(bh2); - } - } - 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; } } @@ -1212,249 +1710,370 @@ static int udf_process_sequence(struct super_block *sb, long block, long lastblo } /* - * 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; - } else { - return !block; + 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; } + 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 < ARRAY_SIZE(UDF_SB_ANCHOR(sb)); 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; + 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; +} - /* 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; +/* + * 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; + } + } - /* 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; + /* Finally try block 512 in case media is open */ + return udf_check_anchor_block(sb, sbi->s_session + 512, fileset); +} - brelse(bh); +/* + * 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; - /* 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; - } - } - } + ret = udf_scan_anchors(sb, &lastblock, fileset); + if (ret != -EAGAIN) + goto out; - if (i == ARRAY_SIZE(UDF_SB_ANCHOR(sb))) { - udf_debug("No Anchor block found\n"); - return 1; - } else - udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]); - - for (i = 0; i < UDF_SB_NUMPARTS(sb); i++) { - kernel_lb_addr uninitialized_var(ino); - switch (UDF_SB_PARTTYPE(sb, i)) { - case UDF_VIRTUAL_MAP15: - case UDF_VIRTUAL_MAP20: - if (!UDF_SB_LASTBLOCK(sb)) { - UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb); - udf_find_anchor(sb); - } + /* 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_LASTBLOCK(sb)) { - udf_debug("Unable to determine Lastblock (For " - "Virtual Partition)\n"); - return 1; - } + 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; +} - 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; - } - } +/* + * 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 (j == UDF_SB_NUMPARTS(sb)) - return 1; - - if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino))) - return 1; - - if (UDF_SB_PARTTYPE(sb, i) == UDF_VIRTUAL_MAP15) { - UDF_SB_TYPEVIRT(sb, i).s_start_offset = - udf_ext0_offset(UDF_SB_VAT(sb)); - UDF_SB_TYPEVIRT(sb, i).s_num_entries = - (UDF_SB_VAT(sb)->i_size - 36) >> 2; - } else if (UDF_SB_PARTTYPE(sb, i) == UDF_VIRTUAL_MAP20) { - struct buffer_head *bh = NULL; - uint32_t pos; - - pos = udf_block_map(UDF_SB_VAT(sb), 0); - bh = sb_bread(sb, pos); - if (!bh) - return 1; - 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; - brelse(bh); - } - UDF_SB_PARTROOT(sb, i) = udf_get_pblock(sb, 0, i, 0); - UDF_SB_PARTLEN(sb, i) = UDF_SB_PARTLEN(sb, - ino.partitionReferenceNum); + 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; - - UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; - UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) - UDF_SB_LVID(sb)->recordingDateAndTime = - cpu_to_lets(cpu_time); - UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN; - - UDF_SB_LVID(sb)->descTag.descCRC = - cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag), - le16_to_cpu(UDF_SB_LVID(sb)->descTag. - descCRCLength), 0)); - - UDF_SB_LVID(sb)->descTag.tagChecksum = 0; - for (i = 0; i < 16; i++) - if (i != 4) - UDF_SB_LVID(sb)->descTag.tagChecksum += - ((uint8_t *) & - (UDF_SB_LVID(sb)->descTag))[i]; - - mark_buffer_dirty(UDF_SB_LVIDBH(sb)); - } + struct udf_sb_info *sbi = UDF_SB(sb); + struct buffer_head *bh = sbi->s_lvid_bh; + struct logicalVolIntegrityDesc *lvid; + struct logicalVolIntegrityDescImpUse *lvidiu; + + 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); } static void udf_close_lvid(struct super_block *sb) { - kernel_timestamp cpu_time; - int i; + struct udf_sb_info *sbi = UDF_SB(sb); + struct buffer_head *bh = sbi->s_lvid_bh; + struct logicalVolIntegrityDesc *lvid; + struct logicalVolIntegrityDescImpUse *lvidiu; + + 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); +} - if (UDF_SB_LVIDBH(sb) && - UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN) { - 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)); - } +u64 lvid_get_unique_id(struct super_block *sb) +{ + struct buffer_head *bh; + struct udf_sb_info *sbi = UDF_SB(sb); + struct logicalVolIntegrityDesc *lvid; + struct logicalVolHeaderDesc *lvhd; + u64 uniqueID; + u64 ret; + + bh = sbi->s_lvid_bh; + if (!bh) + return 0; + + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; + lvhd = (struct logicalVolHeaderDesc *)lvid->logicalVolContentsUse; + + mutex_lock(&sbi->s_alloc_mutex); + ret = uniqueID = le64_to_cpu(lvhd->uniqueID); + if (!(++uniqueID & 0xFFFFFFFF)) + uniqueID += 16; + lvhd->uniqueID = cpu_to_le64(uniqueID); + mutex_unlock(&sbi->s_alloc_mutex); + mark_buffer_dirty(bh); + + return ret; } -/* - * 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; + 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)); 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"); + udf_err(sb, "utf8 cannot be combined with iocharset\n"); goto error_out; } #ifdef CONFIG_UDF_NLS @@ -1472,64 +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; + 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) - UDF_SB_SESSION(sb) = udf_get_last_session(sb); + 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 (!lvidiu) { + ret = -EINVAL; + goto error_out; + } + minUDFReadRev = le16_to_cpu(lvidiu->minUDFReadRev); + minUDFWriteRev = le16_to_cpu(lvidiu->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); + 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; - } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) { - sb->s_flags |= MS_RDONLY; } - 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); @@ -1537,29 +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 (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_READ_ONLY) { - printk("UDF-fs: Partition marked readonly; forcing readonly mount\n"); - sb->s_flags |= MS_RDONLY; + 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"); + 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); + 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); @@ -1567,188 +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); + inode = udf_iget(sb, &rootdir); if (!inode) { - printk("UDF-fs: Error in udf_iget, block=%d, partition=%d\n", + 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); + sb->s_root = d_make_root(inode); if (!sb->s_root) { - printk("UDF-fs: Couldn't allocate root dentry\n"); - iput(inode); + 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++) - brelse(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); - brelse(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); - vsnprintf(error_buf, sizeof(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); - vsnprintf(error_buf, sizeof(error_buf), fmt, args); + + 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) { - 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++) - brelse(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); - brelse(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_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_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"); + udf_err(sb, "udf_count_free failed\n"); goto out; } else if (ident != TAG_IDENT_SBD) { brelse(bh); - printk(KERN_ERR "udf: udf_count_free failed\n"); + udf_err(sb, "udf_count_free failed\n"); goto out; } @@ -1758,16 +2366,13 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb, struct udf_bit 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--; - } + 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); + newblock = udf_get_lb_pblock(sb, &loc, ++block); bh = udf_tread(sb, newblock); if (!bh) { udf_debug("read failed\n"); @@ -1778,33 +2383,29 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb, struct udf_bit } } 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 elen; - kernel_lb_addr eloc; + struct kernel_lb_addr eloc; int8_t etype; struct extent_position epos; - lock_kernel(); - - epos.block = UDF_I_LOCATION(table); + mutex_lock(&UDF_SB(sb)->s_alloc_mutex); + epos.block = UDF_I(table)->i_location; epos.offset = sizeof(struct unallocSpaceEntry); epos.bh = NULL; - while ((etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) { + while ((etype = udf_next_aext(table, &epos, &eloc, &elen, 1)) != -1) accum += (elen >> table->i_sb->s_blocksize_bits); - } - brelse(epos.bh); - unlock_kernel(); + brelse(epos.bh); + mutex_unlock(&UDF_SB(sb)->s_alloc_mutex); return accum; } @@ -1812,10 +2413,17 @@ static unsigned int udf_count_free_table(struct super_block *sb, struct inode *t 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; } @@ -1824,24 +2432,25 @@ static unsigned int 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; |
