2005-04-17 02:20:36 +04:00
/*
* super . c
*
* PURPOSE
* Super block routines for the OSTA - UDF ( tm ) filesystem .
*
* DESCRIPTION
* OSTA - UDF ( tm ) = Optical Storage Technology Association
* Universal Disk Format .
*
* This code is based on version 2.00 of the UDF specification ,
* and revision 3 of the ECMA 167 standard [ equivalent to ISO 13346 ] .
* http : //www.osta.org/
* http : //www.ecma.ch/
* http : //www.iso.org/
*
* COPYRIGHT
* This file is distributed under the terms of the GNU General Public
* License ( GPL ) . Copies of the GPL can be obtained from :
* ftp : //prep.ai.mit.edu/pub/gnu/GPL
* Each contributing author retains all rights to their own work .
*
* ( C ) 1998 Dave Boynton
* ( C ) 1998 - 2004 Ben Fennema
* ( C ) 2000 Stelias Computing Inc
*
* HISTORY
*
* 09 / 24 / 98 dgb changed to allow compiling outside of kernel , and
* added some debugging .
* 10 / 01 / 98 dgb updated to allow ( some ) possibility of compiling w / 2.0 .34
* 10 / 16 / 98 attempting some multi - session support
* 10 / 17 / 98 added freespace count for " df "
* 11 / 11 / 98 gr added novrs option
* 11 / 26 / 98 dgb added fileset , anchor mount options
* 12 / 06 / 98 blf really hosed things royally . vat / sparing support . sequenced vol descs
* rewrote option handling based on isofs
* 12 / 20 / 98 find the free space bitmap ( if it exists )
*/
# include "udfdecl.h"
# include <linux/config.h>
# include <linux/blkdev.h>
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/parser.h>
# include <linux/stat.h>
# include <linux/cdrom.h>
# include <linux/nls.h>
# include <linux/smp_lock.h>
# include <linux/buffer_head.h>
# include <linux/vfs.h>
# include <linux/vmalloc.h>
# include <asm/byteorder.h>
# include <linux/udf_fs.h>
# include "udf_sb.h"
# include "udf_i.h"
# include <linux/init.h>
# include <asm/uaccess.h>
# define VDS_POS_PRIMARY_VOL_DESC 0
# define VDS_POS_UNALLOC_SPACE_DESC 1
# define VDS_POS_LOGICAL_VOL_DESC 2
# define VDS_POS_PARTITION_DESC 3
# define VDS_POS_IMP_USE_VOL_DESC 4
# define VDS_POS_VOL_DESC_PTR 5
# define VDS_POS_TERMINATING_DESC 6
# define VDS_POS_LENGTH 7
static char error_buf [ 1024 ] ;
/* These are the "meat" - everything else is stuffing */
static int udf_fill_super ( struct super_block * , void * , int ) ;
static void udf_put_super ( struct super_block * ) ;
static void udf_write_super ( struct super_block * ) ;
static int udf_remount_fs ( struct super_block * , int * , char * ) ;
static int udf_check_valid ( struct super_block * , int , int ) ;
static int udf_vrs ( struct super_block * sb , int silent ) ;
static int udf_load_partition ( struct super_block * , kernel_lb_addr * ) ;
static int udf_load_logicalvol ( struct super_block * , struct buffer_head * , kernel_lb_addr * ) ;
static void udf_load_logicalvolint ( struct super_block * , kernel_extent_ad ) ;
static void udf_find_anchor ( struct super_block * ) ;
static int udf_find_fileset ( struct super_block * , kernel_lb_addr * , kernel_lb_addr * ) ;
static void udf_load_pvoldesc ( struct super_block * , struct buffer_head * ) ;
static void udf_load_fileset ( struct super_block * , struct buffer_head * , kernel_lb_addr * ) ;
static void udf_load_partdesc ( struct super_block * , struct buffer_head * ) ;
static void udf_open_lvid ( struct super_block * ) ;
static void udf_close_lvid ( struct super_block * ) ;
static unsigned int udf_count_free ( struct super_block * ) ;
static int udf_statfs ( struct super_block * , struct kstatfs * ) ;
/* UDF filesystem type */
static struct super_block * udf_get_sb ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
{
return get_sb_bdev ( fs_type , flags , dev_name , data , udf_fill_super ) ;
}
static struct file_system_type udf_fstype = {
. owner = THIS_MODULE ,
. name = " udf " ,
. get_sb = udf_get_sb ,
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
static kmem_cache_t * udf_inode_cachep ;
static struct inode * udf_alloc_inode ( struct super_block * sb )
{
struct udf_inode_info * ei ;
ei = ( struct udf_inode_info * ) kmem_cache_alloc ( udf_inode_cachep , SLAB_KERNEL ) ;
if ( ! ei )
return NULL ;
return & ei - > vfs_inode ;
}
static void udf_destroy_inode ( struct inode * inode )
{
kmem_cache_free ( udf_inode_cachep , UDF_I ( inode ) ) ;
}
static void init_once ( void * foo , kmem_cache_t * cachep , unsigned long flags )
{
struct udf_inode_info * ei = ( struct udf_inode_info * ) foo ;
if ( ( flags & ( SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR ) ) = =
SLAB_CTOR_CONSTRUCTOR )
{
ei - > i_ext . i_data = NULL ;
inode_init_once ( & ei - > vfs_inode ) ;
}
}
static int init_inodecache ( void )
{
udf_inode_cachep = kmem_cache_create ( " udf_inode_cache " ,
sizeof ( struct udf_inode_info ) ,
0 , SLAB_RECLAIM_ACCOUNT ,
init_once , NULL ) ;
if ( udf_inode_cachep = = NULL )
return - ENOMEM ;
return 0 ;
}
static void destroy_inodecache ( void )
{
if ( kmem_cache_destroy ( udf_inode_cachep ) )
printk ( KERN_INFO " udf_inode_cache: not all structures were freed \n " ) ;
}
/* Superblock operations */
static struct super_operations udf_sb_ops = {
. alloc_inode = udf_alloc_inode ,
. destroy_inode = udf_destroy_inode ,
. write_inode = udf_write_inode ,
. delete_inode = udf_delete_inode ,
. clear_inode = udf_clear_inode ,
. put_super = udf_put_super ,
. write_super = udf_write_super ,
. statfs = udf_statfs ,
. remount_fs = udf_remount_fs ,
} ;
struct udf_options
{
unsigned char novrs ;
unsigned int blocksize ;
unsigned int session ;
unsigned int lastblock ;
unsigned int anchor ;
unsigned int volume ;
unsigned short partition ;
unsigned int fileset ;
unsigned int rootdir ;
unsigned int flags ;
mode_t umask ;
gid_t gid ;
uid_t uid ;
struct nls_table * nls_map ;
} ;
static int __init init_udf_fs ( void )
{
int err ;
err = init_inodecache ( ) ;
if ( err )
goto out1 ;
err = register_filesystem ( & udf_fstype ) ;
if ( err )
goto out ;
return 0 ;
out :
destroy_inodecache ( ) ;
out1 :
return err ;
}
static void __exit exit_udf_fs ( void )
{
unregister_filesystem ( & udf_fstype ) ;
destroy_inodecache ( ) ;
}
module_init ( init_udf_fs )
module_exit ( exit_udf_fs )
/*
* udf_parse_options
*
* PURPOSE
* Parse mount options .
*
* DESCRIPTION
* The following mount options are supported :
*
* gid = Set the default group .
* umask = Set the default umask .
* uid = Set the default user .
* bs = Set the block size .
* unhide Show otherwise hidden files .
* undelete Show deleted files in lists .
* adinicb Embed data in the inode ( default )
* noadinicb Don ' t embed data in the inode
* shortad Use short ad ' s
* longad Use long ad ' s ( default )
* nostrict Unset strict conformance
* iocharset = Set the NLS character set
*
* The remaining are for debugging and disaster recovery :
*
* novrs Skip volume sequence recognition
*
* The following expect a offset from 0.
*
* session = Set the CDROM session ( default = last session )
* anchor = Override standard anchor location . ( default = 256 )
* volume = Override the VolumeDesc location . ( unused )
* partition = Override the PartitionDesc location . ( unused )
* lastblock = Set the last block of the filesystem /
*
* The following expect a offset from the partition root .
*
* fileset = Override the fileset block location . ( unused )
* rootdir = Override the root directory location . ( unused )
* WARNING : overriding the rootdir to a non - directory may
* yield highly unpredictable results .
*
* PRE - CONDITIONS
* options Pointer to mount options string .
* uopts Pointer to mount options variable .
*
* POST - CONDITIONS
* < return > 1 Mount options parsed okay .
* < return > 0 Error parsing mount options .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
enum {
Opt_novrs , Opt_nostrict , Opt_bs , Opt_unhide , Opt_undelete ,
Opt_noadinicb , Opt_adinicb , Opt_shortad , Opt_longad ,
Opt_gid , Opt_uid , Opt_umask , Opt_session , Opt_lastblock ,
Opt_anchor , Opt_volume , Opt_partition , Opt_fileset ,
Opt_rootdir , Opt_utf8 , Opt_iocharset ,
2006-03-08 08:55:24 +03:00
Opt_err , Opt_uforget , Opt_uignore , Opt_gforget , Opt_gignore
2005-04-17 02:20:36 +04:00
} ;
static match_table_t tokens = {
{ Opt_novrs , " novrs " } ,
{ Opt_nostrict , " nostrict " } ,
{ Opt_bs , " bs=%u " } ,
{ Opt_unhide , " unhide " } ,
{ Opt_undelete , " undelete " } ,
{ Opt_noadinicb , " noadinicb " } ,
{ Opt_adinicb , " adinicb " } ,
{ Opt_shortad , " shortad " } ,
{ Opt_longad , " longad " } ,
2006-03-08 08:55:24 +03:00
{ Opt_uforget , " uid=forget " } ,
{ Opt_uignore , " uid=ignore " } ,
{ Opt_gforget , " gid=forget " } ,
{ Opt_gignore , " gid=ignore " } ,
2005-04-17 02:20:36 +04:00
{ Opt_gid , " gid=%u " } ,
{ Opt_uid , " uid=%u " } ,
{ Opt_umask , " umask=%o " } ,
{ Opt_session , " session=%u " } ,
{ Opt_lastblock , " lastblock=%u " } ,
{ Opt_anchor , " anchor=%u " } ,
{ Opt_volume , " volume=%u " } ,
{ Opt_partition , " partition=%u " } ,
{ Opt_fileset , " fileset=%u " } ,
{ Opt_rootdir , " rootdir=%u " } ,
{ Opt_utf8 , " utf8 " } ,
{ Opt_iocharset , " iocharset=%s " } ,
{ Opt_err , NULL }
} ;
static int
udf_parse_options ( char * options , struct udf_options * uopt )
{
char * p ;
int option ;
uopt - > novrs = 0 ;
uopt - > blocksize = 2048 ;
uopt - > partition = 0xFFFF ;
uopt - > session = 0xFFFFFFFF ;
uopt - > lastblock = 0 ;
uopt - > anchor = 0 ;
uopt - > volume = 0xFFFFFFFF ;
uopt - > rootdir = 0xFFFFFFFF ;
uopt - > fileset = 0xFFFFFFFF ;
uopt - > nls_map = NULL ;
if ( ! options )
return 1 ;
while ( ( p = strsep ( & options , " , " ) ) ! = NULL )
{
substring_t args [ MAX_OPT_ARGS ] ;
int token ;
if ( ! * p )
continue ;
token = match_token ( p , tokens , args ) ;
switch ( token )
{
case Opt_novrs :
uopt - > novrs = 1 ;
case Opt_bs :
if ( match_int ( & args [ 0 ] , & option ) )
return 0 ;
uopt - > blocksize = option ;
break ;
case Opt_unhide :
uopt - > flags | = ( 1 < < UDF_FLAG_UNHIDE ) ;
break ;
case Opt_undelete :
uopt - > flags | = ( 1 < < UDF_FLAG_UNDELETE ) ;
break ;
case Opt_noadinicb :
uopt - > flags & = ~ ( 1 < < UDF_FLAG_USE_AD_IN_ICB ) ;
break ;
case Opt_adinicb :
uopt - > flags | = ( 1 < < UDF_FLAG_USE_AD_IN_ICB ) ;
break ;
case Opt_shortad :
uopt - > flags | = ( 1 < < UDF_FLAG_USE_SHORT_AD ) ;
break ;
case Opt_longad :
uopt - > flags & = ~ ( 1 < < UDF_FLAG_USE_SHORT_AD ) ;
break ;
case Opt_gid :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > gid = option ;
break ;
case Opt_uid :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > uid = option ;
break ;
case Opt_umask :
if ( match_octal ( args , & option ) )
return 0 ;
uopt - > umask = option ;
break ;
case Opt_nostrict :
uopt - > flags & = ~ ( 1 < < UDF_FLAG_STRICT ) ;
break ;
case Opt_session :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > session = option ;
break ;
case Opt_lastblock :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > lastblock = option ;
break ;
case Opt_anchor :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > anchor = option ;
break ;
case Opt_volume :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > volume = option ;
break ;
case Opt_partition :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > partition = option ;
break ;
case Opt_fileset :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > fileset = option ;
break ;
case Opt_rootdir :
if ( match_int ( args , & option ) )
return 0 ;
uopt - > rootdir = option ;
break ;
case Opt_utf8 :
uopt - > flags | = ( 1 < < UDF_FLAG_UTF8 ) ;
break ;
# ifdef CONFIG_UDF_NLS
case Opt_iocharset :
uopt - > nls_map = load_nls ( args [ 0 ] . from ) ;
uopt - > flags | = ( 1 < < UDF_FLAG_NLS_MAP ) ;
break ;
# endif
2006-03-08 08:55:24 +03:00
case Opt_uignore :
uopt - > flags | = ( 1 < < UDF_FLAG_UID_IGNORE ) ;
break ;
case Opt_uforget :
uopt - > flags | = ( 1 < < UDF_FLAG_UID_FORGET ) ;
break ;
case Opt_gignore :
uopt - > flags | = ( 1 < < UDF_FLAG_GID_IGNORE ) ;
break ;
case Opt_gforget :
uopt - > flags | = ( 1 < < UDF_FLAG_GID_FORGET ) ;
break ;
2005-04-17 02:20:36 +04:00
default :
printk ( KERN_ERR " udf: bad mount option \" %s \" "
" or missing value \n " , p ) ;
return 0 ;
}
}
return 1 ;
}
void
udf_write_super ( struct super_block * sb )
{
lock_kernel ( ) ;
if ( ! ( sb - > s_flags & MS_RDONLY ) )
udf_open_lvid ( sb ) ;
sb - > s_dirt = 0 ;
unlock_kernel ( ) ;
}
static int
udf_remount_fs ( struct super_block * sb , int * flags , char * options )
{
struct udf_options uopt ;
uopt . flags = UDF_SB ( sb ) - > s_flags ;
uopt . uid = UDF_SB ( sb ) - > s_uid ;
uopt . gid = UDF_SB ( sb ) - > s_gid ;
uopt . umask = UDF_SB ( sb ) - > s_umask ;
if ( ! udf_parse_options ( options , & uopt ) )
return - EINVAL ;
UDF_SB ( sb ) - > s_flags = uopt . flags ;
UDF_SB ( sb ) - > s_uid = uopt . uid ;
UDF_SB ( sb ) - > s_gid = uopt . gid ;
UDF_SB ( sb ) - > s_umask = uopt . umask ;
if ( UDF_SB_LVIDBH ( sb ) ) {
int write_rev = le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > minUDFWriteRev ) ;
if ( write_rev > UDF_MAX_WRITE_VERSION )
* flags | = MS_RDONLY ;
}
if ( ( * flags & MS_RDONLY ) = = ( sb - > s_flags & MS_RDONLY ) )
return 0 ;
if ( * flags & MS_RDONLY )
udf_close_lvid ( sb ) ;
else
udf_open_lvid ( sb ) ;
return 0 ;
}
/*
* udf_set_blocksize
*
* PURPOSE
* Set the block size to be used in all transfers .
*
* DESCRIPTION
* To allow room for a DMA transfer , it is best to guess big when unsure .
* This routine picks 2048 bytes as the blocksize when guessing . This
* should be adequate until devices with larger block sizes become common .
*
* Note that the Linux kernel can currently only deal with blocksizes of
* 512 , 1024 , 2048 , 4096 , and 8192 bytes .
*
* PRE - CONDITIONS
* sb Pointer to _locked_ superblock .
*
* POST - CONDITIONS
* sb - > s_blocksize Blocksize .
* sb - > s_blocksize_bits log2 of blocksize .
* < return > 0 Blocksize is valid .
* < return > 1 Blocksize is invalid .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
static int
udf_set_blocksize ( struct super_block * sb , int bsize )
{
if ( ! sb_min_blocksize ( sb , bsize ) ) {
udf_debug ( " Bad block size (%d) \n " , bsize ) ;
printk ( KERN_ERR " udf: bad block size (%d) \n " , bsize ) ;
return 0 ;
}
return sb - > s_blocksize ;
}
static int
udf_vrs ( struct super_block * sb , int silent )
{
struct volStructDesc * vsd = NULL ;
int sector = 32768 ;
int sectorsize ;
struct buffer_head * bh = NULL ;
int iso9660 = 0 ;
int nsr02 = 0 ;
int nsr03 = 0 ;
/* Block size must be a multiple of 512 */
if ( sb - > s_blocksize & 511 )
return 0 ;
if ( sb - > s_blocksize < sizeof ( struct volStructDesc ) )
sectorsize = sizeof ( struct volStructDesc ) ;
else
sectorsize = sb - > s_blocksize ;
sector + = ( UDF_SB_SESSION ( sb ) < < sb - > s_blocksize_bits ) ;
udf_debug ( " Starting at sector %u (%ld byte sectors) \n " ,
( sector > > sb - > s_blocksize_bits ) , sb - > s_blocksize ) ;
/* Process the sequence (if applicable) */
for ( ; ! nsr02 & & ! nsr03 ; sector + = sectorsize )
{
/* Read a block */
bh = udf_tread ( sb , sector > > sb - > s_blocksize_bits ) ;
if ( ! bh )
break ;
/* Look for ISO descriptors */
vsd = ( struct volStructDesc * ) ( bh - > b_data +
( sector & ( sb - > s_blocksize - 1 ) ) ) ;
if ( vsd - > stdIdent [ 0 ] = = 0 )
{
udf_release_data ( bh ) ;
break ;
}
else if ( ! strncmp ( vsd - > stdIdent , VSD_STD_ID_CD001 , VSD_STD_ID_LEN ) )
{
iso9660 = sector ;
switch ( vsd - > structType )
{
case 0 :
udf_debug ( " ISO9660 Boot Record found \n " ) ;
break ;
case 1 :
udf_debug ( " ISO9660 Primary Volume Descriptor found \n " ) ;
break ;
case 2 :
udf_debug ( " ISO9660 Supplementary Volume Descriptor found \n " ) ;
break ;
case 3 :
udf_debug ( " ISO9660 Volume Partition Descriptor found \n " ) ;
break ;
case 255 :
udf_debug ( " ISO9660 Volume Descriptor Set Terminator found \n " ) ;
break ;
default :
udf_debug ( " ISO9660 VRS (%u) found \n " , vsd - > structType ) ;
break ;
}
}
else if ( ! strncmp ( vsd - > stdIdent , VSD_STD_ID_BEA01 , VSD_STD_ID_LEN ) )
{
}
else if ( ! strncmp ( vsd - > stdIdent , VSD_STD_ID_TEA01 , VSD_STD_ID_LEN ) )
{
udf_release_data ( bh ) ;
break ;
}
else if ( ! strncmp ( vsd - > stdIdent , VSD_STD_ID_NSR02 , VSD_STD_ID_LEN ) )
{
nsr02 = sector ;
}
else if ( ! strncmp ( vsd - > stdIdent , VSD_STD_ID_NSR03 , VSD_STD_ID_LEN ) )
{
nsr03 = sector ;
}
udf_release_data ( bh ) ;
}
if ( nsr03 )
return nsr03 ;
else if ( nsr02 )
return nsr02 ;
else if ( sector - ( UDF_SB_SESSION ( sb ) < < sb - > s_blocksize_bits ) = = 32768 )
return - 1 ;
else
return 0 ;
}
/*
* udf_find_anchor
*
* PURPOSE
* Find an anchor volume descriptor .
*
* PRE - CONDITIONS
* sb Pointer to _locked_ superblock .
* lastblock Last block on media .
*
* POST - CONDITIONS
* < return > 1 if not found , 0 if ok
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
static void
udf_find_anchor ( struct super_block * sb )
{
int lastblock = UDF_SB_LASTBLOCK ( sb ) ;
struct buffer_head * bh = NULL ;
uint16_t ident ;
uint32_t location ;
int i ;
if ( lastblock )
{
int varlastblock = udf_variable_to_fixed ( lastblock ) ;
int last [ ] = { lastblock , lastblock - 2 ,
lastblock - 150 , lastblock - 152 ,
varlastblock , varlastblock - 2 ,
varlastblock - 150 , varlastblock - 152 } ;
lastblock = 0 ;
/* Search for an anchor volume descriptor pointer */
/* according to spec, anchor is in either:
* block 256
* lastblock - 256
* lastblock
* however , if the disc isn ' t closed , it could be 512 */
for ( i = 0 ; ( ! lastblock & & i < sizeof ( last ) / sizeof ( int ) ) ; i + + )
{
if ( last [ i ] < 0 | | ! ( bh = sb_bread ( sb , last [ i ] ) ) )
{
ident = location = 0 ;
}
else
{
ident = le16_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagIdent ) ;
location = le32_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagLocation ) ;
udf_release_data ( bh ) ;
}
if ( ident = = TAG_IDENT_AVDP )
{
if ( location = = last [ i ] - UDF_SB_SESSION ( sb ) )
{
lastblock = UDF_SB_ANCHOR ( sb ) [ 0 ] = last [ i ] - UDF_SB_SESSION ( sb ) ;
UDF_SB_ANCHOR ( sb ) [ 1 ] = last [ i ] - 256 - UDF_SB_SESSION ( sb ) ;
}
else if ( location = = udf_variable_to_fixed ( last [ i ] ) - UDF_SB_SESSION ( sb ) )
{
UDF_SET_FLAG ( sb , UDF_FLAG_VARCONV ) ;
lastblock = UDF_SB_ANCHOR ( sb ) [ 0 ] = udf_variable_to_fixed ( last [ i ] ) - UDF_SB_SESSION ( sb ) ;
UDF_SB_ANCHOR ( sb ) [ 1 ] = lastblock - 256 - UDF_SB_SESSION ( sb ) ;
}
else
udf_debug ( " Anchor found at block %d, location mismatch %d. \n " ,
last [ i ] , location ) ;
}
else if ( ident = = TAG_IDENT_FE | | ident = = TAG_IDENT_EFE )
{
lastblock = last [ i ] ;
UDF_SB_ANCHOR ( sb ) [ 3 ] = 512 ;
}
else
{
if ( last [ i ] < 256 | | ! ( bh = sb_bread ( sb , last [ i ] - 256 ) ) )
{
ident = location = 0 ;
}
else
{
ident = le16_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagIdent ) ;
location = le32_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagLocation ) ;
udf_release_data ( bh ) ;
}
if ( ident = = TAG_IDENT_AVDP & &
location = = last [ i ] - 256 - UDF_SB_SESSION ( sb ) )
{
lastblock = last [ i ] ;
UDF_SB_ANCHOR ( sb ) [ 1 ] = last [ i ] - 256 ;
}
else
{
if ( last [ i ] < 312 + UDF_SB_SESSION ( sb ) | | ! ( bh = sb_bread ( sb , last [ i ] - 312 - UDF_SB_SESSION ( sb ) ) ) )
{
ident = location = 0 ;
}
else
{
ident = le16_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagIdent ) ;
location = le32_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagLocation ) ;
udf_release_data ( bh ) ;
}
if ( ident = = TAG_IDENT_AVDP & &
location = = udf_variable_to_fixed ( last [ i ] ) - 256 )
{
UDF_SET_FLAG ( sb , UDF_FLAG_VARCONV ) ;
lastblock = udf_variable_to_fixed ( last [ i ] ) ;
UDF_SB_ANCHOR ( sb ) [ 1 ] = lastblock - 256 ;
}
}
}
}
}
if ( ! lastblock )
{
/* We havn't found the lastblock. check 312 */
if ( ( bh = sb_bread ( sb , 312 + UDF_SB_SESSION ( sb ) ) ) )
{
ident = le16_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagIdent ) ;
location = le32_to_cpu ( ( ( tag * ) bh - > b_data ) - > tagLocation ) ;
udf_release_data ( bh ) ;
if ( ident = = TAG_IDENT_AVDP & & location = = 256 )
UDF_SET_FLAG ( sb , UDF_FLAG_VARCONV ) ;
}
}
for ( i = 0 ; i < sizeof ( UDF_SB_ANCHOR ( sb ) ) / sizeof ( int ) ; i + + )
{
if ( UDF_SB_ANCHOR ( sb ) [ i ] )
{
if ( ! ( bh = udf_read_tagged ( sb ,
UDF_SB_ANCHOR ( sb ) [ i ] , UDF_SB_ANCHOR ( sb ) [ i ] , & ident ) ) )
{
UDF_SB_ANCHOR ( sb ) [ i ] = 0 ;
}
else
{
udf_release_data ( bh ) ;
if ( ( ident ! = TAG_IDENT_AVDP ) & & ( i | |
( ident ! = TAG_IDENT_FE & & ident ! = TAG_IDENT_EFE ) ) )
{
UDF_SB_ANCHOR ( sb ) [ i ] = 0 ;
}
}
}
}
UDF_SB_LASTBLOCK ( sb ) = lastblock ;
}
static int
udf_find_fileset ( struct super_block * sb , kernel_lb_addr * fileset , kernel_lb_addr * root )
{
struct buffer_head * bh = NULL ;
long lastblock ;
uint16_t ident ;
if ( fileset - > logicalBlockNum ! = 0xFFFFFFFF | |
fileset - > partitionReferenceNum ! = 0xFFFF )
{
bh = udf_read_ptagged ( sb , * fileset , 0 , & ident ) ;
if ( ! bh )
return 1 ;
else if ( ident ! = TAG_IDENT_FSD )
{
udf_release_data ( bh ) ;
return 1 ;
}
}
if ( ! bh ) /* Search backwards through the partitions */
{
kernel_lb_addr newfileset ;
return 1 ;
for ( newfileset . partitionReferenceNum = UDF_SB_NUMPARTS ( sb ) - 1 ;
( newfileset . partitionReferenceNum ! = 0xFFFF & &
fileset - > logicalBlockNum = = 0xFFFFFFFF & &
fileset - > partitionReferenceNum = = 0xFFFF ) ;
newfileset . partitionReferenceNum - - )
{
lastblock = UDF_SB_PARTLEN ( sb , newfileset . partitionReferenceNum ) ;
newfileset . logicalBlockNum = 0 ;
do
{
bh = udf_read_ptagged ( sb , newfileset , 0 , & ident ) ;
if ( ! bh )
{
newfileset . logicalBlockNum + + ;
continue ;
}
switch ( ident )
{
case TAG_IDENT_SBD :
{
struct spaceBitmapDesc * sp ;
sp = ( struct spaceBitmapDesc * ) bh - > b_data ;
newfileset . logicalBlockNum + = 1 +
( ( le32_to_cpu ( sp - > numOfBytes ) + sizeof ( struct spaceBitmapDesc ) - 1 )
> > sb - > s_blocksize_bits ) ;
udf_release_data ( bh ) ;
break ;
}
case TAG_IDENT_FSD :
{
* fileset = newfileset ;
break ;
}
default :
{
newfileset . logicalBlockNum + + ;
udf_release_data ( bh ) ;
bh = NULL ;
break ;
}
}
}
while ( newfileset . logicalBlockNum < lastblock & &
fileset - > logicalBlockNum = = 0xFFFFFFFF & &
fileset - > partitionReferenceNum = = 0xFFFF ) ;
}
}
if ( ( fileset - > logicalBlockNum ! = 0xFFFFFFFF | |
fileset - > partitionReferenceNum ! = 0xFFFF ) & & bh )
{
udf_debug ( " Fileset at block=%d, partition=%d \n " ,
fileset - > logicalBlockNum , fileset - > partitionReferenceNum ) ;
UDF_SB_PARTITION ( sb ) = fileset - > partitionReferenceNum ;
udf_load_fileset ( sb , bh , root ) ;
udf_release_data ( bh ) ;
return 0 ;
}
return 1 ;
}
static void
udf_load_pvoldesc ( struct super_block * sb , struct buffer_head * bh )
{
struct primaryVolDesc * pvoldesc ;
time_t recording ;
long recording_usec ;
struct ustr instr ;
struct ustr outstr ;
pvoldesc = ( struct primaryVolDesc * ) bh - > b_data ;
if ( udf_stamp_to_time ( & recording , & recording_usec ,
lets_to_cpu ( pvoldesc - > recordingDateAndTime ) ) )
{
kernel_timestamp ts ;
ts = lets_to_cpu ( pvoldesc - > recordingDateAndTime ) ;
udf_debug ( " recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x) \n " ,
recording , recording_usec ,
ts . year , ts . month , ts . day , ts . hour , ts . minute , ts . typeAndTimezone ) ;
UDF_SB_RECORDTIME ( sb ) . tv_sec = recording ;
UDF_SB_RECORDTIME ( sb ) . tv_nsec = recording_usec * 1000 ;
}
if ( ! udf_build_ustr ( & instr , pvoldesc - > volIdent , 32 ) )
{
if ( udf_CS0toUTF8 ( & outstr , & instr ) )
{
strncpy ( UDF_SB_VOLIDENT ( sb ) , outstr . u_name ,
outstr . u_len > 31 ? 31 : outstr . u_len ) ;
udf_debug ( " volIdent[] = '%s' \n " , UDF_SB_VOLIDENT ( sb ) ) ;
}
}
if ( ! udf_build_ustr ( & instr , pvoldesc - > volSetIdent , 128 ) )
{
if ( udf_CS0toUTF8 ( & outstr , & instr ) )
udf_debug ( " volSetIdent[] = '%s' \n " , outstr . u_name ) ;
}
}
static void
udf_load_fileset ( struct super_block * sb , struct buffer_head * bh , kernel_lb_addr * root )
{
struct fileSetDesc * fset ;
fset = ( struct fileSetDesc * ) bh - > b_data ;
* root = lelb_to_cpu ( fset - > rootDirectoryICB . extLocation ) ;
UDF_SB_SERIALNUM ( sb ) = le16_to_cpu ( fset - > descTag . tagSerialNum ) ;
udf_debug ( " Rootdir at block=%d, partition=%d \n " ,
root - > logicalBlockNum , root - > partitionReferenceNum ) ;
}
static void
udf_load_partdesc ( struct super_block * sb , struct buffer_head * bh )
{
struct partitionDesc * p ;
int i ;
p = ( struct partitionDesc * ) bh - > b_data ;
for ( i = 0 ; i < UDF_SB_NUMPARTS ( sb ) ; i + + )
{
udf_debug ( " Searching map: (%d == %d) \n " ,
UDF_SB_PARTMAPS ( sb ) [ i ] . s_partition_num , le16_to_cpu ( p - > partitionNumber ) ) ;
if ( UDF_SB_PARTMAPS ( sb ) [ i ] . s_partition_num = = le16_to_cpu ( p - > partitionNumber ) )
{
UDF_SB_PARTLEN ( sb , i ) = le32_to_cpu ( p - > partitionLength ) ; /* blocks */
UDF_SB_PARTROOT ( sb , i ) = le32_to_cpu ( p - > partitionStartingLocation ) ;
if ( le32_to_cpu ( p - > accessType ) = = PD_ACCESS_TYPE_READ_ONLY )
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_READ_ONLY ;
if ( le32_to_cpu ( p - > accessType ) = = PD_ACCESS_TYPE_WRITE_ONCE )
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_WRITE_ONCE ;
if ( le32_to_cpu ( p - > accessType ) = = PD_ACCESS_TYPE_REWRITABLE )
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_REWRITABLE ;
if ( le32_to_cpu ( p - > accessType ) = = PD_ACCESS_TYPE_OVERWRITABLE )
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_OVERWRITABLE ;
if ( ! strcmp ( p - > partitionContents . ident , PD_PARTITION_CONTENTS_NSR02 ) | |
! strcmp ( p - > partitionContents . ident , PD_PARTITION_CONTENTS_NSR03 ) )
{
struct partitionHeaderDesc * phd ;
phd = ( struct partitionHeaderDesc * ) ( p - > partitionContentsUse ) ;
if ( phd - > unallocSpaceTable . extLength )
{
kernel_lb_addr loc = { le32_to_cpu ( phd - > unallocSpaceTable . extPosition ) , i } ;
UDF_SB_PARTMAPS ( sb ) [ i ] . s_uspace . s_table =
udf_iget ( sb , loc ) ;
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_UNALLOC_TABLE ;
udf_debug ( " unallocSpaceTable (part %d) @ %ld \n " ,
i , UDF_SB_PARTMAPS ( sb ) [ i ] . s_uspace . s_table - > i_ino ) ;
}
if ( phd - > unallocSpaceBitmap . extLength )
{
UDF_SB_ALLOC_BITMAP ( sb , i , s_uspace ) ;
if ( UDF_SB_PARTMAPS ( sb ) [ i ] . s_uspace . s_bitmap ! = NULL )
{
UDF_SB_PARTMAPS ( sb ) [ i ] . s_uspace . s_bitmap - > s_extLength =
le32_to_cpu ( phd - > unallocSpaceBitmap . extLength ) ;
UDF_SB_PARTMAPS ( sb ) [ i ] . s_uspace . s_bitmap - > s_extPosition =
le32_to_cpu ( phd - > unallocSpaceBitmap . extPosition ) ;
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_UNALLOC_BITMAP ;
udf_debug ( " unallocSpaceBitmap (part %d) @ %d \n " ,
i , UDF_SB_PARTMAPS ( sb ) [ i ] . s_uspace . s_bitmap - > s_extPosition ) ;
}
}
if ( phd - > partitionIntegrityTable . extLength )
udf_debug ( " partitionIntegrityTable (part %d) \n " , i ) ;
if ( phd - > freedSpaceTable . extLength )
{
kernel_lb_addr loc = { le32_to_cpu ( phd - > freedSpaceTable . extPosition ) , i } ;
UDF_SB_PARTMAPS ( sb ) [ i ] . s_fspace . s_table =
udf_iget ( sb , loc ) ;
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_FREED_TABLE ;
udf_debug ( " freedSpaceTable (part %d) @ %ld \n " ,
i , UDF_SB_PARTMAPS ( sb ) [ i ] . s_fspace . s_table - > i_ino ) ;
}
if ( phd - > freedSpaceBitmap . extLength )
{
UDF_SB_ALLOC_BITMAP ( sb , i , s_fspace ) ;
if ( UDF_SB_PARTMAPS ( sb ) [ i ] . s_fspace . s_bitmap ! = NULL )
{
UDF_SB_PARTMAPS ( sb ) [ i ] . s_fspace . s_bitmap - > s_extLength =
le32_to_cpu ( phd - > freedSpaceBitmap . extLength ) ;
UDF_SB_PARTMAPS ( sb ) [ i ] . s_fspace . s_bitmap - > s_extPosition =
le32_to_cpu ( phd - > freedSpaceBitmap . extPosition ) ;
UDF_SB_PARTFLAGS ( sb , i ) | = UDF_PART_FLAG_FREED_BITMAP ;
udf_debug ( " freedSpaceBitmap (part %d) @ %d \n " ,
i , UDF_SB_PARTMAPS ( sb ) [ i ] . s_fspace . s_bitmap - > s_extPosition ) ;
}
}
}
break ;
}
}
if ( i = = UDF_SB_NUMPARTS ( sb ) )
{
udf_debug ( " Partition (%d) not found in partition map \n " , le16_to_cpu ( p - > partitionNumber ) ) ;
}
else
{
udf_debug ( " Partition (%d:%d type %x) starts at physical %d, block length %d \n " ,
le16_to_cpu ( p - > partitionNumber ) , i , UDF_SB_PARTTYPE ( sb , i ) ,
UDF_SB_PARTROOT ( sb , i ) , UDF_SB_PARTLEN ( sb , i ) ) ;
}
}
static int
udf_load_logicalvol ( struct super_block * sb , struct buffer_head * bh , kernel_lb_addr * fileset )
{
struct logicalVolDesc * lvd ;
int i , j , offset ;
uint8_t type ;
lvd = ( struct logicalVolDesc * ) bh - > b_data ;
UDF_SB_ALLOC_PARTMAPS ( sb , le32_to_cpu ( lvd - > numPartitionMaps ) ) ;
for ( i = 0 , offset = 0 ;
i < UDF_SB_NUMPARTS ( sb ) & & offset < le32_to_cpu ( lvd - > mapTableLength ) ;
i + + , offset + = ( ( struct genericPartitionMap * ) & ( lvd - > partitionMaps [ offset ] ) ) - > partitionMapLength )
{
type = ( ( struct genericPartitionMap * ) & ( lvd - > partitionMaps [ offset ] ) ) - > partitionMapType ;
if ( type = = 1 )
{
struct genericPartitionMap1 * gpm1 = ( struct genericPartitionMap1 * ) & ( lvd - > partitionMaps [ offset ] ) ;
UDF_SB_PARTTYPE ( sb , i ) = UDF_TYPE1_MAP15 ;
UDF_SB_PARTVSN ( sb , i ) = le16_to_cpu ( gpm1 - > volSeqNum ) ;
UDF_SB_PARTNUM ( sb , i ) = le16_to_cpu ( gpm1 - > partitionNum ) ;
UDF_SB_PARTFUNC ( sb , i ) = NULL ;
}
else if ( type = = 2 )
{
struct udfPartitionMap2 * upm2 = ( struct udfPartitionMap2 * ) & ( lvd - > partitionMaps [ offset ] ) ;
if ( ! strncmp ( upm2 - > partIdent . ident , UDF_ID_VIRTUAL , strlen ( UDF_ID_VIRTUAL ) ) )
{
if ( le16_to_cpu ( ( ( __le16 * ) upm2 - > partIdent . identSuffix ) [ 0 ] ) = = 0x0150 )
{
UDF_SB_PARTTYPE ( sb , i ) = UDF_VIRTUAL_MAP15 ;
UDF_SB_PARTFUNC ( sb , i ) = udf_get_pblock_virt15 ;
}
else if ( le16_to_cpu ( ( ( __le16 * ) upm2 - > partIdent . identSuffix ) [ 0 ] ) = = 0x0200 )
{
UDF_SB_PARTTYPE ( sb , i ) = UDF_VIRTUAL_MAP20 ;
UDF_SB_PARTFUNC ( sb , i ) = udf_get_pblock_virt20 ;
}
}
else if ( ! strncmp ( upm2 - > partIdent . ident , UDF_ID_SPARABLE , strlen ( UDF_ID_SPARABLE ) ) )
{
uint32_t loc ;
uint16_t ident ;
struct sparingTable * st ;
struct sparablePartitionMap * spm = ( struct sparablePartitionMap * ) & ( lvd - > partitionMaps [ offset ] ) ;
UDF_SB_PARTTYPE ( sb , i ) = UDF_SPARABLE_MAP15 ;
UDF_SB_TYPESPAR ( sb , i ) . s_packet_len = le16_to_cpu ( spm - > packetLength ) ;
for ( j = 0 ; j < spm - > numSparingTables ; j + + )
{
loc = le32_to_cpu ( spm - > locSparingTable [ j ] ) ;
UDF_SB_TYPESPAR ( sb , i ) . s_spar_map [ j ] =
udf_read_tagged ( sb , loc , loc , & ident ) ;
if ( UDF_SB_TYPESPAR ( sb , i ) . s_spar_map [ j ] ! = NULL )
{
st = ( struct sparingTable * ) UDF_SB_TYPESPAR ( sb , i ) . s_spar_map [ j ] - > b_data ;
if ( ident ! = 0 | |
strncmp ( st - > sparingIdent . ident , UDF_ID_SPARING , strlen ( UDF_ID_SPARING ) ) )
{
udf_release_data ( UDF_SB_TYPESPAR ( sb , i ) . s_spar_map [ j ] ) ;
UDF_SB_TYPESPAR ( sb , i ) . s_spar_map [ j ] = NULL ;
}
}
}
UDF_SB_PARTFUNC ( sb , i ) = udf_get_pblock_spar15 ;
}
else
{
udf_debug ( " Unknown ident: %s \n " , upm2 - > partIdent . ident ) ;
continue ;
}
UDF_SB_PARTVSN ( sb , i ) = le16_to_cpu ( upm2 - > volSeqNum ) ;
UDF_SB_PARTNUM ( sb , i ) = le16_to_cpu ( upm2 - > partitionNum ) ;
}
udf_debug ( " Partition (%d:%d) type %d on volume %d \n " ,
i , UDF_SB_PARTNUM ( sb , i ) , type , UDF_SB_PARTVSN ( sb , i ) ) ;
}
if ( fileset )
{
long_ad * la = ( long_ad * ) & ( lvd - > logicalVolContentsUse [ 0 ] ) ;
* fileset = lelb_to_cpu ( la - > extLocation ) ;
udf_debug ( " FileSet found in LogicalVolDesc at block=%d, partition=%d \n " ,
fileset - > logicalBlockNum ,
fileset - > partitionReferenceNum ) ;
}
if ( lvd - > integritySeqExt . extLength )
udf_load_logicalvolint ( sb , leea_to_cpu ( lvd - > integritySeqExt ) ) ;
return 0 ;
}
/*
* udf_load_logicalvolint
*
*/
static void
udf_load_logicalvolint ( struct super_block * sb , kernel_extent_ad loc )
{
struct buffer_head * bh = NULL ;
uint16_t ident ;
while ( loc . extLength > 0 & &
( bh = udf_read_tagged ( sb , loc . extLocation ,
loc . extLocation , & ident ) ) & &
ident = = TAG_IDENT_LVID )
{
UDF_SB_LVIDBH ( sb ) = bh ;
if ( UDF_SB_LVID ( sb ) - > nextIntegrityExt . extLength )
udf_load_logicalvolint ( sb , leea_to_cpu ( UDF_SB_LVID ( sb ) - > nextIntegrityExt ) ) ;
if ( UDF_SB_LVIDBH ( sb ) ! = bh )
udf_release_data ( bh ) ;
loc . extLength - = sb - > s_blocksize ;
loc . extLocation + + ;
}
if ( UDF_SB_LVIDBH ( sb ) ! = bh )
udf_release_data ( bh ) ;
}
/*
* udf_process_sequence
*
* PURPOSE
* Process a main / reserve volume descriptor sequence .
*
* PRE - CONDITIONS
* sb Pointer to _locked_ superblock .
* block First block of first extent of the sequence .
* lastblock Lastblock of first extent of the sequence .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
static int
udf_process_sequence ( struct super_block * sb , long block , long lastblock , kernel_lb_addr * fileset )
{
struct buffer_head * bh = NULL ;
struct udf_vds_record vds [ VDS_POS_LENGTH ] ;
struct generic_desc * gd ;
struct volDescPtr * vdp ;
int done = 0 ;
int i , j ;
uint32_t vdsn ;
uint16_t ident ;
long next_s = 0 , next_e = 0 ;
memset ( vds , 0 , sizeof ( struct udf_vds_record ) * VDS_POS_LENGTH ) ;
/* Read the main descriptor sequence */
for ( ; ( ! done & & block < = lastblock ) ; block + + )
{
bh = udf_read_tagged ( sb , block , block , & ident ) ;
if ( ! bh )
break ;
/* Process each descriptor (ISO 13346 3/8.3-8.4) */
gd = ( struct generic_desc * ) bh - > b_data ;
vdsn = le32_to_cpu ( gd - > volDescSeqNum ) ;
switch ( ident )
{
case TAG_IDENT_PVD : /* ISO 13346 3/10.1 */
if ( vdsn > = vds [ VDS_POS_PRIMARY_VOL_DESC ] . volDescSeqNum )
{
vds [ VDS_POS_PRIMARY_VOL_DESC ] . volDescSeqNum = vdsn ;
vds [ VDS_POS_PRIMARY_VOL_DESC ] . block = block ;
}
break ;
case TAG_IDENT_VDP : /* ISO 13346 3/10.3 */
if ( vdsn > = vds [ VDS_POS_VOL_DESC_PTR ] . volDescSeqNum )
{
vds [ VDS_POS_VOL_DESC_PTR ] . volDescSeqNum = vdsn ;
vds [ VDS_POS_VOL_DESC_PTR ] . block = block ;
vdp = ( struct volDescPtr * ) bh - > b_data ;
next_s = le32_to_cpu ( vdp - > nextVolDescSeqExt . extLocation ) ;
next_e = le32_to_cpu ( vdp - > nextVolDescSeqExt . extLength ) ;
next_e = next_e > > sb - > s_blocksize_bits ;
next_e + = next_s ;
}
break ;
case TAG_IDENT_IUVD : /* ISO 13346 3/10.4 */
if ( vdsn > = vds [ VDS_POS_IMP_USE_VOL_DESC ] . volDescSeqNum )
{
vds [ VDS_POS_IMP_USE_VOL_DESC ] . volDescSeqNum = vdsn ;
vds [ VDS_POS_IMP_USE_VOL_DESC ] . block = block ;
}
break ;
case TAG_IDENT_PD : /* ISO 13346 3/10.5 */
if ( ! vds [ VDS_POS_PARTITION_DESC ] . block )
vds [ VDS_POS_PARTITION_DESC ] . block = block ;
break ;
case TAG_IDENT_LVD : /* ISO 13346 3/10.6 */
if ( vdsn > = vds [ VDS_POS_LOGICAL_VOL_DESC ] . volDescSeqNum )
{
vds [ VDS_POS_LOGICAL_VOL_DESC ] . volDescSeqNum = vdsn ;
vds [ VDS_POS_LOGICAL_VOL_DESC ] . block = block ;
}
break ;
case TAG_IDENT_USD : /* ISO 13346 3/10.8 */
if ( vdsn > = vds [ VDS_POS_UNALLOC_SPACE_DESC ] . volDescSeqNum )
{
vds [ VDS_POS_UNALLOC_SPACE_DESC ] . volDescSeqNum = vdsn ;
vds [ VDS_POS_UNALLOC_SPACE_DESC ] . block = block ;
}
break ;
case TAG_IDENT_TD : /* ISO 13346 3/10.9 */
vds [ VDS_POS_TERMINATING_DESC ] . block = block ;
if ( next_e )
{
block = next_s ;
lastblock = next_e ;
next_s = next_e = 0 ;
}
else
done = 1 ;
break ;
}
udf_release_data ( bh ) ;
}
for ( i = 0 ; i < VDS_POS_LENGTH ; i + + )
{
if ( vds [ i ] . block )
{
bh = udf_read_tagged ( sb , vds [ i ] . block , vds [ i ] . block , & ident ) ;
if ( i = = VDS_POS_PRIMARY_VOL_DESC )
udf_load_pvoldesc ( sb , bh ) ;
else if ( i = = VDS_POS_LOGICAL_VOL_DESC )
udf_load_logicalvol ( sb , bh , fileset ) ;
else if ( i = = VDS_POS_PARTITION_DESC )
{
struct buffer_head * bh2 = NULL ;
udf_load_partdesc ( sb , bh ) ;
for ( j = vds [ i ] . block + 1 ; j < vds [ VDS_POS_TERMINATING_DESC ] . block ; j + + )
{
bh2 = udf_read_tagged ( sb , j , j , & ident ) ;
gd = ( struct generic_desc * ) bh2 - > b_data ;
if ( ident = = TAG_IDENT_PD )
udf_load_partdesc ( sb , bh2 ) ;
udf_release_data ( bh2 ) ;
}
}
udf_release_data ( bh ) ;
}
}
return 0 ;
}
/*
* udf_check_valid ( )
*/
static int
udf_check_valid ( struct super_block * sb , int novrs , int silent )
{
long block ;
if ( novrs )
{
udf_debug ( " Validity check skipped because of novrs option \n " ) ;
return 0 ;
}
/* Check that it is NSR02 compliant */
/* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */
else if ( ( block = udf_vrs ( sb , silent ) ) = = - 1 )
{
udf_debug ( " Failed to read byte 32768. Assuming open disc. Skipping validity check \n " ) ;
if ( ! UDF_SB_LASTBLOCK ( sb ) )
UDF_SB_LASTBLOCK ( sb ) = udf_get_last_block ( sb ) ;
return 0 ;
}
else
return ! block ;
}
static int
udf_load_partition ( struct super_block * sb , kernel_lb_addr * fileset )
{
struct anchorVolDescPtr * anchor ;
uint16_t ident ;
struct buffer_head * bh ;
long main_s , main_e , reserve_s , reserve_e ;
int i , j ;
if ( ! sb )
return 1 ;
for ( i = 0 ; i < sizeof ( UDF_SB_ANCHOR ( sb ) ) / sizeof ( int ) ; i + + )
{
if ( UDF_SB_ANCHOR ( sb ) [ i ] & & ( bh = udf_read_tagged ( sb ,
UDF_SB_ANCHOR ( sb ) [ i ] , UDF_SB_ANCHOR ( sb ) [ i ] , & ident ) ) )
{
anchor = ( struct anchorVolDescPtr * ) bh - > b_data ;
/* Locate the main sequence */
main_s = le32_to_cpu ( anchor - > mainVolDescSeqExt . extLocation ) ;
main_e = le32_to_cpu ( anchor - > mainVolDescSeqExt . extLength ) ;
main_e = main_e > > sb - > s_blocksize_bits ;
main_e + = main_s ;
/* Locate the reserve sequence */
reserve_s = le32_to_cpu ( anchor - > reserveVolDescSeqExt . extLocation ) ;
reserve_e = le32_to_cpu ( anchor - > reserveVolDescSeqExt . extLength ) ;
reserve_e = reserve_e > > sb - > s_blocksize_bits ;
reserve_e + = reserve_s ;
udf_release_data ( bh ) ;
/* Process the main & reserve sequences */
/* responsible for finding the PartitionDesc(s) */
if ( ! ( udf_process_sequence ( sb , main_s , main_e , fileset ) & &
udf_process_sequence ( sb , reserve_s , reserve_e , fileset ) ) )
{
break ;
}
}
}
if ( i = = sizeof ( UDF_SB_ANCHOR ( sb ) ) / sizeof ( int ) )
{
udf_debug ( " No Anchor block found \n " ) ;
return 1 ;
}
else
udf_debug ( " Using anchor in block %d \n " , UDF_SB_ANCHOR ( sb ) [ i ] ) ;
for ( i = 0 ; i < UDF_SB_NUMPARTS ( sb ) ; i + + )
{
switch UDF_SB_PARTTYPE ( sb , i )
{
case UDF_VIRTUAL_MAP15 :
case UDF_VIRTUAL_MAP20 :
{
kernel_lb_addr ino ;
if ( ! UDF_SB_LASTBLOCK ( sb ) )
{
UDF_SB_LASTBLOCK ( sb ) = udf_get_last_block ( sb ) ;
udf_find_anchor ( sb ) ;
}
if ( ! UDF_SB_LASTBLOCK ( sb ) )
{
udf_debug ( " Unable to determine Lastblock (For Virtual Partition) \n " ) ;
return 1 ;
}
for ( j = 0 ; j < UDF_SB_NUMPARTS ( sb ) ; j + + )
{
if ( j ! = i & &
UDF_SB_PARTVSN ( sb , i ) = = UDF_SB_PARTVSN ( sb , j ) & &
UDF_SB_PARTNUM ( sb , i ) = = UDF_SB_PARTNUM ( sb , j ) )
{
ino . partitionReferenceNum = j ;
ino . logicalBlockNum = UDF_SB_LASTBLOCK ( sb ) -
UDF_SB_PARTROOT ( sb , j ) ;
break ;
}
}
if ( j = = UDF_SB_NUMPARTS ( sb ) )
return 1 ;
if ( ! ( UDF_SB_VAT ( sb ) = udf_iget ( sb , ino ) ) )
return 1 ;
if ( UDF_SB_PARTTYPE ( sb , i ) = = UDF_VIRTUAL_MAP15 )
{
UDF_SB_TYPEVIRT ( sb , i ) . s_start_offset = udf_ext0_offset ( UDF_SB_VAT ( sb ) ) ;
UDF_SB_TYPEVIRT ( sb , i ) . s_num_entries = ( UDF_SB_VAT ( sb ) - > i_size - 36 ) > > 2 ;
}
else if ( UDF_SB_PARTTYPE ( sb , i ) = = UDF_VIRTUAL_MAP20 )
{
struct buffer_head * bh = NULL ;
uint32_t pos ;
pos = udf_block_map ( UDF_SB_VAT ( sb ) , 0 ) ;
bh = sb_bread ( sb , pos ) ;
UDF_SB_TYPEVIRT ( sb , i ) . s_start_offset =
le16_to_cpu ( ( ( struct virtualAllocationTable20 * ) bh - > b_data + udf_ext0_offset ( UDF_SB_VAT ( sb ) ) ) - > lengthHeader ) +
udf_ext0_offset ( UDF_SB_VAT ( sb ) ) ;
UDF_SB_TYPEVIRT ( sb , i ) . s_num_entries = ( UDF_SB_VAT ( sb ) - > i_size -
UDF_SB_TYPEVIRT ( sb , i ) . s_start_offset ) > > 2 ;
udf_release_data ( bh ) ;
}
UDF_SB_PARTROOT ( sb , i ) = udf_get_pblock ( sb , 0 , i , 0 ) ;
UDF_SB_PARTLEN ( sb , i ) = UDF_SB_PARTLEN ( sb , ino . partitionReferenceNum ) ;
}
}
}
return 0 ;
}
static void udf_open_lvid ( struct super_block * sb )
{
if ( UDF_SB_LVIDBH ( sb ) )
{
int i ;
kernel_timestamp cpu_time ;
UDF_SB_LVIDIU ( sb ) - > impIdent . identSuffix [ 0 ] = UDF_OS_CLASS_UNIX ;
UDF_SB_LVIDIU ( sb ) - > impIdent . identSuffix [ 1 ] = UDF_OS_ID_LINUX ;
if ( udf_time_to_stamp ( & cpu_time , CURRENT_TIME ) )
UDF_SB_LVID ( sb ) - > recordingDateAndTime = cpu_to_lets ( cpu_time ) ;
UDF_SB_LVID ( sb ) - > integrityType = LVID_INTEGRITY_TYPE_OPEN ;
UDF_SB_LVID ( sb ) - > descTag . descCRC =
cpu_to_le16 ( udf_crc ( ( char * ) UDF_SB_LVID ( sb ) + sizeof ( tag ) ,
le16_to_cpu ( UDF_SB_LVID ( sb ) - > descTag . descCRCLength ) , 0 ) ) ;
UDF_SB_LVID ( sb ) - > descTag . tagChecksum = 0 ;
for ( i = 0 ; i < 16 ; i + + )
if ( i ! = 4 )
UDF_SB_LVID ( sb ) - > descTag . tagChecksum + =
( ( uint8_t * ) & ( UDF_SB_LVID ( sb ) - > descTag ) ) [ i ] ;
mark_buffer_dirty ( UDF_SB_LVIDBH ( sb ) ) ;
}
}
static void udf_close_lvid ( struct super_block * sb )
{
if ( UDF_SB_LVIDBH ( sb ) & &
UDF_SB_LVID ( sb ) - > integrityType = = LVID_INTEGRITY_TYPE_OPEN )
{
int i ;
kernel_timestamp cpu_time ;
UDF_SB_LVIDIU ( sb ) - > impIdent . identSuffix [ 0 ] = UDF_OS_CLASS_UNIX ;
UDF_SB_LVIDIU ( sb ) - > impIdent . identSuffix [ 1 ] = UDF_OS_ID_LINUX ;
if ( udf_time_to_stamp ( & cpu_time , CURRENT_TIME ) )
UDF_SB_LVID ( sb ) - > recordingDateAndTime = cpu_to_lets ( cpu_time ) ;
if ( UDF_MAX_WRITE_VERSION > le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > maxUDFWriteRev ) )
UDF_SB_LVIDIU ( sb ) - > maxUDFWriteRev = cpu_to_le16 ( UDF_MAX_WRITE_VERSION ) ;
if ( UDF_SB_UDFREV ( sb ) > le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > minUDFReadRev ) )
UDF_SB_LVIDIU ( sb ) - > minUDFReadRev = cpu_to_le16 ( UDF_SB_UDFREV ( sb ) ) ;
if ( UDF_SB_UDFREV ( sb ) > le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > minUDFWriteRev ) )
UDF_SB_LVIDIU ( sb ) - > minUDFWriteRev = cpu_to_le16 ( UDF_SB_UDFREV ( sb ) ) ;
UDF_SB_LVID ( sb ) - > integrityType = cpu_to_le32 ( LVID_INTEGRITY_TYPE_CLOSE ) ;
UDF_SB_LVID ( sb ) - > descTag . descCRC =
cpu_to_le16 ( udf_crc ( ( char * ) UDF_SB_LVID ( sb ) + sizeof ( tag ) ,
le16_to_cpu ( UDF_SB_LVID ( sb ) - > descTag . descCRCLength ) , 0 ) ) ;
UDF_SB_LVID ( sb ) - > descTag . tagChecksum = 0 ;
for ( i = 0 ; i < 16 ; i + + )
if ( i ! = 4 )
UDF_SB_LVID ( sb ) - > descTag . tagChecksum + =
( ( uint8_t * ) & ( UDF_SB_LVID ( sb ) - > descTag ) ) [ i ] ;
mark_buffer_dirty ( UDF_SB_LVIDBH ( sb ) ) ;
}
}
/*
* udf_read_super
*
* PURPOSE
* Complete the specified super block .
*
* PRE - CONDITIONS
* sb Pointer to superblock to complete - never NULL .
* sb - > s_dev Device to read suberblock from .
* options Pointer to mount options .
* silent Silent flag .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
static int udf_fill_super ( struct super_block * sb , void * options , int silent )
{
int i ;
struct inode * inode = NULL ;
struct udf_options uopt ;
kernel_lb_addr rootdir , fileset ;
struct udf_sb_info * sbi ;
uopt . flags = ( 1 < < UDF_FLAG_USE_AD_IN_ICB ) | ( 1 < < UDF_FLAG_STRICT ) ;
uopt . uid = - 1 ;
uopt . gid = - 1 ;
uopt . umask = 0 ;
sbi = kmalloc ( sizeof ( struct udf_sb_info ) , GFP_KERNEL ) ;
if ( ! sbi )
return - ENOMEM ;
sb - > s_fs_info = sbi ;
memset ( UDF_SB ( sb ) , 0x00 , sizeof ( struct udf_sb_info ) ) ;
init_MUTEX ( & sbi - > s_alloc_sem ) ;
if ( ! udf_parse_options ( ( char * ) options , & uopt ) )
goto error_out ;
if ( uopt . flags & ( 1 < < UDF_FLAG_UTF8 ) & &
uopt . flags & ( 1 < < UDF_FLAG_NLS_MAP ) )
{
udf_error ( sb , " udf_read_super " ,
" utf8 cannot be combined with iocharset \n " ) ;
goto error_out ;
}
# ifdef CONFIG_UDF_NLS
if ( ( uopt . flags & ( 1 < < UDF_FLAG_NLS_MAP ) ) & & ! uopt . nls_map )
{
uopt . nls_map = load_nls_default ( ) ;
if ( ! uopt . nls_map )
uopt . flags & = ~ ( 1 < < UDF_FLAG_NLS_MAP ) ;
else
udf_debug ( " Using default NLS map \n " ) ;
}
# endif
if ( ! ( uopt . flags & ( 1 < < UDF_FLAG_NLS_MAP ) ) )
uopt . flags | = ( 1 < < UDF_FLAG_UTF8 ) ;
fileset . logicalBlockNum = 0xFFFFFFFF ;
fileset . partitionReferenceNum = 0xFFFF ;
UDF_SB ( sb ) - > s_flags = uopt . flags ;
UDF_SB ( sb ) - > s_uid = uopt . uid ;
UDF_SB ( sb ) - > s_gid = uopt . gid ;
UDF_SB ( sb ) - > s_umask = uopt . umask ;
UDF_SB ( sb ) - > s_nls_map = uopt . nls_map ;
/* Set the block size for all transfers */
if ( ! udf_set_blocksize ( sb , uopt . blocksize ) )
goto error_out ;
if ( uopt . session = = 0xFFFFFFFF )
UDF_SB_SESSION ( sb ) = udf_get_last_session ( sb ) ;
else
UDF_SB_SESSION ( sb ) = uopt . session ;
udf_debug ( " Multi-session=%d \n " , UDF_SB_SESSION ( sb ) ) ;
UDF_SB_LASTBLOCK ( sb ) = uopt . lastblock ;
UDF_SB_ANCHOR ( sb ) [ 0 ] = UDF_SB_ANCHOR ( sb ) [ 1 ] = 0 ;
UDF_SB_ANCHOR ( sb ) [ 2 ] = uopt . anchor ;
UDF_SB_ANCHOR ( sb ) [ 3 ] = 256 ;
if ( udf_check_valid ( sb , uopt . novrs , silent ) ) /* read volume recognition sequences */
{
printk ( " UDF-fs: No VRS found \n " ) ;
goto error_out ;
}
udf_find_anchor ( sb ) ;
/* Fill in the rest of the superblock */
sb - > s_op = & udf_sb_ops ;
sb - > dq_op = NULL ;
sb - > s_dirt = 0 ;
sb - > s_magic = UDF_SUPER_MAGIC ;
sb - > s_time_gran = 1000 ;
if ( udf_load_partition ( sb , & fileset ) )
{
printk ( " UDF-fs: No partition found (1) \n " ) ;
goto error_out ;
}
udf_debug ( " Lastblock=%d \n " , UDF_SB_LASTBLOCK ( sb ) ) ;
if ( UDF_SB_LVIDBH ( sb ) )
{
uint16_t minUDFReadRev = le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > minUDFReadRev ) ;
uint16_t minUDFWriteRev = le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > minUDFWriteRev ) ;
/* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */
if ( minUDFReadRev > UDF_MAX_READ_VERSION )
{
printk ( " UDF-fs: minUDFReadRev=%x (max is %x) \n " ,
le16_to_cpu ( UDF_SB_LVIDIU ( sb ) - > minUDFReadRev ) ,
UDF_MAX_READ_VERSION ) ;
goto error_out ;
}
else if ( minUDFWriteRev > UDF_MAX_WRITE_VERSION )
{
sb - > s_flags | = MS_RDONLY ;
}
UDF_SB_UDFREV ( sb ) = minUDFWriteRev ;
if ( minUDFReadRev > = UDF_VERS_USE_EXTENDED_FE )
UDF_SET_FLAG ( sb , UDF_FLAG_USE_EXTENDED_FE ) ;
if ( minUDFReadRev > = UDF_VERS_USE_STREAMS )
UDF_SET_FLAG ( sb , UDF_FLAG_USE_STREAMS ) ;
}
if ( ! UDF_SB_NUMPARTS ( sb ) )
{
printk ( " UDF-fs: No partition found (2) \n " ) ;
goto error_out ;
}
if ( udf_find_fileset ( sb , & fileset , & rootdir ) )
{
printk ( " UDF-fs: No fileset found \n " ) ;
goto error_out ;
}
if ( ! silent )
{
kernel_timestamp ts ;
udf_time_to_stamp ( & ts , UDF_SB_RECORDTIME ( sb ) ) ;
udf_info ( " UDF %s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x) \n " ,
UDFFS_VERSION , UDFFS_DATE ,
UDF_SB_VOLIDENT ( sb ) , ts . year , ts . month , ts . day , ts . hour , ts . minute ,
ts . typeAndTimezone ) ;
}
if ( ! ( sb - > s_flags & MS_RDONLY ) )
udf_open_lvid ( sb ) ;
/* Assign the root inode */
/* assign inodes by physical block number */
/* perhaps it's not extensible enough, but for now ... */
inode = udf_iget ( sb , rootdir ) ;
if ( ! inode )
{
printk ( " UDF-fs: Error in udf_iget, block=%d, partition=%d \n " ,
rootdir . logicalBlockNum , rootdir . partitionReferenceNum ) ;
goto error_out ;
}
/* Allocate a dentry for the root inode */
sb - > s_root = d_alloc_root ( inode ) ;
if ( ! sb - > s_root )
{
printk ( " UDF-fs: Couldn't allocate root dentry \n " ) ;
iput ( inode ) ;
goto error_out ;
}
sb - > s_maxbytes = MAX_LFS_FILESIZE ;
return 0 ;
error_out :
if ( UDF_SB_VAT ( sb ) )
iput ( UDF_SB_VAT ( sb ) ) ;
if ( UDF_SB_NUMPARTS ( sb ) )
{
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_UNALLOC_TABLE )
iput ( UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_uspace . s_table ) ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_FREED_TABLE )
iput ( UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_fspace . s_table ) ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_UNALLOC_BITMAP )
UDF_SB_FREE_BITMAP ( sb , UDF_SB_PARTITION ( sb ) , s_uspace ) ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_FREED_BITMAP )
UDF_SB_FREE_BITMAP ( sb , UDF_SB_PARTITION ( sb ) , s_fspace ) ;
if ( UDF_SB_PARTTYPE ( sb , UDF_SB_PARTITION ( sb ) ) = = UDF_SPARABLE_MAP15 )
{
for ( i = 0 ; i < 4 ; i + + )
udf_release_data ( UDF_SB_TYPESPAR ( sb , UDF_SB_PARTITION ( sb ) ) . s_spar_map [ i ] ) ;
}
}
# ifdef CONFIG_UDF_NLS
if ( UDF_QUERY_FLAG ( sb , UDF_FLAG_NLS_MAP ) )
unload_nls ( UDF_SB ( sb ) - > s_nls_map ) ;
# endif
if ( ! ( sb - > s_flags & MS_RDONLY ) )
udf_close_lvid ( sb ) ;
udf_release_data ( UDF_SB_LVIDBH ( sb ) ) ;
UDF_SB_FREE ( sb ) ;
kfree ( sbi ) ;
sb - > s_fs_info = NULL ;
return - EINVAL ;
}
void udf_error ( struct super_block * sb , const char * function ,
const char * fmt , . . . )
{
va_list args ;
if ( ! ( sb - > s_flags & MS_RDONLY ) )
{
/* mark sb error */
sb - > s_dirt = 1 ;
}
va_start ( args , fmt ) ;
vsprintf ( error_buf , fmt , args ) ;
va_end ( args ) ;
printk ( KERN_CRIT " UDF-fs error (device %s): %s: %s \n " ,
sb - > s_id , function , error_buf ) ;
}
void udf_warning ( struct super_block * sb , const char * function ,
const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
vsprintf ( error_buf , fmt , args ) ;
va_end ( args ) ;
printk ( KERN_WARNING " UDF-fs warning (device %s): %s: %s \n " ,
sb - > s_id , function , error_buf ) ;
}
/*
* udf_put_super
*
* PURPOSE
* Prepare for destruction of the superblock .
*
* DESCRIPTION
* Called before the filesystem is unmounted .
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
static void
udf_put_super ( struct super_block * sb )
{
int i ;
if ( UDF_SB_VAT ( sb ) )
iput ( UDF_SB_VAT ( sb ) ) ;
if ( UDF_SB_NUMPARTS ( sb ) )
{
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_UNALLOC_TABLE )
iput ( UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_uspace . s_table ) ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_FREED_TABLE )
iput ( UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_fspace . s_table ) ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_UNALLOC_BITMAP )
UDF_SB_FREE_BITMAP ( sb , UDF_SB_PARTITION ( sb ) , s_uspace ) ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_FREED_BITMAP )
UDF_SB_FREE_BITMAP ( sb , UDF_SB_PARTITION ( sb ) , s_fspace ) ;
if ( UDF_SB_PARTTYPE ( sb , UDF_SB_PARTITION ( sb ) ) = = UDF_SPARABLE_MAP15 )
{
for ( i = 0 ; i < 4 ; i + + )
udf_release_data ( UDF_SB_TYPESPAR ( sb , UDF_SB_PARTITION ( sb ) ) . s_spar_map [ i ] ) ;
}
}
# ifdef CONFIG_UDF_NLS
if ( UDF_QUERY_FLAG ( sb , UDF_FLAG_NLS_MAP ) )
unload_nls ( UDF_SB ( sb ) - > s_nls_map ) ;
# endif
if ( ! ( sb - > s_flags & MS_RDONLY ) )
udf_close_lvid ( sb ) ;
udf_release_data ( UDF_SB_LVIDBH ( sb ) ) ;
UDF_SB_FREE ( sb ) ;
kfree ( sb - > s_fs_info ) ;
sb - > s_fs_info = NULL ;
}
/*
* udf_stat_fs
*
* PURPOSE
* Return info about the filesystem .
*
* DESCRIPTION
* Called by sys_statfs ( )
*
* HISTORY
* July 1 , 1997 - Andrew E . Mileski
* Written , tested , and released .
*/
static int
udf_statfs ( struct super_block * sb , struct kstatfs * buf )
{
buf - > f_type = UDF_SUPER_MAGIC ;
buf - > f_bsize = sb - > s_blocksize ;
buf - > f_blocks = UDF_SB_PARTLEN ( sb , UDF_SB_PARTITION ( sb ) ) ;
buf - > f_bfree = udf_count_free ( sb ) ;
buf - > f_bavail = buf - > f_bfree ;
buf - > f_files = ( UDF_SB_LVIDBH ( sb ) ?
( le32_to_cpu ( UDF_SB_LVIDIU ( sb ) - > numFiles ) +
le32_to_cpu ( UDF_SB_LVIDIU ( sb ) - > numDirs ) ) : 0 ) + buf - > f_bfree ;
buf - > f_ffree = buf - > f_bfree ;
/* __kernel_fsid_t f_fsid */
buf - > f_namelen = UDF_NAME_LEN - 2 ;
return 0 ;
}
static unsigned char udf_bitmap_lookup [ 16 ] = {
0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4
} ;
static unsigned int
udf_count_free_bitmap ( struct super_block * sb , struct udf_bitmap * bitmap )
{
struct buffer_head * bh = NULL ;
unsigned int accum = 0 ;
int index ;
int block = 0 , newblock ;
kernel_lb_addr loc ;
uint32_t bytes ;
uint8_t value ;
uint8_t * ptr ;
uint16_t ident ;
struct spaceBitmapDesc * bm ;
lock_kernel ( ) ;
loc . logicalBlockNum = bitmap - > s_extPosition ;
loc . partitionReferenceNum = UDF_SB_PARTITION ( sb ) ;
bh = udf_read_ptagged ( sb , loc , 0 , & ident ) ;
if ( ! bh )
{
printk ( KERN_ERR " udf: udf_count_free failed \n " ) ;
goto out ;
}
else if ( ident ! = TAG_IDENT_SBD )
{
udf_release_data ( bh ) ;
printk ( KERN_ERR " udf: udf_count_free failed \n " ) ;
goto out ;
}
bm = ( struct spaceBitmapDesc * ) bh - > b_data ;
bytes = le32_to_cpu ( bm - > numOfBytes ) ;
index = sizeof ( struct spaceBitmapDesc ) ; /* offset in first block only */
ptr = ( uint8_t * ) bh - > b_data ;
while ( bytes > 0 )
{
while ( ( bytes > 0 ) & & ( index < sb - > s_blocksize ) )
{
value = ptr [ index ] ;
accum + = udf_bitmap_lookup [ value & 0x0f ] ;
accum + = udf_bitmap_lookup [ value > > 4 ] ;
index + + ;
bytes - - ;
}
if ( bytes )
{
udf_release_data ( bh ) ;
newblock = udf_get_lb_pblock ( sb , loc , + + block ) ;
bh = udf_tread ( sb , newblock ) ;
if ( ! bh )
{
udf_debug ( " read failed \n " ) ;
goto out ;
}
index = 0 ;
ptr = ( uint8_t * ) bh - > b_data ;
}
}
udf_release_data ( bh ) ;
out :
unlock_kernel ( ) ;
return accum ;
}
static unsigned int
udf_count_free_table ( struct super_block * sb , struct inode * table )
{
unsigned int accum = 0 ;
uint32_t extoffset , elen ;
kernel_lb_addr bloc , eloc ;
int8_t etype ;
struct buffer_head * bh = NULL ;
lock_kernel ( ) ;
bloc = UDF_I_LOCATION ( table ) ;
extoffset = sizeof ( struct unallocSpaceEntry ) ;
while ( ( etype = udf_next_aext ( table , & bloc , & extoffset , & eloc , & elen , & bh , 1 ) ) ! = - 1 )
{
accum + = ( elen > > table - > i_sb - > s_blocksize_bits ) ;
}
udf_release_data ( bh ) ;
unlock_kernel ( ) ;
return accum ;
}
static unsigned int
udf_count_free ( struct super_block * sb )
{
unsigned int accum = 0 ;
if ( UDF_SB_LVIDBH ( sb ) )
{
if ( le32_to_cpu ( UDF_SB_LVID ( sb ) - > numOfPartitions ) > UDF_SB_PARTITION ( sb ) )
{
accum = le32_to_cpu ( UDF_SB_LVID ( sb ) - > freeSpaceTable [ UDF_SB_PARTITION ( sb ) ] ) ;
if ( accum = = 0xFFFFFFFF )
accum = 0 ;
}
}
if ( accum )
return accum ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_UNALLOC_BITMAP )
{
accum + = udf_count_free_bitmap ( sb ,
UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_uspace . s_bitmap ) ;
}
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_FREED_BITMAP )
{
accum + = udf_count_free_bitmap ( sb ,
UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_fspace . s_bitmap ) ;
}
if ( accum )
return accum ;
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_UNALLOC_TABLE )
{
accum + = udf_count_free_table ( sb ,
UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_uspace . s_table ) ;
}
if ( UDF_SB_PARTFLAGS ( sb , UDF_SB_PARTITION ( sb ) ) & UDF_PART_FLAG_FREED_TABLE )
{
accum + = udf_count_free_table ( sb ,
UDF_SB_PARTMAPS ( sb ) [ UDF_SB_PARTITION ( sb ) ] . s_fspace . s_table ) ;
}
return accum ;
}