2018-06-06 05:42:14 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-08-12 14:49:36 +04:00
/*
* Copyright ( c ) 2000 - 2005 Silicon Graphics , Inc .
* Copyright ( c ) 2013 Red Hat , Inc .
* All Rights Reserved .
*/
# include "xfs.h"
# include "xfs_fs.h"
2019-06-29 05:25:35 +03:00
# include "xfs_shared.h"
2013-10-23 03:51:50 +04:00
# include "xfs_format.h"
2013-10-23 03:50:10 +04:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2013-08-12 14:49:36 +04:00
# include "xfs_mount.h"
# include "xfs_inode.h"
2013-08-12 14:49:37 +04:00
# include "xfs_dir2.h"
2013-08-12 14:49:36 +04:00
# include "xfs_dir2_priv.h"
# include "xfs_trace.h"
# include "xfs_bmap.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2019-10-29 02:12:34 +03:00
# include "xfs_error.h"
2013-08-12 14:49:36 +04:00
2013-08-12 14:50:09 +04:00
/*
* Directory file type support functions
*/
static unsigned char xfs_dir3_filetype_table [ ] = {
DT_UNKNOWN , DT_REG , DT_DIR , DT_CHR , DT_BLK ,
DT_FIFO , DT_SOCK , DT_LNK , DT_WHT ,
} ;
2017-10-18 07:37:44 +03:00
unsigned char
2013-08-12 14:50:09 +04:00
xfs_dir3_get_dtype (
struct xfs_mount * mp ,
2017-06-16 21:00:05 +03:00
uint8_t filetype )
2013-08-12 14:50:09 +04:00
{
2021-08-19 04:46:37 +03:00
if ( ! xfs_has_ftype ( mp ) )
2013-08-12 14:50:09 +04:00
return DT_UNKNOWN ;
if ( filetype > = XFS_DIR3_FT_MAX )
return DT_UNKNOWN ;
return xfs_dir3_filetype_table [ filetype ] ;
}
2013-08-12 14:49:36 +04:00
STATIC int
xfs_dir2_sf_getdents (
2014-06-06 09:20:32 +04:00
struct xfs_da_args * args ,
2013-08-12 14:49:36 +04:00
struct dir_context * ctx )
{
int i ; /* shortform entry number */
2014-06-06 09:20:32 +04:00
struct xfs_inode * dp = args - > dp ; /* incore directory inode */
2019-11-09 02:02:38 +03:00
struct xfs_mount * mp = dp - > i_mount ;
2013-08-12 14:49:36 +04:00
xfs_dir2_dataptr_t off ; /* current entry's offset */
xfs_dir2_sf_entry_t * sfep ; /* shortform directory entry */
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
xfs_dir2_dataptr_t dot_offset ;
xfs_dir2_dataptr_t dotdot_offset ;
xfs_ino_t ino ;
2014-06-06 09:20:32 +04:00
struct xfs_da_geometry * geo = args - > geo ;
2013-08-12 14:49:36 +04:00
2021-04-13 21:15:11 +03:00
ASSERT ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL ) ;
2021-03-29 21:11:40 +03:00
ASSERT ( dp - > i_df . if_bytes = = dp - > i_disk_size ) ;
2013-08-12 14:49:36 +04:00
ASSERT ( dp - > i_df . if_u1 . if_data ! = NULL ) ;
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
/*
* If the block number in the offset is out of range , we ' re done .
*/
2014-06-06 09:11:18 +04:00
if ( xfs_dir2_dataptr_to_db ( geo , ctx - > pos ) > geo - > datablk )
2013-08-12 14:49:36 +04:00
return 0 ;
/*
2019-11-09 02:05:30 +03:00
* Precalculate offsets for " . " and " .. " as we will always need them .
* This relies on the fact that directories always start with the
* entries for " . " and " .. " .
2013-08-12 14:49:36 +04:00
*/
2014-06-06 09:11:18 +04:00
dot_offset = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk ,
2019-11-09 02:05:38 +03:00
geo - > data_entry_offset ) ;
2014-06-06 09:11:18 +04:00
dotdot_offset = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk ,
2019-11-09 02:05:38 +03:00
geo - > data_entry_offset +
2019-11-09 02:05:37 +03:00
xfs_dir2_data_entsize ( mp , sizeof ( " . " ) - 1 ) ) ;
2013-08-12 14:49:36 +04:00
/*
* Put . entry unless we ' re starting past it .
*/
if ( ctx - > pos < = dot_offset ) {
ctx - > pos = dot_offset & 0x7fffffff ;
if ( ! dir_emit ( ctx , " . " , 1 , dp - > i_ino , DT_DIR ) )
return 0 ;
}
/*
* Put . . entry unless we ' re starting past it .
*/
if ( ctx - > pos < = dotdot_offset ) {
2019-11-09 02:02:31 +03:00
ino = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2013-08-12 14:49:36 +04:00
ctx - > pos = dotdot_offset & 0x7fffffff ;
if ( ! dir_emit ( ctx , " .. " , 2 , ino , DT_DIR ) )
return 0 ;
}
/*
* Loop while there are more entries and put ' ing works .
*/
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
for ( i = 0 ; i < sfp - > count ; i + + ) {
2017-06-16 21:00:05 +03:00
uint8_t filetype ;
2013-08-12 14:50:09 +04:00
2014-06-06 09:11:18 +04:00
off = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk ,
2013-08-12 14:49:36 +04:00
xfs_dir2_sf_get_offset ( sfep ) ) ;
if ( ctx - > pos > off ) {
2019-11-09 02:02:38 +03:00
sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ;
2013-08-12 14:49:36 +04:00
continue ;
}
2019-11-09 02:02:59 +03:00
ino = xfs_dir2_sf_get_ino ( mp , sfp , sfep ) ;
2019-11-09 02:03:30 +03:00
filetype = xfs_dir2_sf_get_ftype ( mp , sfep ) ;
2013-08-12 14:49:36 +04:00
ctx - > pos = off & 0x7fffffff ;
2019-11-11 23:53:22 +03:00
if ( XFS_IS_CORRUPT ( dp - > i_mount ,
! xfs_dir2_namecheck ( sfep - > name ,
sfep - > namelen ) ) )
2019-10-29 02:12:34 +03:00
return - EFSCORRUPTED ;
2013-08-12 14:50:09 +04:00
if ( ! dir_emit ( ctx , ( char * ) sfep - > name , sfep - > namelen , ino ,
2019-11-09 02:02:38 +03:00
xfs_dir3_get_dtype ( mp , filetype ) ) )
2013-08-12 14:49:36 +04:00
return 0 ;
2019-11-09 02:02:38 +03:00
sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ;
2013-08-12 14:49:36 +04:00
}
2014-06-06 09:11:18 +04:00
ctx - > pos = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk + 1 , 0 ) &
2014-06-06 09:20:32 +04:00
0x7fffffff ;
2013-08-12 14:49:36 +04:00
return 0 ;
}
/*
* Readdir for block directories .
*/
STATIC int
xfs_dir2_block_getdents (
2014-06-06 09:20:32 +04:00
struct xfs_da_args * args ,
2022-01-05 04:38:36 +03:00
struct dir_context * ctx ,
unsigned int * lock_mode )
2013-08-12 14:49:36 +04:00
{
2014-06-06 09:20:32 +04:00
struct xfs_inode * dp = args - > dp ; /* incore directory inode */
2013-08-12 14:49:36 +04:00
struct xfs_buf * bp ; /* buffer for block */
int error ; /* error return value */
int wantoff ; /* starting block offset */
xfs_off_t cook ;
2014-06-06 09:20:32 +04:00
struct xfs_da_geometry * geo = args - > geo ;
2020-03-12 17:43:53 +03:00
unsigned int offset , next_offset ;
2019-11-09 02:05:32 +03:00
unsigned int end ;
2013-08-12 14:49:36 +04:00
/*
* If the block number in the offset is out of range , we ' re done .
*/
2014-06-06 09:11:18 +04:00
if ( xfs_dir2_dataptr_to_db ( geo , ctx - > pos ) > geo - > datablk )
2013-08-12 14:49:36 +04:00
return 0 ;
2017-06-16 21:00:14 +03:00
error = xfs_dir3_block_read ( args - > trans , dp , & bp ) ;
2013-08-12 14:49:36 +04:00
if ( error )
return error ;
2022-01-05 04:38:36 +03:00
xfs_iunlock ( dp , * lock_mode ) ;
* lock_mode = 0 ;
2013-08-12 14:49:36 +04:00
/*
* Extract the byte offset we start at from the seek pointer .
* We ' ll skip entries before this .
*/
2014-06-06 09:08:18 +04:00
wantoff = xfs_dir2_dataptr_to_off ( geo , ctx - > pos ) ;
2013-08-12 14:49:36 +04:00
xfs_dir3_data_check ( dp , bp ) ;
/*
* Loop over the data portion of the block .
* Each object is a real entry ( dep ) or an unused one ( dup ) .
*/
2019-11-09 02:05:36 +03:00
end = xfs_dir3_data_end_offset ( geo , bp - > b_addr ) ;
2020-03-12 17:43:53 +03:00
for ( offset = geo - > data_entry_offset ;
offset < end ;
offset = next_offset ) {
2019-11-09 02:05:32 +03:00
struct xfs_dir2_data_unused * dup = bp - > b_addr + offset ;
struct xfs_dir2_data_entry * dep = bp - > b_addr + offset ;
2017-06-16 21:00:05 +03:00
uint8_t filetype ;
2013-08-12 14:50:09 +04:00
2013-08-12 14:49:36 +04:00
/*
* Unused , skip it .
*/
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
2020-03-12 17:43:53 +03:00
next_offset = offset + be16_to_cpu ( dup - > length ) ;
2013-08-12 14:49:36 +04:00
continue ;
}
/*
* Bump pointer for the next iteration .
*/
2020-03-12 17:43:53 +03:00
next_offset = offset +
xfs_dir2_data_entsize ( dp - > i_mount , dep - > namelen ) ;
2019-11-09 02:05:32 +03:00
2013-08-12 14:49:36 +04:00
/*
* The entry is before the desired starting point , skip it .
*/
2019-11-09 02:05:32 +03:00
if ( offset < wantoff )
2013-08-12 14:49:36 +04:00
continue ;
2019-11-09 02:05:32 +03:00
cook = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk , offset ) ;
2013-08-12 14:49:36 +04:00
ctx - > pos = cook & 0x7fffffff ;
2019-11-09 02:05:48 +03:00
filetype = xfs_dir2_data_get_ftype ( dp - > i_mount , dep ) ;
2013-08-12 14:49:36 +04:00
/*
* If it didn ' t fit , set the final offset to here & return .
*/
2019-11-11 23:53:22 +03:00
if ( XFS_IS_CORRUPT ( dp - > i_mount ,
! xfs_dir2_namecheck ( dep - > name ,
dep - > namelen ) ) ) {
2019-10-29 02:12:34 +03:00
error = - EFSCORRUPTED ;
goto out_rele ;
}
2013-08-12 14:49:36 +04:00
if ( ! dir_emit ( ctx , ( char * ) dep - > name , dep - > namelen ,
2013-08-12 14:50:09 +04:00
be64_to_cpu ( dep - > inumber ) ,
2019-10-29 02:12:34 +03:00
xfs_dir3_get_dtype ( dp - > i_mount , filetype ) ) )
goto out_rele ;
2013-08-12 14:49:36 +04:00
}
/*
* Reached the end of the block .
* Set the offset to a non - existent block 1 and return .
*/
2014-06-06 09:11:18 +04:00
ctx - > pos = xfs_dir2_db_off_to_dataptr ( geo , geo - > datablk + 1 , 0 ) &
2014-06-06 09:20:32 +04:00
0x7fffffff ;
2019-10-29 02:12:34 +03:00
out_rele :
2017-06-16 21:00:14 +03:00
xfs_trans_brelse ( args - > trans , bp ) ;
2019-10-29 02:12:34 +03:00
return error ;
2013-08-12 14:49:36 +04:00
}
2017-06-15 07:23:05 +03:00
/*
* Read a directory block and initiate readahead for blocks beyond that .
* We maintain a sliding readahead window of the remaining space in the
* buffer rounded up to the nearest block .
*/
2013-08-12 14:49:36 +04:00
STATIC int
xfs_dir2_leaf_readbuf (
2014-06-06 09:20:32 +04:00
struct xfs_da_args * args ,
2013-08-12 14:49:36 +04:00
size_t bufsize ,
2017-06-15 07:23:05 +03:00
xfs_dir2_off_t * cur_off ,
xfs_dablk_t * ra_blk ,
struct xfs_buf * * bpp )
2013-08-12 14:49:36 +04:00
{
2014-06-06 09:20:32 +04:00
struct xfs_inode * dp = args - > dp ;
2016-05-18 17:17:26 +03:00
struct xfs_buf * bp = NULL ;
2017-06-15 07:23:05 +03:00
struct xfs_da_geometry * geo = args - > geo ;
struct xfs_ifork * ifp = XFS_IFORK_PTR ( dp , XFS_DATA_FORK ) ;
struct xfs_bmbt_irec map ;
2013-08-12 14:49:36 +04:00
struct blk_plug plug ;
2017-06-15 07:23:05 +03:00
xfs_dir2_off_t new_off ;
xfs_dablk_t next_ra ;
xfs_dablk_t map_off ;
xfs_dablk_t last_da ;
2017-11-03 20:34:43 +03:00
struct xfs_iext_cursor icur ;
2017-06-15 07:23:05 +03:00
int ra_want ;
2013-08-12 14:49:36 +04:00
int error = 0 ;
2021-04-13 21:15:09 +03:00
error = xfs_iread_extents ( args - > trans , dp , XFS_DATA_FORK ) ;
if ( error )
goto out ;
2013-08-12 14:49:36 +04:00
/*
2017-06-15 07:23:05 +03:00
* Look for mapped directory blocks at or above the current offset .
* Truncate down to the nearest directory block to start the scanning
* operation .
2013-08-12 14:49:36 +04:00
*/
2017-06-15 07:23:05 +03:00
last_da = xfs_dir2_byte_to_da ( geo , XFS_DIR2_LEAF_OFFSET ) ;
map_off = xfs_dir2_db_to_da ( geo , xfs_dir2_byte_to_db ( geo , * cur_off ) ) ;
2017-11-03 20:34:43 +03:00
if ( ! xfs_iext_lookup_extent ( dp , ifp , map_off , & icur , & map ) )
2013-08-12 14:49:36 +04:00
goto out ;
2017-06-15 07:23:05 +03:00
if ( map . br_startoff > = last_da )
goto out ;
xfs_trim_extent ( & map , map_off , last_da - map_off ) ;
2013-08-12 14:49:36 +04:00
2017-06-15 07:23:05 +03:00
/* Read the directory block of that first mapping. */
new_off = xfs_dir2_da_to_byte ( geo , map . br_startoff ) ;
if ( new_off > * cur_off )
* cur_off = new_off ;
2019-11-20 20:46:04 +03:00
error = xfs_dir3_data_read ( args - > trans , dp , map . br_startoff , 0 , & bp ) ;
2013-08-12 14:49:36 +04:00
if ( error )
2017-06-15 07:23:05 +03:00
goto out ;
2013-08-12 14:49:36 +04:00
/*
2017-06-15 07:23:05 +03:00
* Start readahead for the next bufsize ' s worth of dir data blocks .
* We may have already issued readahead for some of that range ;
* ra_blk tracks the last block we tried to read ( ahead ) .
2013-08-12 14:49:36 +04:00
*/
2017-06-15 07:23:05 +03:00
ra_want = howmany ( bufsize + geo - > blksize , ( 1 < < geo - > fsblog ) ) ;
if ( * ra_blk > = last_da )
goto out ;
else if ( * ra_blk = = 0 )
* ra_blk = map . br_startoff ;
next_ra = map . br_startoff + geo - > fsbcount ;
if ( next_ra > = last_da )
goto out_no_ra ;
if ( map . br_blockcount < geo - > fsbcount & &
2017-11-03 20:34:43 +03:00
! xfs_iext_next_extent ( ifp , & icur , & map ) )
2017-06-15 07:23:05 +03:00
goto out_no_ra ;
if ( map . br_startoff > = last_da )
goto out_no_ra ;
xfs_trim_extent ( & map , next_ra , last_da - next_ra ) ;
/* Start ra for each dir (not fs) block that has a mapping. */
2013-08-12 14:49:36 +04:00
blk_start_plug ( & plug ) ;
2017-06-15 07:23:05 +03:00
while ( ra_want > 0 ) {
next_ra = roundup ( ( xfs_dablk_t ) map . br_startoff , geo - > fsbcount ) ;
while ( ra_want > 0 & &
next_ra < map . br_startoff + map . br_blockcount ) {
if ( next_ra > = last_da ) {
* ra_blk = last_da ;
break ;
}
if ( next_ra > * ra_blk ) {
2019-11-20 20:46:02 +03:00
xfs_dir3_data_readahead ( dp , next_ra ,
XFS_DABUF_MAP_HOLE_OK ) ;
2017-06-15 07:23:05 +03:00
* ra_blk = next_ra ;
2013-08-12 14:49:36 +04:00
}
2017-06-15 07:23:05 +03:00
ra_want - = geo - > fsbcount ;
next_ra + = geo - > fsbcount ;
}
2017-11-03 20:34:43 +03:00
if ( ! xfs_iext_next_extent ( ifp , & icur , & map ) ) {
2017-06-15 07:23:05 +03:00
* ra_blk = last_da ;
break ;
2013-08-12 14:49:36 +04:00
}
}
blk_finish_plug ( & plug ) ;
out :
* bpp = bp ;
return error ;
2017-06-15 07:23:05 +03:00
out_no_ra :
* ra_blk = last_da ;
goto out ;
2013-08-12 14:49:36 +04:00
}
/*
* Getdents ( readdir ) for leaf and node directories .
* This reads the data blocks only , so is the same for both forms .
*/
STATIC int
xfs_dir2_leaf_getdents (
2014-06-06 09:20:32 +04:00
struct xfs_da_args * args ,
2013-08-12 14:49:36 +04:00
struct dir_context * ctx ,
2022-01-05 04:38:36 +03:00
size_t bufsize ,
unsigned int * lock_mode )
2013-08-12 14:49:36 +04:00
{
2014-06-06 09:20:32 +04:00
struct xfs_inode * dp = args - > dp ;
2019-11-09 02:05:37 +03:00
struct xfs_mount * mp = dp - > i_mount ;
2013-08-12 14:49:36 +04:00
struct xfs_buf * bp = NULL ; /* data block buffer */
xfs_dir2_data_entry_t * dep ; /* data entry */
xfs_dir2_data_unused_t * dup ; /* unused entry */
2014-06-06 09:20:32 +04:00
struct xfs_da_geometry * geo = args - > geo ;
2017-06-15 07:23:05 +03:00
xfs_dablk_t rablk = 0 ; /* current readahead block */
xfs_dir2_off_t curoff ; /* current overall offset */
int length ; /* temporary length value */
int byteoff ; /* offset in current block */
2019-11-09 02:05:32 +03:00
unsigned int offset = 0 ;
2017-06-15 07:23:05 +03:00
int error = 0 ; /* error return value */
2013-08-12 14:49:36 +04:00
/*
* If the offset is at or past the largest allowed value ,
* give up right away .
*/
if ( ctx - > pos > = XFS_DIR2_MAX_DATAPTR )
return 0 ;
/*
* Inside the loop we keep the main offset value as a byte offset
* in the directory file .
*/
2014-04-14 13:02:30 +04:00
curoff = xfs_dir2_dataptr_to_byte ( ctx - > pos ) ;
2013-08-12 14:49:36 +04:00
/*
* Loop over directory entries until we reach the end offset .
* Get more blocks and readahead as necessary .
*/
while ( curoff < XFS_DIR2_LEAF_OFFSET ) {
2017-06-16 21:00:05 +03:00
uint8_t filetype ;
2013-08-12 14:50:09 +04:00
2013-08-12 14:49:36 +04:00
/*
* If we have no buffer , or we ' re off the end of the
* current buffer , need to get another one .
*/
2019-11-09 02:05:32 +03:00
if ( ! bp | | offset > = geo - > blksize ) {
2016-05-18 17:17:26 +03:00
if ( bp ) {
2017-06-15 07:23:05 +03:00
xfs_trans_brelse ( args - > trans , bp ) ;
2016-05-18 17:17:26 +03:00
bp = NULL ;
}
2013-08-12 14:49:36 +04:00
2022-01-05 04:38:36 +03:00
if ( * lock_mode = = 0 )
* lock_mode = xfs_ilock_data_map_shared ( dp ) ;
2017-06-15 07:23:05 +03:00
error = xfs_dir2_leaf_readbuf ( args , bufsize , & curoff ,
& rablk , & bp ) ;
if ( error | | ! bp )
2013-08-12 14:49:36 +04:00
break ;
2022-01-05 04:38:36 +03:00
xfs_iunlock ( dp , * lock_mode ) ;
* lock_mode = 0 ;
2013-08-12 14:49:36 +04:00
xfs_dir3_data_check ( dp , bp ) ;
/*
* Find our position in the block .
*/
2019-11-09 02:05:38 +03:00
offset = geo - > data_entry_offset ;
2014-06-06 09:20:32 +04:00
byteoff = xfs_dir2_byte_to_off ( geo , curoff ) ;
2013-08-12 14:49:36 +04:00
/*
* Skip past the header .
*/
if ( byteoff = = 0 )
2019-11-09 02:05:38 +03:00
curoff + = geo - > data_entry_offset ;
2013-08-12 14:49:36 +04:00
/*
* Skip past entries until we reach our offset .
*/
else {
2019-11-09 02:05:32 +03:00
while ( offset < byteoff ) {
dup = bp - > b_addr + offset ;
2013-08-12 14:49:36 +04:00
if ( be16_to_cpu ( dup - > freetag )
= = XFS_DIR2_DATA_FREE_TAG ) {
length = be16_to_cpu ( dup - > length ) ;
2019-11-09 02:05:32 +03:00
offset + = length ;
2013-08-12 14:49:36 +04:00
continue ;
}
2019-11-09 02:05:32 +03:00
dep = bp - > b_addr + offset ;
2019-11-09 02:05:37 +03:00
length = xfs_dir2_data_entsize ( mp ,
dep - > namelen ) ;
2019-11-09 02:05:32 +03:00
offset + = length ;
2013-08-12 14:49:36 +04:00
}
/*
* Now set our real offset .
*/
curoff =
2014-06-06 09:08:18 +04:00
xfs_dir2_db_off_to_byte ( geo ,
xfs_dir2_byte_to_db ( geo , curoff ) ,
2019-11-09 02:05:32 +03:00
offset ) ;
if ( offset > = geo - > blksize )
2013-08-12 14:49:36 +04:00
continue ;
}
}
2019-11-09 02:05:32 +03:00
2013-08-12 14:49:36 +04:00
/*
2019-11-09 02:05:32 +03:00
* We have a pointer to an entry . Is it a live one ?
2013-08-12 14:49:36 +04:00
*/
2019-11-09 02:05:32 +03:00
dup = bp - > b_addr + offset ;
2013-08-12 14:49:36 +04:00
/*
* No , it ' s unused , skip over it .
*/
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
length = be16_to_cpu ( dup - > length ) ;
2019-11-09 02:05:32 +03:00
offset + = length ;
2013-08-12 14:49:36 +04:00
curoff + = length ;
continue ;
}
2019-11-09 02:05:32 +03:00
dep = bp - > b_addr + offset ;
2019-11-09 02:05:37 +03:00
length = xfs_dir2_data_entsize ( mp , dep - > namelen ) ;
2019-11-09 02:05:48 +03:00
filetype = xfs_dir2_data_get_ftype ( mp , dep ) ;
2013-08-12 14:49:36 +04:00
2014-04-14 13:02:30 +04:00
ctx - > pos = xfs_dir2_byte_to_dataptr ( curoff ) & 0x7fffffff ;
2019-11-11 23:53:22 +03:00
if ( XFS_IS_CORRUPT ( dp - > i_mount ,
! xfs_dir2_namecheck ( dep - > name ,
dep - > namelen ) ) ) {
2019-10-29 02:12:34 +03:00
error = - EFSCORRUPTED ;
break ;
}
2013-08-12 14:49:36 +04:00
if ( ! dir_emit ( ctx , ( char * ) dep - > name , dep - > namelen ,
2013-08-12 14:50:09 +04:00
be64_to_cpu ( dep - > inumber ) ,
2014-06-06 09:20:32 +04:00
xfs_dir3_get_dtype ( dp - > i_mount , filetype ) ) )
2013-08-12 14:49:36 +04:00
break ;
/*
* Advance to next entry in the block .
*/
2019-11-09 02:05:32 +03:00
offset + = length ;
2013-08-12 14:49:36 +04:00
curoff + = length ;
/* bufsize may have just been a guess; don't go negative */
bufsize = bufsize > length ? bufsize - length : 0 ;
}
/*
* All done . Set output offset value to current offset .
*/
2014-04-14 13:02:30 +04:00
if ( curoff > xfs_dir2_dataptr_to_byte ( XFS_DIR2_MAX_DATAPTR ) )
2013-08-12 14:49:36 +04:00
ctx - > pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff ;
else
2014-04-14 13:02:30 +04:00
ctx - > pos = xfs_dir2_byte_to_dataptr ( curoff ) & 0x7fffffff ;
2013-08-12 14:49:36 +04:00
if ( bp )
2017-06-16 21:00:14 +03:00
xfs_trans_brelse ( args - > trans , bp ) ;
2013-08-12 14:49:36 +04:00
return error ;
}
/*
* Read a directory .
2017-06-16 21:00:14 +03:00
*
* If supplied , the transaction collects locked dir buffers to avoid
* nested buffer deadlocks . This function does not dirty the
2022-01-05 04:38:36 +03:00
* transaction . The caller must hold the IOLOCK ( shared or exclusive )
2017-06-16 21:00:14 +03:00
* before calling this function .
2013-08-12 14:49:36 +04:00
*/
int
xfs_readdir (
2017-06-16 21:00:14 +03:00
struct xfs_trans * tp ,
2014-06-06 09:20:32 +04:00
struct xfs_inode * dp ,
struct dir_context * ctx ,
size_t bufsize )
2013-08-12 14:49:36 +04:00
{
2014-06-10 01:30:36 +04:00
struct xfs_da_args args = { NULL } ;
2022-01-05 04:38:36 +03:00
unsigned int lock_mode ;
int isblock ;
int error ;
2013-08-12 14:49:36 +04:00
trace_xfs_readdir ( dp ) ;
2021-08-19 04:46:53 +03:00
if ( xfs_is_shutdown ( dp - > i_mount ) )
2014-06-25 08:58:08 +04:00
return - EIO ;
2013-08-12 14:49:36 +04:00
2016-02-09 08:54:58 +03:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2022-01-05 04:38:36 +03:00
ASSERT ( xfs_isilocked ( dp , XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL ) ) ;
2015-10-12 10:21:22 +03:00
XFS_STATS_INC ( dp - > i_mount , xs_dir_getdents ) ;
2013-08-12 14:49:36 +04:00
2014-06-06 09:20:32 +04:00
args . dp = dp ;
args . geo = dp - > i_mount - > m_dir_geo ;
2017-06-16 21:00:14 +03:00
args . trans = tp ;
2014-06-06 09:20:32 +04:00
2020-05-18 20:28:05 +03:00
if ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL )
2022-01-05 04:38:36 +03:00
return xfs_dir2_sf_getdents ( & args , ctx ) ;
2013-12-07 00:30:11 +04:00
2022-01-05 04:38:36 +03:00
lock_mode = xfs_ilock_data_map_shared ( dp ) ;
error = xfs_dir2_isblock ( & args , & isblock ) ;
if ( error )
goto out_unlock ;
if ( isblock ) {
error = xfs_dir2_block_getdents ( & args , ctx , & lock_mode ) ;
goto out_unlock ;
}
error = xfs_dir2_leaf_getdents ( & args , ctx , bufsize , & lock_mode ) ;
out_unlock :
if ( lock_mode )
xfs_iunlock ( dp , lock_mode ) ;
return error ;
2013-08-12 14:49:36 +04:00
}