2005-04-17 02:20:36 +04:00
/*
* dir . c
*
* PURPOSE
* Directory handling routines for the OSTA - UDF ( tm ) filesystem .
*
* 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 - 2004 Ben Fennema
*
* HISTORY
*
* 10 / 05 / 98 dgb Split directory operations into its own file
* Implemented directory reads via do_udf_readdir
* 10 / 06 / 98 Made directory operations work !
* 11 / 17 / 98 Rewrote directory to support ICBTAG_FLAG_AD_LONG
* 11 / 25 / 98 blf Rewrote directory handling ( readdir + lookup ) to support reading
* across blocks .
* 12 / 12 / 98 Split out the lookup code to namei . c . bulk of directory
* code now in directory . c : udf_fileident_read .
*/
# include "udfdecl.h"
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/smp_lock.h>
# include <linux/buffer_head.h>
# include "udf_i.h"
# include "udf_sb.h"
2008-02-08 15:20:47 +03:00
static int do_udf_readdir ( struct inode * dir , struct file * filp ,
filldir_t filldir , void * dirent )
2005-04-17 02:20:36 +04:00
{
2008-03-04 16:14:05 +03:00
struct udf_fileident_bh fibh = { . sbh = NULL , . ebh = NULL } ;
2007-07-19 12:47:43 +04:00
struct fileIdentDesc * fi = NULL ;
2005-04-17 02:20:36 +04:00
struct fileIdentDesc cfi ;
int block , iblock ;
2008-02-14 02:03:33 +03:00
loff_t nf_pos = ( filp - > f_pos - 1 ) < < 2 ;
2005-04-17 02:20:36 +04:00
int flen ;
2008-03-04 16:14:05 +03:00
char * fname = NULL ;
2005-04-17 02:20:36 +04:00
char * nameptr ;
uint16_t liu ;
uint8_t lfi ;
2008-02-14 02:03:33 +03:00
loff_t size = udf_ext0_offset ( dir ) + dir - > i_size ;
2007-05-08 11:35:14 +04:00
struct buffer_head * tmp , * bha [ 16 ] ;
kernel_lb_addr eloc ;
uint32_t elen ;
2007-05-08 11:35:13 +04:00
sector_t offset ;
2008-03-04 16:14:05 +03:00
int i , num , ret = 0 ;
2005-04-17 02:20:36 +04:00
unsigned int dt_type ;
2007-07-19 12:47:43 +04:00
struct extent_position epos = { NULL , 0 , { 0 , 0 } } ;
2008-02-08 15:20:44 +03:00
struct udf_inode_info * iinfo ;
2005-04-17 02:20:36 +04:00
if ( nf_pos > = size )
2008-03-04 16:14:05 +03:00
goto out ;
fname = kmalloc ( UDF_NAME_LEN , GFP_NOFS ) ;
if ( ! fname ) {
ret = - ENOMEM ;
goto out ;
}
2005-04-17 02:20:36 +04:00
if ( nf_pos = = 0 )
2008-02-14 02:03:33 +03:00
nf_pos = udf_ext0_offset ( dir ) ;
2005-04-17 02:20:36 +04:00
2008-02-14 02:03:33 +03:00
fibh . soffset = fibh . eoffset = nf_pos & ( dir - > i_sb - > s_blocksize - 1 ) ;
2008-02-08 15:20:44 +03:00
iinfo = UDF_I ( dir ) ;
2008-03-04 16:14:05 +03:00
if ( iinfo - > i_alloc_type ! = ICBTAG_FLAG_AD_IN_ICB ) {
if ( inode_bmap ( dir , nf_pos > > dir - > i_sb - > s_blocksize_bits ,
& epos , & eloc , & elen , & offset )
! = ( EXT_RECORDED_ALLOCATED > > 30 ) ) {
ret = - ENOENT ;
goto out ;
}
2005-04-17 02:20:36 +04:00
block = udf_get_lb_pblock ( dir - > i_sb , eloc , offset ) ;
2007-07-19 12:47:43 +04:00
if ( ( + + offset < < dir - > i_sb - > s_blocksize_bits ) < elen ) {
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_SHORT )
2007-05-08 11:35:14 +04:00
epos . offset - = sizeof ( short_ad ) ;
2008-02-08 15:20:44 +03:00
else if ( iinfo - > i_alloc_type = =
2008-02-08 15:20:42 +03:00
ICBTAG_FLAG_AD_LONG )
2007-05-08 11:35:14 +04:00
epos . offset - = sizeof ( long_ad ) ;
2007-07-21 15:37:18 +04:00
} else {
2005-04-17 02:20:36 +04:00
offset = 0 ;
2007-07-21 15:37:18 +04:00
}
2005-04-17 02:20:36 +04:00
2007-07-19 12:47:43 +04:00
if ( ! ( fibh . sbh = fibh . ebh = udf_tread ( dir - > i_sb , block ) ) ) {
2008-03-04 16:14:05 +03:00
ret = - EIO ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2007-07-19 12:47:43 +04:00
if ( ! ( offset & ( ( 16 > > ( dir - > i_sb - > s_blocksize_bits - 9 ) ) - 1 ) ) ) {
2005-04-17 02:20:36 +04:00
i = 16 > > ( dir - > i_sb - > s_blocksize_bits - 9 ) ;
2007-07-19 12:47:43 +04:00
if ( i + offset > ( elen > > dir - > i_sb - > s_blocksize_bits ) )
2007-07-21 15:37:18 +04:00
i = ( elen > > dir - > i_sb - > s_blocksize_bits ) - offset ;
2007-07-19 12:47:43 +04:00
for ( num = 0 ; i > 0 ; i - - ) {
2007-07-21 15:37:18 +04:00
block = udf_get_lb_pblock ( dir - > i_sb , eloc , offset + i ) ;
2005-04-17 02:20:36 +04:00
tmp = udf_tgetblk ( dir - > i_sb , block ) ;
2007-07-21 15:37:18 +04:00
if ( tmp & & ! buffer_uptodate ( tmp ) & & ! buffer_locked ( tmp ) )
2005-04-17 02:20:36 +04:00
bha [ num + + ] = tmp ;
else
brelse ( tmp ) ;
}
2007-07-19 12:47:43 +04:00
if ( num ) {
2005-04-17 02:20:36 +04:00
ll_rw_block ( READA , num , bha ) ;
2007-07-19 12:47:43 +04:00
for ( i = 0 ; i < num ; i + + )
2005-04-17 02:20:36 +04:00
brelse ( bha [ i ] ) ;
}
}
}
2007-07-19 12:47:43 +04:00
while ( nf_pos < size ) {
2008-02-14 02:03:33 +03:00
filp - > f_pos = ( nf_pos > > 2 ) + 1 ;
2005-04-17 02:20:36 +04:00
2007-07-19 12:47:43 +04:00
fi = udf_fileident_read ( dir , & nf_pos , & fibh , & cfi , & epos , & eloc ,
& elen , & offset ) ;
2008-03-04 16:14:05 +03:00
if ( ! fi )
goto out ;
2005-04-17 02:20:36 +04:00
liu = le16_to_cpu ( cfi . lengthOfImpUse ) ;
lfi = cfi . lengthFileIdent ;
2007-07-21 15:37:18 +04:00
if ( fibh . sbh = = fibh . ebh ) {
2005-04-17 02:20:36 +04:00
nameptr = fi - > fileIdent + liu ;
2007-07-21 15:37:18 +04:00
} else {
2005-04-17 02:20:36 +04:00
int poffset ; /* Unpaded ending offset */
2007-07-21 15:37:18 +04:00
poffset = fibh . soffset + sizeof ( struct fileIdentDesc ) + liu + lfi ;
2005-04-17 02:20:36 +04:00
2007-07-21 15:37:18 +04:00
if ( poffset > = lfi ) {
nameptr = ( char * ) ( fibh . ebh - > b_data + poffset - lfi ) ;
} else {
2005-04-17 02:20:36 +04:00
nameptr = fname ;
2007-07-19 12:47:43 +04:00
memcpy ( nameptr , fi - > fileIdent + liu ,
lfi - poffset ) ;
memcpy ( nameptr + lfi - poffset ,
fibh . ebh - > b_data , poffset ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-07-19 12:47:43 +04:00
if ( ( cfi . fileCharacteristics & FID_FILE_CHAR_DELETED ) ! = 0 ) {
if ( ! UDF_QUERY_FLAG ( dir - > i_sb , UDF_FLAG_UNDELETE ) )
2005-04-17 02:20:36 +04:00
continue ;
}
2007-07-19 12:47:43 +04:00
if ( ( cfi . fileCharacteristics & FID_FILE_CHAR_HIDDEN ) ! = 0 ) {
if ( ! UDF_QUERY_FLAG ( dir - > i_sb , UDF_FLAG_UNHIDE ) )
2005-04-17 02:20:36 +04:00
continue ;
}
2007-07-19 12:47:43 +04:00
if ( cfi . fileCharacteristics & FID_FILE_CHAR_PARENT ) {
2006-12-08 13:37:44 +03:00
iblock = parent_ino ( filp - > f_path . dentry ) ;
2005-04-17 02:20:36 +04:00
flen = 2 ;
memcpy ( fname , " .. " , flen ) ;
dt_type = DT_DIR ;
2007-07-19 12:47:43 +04:00
} else {
2005-04-17 02:20:36 +04:00
kernel_lb_addr tloc = lelb_to_cpu ( cfi . icb . extLocation ) ;
iblock = udf_get_lb_pblock ( dir - > i_sb , tloc , 0 ) ;
flen = udf_get_filename ( dir - > i_sb , nameptr , fname , lfi ) ;
dt_type = DT_UNKNOWN ;
}
2008-03-04 16:14:05 +03:00
if ( flen & & filldir ( dirent , fname , flen , filp - > f_pos ,
iblock , dt_type ) < 0 )
goto out ;
2007-07-21 15:37:18 +04:00
} /* end while */
2005-04-17 02:20:36 +04:00
2008-02-14 02:03:33 +03:00
filp - > f_pos = ( nf_pos > > 2 ) + 1 ;
2005-04-17 02:20:36 +04:00
2008-03-04 16:14:05 +03:00
out :
2005-04-17 02:20:36 +04:00
if ( fibh . sbh ! = fibh . ebh )
2007-05-08 11:35:16 +04:00
brelse ( fibh . ebh ) ;
brelse ( fibh . sbh ) ;
brelse ( epos . bh ) ;
2008-03-04 16:14:05 +03:00
kfree ( fname ) ;
2005-04-17 02:20:36 +04:00
2008-03-04 16:14:05 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2008-02-08 15:20:47 +03:00
static int udf_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
struct inode * dir = filp - > f_path . dentry - > d_inode ;
int result ;
lock_kernel ( ) ;
if ( filp - > f_pos = = 0 ) {
if ( filldir ( dirent , " . " , 1 , filp - > f_pos , dir - > i_ino , DT_DIR ) < 0 ) {
unlock_kernel ( ) ;
return 0 ;
}
filp - > f_pos + + ;
}
result = do_udf_readdir ( dir , filp , filldir , dirent ) ;
unlock_kernel ( ) ;
return result ;
}
/* readdir and lookup functions */
const struct file_operations udf_dir_operations = {
. read = generic_read_dir ,
. readdir = udf_readdir ,
. ioctl = udf_ioctl ,
. fsync = udf_fsync_file ,
} ;