2019-07-31 18:57:31 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2018-07-26 15:21:49 +03:00
/*
* Copyright ( C ) 2017 - 2018 HUAWEI , Inc .
2020-07-13 16:09:44 +03:00
* https : //www.huawei.com/
2018-07-26 15:21:49 +03:00
*/
# include "internal.h"
2019-03-28 23:14:58 +03:00
static void debug_one_dentry ( unsigned char d_type , const char * de_name ,
unsigned int de_namelen )
{
# ifdef CONFIG_EROFS_FS_DEBUG
/* since the on-disk name could not have the trailing '\0' */
unsigned char dbg_namebuf [ EROFS_NAME_LEN + 1 ] ;
memcpy ( dbg_namebuf , de_name , de_namelen ) ;
dbg_namebuf [ de_namelen ] = ' \0 ' ;
2019-09-04 05:09:09 +03:00
erofs_dbg ( " found dirent %s de_len %u d_type %d " , dbg_namebuf ,
de_namelen , d_type ) ;
2019-03-28 23:14:58 +03:00
# endif
}
2019-08-14 13:37:03 +03:00
static int erofs_fill_dentries ( struct inode * dir , struct dir_context * ctx ,
2019-02-19 12:25:25 +03:00
void * dentry_blk , unsigned int * ofs ,
unsigned int nameoff , unsigned int maxsize )
2018-07-26 15:21:49 +03:00
{
2019-06-17 15:55:29 +03:00
struct erofs_dirent * de = dentry_blk + * ofs ;
2018-07-26 15:21:49 +03:00
const struct erofs_dirent * end = dentry_blk + nameoff ;
while ( de < end ) {
const char * de_name ;
2019-03-28 23:14:58 +03:00
unsigned int de_namelen ;
2018-07-26 15:21:49 +03:00
unsigned char d_type ;
2019-08-16 10:11:42 +03:00
d_type = fs_ftype_to_dtype ( de - > file_type ) ;
2018-07-26 15:21:49 +03:00
nameoff = le16_to_cpu ( de - > nameoff ) ;
de_name = ( char * ) dentry_blk + nameoff ;
2019-03-28 23:14:58 +03:00
/* the last dirent in the block? */
if ( de + 1 > = end )
de_namelen = strnlen ( de_name , maxsize - nameoff ) ;
else
de_namelen = le16_to_cpu ( de [ 1 ] . nameoff ) - nameoff ;
2018-07-26 15:21:49 +03:00
2018-12-05 16:23:13 +03:00
/* a corrupted entry is found */
2019-08-29 19:38:27 +03:00
if ( nameoff + de_namelen > maxsize | |
de_namelen > EROFS_NAME_LEN ) {
2019-09-04 05:09:09 +03:00
erofs_err ( dir - > i_sb , " bogus dirent @ nid %llu " ,
EROFS_I ( dir ) - > nid ) ;
2018-12-05 16:23:13 +03:00
DBG_BUGON ( 1 ) ;
2019-08-14 13:37:03 +03:00
return - EFSCORRUPTED ;
2018-12-05 16:23:13 +03:00
}
2018-07-26 15:21:49 +03:00
2019-03-28 23:14:58 +03:00
debug_one_dentry ( d_type , de_name , de_namelen ) ;
2018-07-26 15:21:49 +03:00
if ( ! dir_emit ( ctx , de_name , de_namelen ,
2018-12-11 13:54:41 +03:00
le64_to_cpu ( de - > nid ) , d_type ) )
/* stopped by some reason */
2018-07-26 15:21:49 +03:00
return 1 ;
+ + de ;
* ofs + = sizeof ( struct erofs_dirent ) ;
}
* ofs = maxsize ;
return 0 ;
}
static int erofs_readdir ( struct file * f , struct dir_context * ctx )
{
struct inode * dir = file_inode ( f ) ;
struct address_space * mapping = dir - > i_mapping ;
const size_t dirsize = i_size_read ( dir ) ;
2018-09-10 22:41:14 +03:00
unsigned int i = ctx - > pos / EROFS_BLKSIZ ;
unsigned int ofs = ctx - > pos % EROFS_BLKSIZ ;
2018-07-26 15:21:49 +03:00
int err = 0 ;
bool initial = true ;
while ( ctx - > pos < dirsize ) {
struct page * dentry_page ;
struct erofs_dirent * de ;
2018-09-10 22:41:14 +03:00
unsigned int nameoff , maxsize ;
2018-07-26 15:21:49 +03:00
dentry_page = read_mapping_page ( mapping , i , NULL ) ;
2019-08-18 15:54:57 +03:00
if ( dentry_page = = ERR_PTR ( - ENOMEM ) ) {
err = - ENOMEM ;
break ;
} else if ( IS_ERR ( dentry_page ) ) {
2019-09-04 05:09:09 +03:00
erofs_err ( dir - > i_sb ,
" fail to readdir of logical block %u of nid %llu " ,
i , EROFS_I ( dir ) - > nid ) ;
2019-08-18 15:54:57 +03:00
err = - EFSCORRUPTED ;
break ;
}
2018-07-26 15:21:49 +03:00
de = ( struct erofs_dirent * ) kmap ( dentry_page ) ;
nameoff = le16_to_cpu ( de - > nameoff ) ;
2019-08-29 19:38:27 +03:00
if ( nameoff < sizeof ( struct erofs_dirent ) | |
nameoff > = PAGE_SIZE ) {
2019-09-04 05:09:09 +03:00
erofs_err ( dir - > i_sb ,
" invalid de[0].nameoff %u @ nid %llu " ,
nameoff , EROFS_I ( dir ) - > nid ) ;
2019-08-14 13:37:03 +03:00
err = - EFSCORRUPTED ;
2018-07-26 15:21:49 +03:00
goto skip_this ;
}
2018-09-10 22:41:14 +03:00
maxsize = min_t ( unsigned int ,
dirsize - ctx - > pos + ofs , PAGE_SIZE ) ;
2018-07-26 15:21:49 +03:00
/* search dirents at the arbitrary position */
2019-08-29 19:38:27 +03:00
if ( initial ) {
2018-07-26 15:21:49 +03:00
initial = false ;
ofs = roundup ( ofs , sizeof ( struct erofs_dirent ) ) ;
2019-08-29 19:38:27 +03:00
if ( ofs > = nameoff )
2018-07-26 15:21:49 +03:00
goto skip_this ;
}
2019-08-14 13:37:03 +03:00
err = erofs_fill_dentries ( dir , ctx , de , & ofs ,
nameoff , maxsize ) ;
2018-07-26 15:21:49 +03:00
skip_this :
kunmap ( dentry_page ) ;
put_page ( dentry_page ) ;
ctx - > pos = blknr_to_addr ( i ) + ofs ;
2019-08-29 19:38:27 +03:00
if ( err )
2018-07-26 15:21:49 +03:00
break ;
+ + i ;
ofs = 0 ;
}
return err < 0 ? err : 0 ;
}
const struct file_operations erofs_dir_fops = {
. llseek = generic_file_llseek ,
. read = generic_read_dir ,
2019-02-21 05:34:11 +03:00
. iterate_shared = erofs_readdir ,
2018-07-26 15:21:49 +03:00
} ;