2005-04-17 02:20:36 +04:00
/*
* linux / fs / affs / dir . c
*
* ( c ) 1996 Hans - Joachim Widmaier - Rewritten
*
* ( C ) 1993 Ray Burr - Modified for Amiga FFS filesystem .
*
* ( C ) 1992 Eric Youngdale Modified for ISO 9660 filesystem .
*
* ( C ) 1991 Linus Torvalds - minix filesystem
*
* affs directory handling functions
*
*/
# include "affs.h"
2013-05-18 01:44:42 +04:00
static int affs_readdir ( struct file * , struct dir_context * ) ;
2005-04-17 02:20:36 +04:00
2006-03-28 13:56:42 +04:00
const struct file_operations affs_dir_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
2008-08-24 15:24:41 +04:00
. llseek = generic_file_llseek ,
2016-04-21 06:42:46 +03:00
. iterate_shared = affs_readdir ,
2009-06-08 09:22:00 +04:00
. fsync = affs_file_fsync ,
2005-04-17 02:20:36 +04:00
} ;
/*
* directories can handle most operations . . .
*/
2007-02-12 11:55:38 +03:00
const struct inode_operations affs_dir_inode_operations = {
2005-04-17 02:20:36 +04:00
. create = affs_create ,
. lookup = affs_lookup ,
. link = affs_link ,
. unlink = affs_unlink ,
. symlink = affs_symlink ,
. mkdir = affs_mkdir ,
. rmdir = affs_rmdir ,
. rename = affs_rename ,
. setattr = affs_notify_change ,
} ;
static int
2013-05-18 01:44:42 +04:00
affs_readdir ( struct file * file , struct dir_context * ctx )
2005-04-17 02:20:36 +04:00
{
2013-05-18 01:44:42 +04:00
struct inode * inode = file_inode ( file ) ;
2005-04-17 02:20:36 +04:00
struct super_block * sb = inode - > i_sb ;
2013-05-18 01:44:42 +04:00
struct buffer_head * dir_bh = NULL ;
struct buffer_head * fh_bh = NULL ;
2005-04-17 02:20:36 +04:00
unsigned char * name ;
int namelen ;
u32 i ;
int hash_pos ;
int chain_pos ;
u32 ino ;
2014-04-08 02:39:00 +04:00
int error = 0 ;
2005-04-17 02:20:36 +04:00
2015-02-18 00:46:10 +03:00
pr_debug ( " %s(ino=%lu,f_pos=%llx) \n " , __func__ , inode - > i_ino , ctx - > pos ) ;
2005-04-17 02:20:36 +04:00
2013-05-18 01:44:42 +04:00
if ( ctx - > pos < 2 ) {
file - > private_data = ( void * ) 0 ;
if ( ! dir_emit_dots ( file , ctx ) )
2005-04-17 02:20:36 +04:00
return 0 ;
}
affs_lock_dir ( inode ) ;
2013-05-18 01:44:42 +04:00
chain_pos = ( ctx - > pos - 2 ) & 0xffff ;
hash_pos = ( ctx - > pos - 2 ) > > 16 ;
2005-04-17 02:20:36 +04:00
if ( chain_pos = = 0xffff ) {
affs_warning ( sb , " readdir " , " More than 65535 entries in chain " ) ;
chain_pos = 0 ;
hash_pos + + ;
2013-05-18 01:44:42 +04:00
ctx - > pos = ( ( hash_pos < < 16 ) | chain_pos ) + 2 ;
2005-04-17 02:20:36 +04:00
}
dir_bh = affs_bread ( sb , inode - > i_ino ) ;
if ( ! dir_bh )
2014-04-08 02:39:00 +04:00
goto out_unlock_dir ;
2005-04-17 02:20:36 +04:00
/* If the directory hasn't changed since the last call to readdir(),
* we can jump directly to where we left off .
*/
2013-05-18 01:44:42 +04:00
ino = ( u32 ) ( long ) file - > private_data ;
if ( ino & & file - > f_version = = inode - > i_version ) {
2014-06-07 01:37:25 +04:00
pr_debug ( " readdir() left off=%d \n " , ino ) ;
2005-04-17 02:20:36 +04:00
goto inside ;
}
ino = be32_to_cpu ( AFFS_HEAD ( dir_bh ) - > table [ hash_pos ] ) ;
for ( i = 0 ; ino & & i < chain_pos ; i + + ) {
fh_bh = affs_bread ( sb , ino ) ;
if ( ! fh_bh ) {
affs_error ( sb , " readdir " , " Cannot read block %d " , i ) ;
2014-04-08 02:39:00 +04:00
error = - EIO ;
goto out_brelse_dir ;
2005-04-17 02:20:36 +04:00
}
ino = be32_to_cpu ( AFFS_TAIL ( sb , fh_bh ) - > hash_chain ) ;
affs_brelse ( fh_bh ) ;
fh_bh = NULL ;
}
if ( ino )
goto inside ;
hash_pos + + ;
for ( ; hash_pos < AFFS_SB ( sb ) - > s_hashsize ; hash_pos + + ) {
ino = be32_to_cpu ( AFFS_HEAD ( dir_bh ) - > table [ hash_pos ] ) ;
if ( ! ino )
continue ;
2013-05-18 01:44:42 +04:00
ctx - > pos = ( hash_pos < < 16 ) + 2 ;
2005-04-17 02:20:36 +04:00
inside :
do {
fh_bh = affs_bread ( sb , ino ) ;
if ( ! fh_bh ) {
2014-04-08 02:39:00 +04:00
affs_error ( sb , " readdir " ,
" Cannot read block %d " , ino ) ;
2013-05-18 01:44:42 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2015-02-18 00:46:23 +03:00
namelen = min ( AFFS_TAIL ( sb , fh_bh ) - > name [ 0 ] ,
( u8 ) AFFSNAMEMAX ) ;
2005-04-17 02:20:36 +04:00
name = AFFS_TAIL ( sb , fh_bh ) - > name + 1 ;
2015-02-18 00:46:10 +03:00
pr_debug ( " readdir(): dir_emit( \" %.*s \" , ino=%u), hash=%d, f_pos=%llx \n " ,
namelen , name , ino , hash_pos , ctx - > pos ) ;
2014-04-08 02:39:00 +04:00
2013-05-18 01:44:42 +04:00
if ( ! dir_emit ( ctx , name , namelen , ino , DT_UNKNOWN ) )
2014-04-08 02:39:00 +04:00
goto done ;
2013-05-18 01:44:42 +04:00
ctx - > pos + + ;
2005-04-17 02:20:36 +04:00
ino = be32_to_cpu ( AFFS_TAIL ( sb , fh_bh ) - > hash_chain ) ;
affs_brelse ( fh_bh ) ;
fh_bh = NULL ;
} while ( ino ) ;
}
2014-04-08 02:39:00 +04:00
done :
2013-05-18 01:44:42 +04:00
file - > f_version = inode - > i_version ;
file - > private_data = ( void * ) ( long ) ino ;
2014-04-08 02:39:00 +04:00
affs_brelse ( fh_bh ) ;
2005-04-17 02:20:36 +04:00
2014-04-08 02:39:00 +04:00
out_brelse_dir :
2005-04-17 02:20:36 +04:00
affs_brelse ( dir_bh ) ;
2014-04-08 02:39:00 +04:00
out_unlock_dir :
2005-04-17 02:20:36 +04:00
affs_unlock_dir ( inode ) ;
2014-04-08 02:39:00 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}