2005-04-17 02:20:36 +04:00
/*
* linux / fs / readdir . c
*
* Copyright ( C ) 1995 Linus Torvalds
*/
2010-08-10 04:20:22 +04:00
# include <linux/stddef.h>
2007-05-08 11:29:02 +04:00
# include <linux/kernel.h>
2011-11-17 08:57:37 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/time.h>
# include <linux/mm.h>
# include <linux/errno.h>
# include <linux/stat.h>
# include <linux/file.h>
# include <linux/fs.h>
2014-06-05 03:05:41 +04:00
# include <linux/fsnotify.h>
2005-04-17 02:20:36 +04:00
# include <linux/dirent.h>
# include <linux/security.h>
# include <linux/syscalls.h>
# include <linux/unistd.h>
2017-04-09 01:10:08 +03:00
# include <linux/compat.h>
2005-04-17 02:20:36 +04:00
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
2013-05-15 21:52:59 +04:00
int iterate_dir ( struct file * file , struct dir_context * ctx )
2005-04-17 02:20:36 +04:00
{
2013-01-24 02:07:38 +04:00
struct inode * inode = file_inode ( file ) ;
2016-04-21 06:08:32 +03:00
bool shared = false ;
2005-04-17 02:20:36 +04:00
int res = - ENOTDIR ;
2016-04-21 06:08:32 +03:00
if ( file - > f_op - > iterate_shared )
shared = true ;
else if ( ! file - > f_op - > iterate )
2005-04-17 02:20:36 +04:00
goto out ;
res = security_file_permission ( file , MAY_READ ) ;
if ( res )
goto out ;
2016-05-26 07:05:12 +03:00
if ( shared ) {
2016-04-21 06:08:32 +03:00
inode_lock_shared ( inode ) ;
2016-05-26 07:05:12 +03:00
} else {
res = down_write_killable ( & inode - > i_rwsem ) ;
if ( res )
goto out ;
}
2007-12-07 01:39:54 +03:00
2005-04-17 02:20:36 +04:00
res = - ENOENT ;
if ( ! IS_DEADDIR ( inode ) ) {
2013-05-23 05:44:23 +04:00
ctx - > pos = file - > f_pos ;
2016-04-21 06:08:32 +03:00
if ( shared )
res = file - > f_op - > iterate_shared ( file , ctx ) ;
else
res = file - > f_op - > iterate ( file , ctx ) ;
2013-05-23 05:44:23 +04:00
file - > f_pos = ctx - > pos ;
2014-06-05 03:05:41 +04:00
fsnotify_access ( file ) ;
2005-04-17 02:20:36 +04:00
file_accessed ( file ) ;
}
2016-04-21 06:08:32 +03:00
if ( shared )
inode_unlock_shared ( inode ) ;
else
inode_unlock ( inode ) ;
2005-04-17 02:20:36 +04:00
out :
return res ;
}
2013-05-15 21:52:59 +04:00
EXPORT_SYMBOL ( iterate_dir ) ;
2005-04-17 02:20:36 +04:00
/*
* Traditional linux readdir ( ) handling . .
*
* " count=1 " is a special case , meaning that the buffer is one
* dirent - structure in size and that the code can ' t handle more
* anyway . Thus the special " fillonedir() " function for that
* case ( the low - level handlers don ' t need to care about this ) .
*/
# ifdef __ARCH_WANT_OLD_READDIR
struct old_linux_dirent {
unsigned long d_ino ;
unsigned long d_offset ;
unsigned short d_namlen ;
char d_name [ 1 ] ;
} ;
struct readdir_callback {
2013-05-15 21:52:59 +04:00
struct dir_context ctx ;
2005-04-17 02:20:36 +04:00
struct old_linux_dirent __user * dirent ;
int result ;
} ;
2014-10-30 19:37:34 +03:00
static int fillonedir ( struct dir_context * ctx , const char * name , int namlen ,
loff_t offset , u64 ino , unsigned int d_type )
2005-04-17 02:20:36 +04:00
{
2014-10-30 19:37:34 +03:00
struct readdir_callback * buf =
container_of ( ctx , struct readdir_callback , ctx ) ;
2005-04-17 02:20:36 +04:00
struct old_linux_dirent __user * dirent ;
2006-10-03 12:13:46 +04:00
unsigned long d_ino ;
2005-04-17 02:20:36 +04:00
if ( buf - > result )
return - EINVAL ;
2006-10-03 12:13:46 +04:00
d_ino = ino ;
2008-08-12 08:28:24 +04:00
if ( sizeof ( d_ino ) < sizeof ( ino ) & & d_ino ! = ino ) {
buf - > result = - EOVERFLOW ;
2006-10-03 12:13:46 +04:00
return - EOVERFLOW ;
2008-08-12 08:28:24 +04:00
}
2005-04-17 02:20:36 +04:00
buf - > result + + ;
dirent = buf - > dirent ;
if ( ! access_ok ( VERIFY_WRITE , dirent ,
( unsigned long ) ( dirent - > d_name + namlen + 1 ) -
( unsigned long ) dirent ) )
goto efault ;
2006-10-03 12:13:46 +04:00
if ( __put_user ( d_ino , & dirent - > d_ino ) | |
2005-04-17 02:20:36 +04:00
__put_user ( offset , & dirent - > d_offset ) | |
__put_user ( namlen , & dirent - > d_namlen ) | |
__copy_to_user ( dirent - > d_name , name , namlen ) | |
__put_user ( 0 , dirent - > d_name + namlen ) )
goto efault ;
return 0 ;
efault :
buf - > result = - EFAULT ;
return - EFAULT ;
}
2009-01-14 16:14:34 +03:00
SYSCALL_DEFINE3 ( old_readdir , unsigned int , fd ,
struct old_linux_dirent __user * , dirent , unsigned int , count )
2005-04-17 02:20:36 +04:00
{
int error ;
2016-04-21 00:08:21 +03:00
struct fd f = fdget_pos ( fd ) ;
2013-05-23 06:22:04 +04:00
struct readdir_callback buf = {
. ctx . actor = fillonedir ,
. dirent = dirent
} ;
2005-04-17 02:20:36 +04:00
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2012-04-22 02:40:32 +04:00
return - EBADF ;
2005-04-17 02:20:36 +04:00
2013-05-15 21:52:59 +04:00
error = iterate_dir ( f . file , & buf . ctx ) ;
2008-08-24 15:29:52 +04:00
if ( buf . result )
2005-04-17 02:20:36 +04:00
error = buf . result ;
2016-04-21 00:08:21 +03:00
fdput_pos ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
# endif /* __ARCH_WANT_OLD_READDIR */
/*
* New , all - improved , singing , dancing , iBCS2 - compliant getdents ( )
* interface .
*/
struct linux_dirent {
unsigned long d_ino ;
unsigned long d_off ;
unsigned short d_reclen ;
char d_name [ 1 ] ;
} ;
struct getdents_callback {
2013-05-15 21:52:59 +04:00
struct dir_context ctx ;
2005-04-17 02:20:36 +04:00
struct linux_dirent __user * current_dir ;
struct linux_dirent __user * previous ;
int count ;
int error ;
} ;
2014-10-30 19:37:34 +03:00
static int filldir ( struct dir_context * ctx , const char * name , int namlen ,
loff_t offset , u64 ino , unsigned int d_type )
2005-04-17 02:20:36 +04:00
{
struct linux_dirent __user * dirent ;
2014-10-30 19:37:34 +03:00
struct getdents_callback * buf =
container_of ( ctx , struct getdents_callback , ctx ) ;
2006-10-03 12:13:46 +04:00
unsigned long d_ino ;
2010-08-10 04:20:22 +04:00
int reclen = ALIGN ( offsetof ( struct linux_dirent , d_name ) + namlen + 2 ,
sizeof ( long ) ) ;
2005-04-17 02:20:36 +04:00
buf - > error = - EINVAL ; /* only used if we fail.. */
if ( reclen > buf - > count )
return - EINVAL ;
2006-10-03 12:13:46 +04:00
d_ino = ino ;
2008-08-12 08:28:24 +04:00
if ( sizeof ( d_ino ) < sizeof ( ino ) & & d_ino ! = ino ) {
buf - > error = - EOVERFLOW ;
2006-10-03 12:13:46 +04:00
return - EOVERFLOW ;
2008-08-12 08:28:24 +04:00
}
2005-04-17 02:20:36 +04:00
dirent = buf - > previous ;
if ( dirent ) {
2016-04-24 05:50:07 +03:00
if ( signal_pending ( current ) )
return - EINTR ;
2005-04-17 02:20:36 +04:00
if ( __put_user ( offset , & dirent - > d_off ) )
goto efault ;
}
dirent = buf - > current_dir ;
2006-10-03 12:13:46 +04:00
if ( __put_user ( d_ino , & dirent - > d_ino ) )
2005-04-17 02:20:36 +04:00
goto efault ;
if ( __put_user ( reclen , & dirent - > d_reclen ) )
goto efault ;
if ( copy_to_user ( dirent - > d_name , name , namlen ) )
goto efault ;
if ( __put_user ( 0 , dirent - > d_name + namlen ) )
goto efault ;
if ( __put_user ( d_type , ( char __user * ) dirent + reclen - 1 ) )
goto efault ;
buf - > previous = dirent ;
dirent = ( void __user * ) dirent + reclen ;
buf - > current_dir = dirent ;
buf - > count - = reclen ;
return 0 ;
efault :
buf - > error = - EFAULT ;
return - EFAULT ;
}
2009-01-14 16:14:23 +03:00
SYSCALL_DEFINE3 ( getdents , unsigned int , fd ,
struct linux_dirent __user * , dirent , unsigned int , count )
2005-04-17 02:20:36 +04:00
{
2012-08-28 20:52:22 +04:00
struct fd f ;
2005-04-17 02:20:36 +04:00
struct linux_dirent __user * lastdirent ;
2013-05-23 06:22:04 +04:00
struct getdents_callback buf = {
. ctx . actor = filldir ,
. count = count ,
. current_dir = dirent
} ;
2005-04-17 02:20:36 +04:00
int error ;
if ( ! access_ok ( VERIFY_WRITE , dirent , count ) )
2012-04-22 02:40:32 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2016-04-21 00:08:21 +03:00
f = fdget_pos ( fd ) ;
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2012-04-22 02:40:32 +04:00
return - EBADF ;
2005-04-17 02:20:36 +04:00
2013-05-15 21:52:59 +04:00
error = iterate_dir ( f . file , & buf . ctx ) ;
2008-08-24 15:29:52 +04:00
if ( error > = 0 )
error = buf . error ;
2005-04-17 02:20:36 +04:00
lastdirent = buf . previous ;
if ( lastdirent ) {
2013-05-16 02:49:12 +04:00
if ( put_user ( buf . ctx . pos , & lastdirent - > d_off ) )
2005-04-17 02:20:36 +04:00
error = - EFAULT ;
else
error = count - buf . count ;
}
2016-04-21 00:08:21 +03:00
fdput_pos ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
struct getdents_callback64 {
2013-05-15 21:52:59 +04:00
struct dir_context ctx ;
2005-04-17 02:20:36 +04:00
struct linux_dirent64 __user * current_dir ;
struct linux_dirent64 __user * previous ;
int count ;
int error ;
} ;
2014-10-30 19:37:34 +03:00
static int filldir64 ( struct dir_context * ctx , const char * name , int namlen ,
loff_t offset , u64 ino , unsigned int d_type )
2005-04-17 02:20:36 +04:00
{
struct linux_dirent64 __user * dirent ;
2014-10-30 19:37:34 +03:00
struct getdents_callback64 * buf =
container_of ( ctx , struct getdents_callback64 , ctx ) ;
2010-08-10 04:20:22 +04:00
int reclen = ALIGN ( offsetof ( struct linux_dirent64 , d_name ) + namlen + 1 ,
sizeof ( u64 ) ) ;
2005-04-17 02:20:36 +04:00
buf - > error = - EINVAL ; /* only used if we fail.. */
if ( reclen > buf - > count )
return - EINVAL ;
dirent = buf - > previous ;
if ( dirent ) {
2016-04-24 05:50:07 +03:00
if ( signal_pending ( current ) )
return - EINTR ;
2005-04-17 02:20:36 +04:00
if ( __put_user ( offset , & dirent - > d_off ) )
goto efault ;
}
dirent = buf - > current_dir ;
if ( __put_user ( ino , & dirent - > d_ino ) )
goto efault ;
if ( __put_user ( 0 , & dirent - > d_off ) )
goto efault ;
if ( __put_user ( reclen , & dirent - > d_reclen ) )
goto efault ;
if ( __put_user ( d_type , & dirent - > d_type ) )
goto efault ;
if ( copy_to_user ( dirent - > d_name , name , namlen ) )
goto efault ;
if ( __put_user ( 0 , dirent - > d_name + namlen ) )
goto efault ;
buf - > previous = dirent ;
dirent = ( void __user * ) dirent + reclen ;
buf - > current_dir = dirent ;
buf - > count - = reclen ;
return 0 ;
efault :
buf - > error = - EFAULT ;
return - EFAULT ;
}
2009-01-14 16:14:23 +03:00
SYSCALL_DEFINE3 ( getdents64 , unsigned int , fd ,
struct linux_dirent64 __user * , dirent , unsigned int , count )
2005-04-17 02:20:36 +04:00
{
2012-08-28 20:52:22 +04:00
struct fd f ;
2005-04-17 02:20:36 +04:00
struct linux_dirent64 __user * lastdirent ;
2013-05-23 06:22:04 +04:00
struct getdents_callback64 buf = {
. ctx . actor = filldir64 ,
. count = count ,
. current_dir = dirent
} ;
2005-04-17 02:20:36 +04:00
int error ;
if ( ! access_ok ( VERIFY_WRITE , dirent , count ) )
2012-04-22 02:40:32 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2016-04-21 00:08:21 +03:00
f = fdget_pos ( fd ) ;
2012-08-28 20:52:22 +04:00
if ( ! f . file )
2012-04-22 02:40:32 +04:00
return - EBADF ;
2005-04-17 02:20:36 +04:00
2013-05-15 21:52:59 +04:00
error = iterate_dir ( f . file , & buf . ctx ) ;
2008-08-24 15:29:52 +04:00
if ( error > = 0 )
error = buf . error ;
2005-04-17 02:20:36 +04:00
lastdirent = buf . previous ;
if ( lastdirent ) {
2013-05-16 02:49:12 +04:00
typeof ( lastdirent - > d_off ) d_off = buf . ctx . pos ;
2005-04-17 02:20:36 +04:00
if ( __put_user ( d_off , & lastdirent - > d_off ) )
2008-08-24 15:29:52 +04:00
error = - EFAULT ;
else
error = count - buf . count ;
2005-04-17 02:20:36 +04:00
}
2016-04-21 00:08:21 +03:00
fdput_pos ( f ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
2017-04-09 01:10:08 +03:00
# ifdef CONFIG_COMPAT
struct compat_old_linux_dirent {
compat_ulong_t d_ino ;
compat_ulong_t d_offset ;
unsigned short d_namlen ;
char d_name [ 1 ] ;
} ;
struct compat_readdir_callback {
struct dir_context ctx ;
struct compat_old_linux_dirent __user * dirent ;
int result ;
} ;
static int compat_fillonedir ( struct dir_context * ctx , const char * name ,
int namlen , loff_t offset , u64 ino ,
unsigned int d_type )
{
struct compat_readdir_callback * buf =
container_of ( ctx , struct compat_readdir_callback , ctx ) ;
struct compat_old_linux_dirent __user * dirent ;
compat_ulong_t d_ino ;
if ( buf - > result )
return - EINVAL ;
d_ino = ino ;
if ( sizeof ( d_ino ) < sizeof ( ino ) & & d_ino ! = ino ) {
buf - > result = - EOVERFLOW ;
return - EOVERFLOW ;
}
buf - > result + + ;
dirent = buf - > dirent ;
if ( ! access_ok ( VERIFY_WRITE , dirent ,
( unsigned long ) ( dirent - > d_name + namlen + 1 ) -
( unsigned long ) dirent ) )
goto efault ;
if ( __put_user ( d_ino , & dirent - > d_ino ) | |
__put_user ( offset , & dirent - > d_offset ) | |
__put_user ( namlen , & dirent - > d_namlen ) | |
__copy_to_user ( dirent - > d_name , name , namlen ) | |
__put_user ( 0 , dirent - > d_name + namlen ) )
goto efault ;
return 0 ;
efault :
buf - > result = - EFAULT ;
return - EFAULT ;
}
COMPAT_SYSCALL_DEFINE3 ( old_readdir , unsigned int , fd ,
struct compat_old_linux_dirent __user * , dirent , unsigned int , count )
{
int error ;
struct fd f = fdget_pos ( fd ) ;
struct compat_readdir_callback buf = {
. ctx . actor = compat_fillonedir ,
. dirent = dirent
} ;
if ( ! f . file )
return - EBADF ;
error = iterate_dir ( f . file , & buf . ctx ) ;
if ( buf . result )
error = buf . result ;
fdput_pos ( f ) ;
return error ;
}
struct compat_linux_dirent {
compat_ulong_t d_ino ;
compat_ulong_t d_off ;
unsigned short d_reclen ;
char d_name [ 1 ] ;
} ;
struct compat_getdents_callback {
struct dir_context ctx ;
struct compat_linux_dirent __user * current_dir ;
struct compat_linux_dirent __user * previous ;
int count ;
int error ;
} ;
static int compat_filldir ( struct dir_context * ctx , const char * name , int namlen ,
loff_t offset , u64 ino , unsigned int d_type )
{
struct compat_linux_dirent __user * dirent ;
struct compat_getdents_callback * buf =
container_of ( ctx , struct compat_getdents_callback , ctx ) ;
compat_ulong_t d_ino ;
int reclen = ALIGN ( offsetof ( struct compat_linux_dirent , d_name ) +
namlen + 2 , sizeof ( compat_long_t ) ) ;
buf - > error = - EINVAL ; /* only used if we fail.. */
if ( reclen > buf - > count )
return - EINVAL ;
d_ino = ino ;
if ( sizeof ( d_ino ) < sizeof ( ino ) & & d_ino ! = ino ) {
buf - > error = - EOVERFLOW ;
return - EOVERFLOW ;
}
dirent = buf - > previous ;
if ( dirent ) {
if ( signal_pending ( current ) )
return - EINTR ;
if ( __put_user ( offset , & dirent - > d_off ) )
goto efault ;
}
dirent = buf - > current_dir ;
if ( __put_user ( d_ino , & dirent - > d_ino ) )
goto efault ;
if ( __put_user ( reclen , & dirent - > d_reclen ) )
goto efault ;
if ( copy_to_user ( dirent - > d_name , name , namlen ) )
goto efault ;
if ( __put_user ( 0 , dirent - > d_name + namlen ) )
goto efault ;
if ( __put_user ( d_type , ( char __user * ) dirent + reclen - 1 ) )
goto efault ;
buf - > previous = dirent ;
dirent = ( void __user * ) dirent + reclen ;
buf - > current_dir = dirent ;
buf - > count - = reclen ;
return 0 ;
efault :
buf - > error = - EFAULT ;
return - EFAULT ;
}
COMPAT_SYSCALL_DEFINE3 ( getdents , unsigned int , fd ,
struct compat_linux_dirent __user * , dirent , unsigned int , count )
{
struct fd f ;
struct compat_linux_dirent __user * lastdirent ;
struct compat_getdents_callback buf = {
. ctx . actor = compat_filldir ,
. current_dir = dirent ,
. count = count
} ;
int error ;
if ( ! access_ok ( VERIFY_WRITE , dirent , count ) )
return - EFAULT ;
f = fdget_pos ( fd ) ;
if ( ! f . file )
return - EBADF ;
error = iterate_dir ( f . file , & buf . ctx ) ;
if ( error > = 0 )
error = buf . error ;
lastdirent = buf . previous ;
if ( lastdirent ) {
if ( put_user ( buf . ctx . pos , & lastdirent - > d_off ) )
error = - EFAULT ;
else
error = count - buf . count ;
}
fdput_pos ( f ) ;
return error ;
}
# endif