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 - 2003 , 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"
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"
2009-12-14 23:14:59 +00:00
# include "xfs_trace.h"
2005-04-16 15:20:36 -07:00
/*
* Prototypes for internal functions .
*/
static void xfs_dir2_sf_addname_easy ( xfs_da_args_t * args ,
xfs_dir2_sf_entry_t * sfep ,
xfs_dir2_data_aoff_t offset ,
int new_isize ) ;
static void xfs_dir2_sf_addname_hard ( xfs_da_args_t * args , int objchange ,
int new_isize ) ;
static int xfs_dir2_sf_addname_pick ( xfs_da_args_t * args , int objchange ,
xfs_dir2_sf_entry_t * * sfepp ,
xfs_dir2_data_aoff_t * offsetp ) ;
# ifdef DEBUG
static void xfs_dir2_sf_check ( xfs_da_args_t * args ) ;
# else
# define xfs_dir2_sf_check(args)
# endif /* DEBUG */
2014-07-30 09:12:05 +10:00
2005-04-16 15:20:36 -07:00
static void xfs_dir2_sf_toino4 ( xfs_da_args_t * args ) ;
static void xfs_dir2_sf_toino8 ( xfs_da_args_t * args ) ;
2019-12-16 11:14:09 -08:00
int
2019-11-08 15:02:38 -08:00
xfs_dir2_sf_entsize (
struct xfs_mount * mp ,
struct xfs_dir2_sf_hdr * hdr ,
int len )
{
int count = len ;
count + = sizeof ( struct xfs_dir2_sf_entry ) ; /* namelen + offset */
count + = hdr - > i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE ; /* ino # */
2021-08-18 18:46:55 -07:00
if ( xfs_has_ftype ( mp ) )
2019-11-08 15:02:38 -08:00
count + = sizeof ( uint8_t ) ;
return count ;
}
struct xfs_dir2_sf_entry *
xfs_dir2_sf_nextentry (
struct xfs_mount * mp ,
struct xfs_dir2_sf_hdr * hdr ,
struct xfs_dir2_sf_entry * sfep )
{
return ( void * ) sfep + xfs_dir2_sf_entsize ( mp , hdr , sfep - > namelen ) ;
}
2019-11-08 15:02:59 -08:00
/*
* In short - form directory entries the inode numbers are stored at variable
* offset behind the entry name . If the entry stores a filetype value , then it
* sits between the name and the inode number . The actual inode numbers can
* come in two formats as well , either 4 bytes or 8 bytes wide .
*/
xfs_ino_t
xfs_dir2_sf_get_ino (
struct xfs_mount * mp ,
struct xfs_dir2_sf_hdr * hdr ,
struct xfs_dir2_sf_entry * sfep )
{
uint8_t * from = sfep - > name + sfep - > namelen ;
2021-08-18 18:46:55 -07:00
if ( xfs_has_ftype ( mp ) )
2019-11-08 15:02:59 -08:00
from + + ;
if ( ! hdr - > i8count )
return get_unaligned_be32 ( from ) ;
return get_unaligned_be64 ( from ) & XFS_MAXINUMBER ;
}
2019-12-16 11:14:09 -08:00
void
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino (
struct xfs_mount * mp ,
struct xfs_dir2_sf_hdr * hdr ,
struct xfs_dir2_sf_entry * sfep ,
xfs_ino_t ino )
{
uint8_t * to = sfep - > name + sfep - > namelen ;
ASSERT ( ino < = XFS_MAXINUMBER ) ;
2021-08-18 18:46:55 -07:00
if ( xfs_has_ftype ( mp ) )
2019-11-08 15:02:59 -08:00
to + + ;
if ( hdr - > i8count )
put_unaligned_be64 ( ino , to ) ;
else
put_unaligned_be32 ( ino , to ) ;
}
xfs_ino_t
xfs_dir2_sf_get_parent_ino (
struct xfs_dir2_sf_hdr * hdr )
{
if ( ! hdr - > i8count )
return get_unaligned_be32 ( hdr - > parent ) ;
return get_unaligned_be64 ( hdr - > parent ) & XFS_MAXINUMBER ;
}
void
xfs_dir2_sf_put_parent_ino (
struct xfs_dir2_sf_hdr * hdr ,
xfs_ino_t ino )
{
ASSERT ( ino < = XFS_MAXINUMBER ) ;
if ( hdr - > i8count )
put_unaligned_be64 ( ino , hdr - > parent ) ;
else
put_unaligned_be32 ( ino , hdr - > parent ) ;
}
2019-11-08 15:03:30 -08:00
/*
* The file type field is stored at the end of the name for filetype enabled
* shortform directories , or not at all otherwise .
*/
uint8_t
xfs_dir2_sf_get_ftype (
struct xfs_mount * mp ,
struct xfs_dir2_sf_entry * sfep )
{
2021-08-18 18:46:55 -07:00
if ( xfs_has_ftype ( mp ) ) {
2019-11-08 15:03:30 -08:00
uint8_t ftype = sfep - > name [ sfep - > namelen ] ;
if ( ftype < XFS_DIR3_FT_MAX )
return ftype ;
}
return XFS_DIR3_FT_UNKNOWN ;
}
2019-12-16 11:14:09 -08:00
void
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype (
struct xfs_mount * mp ,
struct xfs_dir2_sf_entry * sfep ,
uint8_t ftype )
{
ASSERT ( ftype < XFS_DIR3_FT_MAX ) ;
2021-08-18 18:46:55 -07:00
if ( xfs_has_ftype ( mp ) )
2019-11-08 15:03:30 -08:00
sfep - > name [ sfep - > namelen ] = ftype ;
}
2005-04-16 15:20:36 -07:00
/*
* Given a block directory ( dp / block ) , calculate its size as a shortform ( sf )
* directory and a header for the sf directory , if it will fit it the
* space currently present in the inode . If it won ' t fit , the output
* size is too big ( but not accurate ) .
*/
int /* size for sf form */
xfs_dir2_block_sfsize (
xfs_inode_t * dp , /* incore inode pointer */
2011-07-08 14:35:27 +02:00
xfs_dir2_data_hdr_t * hdr , /* block directory data */
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_hdr_t * sfhp ) /* output: header for sf form */
{
xfs_dir2_dataptr_t addr ; /* data entry address */
xfs_dir2_leaf_entry_t * blp ; /* leaf area of the block */
xfs_dir2_block_tail_t * btp ; /* tail area of the block */
int count ; /* shortform entry count */
xfs_dir2_data_entry_t * dep ; /* data entry in the block */
int i ; /* block entry index */
int i8count ; /* count of big-inode entries */
int isdot ; /* entry is "." */
int isdotdot ; /* entry is ".." */
xfs_mount_t * mp ; /* mount structure pointer */
int namelen ; /* total name bytes */
2005-11-02 15:06:18 +11:00
xfs_ino_t parent = 0 ; /* parent inode number */
2005-04-16 15:20:36 -07:00
int size = 0 ; /* total computed size */
2013-08-12 20:50:09 +10:00
int has_ftype ;
2014-06-06 15:15:59 +10:00
struct xfs_da_geometry * geo ;
2005-04-16 15:20:36 -07:00
mp = dp - > i_mount ;
2014-06-06 15:15:59 +10:00
geo = mp - > m_dir_geo ;
2005-04-16 15:20:36 -07:00
2013-08-12 20:50:09 +10:00
/*
* if there is a filetype field , add the extra byte to the namelen
* for each entry that we see .
*/
2021-08-18 18:46:37 -07:00
has_ftype = xfs_has_ftype ( mp ) ? 1 : 0 ;
2013-08-12 20:50:09 +10:00
2005-04-16 15:20:36 -07:00
count = i8count = namelen = 0 ;
2014-06-06 15:15:59 +10:00
btp = xfs_dir2_block_tail_p ( geo , hdr ) ;
2007-06-28 16:43:50 +10:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-16 15:20:36 -07:00
/*
* Iterate over the block ' s data entries by using the leaf pointers .
*/
2006-03-17 17:27:56 +11:00
for ( i = 0 ; i < be32_to_cpu ( btp - > count ) ; i + + ) {
2006-03-17 17:28:18 +11:00
if ( ( addr = be32_to_cpu ( blp [ i ] . address ) ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-16 15:20:36 -07:00
continue ;
/*
* Calculate the pointer to the entry at hand .
*/
2014-06-06 15:08:18 +10:00
dep = ( xfs_dir2_data_entry_t * ) ( ( char * ) hdr +
2014-06-06 15:15:59 +10:00
xfs_dir2_dataptr_to_off ( geo , addr ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Detect . and . . , so we can special - case them .
* . is not included in sf directories .
* . . is included by just the parent inode number .
*/
isdot = dep - > namelen = = 1 & & dep - > name [ 0 ] = = ' . ' ;
isdotdot =
dep - > namelen = = 2 & &
dep - > name [ 0 ] = = ' . ' & & dep - > name [ 1 ] = = ' . ' ;
2014-07-30 09:12:05 +10:00
2005-04-16 15:20:36 -07:00
if ( ! isdot )
2006-06-09 14:48:37 +10:00
i8count + = be64_to_cpu ( dep - > inumber ) > XFS_DIR2_MAX_SHORT_INUM ;
2014-07-30 09:12:05 +10:00
2013-08-12 20:50:09 +10:00
/* take into account the file type field */
2005-04-16 15:20:36 -07:00
if ( ! isdot & & ! isdotdot ) {
count + + ;
2013-08-12 20:50:09 +10:00
namelen + = dep - > namelen + has_ftype ;
2005-04-16 15:20:36 -07:00
} else if ( isdotdot )
2006-06-09 14:48:37 +10:00
parent = be64_to_cpu ( dep - > inumber ) ;
2005-04-16 15:20:36 -07:00
/*
* Calculate the new size , see if we should give up yet .
*/
2016-07-20 11:47:21 +10:00
size = xfs_dir2_sf_hdr_size ( i8count ) + /* header */
count * 3 * sizeof ( u8 ) + /* namelen + offset */
namelen + /* name */
( i8count ? /* inumber */
2016-07-20 11:48:31 +10:00
count * XFS_INO64_SIZE :
count * XFS_INO32_SIZE ) ;
2022-07-09 10:56:07 -07:00
if ( size > xfs_inode_data_fork_size ( dp ) )
2005-04-16 15:20:36 -07:00
return size ; /* size value is a failure */
}
/*
* Create the output header , if it worked .
*/
sfhp - > count = count ;
sfhp - > i8count = i8count ;
2019-11-08 15:02:31 -08:00
xfs_dir2_sf_put_parent_ino ( sfhp , parent ) ;
2005-04-16 15:20:36 -07:00
return size ;
}
/*
* Convert a block format directory to shortform .
* Caller has already checked that it will fit , and built us a header .
*/
int /* error */
xfs_dir2_block_to_sf (
2019-11-08 15:05:34 -08:00
struct xfs_da_args * args , /* operation arguments */
2012-06-22 18:50:14 +10:00
struct xfs_buf * bp ,
2005-04-16 15:20:36 -07:00
int size , /* shortform directory size */
2019-11-08 15:05:34 -08:00
struct xfs_dir2_sf_hdr * sfhp ) /* shortform directory hdr */
2005-04-16 15:20:36 -07:00
{
2019-11-08 15:05:34 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2005-04-16 15:20:36 -07:00
int error ; /* error return value */
int logflags ; /* inode logging flags */
2019-11-08 15:05:34 -08:00
struct xfs_dir2_sf_entry * sfep ; /* shortform entry */
struct xfs_dir2_sf_hdr * sfp ; /* shortform directory header */
2019-11-08 15:05:38 -08:00
unsigned int offset = args - > geo - > data_entry_offset ;
2019-11-08 15:05:34 -08:00
unsigned int end ;
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_block_to_sf ( args ) ;
2005-04-16 15:20:36 -07:00
/*
2019-11-08 15:05:34 -08:00
* Allocate a temporary destination buffer the size of the inode to
* format the data into . Once we have formatted the data , we can free
* the block and copy the formatted data into the inode literal area .
2005-04-16 15:20:36 -07:00
*/
2024-01-16 09:59:40 +11:00
sfp = kmalloc ( mp - > m_sb . sb_inodesize , GFP_KERNEL | __GFP_NOFAIL ) ;
2007-06-28 16:43:50 +10:00
memcpy ( sfp , sfhp , xfs_dir2_sf_hdr_size ( sfhp - > i8count ) ) ;
2013-12-03 23:50:57 +11:00
2005-04-16 15:20:36 -07:00
/*
2019-11-08 15:05:34 -08:00
* Loop over the active and unused entries . Stop when we reach the
* leaf / tail portion of the block .
2005-04-16 15:20:36 -07:00
*/
2019-11-08 15:05:36 -08:00
end = xfs_dir3_data_end_offset ( args - > geo , bp - > b_addr ) ;
2007-06-28 16:43:50 +10:00
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2019-11-08 15:05:34 -08:00
while ( offset < end ) {
struct xfs_dir2_data_unused * dup = bp - > b_addr + offset ;
struct xfs_dir2_data_entry * dep = bp - > b_addr + offset ;
2005-04-16 15:20:36 -07:00
/*
* If it ' s unused , just skip over it .
*/
2006-03-17 17:27:37 +11:00
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
2019-11-08 15:05:34 -08:00
offset + = be16_to_cpu ( dup - > length ) ;
2005-04-16 15:20:36 -07:00
continue ;
}
2019-11-08 15:05:34 -08:00
2005-04-16 15:20:36 -07:00
/*
* Skip .
*/
if ( dep - > namelen = = 1 & & dep - > name [ 0 ] = = ' . ' )
2006-06-09 14:48:37 +10:00
ASSERT ( be64_to_cpu ( dep - > inumber ) = = dp - > i_ino ) ;
2005-04-16 15:20:36 -07:00
/*
* Skip . . , but make sure the inode number is right .
*/
else if ( dep - > namelen = = 2 & &
dep - > name [ 0 ] = = ' . ' & & dep - > name [ 1 ] = = ' . ' )
2006-06-09 14:48:37 +10:00
ASSERT ( be64_to_cpu ( dep - > inumber ) = =
2019-11-08 15:02:31 -08:00
xfs_dir2_sf_get_parent_ino ( sfp ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Normal entry , copy it into shortform .
*/
else {
sfep - > namelen = dep - > namelen ;
2019-11-08 15:05:34 -08:00
xfs_dir2_sf_put_offset ( sfep , offset ) ;
2005-04-16 15:20:36 -07:00
memcpy ( sfep - > name , dep - > name , dep - > namelen ) ;
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino ( mp , sfp , sfep ,
2013-10-29 22:11:47 +11:00
be64_to_cpu ( dep - > inumber ) ) ;
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype ( mp , sfep ,
2019-11-08 15:05:48 -08:00
xfs_dir2_data_get_ftype ( mp , dep ) ) ;
2011-07-08 14:35:03 +02:00
2019-11-08 15:02:38 -08:00
sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ;
2005-04-16 15:20:36 -07:00
}
2019-11-08 15:05:37 -08:00
offset + = xfs_dir2_data_entsize ( mp , dep - > namelen ) ;
2005-04-16 15:20:36 -07:00
}
ASSERT ( ( char * ) sfep - ( char * ) sfp = = size ) ;
2013-12-03 23:50:57 +11:00
/* now we are done with the block, we can shrink the inode */
logflags = XFS_ILOG_CORE ;
2014-06-06 15:11:18 +10:00
error = xfs_dir2_shrink_inode ( args , args - > geo - > datablk , bp ) ;
2013-12-03 23:50:57 +11:00
if ( error ) {
2014-06-25 14:58:08 +10:00
ASSERT ( error ! = - ENOSPC ) ;
2013-12-03 23:50:57 +11:00
goto out ;
}
/*
* The buffer is now unconditionally gone , whether
* xfs_dir2_shrink_inode worked or not .
*
* Convert the inode to local format and copy the data in .
*/
ASSERT ( dp - > i_df . if_bytes = = 0 ) ;
2019-11-08 15:05:34 -08:00
xfs_init_local_fork ( dp , XFS_DATA_FORK , sfp , size ) ;
2020-05-18 10:28:05 -07:00
dp - > i_df . if_format = XFS_DINODE_FMT_LOCAL ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = size ;
2013-12-03 23:50:57 +11:00
logflags | = XFS_ILOG_DDATA ;
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_check ( args ) ;
out :
xfs_trans_log_inode ( args - > trans , dp , logflags ) ;
2024-01-16 09:59:43 +11:00
kfree ( sfp ) ;
2005-04-16 15:20:36 -07:00
return error ;
}
/*
* Add a name to a shortform directory .
* There are two algorithms , " easy " and " hard " which we decide on
* before changing anything .
* Convert to block form if necessary , if the new entry won ' t fit .
*/
int /* error */
xfs_dir2_sf_addname (
xfs_da_args_t * args ) /* operation arguments */
{
2023-12-20 07:34:55 +01:00
struct xfs_inode * dp = args - > dp ;
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
int error ; /* error return value */
int incr_isize ; /* total change in size */
2021-03-29 11:11:40 -07:00
int new_isize ; /* size after adding name */
2005-04-16 15:20:36 -07:00
int objchange ; /* changing to 8-byte inodes */
2005-11-02 15:06:18 +11:00
xfs_dir2_data_aoff_t offset = 0 ; /* offset for new entry */
2005-04-16 15:20:36 -07:00
int pick ; /* which algorithm to use */
2005-11-02 15:06:18 +11:00
xfs_dir2_sf_entry_t * sfep = NULL ; /* shortform entry */
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_addname ( args ) ;
2014-06-25 14:58:08 +10:00
ASSERT ( xfs_dir2_sf_lookup ( args ) = = - ENOENT ) ;
2021-04-13 11:15:11 -07:00
ASSERT ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size > = offsetof ( struct xfs_dir2_sf_hdr , parent ) ) ;
ASSERT ( dp - > i_df . if_bytes = = dp - > i_disk_size ) ;
2023-12-20 07:34:55 +01:00
ASSERT ( sfp ! = NULL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Compute entry ( and change in ) size .
*/
2019-11-08 15:02:38 -08:00
incr_isize = xfs_dir2_sf_entsize ( dp - > i_mount , sfp , args - > namelen ) ;
2005-04-16 15:20:36 -07:00
objchange = 0 ;
2014-07-30 09:12:05 +10:00
2005-04-16 15:20:36 -07:00
/*
* Do we have to change to 8 byte inodes ?
*/
2011-07-08 14:35:13 +02:00
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM & & sfp - > i8count = = 0 ) {
2005-04-16 15:20:36 -07:00
/*
2014-04-14 19:07:23 +10:00
* Yes , adjust the inode size . old count + ( parent + new )
2005-04-16 15:20:36 -07:00
*/
2016-07-20 11:48:31 +10:00
incr_isize + = ( sfp - > count + 2 ) * XFS_INO64_DIFF ;
2005-04-16 15:20:36 -07:00
objchange = 1 ;
}
2014-07-30 09:12:05 +10:00
2021-03-29 11:11:40 -07:00
new_isize = ( int ) dp - > i_disk_size + incr_isize ;
2005-04-16 15:20:36 -07:00
/*
* Won ' t fit as shortform any more ( due to size ) ,
* or the pick routine says it won ' t ( due to offset values ) .
*/
2022-07-09 10:56:07 -07:00
if ( new_isize > xfs_inode_data_fork_size ( dp ) | |
2005-04-16 15:20:36 -07:00
( pick =
xfs_dir2_sf_addname_pick ( args , objchange , & sfep , & offset ) ) = = 0 ) {
/*
* Just checking or no space reservation , it doesn ' t fit .
*/
2008-05-21 16:42:05 +10:00
if ( ( args - > op_flags & XFS_DA_OP_JUSTCHECK ) | | args - > total = = 0 )
2014-06-25 14:58:08 +10:00
return - ENOSPC ;
2005-04-16 15:20:36 -07:00
/*
* Convert to block form then add the name .
*/
error = xfs_dir2_sf_to_block ( args ) ;
if ( error )
return error ;
return xfs_dir2_block_addname ( args ) ;
}
/*
* Just checking , it fits .
*/
2008-05-21 16:42:05 +10:00
if ( args - > op_flags & XFS_DA_OP_JUSTCHECK )
2005-04-16 15:20:36 -07:00
return 0 ;
/*
* Do it the easy way - just add it at the end .
*/
if ( pick = = 1 )
xfs_dir2_sf_addname_easy ( args , sfep , offset , new_isize ) ;
/*
* Do it the hard way - look for a place to insert the new entry .
* Convert to 8 byte inode numbers first if necessary .
*/
else {
ASSERT ( pick = = 2 ) ;
if ( objchange )
xfs_dir2_sf_toino8 ( args ) ;
xfs_dir2_sf_addname_hard ( args , objchange , new_isize ) ;
}
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
return 0 ;
}
/*
* Add the new entry the " easy " way .
* This is copying the old directory and adding the new entry at the end .
* Since it ' s sorted by " offset " we need room after the last offset
* that ' s already there , and then room to convert to a block directory .
* This is already checked by the pick routine .
*/
static void
xfs_dir2_sf_addname_easy (
xfs_da_args_t * args , /* operation arguments */
xfs_dir2_sf_entry_t * sfep , /* pointer to new entry */
xfs_dir2_data_aoff_t offset , /* offset to use for new ent */
int new_isize ) /* new directory size */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
int byteoff = ( int ) ( ( char * ) sfep - ( char * ) sfp ) ;
2005-04-16 15:20:36 -07:00
/*
* Grow the in - inode space .
*/
2023-12-20 07:34:56 +01:00
sfp = xfs_idata_realloc ( dp , xfs_dir2_sf_entsize ( mp , sfp , args - > namelen ) ,
2013-08-12 20:50:09 +10:00
XFS_DATA_FORK ) ;
2005-04-16 15:20:36 -07:00
/*
* Need to set up again due to realloc of the inode data .
*/
sfep = ( xfs_dir2_sf_entry_t * ) ( ( char * ) sfp + byteoff ) ;
/*
* Fill in the new entry .
*/
sfep - > namelen = args - > namelen ;
2007-06-28 16:43:50 +10:00
xfs_dir2_sf_put_offset ( sfep , offset ) ;
2005-04-16 15:20:36 -07:00
memcpy ( sfep - > name , args - > name , sfep - > namelen ) ;
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino ( mp , sfp , sfep , args - > inumber ) ;
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype ( mp , sfep , args - > filetype ) ;
2013-08-12 20:50:10 +10:00
2005-04-16 15:20:36 -07:00
/*
* Update the header and inode .
*/
2011-07-08 14:35:13 +02:00
sfp - > count + + ;
2005-04-16 15:20:36 -07:00
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM )
2011-07-08 14:35:13 +02:00
sfp - > i8count + + ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = new_isize ;
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_check ( args ) ;
}
/*
* Add the new entry the " hard " way .
* The caller has already converted to 8 byte inode numbers if necessary ,
* in which case we need to leave the i8count at 1.
* Find a hole that the new entry will fit into , and copy
* the first part of the entries , the new entry , and the last part of
* the entries .
*/
/* ARGSUSED */
static void
xfs_dir2_sf_addname_hard (
xfs_da_args_t * args , /* operation arguments */
int objchange , /* changing inode number size */
int new_isize ) /* new directory size */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2005-04-16 15:20:36 -07:00
int add_datasize ; /* data size need for new ent */
char * buf ; /* buffer for old */
int eof ; /* reached end of old dir */
int nbytes ; /* temp for byte copies */
xfs_dir2_data_aoff_t new_offset ; /* next offset value */
xfs_dir2_data_aoff_t offset ; /* current offset value */
2021-03-29 11:11:40 -07:00
int old_isize ; /* previous size */
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_entry_t * oldsfep ; /* entry in original dir */
2011-07-08 14:35:13 +02:00
xfs_dir2_sf_hdr_t * oldsfp ; /* original shortform dir */
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_entry_t * sfep ; /* entry in new dir */
2011-07-08 14:35:13 +02:00
xfs_dir2_sf_hdr_t * sfp ; /* new shortform dir */
2005-04-16 15:20:36 -07:00
/*
* Copy the old directory to the stack buffer .
*/
2021-03-29 11:11:40 -07:00
old_isize = ( int ) dp - > i_disk_size ;
2024-01-16 09:59:40 +11:00
buf = kmalloc ( old_isize , GFP_KERNEL | __GFP_NOFAIL ) ;
2011-07-08 14:35:13 +02:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) buf ;
2023-12-20 07:34:55 +01:00
memcpy ( oldsfp , dp - > i_df . if_data , old_isize ) ;
2005-04-16 15:20:36 -07:00
/*
* Loop over the old directory finding the place we ' re going
* to insert the new entry .
* If it ' s going to end up at the end then oldsfep will point there .
*/
2019-11-08 15:05:38 -08:00
for ( offset = args - > geo - > data_first_offset ,
2007-06-28 16:43:50 +10:00
oldsfep = xfs_dir2_sf_firstentry ( oldsfp ) ,
2019-11-08 15:05:37 -08:00
add_datasize = xfs_dir2_data_entsize ( mp , args - > namelen ) ,
2005-04-16 15:20:36 -07:00
eof = ( char * ) oldsfep = = & buf [ old_isize ] ;
! eof ;
2019-11-08 15:05:37 -08:00
offset = new_offset + xfs_dir2_data_entsize ( mp , oldsfep - > namelen ) ,
2019-11-08 15:02:38 -08:00
oldsfep = xfs_dir2_sf_nextentry ( mp , oldsfp , oldsfep ) ,
2005-04-16 15:20:36 -07:00
eof = ( char * ) oldsfep = = & buf [ old_isize ] ) {
2007-06-28 16:43:50 +10:00
new_offset = xfs_dir2_sf_get_offset ( oldsfep ) ;
2005-04-16 15:20:36 -07:00
if ( offset + add_datasize < = new_offset )
break ;
}
/*
* Get rid of the old directory , then allocate space for
* the new one . We do this so xfs_idata_realloc won ' t copy
* the data .
*/
xfs_idata_realloc ( dp , - old_isize , XFS_DATA_FORK ) ;
2023-12-20 07:34:56 +01:00
sfp = xfs_idata_realloc ( dp , new_isize , XFS_DATA_FORK ) ;
2005-04-16 15:20:36 -07:00
/*
* Copy the first part of the directory , including the header .
*/
nbytes = ( int ) ( ( char * ) oldsfep - ( char * ) oldsfp ) ;
memcpy ( sfp , oldsfp , nbytes ) ;
sfep = ( xfs_dir2_sf_entry_t * ) ( ( char * ) sfp + nbytes ) ;
/*
* Fill in the new entry , and update the header counts .
*/
sfep - > namelen = args - > namelen ;
2007-06-28 16:43:50 +10:00
xfs_dir2_sf_put_offset ( sfep , offset ) ;
2005-04-16 15:20:36 -07:00
memcpy ( sfep - > name , args - > name , sfep - > namelen ) ;
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino ( mp , sfp , sfep , args - > inumber ) ;
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype ( mp , sfep , args - > filetype ) ;
2011-07-08 14:35:13 +02:00
sfp - > count + + ;
2005-04-16 15:20:36 -07:00
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM & & ! objchange )
2011-07-08 14:35:13 +02:00
sfp - > i8count + + ;
2005-04-16 15:20:36 -07:00
/*
* If there ' s more left to copy , do that .
*/
if ( ! eof ) {
2019-11-08 15:02:38 -08:00
sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ;
2005-04-16 15:20:36 -07:00
memcpy ( sfep , oldsfep , old_isize - nbytes ) ;
}
2024-01-16 09:59:43 +11:00
kfree ( buf ) ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = new_isize ;
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_check ( args ) ;
}
/*
* Decide if the new entry will fit at all .
* If it will fit , pick between adding the new entry to the end ( easy )
* or somewhere else ( hard ) .
* Return 0 ( won ' t fit ) , 1 ( easy ) , 2 ( hard ) .
*/
/*ARGSUSED*/
static int /* pick result */
xfs_dir2_sf_addname_pick (
xfs_da_args_t * args , /* operation arguments */
int objchange , /* inode # size changes */
xfs_dir2_sf_entry_t * * sfepp , /* out(1): new entry ptr */
xfs_dir2_data_aoff_t * offsetp ) /* out(1): new offset */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2005-04-16 15:20:36 -07:00
int holefit ; /* found hole it will fit in */
int i ; /* entry number */
xfs_dir2_data_aoff_t offset ; /* data block offset */
xfs_dir2_sf_entry_t * sfep ; /* shortform entry */
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
int size ; /* entry's data size */
int used ; /* data bytes used */
2019-11-08 15:05:37 -08:00
size = xfs_dir2_data_entsize ( mp , args - > namelen ) ;
2019-11-08 15:05:38 -08:00
offset = args - > geo - > data_first_offset ;
2007-06-28 16:43:50 +10:00
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2005-04-16 15:20:36 -07:00
holefit = 0 ;
/*
* Loop over sf entries .
* Keep track of data offset and whether we ' ve seen a place
* to insert the new entry .
*/
2011-07-08 14:35:13 +02:00
for ( i = 0 ; i < sfp - > count ; i + + ) {
2005-04-16 15:20:36 -07:00
if ( ! holefit )
2007-06-28 16:43:50 +10:00
holefit = offset + size < = xfs_dir2_sf_get_offset ( sfep ) ;
offset = xfs_dir2_sf_get_offset ( sfep ) +
2019-11-08 15:05:37 -08:00
xfs_dir2_data_entsize ( mp , sfep - > namelen ) ;
2019-11-08 15:02:38 -08:00
sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Calculate data bytes used excluding the new entry , if this
* was a data block ( block form directory ) .
*/
used = offset +
2011-07-08 14:35:13 +02:00
( sfp - > count + 3 ) * ( uint ) sizeof ( xfs_dir2_leaf_entry_t ) +
2005-04-16 15:20:36 -07:00
( uint ) sizeof ( xfs_dir2_block_tail_t ) ;
/*
* If it won ' t fit in a block form then we can ' t insert it ,
* we ' ll go back , convert to block , then try the insert and convert
* to leaf .
*/
2014-06-06 15:15:59 +10:00
if ( used + ( holefit ? 0 : size ) > args - > geo - > blksize )
2005-04-16 15:20:36 -07:00
return 0 ;
/*
* If changing the inode number size , do it the hard way .
*/
2014-07-30 09:12:05 +10:00
if ( objchange )
2005-04-16 15:20:36 -07:00
return 2 ;
/*
* If it won ' t fit at the end then do it the hard way ( use the hole ) .
*/
2014-06-06 15:15:59 +10:00
if ( used + size > args - > geo - > blksize )
2005-04-16 15:20:36 -07:00
return 2 ;
/*
* Do it the easy way .
*/
* sfepp = sfep ;
* offsetp = offset ;
return 1 ;
}
# ifdef DEBUG
/*
* Check consistency of shortform directory , assert if bad .
*/
static void
xfs_dir2_sf_check (
xfs_da_args_t * args ) /* operation arguments */
{
2019-11-08 15:03:30 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
int i ; /* entry number */
int i8count ; /* number of big inode#s */
xfs_ino_t ino ; /* entry inode number */
int offset ; /* data offset */
xfs_dir2_sf_entry_t * sfep ; /* shortform dir entry */
2019-11-08 15:05:38 -08:00
offset = args - > geo - > data_first_offset ;
2019-11-08 15:02:31 -08:00
ino = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2005-04-16 15:20:36 -07:00
i8count = ino > XFS_DIR2_MAX_SHORT_INUM ;
2007-06-28 16:43:50 +10:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2011-07-08 14:35:13 +02:00
i < sfp - > count ;
2019-11-08 15:03:30 -08:00
i + + , sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ) {
2007-06-28 16:43:50 +10:00
ASSERT ( xfs_dir2_sf_get_offset ( sfep ) > = offset ) ;
2019-11-08 15:03:30 -08:00
ino = xfs_dir2_sf_get_ino ( mp , sfp , sfep ) ;
2005-04-16 15:20:36 -07:00
i8count + = ino > XFS_DIR2_MAX_SHORT_INUM ;
offset =
2007-06-28 16:43:50 +10:00
xfs_dir2_sf_get_offset ( sfep ) +
2019-11-08 15:05:37 -08:00
xfs_dir2_data_entsize ( mp , sfep - > namelen ) ;
2019-11-08 15:03:30 -08:00
ASSERT ( xfs_dir2_sf_get_ftype ( mp , sfep ) < XFS_DIR3_FT_MAX ) ;
2005-04-16 15:20:36 -07:00
}
2011-07-08 14:35:13 +02:00
ASSERT ( i8count = = sfp - > i8count ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( ( char * ) sfep - ( char * ) sfp = = dp - > i_disk_size ) ;
2005-04-16 15:20:36 -07:00
ASSERT ( offset +
2011-07-08 14:35:13 +02:00
( sfp - > count + 2 ) * ( uint ) sizeof ( xfs_dir2_leaf_entry_t ) +
2014-06-06 15:15:59 +10:00
( uint ) sizeof ( xfs_dir2_block_tail_t ) < = args - > geo - > blksize ) ;
2005-04-16 15:20:36 -07:00
}
# endif /* DEBUG */
2017-03-15 00:24:25 -07:00
/* Verify the consistency of an inline directory. */
2018-01-08 10:51:06 -08:00
xfs_failaddr_t
2017-03-15 00:24:25 -07:00
xfs_dir2_sf_verify (
2023-12-15 10:03:37 -08:00
struct xfs_mount * mp ,
struct xfs_dir2_sf_hdr * sfp ,
int64_t size )
2017-03-15 00:24:25 -07:00
{
struct xfs_dir2_sf_entry * sfep ;
struct xfs_dir2_sf_entry * next_sfep ;
char * endp ;
xfs_ino_t ino ;
int i ;
int i8count ;
int offset ;
2017-04-03 12:22:20 -07:00
int error ;
2017-06-16 11:00:05 -07:00
uint8_t filetype ;
2017-03-15 00:24:25 -07:00
/*
* Give up if the directory is way too short .
*/
2017-04-03 12:22:20 -07:00
if ( size < = offsetof ( struct xfs_dir2_sf_hdr , parent ) | |
size < xfs_dir2_sf_hdr_size ( sfp - > i8count ) )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
endp = ( char * ) sfp + size ;
/* Check .. entry */
2019-11-08 15:02:31 -08:00
ino = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2017-03-15 00:24:25 -07:00
i8count = ino > XFS_DIR2_MAX_SHORT_INUM ;
2017-04-03 12:22:20 -07:00
error = xfs_dir_ino_validate ( mp , ino ) ;
if ( error )
2018-01-08 10:51:06 -08:00
return __this_address ;
2019-11-08 15:05:38 -08:00
offset = mp - > m_dir_geo - > data_first_offset ;
2017-03-15 00:24:25 -07:00
/* Check all reported entries */
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
for ( i = 0 ; i < sfp - > count ; i + + ) {
/*
* struct xfs_dir2_sf_entry has a variable length .
* Check the fixed - offset parts of the structure are
* within the data buffer .
*/
2017-04-03 12:22:20 -07:00
if ( ( ( char * ) sfep + sizeof ( * sfep ) ) > = endp )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
/* Don't allow names with known bad length. */
2017-04-03 12:22:20 -07:00
if ( sfep - > namelen = = 0 )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
/*
* Check that the variable - length part of the structure is
* within the data buffer . The next entry starts after the
* name component , so nextentry is an acceptable test .
*/
2019-11-08 15:02:38 -08:00
next_sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ;
2017-04-03 12:22:20 -07:00
if ( endp < ( char * ) next_sfep )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
/* Check that the offsets always increase. */
2017-04-03 12:22:20 -07:00
if ( xfs_dir2_sf_get_offset ( sfep ) < offset )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
/* Check the inode number. */
2019-11-08 15:02:59 -08:00
ino = xfs_dir2_sf_get_ino ( mp , sfp , sfep ) ;
2017-03-15 00:24:25 -07:00
i8count + = ino > XFS_DIR2_MAX_SHORT_INUM ;
2017-04-03 12:22:20 -07:00
error = xfs_dir_ino_validate ( mp , ino ) ;
if ( error )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
/* Check the file type. */
2019-11-08 15:03:30 -08:00
filetype = xfs_dir2_sf_get_ftype ( mp , sfep ) ;
2017-04-03 12:22:20 -07:00
if ( filetype > = XFS_DIR3_FT_MAX )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
offset = xfs_dir2_sf_get_offset ( sfep ) +
2019-11-08 15:05:37 -08:00
xfs_dir2_data_entsize ( mp , sfep - > namelen ) ;
2017-03-15 00:24:25 -07:00
sfep = next_sfep ;
}
2017-04-03 12:22:20 -07:00
if ( i8count ! = sfp - > i8count )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-04-03 12:22:20 -07:00
if ( ( void * ) sfep ! = ( void * ) endp )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
/* Make sure this whole thing ought to be in local format. */
2017-04-03 12:22:20 -07:00
if ( offset + ( sfp - > count + 2 ) * ( uint ) sizeof ( xfs_dir2_leaf_entry_t ) +
( uint ) sizeof ( xfs_dir2_block_tail_t ) > mp - > m_dir_geo - > blksize )
2018-01-08 10:51:06 -08:00
return __this_address ;
2017-03-15 00:24:25 -07:00
2018-01-08 10:51:06 -08:00
return NULL ;
2017-03-15 00:24:25 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* Create a new ( shortform ) directory .
*/
int /* error, always 0 */
xfs_dir2_sf_create (
xfs_da_args_t * args , /* operation arguments */
xfs_ino_t pino ) /* parent inode number */
{
xfs_inode_t * dp ; /* incore directory inode */
int i8count ; /* parent inode is an 8-byte number */
2011-07-08 14:35:13 +02:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-04-16 15:20:36 -07:00
int size ; /* directory size */
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_create ( args ) ;
2005-04-16 15:20:36 -07:00
dp = args - > dp ;
ASSERT ( dp ! = NULL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size = = 0 ) ;
2005-04-16 15:20:36 -07:00
/*
* If it ' s currently a zero - length extent file ,
* convert it to local format .
*/
2020-05-18 10:28:05 -07:00
if ( dp - > i_df . if_format = = XFS_DINODE_FMT_EXTENTS ) {
dp - > i_df . if_format = XFS_DINODE_FMT_LOCAL ;
2005-04-16 15:20:36 -07:00
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE ) ;
}
2021-04-13 11:15:11 -07:00
ASSERT ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL ) ;
2005-04-16 15:20:36 -07:00
ASSERT ( dp - > i_df . if_bytes = = 0 ) ;
i8count = pino > XFS_DIR2_MAX_SHORT_INUM ;
2007-06-28 16:43:50 +10:00
size = xfs_dir2_sf_hdr_size ( i8count ) ;
2023-12-20 07:34:56 +01:00
2005-04-16 15:20:36 -07:00
/*
2023-12-20 07:34:56 +01:00
* Make a buffer for the data and fill in the header .
2005-04-16 15:20:36 -07:00
*/
2023-12-20 07:34:56 +01:00
sfp = xfs_idata_realloc ( dp , size , XFS_DATA_FORK ) ;
2011-07-08 14:35:13 +02:00
sfp - > i8count = i8count ;
2023-12-20 07:34:56 +01:00
2005-04-16 15:20:36 -07:00
/*
* Now can put in the inode number , since i8count is set .
*/
2019-11-08 15:02:31 -08:00
xfs_dir2_sf_put_parent_ino ( sfp , pino ) ;
2011-07-08 14:35:13 +02:00
sfp - > count = 0 ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = size ;
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_check ( args ) ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
return 0 ;
}
/*
* Lookup an entry in a shortform directory .
* Returns EEXIST if found , ENOENT if not found .
*/
int /* error */
xfs_dir2_sf_lookup (
xfs_da_args_t * args ) /* operation arguments */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
int i ; /* entry index */
xfs_dir2_sf_entry_t * sfep ; /* shortform directory entry */
2008-05-21 16:41:01 +10:00
enum xfs_dacmp cmp ; /* comparison result */
2008-05-21 16:58:22 +10:00
xfs_dir2_sf_entry_t * ci_sfep ; /* case-insens. entry */
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_lookup ( args ) ;
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_check ( args ) ;
2021-04-13 11:15:11 -07:00
ASSERT ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size > = offsetof ( struct xfs_dir2_sf_hdr , parent ) ) ;
ASSERT ( dp - > i_df . if_bytes = = dp - > i_disk_size ) ;
2023-12-20 07:34:55 +01:00
ASSERT ( sfp ! = NULL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Special case for .
*/
if ( args - > namelen = = 1 & & args - > name [ 0 ] = = ' . ' ) {
args - > inumber = dp - > i_ino ;
2008-05-21 16:41:01 +10:00
args - > cmpresult = XFS_CMP_EXACT ;
2013-08-12 20:50:10 +10:00
args - > filetype = XFS_DIR3_FT_DIR ;
2014-06-25 14:58:08 +10:00
return - EEXIST ;
2005-04-16 15:20:36 -07:00
}
/*
* Special case for . .
*/
if ( args - > namelen = = 2 & &
args - > name [ 0 ] = = ' . ' & & args - > name [ 1 ] = = ' . ' ) {
2019-11-08 15:02:31 -08:00
args - > inumber = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2008-05-21 16:41:01 +10:00
args - > cmpresult = XFS_CMP_EXACT ;
2013-08-12 20:50:10 +10:00
args - > filetype = XFS_DIR3_FT_DIR ;
2014-06-25 14:58:08 +10:00
return - EEXIST ;
2005-04-16 15:20:36 -07:00
}
/*
* Loop over all the entries trying to match ours .
*/
2008-05-21 16:58:22 +10:00
ci_sfep = NULL ;
2011-07-08 14:35:13 +02:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ; i < sfp - > count ;
2019-11-08 15:02:38 -08:00
i + + , sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ) {
2008-05-21 16:41:01 +10:00
/*
* Compare name and if it ' s an exact match , return the inode
* number . If it ' s the first case - insensitive match , store the
* inode number and continue looking for an exact match .
*/
2019-11-11 12:59:26 -08:00
cmp = xfs_dir2_compname ( args , sfep - > name , sfep - > namelen ) ;
2008-05-21 16:41:01 +10:00
if ( cmp ! = XFS_CMP_DIFFERENT & & cmp ! = args - > cmpresult ) {
args - > cmpresult = cmp ;
2019-11-08 15:02:59 -08:00
args - > inumber = xfs_dir2_sf_get_ino ( mp , sfp , sfep ) ;
2019-11-08 15:03:30 -08:00
args - > filetype = xfs_dir2_sf_get_ftype ( mp , sfep ) ;
2008-05-21 16:41:01 +10:00
if ( cmp = = XFS_CMP_EXACT )
2014-06-25 14:58:08 +10:00
return - EEXIST ;
2008-05-21 16:58:22 +10:00
ci_sfep = sfep ;
2005-04-16 15:20:36 -07:00
}
}
2008-05-21 16:42:05 +10:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2008-05-21 16:41:01 +10:00
/*
* Here , we can only be doing a lookup ( not a rename or replace ) .
2014-06-25 14:58:08 +10:00
* If a case - insensitive match was not found , return - ENOENT .
2008-05-21 16:41:01 +10:00
*/
2008-05-21 16:58:22 +10:00
if ( ! ci_sfep )
2014-06-25 14:58:08 +10:00
return - ENOENT ;
2008-05-21 16:58:22 +10:00
/* otherwise process the CI match as required by the caller */
2022-09-19 06:52:14 +10:00
return xfs_dir_cilookup_result ( args , ci_sfep - > name , ci_sfep - > namelen ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Remove an entry from a shortform directory .
*/
int /* error */
xfs_dir2_sf_removename (
xfs_da_args_t * args )
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
int byteoff ; /* offset of removed entry */
int entsize ; /* this entry's size */
int i ; /* shortform entry index */
int newsize ; /* new inode size */
int oldsize ; /* old inode size */
xfs_dir2_sf_entry_t * sfep ; /* shortform directory entry */
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_removename ( args ) ;
2021-04-13 11:15:11 -07:00
ASSERT ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL ) ;
2021-03-29 11:11:40 -07:00
oldsize = ( int ) dp - > i_disk_size ;
2019-11-02 09:38:08 -07:00
ASSERT ( oldsize > = offsetof ( struct xfs_dir2_sf_hdr , parent ) ) ;
2005-04-16 15:20:36 -07:00
ASSERT ( dp - > i_df . if_bytes = = oldsize ) ;
2023-12-20 07:34:55 +01:00
ASSERT ( sfp ! = NULL ) ;
2011-07-08 14:35:13 +02:00
ASSERT ( oldsize > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Loop over the old directory entries .
* Find the one we ' re deleting .
*/
2011-07-08 14:35:13 +02:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ; i < sfp - > count ;
2019-11-08 15:02:38 -08:00
i + + , sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ) {
2008-05-21 16:41:01 +10:00
if ( xfs_da_compname ( args , sfep - > name , sfep - > namelen ) = =
XFS_CMP_EXACT ) {
2019-11-08 15:02:59 -08:00
ASSERT ( xfs_dir2_sf_get_ino ( mp , sfp , sfep ) = =
2011-07-08 14:35:03 +02:00
args - > inumber ) ;
2005-04-16 15:20:36 -07:00
break ;
}
}
/*
* Didn ' t find it .
*/
2011-07-08 14:35:13 +02:00
if ( i = = sfp - > count )
2014-06-25 14:58:08 +10:00
return - ENOENT ;
2005-04-16 15:20:36 -07:00
/*
* Calculate sizes .
*/
byteoff = ( int ) ( ( char * ) sfep - ( char * ) sfp ) ;
2019-11-08 15:02:38 -08:00
entsize = xfs_dir2_sf_entsize ( mp , sfp , args - > namelen ) ;
2005-04-16 15:20:36 -07:00
newsize = oldsize - entsize ;
/*
* Copy the part if any after the removed entry , sliding it down .
*/
if ( byteoff + entsize < oldsize )
memmove ( ( char * ) sfp + byteoff , ( char * ) sfp + byteoff + entsize ,
oldsize - ( byteoff + entsize ) ) ;
/*
* Fix up the header and file size .
*/
2011-07-08 14:35:13 +02:00
sfp - > count - - ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = newsize ;
2023-12-20 07:34:56 +01:00
2005-04-16 15:20:36 -07:00
/*
* Reallocate , making it smaller .
*/
2023-12-20 07:34:56 +01:00
sfp = xfs_idata_realloc ( dp , newsize - oldsize , XFS_DATA_FORK ) ;
2005-04-16 15:20:36 -07:00
/*
* Are we changing inode number size ?
*/
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM ) {
2011-07-08 14:35:13 +02:00
if ( sfp - > i8count = = 1 )
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_toino4 ( args ) ;
else
2011-07-08 14:35:13 +02:00
sfp - > i8count - - ;
2005-04-16 15:20:36 -07:00
}
xfs_dir2_sf_check ( args ) ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
return 0 ;
}
2019-11-12 08:34:23 -08:00
/*
* Check whether the sf dir replace operation need more blocks .
*/
2021-01-22 16:48:32 -08:00
static bool
2019-11-12 08:34:23 -08:00
xfs_dir2_sf_replace_needblock (
struct xfs_inode * dp ,
xfs_ino_t inum )
{
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2019-11-12 08:34:23 -08:00
int newsize ;
2020-05-18 10:28:05 -07:00
if ( dp - > i_df . if_format ! = XFS_DINODE_FMT_LOCAL )
2019-11-12 08:34:23 -08:00
return false ;
newsize = dp - > i_df . if_bytes + ( sfp - > count + 1 ) * XFS_INO64_DIFF ;
return inum > XFS_DIR2_MAX_SHORT_INUM & &
2022-07-09 10:56:07 -07:00
sfp - > i8count = = 0 & & newsize > xfs_inode_data_fork_size ( dp ) ;
2019-11-12 08:34:23 -08:00
}
2005-04-16 15:20:36 -07:00
/*
* Replace the inode number of an entry in a shortform directory .
*/
int /* error */
xfs_dir2_sf_replace (
xfs_da_args_t * args ) /* operation arguments */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
int i ; /* entry index */
xfs_ino_t ino = 0 ; /* entry old inode number */
int i8elevated ; /* sf_toino8 set i8count=1 */
xfs_dir2_sf_entry_t * sfep ; /* shortform directory entry */
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_replace ( args ) ;
2021-04-13 11:15:11 -07:00
ASSERT ( dp - > i_df . if_format = = XFS_DINODE_FMT_LOCAL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size > = offsetof ( struct xfs_dir2_sf_hdr , parent ) ) ;
ASSERT ( dp - > i_df . if_bytes = = dp - > i_disk_size ) ;
2023-12-20 07:34:55 +01:00
ASSERT ( sfp ! = NULL ) ;
2021-03-29 11:11:40 -07:00
ASSERT ( dp - > i_disk_size > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2014-07-30 09:12:05 +10:00
2005-04-16 15:20:36 -07:00
/*
* New inode number is large , and need to convert to 8 - byte inodes .
*/
2011-07-08 14:35:13 +02:00
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM & & sfp - > i8count = = 0 ) {
2005-04-16 15:20:36 -07:00
int error ; /* error return value */
/*
* Won ' t fit as shortform , convert to block then do replace .
*/
2019-11-12 08:34:23 -08:00
if ( xfs_dir2_sf_replace_needblock ( dp , args - > inumber ) ) {
2005-04-16 15:20:36 -07:00
error = xfs_dir2_sf_to_block ( args ) ;
2019-11-12 08:34:23 -08:00
if ( error )
2005-04-16 15:20:36 -07:00
return error ;
return xfs_dir2_block_replace ( args ) ;
}
/*
* Still fits , convert to 8 - byte now .
*/
xfs_dir2_sf_toino8 ( args ) ;
i8elevated = 1 ;
2023-12-20 07:34:55 +01:00
sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
} else
i8elevated = 0 ;
2014-07-30 09:12:05 +10:00
2005-04-16 15:20:36 -07:00
ASSERT ( args - > namelen ! = 1 | | args - > name [ 0 ] ! = ' . ' ) ;
/*
* Replace . . ' s entry .
*/
if ( args - > namelen = = 2 & &
args - > name [ 0 ] = = ' . ' & & args - > name [ 1 ] = = ' . ' ) {
2019-11-08 15:02:31 -08:00
ino = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2005-04-16 15:20:36 -07:00
ASSERT ( args - > inumber ! = ino ) ;
2019-11-08 15:02:31 -08:00
xfs_dir2_sf_put_parent_ino ( sfp , args - > inumber ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Normal entry , look for the name .
*/
else {
2013-08-12 20:50:09 +10:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ; i < sfp - > count ;
2019-11-08 15:02:38 -08:00
i + + , sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ) {
2008-05-21 16:41:01 +10:00
if ( xfs_da_compname ( args , sfep - > name , sfep - > namelen ) = =
XFS_CMP_EXACT ) {
2019-11-08 15:02:59 -08:00
ino = xfs_dir2_sf_get_ino ( mp , sfp , sfep ) ;
2005-04-16 15:20:36 -07:00
ASSERT ( args - > inumber ! = ino ) ;
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino ( mp , sfp , sfep ,
args - > inumber ) ;
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype ( mp , sfep , args - > filetype ) ;
2005-04-16 15:20:36 -07:00
break ;
}
}
/*
* Didn ' t find it .
*/
2011-07-08 14:35:13 +02:00
if ( i = = sfp - > count ) {
2008-05-21 16:42:05 +10:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2005-04-16 15:20:36 -07:00
if ( i8elevated )
xfs_dir2_sf_toino4 ( args ) ;
2014-06-25 14:58:08 +10:00
return - ENOENT ;
2005-04-16 15:20:36 -07:00
}
}
/*
* See if the old number was large , the new number is small .
*/
if ( ino > XFS_DIR2_MAX_SHORT_INUM & &
args - > inumber < = XFS_DIR2_MAX_SHORT_INUM ) {
/*
* And the old count was one , so need to convert to small .
*/
2011-07-08 14:35:13 +02:00
if ( sfp - > i8count = = 1 )
2005-04-16 15:20:36 -07:00
xfs_dir2_sf_toino4 ( args ) ;
else
2011-07-08 14:35:13 +02:00
sfp - > i8count - - ;
2005-04-16 15:20:36 -07:00
}
/*
* See if the old number was small , the new number is large .
*/
if ( ino < = XFS_DIR2_MAX_SHORT_INUM & &
args - > inumber > XFS_DIR2_MAX_SHORT_INUM ) {
/*
* add to the i8count unless we just converted to 8 - byte
* inodes ( which does an implied i8count = 1 )
*/
2011-07-08 14:35:13 +02:00
ASSERT ( sfp - > i8count ! = 0 ) ;
2005-04-16 15:20:36 -07:00
if ( ! i8elevated )
2011-07-08 14:35:13 +02:00
sfp - > i8count + + ;
2005-04-16 15:20:36 -07:00
}
xfs_dir2_sf_check ( args ) ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_DDATA ) ;
return 0 ;
}
/*
* Convert from 8 - byte inode numbers to 4 - byte inode numbers .
* The last 8 - byte inode number is gone , but the count is still 1.
*/
static void
xfs_dir2_sf_toino4 (
xfs_da_args_t * args ) /* operation arguments */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * oldsfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
char * buf ; /* old dir's buffer */
int i ; /* entry index */
int newsize ; /* new inode size */
xfs_dir2_sf_entry_t * oldsfep ; /* old sf entry */
int oldsize ; /* old inode size */
xfs_dir2_sf_entry_t * sfep ; /* new sf entry */
2011-07-08 14:35:13 +02:00
xfs_dir2_sf_hdr_t * sfp ; /* new sf directory */
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_toino4 ( args ) ;
2005-04-16 15:20:36 -07:00
/*
* Copy the old directory to the buffer .
* Then nuke it from the inode , and add the new buffer to the inode .
* Don ' t want xfs_idata_realloc copying the data here .
*/
oldsize = dp - > i_df . if_bytes ;
2024-01-16 09:59:40 +11:00
buf = kmalloc ( oldsize , GFP_KERNEL | __GFP_NOFAIL ) ;
2011-07-08 14:35:13 +02:00
ASSERT ( oldsfp - > i8count = = 1 ) ;
2005-04-16 15:20:36 -07:00
memcpy ( buf , oldsfp , oldsize ) ;
/*
* Compute the new inode size .
*/
2016-07-20 11:48:31 +10:00
newsize = oldsize - ( oldsfp - > count + 1 ) * XFS_INO64_DIFF ;
2005-04-16 15:20:36 -07:00
xfs_idata_realloc ( dp , - oldsize , XFS_DATA_FORK ) ;
xfs_idata_realloc ( dp , newsize , XFS_DATA_FORK ) ;
/*
* Reset our pointers , the data has moved .
*/
2011-07-08 14:35:13 +02:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) buf ;
2023-12-20 07:34:55 +01:00
sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
/*
* Fill in the new header .
*/
2011-07-08 14:35:13 +02:00
sfp - > count = oldsfp - > count ;
sfp - > i8count = 0 ;
2019-11-08 15:02:31 -08:00
xfs_dir2_sf_put_parent_ino ( sfp , xfs_dir2_sf_get_parent_ino ( oldsfp ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Copy the entries field by field .
*/
2007-06-28 16:43:50 +10:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ,
oldsfep = xfs_dir2_sf_firstentry ( oldsfp ) ;
2011-07-08 14:35:13 +02:00
i < sfp - > count ;
2019-11-08 15:02:38 -08:00
i + + , sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ,
oldsfep = xfs_dir2_sf_nextentry ( mp , oldsfp , oldsfep ) ) {
2005-04-16 15:20:36 -07:00
sfep - > namelen = oldsfep - > namelen ;
2016-07-20 11:47:21 +10:00
memcpy ( sfep - > offset , oldsfep - > offset , sizeof ( sfep - > offset ) ) ;
2005-04-16 15:20:36 -07:00
memcpy ( sfep - > name , oldsfep - > name , sfep - > namelen ) ;
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino ( mp , sfp , sfep ,
xfs_dir2_sf_get_ino ( mp , oldsfp , oldsfep ) ) ;
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype ( mp , sfep ,
xfs_dir2_sf_get_ftype ( mp , oldsfep ) ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Clean up the inode .
*/
2024-01-16 09:59:43 +11:00
kfree ( buf ) ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = newsize ;
2005-04-16 15:20:36 -07:00
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
}
/*
2014-04-14 19:07:23 +10:00
* Convert existing entries from 4 - byte inode numbers to 8 - byte inode numbers .
* The new entry w / an 8 - byte inode number is not there yet ; we leave with
* i8count set to 1 , but no corresponding 8 - byte entry .
2005-04-16 15:20:36 -07:00
*/
static void
xfs_dir2_sf_toino8 (
xfs_da_args_t * args ) /* operation arguments */
{
2019-11-08 15:02:38 -08:00
struct xfs_inode * dp = args - > dp ;
struct xfs_mount * mp = dp - > i_mount ;
2023-12-20 07:34:55 +01:00
struct xfs_dir2_sf_hdr * oldsfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
char * buf ; /* old dir's buffer */
int i ; /* entry index */
int newsize ; /* new inode size */
xfs_dir2_sf_entry_t * oldsfep ; /* old sf entry */
int oldsize ; /* old inode size */
xfs_dir2_sf_entry_t * sfep ; /* new sf entry */
2011-07-08 14:35:13 +02:00
xfs_dir2_sf_hdr_t * sfp ; /* new sf directory */
2005-04-16 15:20:36 -07:00
2009-12-14 23:14:59 +00:00
trace_xfs_dir2_sf_toino8 ( args ) ;
2005-04-16 15:20:36 -07:00
/*
* Copy the old directory to the buffer .
* Then nuke it from the inode , and add the new buffer to the inode .
* Don ' t want xfs_idata_realloc copying the data here .
*/
oldsize = dp - > i_df . if_bytes ;
2024-01-16 09:59:40 +11:00
buf = kmalloc ( oldsize , GFP_KERNEL | __GFP_NOFAIL ) ;
2011-07-08 14:35:13 +02:00
ASSERT ( oldsfp - > i8count = = 0 ) ;
2005-04-16 15:20:36 -07:00
memcpy ( buf , oldsfp , oldsize ) ;
/*
2014-04-14 19:07:23 +10:00
* Compute the new inode size ( nb : entry count + 1 for parent )
2005-04-16 15:20:36 -07:00
*/
2016-07-20 11:48:31 +10:00
newsize = oldsize + ( oldsfp - > count + 1 ) * XFS_INO64_DIFF ;
2005-04-16 15:20:36 -07:00
xfs_idata_realloc ( dp , - oldsize , XFS_DATA_FORK ) ;
xfs_idata_realloc ( dp , newsize , XFS_DATA_FORK ) ;
/*
* Reset our pointers , the data has moved .
*/
2011-07-08 14:35:13 +02:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) buf ;
2023-12-20 07:34:55 +01:00
sfp = dp - > i_df . if_data ;
2005-04-16 15:20:36 -07:00
/*
* Fill in the new header .
*/
2011-07-08 14:35:13 +02:00
sfp - > count = oldsfp - > count ;
sfp - > i8count = 1 ;
2019-11-08 15:02:31 -08:00
xfs_dir2_sf_put_parent_ino ( sfp , xfs_dir2_sf_get_parent_ino ( oldsfp ) ) ;
2005-04-16 15:20:36 -07:00
/*
* Copy the entries field by field .
*/
2007-06-28 16:43:50 +10:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ,
oldsfep = xfs_dir2_sf_firstentry ( oldsfp ) ;
2011-07-08 14:35:13 +02:00
i < sfp - > count ;
2019-11-08 15:02:38 -08:00
i + + , sfep = xfs_dir2_sf_nextentry ( mp , sfp , sfep ) ,
oldsfep = xfs_dir2_sf_nextentry ( mp , oldsfp , oldsfep ) ) {
2005-04-16 15:20:36 -07:00
sfep - > namelen = oldsfep - > namelen ;
2016-07-20 11:47:21 +10:00
memcpy ( sfep - > offset , oldsfep - > offset , sizeof ( sfep - > offset ) ) ;
2005-04-16 15:20:36 -07:00
memcpy ( sfep - > name , oldsfep - > name , sfep - > namelen ) ;
2019-11-08 15:02:59 -08:00
xfs_dir2_sf_put_ino ( mp , sfp , sfep ,
xfs_dir2_sf_get_ino ( mp , oldsfp , oldsfep ) ) ;
2019-11-08 15:03:30 -08:00
xfs_dir2_sf_put_ftype ( mp , sfep ,
xfs_dir2_sf_get_ftype ( mp , oldsfep ) ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Clean up the inode .
*/
2024-01-16 09:59:43 +11:00
kfree ( buf ) ;
2021-03-29 11:11:40 -07:00
dp - > i_disk_size = newsize ;
2005-04-16 15:20:36 -07:00
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
}