2020-03-02 15:21:35 +09:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) 2012 - 2013 Samsung Electronics Co . , Ltd .
*/
# include <linux/slab.h>
2021-03-04 09:20:35 +09:00
# include <linux/compat.h>
2020-03-02 15:21:35 +09:00
# include <linux/bio.h>
# include <linux/buffer_head.h>
# include "exfat_raw.h"
# include "exfat_fs.h"
static int exfat_extract_uni_name ( struct exfat_dentry * ep ,
unsigned short * uniname )
{
int i , len = 0 ;
for ( i = 0 ; i < EXFAT_FILE_NAME_LEN ; i + + ) {
* uniname = le16_to_cpu ( ep - > dentry . name . unicode_0_14 [ i ] ) ;
if ( * uniname = = 0x0 )
return len ;
uniname + + ;
len + + ;
}
* uniname = 0x0 ;
return len ;
}
2023-01-14 13:09:48 +09:00
static int exfat_get_uniname_from_ext_entry ( struct super_block * sb ,
2020-03-02 15:21:35 +09:00
struct exfat_chain * p_dir , int entry , unsigned short * uniname )
{
2023-01-14 13:09:48 +09:00
int i , err ;
2022-11-17 11:37:13 +08:00
struct exfat_entry_set_cache es ;
2020-03-02 15:21:35 +09:00
2023-01-14 13:09:48 +09:00
err = exfat_get_dentry_set ( & es , sb , p_dir , entry , ES_ALL_ENTRIES ) ;
if ( err )
return err ;
2020-03-02 15:21:35 +09:00
/*
* First entry : file entry
* Second entry : stream - extension entry
* Third entry : first file - name entry
* So , the index of first file - name dentry should start from 2.
*/
2022-03-17 19:39:20 +08:00
for ( i = ES_IDX_FIRST_FILENAME ; i < es . num_entries ; i + + ) {
2022-11-17 11:37:13 +08:00
struct exfat_dentry * ep = exfat_get_dentry_cached ( & es , i ) ;
2020-05-20 16:56:41 +09:00
2020-03-02 15:21:35 +09:00
/* end of name entry */
if ( exfat_get_entry_type ( ep ) ! = TYPE_EXTEND )
2020-05-20 16:56:41 +09:00
break ;
2020-03-02 15:21:35 +09:00
exfat_extract_uni_name ( ep , uniname ) ;
uniname + = EXFAT_FILE_NAME_LEN ;
}
2022-03-17 18:12:40 +08:00
exfat_put_dentry_set ( & es , false ) ;
2023-01-14 13:09:48 +09:00
return 0 ;
2020-03-02 15:21:35 +09:00
}
/* read a directory entry from the opened directory */
2020-09-17 10:39:16 +09:00
static int exfat_readdir ( struct inode * inode , loff_t * cpos , struct exfat_dir_entry * dir_entry )
2020-03-02 15:21:35 +09:00
{
2023-01-14 13:09:48 +09:00
int i , dentries_per_clu , num_ext , err ;
2021-06-11 09:40:24 +09:00
unsigned int type , clu_offset , max_dentries ;
2020-03-02 15:21:35 +09:00
struct exfat_chain dir , clu ;
struct exfat_uni_name uni_name ;
struct exfat_dentry * ep ;
struct super_block * sb = inode - > i_sb ;
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
struct exfat_inode_info * ei = EXFAT_I ( inode ) ;
2020-09-17 10:39:16 +09:00
unsigned int dentry = EXFAT_B_TO_DEN ( * cpos ) & 0xFFFFFFFF ;
2020-03-02 15:21:35 +09:00
struct buffer_head * bh ;
/* check if the given file ID is opened */
if ( ei - > type ! = TYPE_DIR )
return - EPERM ;
if ( ei - > entry = = - 1 )
exfat_chain_set ( & dir , sbi - > root_dir , 0 , ALLOC_FAT_CHAIN ) ;
else
exfat_chain_set ( & dir , ei - > start_clu ,
EXFAT_B_TO_CLU ( i_size_read ( inode ) , sbi ) , ei - > flags ) ;
dentries_per_clu = sbi - > dentries_per_clu ;
2021-06-11 09:40:24 +09:00
max_dentries = ( unsigned int ) min_t ( u64 , MAX_EXFAT_DENTRIES ,
2022-08-16 16:55:06 +08:00
( u64 ) EXFAT_CLU_TO_DEN ( sbi - > num_clusters , sbi ) ) ;
2020-03-02 15:21:35 +09:00
2022-08-16 16:55:06 +08:00
clu_offset = EXFAT_DEN_TO_CLU ( dentry , sbi ) ;
2020-03-02 15:21:35 +09:00
exfat_chain_dup ( & clu , & dir ) ;
if ( clu . flags = = ALLOC_NO_FAT_CHAIN ) {
clu . dir + = clu_offset ;
clu . size - = clu_offset ;
} else {
/* hint_information */
if ( clu_offset > 0 & & ei - > hint_bmap . off ! = EXFAT_EOF_CLUSTER & &
ei - > hint_bmap . off > 0 & & clu_offset > = ei - > hint_bmap . off ) {
clu_offset - = ei - > hint_bmap . off ;
clu . dir = ei - > hint_bmap . clu ;
}
2022-10-20 14:27:37 +08:00
while ( clu_offset > 0 & & clu . dir ! = EXFAT_EOF_CLUSTER ) {
2020-03-02 15:21:35 +09:00
if ( exfat_get_next_cluster ( sb , & ( clu . dir ) ) )
return - EIO ;
clu_offset - - ;
}
}
2021-06-11 09:40:24 +09:00
while ( clu . dir ! = EXFAT_EOF_CLUSTER & & dentry < max_dentries ) {
2020-03-02 15:21:35 +09:00
i = dentry & ( dentries_per_clu - 1 ) ;
for ( ; i < dentries_per_clu ; i + + , dentry + + ) {
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , & clu , i , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
type = exfat_get_entry_type ( ep ) ;
if ( type = = TYPE_UNUSED ) {
brelse ( bh ) ;
break ;
}
if ( type ! = TYPE_FILE & & type ! = TYPE_DIR ) {
brelse ( bh ) ;
continue ;
}
2020-09-17 10:39:16 +09:00
num_ext = ep - > dentry . file . num_ext ;
2020-03-02 15:21:35 +09:00
dir_entry - > attr = le16_to_cpu ( ep - > dentry . file . attr ) ;
exfat_get_entry_time ( sbi , & dir_entry - > crtime ,
ep - > dentry . file . create_tz ,
ep - > dentry . file . create_time ,
ep - > dentry . file . create_date ,
2020-04-22 08:30:56 +09:00
ep - > dentry . file . create_time_cs ) ;
2020-03-02 15:21:35 +09:00
exfat_get_entry_time ( sbi , & dir_entry - > mtime ,
ep - > dentry . file . modify_tz ,
ep - > dentry . file . modify_time ,
ep - > dentry . file . modify_date ,
2020-04-22 08:30:56 +09:00
ep - > dentry . file . modify_time_cs ) ;
2020-03-02 15:21:35 +09:00
exfat_get_entry_time ( sbi , & dir_entry - > atime ,
ep - > dentry . file . access_tz ,
ep - > dentry . file . access_time ,
ep - > dentry . file . access_date ,
0 ) ;
* uni_name . name = 0x0 ;
2023-01-14 13:09:48 +09:00
err = exfat_get_uniname_from_ext_entry ( sb , & clu , i ,
2020-03-02 15:21:35 +09:00
uni_name . name ) ;
2023-01-14 13:09:48 +09:00
if ( err ) {
brelse ( bh ) ;
continue ;
}
2020-03-02 15:21:35 +09:00
exfat_utf16_to_nls ( sb , & uni_name ,
dir_entry - > namebuf . lfn ,
dir_entry - > namebuf . lfnbuf_len ) ;
brelse ( bh ) ;
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , & clu , i + 1 , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
dir_entry - > size =
le64_to_cpu ( ep - > dentry . stream . valid_size ) ;
2020-09-17 10:39:16 +09:00
dir_entry - > entry = dentry ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
2022-08-16 16:55:06 +08:00
ei - > hint_bmap . off = EXFAT_DEN_TO_CLU ( dentry , sbi ) ;
2020-03-02 15:21:35 +09:00
ei - > hint_bmap . clu = clu . dir ;
2020-09-17 10:39:16 +09:00
* cpos = EXFAT_DEN_TO_B ( dentry + 1 + num_ext ) ;
2020-03-02 15:21:35 +09:00
return 0 ;
}
if ( clu . flags = = ALLOC_NO_FAT_CHAIN ) {
if ( - - clu . size > 0 )
clu . dir + + ;
else
clu . dir = EXFAT_EOF_CLUSTER ;
} else {
if ( exfat_get_next_cluster ( sb , & ( clu . dir ) ) )
return - EIO ;
}
}
dir_entry - > namebuf . lfn [ 0 ] = ' \0 ' ;
2020-09-17 10:39:16 +09:00
* cpos = EXFAT_DEN_TO_B ( dentry ) ;
2020-03-02 15:21:35 +09:00
return 0 ;
}
static void exfat_init_namebuf ( struct exfat_dentry_namebuf * nb )
{
nb - > lfn = NULL ;
nb - > lfnbuf_len = 0 ;
}
static int exfat_alloc_namebuf ( struct exfat_dentry_namebuf * nb )
{
nb - > lfn = __getname ( ) ;
if ( ! nb - > lfn )
return - ENOMEM ;
nb - > lfnbuf_len = MAX_VFSNAME_BUF_SIZE ;
return 0 ;
}
static void exfat_free_namebuf ( struct exfat_dentry_namebuf * nb )
{
if ( ! nb - > lfn )
return ;
__putname ( nb - > lfn ) ;
exfat_init_namebuf ( nb ) ;
}
/* skip iterating emit_dots when dir is empty */
# define ITER_POS_FILLED_DOTS (2)
2022-08-03 21:27:11 -04:00
static int exfat_iterate ( struct file * file , struct dir_context * ctx )
2020-03-02 15:21:35 +09:00
{
2022-08-03 21:27:11 -04:00
struct inode * inode = file_inode ( file ) ;
2020-03-02 15:21:35 +09:00
struct super_block * sb = inode - > i_sb ;
struct inode * tmp ;
struct exfat_dir_entry de ;
struct exfat_dentry_namebuf * nb = & ( de . namebuf ) ;
struct exfat_inode_info * ei = EXFAT_I ( inode ) ;
unsigned long inum ;
loff_t cpos , i_pos ;
int err = 0 , fake_offset = 0 ;
exfat_init_namebuf ( nb ) ;
mutex_lock ( & EXFAT_SB ( sb ) - > s_lock ) ;
cpos = ctx - > pos ;
2022-08-03 21:27:11 -04:00
if ( ! dir_emit_dots ( file , ctx ) )
2020-03-02 15:21:35 +09:00
goto unlock ;
if ( ctx - > pos = = ITER_POS_FILLED_DOTS ) {
cpos = 0 ;
fake_offset = 1 ;
}
2022-09-22 14:43:47 +08:00
cpos = round_up ( cpos , DENTRY_SIZE ) ;
2020-03-02 15:21:35 +09:00
/* name buffer should be allocated before use */
err = exfat_alloc_namebuf ( nb ) ;
if ( err )
goto unlock ;
get_new :
2021-06-11 09:40:24 +09:00
if ( ei - > flags = = ALLOC_NO_FAT_CHAIN & & cpos > = i_size_read ( inode ) )
2020-03-02 15:21:35 +09:00
goto end_of_dir ;
2020-09-17 10:39:16 +09:00
err = exfat_readdir ( inode , & cpos , & de ) ;
2020-03-02 15:21:35 +09:00
if ( err ) {
/*
* At least we tried to read a sector . Move cpos to next sector
* position ( should be aligned ) .
*/
if ( err = = - EIO ) {
cpos + = 1 < < ( sb - > s_blocksize_bits ) ;
cpos & = ~ ( sb - > s_blocksize - 1 ) ;
}
err = - EIO ;
goto end_of_dir ;
}
if ( ! nb - > lfn [ 0 ] )
goto end_of_dir ;
2020-09-17 10:39:16 +09:00
i_pos = ( ( loff_t ) ei - > start_clu < < 32 ) | ( de . entry & 0xffffffff ) ;
2020-03-02 15:21:35 +09:00
tmp = exfat_iget ( sb , i_pos ) ;
if ( tmp ) {
inum = tmp - > i_ino ;
iput ( tmp ) ;
} else {
inum = iunique ( sb , EXFAT_ROOT_INO ) ;
}
/*
* Before calling dir_emit ( ) , sb_lock should be released .
* Because page fault can occur in dir_emit ( ) when the size
* of buffer given from user is larger than one page size .
*/
mutex_unlock ( & EXFAT_SB ( sb ) - > s_lock ) ;
if ( ! dir_emit ( ctx , nb - > lfn , strlen ( nb - > lfn ) , inum ,
( de . attr & ATTR_SUBDIR ) ? DT_DIR : DT_REG ) )
goto out_unlocked ;
mutex_lock ( & EXFAT_SB ( sb ) - > s_lock ) ;
ctx - > pos = cpos ;
goto get_new ;
end_of_dir :
if ( ! cpos & & fake_offset )
cpos = ITER_POS_FILLED_DOTS ;
ctx - > pos = cpos ;
unlock :
mutex_unlock ( & EXFAT_SB ( sb ) - > s_lock ) ;
out_unlocked :
/*
* To improve performance , free namebuf after unlock sb_lock .
* If namebuf is not allocated , this function do nothing
*/
exfat_free_namebuf ( nb ) ;
return err ;
}
const struct file_operations exfat_dir_operations = {
. llseek = generic_file_llseek ,
. read = generic_read_dir ,
. iterate = exfat_iterate ,
2021-03-04 09:20:35 +09:00
. unlocked_ioctl = exfat_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = exfat_compat_ioctl ,
# endif
2020-06-18 20:43:26 +09:00
. fsync = exfat_file_fsync ,
2020-03-02 15:21:35 +09:00
} ;
int exfat_alloc_new_dir ( struct inode * inode , struct exfat_chain * clu )
{
int ret ;
exfat_chain_set ( clu , EXFAT_EOF_CLUSTER , 0 , ALLOC_NO_FAT_CHAIN ) ;
2021-03-15 13:12:55 +09:00
ret = exfat_alloc_cluster ( inode , 1 , clu , IS_DIRSYNC ( inode ) ) ;
2020-03-02 15:21:35 +09:00
if ( ret )
return ret ;
return exfat_zeroed_cluster ( inode , clu - > dir ) ;
}
int exfat_calc_num_entries ( struct exfat_uni_name * p_uniname )
{
int len ;
len = p_uniname - > name_len ;
if ( len = = 0 )
return - EINVAL ;
/* 1 file entry + 1 stream entry + name entries */
2022-03-17 19:39:20 +08:00
return ES_ENTRY_NUM ( len ) ;
2020-03-02 15:21:35 +09:00
}
unsigned int exfat_get_entry_type ( struct exfat_dentry * ep )
{
if ( ep - > type = = EXFAT_UNUSED )
return TYPE_UNUSED ;
if ( IS_EXFAT_DELETED ( ep - > type ) )
return TYPE_DELETED ;
if ( ep - > type = = EXFAT_INVAL )
return TYPE_INVALID ;
if ( IS_EXFAT_CRITICAL_PRI ( ep - > type ) ) {
if ( ep - > type = = EXFAT_BITMAP )
return TYPE_BITMAP ;
if ( ep - > type = = EXFAT_UPCASE )
return TYPE_UPCASE ;
if ( ep - > type = = EXFAT_VOLUME )
return TYPE_VOLUME ;
if ( ep - > type = = EXFAT_FILE ) {
if ( le16_to_cpu ( ep - > dentry . file . attr ) & ATTR_SUBDIR )
return TYPE_DIR ;
return TYPE_FILE ;
}
return TYPE_CRITICAL_PRI ;
}
if ( IS_EXFAT_BENIGN_PRI ( ep - > type ) ) {
if ( ep - > type = = EXFAT_GUID )
return TYPE_GUID ;
if ( ep - > type = = EXFAT_PADDING )
return TYPE_PADDING ;
if ( ep - > type = = EXFAT_ACLTAB )
return TYPE_ACLTAB ;
return TYPE_BENIGN_PRI ;
}
if ( IS_EXFAT_CRITICAL_SEC ( ep - > type ) ) {
if ( ep - > type = = EXFAT_STREAM )
return TYPE_STREAM ;
if ( ep - > type = = EXFAT_NAME )
return TYPE_EXTEND ;
if ( ep - > type = = EXFAT_ACL )
return TYPE_ACL ;
return TYPE_CRITICAL_SEC ;
}
2023-01-14 13:09:48 +09:00
if ( ep - > type = = EXFAT_VENDOR_EXT )
return TYPE_VENDOR_EXT ;
if ( ep - > type = = EXFAT_VENDOR_ALLOC )
return TYPE_VENDOR_ALLOC ;
2020-03-02 15:21:35 +09:00
return TYPE_BENIGN_SEC ;
}
static void exfat_set_entry_type ( struct exfat_dentry * ep , unsigned int type )
{
if ( type = = TYPE_UNUSED ) {
ep - > type = EXFAT_UNUSED ;
} else if ( type = = TYPE_DELETED ) {
ep - > type & = EXFAT_DELETE ;
} else if ( type = = TYPE_STREAM ) {
ep - > type = EXFAT_STREAM ;
} else if ( type = = TYPE_EXTEND ) {
ep - > type = EXFAT_NAME ;
} else if ( type = = TYPE_BITMAP ) {
ep - > type = EXFAT_BITMAP ;
} else if ( type = = TYPE_UPCASE ) {
ep - > type = EXFAT_UPCASE ;
} else if ( type = = TYPE_VOLUME ) {
ep - > type = EXFAT_VOLUME ;
} else if ( type = = TYPE_DIR ) {
ep - > type = EXFAT_FILE ;
ep - > dentry . file . attr = cpu_to_le16 ( ATTR_SUBDIR ) ;
} else if ( type = = TYPE_FILE ) {
ep - > type = EXFAT_FILE ;
ep - > dentry . file . attr = cpu_to_le16 ( ATTR_ARCHIVE ) ;
}
}
static void exfat_init_stream_entry ( struct exfat_dentry * ep ,
unsigned char flags , unsigned int start_clu ,
unsigned long long size )
{
exfat_set_entry_type ( ep , TYPE_STREAM ) ;
ep - > dentry . stream . flags = flags ;
ep - > dentry . stream . start_clu = cpu_to_le32 ( start_clu ) ;
ep - > dentry . stream . valid_size = cpu_to_le64 ( size ) ;
ep - > dentry . stream . size = cpu_to_le64 ( size ) ;
}
static void exfat_init_name_entry ( struct exfat_dentry * ep ,
unsigned short * uniname )
{
int i ;
exfat_set_entry_type ( ep , TYPE_EXTEND ) ;
ep - > dentry . name . flags = 0x0 ;
for ( i = 0 ; i < EXFAT_FILE_NAME_LEN ; i + + ) {
2020-06-09 14:30:44 +09:00
if ( * uniname ! = 0x0 ) {
ep - > dentry . name . unicode_0_14 [ i ] = cpu_to_le16 ( * uniname ) ;
uniname + + ;
} else {
ep - > dentry . name . unicode_0_14 [ i ] = 0x0 ;
}
2020-03-02 15:21:35 +09:00
}
}
int exfat_init_dir_entry ( struct inode * inode , struct exfat_chain * p_dir ,
int entry , unsigned int type , unsigned int start_clu ,
unsigned long long size )
{
struct super_block * sb = inode - > i_sb ;
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
struct timespec64 ts = current_time ( inode ) ;
struct exfat_dentry * ep ;
struct buffer_head * bh ;
/*
* We cannot use exfat_get_dentry_set here because file ep is not
* initialized yet .
*/
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
exfat_set_entry_type ( ep , type ) ;
exfat_set_entry_time ( sbi , & ts ,
& ep - > dentry . file . create_tz ,
& ep - > dentry . file . create_time ,
& ep - > dentry . file . create_date ,
2020-04-22 08:30:56 +09:00
& ep - > dentry . file . create_time_cs ) ;
2020-03-02 15:21:35 +09:00
exfat_set_entry_time ( sbi , & ts ,
& ep - > dentry . file . modify_tz ,
& ep - > dentry . file . modify_time ,
& ep - > dentry . file . modify_date ,
2020-04-22 08:30:56 +09:00
& ep - > dentry . file . modify_time_cs ) ;
2020-03-02 15:21:35 +09:00
exfat_set_entry_time ( sbi , & ts ,
& ep - > dentry . file . access_tz ,
& ep - > dentry . file . access_time ,
& ep - > dentry . file . access_date ,
NULL ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( bh , IS_DIRSYNC ( inode ) ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry + 1 , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
exfat_init_stream_entry ( ep ,
( type = = TYPE_FILE ) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN ,
start_clu , size ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( bh , IS_DIRSYNC ( inode ) ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
return 0 ;
}
int exfat_update_dir_chksum ( struct inode * inode , struct exfat_chain * p_dir ,
int entry )
{
struct super_block * sb = inode - > i_sb ;
int ret = 0 ;
int i , num_entries ;
2020-05-29 19:14:59 +09:00
u16 chksum ;
2020-03-02 15:21:35 +09:00
struct exfat_dentry * ep , * fep ;
struct buffer_head * fbh , * bh ;
2021-12-16 21:58:37 +09:00
fep = exfat_get_dentry ( sb , p_dir , entry , & fbh ) ;
2020-03-02 15:21:35 +09:00
if ( ! fep )
return - EIO ;
num_entries = fep - > dentry . file . num_ext + 1 ;
2020-05-29 19:14:59 +09:00
chksum = exfat_calc_chksum16 ( fep , DENTRY_SIZE , 0 , CS_DIR_ENTRY ) ;
2020-03-02 15:21:35 +09:00
for ( i = 1 ; i < num_entries ; i + + ) {
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry + i , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep ) {
ret = - EIO ;
goto release_fbh ;
}
2020-05-29 19:14:59 +09:00
chksum = exfat_calc_chksum16 ( ep , DENTRY_SIZE , chksum ,
2020-03-02 15:21:35 +09:00
CS_DEFAULT ) ;
brelse ( bh ) ;
}
fep - > dentry . file . checksum = cpu_to_le16 ( chksum ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( fbh , IS_DIRSYNC ( inode ) ) ;
2020-03-02 15:21:35 +09:00
release_fbh :
brelse ( fbh ) ;
return ret ;
}
2023-01-14 13:09:48 +09:00
static void exfat_free_benign_secondary_clusters ( struct inode * inode ,
struct exfat_dentry * ep )
{
struct super_block * sb = inode - > i_sb ;
struct exfat_chain dir ;
unsigned int start_clu =
le32_to_cpu ( ep - > dentry . generic_secondary . start_clu ) ;
u64 size = le64_to_cpu ( ep - > dentry . generic_secondary . size ) ;
unsigned char flags = ep - > dentry . generic_secondary . flags ;
if ( ! ( flags & ALLOC_POSSIBLE ) | | ! start_clu | | ! size )
return ;
exfat_chain_set ( & dir , start_clu ,
EXFAT_B_TO_CLU_ROUND_UP ( size , EXFAT_SB ( sb ) ) ,
flags ) ;
exfat_free_cluster ( inode , & dir ) ;
}
2020-03-02 15:21:35 +09:00
int exfat_init_ext_entry ( struct inode * inode , struct exfat_chain * p_dir ,
int entry , int num_entries , struct exfat_uni_name * p_uniname )
{
struct super_block * sb = inode - > i_sb ;
int i ;
unsigned short * uniname = p_uniname - > name ;
struct exfat_dentry * ep ;
struct buffer_head * bh ;
int sync = IS_DIRSYNC ( inode ) ;
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
ep - > dentry . file . num_ext = ( unsigned char ) ( num_entries - 1 ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( bh , sync ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry + 1 , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
ep - > dentry . stream . name_len = p_uniname - > name_len ;
ep - > dentry . stream . name_hash = cpu_to_le16 ( p_uniname - > name_hash ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( bh , sync ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
for ( i = EXFAT_FIRST_CLUSTER ; i < num_entries ; i + + ) {
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry + i , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
2023-01-14 13:09:48 +09:00
if ( exfat_get_entry_type ( ep ) & TYPE_BENIGN_SEC )
exfat_free_benign_secondary_clusters ( inode , ep ) ;
2020-03-02 15:21:35 +09:00
exfat_init_name_entry ( ep , uniname ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( bh , sync ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
uniname + = EXFAT_FILE_NAME_LEN ;
}
exfat_update_dir_chksum ( inode , p_dir , entry ) ;
return 0 ;
}
int exfat_remove_entries ( struct inode * inode , struct exfat_chain * p_dir ,
int entry , int order , int num_entries )
{
struct super_block * sb = inode - > i_sb ;
int i ;
struct exfat_dentry * ep ;
struct buffer_head * bh ;
for ( i = order ; i < num_entries ; i + + ) {
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , p_dir , entry + i , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
2023-01-14 13:09:48 +09:00
if ( exfat_get_entry_type ( ep ) & TYPE_BENIGN_SEC )
exfat_free_benign_secondary_clusters ( inode , ep ) ;
2020-03-02 15:21:35 +09:00
exfat_set_entry_type ( ep , TYPE_DELETED ) ;
2020-06-16 11:18:07 +09:00
exfat_update_bh ( bh , IS_DIRSYNC ( inode ) ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
}
return 0 ;
}
2020-05-20 16:56:41 +09:00
void exfat_update_dir_chksum_with_entry_set ( struct exfat_entry_set_cache * es )
2020-03-02 15:21:35 +09:00
{
2020-05-20 16:56:41 +09:00
int chksum_type = CS_DIR_ENTRY , i ;
2020-03-02 15:21:35 +09:00
unsigned short chksum = 0 ;
2020-05-20 16:56:41 +09:00
struct exfat_dentry * ep ;
2020-03-02 15:21:35 +09:00
2022-03-17 19:39:20 +08:00
for ( i = ES_IDX_FILE ; i < es - > num_entries ; i + + ) {
2020-05-20 16:56:41 +09:00
ep = exfat_get_dentry_cached ( es , i ) ;
2020-05-29 19:14:59 +09:00
chksum = exfat_calc_chksum16 ( ep , DENTRY_SIZE , chksum ,
chksum_type ) ;
2020-03-02 15:21:35 +09:00
chksum_type = CS_DEFAULT ;
}
2022-03-17 19:39:20 +08:00
ep = exfat_get_dentry_cached ( es , ES_IDX_FILE ) ;
2020-05-20 16:56:41 +09:00
ep - > dentry . file . checksum = cpu_to_le16 ( chksum ) ;
es - > modified = true ;
}
2020-03-02 15:21:35 +09:00
2022-03-17 18:12:40 +08:00
int exfat_put_dentry_set ( struct exfat_entry_set_cache * es , int sync )
2020-05-20 16:56:41 +09:00
{
2020-06-23 15:22:19 +09:00
int i , err = 0 ;
2020-03-02 15:21:35 +09:00
2020-06-23 15:22:19 +09:00
if ( es - > modified )
err = exfat_update_bhs ( es - > bh , es - > num_bh , sync ) ;
for ( i = 0 ; i < es - > num_bh ; i + + )
if ( err )
bforget ( es - > bh [ i ] ) ;
else
brelse ( es - > bh [ i ] ) ;
2022-11-09 13:50:22 +08:00
if ( IS_DYNAMIC_ES ( es ) )
kfree ( es - > bh ) ;
2020-06-24 09:54:54 +09:00
return err ;
2020-03-02 15:21:35 +09:00
}
static int exfat_walk_fat_chain ( struct super_block * sb ,
struct exfat_chain * p_dir , unsigned int byte_offset ,
unsigned int * clu )
{
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
unsigned int clu_offset ;
unsigned int cur_clu ;
clu_offset = EXFAT_B_TO_CLU ( byte_offset , sbi ) ;
cur_clu = p_dir - > dir ;
if ( p_dir - > flags = = ALLOC_NO_FAT_CHAIN ) {
cur_clu + = clu_offset ;
} else {
while ( clu_offset > 0 ) {
if ( exfat_get_next_cluster ( sb , & cur_clu ) )
return - EIO ;
if ( cur_clu = = EXFAT_EOF_CLUSTER ) {
exfat_fs_error ( sb ,
" invalid dentry access beyond EOF (clu : %u, eidx : %d) " ,
p_dir - > dir ,
EXFAT_B_TO_DEN ( byte_offset ) ) ;
return - EIO ;
}
clu_offset - - ;
}
}
* clu = cur_clu ;
return 0 ;
}
2021-11-02 22:23:57 +01:00
static int exfat_find_location ( struct super_block * sb , struct exfat_chain * p_dir ,
int entry , sector_t * sector , int * offset )
2020-03-02 15:21:35 +09:00
{
int ret ;
unsigned int off , clu = 0 ;
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
off = EXFAT_DEN_TO_B ( entry ) ;
ret = exfat_walk_fat_chain ( sb , p_dir , off , & clu ) ;
if ( ret )
return ret ;
/* byte offset in cluster */
off = EXFAT_CLU_OFFSET ( off , sbi ) ;
/* byte offset in sector */
* offset = EXFAT_BLK_OFFSET ( off , sb ) ;
/* sector offset in cluster */
* sector = EXFAT_B_TO_BLK ( off , sb ) ;
* sector + = exfat_cluster_to_sector ( sbi , clu ) ;
return 0 ;
}
# define EXFAT_MAX_RA_SIZE (128*1024)
static int exfat_dir_readahead ( struct super_block * sb , sector_t sec )
{
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
struct buffer_head * bh ;
unsigned int max_ra_count = EXFAT_MAX_RA_SIZE > > sb - > s_blocksize_bits ;
unsigned int page_ra_count = PAGE_SIZE > > sb - > s_blocksize_bits ;
unsigned int adj_ra_count = max ( sbi - > sect_per_clus , page_ra_count ) ;
unsigned int ra_count = min ( adj_ra_count , max_ra_count ) ;
/* Read-ahead is not required */
if ( sbi - > sect_per_clus = = 1 )
return 0 ;
if ( sec < sbi - > data_start_sector ) {
2020-04-24 13:31:12 +09:00
exfat_err ( sb , " requested sector is invalid(sect:%llu, root:%llu) " ,
( unsigned long long ) sec , sbi - > data_start_sector ) ;
2020-03-02 15:21:35 +09:00
return - EIO ;
}
/* Not sector aligned with ra_count, resize ra_count to page size */
if ( ( sec - sbi - > data_start_sector ) & ( ra_count - 1 ) )
ra_count = page_ra_count ;
bh = sb_find_get_block ( sb , sec ) ;
if ( ! bh | | ! buffer_uptodate ( bh ) ) {
unsigned int i ;
for ( i = 0 ; i < ra_count ; i + + )
sb_breadahead ( sb , ( sector_t ) ( sec + i ) ) ;
}
brelse ( bh ) ;
return 0 ;
}
struct exfat_dentry * exfat_get_dentry ( struct super_block * sb ,
2021-12-16 21:58:37 +09:00
struct exfat_chain * p_dir , int entry , struct buffer_head * * bh )
2020-03-02 15:21:35 +09:00
{
unsigned int dentries_per_page = EXFAT_B_TO_DEN ( PAGE_SIZE ) ;
int off ;
sector_t sec ;
if ( p_dir - > dir = = DIR_DELETED ) {
2020-04-24 13:31:12 +09:00
exfat_err ( sb , " abnormal access to deleted dentry " ) ;
2020-03-02 15:21:35 +09:00
return NULL ;
}
if ( exfat_find_location ( sb , p_dir , entry , & sec , & off ) )
return NULL ;
if ( p_dir - > dir ! = EXFAT_FREE_CLUSTER & &
! ( entry & ( dentries_per_page - 1 ) ) )
exfat_dir_readahead ( sb , sec ) ;
* bh = sb_bread ( sb , sec ) ;
if ( ! * bh )
return NULL ;
return ( struct exfat_dentry * ) ( ( * bh ) - > b_data + off ) ;
}
enum exfat_validate_dentry_mode {
ES_MODE_STARTED ,
ES_MODE_GET_FILE_ENTRY ,
ES_MODE_GET_STRM_ENTRY ,
ES_MODE_GET_NAME_ENTRY ,
ES_MODE_GET_CRITICAL_SEC_ENTRY ,
2023-01-14 13:09:48 +09:00
ES_MODE_GET_BENIGN_SEC_ENTRY ,
2020-03-02 15:21:35 +09:00
} ;
static bool exfat_validate_entry ( unsigned int type ,
enum exfat_validate_dentry_mode * mode )
{
if ( type = = TYPE_UNUSED | | type = = TYPE_DELETED )
return false ;
switch ( * mode ) {
case ES_MODE_STARTED :
if ( type ! = TYPE_FILE & & type ! = TYPE_DIR )
return false ;
* mode = ES_MODE_GET_FILE_ENTRY ;
2023-01-14 13:09:48 +09:00
break ;
2020-03-02 15:21:35 +09:00
case ES_MODE_GET_FILE_ENTRY :
if ( type ! = TYPE_STREAM )
return false ;
* mode = ES_MODE_GET_STRM_ENTRY ;
2023-01-14 13:09:48 +09:00
break ;
2020-03-02 15:21:35 +09:00
case ES_MODE_GET_STRM_ENTRY :
if ( type ! = TYPE_EXTEND )
return false ;
* mode = ES_MODE_GET_NAME_ENTRY ;
2023-01-14 13:09:48 +09:00
break ;
2020-03-02 15:21:35 +09:00
case ES_MODE_GET_NAME_ENTRY :
2023-01-14 13:09:48 +09:00
if ( type & TYPE_BENIGN_SEC )
* mode = ES_MODE_GET_BENIGN_SEC_ENTRY ;
else if ( type ! = TYPE_EXTEND )
2020-03-02 15:21:35 +09:00
return false ;
2023-01-14 13:09:48 +09:00
break ;
case ES_MODE_GET_BENIGN_SEC_ENTRY :
/* Assume unreconized benign secondary entry */
if ( ! ( type & TYPE_BENIGN_SEC ) )
2020-03-02 15:21:35 +09:00
return false ;
2023-01-14 13:09:48 +09:00
break ;
2020-03-02 15:21:35 +09:00
default :
return false ;
}
2023-01-14 13:09:48 +09:00
return true ;
2020-03-02 15:21:35 +09:00
}
2020-05-20 16:56:41 +09:00
struct exfat_dentry * exfat_get_dentry_cached (
struct exfat_entry_set_cache * es , int num )
{
int off = es - > start_off + num * DENTRY_SIZE ;
struct buffer_head * bh = es - > bh [ EXFAT_B_TO_BLK ( off , es - > sb ) ] ;
char * p = bh - > b_data + EXFAT_BLK_OFFSET ( off , es - > sb ) ;
return ( struct exfat_dentry * ) p ;
}
2020-03-02 15:21:35 +09:00
/*
* Returns a set of dentries for a file or dir .
*
2020-05-20 16:56:41 +09:00
* Note It provides a direct pointer to bh - > data via exfat_get_dentry_cached ( ) .
* User should call exfat_get_dentry_set ( ) after setting ' modified ' to apply
* changes made in this entry set to the real device .
2020-03-02 15:21:35 +09:00
*
* in :
* sb + p_dir + entry : indicates a file / dir
* type : specifies how many dentries should be included .
* return :
* pointer of entry set on success ,
* NULL on failure .
*/
2022-11-17 11:37:13 +08:00
int exfat_get_dentry_set ( struct exfat_entry_set_cache * es ,
struct super_block * sb , struct exfat_chain * p_dir , int entry ,
unsigned int type )
2020-03-02 15:21:35 +09:00
{
2020-05-20 16:56:41 +09:00
int ret , i , num_bh ;
2022-11-17 11:31:30 +08:00
unsigned int off ;
2020-03-02 15:21:35 +09:00
sector_t sec ;
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
2020-05-20 16:56:41 +09:00
struct exfat_dentry * ep ;
int num_entries ;
2020-03-02 15:21:35 +09:00
enum exfat_validate_dentry_mode mode = ES_MODE_STARTED ;
struct buffer_head * bh ;
if ( p_dir - > dir = = DIR_DELETED ) {
2020-04-24 13:31:12 +09:00
exfat_err ( sb , " access to deleted dentry " ) ;
2022-11-17 11:37:13 +08:00
return - EIO ;
2020-03-02 15:21:35 +09:00
}
2022-11-17 11:31:30 +08:00
ret = exfat_find_location ( sb , p_dir , entry , & sec , & off ) ;
2020-03-02 15:21:35 +09:00
if ( ret )
2022-11-17 11:37:13 +08:00
return ret ;
2020-03-02 15:21:35 +09:00
2022-11-17 11:37:13 +08:00
memset ( es , 0 , sizeof ( * es ) ) ;
2020-05-20 16:56:41 +09:00
es - > sb = sb ;
es - > modified = false ;
es - > start_off = off ;
2022-11-09 13:50:22 +08:00
es - > bh = es - > __bh ;
2020-03-02 15:21:35 +09:00
bh = sb_bread ( sb , sec ) ;
if ( ! bh )
2022-11-17 11:37:13 +08:00
return - EIO ;
2020-05-20 16:56:41 +09:00
es - > bh [ es - > num_bh + + ] = bh ;
2020-03-02 15:21:35 +09:00
2022-03-17 19:39:20 +08:00
ep = exfat_get_dentry_cached ( es , ES_IDX_FILE ) ;
2020-05-20 16:56:41 +09:00
if ( ! exfat_validate_entry ( exfat_get_entry_type ( ep ) , & mode ) )
2022-03-17 18:12:40 +08:00
goto put_es ;
2020-03-02 15:21:35 +09:00
num_entries = type = = ES_ALL_ENTRIES ?
ep - > dentry . file . num_ext + 1 : type ;
es - > num_entries = num_entries ;
2020-05-20 16:56:41 +09:00
num_bh = EXFAT_B_TO_BLK_ROUND_UP ( off + num_entries * DENTRY_SIZE , sb ) ;
2022-11-09 13:50:22 +08:00
if ( num_bh > ARRAY_SIZE ( es - > __bh ) ) {
es - > bh = kmalloc_array ( num_bh , sizeof ( * es - > bh ) , GFP_KERNEL ) ;
if ( ! es - > bh ) {
brelse ( bh ) ;
2022-11-17 11:37:13 +08:00
return - ENOMEM ;
2022-11-09 13:50:22 +08:00
}
es - > bh [ 0 ] = bh ;
}
2020-05-20 16:56:41 +09:00
for ( i = 1 ; i < num_bh ; i + + ) {
/* get the next sector */
if ( exfat_is_last_sector_in_cluster ( sbi , sec ) ) {
2022-11-17 11:31:30 +08:00
unsigned int clu = exfat_sector_to_cluster ( sbi , sec ) ;
2020-05-20 16:56:41 +09:00
if ( p_dir - > flags = = ALLOC_NO_FAT_CHAIN )
clu + + ;
else if ( exfat_get_next_cluster ( sb , & clu ) )
2022-03-17 18:12:40 +08:00
goto put_es ;
2020-05-20 16:56:41 +09:00
sec = exfat_cluster_to_sector ( sbi , clu ) ;
2020-03-02 15:21:35 +09:00
} else {
2020-05-20 16:56:41 +09:00
sec + + ;
2020-03-02 15:21:35 +09:00
}
2020-05-20 16:56:41 +09:00
bh = sb_bread ( sb , sec ) ;
if ( ! bh )
2022-03-17 18:12:40 +08:00
goto put_es ;
2020-05-20 16:56:41 +09:00
es - > bh [ es - > num_bh + + ] = bh ;
2020-03-02 15:21:35 +09:00
}
2021-11-02 22:23:56 +01:00
/* validate cached dentries */
2022-03-17 19:39:20 +08:00
for ( i = ES_IDX_STREAM ; i < num_entries ; i + + ) {
2020-05-20 16:56:41 +09:00
ep = exfat_get_dentry_cached ( es , i ) ;
if ( ! exfat_validate_entry ( exfat_get_entry_type ( ep ) , & mode ) )
2022-03-17 18:12:40 +08:00
goto put_es ;
2020-05-20 16:56:41 +09:00
}
2022-11-17 11:37:13 +08:00
return 0 ;
2020-03-02 15:21:35 +09:00
2022-03-17 18:12:40 +08:00
put_es :
exfat_put_dentry_set ( es , false ) ;
2022-11-17 11:37:13 +08:00
return - EIO ;
2020-03-02 15:21:35 +09:00
}
2022-07-21 09:59:32 +08:00
static inline void exfat_reset_empty_hint ( struct exfat_hint_femp * hint_femp )
{
hint_femp - > eidx = EXFAT_HINT_NONE ;
hint_femp - > count = 0 ;
}
static inline void exfat_set_empty_hint ( struct exfat_inode_info * ei ,
struct exfat_hint_femp * candi_empty , struct exfat_chain * clu ,
2022-11-07 17:22:13 +09:00
int dentry , int num_entries , int entry_type )
2022-07-21 09:59:32 +08:00
{
if ( ei - > hint_femp . eidx = = EXFAT_HINT_NONE | |
ei - > hint_femp . eidx > dentry ) {
2022-11-07 17:22:13 +09:00
int total_entries = EXFAT_B_TO_DEN ( i_size_read ( & ei - > vfs_inode ) ) ;
2022-07-21 09:59:32 +08:00
if ( candi_empty - > count = = 0 ) {
candi_empty - > cur = * clu ;
candi_empty - > eidx = dentry ;
}
2022-11-07 17:22:13 +09:00
if ( entry_type = = TYPE_UNUSED )
candi_empty - > count + = total_entries - dentry ;
else
candi_empty - > count + + ;
if ( candi_empty - > count = = num_entries | |
candi_empty - > count + candi_empty - > eidx = = total_entries )
2022-07-21 09:59:32 +08:00
ei - > hint_femp = * candi_empty ;
}
}
2020-03-02 15:21:35 +09:00
enum {
DIRENT_STEP_FILE ,
DIRENT_STEP_STRM ,
DIRENT_STEP_NAME ,
DIRENT_STEP_SECD ,
} ;
/*
2021-03-22 12:53:36 +09:00
* @ ei : inode info of parent directory
* @ p_dir : directory structure of parent directory
* @ num_entries : entry size of p_uniname
* @ hint_opt : If p_uniname is found , filled with optimized dir / entry
* for traversing cluster chain .
* @ return :
* > = 0 : file directory entry position where the name exists
* - ENOENT : entry with the name does not exist
* - EIO : I / O error
2020-03-02 15:21:35 +09:00
*/
int exfat_find_dir_entry ( struct super_block * sb , struct exfat_inode_info * ei ,
struct exfat_chain * p_dir , struct exfat_uni_name * p_uniname ,
2022-04-10 16:12:14 +08:00
struct exfat_hint * hint_opt )
2020-03-02 15:21:35 +09:00
{
int i , rewind = 0 , dentry = 0 , end_eidx = 0 , num_ext = 0 , len ;
int order , step , name_len = 0 ;
2022-07-21 09:59:32 +08:00
int dentries_per_clu ;
2020-03-02 15:21:35 +09:00
unsigned int entry_type ;
unsigned short * uniname = NULL ;
struct exfat_chain clu ;
struct exfat_hint * hint_stat = & ei - > hint_stat ;
struct exfat_hint_femp candi_empty ;
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
2022-04-10 16:12:14 +08:00
int num_entries = exfat_calc_num_entries ( p_uniname ) ;
if ( num_entries < 0 )
return num_entries ;
2020-03-02 15:21:35 +09:00
dentries_per_clu = sbi - > dentries_per_clu ;
exfat_chain_dup ( & clu , p_dir ) ;
if ( hint_stat - > eidx ) {
clu . dir = hint_stat - > clu ;
dentry = hint_stat - > eidx ;
end_eidx = dentry ;
}
2022-07-21 09:59:32 +08:00
exfat_reset_empty_hint ( & ei - > hint_femp ) ;
2020-03-02 15:21:35 +09:00
rewind :
order = 0 ;
step = DIRENT_STEP_FILE ;
2022-07-21 09:59:32 +08:00
exfat_reset_empty_hint ( & candi_empty ) ;
2020-03-02 15:21:35 +09:00
while ( clu . dir ! = EXFAT_EOF_CLUSTER ) {
i = dentry & ( dentries_per_clu - 1 ) ;
for ( ; i < dentries_per_clu ; i + + , dentry + + ) {
struct exfat_dentry * ep ;
struct buffer_head * bh ;
if ( rewind & & dentry = = end_eidx )
goto not_found ;
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , & clu , i , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
entry_type = exfat_get_entry_type ( ep ) ;
if ( entry_type = = TYPE_UNUSED | |
entry_type = = TYPE_DELETED ) {
step = DIRENT_STEP_FILE ;
2022-07-21 09:59:32 +08:00
exfat_set_empty_hint ( ei , & candi_empty , & clu ,
2022-11-07 17:22:13 +09:00
dentry , num_entries ,
entry_type ) ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
if ( entry_type = = TYPE_UNUSED )
goto not_found ;
continue ;
}
2022-07-21 09:59:32 +08:00
exfat_reset_empty_hint ( & candi_empty ) ;
2020-03-02 15:21:35 +09:00
if ( entry_type = = TYPE_FILE | | entry_type = = TYPE_DIR ) {
step = DIRENT_STEP_FILE ;
2021-03-22 12:53:36 +09:00
hint_opt - > clu = clu . dir ;
hint_opt - > eidx = i ;
2022-04-10 16:12:14 +08:00
num_ext = ep - > dentry . file . num_ext ;
step = DIRENT_STEP_STRM ;
2020-03-02 15:21:35 +09:00
brelse ( bh ) ;
continue ;
}
if ( entry_type = = TYPE_STREAM ) {
2020-05-29 19:14:59 +09:00
u16 name_hash ;
2020-03-02 15:21:35 +09:00
if ( step ! = DIRENT_STEP_STRM ) {
step = DIRENT_STEP_FILE ;
brelse ( bh ) ;
continue ;
}
step = DIRENT_STEP_FILE ;
name_hash = le16_to_cpu (
ep - > dentry . stream . name_hash ) ;
if ( p_uniname - > name_hash = = name_hash & &
p_uniname - > name_len = =
ep - > dentry . stream . name_len ) {
step = DIRENT_STEP_NAME ;
order = 1 ;
name_len = 0 ;
}
brelse ( bh ) ;
continue ;
}
brelse ( bh ) ;
if ( entry_type = = TYPE_EXTEND ) {
unsigned short entry_uniname [ 16 ] , unichar ;
if ( step ! = DIRENT_STEP_NAME ) {
step = DIRENT_STEP_FILE ;
continue ;
}
if ( + + order = = 2 )
uniname = p_uniname - > name ;
else
uniname + = EXFAT_FILE_NAME_LEN ;
len = exfat_extract_uni_name ( ep , entry_uniname ) ;
name_len + = len ;
unichar = * ( uniname + len ) ;
* ( uniname + len ) = 0x0 ;
if ( exfat_uniname_ncmp ( sb , uniname ,
entry_uniname , len ) ) {
step = DIRENT_STEP_FILE ;
} else if ( p_uniname - > name_len = = name_len ) {
if ( order = = num_ext )
goto found ;
step = DIRENT_STEP_SECD ;
}
* ( uniname + len ) = unichar ;
continue ;
}
if ( entry_type &
( TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC ) ) {
if ( step = = DIRENT_STEP_SECD ) {
if ( + + order = = num_ext )
goto found ;
continue ;
}
}
step = DIRENT_STEP_FILE ;
}
if ( clu . flags = = ALLOC_NO_FAT_CHAIN ) {
if ( - - clu . size > 0 )
clu . dir + + ;
else
clu . dir = EXFAT_EOF_CLUSTER ;
} else {
if ( exfat_get_next_cluster ( sb , & clu . dir ) )
return - EIO ;
}
}
not_found :
/*
* We started at not 0 index , so we should try to find target
* from 0 index to the index we started at .
*/
if ( ! rewind & & end_eidx ) {
rewind = 1 ;
dentry = 0 ;
clu . dir = p_dir - > dir ;
goto rewind ;
}
2022-11-07 17:22:13 +09:00
/*
* set the EXFAT_EOF_CLUSTER flag to avoid search
* from the beginning again when allocated a new cluster
*/
if ( ei - > hint_femp . eidx = = EXFAT_HINT_NONE ) {
ei - > hint_femp . cur . dir = EXFAT_EOF_CLUSTER ;
ei - > hint_femp . eidx = p_dir - > size * dentries_per_clu ;
ei - > hint_femp . count = 0 ;
}
2020-03-02 15:21:35 +09:00
/* initialized hint_stat */
hint_stat - > clu = p_dir - > dir ;
hint_stat - > eidx = 0 ;
return - ENOENT ;
found :
/* next dentry we'll find is out of this cluster */
if ( ! ( ( dentry + 1 ) & ( dentries_per_clu - 1 ) ) ) {
int ret = 0 ;
if ( clu . flags = = ALLOC_NO_FAT_CHAIN ) {
if ( - - clu . size > 0 )
clu . dir + + ;
else
clu . dir = EXFAT_EOF_CLUSTER ;
} else {
ret = exfat_get_next_cluster ( sb , & clu . dir ) ;
}
2020-07-03 11:19:46 +09:00
if ( ret | | clu . dir = = EXFAT_EOF_CLUSTER ) {
2020-03-02 15:21:35 +09:00
/* just initialized hint_stat */
hint_stat - > clu = p_dir - > dir ;
hint_stat - > eidx = 0 ;
return ( dentry - num_ext ) ;
}
}
hint_stat - > clu = clu . dir ;
hint_stat - > eidx = dentry + 1 ;
return dentry - num_ext ;
}
int exfat_count_ext_entries ( struct super_block * sb , struct exfat_chain * p_dir ,
int entry , struct exfat_dentry * ep )
{
int i , count = 0 ;
unsigned int type ;
struct exfat_dentry * ext_ep ;
struct buffer_head * bh ;
for ( i = 0 , entry + + ; i < ep - > dentry . file . num_ext ; i + + , entry + + ) {
2021-12-16 21:58:37 +09:00
ext_ep = exfat_get_dentry ( sb , p_dir , entry , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ext_ep )
return - EIO ;
type = exfat_get_entry_type ( ext_ep ) ;
brelse ( bh ) ;
2023-01-14 13:09:48 +09:00
if ( type & TYPE_CRITICAL_SEC | | type & TYPE_BENIGN_SEC )
2020-03-02 15:21:35 +09:00
count + + ;
}
return count ;
}
int exfat_count_dir_entries ( struct super_block * sb , struct exfat_chain * p_dir )
{
int i , count = 0 ;
int dentries_per_clu ;
unsigned int entry_type ;
struct exfat_chain clu ;
struct exfat_dentry * ep ;
struct exfat_sb_info * sbi = EXFAT_SB ( sb ) ;
struct buffer_head * bh ;
dentries_per_clu = sbi - > dentries_per_clu ;
exfat_chain_dup ( & clu , p_dir ) ;
while ( clu . dir ! = EXFAT_EOF_CLUSTER ) {
for ( i = 0 ; i < dentries_per_clu ; i + + ) {
2021-12-16 21:58:37 +09:00
ep = exfat_get_dentry ( sb , & clu , i , & bh ) ;
2020-03-02 15:21:35 +09:00
if ( ! ep )
return - EIO ;
entry_type = exfat_get_entry_type ( ep ) ;
brelse ( bh ) ;
if ( entry_type = = TYPE_UNUSED )
return count ;
if ( entry_type ! = TYPE_DIR )
continue ;
count + + ;
}
if ( clu . flags = = ALLOC_NO_FAT_CHAIN ) {
if ( - - clu . size > 0 )
clu . dir + + ;
else
clu . dir = EXFAT_EOF_CLUSTER ;
} else {
if ( exfat_get_next_cluster ( sb , & ( clu . dir ) ) )
return - EIO ;
}
}
return count ;
}