X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fudf%2Fsuper.c;h=6832135159b680e8563a9ebadd03ebf19eb8f80c;hb=52b5226f481c09cc499cc28b1e9347d314b340f1;hp=3afe7647f94a5d1f89a28dab9f425aef5c26806a;hpb=a9ca663578321695658675103c35452d8ce91d85;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/udf/super.c b/fs/udf/super.c index 3afe764..6832135 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -53,9 +53,12 @@ #include #include #include +#include +#include +#include +#include #include -#include #include "udf_sb.h" #include "udf_i.h" @@ -71,30 +74,27 @@ #define VDS_POS_TERMINATING_DESC 6 #define VDS_POS_LENGTH 7 +#define UDF_DEFAULT_BLOCKSIZE 2048 + static char error_buf[1024]; /* These are the "meat" - everything else is stuffing */ static int udf_fill_super(struct super_block *, void *, int); static void udf_put_super(struct super_block *); -static void udf_write_super(struct super_block *); +static int udf_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 vfsmount *); +static void udf_error(struct super_block *sb, const char *function, + const char *fmt, ...); struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct udf_sb_info *sbi) { @@ -145,7 +145,7 @@ static void udf_destroy_inode(struct inode *inode) kmem_cache_free(udf_inode_cachep, UDF_I(inode)); } -static void init_once(struct kmem_cache *cachep, void *foo) +static void init_once(void *foo) { struct udf_inode_info *ei = (struct udf_inode_info *)foo; @@ -178,9 +178,10 @@ static const struct super_operations udf_sb_ops = { .delete_inode = udf_delete_inode, .clear_inode = udf_clear_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 { @@ -197,6 +198,8 @@ struct udf_options { mode_t umask; gid_t gid; uid_t uid; + mode_t fmode; + mode_t dmode; struct nls_table *nls_map; }; @@ -236,7 +239,7 @@ static int udf_sb_alloc_partition_maps(struct super_block *sb, u32 count) sbi->s_partmaps = kcalloc(count, sizeof(struct udf_part_map), GFP_KERNEL); if (!sbi->s_partmaps) { - udf_error(sb, __FUNCTION__, + udf_error(sb, __func__, "Unable to allocate space for %d partition maps", count); sbi->s_partitions = 0; @@ -247,6 +250,59 @@ static int udf_sb_alloc_partition_maps(struct super_block *sb, u32 count) return 0; } +static int udf_show_options(struct seq_file *seq, struct vfsmount *mnt) +{ + struct super_block *sb = mnt->mnt_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", sbi->s_uid); + if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET)) + seq_printf(seq, ",gid=%u", sbi->s_gid); + if (sbi->s_umask != 0) + seq_printf(seq, ",umask=%o", sbi->s_umask); + if (sbi->s_fmode != UDF_INVALID_MODE) + seq_printf(seq, ",mode=%o", sbi->s_fmode); + if (sbi->s_dmode != UDF_INVALID_MODE) + seq_printf(seq, ",dmode=%o", 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 * @@ -258,6 +314,8 @@ static int udf_sb_alloc_partition_maps(struct super_block *sb, u32 count) * * 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. @@ -307,10 +365,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"}, @@ -336,16 +395,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; @@ -368,10 +429,12 @@ 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; + uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET); break; case Opt_unhide: uopt->flags |= (1 << UDF_FLAG_UNHIDE); @@ -415,11 +478,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)) @@ -467,6 +534,16 @@ 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); @@ -476,17 +553,6 @@ static int udf_parse_options(char *options, struct udf_options *uopt) return 1; } -static 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; @@ -496,14 +562,19 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options) 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; + lock_kernel(); 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; if (sbi->s_lvid_bh) { int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev); @@ -511,70 +582,32 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options) *flags |= MS_RDONLY; } - if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) { + unlock_kernel(); return 0; + } if (*flags & MS_RDONLY) udf_close_lvid(sb); else udf_open_lvid(sb); + unlock_kernel(); return 0; } -/* - * udf_set_blocksize - * - * PURPOSE - * Set the block size to be used in all transfers. - * - * DESCRIPTION - * To allow room for a DMA transfer, it is best to guess big when unsure. - * This routine picks 2048 bytes as the blocksize when guessing. This - * should be adequate until devices with larger block sizes become common. - * - * Note that the Linux kernel can currently only deal with blocksizes of - * 512, 1024, 2048, 4096, and 8192 bytes. - * - * PRE-CONDITIONS - * sb Pointer to _locked_ superblock. - * - * POST-CONDITIONS - * sb->s_blocksize Blocksize. - * sb->s_blocksize_bits log2 of blocksize. - * 0 Blocksize is valid. - * 1 Blocksize is invalid. - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static int udf_set_blocksize(struct super_block *sb, int bsize) -{ - 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 = 32768; 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 @@ -583,7 +616,8 @@ static int udf_vrs(struct super_block *sb, int silent) 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); + (unsigned int)(sector >> sb->s_blocksize_bits), + sb->s_blocksize); /* Process the sequence (if applicable) */ for (; !nsr02 && !nsr03; sector += sectorsize) { /* Read a block */ @@ -600,7 +634,6 @@ static int udf_vrs(struct super_block *sb, int silent) break; } else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN)) { - iso9660 = sector; switch (vsd->structType) { case 0: udf_debug("ISO9660 Boot Record found\n"); @@ -652,172 +685,9 @@ static int udf_vrs(struct super_block *sb, int silent) return 0; } -/* - * udf_find_anchor - * - * PURPOSE - * Find an anchor volume descriptor. - * - * PRE-CONDITIONS - * sb Pointer to _locked_ superblock. - * lastblock Last block on media. - * - * POST-CONDITIONS - * 1 if not found, 0 if ok - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ -static void udf_find_anchor(struct super_block *sb) -{ - int lastblock; - struct buffer_head *bh = NULL; - uint16_t ident; - uint32_t location; - int i; - struct udf_sb_info *sbi; - - sbi = UDF_SB(sb); - lastblock = sbi->s_last_block; - - 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++) { - ident = location = 0; - if (last[i] >= 0) { - bh = sb_bread(sb, last[i]); - if (bh) { - tag *t = (tag *)bh->b_data; - ident = le16_to_cpu(t->tagIdent); - location = le32_to_cpu(t->tagLocation); - brelse(bh); - } - } - - if (ident == TAG_IDENT_AVDP) { - if (location == last[i] - sbi->s_session) { - lastblock = last[i] - sbi->s_session; - sbi->s_anchor[0] = lastblock; - sbi->s_anchor[1] = lastblock - 256; - } else if (location == - udf_variable_to_fixed(last[i]) - - sbi->s_session) { - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - lastblock = - udf_variable_to_fixed(last[i]) - - sbi->s_session; - sbi->s_anchor[0] = lastblock; - sbi->s_anchor[1] = lastblock - 256 - - sbi->s_session; - } 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]; - sbi->s_anchor[3] = 512; - } else { - ident = location = 0; - if (last[i] >= 256) { - bh = sb_bread(sb, last[i] - 256); - if (bh) { - tag *t = (tag *)bh->b_data; - ident = le16_to_cpu( - t->tagIdent); - location = le32_to_cpu( - t->tagLocation); - brelse(bh); - } - } - - if (ident == TAG_IDENT_AVDP && - location == last[i] - 256 - - sbi->s_session) { - lastblock = last[i]; - sbi->s_anchor[1] = last[i] - 256; - } else { - ident = location = 0; - if (last[i] >= 312 + sbi->s_session) { - bh = sb_bread(sb, - last[i] - 312 - - sbi->s_session); - if (bh) { - tag *t = (tag *) - bh->b_data; - ident = le16_to_cpu( - t->tagIdent); - location = le32_to_cpu( - t->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]); - sbi->s_anchor[1] = lastblock - 256; - } - } - } - } - } - - if (!lastblock) { - /* We haven't found the lastblock. check 312 */ - bh = sb_bread(sb, 312 + sbi->s_session); - if (bh) { - tag *t = (tag *)bh->b_data; - ident = le16_to_cpu(t->tagIdent); - location = le32_to_cpu(t->tagLocation); - brelse(bh); - - if (ident == TAG_IDENT_AVDP && location == 256) - UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - } - } - - for (i = 0; i < ARRAY_SIZE(sbi->s_anchor); i++) { - if (sbi->s_anchor[i]) { - bh = udf_read_tagged(sb, sbi->s_anchor[i], - sbi->s_anchor[i], &ident); - if (!bh) - sbi->s_anchor[i] = 0; - else { - brelse(bh); - if ((ident != TAG_IDENT_AVDP) && - (i || (ident != TAG_IDENT_FE && - ident != TAG_IDENT_EFE))) - sbi->s_anchor[i] = 0; - } - } - } - - sbi->s_last_block = lastblock; -} - static int udf_find_fileset(struct super_block *sb, - kernel_lb_addr *fileset, - kernel_lb_addr *root) + struct kernel_lb_addr *fileset, + struct kernel_lb_addr *root) { struct buffer_head *bh = NULL; long lastblock; @@ -826,7 +696,7 @@ static int udf_find_fileset(struct super_block *sb, 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; @@ -840,7 +710,7 @@ static int udf_find_fileset(struct super_block *sb, sbi = UDF_SB(sb); if (!bh) { /* Search backwards through the partitions */ - kernel_lb_addr newfileset; + struct kernel_lb_addr newfileset; /* --> cvg: FIXME - is it reasonable? */ return 1; @@ -856,7 +726,7 @@ static int udf_find_fileset(struct super_block *sb, newfileset.logicalBlockNum = 0; do { - bh = udf_read_ptagged(sb, newfileset, 0, + bh = udf_read_ptagged(sb, &newfileset, 0, &ident); if (!bh) { newfileset.logicalBlockNum++; @@ -905,44 +775,159 @@ static int udf_find_fileset(struct super_block *sb, return 1; } -static void udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh) +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 = 1; + + instr = kmalloc(sizeof(struct ustr), GFP_NOFS); + if (!instr) + return 1; + + outstr = kmalloc(sizeof(struct ustr), GFP_NOFS); + if (!outstr) + goto out1; + + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + goto out2; + + BUG_ON(ident != TAG_IDENT_PVD); 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" + 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", - recording, recording_usec, - ts.year, ts.month, ts.day, ts.hour, - ts.minute, ts.typeAndTimezone); - UDF_SB(sb)->s_record_time.tv_sec = recording; - UDF_SB(sb)->s_record_time.tv_nsec = recording_usec * 1000; + 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); + 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); + if (!udf_build_ustr(instr, pvoldesc->volSetIdent, 128)) + if (udf_CS0toUTF8(outstr, instr)) + udf_debug("volSetIdent[] = '%s'\n", outstr->u_name); + + brelse(bh); + ret = 0; +out2: + kfree(outstr); +out1: + kfree(instr); + return ret; +} + +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; + int fe_error = 0; + + map = &sbi->s_partmaps[partition]; + mdata = &map->s_type_specific.s_metadata; + + /* metadata address */ + addr.logicalBlockNum = mdata->s_meta_file_loc; + addr.partitionReferenceNum = map->s_partition_num; + + udf_debug("Metadata file location: block = %d part = %d\n", + addr.logicalBlockNum, addr.partitionReferenceNum); + + mdata->s_metadata_fe = udf_iget(sb, &addr); + + if (mdata->s_metadata_fe == NULL) { + udf_warning(sb, __func__, "metadata inode efe not found, " + "will try mirror inode."); + fe_error = 1; + } else if (UDF_I(mdata->s_metadata_fe)->i_alloc_type != + ICBTAG_FLAG_AD_SHORT) { + udf_warning(sb, __func__, "metadata inode efe does not have " + "short allocation descriptors!"); + fe_error = 1; + iput(mdata->s_metadata_fe); + mdata->s_metadata_fe = NULL; + } + + /* mirror file entry */ + addr.logicalBlockNum = mdata->s_mirror_file_loc; + addr.partitionReferenceNum = map->s_partition_num; + + udf_debug("Mirror metadata file location: block = %d part = %d\n", + addr.logicalBlockNum, addr.partitionReferenceNum); + + mdata->s_mirror_fe = udf_iget(sb, &addr); + + if (mdata->s_mirror_fe == NULL) { + if (fe_error) { + udf_error(sb, __func__, "mirror inode efe not found " + "and metadata inode is missing too, exiting..."); + goto error_exit; + } else + udf_warning(sb, __func__, "mirror inode efe not found," + " but metadata inode is OK"); + } else if (UDF_I(mdata->s_mirror_fe)->i_alloc_type != + ICBTAG_FLAG_AD_SHORT) { + udf_warning(sb, __func__, "mirror inode efe does not have " + "short allocation descriptors!"); + iput(mdata->s_mirror_fe); + mdata->s_mirror_fe = NULL; + if (fe_error) + goto error_exit; + } + + /* + * 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_warning(sb, __func__, "bitmap inode efe " + "not found but it's ok since the disc" + " is mounted read-only"); + else { + udf_error(sb, __func__, "bitmap inode efe not " + "found and attempted read-write mount"); + goto error_exit; + } + } + } + + udf_debug("udf_load_metadata_files Ok\n"); + + return 0; + +error_exit: + return 1; } 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; @@ -959,10 +944,9 @@ static void udf_load_fileset(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 (map->s_partition_len + - (sizeof(struct spaceBitmapDesc) << 3) + - (sb->s_blocksize * 8) - 1) / - (sb->s_blocksize * 8); + 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) @@ -981,7 +965,7 @@ static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index) bitmap = vmalloc(size); /* TODO: get rid of vmalloc */ if (bitmap == NULL) { - udf_error(sb, __FUNCTION__, + udf_error(sb, __func__, "Unable to allocate space for bitmap " "and %d buffer_head pointers", nr_groups); return NULL; @@ -993,147 +977,263 @@ static struct udf_bitmap *udf_sb_alloc_bitmap(struct super_block *sb, u32 index) return bitmap; } -static int udf_load_partdesc(struct super_block *sb, struct buffer_head *bh) +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 1; + } + 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 1; + map->s_uspace.s_bitmap = bitmap; + bitmap->s_extLength = le32_to_cpu( + phd->unallocSpaceBitmap.extLength); + 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 1; + } + + 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 1; + map->s_fspace.s_bitmap = bitmap; + bitmap->s_extLength = le32_to_cpu( + phd->freedSpaceBitmap.extLength); + 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 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 kernel_lb_addr ino; + struct buffer_head *bh = NULL; + struct udf_inode_info *vati; + uint32_t pos; + struct virtualAllocationTable20 *vat20; + + /* VAT file entry is in the last recorded block */ + ino.partitionReferenceNum = type1_index; + ino.logicalBlockNum = sbi->s_last_block - map->s_partition_root; + sbi->s_vat_inode = udf_iget(sb, &ino); + if (!sbi->s_vat_inode) + return 1; + + 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 1; + 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; +} + +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; + struct udf_sb_info *sbi = UDF_SB(sb); + int i, type1_idx; + uint16_t partitionNumber; + uint16_t ident; + int ret = 0; + + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + return 1; + if (ident != TAG_IDENT_PD) + goto out_bh; p = (struct partitionDesc *)bh->b_data; - sbi = UDF_SB(sb); + partitionNumber = le16_to_cpu(p->partitionNumber); + /* First scan for TYPE1, SPARABLE and METADATA partitions */ for (i = 0; i < sbi->s_partitions; i++) { map = &sbi->s_partmaps[i]; udf_debug("Searching map: (%d == %d)\n", - map->s_partition_num, - le16_to_cpu(p->partitionNumber)); - if (map->s_partition_num == - le16_to_cpu(p->partitionNumber)) { - 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; - - 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, - }; - - map->s_uspace.s_table = - udf_iget(sb, loc); - if (!map->s_uspace.s_table) { - udf_debug("cannot load unallocSpaceTable (part %d)\n", i); - return 1; - } - map->s_partition_flags |= - UDF_PART_FLAG_UNALLOC_TABLE; - udf_debug("unallocSpaceTable (part %d) @ %ld\n", - i, map->s_uspace.s_table->i_ino); - } - if (phd->unallocSpaceBitmap.extLength) { - struct udf_bitmap *bitmap = - udf_sb_alloc_bitmap(sb, i); - map->s_uspace.s_bitmap = bitmap; - if (bitmap != NULL) { - bitmap->s_extLength = - le32_to_cpu(phd->unallocSpaceBitmap.extLength); - 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", - i, 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, - }; - - map->s_fspace.s_table = - udf_iget(sb, loc); - if (!map->s_fspace.s_table) { - udf_debug("cannot load freedSpaceTable (part %d)\n", i); - return 1; - } - map->s_partition_flags |= - UDF_PART_FLAG_FREED_TABLE; - udf_debug("freedSpaceTable (part %d) @ %ld\n", - i, map->s_fspace.s_table->i_ino); - } - if (phd->freedSpaceBitmap.extLength) { - struct udf_bitmap *bitmap = - udf_sb_alloc_bitmap(sb, i); - map->s_fspace.s_bitmap = bitmap; - if (bitmap != NULL) { - bitmap->s_extLength = - le32_to_cpu(phd->freedSpaceBitmap.extLength); - 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", - i, 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 == sbi->s_partitions) + + if (i >= sbi->s_partitions) { udf_debug("Partition (%d) not found in partition map\n", - le16_to_cpu(p->partitionNumber)); - else - udf_debug("Partition (%d:%d type %x) starts at physical %d, " - "block length %d\n", - le16_to_cpu(p->partitionNumber), i, - map->s_partition_type, - map->s_partition_root, - map->s_partition_len); - return 0; + partitionNumber); + goto out_bh; + } + + ret = udf_fill_partdesc_info(sb, p, i); + + /* + * Now rescan for VIRTUAL or METADATA partitions when SPARABLE and + * PHYSICAL partitions are already set up + */ + type1_idx = i; + 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) + goto out_bh; + + ret = udf_fill_partdesc_info(sb, p, i); + if (ret) + goto out_bh; + + if (map->s_partition_type == UDF_METADATA_MAP25) { + ret = udf_load_metadata_files(sb, i); + if (ret) { + printk(KERN_ERR "UDF-fs: error loading MetaData " + "partition map %d\n", i); + goto out_bh; + } + } else { + ret = udf_load_vat(sb, i, type1_idx); + if (ret) + goto out_bh; + /* + * Mark filesystem read-only if we have a partition with + * virtual map since we don't handle writing to it (we + * overwrite blocks instead of relocating them). + */ + sb->s_flags |= MS_RDONLY; + printk(KERN_NOTICE "UDF-fs: Filesystem marked read-only " + "because writing to pseudooverwrite partition is " + "not implemented.\n"); + } +out_bh: + /* In case loading failed, we handle cleanup in udf_fill_super */ + brelse(bh); + return ret; } -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; uint8_t type; struct udf_sb_info *sbi = UDF_SB(sb); struct genericPartitionMap *gpm; + uint16_t ident; + struct buffer_head *bh; + int ret = 0; + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + return 1; + BUG_ON(ident != TAG_IDENT_LVD); lvd = (struct logicalVolDesc *)bh->b_data; i = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); - if (i != 0) - return i; + if (i != 0) { + ret = i; + goto out_bh; + } for (i = 0, offset = 0; i < sbi->s_partitions && offset < le32_to_cpu(lvd->mapTableLength); @@ -1157,12 +1257,12 @@ static int udf_load_logicalvol(struct super_block *sb, struct buffer_head *bh, u16 suf = le16_to_cpu(((__le16 *)upm2->partIdent. identSuffix)[0]); - if (suf == 0x0150) { + if (suf < 0x0200) { map->s_partition_type = UDF_VIRTUAL_MAP15; map->s_partition_func = udf_get_pblock_virt15; - } else if (suf == 0x0200) { + } else { map->s_partition_type = UDF_VIRTUAL_MAP20; map->s_partition_func = @@ -1172,7 +1272,6 @@ static int udf_load_logicalvol(struct super_block *sb, struct buffer_head *bh, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { uint32_t loc; - uint16_t ident; struct sparingTable *st; struct sparablePartitionMap *spm = (struct sparablePartitionMap *)gpm; @@ -1190,22 +1289,64 @@ static int udf_load_logicalvol(struct super_block *sb, struct buffer_head *bh, map->s_type_specific.s_sparing. s_spar_map[j] = bh2; - if (bh2 != NULL) { - st = (struct sparingTable *) - bh2->b_data; - if (ident != 0 || strncmp( - st->sparingIdent.ident, - UDF_ID_SPARING, - strlen(UDF_ID_SPARING))) { - brelse(bh2); - map->s_type_specific. - s_sparing. - s_spar_map[j] = - NULL; - } + if (bh2 == NULL) + continue; + + st = (struct sparingTable *)bh2->b_data; + if (ident != 0 || strncmp( + st->sparingIdent.ident, + UDF_ID_SPARING, + strlen(UDF_ID_SPARING))) { + brelse(bh2); + map->s_type_specific.s_sparing. + s_spar_map[j] = NULL; } } map->s_partition_func = udf_get_pblock_spar15; + } 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); + mdata->s_dup_md_flag = + mdm->flags & 0x01; + + udf_debug("Metadata Ident suffix=0x%x\n", + (le16_to_cpu( + ((__le16 *) + mdm->partIdent.identSuffix)[0]))); + 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("Duplicate Flag: %d %d\n", + mdata->s_dup_md_flag, mdm->flags); } else { udf_debug("Unknown ident: %s\n", upm2->partIdent.ident); @@ -1220,7 +1361,7 @@ static int udf_load_logicalvol(struct super_block *sb, struct buffer_head *bh, } 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, " @@ -1230,14 +1371,16 @@ 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; +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; @@ -1279,8 +1422,8 @@ static void udf_load_logicalvolint(struct super_block *sb, kernel_extent_ad loc) * July 1, 1997 - Andrew E. Mileski * Written, tested, and released. */ -static int udf_process_sequence(struct super_block *sb, long block, - long lastblock, kernel_lb_addr *fileset) +static noinline int udf_process_sequence(struct super_block *sb, long block, + long lastblock, struct kernel_lb_addr *fileset) { struct buffer_head *bh = NULL; struct udf_vds_record vds[VDS_POS_LENGTH]; @@ -1288,19 +1431,25 @@ static int udf_process_sequence(struct super_block *sb, long block, struct generic_desc *gd; struct volDescPtr *vdp; int done = 0; - int i, j; uint32_t vdsn; uint16_t ident; long next_s = 0, next_e = 0; memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); - /* Read the main descriptor sequence */ + /* + * 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) { + printk(KERN_ERR "udf: Block %Lu of volume descriptor " + "sequence is corrupted or we could not read " + "it.\n", (unsigned long long)block); + return 1; + } /* Process each descriptor (ISO 13346 3/8.3-8.4) */ gd = (struct generic_desc *)bh->b_data; @@ -1366,263 +1515,288 @@ static int udf_process_sequence(struct super_block *sb, long block, } 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) { - if (udf_load_logicalvol(sb, bh, fileset)) { - brelse(bh); - return 1; - } - } 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) { + printk(KERN_ERR "udf: Primary Volume Descriptor not found!\n"); + return 1; } + if (udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block)) + return 1; - return 0; -} - -/* - * udf_check_valid() - */ -static int udf_check_valid(struct super_block *sb, int novrs, int silent) -{ - long block; + if (vds[VDS_POS_LOGICAL_VOL_DESC].block && udf_load_logicalvol(sb, + vds[VDS_POS_LOGICAL_VOL_DESC].block, fileset)) + return 1; - 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 { - block = udf_vrs(sb, silent); - if (block == -1) { - struct udf_sb_info *sbi = UDF_SB(sb); - udf_debug("Failed to read byte 32768. Assuming open " - "disc. Skipping validity check\n"); - if (!sbi->s_last_block) - sbi->s_last_block = udf_get_last_block(sb); - return 0; - } else - return !block; + 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++) + if (udf_load_partdesc(sb, block)) + return 1; } + + return 0; } -static int udf_load_partition(struct super_block *sb, kernel_lb_addr *fileset) +static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh, + 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; struct udf_sb_info *sbi; - if (!sb) - return 1; sbi = UDF_SB(sb); + 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) */ + if (!udf_process_sequence(sb, main_s, main_e, fileset)) + return 1; + return !udf_process_sequence(sb, reserve_s, reserve_e, fileset); +} - for (i = 0; i < ARRAY_SIZE(sbi->s_anchor); i++) { - if (!sbi->s_anchor[i]) - continue; - bh = udf_read_tagged(sb, sbi->s_anchor[i], sbi->s_anchor[i], - &ident); - if (!bh) - continue; - - 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; +/* + * Check whether there is an anchor block in the given block and + * load Volume Descriptor Sequence if so. + */ +static int udf_check_anchor_block(struct super_block *sb, sector_t block, + struct kernel_lb_addr *fileset) +{ + struct buffer_head *bh; + uint16_t ident; + int ret; - /* 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; + 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 0; + bh = udf_read_tagged(sb, block, block, &ident); + if (!bh) + return 0; + if (ident != TAG_IDENT_AVDP) { brelse(bh); - - /* Process the main & reserve sequences */ - /* responsible for finding the PartitionDesc(s) */ - if (!(udf_process_sequence(sb, main_s, main_e, - fileset) && - udf_process_sequence(sb, reserve_s, reserve_e, - fileset))) - break; + return 0; } + ret = udf_load_sequence(sb, bh, fileset); + brelse(bh); + return ret; +} - if (i == ARRAY_SIZE(sbi->s_anchor)) { - udf_debug("No Anchor block found\n"); - return 1; +/* Search for an anchor volume descriptor pointer */ +static sector_t 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; + + /* First try user provided anchor */ + if (sbi->s_anchor) { + if (udf_check_anchor_block(sb, sbi->s_anchor, fileset)) + return lastblock; + } + /* + * according to spec, anchor is in either: + * block 256 + * lastblock-256 + * lastblock + * however, if the disc isn't closed, it could be 512. + */ + if (udf_check_anchor_block(sb, sbi->s_session + 256, fileset)) + return lastblock; + /* + * 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; + if (udf_check_anchor_block(sb, last[i], fileset)) + return last[i]; + if (last[i] < 256) + continue; + if (udf_check_anchor_block(sb, last[i] - 256, fileset)) + return last[i]; } - udf_debug("Using anchor in block %d\n", sbi->s_anchor[i]); - for (i = 0; i < sbi->s_partitions; i++) { - kernel_lb_addr uninitialized_var(ino); - struct udf_part_map *map = &sbi->s_partmaps[i]; - switch (map->s_partition_type) { - case UDF_VIRTUAL_MAP15: - case UDF_VIRTUAL_MAP20: - if (!sbi->s_last_block) { - sbi->s_last_block = udf_get_last_block(sb); - udf_find_anchor(sb); - } + /* Finally try block 512 in case media is open */ + if (udf_check_anchor_block(sb, sbi->s_session + 512, fileset)) + return last[0]; + return 0; +} - if (!sbi->s_last_block) { - udf_debug("Unable to determine Lastblock (For " - "Virtual Partition)\n"); - return 1; - } +/* + * 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 1 if ok, 0 if not found. + * + */ +static int udf_find_anchor(struct super_block *sb, + struct kernel_lb_addr *fileset) +{ + sector_t lastblock; + struct udf_sb_info *sbi = UDF_SB(sb); - for (j = 0; j < sbi->s_partitions; j++) { - struct udf_part_map *map2 = &sbi->s_partmaps[j]; - if (j != i && - map->s_volumeseqnum == - map2->s_volumeseqnum && - map->s_partition_num == - map2->s_partition_num) { - ino.partitionReferenceNum = j; - ino.logicalBlockNum = - sbi->s_last_block - - map2->s_partition_root; - break; - } - } + lastblock = udf_scan_anchors(sb, sbi->s_last_block, fileset); + if (lastblock) + goto out; - if (j == sbi->s_partitions) - return 1; + /* No anchor found? Try VARCONV conversion of block numbers */ + UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); + /* Firstly, we try to not convert number of the last block */ + lastblock = udf_scan_anchors(sb, + udf_variable_to_fixed(sbi->s_last_block), + fileset); + if (lastblock) + goto out; - sbi->s_vat_inode = udf_iget(sb, ino); - if (!sbi->s_vat_inode) - return 1; + /* Secondly, we try with converted number of the last block */ + lastblock = udf_scan_anchors(sb, sbi->s_last_block, fileset); + if (!lastblock) { + /* VARCONV didn't help. Clear it. */ + UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV); + return 0; + } +out: + sbi->s_last_block = lastblock; + return 1; +} - if (map->s_partition_type == UDF_VIRTUAL_MAP15) { - map->s_type_specific.s_virtual.s_start_offset = - udf_ext0_offset(sbi->s_vat_inode); - 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) { - uint32_t pos; - struct virtualAllocationTable20 *vat20; - - pos = udf_block_map(sbi->s_vat_inode, 0); - bh = sb_bread(sb, pos); - if (!bh) - return 1; - vat20 = (struct virtualAllocationTable20 *) - bh->b_data + - udf_ext0_offset(sbi->s_vat_inode); - map->s_type_specific.s_virtual.s_start_offset = - le16_to_cpu(vat20->lengthHeader) + - udf_ext0_offset(sbi->s_vat_inode); - 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); - } - map->s_partition_root = udf_get_pblock(sb, 0, i, 0); - map->s_partition_len = - sbi->s_partmaps[ino.partitionReferenceNum]. - s_partition_len; +/* + * Check Volume Structure Descriptor, find Anchor block and load Volume + * Descriptor Sequence + */ +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; + + if (!sb_set_blocksize(sb, uopt->blocksize)) { + if (!silent) + printk(KERN_WARNING "UDF-fs: Bad block size\n"); + return 0; + } + 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) + printk(KERN_WARNING "UDF-fs: No VRS found\n"); + return 0; } + if (nsr_off == -1) + udf_debug("Failed to read byte 32768. Assuming open " + "disc. Skipping validity check\n"); + 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"); } - return 0; + + /* Look for anchor block and load Volume Descriptor Sequence */ + sbi->s_anchor = uopt->anchor; + if (!udf_find_anchor(sb, fileset)) { + if (!silent) + printk(KERN_WARNING "UDF-fs: No anchor found\n"); + return 0; + } + return 1; } static void udf_open_lvid(struct super_block *sb) { struct udf_sb_info *sbi = UDF_SB(sb); struct buffer_head *bh = sbi->s_lvid_bh; - if (bh) { - kernel_timestamp cpu_time; - struct logicalVolIntegrityDesc *lvid = - (struct logicalVolIntegrityDesc *)bh->b_data; - struct logicalVolIntegrityDescImpUse *lvidiu = - udf_sb_lvidiu(sbi); + struct logicalVolIntegrityDesc *lvid; + struct logicalVolIntegrityDescImpUse *lvidiu; - lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; - lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) - lvid->recordingDateAndTime = cpu_to_lets(cpu_time); - lvid->integrityType = LVID_INTEGRITY_TYPE_OPEN; + if (!bh) + return; + lvid = (struct logicalVolIntegrityDesc *)bh->b_data; + lvidiu = udf_sb_lvidiu(sbi); - lvid->descTag.descCRC = cpu_to_le16( - udf_crc((char *)lvid + sizeof(tag), - le16_to_cpu(lvid->descTag.descCRCLength), - 0)); + 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.tagChecksum = udf_tag_checksum(&lvid->descTag); - mark_buffer_dirty(bh); - } + 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; } static void udf_close_lvid(struct super_block *sb) { - kernel_timestamp cpu_time; struct udf_sb_info *sbi = UDF_SB(sb); struct buffer_head *bh = sbi->s_lvid_bh; struct logicalVolIntegrityDesc *lvid; + struct logicalVolIntegrityDescImpUse *lvidiu; if (!bh) return; lvid = (struct logicalVolIntegrityDesc *)bh->b_data; - - if (lvid->integrityType == LVID_INTEGRITY_TYPE_OPEN) { - struct logicalVolIntegrityDescImpUse *lvidiu = - udf_sb_lvidiu(sbi); - lvidiu->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX; - lvidiu->impIdent.identSuffix[1] = UDF_OS_ID_LINUX; - if (udf_time_to_stamp(&cpu_time, CURRENT_TIME)) - lvid->recordingDateAndTime = cpu_to_lets(cpu_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( - udf_crc((char *)lvid + sizeof(tag), - le16_to_cpu(lvid->descTag.descCRCLength), - 0)); - - lvid->descTag.tagChecksum = udf_tag_checksum(&lvid->descTag); - mark_buffer_dirty(bh); - } + lvidiu = udf_sb_lvidiu(sbi); + 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); + mark_buffer_dirty(bh); + sbi->s_lvid_dirty = 0; } static void udf_sb_free_bitmap(struct udf_bitmap *bitmap) @@ -1642,34 +1816,50 @@ static void udf_sb_free_bitmap(struct udf_bitmap *bitmap) vfree(bitmap); } -/* - * 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 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 int udf_fill_super(struct super_block *sb, void *options, int silent) { int i; + int ret; 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.umask = 0; + uopt.fmode = UDF_INVALID_MODE; + uopt.dmode = UDF_INVALID_MODE; sbi = kzalloc(sizeof(struct udf_sb_info), GFP_KERNEL); if (!sbi) @@ -1679,7 +1869,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) 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) && @@ -1707,12 +1897,10 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) 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; - /* Set the block size for all transfers */ - if (!udf_set_blocksize(sb, uopt.blocksize)) - goto error_out; - if (uopt.session == 0xFFFFFFFF) sbi->s_session = udf_get_last_session(sb); else @@ -1720,27 +1908,29 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) udf_debug("Multi-session=%d\n", sbi->s_session); - sbi->s_last_block = uopt.lastblock; - sbi->s_anchor[0] = sbi->s_anchor[1] = 0; - sbi->s_anchor[2] = uopt.anchor; - sbi->s_anchor[3] = 256; - - if (udf_check_valid(sb, uopt.novrs, silent)) { - /* read volume recognition sequences */ - printk(KERN_WARNING "UDF-fs: No VRS found\n"); - goto error_out; - } - - udf_find_anchor(sb); - /* Fill in the rest of the superblock */ sb->s_op = &udf_sb_ops; + sb->s_export_op = &udf_export_ops; sb->dq_op = NULL; sb->s_dirt = 0; sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; - if (udf_load_partition(sb, &fileset)) { + 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 && uopt.blocksize != UDF_DEFAULT_BLOCKSIZE) { + if (!silent) + printk(KERN_NOTICE + "UDF-fs: Rescanning with blocksize " + "%d\n", UDF_DEFAULT_BLOCKSIZE); + uopt.blocksize = UDF_DEFAULT_BLOCKSIZE; + ret = udf_load_vrs(sb, &uopt, silent, &fileset); + } + } + if (!ret) { printk(KERN_WARNING "UDF-fs: No partition found (1)\n"); goto error_out; } @@ -1790,12 +1980,12 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) } if (!silent) { - kernel_timestamp ts; - udf_time_to_stamp(&ts, sbi->s_record_time); + struct timestamp ts; + udf_time_to_disk_stamp(&ts, sbi->s_record_time); udf_info("UDF: Mounting volume '%s', " "timestamp %04u/%02u/%02u %02u:%02u (%x)\n", - sbi->s_volume_ident, ts.year, ts.month, ts.day, - ts.hour, ts.minute, ts.typeAndTimezone); + 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); @@ -1803,7 +1993,7 @@ 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(KERN_ERR "UDF-fs: Error in udf_iget, block=%d, " "partition=%d\n", @@ -1824,21 +2014,9 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) error_out: if (sbi->s_vat_inode) iput(sbi->s_vat_inode); - if (sbi->s_partitions) { - struct udf_part_map *map = &sbi->s_partmaps[sbi->s_partition]; - 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]); - } + if (sbi->s_partitions) + for (i = 0; i < sbi->s_partitions; i++) + udf_free_partition(&sbi->s_partmaps[i]); #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) unload_nls(sbi->s_nls_map); @@ -1854,8 +2032,8 @@ error_out: return -EINVAL; } -void udf_error(struct super_block *sb, const char *function, - const char *fmt, ...) +static void udf_error(struct super_block *sb, const char *function, + const char *fmt, ...) { va_list args; @@ -1882,42 +2060,20 @@ void udf_warning(struct super_block *sb, const char *function, 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; sbi = UDF_SB(sb); + + lock_kernel(); + if (sbi->s_vat_inode) iput(sbi->s_vat_inode); - if (sbi->s_partitions) { - struct udf_part_map *map = &sbi->s_partmaps[sbi->s_partition]; - 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]); - } + if (sbi->s_partitions) + for (i = 0; i < sbi->s_partitions; i++) + udf_free_partition(&sbi->s_partmaps[i]); #ifdef CONFIG_UDF_NLS if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP)) unload_nls(sbi->s_nls_map); @@ -1928,26 +2084,35 @@ static void udf_put_super(struct super_block *sb) kfree(sbi->s_partmaps); kfree(sb->s_fs_info); sb->s_fs_info = NULL; + + unlock_kernel(); +} + +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); + sb->s_dirt = 0; + sbi->s_lvid_dirty = 0; + } + mutex_unlock(&sbi->s_alloc_mutex); + + return 0; } -/* - * udf_stat_fs - * - * PURPOSE - * Return info about the filesystem. - * - * DESCRIPTION - * Called by sys_statfs() - * - * HISTORY - * July 1, 1997 - Andrew E. Mileski - * Written, tested, and released. - */ static int udf_statfs(struct 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); if (sbi->s_lvid_bh != NULL) lvidiu = udf_sb_lvidiu(sbi); @@ -1963,16 +2128,13 @@ static int udf_statfs(struct dentry *dentry, struct kstatfs *buf) 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) { @@ -1980,9 +2142,8 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb, 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; @@ -1991,7 +2152,7 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb, loc.logicalBlockNum = bitmap->s_extPosition; loc.partitionReferenceNum = UDF_SB(sb)->s_partition; - bh = udf_read_ptagged(sb, loc, 0, &ident); + bh = udf_read_ptagged(sb, &loc, 0, &ident); if (!bh) { printk(KERN_ERR "udf: udf_count_free failed\n"); @@ -2008,16 +2169,13 @@ static unsigned int udf_count_free_bitmap(struct super_block *sb, 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"); @@ -2040,7 +2198,7 @@ static unsigned int udf_count_free_table(struct super_block *sb, { unsigned int accum = 0; uint32_t elen; - kernel_lb_addr eloc; + struct kernel_lb_addr eloc; int8_t etype; struct extent_position epos;