2015-07-17 17:38:12 +03:00
/*
* ( C ) 2001 Clemson University and The University of Chicago
*
* See COPYING in top - level directory .
*/
# include "protocol.h"
2015-12-04 20:56:14 +03:00
# include "orangefs-kernel.h"
# include "orangefs-bufmap.h"
2015-07-17 17:38:12 +03:00
/*
2016-01-15 21:10:52 +03:00
* decode routine used by kmod to deal with the blob sent from
* userspace for readdirs . The blob contains zero or more of these
* sub - blobs :
* __u32 - represents length of the character string that follows .
* string - between 1 and ORANGEFS_NAME_MAX bytes long .
* padding - ( if needed ) to cause the __u32 plus the string to be
* eight byte aligned .
* khandle - sizeof ( khandle ) bytes .
2015-07-17 17:38:12 +03:00
*/
2015-10-10 01:11:10 +03:00
static long decode_dirents ( char * ptr , size_t size ,
2015-11-24 23:12:14 +03:00
struct orangefs_readdir_response_s * readdir )
2015-07-17 17:38:12 +03:00
{
int i ;
2015-11-24 23:12:14 +03:00
struct orangefs_readdir_response_s * rd =
( struct orangefs_readdir_response_s * ) ptr ;
2015-07-17 17:38:12 +03:00
char * buf = ptr ;
2016-01-15 21:10:52 +03:00
int khandle_size = sizeof ( struct orangefs_khandle ) ;
size_t offset = offsetof ( struct orangefs_readdir_response_s ,
dirent_array ) ;
/* 8 reflects eight byte alignment */
int smallest_blob = khandle_size + 8 ;
__u32 len ;
int aligned_len ;
int sizeof_u32 = sizeof ( __u32 ) ;
long ret ;
2015-07-17 17:38:12 +03:00
2016-01-15 21:10:52 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG , " %s: size:%zu: \n " , __func__ , size ) ;
/* size is = offset on empty dirs, > offset on non-empty dirs... */
if ( size < offset ) {
gossip_err ( " %s: size:%zu: offset:%zu: \n " ,
__func__ ,
size ,
offset ) ;
ret = - EINVAL ;
goto out ;
}
if ( ( size = = offset ) & & ( readdir - > orangefs_dirent_outcount ! = 0 ) ) {
gossip_err ( " %s: size:%zu: dirent_outcount:%d: \n " ,
__func__ ,
size ,
readdir - > orangefs_dirent_outcount ) ;
ret = - EINVAL ;
goto out ;
}
2015-10-10 01:11:10 +03:00
2015-07-17 17:38:12 +03:00
readdir - > token = rd - > token ;
2015-11-24 23:12:14 +03:00
readdir - > orangefs_dirent_outcount = rd - > orangefs_dirent_outcount ;
readdir - > dirent_array = kcalloc ( readdir - > orangefs_dirent_outcount ,
2015-07-17 17:38:12 +03:00
sizeof ( * readdir - > dirent_array ) ,
GFP_KERNEL ) ;
2016-01-15 21:10:52 +03:00
if ( readdir - > dirent_array = = NULL ) {
gossip_err ( " %s: kcalloc failed. \n " , __func__ ) ;
ret = - ENOMEM ;
goto out ;
}
2015-10-10 01:11:10 +03:00
2016-01-15 21:10:52 +03:00
buf + = offset ;
size - = offset ;
2015-10-10 01:11:10 +03:00
2015-11-24 23:12:14 +03:00
for ( i = 0 ; i < readdir - > orangefs_dirent_outcount ; i + + ) {
2016-01-15 21:10:52 +03:00
if ( size < smallest_blob ) {
gossip_err ( " %s: size:%zu: smallest_blob:%d: \n " ,
__func__ ,
size ,
smallest_blob ) ;
ret = - EINVAL ;
goto free ;
}
2015-10-10 01:11:10 +03:00
len = * ( __u32 * ) buf ;
2016-01-15 21:10:52 +03:00
if ( ( len < 1 ) | | ( len > ORANGEFS_NAME_MAX ) ) {
gossip_err ( " %s: len:%d: \n " , __func__ , len ) ;
ret = - EINVAL ;
goto free ;
}
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: size:%zu: len:%d: \n " ,
__func__ ,
size ,
len ) ;
readdir - > dirent_array [ i ] . d_name = buf + sizeof_u32 ;
2015-10-10 00:43:15 +03:00
readdir - > dirent_array [ i ] . d_length = len ;
2015-10-10 01:11:10 +03:00
2016-01-04 23:05:28 +03:00
/*
2016-01-15 21:10:52 +03:00
* Calculate " aligned " length of this string and its
* associated __u32 descriptor .
*/
aligned_len = ( ( sizeof_u32 + len + 1 ) + 7 ) & ~ 7 ;
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: aligned_len:%d: \n " ,
__func__ ,
aligned_len ) ;
/*
* The end of the blob should coincide with the end
* of the last sub - blob .
2016-01-04 23:05:28 +03:00
*/
2016-01-15 21:10:52 +03:00
if ( size < aligned_len + khandle_size ) {
gossip_err ( " %s: ran off the end of the blob. \n " ,
__func__ ) ;
ret = - EINVAL ;
goto free ;
}
size - = aligned_len + khandle_size ;
2015-10-10 01:11:10 +03:00
2016-01-15 21:10:52 +03:00
buf + = aligned_len ;
2015-10-10 01:11:10 +03:00
2015-07-17 17:38:12 +03:00
readdir - > dirent_array [ i ] . khandle =
2015-11-24 23:12:14 +03:00
* ( struct orangefs_khandle * ) buf ;
2016-01-15 21:10:52 +03:00
buf + = khandle_size ;
2015-07-17 17:38:12 +03:00
}
2016-01-15 21:10:52 +03:00
ret = buf - ptr ;
gossip_debug ( GOSSIP_DIR_DEBUG , " %s: returning:%ld: \n " , __func__ , ret ) ;
goto out ;
free :
2015-10-10 01:11:10 +03:00
kfree ( readdir - > dirent_array ) ;
readdir - > dirent_array = NULL ;
2016-01-15 21:10:52 +03:00
out :
return ret ;
2015-07-17 17:38:12 +03:00
}
/*
* Read directory entries from an instance of an open directory .
*/
2015-11-24 23:12:14 +03:00
static int orangefs_readdir ( struct file * file , struct dir_context * ctx )
2015-07-17 17:38:12 +03:00
{
int ret = 0 ;
int buffer_index ;
2015-09-23 23:48:40 +03:00
/*
* ptoken supports Orangefs ' distributed directory logic , added
* in 2.9 .2 .
*/
2015-07-17 17:38:12 +03:00
__u64 * ptoken = file - > private_data ;
__u64 pos = 0 ;
ino_t ino = 0 ;
struct dentry * dentry = file - > f_path . dentry ;
2015-11-24 23:12:14 +03:00
struct orangefs_kernel_op_s * new_op = NULL ;
struct orangefs_inode_s * orangefs_inode = ORANGEFS_I ( dentry - > d_inode ) ;
2015-07-17 17:38:12 +03:00
int buffer_full = 0 ;
2016-02-17 03:54:13 +03:00
struct orangefs_readdir_response_s readdir_response ;
void * dents_buf ;
2015-07-17 17:38:12 +03:00
int i = 0 ;
int len = 0 ;
ino_t current_ino = 0 ;
char * current_entry = NULL ;
long bytes_decoded ;
2015-09-23 23:48:40 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: ctx->pos:%lld, ptoken = %llu \n " ,
__func__ ,
lld ( ctx - > pos ) ,
llu ( * ptoken ) ) ;
2015-07-17 17:38:12 +03:00
pos = ( __u64 ) ctx - > pos ;
/* are we done? */
2015-11-24 23:12:14 +03:00
if ( pos = = ORANGEFS_READDIR_END ) {
2015-07-17 17:38:12 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" Skipping to termination path \n " ) ;
return 0 ;
}
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-11-24 23:12:14 +03:00
" orangefs_readdir called on %s (pos=%llu) \n " ,
2015-07-17 17:38:12 +03:00
dentry - > d_name . name , llu ( pos ) ) ;
2016-02-17 03:54:13 +03:00
memset ( & readdir_response , 0 , sizeof ( readdir_response ) ) ;
2015-07-17 17:38:12 +03:00
2015-11-24 23:12:14 +03:00
new_op = op_alloc ( ORANGEFS_VFS_OP_READDIR ) ;
2015-07-17 17:38:12 +03:00
if ( ! new_op )
return - ENOMEM ;
2016-02-17 20:55:42 +03:00
/*
* Only the indices are shared . No memory is actually shared , but the
* mechanism is used .
*/
2015-07-17 17:38:12 +03:00
new_op - > uses_shared_memory = 1 ;
2015-11-24 23:12:14 +03:00
new_op - > upcall . req . readdir . refn = orangefs_inode - > refn ;
2016-01-04 23:05:28 +03:00
new_op - > upcall . req . readdir . max_dirent_count =
ORANGEFS_MAX_DIRENT_COUNT_READDIR ;
2015-07-17 17:38:12 +03: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-02-17 04:10:26 +03:00
buffer_index = orangefs_readdir_index_get ( ) ;
if ( buffer_index < 0 ) {
ret = buffer_index ;
2016-01-04 23:05:28 +03:00
gossip_lerr ( " orangefs_readdir: orangefs_readdir_index_get() failure (%d) \n " ,
2015-07-17 17:38:12 +03:00
ret ) ;
goto out_free_op ;
}
new_op - > upcall . req . readdir . buf_index = buffer_index ;
ret = service_operation ( new_op ,
2015-11-24 23:12:14 +03:00
" orangefs_readdir " ,
2015-07-17 17:38:12 +03: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 ) ;
2016-02-17 20:55:42 +03:00
orangefs_readdir_index_put ( buffer_index ) ;
2015-07-17 17:38:12 +03:00
if ( ret = = - EAGAIN & & op_state_purged ( new_op ) ) {
2016-02-17 20:55:42 +03:00
/* Client-core indices are invalid after it restarted. */
2015-07-17 17:38:12 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: Getting new buffer_index for retry of readdir.. \n " ,
__func__ ) ;
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-03-29 00:18:27 +03:00
goto out_free_op ;
2015-07-17 17:38:12 +03:00
}
if ( ret < 0 | | new_op - > downcall . status ! = 0 ) {
gossip_debug ( GOSSIP_DIR_DEBUG ,
" Readdir request failed. Status:%d \n " ,
new_op - > downcall . status ) ;
if ( ret > = 0 )
ret = new_op - > downcall . status ;
2016-03-29 00:18:27 +03:00
goto out_free_op ;
2016-02-17 03:54:13 +03:00
}
dents_buf = new_op - > downcall . trailer_buf ;
if ( dents_buf = = NULL ) {
gossip_err ( " Invalid NULL buffer in readdir response \n " ) ;
ret = - ENOMEM ;
2016-03-29 00:18:27 +03:00
goto out_free_op ;
2015-07-17 17:38:12 +03:00
}
2016-02-17 03:54:13 +03:00
bytes_decoded = decode_dirents ( dents_buf , new_op - > downcall . trailer_size ,
& readdir_response ) ;
2015-07-17 17:38:12 +03:00
if ( bytes_decoded < 0 ) {
ret = bytes_decoded ;
2016-02-17 03:54:13 +03:00
gossip_err ( " Could not decode readdir from buffer %d \n " , ret ) ;
goto out_vfree ;
2015-07-17 17:38:12 +03:00
}
if ( bytes_decoded ! = new_op - > downcall . trailer_size ) {
2015-11-24 23:12:14 +03:00
gossip_err ( " orangefs_readdir: # bytes decoded (%ld) "
2015-09-23 23:48:40 +03:00
" != trailer size (%ld) \n " ,
bytes_decoded ,
( long ) new_op - > downcall . trailer_size ) ;
2015-07-17 17:38:12 +03:00
ret = - EINVAL ;
goto out_destroy_handle ;
}
2015-09-23 23:48:40 +03:00
/*
2015-11-24 23:12:14 +03:00
* orangefs doesn ' t actually store dot and dot - dot , but
2015-09-23 23:48:40 +03:00
* we need to have them represented .
*/
2015-07-17 17:38:12 +03: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 23:48:40 +03:00
pos + = 1 ;
2015-07-17 17:38:12 +03: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 23:48:40 +03:00
pos + = 1 ;
2015-07-17 17:38:12 +03:00
}
2015-09-23 23:48:40 +03:00
/*
2015-11-24 23:12:14 +03:00
* we stored ORANGEFS_ITERATE_NEXT in ctx - > pos last time around
2015-09-23 23:48:40 +03:00
* to prevent " finding " dot and dot - dot on any iteration
* other than the first .
*/
2015-11-24 23:12:14 +03:00
if ( ctx - > pos = = ORANGEFS_ITERATE_NEXT )
2015-09-23 23:48:40 +03:00
ctx - > pos = 0 ;
2016-03-09 21:11:45 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
" %s: dirent_outcount:%d: \n " ,
__func__ ,
2016-02-17 03:54:13 +03:00
readdir_response . orangefs_dirent_outcount ) ;
2015-09-23 23:48:40 +03:00
for ( i = ctx - > pos ;
2016-02-17 03:54:13 +03:00
i < readdir_response . orangefs_dirent_outcount ;
2015-09-23 23:48:40 +03:00
i + + ) {
2016-02-17 03:54:13 +03:00
len = readdir_response . dirent_array [ i ] . d_length ;
current_entry = readdir_response . dirent_array [ i ] . d_name ;
2015-11-24 23:12:14 +03:00
current_ino = orangefs_khandle_to_ino (
2016-02-17 03:54:13 +03:00
& readdir_response . dirent_array [ i ] . khandle ) ;
2015-07-17 17:38:12 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-09-23 23:48:40 +03:00
" calling dir_emit for %s with len %d "
" , ctx->pos %ld \n " ,
2015-07-17 17:38:12 +03:00
current_entry ,
len ,
2015-09-23 23:48:40 +03: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 17:38:12 +03:00
ret =
dir_emit ( ctx , current_entry , len , current_ino , DT_UNKNOWN ) ;
2015-09-23 23:48:40 +03:00
if ( ! ret )
break ;
2015-07-17 17:38:12 +03:00
ctx - > pos + + ;
2015-09-23 23:48:40 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-07-17 17:38:12 +03:00
" %s: ctx->pos:%lld \n " ,
__func__ ,
lld ( ctx - > pos ) ) ;
}
2015-10-05 20:44:24 +03:00
/*
2015-09-23 23:48:40 +03:00
* we ran all the way through the last batch , set up for
* getting another batch . . .
*/
if ( ret ) {
2016-02-17 03:54:13 +03:00
* ptoken = readdir_response . token ;
2015-11-24 23:12:14 +03:00
ctx - > pos = ORANGEFS_ITERATE_NEXT ;
2015-07-17 17:38:12 +03:00
}
/*
* Did we hit the end of the directory ?
*/
2016-02-17 03:54:13 +03:00
if ( readdir_response . token = = ORANGEFS_READDIR_END & &
2015-07-17 17:38:12 +03:00
! buffer_full ) {
2015-09-23 23:48:40 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG ,
2015-11-24 23:12:14 +03:00
" End of dir detected; setting ctx->pos to ORANGEFS_READDIR_END. \n " ) ;
ctx - > pos = ORANGEFS_READDIR_END ;
2015-07-17 17:38:12 +03:00
}
out_destroy_handle :
2016-02-17 03:54:13 +03:00
/* kfree(NULL) is safe */
kfree ( readdir_response . dirent_array ) ;
out_vfree :
gossip_debug ( GOSSIP_DIR_DEBUG , " vfree %p \n " , dents_buf ) ;
vfree ( dents_buf ) ;
2015-07-17 17:38:12 +03:00
out_free_op :
op_release ( new_op ) ;
2015-11-24 23:12:14 +03:00
gossip_debug ( GOSSIP_DIR_DEBUG , " orangefs_readdir returning %d \n " , ret ) ;
2015-07-17 17:38:12 +03:00
return ret ;
}
2015-11-24 23:12:14 +03:00
static int orangefs_dir_open ( struct inode * inode , struct file * file )
2015-07-17 17:38:12 +03:00
{
__u64 * ptoken ;
file - > private_data = kmalloc ( sizeof ( __u64 ) , GFP_KERNEL ) ;
if ( ! file - > private_data )
return - ENOMEM ;
ptoken = file - > private_data ;
2015-11-24 23:12:14 +03:00
* ptoken = ORANGEFS_READDIR_START ;
2015-07-17 17:38:12 +03:00
return 0 ;
}
2015-11-24 23:12:14 +03:00
static int orangefs_dir_release ( struct inode * inode , struct file * file )
2015-07-17 17:38:12 +03:00
{
2015-11-24 23:12:14 +03:00
orangefs_flush_inode ( inode ) ;
2015-07-17 17:38:12 +03:00
kfree ( file - > private_data ) ;
return 0 ;
}
2015-11-24 23:12:14 +03:00
/** ORANGEFS implementation of VFS directory operations */
const struct file_operations orangefs_dir_operations = {
2015-07-17 17:38:12 +03:00
. read = generic_read_dir ,
2015-11-24 23:12:14 +03:00
. iterate = orangefs_readdir ,
. open = orangefs_dir_open ,
. release = orangefs_dir_release ,
2015-07-17 17:38:12 +03:00
} ;