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