2018-06-05 19:42:14 -07:00
// SPDX-License-Identifier: GPL-2.0
2005-04-16 15:20:36 -07:00
/*
2005-11-02 14:58:39 +11:00
* Copyright ( c ) 2000 - 2001 , 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-16 15:20:36 -07:00
*/
# include "xfs.h"
2005-11-02 14:38:42 +11:00
# include "xfs_fs.h"
2019-06-28 19:25:35 -07:00
# include "xfs_shared.h"
2013-10-23 10:51:50 +11:00
# include "xfs_format.h"
2013-10-23 10:50:10 +11:00
# include "xfs_log_format.h"
# include "xfs_trans_resv.h"
2005-04-16 15:20:36 -07:00
# include "xfs_mount.h"
# include "xfs_inode.h"
2013-10-23 10:50:10 +11:00
# include "xfs_trans.h"
2005-04-16 15:20:36 -07:00
# include "xfs_bmap.h"
2013-08-12 20:49:37 +10:00
# include "xfs_dir2.h"
2011-07-13 13:43:48 +02:00
# include "xfs_dir2_priv.h"
2017-10-31 12:04:49 -07:00
# include "xfs_errortag.h"
2005-04-16 15:20:36 -07:00
# include "xfs_error.h"
2009-12-14 23:14:59 +00:00
# include "xfs_trace.h"
2024-02-22 12:32:18 -08:00
# include "xfs_health.h"
2005-04-16 15:20:36 -07:00
2022-03-09 10:16:12 -08:00
const struct xfs_name xfs_name_dotdot = {
. name = ( const unsigned char * ) " .. " ,
. len = 2 ,
. type = XFS_DIR3_FT_DIR ,
} ;
2013-08-12 20:50:09 +10:00
2024-02-22 12:30:48 -08:00
const struct xfs_name xfs_name_dot = {
. name = ( const unsigned char * ) " . " ,
. len = 1 ,
. type = XFS_DIR3_FT_DIR ,
} ;
2014-12-04 09:43:17 +11:00
/*
2017-01-17 11:41:43 -08:00
* Convert inode mode to directory entry filetype
2014-12-04 09:43:17 +11:00
*/
2017-10-17 21:37:44 -07:00
unsigned char
xfs_mode_to_ftype (
int mode )
2017-01-17 11:41:43 -08:00
{
switch ( mode & S_IFMT ) {
case S_IFREG :
return XFS_DIR3_FT_REG_FILE ;
case S_IFDIR :
return XFS_DIR3_FT_DIR ;
case S_IFCHR :
return XFS_DIR3_FT_CHRDEV ;
case S_IFBLK :
return XFS_DIR3_FT_BLKDEV ;
case S_IFIFO :
return XFS_DIR3_FT_FIFO ;
case S_IFSOCK :
return XFS_DIR3_FT_SOCK ;
case S_IFLNK :
return XFS_DIR3_FT_SYMLINK ;
default :
return XFS_DIR3_FT_UNKNOWN ;
}
}
2005-04-16 15:20:36 -07:00
2008-05-21 16:58:55 +10:00
/*
* ASCII case - insensitive ( ie . A - Z ) support for directories that was
* used in IRIX .
*/
2019-11-11 12:59:26 -08:00
xfs_dahash_t
2008-05-21 16:58:55 +10:00
xfs_ascii_ci_hashname (
2022-03-09 10:16:09 -08:00
const struct xfs_name * name )
2008-05-21 16:58:55 +10:00
{
2022-03-09 10:16:09 -08:00
xfs_dahash_t hash ;
int i ;
2008-05-21 16:58:55 +10:00
for ( i = 0 , hash = 0 ; i < name - > len ; i + + )
2023-04-11 19:05:04 -07:00
hash = xfs_ascii_ci_xfrm ( name - > name [ i ] ) ^ rol32 ( hash , 7 ) ;
2008-05-21 16:58:55 +10:00
return hash ;
}
2019-11-11 12:59:26 -08:00
enum xfs_dacmp
2008-05-21 16:58:55 +10:00
xfs_ascii_ci_compname (
2019-11-11 12:59:26 -08:00
struct xfs_da_args * args ,
const unsigned char * name ,
int len )
2008-05-21 16:58:55 +10:00
{
2019-11-11 12:59:26 -08:00
enum xfs_dacmp result ;
int i ;
2008-05-21 16:58:55 +10:00
if ( args - > namelen ! = len )
return XFS_CMP_DIFFERENT ;
result = XFS_CMP_EXACT ;
for ( i = 0 ; i < len ; i + + ) {
if ( args - > name [ i ] = = name [ i ] )
continue ;
2023-04-11 19:05:04 -07:00
if ( xfs_ascii_ci_xfrm ( args - > name [ i ] ) ! =
xfs_ascii_ci_xfrm ( name [ i ] ) )
2008-05-21 16:58:55 +10:00
return XFS_CMP_DIFFERENT ;
result = XFS_CMP_CASE ;
}
return result ;
}
2014-06-06 15:01:58 +10:00
int
xfs_da_mount (
struct xfs_mount * mp )
2005-04-16 15:20:36 -07:00
{
2014-06-06 15:01:58 +10:00
struct xfs_da_geometry * dageo ;
2013-08-26 14:13:30 +10:00
2014-05-20 07:46:55 +10:00
ASSERT ( mp - > m_sb . sb_versionnum & XFS_SB_VERSION_DIRV2BIT ) ;
2018-01-08 10:51:27 -08:00
ASSERT ( xfs_dir2_dirblock_bytes ( & mp - > m_sb ) < = XFS_MAX_BLOCKSIZE ) ;
2013-10-29 22:11:51 +11:00
2024-01-16 09:59:39 +11:00
mp - > m_dir_geo = kzalloc ( sizeof ( struct xfs_da_geometry ) ,
GFP_KERNEL | __GFP_RETRY_MAYFAIL ) ;
mp - > m_attr_geo = kzalloc ( sizeof ( struct xfs_da_geometry ) ,
GFP_KERNEL | __GFP_RETRY_MAYFAIL ) ;
2014-06-06 15:01:58 +10:00
if ( ! mp - > m_dir_geo | | ! mp - > m_attr_geo ) {
2024-01-16 09:59:43 +11:00
kfree ( mp - > m_dir_geo ) ;
kfree ( mp - > m_attr_geo ) ;
2014-06-25 14:58:08 +10:00
return - ENOMEM ;
2014-06-06 15:01:58 +10:00
}
/* set up directory geometry */
dageo = mp - > m_dir_geo ;
dageo - > blklog = mp - > m_sb . sb_blocklog + mp - > m_sb . sb_dirblklog ;
dageo - > fsblog = mp - > m_sb . sb_blocklog ;
2018-01-08 10:51:27 -08:00
dageo - > blksize = xfs_dir2_dirblock_bytes ( & mp - > m_sb ) ;
2014-06-06 15:01:58 +10:00
dageo - > fsbcount = 1 < < mp - > m_sb . sb_dirblklog ;
2021-08-18 18:46:55 -07:00
if ( xfs_has_crc ( mp ) ) {
2019-11-08 14:57:49 -08:00
dageo - > node_hdr_size = sizeof ( struct xfs_da3_node_hdr ) ;
2019-11-08 14:57:51 -08:00
dageo - > leaf_hdr_size = sizeof ( struct xfs_dir3_leaf_hdr ) ;
2019-11-08 15:01:29 -08:00
dageo - > free_hdr_size = sizeof ( struct xfs_dir3_free_hdr ) ;
2019-11-08 15:05:38 -08:00
dageo - > data_entry_offset =
sizeof ( struct xfs_dir3_data_hdr ) ;
2019-11-08 14:57:51 -08:00
} else {
2019-11-08 14:57:49 -08:00
dageo - > node_hdr_size = sizeof ( struct xfs_da_node_hdr ) ;
2019-11-08 14:57:51 -08:00
dageo - > leaf_hdr_size = sizeof ( struct xfs_dir2_leaf_hdr ) ;
2019-11-08 15:01:29 -08:00
dageo - > free_hdr_size = sizeof ( struct xfs_dir2_free_hdr ) ;
2019-11-08 15:05:38 -08:00
dageo - > data_entry_offset =
sizeof ( struct xfs_dir2_data_hdr ) ;
2019-11-08 14:57:51 -08:00
}
2019-11-08 14:57:51 -08:00
dageo - > leaf_max_ents = ( dageo - > blksize - dageo - > leaf_hdr_size ) /
sizeof ( struct xfs_dir2_leaf_entry ) ;
2019-11-08 15:01:30 -08:00
dageo - > free_max_bests = ( dageo - > blksize - dageo - > free_hdr_size ) /
sizeof ( xfs_dir2_data_off_t ) ;
2014-06-06 15:08:18 +10:00
2019-11-08 15:05:38 -08:00
dageo - > data_first_offset = dageo - > data_entry_offset +
xfs_dir2_data_entsize ( mp , 1 ) +
xfs_dir2_data_entsize ( mp , 2 ) ;
2014-06-06 15:08:18 +10:00
/*
* Now we ' ve set up the block conversion variables , we can calculate the
* segment block constants using the geometry structure .
*/
dageo - > datablk = xfs_dir2_byte_to_da ( dageo , XFS_DIR2_DATA_OFFSET ) ;
dageo - > leafblk = xfs_dir2_byte_to_da ( dageo , XFS_DIR2_LEAF_OFFSET ) ;
dageo - > freeblk = xfs_dir2_byte_to_da ( dageo , XFS_DIR2_FREE_OFFSET ) ;
2019-11-08 14:57:49 -08:00
dageo - > node_ents = ( dageo - > blksize - dageo - > node_hdr_size ) /
2013-08-26 14:13:30 +10:00
( uint ) sizeof ( xfs_da_node_entry_t ) ;
2022-03-29 06:14:00 +00:00
dageo - > max_extents = ( XFS_DIR2_MAX_SPACES * XFS_DIR2_SPACE_SIZE ) > >
mp - > m_sb . sb_blocklog ;
2014-06-06 15:01:58 +10:00
dageo - > magicpct = ( dageo - > blksize * 37 ) / 100 ;
/* set up attribute geometry - single fsb only */
dageo = mp - > m_attr_geo ;
dageo - > blklog = mp - > m_sb . sb_blocklog ;
dageo - > fsblog = mp - > m_sb . sb_blocklog ;
dageo - > blksize = 1 < < dageo - > blklog ;
dageo - > fsbcount = 1 ;
2019-11-08 14:57:49 -08:00
dageo - > node_hdr_size = mp - > m_dir_geo - > node_hdr_size ;
dageo - > node_ents = ( dageo - > blksize - dageo - > node_hdr_size ) /
2013-08-26 14:13:30 +10:00
( uint ) sizeof ( xfs_da_node_entry_t ) ;
2022-03-29 06:14:00 +00:00
if ( xfs_has_large_extent_counts ( mp ) )
dageo - > max_extents = XFS_MAX_EXTCNT_ATTR_FORK_LARGE ;
else
dageo - > max_extents = XFS_MAX_EXTCNT_ATTR_FORK_SMALL ;
2014-06-06 15:01:58 +10:00
dageo - > magicpct = ( dageo - > blksize * 37 ) / 100 ;
return 0 ;
}
void
xfs_da_unmount (
struct xfs_mount * mp )
{
2024-01-16 09:59:43 +11:00
kfree ( mp - > m_dir_geo ) ;
kfree ( mp - > m_attr_geo ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Return 1 if directory contains only " . " and " .. " .
*/
2006-06-20 13:04:51 +10:00
int
xfs_dir_isempty (
xfs_inode_t * dp )
2005-04-16 15:20:36 -07:00
{
2011-07-08 14:35:13 +02:00
xfs_dir2_sf_hdr_t * sfp ;
2005-04-16 15:20:36 -07:00
2016-02-09 16:54:58 +11:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2021-03-29 11:11:40 -07:00
if ( dp - > i_disk_size = = 0 ) /* might happen during shutdown. */
2005-04-16 15:20:36 -07:00
return 1 ;
2022-07-09 10:56:07 -07:00
if ( dp - > i_disk_size > xfs_inode_data_fork_size ( dp ) )
2005-04-16 15:20:36 -07:00
return 0 ;
2023-12-20 07:34:55 +01:00
sfp = dp - > i_df . if_data ;
2011-07-08 14:35:13 +02:00
return ! sfp - > count ;
2005-04-16 15:20:36 -07:00
}
2006-06-20 13:04:51 +10:00
/*
* Validate a given inode number .
*/
int
xfs_dir_ino_validate (
xfs_mount_t * mp ,
xfs_ino_t ino )
{
2017-10-17 21:37:34 -07:00
bool ino_ok = xfs_verify_dir_ino ( mp , ino ) ;
2019-11-11 12:53:22 -08:00
if ( XFS_IS_CORRUPT ( mp , ! ino_ok ) | |
XFS_TEST_ERROR ( false , mp , XFS_ERRTAG_DIR_INO_VALIDATE ) ) {
2011-03-07 10:05:35 +11:00
xfs_warn ( mp , " Invalid inode number 0x%Lx " ,
2006-06-20 13:04:51 +10:00
( unsigned long long ) ino ) ;
2014-06-25 14:58:08 +10:00
return - EFSCORRUPTED ;
2006-06-20 13:04:51 +10:00
}
return 0 ;
}
2005-04-16 15:20:36 -07:00
/*
* Initialize a directory with its " . " and " .. " entries .
*/
2006-06-20 13:04:51 +10:00
int
xfs_dir_init (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
xfs_inode_t * pdp )
2005-04-16 15:20:36 -07:00
{
2014-02-27 16:51:26 +11:00
struct xfs_da_args * args ;
2006-06-20 13:04:51 +10:00
int error ;
2005-04-16 15:20:36 -07:00
2016-02-09 16:54:58 +11:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2014-02-27 16:51:26 +11:00
error = xfs_dir_ino_validate ( tp - > t_mountp , pdp - > i_ino ) ;
if ( error )
2005-04-16 15:20:36 -07:00
return error ;
2014-02-27 16:51:26 +11:00
2024-01-16 09:59:46 +11:00
args = kzalloc ( sizeof ( * args ) , GFP_KERNEL | __GFP_NOFAIL ) ;
2014-02-27 16:51:26 +11:00
if ( ! args )
2014-06-25 14:58:08 +10:00
return - ENOMEM ;
2014-02-27 16:51:26 +11:00
2014-06-06 15:01:58 +10:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 16:51:26 +11:00
args - > dp = dp ;
args - > trans = tp ;
2024-04-15 14:54:34 -07:00
args - > owner = dp - > i_ino ;
2014-02-27 16:51:26 +11:00
error = xfs_dir2_sf_create ( args , pdp - > i_ino ) ;
2024-01-16 09:59:43 +11:00
kfree ( args ) ;
2014-02-27 16:51:26 +11:00
return error ;
2005-04-16 15:20:36 -07:00
}
2024-04-25 15:17:03 +02:00
enum xfs_dir2_fmt
xfs_dir2_format (
struct xfs_da_args * args ,
int * error )
{
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
struct xfs_da_geometry * geo = mp - > m_dir_geo ;
xfs_fileoff_t eof ;
xfs_assert_ilocked ( dp , XFS_ILOCK_SHARED | XFS_ILOCK_EXCL ) ;
* error = 0 ;
if ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL )
return XFS_DIR2_FMT_SF ;
* error = xfs_bmap_last_offset ( dp , & eof , XFS_DATA_FORK ) ;
if ( * error )
return XFS_DIR2_FMT_ERROR ;
if ( eof = = XFS_B_TO_FSB ( mp , geo - > blksize ) ) {
if ( XFS_IS_CORRUPT ( mp , dp - > i_disk_size ! = geo - > blksize ) ) {
xfs_da_mark_sick ( args ) ;
* error = - EFSCORRUPTED ;
return XFS_DIR2_FMT_ERROR ;
}
return XFS_DIR2_FMT_BLOCK ;
}
if ( eof = = geo - > leafblk + geo - > fsbcount )
return XFS_DIR2_FMT_LEAF ;
return XFS_DIR2_FMT_NODE ;
}
2024-04-25 15:17:00 +02:00
int
xfs_dir_createname_args (
struct xfs_da_args * args )
{
int error ;
if ( ! args - > inumber )
args - > op_flags | = XFS_DA_OP_JUSTCHECK ;
2024-04-25 15:17:03 +02:00
switch ( xfs_dir2_format ( args , & error ) ) {
case XFS_DIR2_FMT_SF :
2024-04-25 15:17:00 +02:00
return xfs_dir2_sf_addname ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_BLOCK :
2024-04-25 15:17:00 +02:00
return xfs_dir2_block_addname ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_LEAF :
2024-04-25 15:17:00 +02:00
return xfs_dir2_leaf_addname ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_NODE :
return xfs_dir2_node_addname ( args ) ;
default :
return error ;
}
2024-04-25 15:17:00 +02:00
}
2005-04-16 15:20:36 -07:00
/*
2014-09-09 11:58:07 +10:00
* Enter a name in a directory , or check for available space .
* If inum is 0 , only the available space test is performed .
2005-04-16 15:20:36 -07:00
*/
2006-06-20 13:04:51 +10:00
int
xfs_dir_createname (
2018-07-11 22:26:11 -07:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
2022-03-09 10:16:09 -08:00
const struct xfs_name * name ,
2005-04-16 15:20:36 -07:00
xfs_ino_t inum , /* new entry inode number */
xfs_extlen_t total ) /* bmap's total block count */
{
2014-02-27 16:51:26 +11:00
struct xfs_da_args * args ;
2006-06-20 13:04:51 +10:00
int rval ;
2005-04-16 15:20:36 -07:00
2016-02-09 16:54:58 +11:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2018-07-11 22:26:08 -07:00
2014-09-09 11:58:07 +10:00
if ( inum ) {
rval = xfs_dir_ino_validate ( tp - > t_mountp , inum ) ;
if ( rval )
return rval ;
2015-10-12 18:21:22 +11:00
XFS_STATS_INC ( dp - > i_mount , xs_dir_create ) ;
2014-09-09 11:58:07 +10:00
}
2006-06-20 13:04:51 +10:00
2024-01-16 09:59:46 +11:00
args = kzalloc ( sizeof ( * args ) , GFP_KERNEL | __GFP_NOFAIL ) ;
2014-02-27 16:51:26 +11:00
if ( ! args )
2014-06-25 14:58:08 +10:00
return - ENOMEM ;
2014-02-27 16:51:26 +11:00
2014-06-06 15:01:58 +10:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 16:51:26 +11:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
2019-11-11 12:59:26 -08:00
args - > hashval = xfs_dir2_hashname ( dp - > i_mount , name ) ;
2014-02-27 16:51:26 +11:00
args - > inumber = inum ;
args - > dp = dp ;
args - > total = total ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
args - > op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT ;
2024-04-15 14:54:34 -07:00
args - > owner = dp - > i_ino ;
2014-02-27 16:51:26 +11:00
2024-04-25 15:17:00 +02:00
rval = xfs_dir_createname_args ( args ) ;
2024-01-16 09:59:43 +11:00
kfree ( args ) ;
2005-04-16 15:20:36 -07:00
return rval ;
}
2008-05-21 16:58:22 +10:00
/*
* If doing a CI lookup and case - insensitive match , dup actual name into
* args . value . Return EEXIST for success ( ie . name found ) or an error .
*/
int
xfs_dir_cilookup_result (
struct xfs_da_args * args ,
2010-01-20 10:47:25 +11:00
const unsigned char * name ,
2008-05-21 16:58:22 +10:00
int len )
{
if ( args - > cmpresult = = XFS_CMP_DIFFERENT )
2014-06-25 14:58:08 +10:00
return - ENOENT ;
2008-05-21 16:58:22 +10:00
if ( args - > cmpresult ! = XFS_CMP_CASE | |
! ( args - > op_flags & XFS_DA_OP_CILOOKUP ) )
2014-06-25 14:58:08 +10:00
return - EEXIST ;
2008-05-21 16:58:22 +10:00
2024-01-16 09:59:45 +11:00
args - > value = kmalloc ( len ,
GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_RETRY_MAYFAIL ) ;
2008-05-21 16:58:22 +10:00
if ( ! args - > value )
2014-06-25 14:58:08 +10:00
return - ENOMEM ;
2008-05-21 16:58:22 +10:00
memcpy ( args - > value , name , len ) ;
args - > valuelen = len ;
2014-06-25 14:58:08 +10:00
return - EEXIST ;
2008-05-21 16:58:22 +10:00
}
2024-04-25 15:16:59 +02:00
int
xfs_dir_lookup_args (
struct xfs_da_args * args )
{
int error ;
2024-04-25 15:17:03 +02:00
switch ( xfs_dir2_format ( args , & error ) ) {
case XFS_DIR2_FMT_SF :
2024-04-25 15:16:59 +02:00
error = xfs_dir2_sf_lookup ( args ) ;
2024-04-25 15:17:03 +02:00
break ;
case XFS_DIR2_FMT_BLOCK :
2024-04-25 15:16:59 +02:00
error = xfs_dir2_block_lookup ( args ) ;
2024-04-25 15:17:03 +02:00
break ;
case XFS_DIR2_FMT_LEAF :
2024-04-25 15:16:59 +02:00
error = xfs_dir2_leaf_lookup ( args ) ;
2024-04-25 15:17:03 +02:00
break ;
case XFS_DIR2_FMT_NODE :
2024-04-25 15:16:59 +02:00
error = xfs_dir2_node_lookup ( args ) ;
2024-04-25 15:17:03 +02:00
break ;
default :
break ;
}
2024-04-25 15:16:59 +02:00
if ( error ! = - EEXIST )
return error ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
/*
* Lookup a name in a directory , give back the inode number .
2008-05-21 16:58:22 +10:00
* If ci_name is not NULL , returns the actual name in ci_name if it differs
* to name , or ci_name - > name is set to NULL for an exact match .
2005-04-16 15:20:36 -07:00
*/
2008-05-21 16:58:22 +10:00
2006-06-20 13:04:51 +10:00
int
xfs_dir_lookup (
2022-03-09 10:16:09 -08:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
const struct xfs_name * name ,
xfs_ino_t * inum , /* out: inode number */
struct xfs_name * ci_name ) /* out: actual name if CI match */
2005-04-16 15:20:36 -07:00
{
2022-03-09 10:16:09 -08:00
struct xfs_da_args * args ;
int rval ;
int lock_mode ;
2005-04-16 15:20:36 -07:00
2016-02-09 16:54:58 +11:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2015-10-12 18:21:22 +11:00
XFS_STATS_INC ( dp - > i_mount , xs_dir_lookup ) ;
2005-04-16 15:20:36 -07:00
2024-01-16 09:59:45 +11:00
args = kzalloc ( sizeof ( * args ) ,
GFP_KERNEL | __GFP_NOLOCKDEP | __GFP_NOFAIL ) ;
2014-06-06 15:01:58 +10:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 16:51:26 +11:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
2019-11-11 12:59:26 -08:00
args - > hashval = xfs_dir2_hashname ( dp - > i_mount , name ) ;
2014-02-27 16:51:26 +11:00
args - > dp = dp ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
args - > op_flags = XFS_DA_OP_OKNOENT ;
2024-04-15 14:54:34 -07:00
args - > owner = dp - > i_ino ;
2008-05-21 16:58:22 +10:00
if ( ci_name )
2014-02-27 16:51:26 +11:00
args - > op_flags | = XFS_DA_OP_CILOOKUP ;
2006-06-20 13:04:51 +10:00
xfs: stop holding ILOCK over filldir callbacks
The recent change to the readdir locking made in 40194ec ("xfs:
reinstate the ilock in xfs_readdir") for CXFS directory sanity was
probably the wrong thing to do. Deep in the readdir code we
can take page faults in the filldir callback, and so taking a page
fault while holding an inode ilock creates a new set of locking
issues that lockdep warns all over the place about.
The locking order for regular inodes w.r.t. page faults is io_lock
-> pagefault -> mmap_sem -> ilock. The directory readdir code now
triggers ilock -> page fault -> mmap_sem. While we cannot deadlock
at this point, it inverts all the locking patterns that lockdep
normally sees on XFS inodes, and so triggers lockdep. We worked
around this with commit 93a8614 ("xfs: fix directory inode iolock
lockdep false positive"), but that then just moved the lockdep
warning to deeper in the page fault path and triggered on security
inode locks. Fixing the shmem issue there just moved the lockdep
reports somewhere else, and now we are getting false positives from
filesystem freezing annotations getting confused.
Further, if we enter memory reclaim in a readdir path, we now get
lockdep warning about potential deadlocks because the ilock is held
when we enter reclaim. This, again, is different to a regular file
in that we never allow memory reclaim to run while holding the ilock
for regular files. Hence lockdep now throws
ilock->kmalloc->reclaim->ilock warnings.
Basically, the problem is that the ilock is being used to protect
the directory data and the inode metadata, whereas for a regular
file the iolock protects the data and the ilock protects the
metadata. From the VFS perspective, the i_mutex serialises all
accesses to the directory data, and so not holding the ilock for
readdir doesn't matter. The issue is that CXFS doesn't access
directory data via the VFS, so it has no "data serialisaton"
mechanism. Hence we need to hold the IOLOCK in the correct places to
provide this low level directory data access serialisation.
The ilock can then be used just when the extent list needs to be
read, just like we do for regular files. The directory modification
code can take the iolock exclusive when the ilock is also taken,
and this then ensures that readdir is correct excluded while
modifications are in progress.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-08-19 10:33:00 +10:00
lock_mode = xfs_ilock_data_map_shared ( dp ) ;
2024-04-25 15:16:59 +02:00
rval = xfs_dir_lookup_args ( args ) ;
2008-05-21 16:58:22 +10:00
if ( ! rval ) {
2014-02-27 16:51:26 +11:00
* inum = args - > inumber ;
2008-05-21 16:58:22 +10:00
if ( ci_name ) {
2014-02-27 16:51:26 +11:00
ci_name - > name = args - > value ;
ci_name - > len = args - > valuelen ;
2008-05-21 16:58:22 +10:00
}
}
xfs: stop holding ILOCK over filldir callbacks
The recent change to the readdir locking made in 40194ec ("xfs:
reinstate the ilock in xfs_readdir") for CXFS directory sanity was
probably the wrong thing to do. Deep in the readdir code we
can take page faults in the filldir callback, and so taking a page
fault while holding an inode ilock creates a new set of locking
issues that lockdep warns all over the place about.
The locking order for regular inodes w.r.t. page faults is io_lock
-> pagefault -> mmap_sem -> ilock. The directory readdir code now
triggers ilock -> page fault -> mmap_sem. While we cannot deadlock
at this point, it inverts all the locking patterns that lockdep
normally sees on XFS inodes, and so triggers lockdep. We worked
around this with commit 93a8614 ("xfs: fix directory inode iolock
lockdep false positive"), but that then just moved the lockdep
warning to deeper in the page fault path and triggered on security
inode locks. Fixing the shmem issue there just moved the lockdep
reports somewhere else, and now we are getting false positives from
filesystem freezing annotations getting confused.
Further, if we enter memory reclaim in a readdir path, we now get
lockdep warning about potential deadlocks because the ilock is held
when we enter reclaim. This, again, is different to a regular file
in that we never allow memory reclaim to run while holding the ilock
for regular files. Hence lockdep now throws
ilock->kmalloc->reclaim->ilock warnings.
Basically, the problem is that the ilock is being used to protect
the directory data and the inode metadata, whereas for a regular
file the iolock protects the data and the ilock protects the
metadata. From the VFS perspective, the i_mutex serialises all
accesses to the directory data, and so not holding the ilock for
readdir doesn't matter. The issue is that CXFS doesn't access
directory data via the VFS, so it has no "data serialisaton"
mechanism. Hence we need to hold the IOLOCK in the correct places to
provide this low level directory data access serialisation.
The ilock can then be used just when the extent list needs to be
read, just like we do for regular files. The directory modification
code can take the iolock exclusive when the ilock is also taken,
and this then ensures that readdir is correct excluded while
modifications are in progress.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-08-19 10:33:00 +10:00
xfs_iunlock ( dp , lock_mode ) ;
2024-01-16 09:59:43 +11:00
kfree ( args ) ;
2005-04-16 15:20:36 -07:00
return rval ;
}
2024-04-25 15:17:01 +02:00
int
xfs_dir_removename_args (
struct xfs_da_args * args )
{
int error ;
2024-04-25 15:17:03 +02:00
switch ( xfs_dir2_format ( args , & error ) ) {
case XFS_DIR2_FMT_SF :
2024-04-25 15:17:01 +02:00
return xfs_dir2_sf_removename ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_BLOCK :
2024-04-25 15:17:01 +02:00
return xfs_dir2_block_removename ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_LEAF :
2024-04-25 15:17:01 +02:00
return xfs_dir2_leaf_removename ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_NODE :
return xfs_dir2_node_removename ( args ) ;
default :
return error ;
}
2024-04-25 15:17:01 +02:00
}
2005-04-16 15:20:36 -07:00
/*
* Remove an entry from a directory .
*/
2006-06-20 13:04:51 +10:00
int
xfs_dir_removename (
2018-07-11 22:26:11 -07:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
2024-04-22 09:48:09 -07:00
const struct xfs_name * name ,
2018-07-11 22:26:11 -07:00
xfs_ino_t ino ,
xfs_extlen_t total ) /* bmap's total block count */
2005-04-16 15:20:36 -07:00
{
2018-07-11 22:26:11 -07:00
struct xfs_da_args * args ;
int rval ;
2005-04-16 15:20:36 -07:00
2016-02-09 16:54:58 +11:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2015-10-12 18:21:22 +11:00
XFS_STATS_INC ( dp - > i_mount , xs_dir_remove ) ;
2006-06-20 13:04:51 +10:00
2024-01-16 09:59:46 +11:00
args = kzalloc ( sizeof ( * args ) , GFP_KERNEL | __GFP_NOFAIL ) ;
2014-02-27 16:51:26 +11:00
if ( ! args )
2014-06-25 14:58:08 +10:00
return - ENOMEM ;
2014-02-27 16:51:26 +11:00
2014-06-06 15:01:58 +10:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 16:51:26 +11:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
2019-11-11 12:59:26 -08:00
args - > hashval = xfs_dir2_hashname ( dp - > i_mount , name ) ;
2014-02-27 16:51:26 +11:00
args - > inumber = ino ;
args - > dp = dp ;
args - > total = total ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
2024-04-15 14:54:34 -07:00
args - > owner = dp - > i_ino ;
2024-04-25 15:17:01 +02:00
rval = xfs_dir_removename_args ( args ) ;
2024-01-16 09:59:43 +11:00
kfree ( args ) ;
2005-04-16 15:20:36 -07:00
return rval ;
}
2024-04-25 15:17:02 +02:00
int
xfs_dir_replace_args (
struct xfs_da_args * args )
{
int error ;
2024-04-25 15:17:03 +02:00
switch ( xfs_dir2_format ( args , & error ) ) {
case XFS_DIR2_FMT_SF :
2024-04-25 15:17:02 +02:00
return xfs_dir2_sf_replace ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_BLOCK :
2024-04-25 15:17:02 +02:00
return xfs_dir2_block_replace ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_LEAF :
2024-04-25 15:17:02 +02:00
return xfs_dir2_leaf_replace ( args ) ;
2024-04-25 15:17:03 +02:00
case XFS_DIR2_FMT_NODE :
return xfs_dir2_node_replace ( args ) ;
default :
return error ;
}
2024-04-25 15:17:02 +02:00
}
2005-04-16 15:20:36 -07:00
/*
* Replace the inode number of a directory entry .
*/
2006-06-20 13:04:51 +10:00
int
xfs_dir_replace (
2018-07-11 22:26:11 -07:00
struct xfs_trans * tp ,
struct xfs_inode * dp ,
2022-03-09 10:16:09 -08:00
const struct xfs_name * name , /* name of entry to replace */
2018-07-11 22:26:11 -07:00
xfs_ino_t inum , /* new inode number */
xfs_extlen_t total ) /* bmap's total block count */
2005-04-16 15:20:36 -07:00
{
2018-07-11 22:26:11 -07:00
struct xfs_da_args * args ;
int rval ;
2005-04-16 15:20:36 -07:00
2016-02-09 16:54:58 +11:00
ASSERT ( S_ISDIR ( VFS_I ( dp ) - > i_mode ) ) ;
2005-04-16 15:20:36 -07:00
2014-02-27 16:51:26 +11:00
rval = xfs_dir_ino_validate ( tp - > t_mountp , inum ) ;
if ( rval )
2005-04-16 15:20:36 -07:00
return rval ;
2006-06-20 13:04:51 +10:00
2024-01-16 09:59:46 +11:00
args = kzalloc ( sizeof ( * args ) , GFP_KERNEL | __GFP_NOFAIL ) ;
2014-02-27 16:51:26 +11:00
if ( ! args )
2014-06-25 14:58:08 +10:00
return - ENOMEM ;
2014-02-27 16:51:26 +11:00
2014-06-06 15:01:58 +10:00
args - > geo = dp - > i_mount - > m_dir_geo ;
2014-02-27 16:51:26 +11:00
args - > name = name - > name ;
args - > namelen = name - > len ;
args - > filetype = name - > type ;
2019-11-11 12:59:26 -08:00
args - > hashval = xfs_dir2_hashname ( dp - > i_mount , name ) ;
2014-02-27 16:51:26 +11:00
args - > inumber = inum ;
args - > dp = dp ;
args - > total = total ;
args - > whichfork = XFS_DATA_FORK ;
args - > trans = tp ;
2024-04-15 14:54:34 -07:00
args - > owner = dp - > i_ino ;
2024-04-25 15:17:02 +02:00
rval = xfs_dir_replace_args ( args ) ;
2024-01-16 09:59:43 +11:00
kfree ( args ) ;
2005-04-16 15:20:36 -07:00
return rval ;
}
/*
* See if this entry can be added to the directory without allocating space .
*/
2006-06-20 13:04:51 +10:00
int
xfs_dir_canenter (
xfs_trans_t * tp ,
xfs_inode_t * dp ,
2014-09-09 11:57:52 +10:00
struct xfs_name * name ) /* name of entry to add */
2005-04-16 15:20:36 -07:00
{
2018-07-11 22:26:21 -07:00
return xfs_dir_createname ( tp , dp , name , 0 , 0 ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Utility routines .
*/
/*
* Add a block to the directory .
2011-07-13 13:43:49 +02:00
*
* This routine is for data and free blocks , not leaf / node blocks which are
* handled by xfs_da_grow_inode .
2005-04-16 15:20:36 -07:00
*/
2006-06-20 13:04:51 +10:00
int
2005-04-16 15:20:36 -07:00
xfs_dir2_grow_inode (
2011-07-13 13:43:49 +02:00
struct xfs_da_args * args ,
int space , /* v2 dir's space XFS_DIR2_xxx_SPACE */
xfs_dir2_db_t * dbp ) /* out: block number added */
2005-04-16 15:20:36 -07:00
{
2011-07-13 13:43:49 +02:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
xfs_fileoff_t bno ; /* directory offset of new block */
int count ; /* count of filesystem blocks */
int error ;
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_grow_inode ( args , space ) ;
2005-04-16 15:20:36 -07:00
/*
* Set lowest possible block in the space requested .
*/
bno = XFS_B_TO_FSBT ( mp , space * XFS_DIR2_SPACE_SIZE ) ;
2014-06-06 15:14:11 +10:00
count = args - > geo - > fsbcount ;
2011-07-13 13:43:49 +02:00
error = xfs_da_grow_inode_int ( args , & bno , count ) ;
if ( error )
2005-04-16 15:20:36 -07:00
return error ;
2008-10-30 17:38:12 +11:00
2014-06-06 15:07:53 +10:00
* dbp = xfs_dir2_da_to_db ( args - > geo , ( xfs_dablk_t ) bno ) ;
2008-10-30 17:38:12 +11:00
2005-04-16 15:20:36 -07:00
/*
* Update file ' s size if this is the data space and it grew .
*/
if ( space = = XFS_DIR2_DATA_SPACE ) {
xfs_fsize_t size ; /* directory file (data) size */
size = XFS_FSB_TO_B ( mp , bno + count ) ;
2021-03-29 11:11:40 -07:00
if ( size > dp - > i_disk_size ) {
dp - > i_disk_size = size ;
2011-07-13 13:43:49 +02:00
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE ) ;
2005-04-16 15:20:36 -07:00
}
}
return 0 ;
}
/*
* Remove the given block from the directory .
* This routine is used for data and free blocks , leaf / node are done
* by xfs_da_shrink_inode .
*/
int
xfs_dir2_shrink_inode (
2018-07-11 22:26:11 -07:00
struct xfs_da_args * args ,
xfs_dir2_db_t db ,
struct xfs_buf * bp )
2005-04-16 15:20:36 -07:00
{
2018-07-11 22:26:11 -07:00
xfs_fileoff_t bno ; /* directory file offset */
xfs_dablk_t da ; /* directory file offset */
int done ; /* bunmap is finished */
struct xfs_inode * dp ;
int error ;
struct xfs_mount * mp ;
struct xfs_trans * tp ;
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_shrink_inode ( args , db ) ;
2005-04-16 15:20:36 -07:00
dp = args - > dp ;
mp = dp - > i_mount ;
tp = args - > trans ;
2014-06-06 15:07:53 +10:00
da = xfs_dir2_db_to_da ( args - > geo , db ) ;
2015-07-29 11:51:01 +10:00
/* Unmap the fsblock(s). */
2018-07-11 22:26:25 -07:00
error = xfs_bunmapi ( tp , dp , da , args - > geo - > fsbcount , 0 , 0 , & done ) ;
2015-07-29 11:51:01 +10:00
if ( error ) {
2005-04-16 15:20:36 -07:00
/*
2015-07-29 11:51:01 +10:00
* ENOSPC actually can happen if we ' re in a removename with no
* space reservation , and the resulting block removal would
* cause a bmap btree split or conversion from extents to btree .
* This can only happen for un - fragmented directory blocks ,
* since you need to be punching out the middle of an extent .
* In this case we need to leave the block in the file , and not
* binval it . So the block has to be in a consistent empty
* state and appropriately logged . We don ' t free up the buffer ,
* the caller can tell it hasn ' t happened since it got an error
* back .
2005-04-16 15:20:36 -07:00
*/
return error ;
}
ASSERT ( done ) ;
/*
* Invalidate the buffer from the transaction .
*/
2012-06-22 18:50:14 +10:00
xfs_trans_binval ( tp , bp ) ;
2005-04-16 15:20:36 -07:00
/*
* If it ' s not a data block , we ' re done .
*/
2014-06-06 15:08:18 +10:00
if ( db > = xfs_dir2_byte_to_db ( args - > geo , XFS_DIR2_LEAF_OFFSET ) )
2005-04-16 15:20:36 -07:00
return 0 ;
/*
* If the block isn ' t the last one in the directory , we ' re done .
*/
2021-03-29 11:11:40 -07:00
if ( dp - > i_disk_size > xfs_dir2_db_off_to_byte ( args - > geo , db + 1 , 0 ) )
2005-04-16 15:20:36 -07:00
return 0 ;
bno = da ;
if ( ( error = xfs_bmap_last_before ( tp , dp , & bno , XFS_DATA_FORK ) ) ) {
/*
* This can ' t really happen unless there ' s kernel corruption .
*/
return error ;
}
2014-06-06 15:11:18 +10:00
if ( db = = args - > geo - > datablk )
2005-04-16 15:20:36 -07:00
ASSERT ( bno = = 0 ) ;
else
ASSERT ( bno > 0 ) ;
/*
* Set the size to the new last block .
*/
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = XFS_FSB_TO_B ( mp , bno ) ;
2005-04-16 15:20:36 -07:00
xfs_trans_log_inode ( tp , dp , XFS_ILOG_CORE ) ;
return 0 ;
}
2019-02-01 09:08:54 -08:00
/* Returns true if the directory entry name is valid. */
bool
xfs_dir2_namecheck (
const void * name ,
size_t length )
{
/*
* MAXNAMELEN includes the trailing null , but ( name / length ) leave it
* out , so use > = for the length check .
*/
if ( length > = MAXNAMELEN )
return false ;
/* There shouldn't be any slashes or nulls here */
return ! memchr ( name , ' / ' , length ) & & ! memchr ( name , 0 , length ) ;
}
2019-12-16 11:14:09 -08:00
xfs_dahash_t
xfs_dir2_hashname (
struct xfs_mount * mp ,
2022-03-09 10:16:09 -08:00
const struct xfs_name * name )
2019-12-16 11:14:09 -08:00
{
2021-08-18 18:46:55 -07:00
if ( unlikely ( xfs_has_asciici ( mp ) ) )
2019-12-16 11:14:09 -08:00
return xfs_ascii_ci_hashname ( name ) ;
return xfs_da_hashname ( name - > name , name - > len ) ;
}
enum xfs_dacmp
xfs_dir2_compname (
struct xfs_da_args * args ,
const unsigned char * name ,
int len )
{
2021-08-18 18:46:55 -07:00
if ( unlikely ( xfs_has_asciici ( args - > dp - > i_mount ) ) )
2019-12-16 11:14:09 -08:00
return xfs_ascii_ci_compname ( args , name , len ) ;
return xfs_da_compname ( args , name , len ) ;
}