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>
2016-11-01 16:40:13 +03:00
# include <linux/bio.h>
2021-11-04 17:22:35 +03:00
# include <linux/iversion.h>
2005-04-17 02:20:36 +04:00
# include "udf_i.h"
# include "udf_sb.h"
2013-05-16 09:09:37 +04:00
static int udf_readdir ( struct file * file , struct dir_context * ctx )
2005-04-17 02:20:36 +04:00
{
2013-05-16 09:09:37 +04:00
struct inode * dir = file_inode ( file ) ;
struct udf_inode_info * iinfo = UDF_I ( dir ) ;
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 ;
2017-10-12 16:48:40 +03:00
udf_pblk_t block , iblock ;
2021-11-04 17:22:35 +03:00
loff_t nf_pos , emit_pos = 0 ;
2005-04-17 02:20:36 +04:00
int flen ;
2016-01-26 23:07:30 +03:00
unsigned char * fname = NULL , * copy_name = NULL ;
2010-02-01 05:28:48 +03:00
unsigned char * nameptr ;
2005-04-17 02:20:36 +04:00
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 ] ;
2008-10-15 14:28:03 +04:00
struct kernel_lb_addr eloc ;
2007-05-08 11:35:14 +04:00
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 ;
2007-07-19 12:47:43 +04:00
struct extent_position epos = { NULL , 0 , { 0 , 0 } } ;
2014-12-19 00:49:12 +03:00
struct super_block * sb = dir - > i_sb ;
2021-11-04 17:22:35 +03:00
bool pos_valid = false ;
2005-04-17 02:20:36 +04:00
2013-05-16 09:09:37 +04:00
if ( ctx - > pos = = 0 ) {
if ( ! dir_emit_dot ( file , ctx ) )
return 0 ;
ctx - > pos = 1 ;
}
nf_pos = ( ctx - > pos - 1 ) < < 2 ;
2005-04-17 02:20:36 +04:00
if ( nf_pos > = size )
2008-03-04 16:14:05 +03:00
goto out ;
2021-11-04 17:22:35 +03:00
/*
* Something changed since last readdir ( either lseek was called or dir
* changed ) ? We need to verify the position correctly points at the
* beginning of some dir entry so that the directory parsing code does
* not get confused . Since UDF does not have any reliable way of
* identifying beginning of dir entry ( names are under user control ) ,
* we need to scan the directory from the beginning .
*/
if ( ! inode_eq_iversion ( dir , file - > f_version ) ) {
emit_pos = nf_pos ;
nf_pos = 0 ;
} else {
pos_valid = true ;
}
2008-03-04 16:14:05 +03:00
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
2014-12-19 00:49:12 +03:00
fibh . soffset = fibh . eoffset = nf_pos & ( sb - > s_blocksize - 1 ) ;
2008-03-04 16:14:05 +03:00
if ( iinfo - > i_alloc_type ! = ICBTAG_FLAG_AD_IN_ICB ) {
2014-12-19 00:49:12 +03:00
if ( inode_bmap ( dir , nf_pos > > sb - > s_blocksize_bits ,
2008-03-04 16:14:05 +03:00
& epos , & eloc , & elen , & offset )
! = ( EXT_RECORDED_ALLOCATED > > 30 ) ) {
ret = - ENOENT ;
goto out ;
}
2014-12-19 00:49:12 +03:00
block = udf_get_lb_pblock ( sb , & eloc , offset ) ;
if ( ( + + offset < < sb - > s_blocksize_bits ) < elen ) {
2008-02-08 15:20:44 +03:00
if ( iinfo - > i_alloc_type = = ICBTAG_FLAG_AD_SHORT )
2008-10-15 14:28:03 +04:00
epos . offset - = sizeof ( struct 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 )
2008-10-15 14:28:03 +04:00
epos . offset - = sizeof ( struct 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
2014-12-19 00:49:12 +03:00
if ( ! ( fibh . sbh = fibh . ebh = udf_tread ( 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
2014-12-19 00:49:12 +03:00
if ( ! ( offset & ( ( 16 > > ( sb - > s_blocksize_bits - 9 ) ) - 1 ) ) ) {
i = 16 > > ( sb - > s_blocksize_bits - 9 ) ;
if ( i + offset > ( elen > > sb - > s_blocksize_bits ) )
i = ( elen > > sb - > s_blocksize_bits ) - offset ;
2007-07-19 12:47:43 +04:00
for ( num = 0 ; i > 0 ; i - - ) {
2014-12-19 00:49:12 +03:00
block = udf_get_lb_pblock ( sb , & eloc , offset + i ) ;
tmp = udf_tgetblk ( 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 ) {
2022-09-01 16:35:01 +03:00
bh_readahead_batch ( num , bha , REQ_RAHEAD ) ;
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 ) {
2013-05-16 09:09:37 +04:00
struct kernel_lb_addr tloc ;
2021-11-04 17:22:35 +03:00
loff_t cur_pos = nf_pos ;
2013-05-16 09:09:37 +04:00
2021-11-04 17:22:35 +03:00
/* Update file position only if we got past the current one */
if ( nf_pos > = emit_pos ) {
ctx - > pos = ( nf_pos > > 2 ) + 1 ;
pos_valid = true ;
}
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 ;
2021-11-04 17:22:35 +03:00
/* Still not at offset where user asked us to read from? */
if ( cur_pos < emit_pos )
continue ;
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 ) {
2021-05-03 12:54:24 +03:00
nameptr = udf_get_fi_ident ( fi ) ;
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 {
2016-01-26 23:07:30 +03:00
if ( ! copy_name ) {
copy_name = kmalloc ( UDF_NAME_LEN ,
GFP_NOFS ) ;
if ( ! copy_name ) {
ret = - ENOMEM ;
goto out ;
}
}
nameptr = copy_name ;
2021-05-03 12:54:24 +03:00
memcpy ( nameptr , udf_get_fi_ident ( fi ) ,
2007-07-19 12:47:43 +04:00
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 ) {
2014-12-19 00:49:12 +03:00
if ( ! UDF_QUERY_FLAG ( 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 ) {
2014-12-19 00:49:12 +03:00
if ( ! UDF_QUERY_FLAG ( 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 ) {
2013-05-16 09:09:37 +04:00
if ( ! dir_emit_dotdot ( file , ctx ) )
goto out ;
continue ;
2005-04-17 02:20:36 +04:00
}
2014-12-19 00:49:12 +03:00
flen = udf_get_filename ( sb , nameptr , lfi , fname , UDF_NAME_LEN ) ;
2015-04-08 22:23:57 +03:00
if ( flen < 0 )
2013-05-16 09:09:37 +04:00
continue ;
tloc = lelb_to_cpu ( cfi . icb . extLocation ) ;
2014-12-19 00:49:12 +03:00
iblock = udf_get_lb_pblock ( sb , & tloc , 0 ) ;
2013-05-16 09:09:37 +04:00
if ( ! dir_emit ( ctx , fname , flen , iblock , DT_UNKNOWN ) )
2008-03-04 16:14:05 +03:00
goto out ;
2007-07-21 15:37:18 +04:00
} /* end while */
2005-04-17 02:20:36 +04:00
2013-05-16 09:09:37 +04:00
ctx - > pos = ( nf_pos > > 2 ) + 1 ;
2021-11-04 17:22:35 +03:00
pos_valid = true ;
2005-04-17 02:20:36 +04:00
2008-03-04 16:14:05 +03:00
out :
2021-11-04 17:22:35 +03:00
if ( pos_valid )
file - > f_version = inode_query_iversion ( dir ) ;
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 ) ;
2016-01-26 23:07:30 +03:00
kfree ( copy_name ) ;
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
/* readdir and lookup functions */
const struct file_operations udf_dir_operations = {
2010-05-27 01:44:53 +04:00
. llseek = generic_file_llseek ,
2008-02-08 15:20:47 +03:00
. read = generic_read_dir ,
2016-05-01 05:37:34 +03:00
. iterate_shared = udf_readdir ,
2010-05-05 17:15:39 +04:00
. unlocked_ioctl = udf_ioctl ,
2010-05-26 19:53:41 +04:00
. fsync = generic_file_fsync ,
2008-02-08 15:20:47 +03:00
} ;