2015-07-17 10:38:12 -04:00
/*
* ( C ) 2001 Clemson University and The University of Chicago
*
* See COPYING in top - level directory .
*/
# include "protocol.h"
2015-12-04 12:56:14 -05:00
# include "orangefs-kernel.h"
# include "orangefs-bufmap.h"
2015-07-17 10:38:12 -04:00
struct readdir_handle_s {
int buffer_index ;
2015-11-24 15:12:14 -05:00
struct orangefs_readdir_response_s readdir_response ;
2015-07-17 10:38:12 -04:00
void * dents_buf ;
} ;
/*
* decode routine needed by kmod to make sense of the shared page for readdirs .
*/
2015-10-09 18:11:10 -04:00
static long decode_dirents ( char * ptr , size_t size ,
2015-11-24 15:12:14 -05:00
struct orangefs_readdir_response_s * readdir )
2015-07-17 10:38:12 -04:00
{
int i ;
2015-11-24 15:12:14 -05:00
struct orangefs_readdir_response_s * rd =
( struct orangefs_readdir_response_s * ) ptr ;
2015-07-17 10:38:12 -04:00
char * buf = ptr ;
2015-11-24 15:12:14 -05:00
if ( size < offsetof ( struct orangefs_readdir_response_s , dirent_array ) )
2015-10-09 18:11:10 -04:00
return - EINVAL ;
2015-07-17 10:38:12 -04:00
readdir - > token = rd - > token ;
2015-11-24 15:12:14 -05:00
readdir - > orangefs_dirent_outcount = rd - > orangefs_dirent_outcount ;
readdir - > dirent_array = kcalloc ( readdir - > orangefs_dirent_outcount ,
2015-07-17 10:38:12 -04:00
sizeof ( * readdir - > dirent_array ) ,
GFP_KERNEL ) ;
if ( readdir - > dirent_array = = NULL )
return - ENOMEM ;
2015-10-09 18:11:10 -04:00
2015-11-24 15:12:14 -05:00
buf + = offsetof ( struct orangefs_readdir_response_s , dirent_array ) ;
size - = offsetof ( struct orangefs_readdir_response_s , dirent_array ) ;
2015-10-09 18:11:10 -04:00
2015-11-24 15:12:14 -05:00
for ( i = 0 ; i < readdir - > orangefs_dirent_outcount ; i + + ) {
2015-10-09 18:11:10 -04:00
__u32 len ;
if ( size < 4 )
goto Einval ;
len = * ( __u32 * ) buf ;
if ( len > = ( unsigned ) - 24 )
goto Einval ;
2015-10-09 17:43:15 -04:00
readdir - > dirent_array [ i ] . d_name = buf + 4 ;
readdir - > dirent_array [ i ] . d_length = len ;
2015-10-09 18:11:10 -04:00
2016-01-04 15:05:28 -05:00
/*
* Round 4 + len + 1 , which is the encoded size plus the string
* plus the null terminator to the nearest eight byte boundry .
*/
len = ( ( 4 + len + 1 ) + 7 ) & ~ 7 ;
2015-10-09 18:11:10 -04:00
if ( size < len + 16 )
goto Einval ;
size - = len + 16 ;
buf + = len ;
2015-07-17 10:38:12 -04:00
readdir - > dirent_array [ i ] . khandle =
2015-11-24 15:12:14 -05:00
* ( struct orangefs_khandle * ) buf ;
2015-10-09 17:43:15 -04:00
buf + = 16 ;
2015-07-17 10:38:12 -04:00
}
2015-10-09 17:43:15 -04:00
return buf - ptr ;
2015-10-09 18:11:10 -04:00
Einval :
kfree ( readdir - > dirent_array ) ;
readdir - > dirent_array = NULL ;
return - EINVAL ;
2015-07-17 10:38:12 -04:00
}
static long readdir_handle_ctor ( struct readdir_handle_s * rhandle , void * buf ,
2015-10-09 18:11:10 -04:00
size_t size , int buffer_index )
2015-07-17 10:38:12 -04:00
{
long ret ;
if ( buf = = NULL ) {
gossip_err
( " Invalid NULL buffer specified in readdir_handle_ctor \n " ) ;
return - ENOMEM ;
}
if ( buffer_index < 0 ) {
gossip_err
( " Invalid buffer index specified in readdir_handle_ctor \n " ) ;
return - EINVAL ;
}
rhandle - > buffer_index = buffer_index ;
rhandle - > dents_buf = buf ;
2015-10-09 18:11:10 -04:00
ret = decode_dirents ( buf , size , & rhandle - > readdir_response ) ;
2015-07-17 10:38:12 -04:00
if ( ret < 0 ) {
gossip_err ( " Could not decode readdir from buffer %ld \n " , ret ) ;
rhandle - > buffer_index = - 1 ;
gossip_debug ( GOSSIP_DIR_DEBUG , " vfree %p \n " , buf ) ;
vfree ( buf ) ;
rhandle - > dents_buf = NULL ;
}
return ret ;
}
2015-11-24 15:12:14 -05:00
static void readdir_handle_dtor ( struct orangefs_bufmap * bufmap ,
2015-07-17 10:38:12 -04:00
struct readdir_handle_s * rhandle )
{
if ( rhandle = = NULL )
return ;
/* kfree(NULL) is safe */
kfree ( rhandle - > readdir_response . dirent_array ) ;
rhandle - > readdir_response . dirent_array = NULL ;
if ( rhandle - > buffer_index > = 0 ) {
2016-01-04 15:05:28 -05:00
orangefs_readdir_index_put ( bufmap , rhandle - > buffer_index ) ;
2015-07-17 10:38:12 -04:00
rhandle - > buffer_index = - 1 ;
}
if ( rhandle - > dents_buf ) {
gossip_debug ( GOSSIP_DIR_DEBUG , " vfree %p \n " ,
rhandle - > dents_buf ) ;
vfree ( rhandle - > dents_buf ) ;
rhandle - > dents_buf = NULL ;
}
}
/*
* Read directory entries from an instance of an open directory .
*/
2015-11-24 15:12:14 -05:00
static int orangefs_readdir ( struct file * file , struct dir_context * ctx )
2015-07-17 10:38:12 -04:00
{
2015-11-24 15:12:14 -05:00
struct orangefs_bufmap * bufmap = NULL ;
2015-07-17 10:38:12 -04:00
int ret = 0 ;
int buffer_index ;
2015-09-23 16:48:40 -04:00
/*
* ptoken supports Orangefs ' distributed directory logic , added
* in 2.9 .2 .
*/
2015-07-17 10:38:12 -04:00
__u64 * ptoken = file - > private_data ;
__u64 pos = 0 ;
ino_t ino = 0 ;
struct dentry * dentry = file - > f_path . dentry ;
2015-11-24 15:12:14 -05:00
struct orangefs_kernel_op_s * new_op = NULL ;
struct orangefs_inode_s * orangefs_inode = ORANGEFS_I ( dentry - > d_inode ) ;
2015-07-17 10:38:12 -04:00
int buffer_full = 0 ;
struct readdir_handle_s rhandle ;
int i = 0 ;
int len = 0 ;
ino_t current_ino = 0 ;
char * current_entry = NULL ;
long bytes_decoded ;
2015-09-23 16:48:40 -04:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: ctx->pos:%lld, ptoken = %llu \n " ,
__func__ ,
lld ( ctx - > pos ) ,
llu ( * ptoken ) ) ;
2015-07-17 10:38:12 -04:00
pos = ( __u64 ) ctx - > pos ;
/* are we done? */
2015-11-24 15:12:14 -05:00
if ( pos = = ORANGEFS_READDIR_END ) {
2015-07-17 10:38:12 -04:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" Skipping to termination path \n " ) ;
return 0 ;
}
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-11-24 15:12:14 -05:00
" orangefs_readdir called on %s (pos=%llu) \n " ,
2015-07-17 10:38:12 -04:00
dentry - > d_name . name , llu ( pos ) ) ;
rhandle . buffer_index = - 1 ;
rhandle . dents_buf = NULL ;
memset ( & rhandle . readdir_response , 0 , sizeof ( rhandle . readdir_response ) ) ;
2015-11-24 15:12:14 -05:00
new_op = op_alloc ( ORANGEFS_VFS_OP_READDIR ) ;
2015-07-17 10:38:12 -04:00
if ( ! new_op )
return - ENOMEM ;
new_op - > uses_shared_memory = 1 ;
2015-11-24 15:12:14 -05:00
new_op - > upcall . req . readdir . refn = orangefs_inode - > refn ;
2016-01-04 15:05:28 -05:00
new_op - > upcall . req . readdir . max_dirent_count =
ORANGEFS_MAX_DIRENT_COUNT_READDIR ;
2015-07-17 10:38:12 -04:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: upcall.req.readdir.refn.khandle: %pU \n " ,
__func__ ,
& new_op - > upcall . req . readdir . refn . khandle ) ;
new_op - > upcall . req . readdir . token = * ptoken ;
get_new_buffer_index :
2016-01-04 15:05:28 -05:00
ret = orangefs_readdir_index_get ( & bufmap , & buffer_index ) ;
2015-07-17 10:38:12 -04:00
if ( ret < 0 ) {
2016-01-04 15:05:28 -05:00
gossip_lerr ( " orangefs_readdir: orangefs_readdir_index_get() failure (%d) \n " ,
2015-07-17 10:38:12 -04:00
ret ) ;
goto out_free_op ;
}
new_op - > upcall . req . readdir . buf_index = buffer_index ;
ret = service_operation ( new_op ,
2015-11-24 15:12:14 -05:00
" orangefs_readdir " ,
2015-07-17 10:38:12 -04:00
get_interruptible_flag ( dentry - > d_inode ) ) ;
gossip_debug ( GOSSIP_DIR_DEBUG ,
" Readdir downcall status is %d. ret:%d \n " ,
new_op - > downcall . status ,
ret ) ;
if ( ret = = - EAGAIN & & op_state_purged ( new_op ) ) {
/*
* readdir shared memory aread has been wiped due to
* pvfs2 - client - core restarting , so we must get a new
* index into the shared memory .
*/
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: Getting new buffer_index for retry of readdir.. \n " ,
__func__ ) ;
2016-01-04 15:05:28 -05:00
orangefs_readdir_index_put ( bufmap , buffer_index ) ;
2015-07-17 10:38:12 -04:00
goto get_new_buffer_index ;
}
if ( ret = = - EIO & & op_state_purged ( new_op ) ) {
gossip_err ( " %s: Client is down. Aborting readdir call. \n " ,
__func__ ) ;
2016-01-04 15:05:28 -05:00
orangefs_readdir_index_put ( bufmap , buffer_index ) ;
2015-07-17 10:38:12 -04:00
goto out_free_op ;
}
if ( ret < 0 | | new_op - > downcall . status ! = 0 ) {
gossip_debug ( GOSSIP_DIR_DEBUG ,
" Readdir request failed. Status:%d \n " ,
new_op - > downcall . status ) ;
2016-01-04 15:05:28 -05:00
orangefs_readdir_index_put ( bufmap , buffer_index ) ;
2015-07-17 10:38:12 -04:00
if ( ret > = 0 )
ret = new_op - > downcall . status ;
goto out_free_op ;
}
bytes_decoded =
readdir_handle_ctor ( & rhandle ,
new_op - > downcall . trailer_buf ,
2015-10-09 18:11:10 -04:00
new_op - > downcall . trailer_size ,
2015-07-17 10:38:12 -04:00
buffer_index ) ;
if ( bytes_decoded < 0 ) {
2015-11-24 15:12:14 -05:00
gossip_err ( " orangefs_readdir: Could not decode trailer buffer into a readdir response %d \n " ,
2015-07-17 10:38:12 -04:00
ret ) ;
ret = bytes_decoded ;
2016-01-04 15:05:28 -05:00
orangefs_readdir_index_put ( bufmap , buffer_index ) ;
2015-07-17 10:38:12 -04:00
goto out_free_op ;
}
if ( bytes_decoded ! = new_op - > downcall . trailer_size ) {
2015-11-24 15:12:14 -05:00
gossip_err ( " orangefs_readdir: # bytes decoded (%ld) "
2015-09-23 16:48:40 -04:00
" != trailer size (%ld) \n " ,
bytes_decoded ,
( long ) new_op - > downcall . trailer_size ) ;
2015-07-17 10:38:12 -04:00
ret = - EINVAL ;
goto out_destroy_handle ;
}
2015-09-23 16:48:40 -04:00
/*
2015-11-24 15:12:14 -05:00
* orangefs doesn ' t actually store dot and dot - dot , but
2015-09-23 16:48:40 -04:00
* we need to have them represented .
*/
2015-07-17 10:38:12 -04:00
if ( pos = = 0 ) {
ino = get_ino_from_khandle ( dentry - > d_inode ) ;
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: calling dir_emit of \" . \" with pos = %llu \n " ,
__func__ ,
llu ( pos ) ) ;
ret = dir_emit ( ctx , " . " , 1 , ino , DT_DIR ) ;
2015-09-23 16:48:40 -04:00
pos + = 1 ;
2015-07-17 10:38:12 -04:00
}
if ( pos = = 1 ) {
ino = get_parent_ino_from_dentry ( dentry ) ;
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: calling dir_emit of \" .. \" with pos = %llu \n " ,
__func__ ,
llu ( pos ) ) ;
ret = dir_emit ( ctx , " .. " , 2 , ino , DT_DIR ) ;
2015-09-23 16:48:40 -04:00
pos + = 1 ;
2015-07-17 10:38:12 -04:00
}
2015-09-23 16:48:40 -04:00
/*
2015-11-24 15:12:14 -05:00
* we stored ORANGEFS_ITERATE_NEXT in ctx - > pos last time around
2015-09-23 16:48:40 -04:00
* to prevent " finding " dot and dot - dot on any iteration
* other than the first .
*/
2015-11-24 15:12:14 -05:00
if ( ctx - > pos = = ORANGEFS_ITERATE_NEXT )
2015-09-23 16:48:40 -04:00
ctx - > pos = 0 ;
for ( i = ctx - > pos ;
2015-11-24 15:12:14 -05:00
i < rhandle . readdir_response . orangefs_dirent_outcount ;
2015-09-23 16:48:40 -04:00
i + + ) {
2015-07-17 10:38:12 -04:00
len = rhandle . readdir_response . dirent_array [ i ] . d_length ;
current_entry = rhandle . readdir_response . dirent_array [ i ] . d_name ;
2015-11-24 15:12:14 -05:00
current_ino = orangefs_khandle_to_ino (
2015-07-17 10:38:12 -04:00
& ( rhandle . readdir_response . dirent_array [ i ] . khandle ) ) ;
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-09-23 16:48:40 -04:00
" calling dir_emit for %s with len %d "
" , ctx->pos %ld \n " ,
2015-07-17 10:38:12 -04:00
current_entry ,
len ,
2015-09-23 16:48:40 -04:00
( unsigned long ) ctx - > pos ) ;
/*
* type is unknown . We don ' t return object type
* in the dirent_array . This leaves getdents
* clueless about type .
*/
2015-07-17 10:38:12 -04:00
ret =
dir_emit ( ctx , current_entry , len , current_ino , DT_UNKNOWN ) ;
2015-09-23 16:48:40 -04:00
if ( ! ret )
break ;
2015-07-17 10:38:12 -04:00
ctx - > pos + + ;
2015-09-23 16:48:40 -04:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-07-17 10:38:12 -04:00
" %s: ctx->pos:%lld \n " ,
__func__ ,
lld ( ctx - > pos ) ) ;
}
2015-10-05 13:44:24 -04:00
/*
2015-09-23 16:48:40 -04:00
* we ran all the way through the last batch , set up for
* getting another batch . . .
*/
if ( ret ) {
2015-07-17 10:38:12 -04:00
* ptoken = rhandle . readdir_response . token ;
2015-11-24 15:12:14 -05:00
ctx - > pos = ORANGEFS_ITERATE_NEXT ;
2015-07-17 10:38:12 -04:00
}
/*
* Did we hit the end of the directory ?
*/
2015-11-24 15:12:14 -05:00
if ( rhandle . readdir_response . token = = ORANGEFS_READDIR_END & &
2015-07-17 10:38:12 -04:00
! buffer_full ) {
2015-09-23 16:48:40 -04:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-11-24 15:12:14 -05:00
" End of dir detected; setting ctx->pos to ORANGEFS_READDIR_END. \n " ) ;
ctx - > pos = ORANGEFS_READDIR_END ;
2015-07-17 10:38:12 -04:00
}
out_destroy_handle :
readdir_handle_dtor ( bufmap , & rhandle ) ;
out_free_op :
op_release ( new_op ) ;
2015-11-24 15:12:14 -05:00
gossip_debug ( GOSSIP_DIR_DEBUG , " orangefs_readdir returning %d \n " , ret ) ;
2015-07-17 10:38:12 -04:00
return ret ;
}
2015-11-24 15:12:14 -05:00
static int orangefs_dir_open ( struct inode * inode , struct file * file )
2015-07-17 10:38:12 -04:00
{
__u64 * ptoken ;
file - > private_data = kmalloc ( sizeof ( __u64 ) , GFP_KERNEL ) ;
if ( ! file - > private_data )
return - ENOMEM ;
ptoken = file - > private_data ;
2015-11-24 15:12:14 -05:00
* ptoken = ORANGEFS_READDIR_START ;
2015-07-17 10:38:12 -04:00
return 0 ;
}
2015-11-24 15:12:14 -05:00
static int orangefs_dir_release ( struct inode * inode , struct file * file )
2015-07-17 10:38:12 -04:00
{
2015-11-24 15:12:14 -05:00
orangefs_flush_inode ( inode ) ;
2015-07-17 10:38:12 -04:00
kfree ( file - > private_data ) ;
return 0 ;
}
2015-11-24 15:12:14 -05:00
/** ORANGEFS implementation of VFS directory operations */
const struct file_operations orangefs_dir_operations = {
2015-07-17 10:38:12 -04:00
. read = generic_read_dir ,
2015-11-24 15:12:14 -05:00
. iterate = orangefs_readdir ,
. open = orangefs_dir_open ,
. release = orangefs_dir_release ,
2015-07-17 10:38:12 -04:00
} ;