2005-04-17 02:20:36 +04:00
/*
2005-11-02 06:58:39 +03:00
* Copyright ( c ) 2000 - 2003 , 2005 Silicon Graphics , Inc .
* All Rights Reserved .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
2005-04-17 02:20:36 +04:00
* published by the Free Software Foundation .
*
2005-11-02 06:58:39 +03:00
* This program is distributed in the hope that it would be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2005-04-17 02:20:36 +04:00
*
2005-11-02 06:58:39 +03:00
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
2005-04-17 02:20:36 +04:00
*/
# include "xfs.h"
2005-11-02 06:38:42 +03:00
# include "xfs_fs.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"
2005-04-17 02:20:36 +04:00
# include "xfs_sb.h"
2007-08-28 08:00:13 +04:00
# include "xfs_ag.h"
2005-04-17 02:20:36 +04:00
# include "xfs_mount.h"
2013-10-15 02:17:51 +04:00
# include "xfs_da_format.h"
2005-11-02 06:38:42 +03:00
# include "xfs_da_btree.h"
2005-04-17 02:20:36 +04:00
# include "xfs_inode.h"
2013-10-23 03:50:10 +04:00
# include "xfs_trans.h"
2005-11-02 06:38:42 +03:00
# include "xfs_inode_item.h"
2005-04-17 02:20:36 +04:00
# include "xfs_error.h"
2013-08-12 14:49:37 +04:00
# include "xfs_dir2.h"
2011-07-13 15:43:48 +04:00
# include "xfs_dir2_priv.h"
2009-12-15 02:14:59 +03:00
# include "xfs_trace.h"
2013-10-23 03:51:50 +04:00
# include "xfs_dinode.h"
2005-04-17 02:20:36 +04: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 */
# if XFS_BIG_INUMS
static void xfs_dir2_sf_toino4 ( xfs_da_args_t * args ) ;
static void xfs_dir2_sf_toino8 ( xfs_da_args_t * args ) ;
# endif /* XFS_BIG_INUMS */
2011-07-08 16:35:03 +04:00
/*
* Inode numbers in short - form directories can come in two versions ,
* either 4 bytes or 8 bytes wide . These helpers deal with the
* two forms transparently by looking at the headers i8count field .
2011-07-08 16:35:58 +04:00
*
* For 64 - bit inode number the most significant byte must be zero .
2011-07-08 16:35:03 +04:00
*/
static xfs_ino_t
xfs_dir2_sf_get_ino (
2011-07-08 16:35:13 +04:00
struct xfs_dir2_sf_hdr * hdr ,
2011-07-08 16:35:03 +04:00
xfs_dir2_inou_t * from )
{
2011-07-08 16:35:13 +04:00
if ( hdr - > i8count )
2011-07-08 16:35:58 +04:00
return get_unaligned_be64 ( & from - > i8 . i ) & 0x00ffffffffffffffULL ;
2011-07-08 16:35:03 +04:00
else
2011-07-08 16:35:58 +04:00
return get_unaligned_be32 ( & from - > i4 . i ) ;
2011-07-08 16:35:03 +04:00
}
static void
xfs_dir2_sf_put_ino (
2011-07-08 16:35:13 +04:00
struct xfs_dir2_sf_hdr * hdr ,
2011-07-08 16:35:03 +04:00
xfs_dir2_inou_t * to ,
xfs_ino_t ino )
{
2011-07-08 16:35:58 +04:00
ASSERT ( ( ino & 0xff00000000000000ULL ) = = 0 ) ;
2011-07-08 16:35:13 +04:00
if ( hdr - > i8count )
2011-07-08 16:35:58 +04:00
put_unaligned_be64 ( ino , & to - > i8 . i ) ;
2011-07-08 16:35:03 +04:00
else
2011-07-08 16:35:58 +04:00
put_unaligned_be32 ( ino , & to - > i4 . i ) ;
2011-07-08 16:35:03 +04:00
}
xfs_ino_t
xfs_dir2_sf_get_parent_ino (
2011-07-08 16:35:13 +04:00
struct xfs_dir2_sf_hdr * hdr )
2011-07-08 16:35:03 +04:00
{
2011-07-08 16:35:13 +04:00
return xfs_dir2_sf_get_ino ( hdr , & hdr - > parent ) ;
2011-07-08 16:35:03 +04:00
}
2013-08-12 14:49:37 +04:00
void
2011-07-08 16:35:03 +04:00
xfs_dir2_sf_put_parent_ino (
2011-07-08 16:35:13 +04:00
struct xfs_dir2_sf_hdr * hdr ,
2011-07-08 16:35:03 +04:00
xfs_ino_t ino )
{
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_put_ino ( hdr , & hdr - > parent , ino ) ;
2011-07-08 16:35:03 +04:00
}
/*
* In short - form directory entries the inode numbers are stored at variable
2013-08-12 14:50:09 +04:00
* offset behind the entry name . If the entry stores a filetype value , then it
* sits between the name and the inode number . Hence the inode numbers may only
* be accessed through the helpers below .
2011-07-08 16:35:03 +04:00
*/
static xfs_dir2_inou_t *
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_inop (
struct xfs_mount * mp ,
2011-07-08 16:35:03 +04:00
struct xfs_dir2_sf_entry * sfep )
{
2013-08-12 14:50:09 +04:00
__uint8_t * ptr = & sfep - > name [ sfep - > namelen ] ;
if ( xfs_sb_version_hasftype ( & mp - > m_sb ) )
ptr + + ;
return ( xfs_dir2_inou_t * ) ptr ;
2011-07-08 16:35:03 +04:00
}
xfs_ino_t
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_get_ino (
struct xfs_mount * mp ,
2011-07-08 16:35:13 +04:00
struct xfs_dir2_sf_hdr * hdr ,
2011-07-08 16:35:03 +04:00
struct xfs_dir2_sf_entry * sfep )
{
2013-08-12 14:50:09 +04:00
return xfs_dir2_sf_get_ino ( hdr , xfs_dir3_sfe_inop ( mp , sfep ) ) ;
2011-07-08 16:35:03 +04:00
}
2013-09-02 06:15:59 +04:00
void
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_put_ino (
struct xfs_mount * mp ,
2011-07-08 16:35:13 +04:00
struct xfs_dir2_sf_hdr * hdr ,
2011-07-08 16:35:03 +04:00
struct xfs_dir2_sf_entry * sfep ,
xfs_ino_t ino )
{
2013-08-12 14:50:09 +04:00
xfs_dir2_sf_put_ino ( hdr , xfs_dir3_sfe_inop ( mp , sfep ) , ino ) ;
2011-07-08 16:35:03 +04:00
}
2005-04-17 02:20:36 +04: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 16:35:27 +04:00
xfs_dir2_data_hdr_t * hdr , /* block directory data */
2005-04-17 02:20:36 +04: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 07:06:18 +03:00
xfs_ino_t parent = 0 ; /* parent inode number */
2005-04-17 02:20:36 +04:00
int size = 0 ; /* total computed size */
2013-08-12 14:50:09 +04:00
int has_ftype ;
2005-04-17 02:20:36 +04:00
mp = dp - > i_mount ;
2013-08-12 14:50:09 +04:00
/*
* if there is a filetype field , add the extra byte to the namelen
* for each entry that we see .
*/
has_ftype = xfs_sb_version_hasftype ( & mp - > m_sb ) ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
count = i8count = namelen = 0 ;
2011-07-08 16:35:27 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2007-06-28 10:43:50 +04:00
blp = xfs_dir2_block_leaf_p ( btp ) ;
2005-04-17 02:20:36 +04:00
/*
* Iterate over the block ' s data entries by using the leaf pointers .
*/
2006-03-17 09:27:56 +03:00
for ( i = 0 ; i < be32_to_cpu ( btp - > count ) ; i + + ) {
2006-03-17 09:28:18 +03:00
if ( ( addr = be32_to_cpu ( blp [ i ] . address ) ) = = XFS_DIR2_NULL_DATAPTR )
2005-04-17 02:20:36 +04:00
continue ;
/*
* Calculate the pointer to the entry at hand .
*/
dep = ( xfs_dir2_data_entry_t * )
2011-07-08 16:35:27 +04:00
( ( char * ) hdr + xfs_dir2_dataptr_to_off ( mp , addr ) ) ;
2005-04-17 02:20:36 +04: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 ] = = ' . ' ;
# if XFS_BIG_INUMS
if ( ! isdot )
2006-06-09 08:48:37 +04:00
i8count + = be64_to_cpu ( dep - > inumber ) > XFS_DIR2_MAX_SHORT_INUM ;
2005-04-17 02:20:36 +04:00
# endif
2013-08-12 14:50:09 +04:00
/* take into account the file type field */
2005-04-17 02:20:36 +04:00
if ( ! isdot & & ! isdotdot ) {
count + + ;
2013-08-12 14:50:09 +04:00
namelen + = dep - > namelen + has_ftype ;
2005-04-17 02:20:36 +04:00
} else if ( isdotdot )
2006-06-09 08:48:37 +04:00
parent = be64_to_cpu ( dep - > inumber ) ;
2005-04-17 02:20:36 +04:00
/*
* Calculate the new size , see if we should give up yet .
*/
2007-06-28 10:43:50 +04:00
size = xfs_dir2_sf_hdr_size ( i8count ) + /* header */
2005-04-17 02:20:36 +04:00
count + /* namelen */
count * ( uint ) sizeof ( xfs_dir2_sf_off_t ) + /* offset */
namelen + /* name */
( i8count ? /* inumber */
( uint ) sizeof ( xfs_dir2_ino8_t ) * count :
( uint ) sizeof ( xfs_dir2_ino4_t ) * count ) ;
if ( size > XFS_IFORK_DSIZE ( dp ) )
return size ; /* size value is a failure */
}
/*
* Create the output header , if it worked .
*/
sfhp - > count = count ;
sfhp - > i8count = i8count ;
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_put_parent_ino ( sfhp , parent ) ;
2005-04-17 02:20:36 +04: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 (
xfs_da_args_t * args , /* operation arguments */
2012-06-22 12:50:14 +04:00
struct xfs_buf * bp ,
2005-04-17 02:20:36 +04:00
int size , /* shortform directory size */
xfs_dir2_sf_hdr_t * sfhp ) /* shortform directory hdr */
{
2011-07-08 16:35:32 +04:00
xfs_dir2_data_hdr_t * hdr ; /* block header */
2005-04-17 02:20:36 +04:00
xfs_dir2_block_tail_t * btp ; /* block tail pointer */
xfs_dir2_data_entry_t * dep ; /* data entry pointer */
xfs_inode_t * dp ; /* incore directory inode */
xfs_dir2_data_unused_t * dup ; /* unused data pointer */
char * endptr ; /* end of data entries */
int error ; /* error return value */
int logflags ; /* inode logging flags */
xfs_mount_t * mp ; /* filesystem mount point */
char * ptr ; /* current data pointer */
xfs_dir2_sf_entry_t * sfep ; /* shortform entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform directory header */
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_block_to_sf ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
mp = dp - > i_mount ;
/*
* Make a copy of the block data , so we can shrink the inode
* and add local data .
*/
2011-07-08 16:35:32 +04:00
hdr = kmem_alloc ( mp - > m_dirblksize , KM_SLEEP ) ;
2012-06-22 12:50:14 +04:00
memcpy ( hdr , bp - > b_addr , mp - > m_dirblksize ) ;
2005-04-17 02:20:36 +04:00
logflags = XFS_ILOG_CORE ;
if ( ( error = xfs_dir2_shrink_inode ( args , mp - > m_dirdatablk , bp ) ) ) {
ASSERT ( error ! = ENOSPC ) ;
goto out ;
}
2011-07-08 16:35:27 +04:00
2005-04-17 02:20:36 +04:00
/*
* The buffer is now unconditionally gone , whether
* xfs_dir2_shrink_inode worked or not .
*
* Convert the inode to local format .
*/
dp - > i_df . if_flags & = ~ XFS_IFEXTENTS ;
dp - > i_df . if_flags | = XFS_IFINLINE ;
dp - > i_d . di_format = XFS_DINODE_FMT_LOCAL ;
ASSERT ( dp - > i_df . if_bytes = = 0 ) ;
xfs_idata_realloc ( dp , size , XFS_DATA_FORK ) ;
logflags | = XFS_ILOG_DDATA ;
/*
* Copy the header into the newly allocate local space .
*/
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2007-06-28 10:43:50 +04:00
memcpy ( sfp , sfhp , xfs_dir2_sf_hdr_size ( sfhp - > i8count ) ) ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = size ;
/*
* Set up to loop over the block ' s entries .
*/
2011-07-08 16:35:32 +04:00
btp = xfs_dir2_block_tail_p ( mp , hdr ) ;
2013-04-03 09:11:20 +04:00
ptr = ( char * ) xfs_dir3_data_entry_p ( hdr ) ;
2007-06-28 10:43:50 +04:00
endptr = ( char * ) xfs_dir2_block_leaf_p ( btp ) ;
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2005-04-17 02:20:36 +04:00
/*
* Loop over the active and unused entries .
* Stop when we reach the leaf / tail portion of the block .
*/
while ( ptr < endptr ) {
/*
* If it ' s unused , just skip over it .
*/
dup = ( xfs_dir2_data_unused_t * ) ptr ;
2006-03-17 09:27:37 +03:00
if ( be16_to_cpu ( dup - > freetag ) = = XFS_DIR2_DATA_FREE_TAG ) {
ptr + = be16_to_cpu ( dup - > length ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
dep = ( xfs_dir2_data_entry_t * ) ptr ;
/*
* Skip .
*/
if ( dep - > namelen = = 1 & & dep - > name [ 0 ] = = ' . ' )
2006-06-09 08:48:37 +04:00
ASSERT ( be64_to_cpu ( dep - > inumber ) = = dp - > i_ino ) ;
2005-04-17 02:20:36 +04:00
/*
* Skip . . , but make sure the inode number is right .
*/
else if ( dep - > namelen = = 2 & &
dep - > name [ 0 ] = = ' . ' & & dep - > name [ 1 ] = = ' . ' )
2006-06-09 08:48:37 +04:00
ASSERT ( be64_to_cpu ( dep - > inumber ) = =
2011-07-08 16:35:03 +04:00
xfs_dir2_sf_get_parent_ino ( sfp ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Normal entry , copy it into shortform .
*/
else {
sfep - > namelen = dep - > namelen ;
2007-06-28 10:43:50 +04:00
xfs_dir2_sf_put_offset ( sfep ,
2005-04-17 02:20:36 +04:00
( xfs_dir2_data_aoff_t )
2011-07-08 16:35:32 +04:00
( ( char * ) dep - ( char * ) hdr ) ) ;
2005-04-17 02:20:36 +04:00
memcpy ( sfep - > name , dep - > name , dep - > namelen ) ;
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_put_ino ( mp , sfp , sfep ,
2011-07-08 16:35:03 +04:00
be64_to_cpu ( dep - > inumber ) ) ;
2013-08-12 14:50:10 +04:00
xfs_dir3_sfe_put_ftype ( mp , sfp , sfep ,
xfs_dir3_dirent_get_ftype ( mp , dep ) ) ;
2011-07-08 16:35:03 +04:00
2013-08-12 14:50:09 +04:00
sfep = xfs_dir3_sf_nextentry ( mp , sfp , sfep ) ;
2005-04-17 02:20:36 +04:00
}
2013-08-12 14:50:09 +04:00
ptr + = xfs_dir3_data_entsize ( mp , dep - > namelen ) ;
2005-04-17 02:20:36 +04:00
}
ASSERT ( ( char * ) sfep - ( char * ) sfp = = size ) ;
xfs_dir2_sf_check ( args ) ;
out :
xfs_trans_log_inode ( args - > trans , dp , logflags ) ;
2011-07-08 16:35:32 +04:00
kmem_free ( hdr ) ;
2005-04-17 02:20:36 +04: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 */
{
int add_entsize ; /* size of the new entry */
xfs_inode_t * dp ; /* incore directory inode */
int error ; /* error return value */
int incr_isize ; /* total change in size */
int new_isize ; /* di_size after adding name */
int objchange ; /* changing to 8-byte inodes */
2005-11-02 07:06:18 +03:00
xfs_dir2_data_aoff_t offset = 0 ; /* offset for new entry */
2005-04-17 02:20:36 +04:00
int old_isize ; /* di_size before adding name */
int pick ; /* which algorithm to use */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-11-02 07:06:18 +03:00
xfs_dir2_sf_entry_t * sfep = NULL ; /* shortform entry */
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_addname ( args ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( xfs_dir2_sf_lookup ( args ) = = ENOENT ) ;
dp = args - > dp ;
ASSERT ( dp - > i_df . if_flags & XFS_IFINLINE ) ;
/*
* Make sure the shortform value has some of its header .
*/
if ( dp - > i_d . di_size < offsetof ( xfs_dir2_sf_hdr_t , parent ) ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) ) ;
return XFS_ERROR ( EIO ) ;
}
ASSERT ( dp - > i_df . if_bytes = = dp - > i_d . di_size ) ;
ASSERT ( dp - > i_df . if_u1 . if_data ! = NULL ) ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
ASSERT ( dp - > i_d . di_size > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Compute entry ( and change in ) size .
*/
2013-08-12 14:50:09 +04:00
add_entsize = xfs_dir3_sf_entsize ( dp - > i_mount , sfp , args - > namelen ) ;
2005-04-17 02:20:36 +04:00
incr_isize = add_entsize ;
objchange = 0 ;
# if XFS_BIG_INUMS
/*
* Do we have to change to 8 byte inodes ?
*/
2011-07-08 16:35:13 +04:00
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM & & sfp - > i8count = = 0 ) {
2005-04-17 02:20:36 +04:00
/*
* Yes , adjust the entry size and the total size .
*/
add_entsize + =
( uint ) sizeof ( xfs_dir2_ino8_t ) -
( uint ) sizeof ( xfs_dir2_ino4_t ) ;
incr_isize + =
2011-07-08 16:35:13 +04:00
( sfp - > count + 2 ) *
2005-04-17 02:20:36 +04:00
( ( uint ) sizeof ( xfs_dir2_ino8_t ) -
( uint ) sizeof ( xfs_dir2_ino4_t ) ) ;
objchange = 1 ;
}
# endif
old_isize = ( int ) dp - > i_d . di_size ;
new_isize = old_isize + incr_isize ;
/*
* Won ' t fit as shortform any more ( due to size ) ,
* or the pick routine says it won ' t ( due to offset values ) .
*/
if ( new_isize > XFS_IFORK_DSIZE ( dp ) | |
( pick =
xfs_dir2_sf_addname_pick ( args , objchange , & sfep , & offset ) ) = = 0 ) {
/*
* Just checking or no space reservation , it doesn ' t fit .
*/
2008-05-21 10:42:05 +04:00
if ( ( args - > op_flags & XFS_DA_OP_JUSTCHECK ) | | args - > total = = 0 )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( ENOSPC ) ;
/*
* 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 10:42:05 +04:00
if ( args - > op_flags & XFS_DA_OP_JUSTCHECK )
2005-04-17 02:20:36 +04: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 XFS_BIG_INUMS
if ( objchange )
xfs_dir2_sf_toino8 ( args ) ;
# endif
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 */
{
int byteoff ; /* byte offset in sf dir */
xfs_inode_t * dp ; /* incore directory inode */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
byteoff = ( int ) ( ( char * ) sfep - ( char * ) sfp ) ;
/*
* Grow the in - inode space .
*/
2013-08-12 14:50:09 +04:00
xfs_idata_realloc ( dp ,
xfs_dir3_sf_entsize ( dp - > i_mount , sfp , args - > namelen ) ,
XFS_DATA_FORK ) ;
2005-04-17 02:20:36 +04:00
/*
* Need to set up again due to realloc of the inode data .
*/
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
sfep = ( xfs_dir2_sf_entry_t * ) ( ( char * ) sfp + byteoff ) ;
/*
* Fill in the new entry .
*/
sfep - > namelen = args - > namelen ;
2007-06-28 10:43:50 +04:00
xfs_dir2_sf_put_offset ( sfep , offset ) ;
2005-04-17 02:20:36 +04:00
memcpy ( sfep - > name , args - > name , sfep - > namelen ) ;
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_put_ino ( dp - > i_mount , sfp , sfep , args - > inumber ) ;
2013-08-12 14:50:10 +04:00
xfs_dir3_sfe_put_ftype ( dp - > i_mount , sfp , sfep , args - > filetype ) ;
2005-04-17 02:20:36 +04:00
/*
* Update the header and inode .
*/
2011-07-08 16:35:13 +04:00
sfp - > count + + ;
2005-04-17 02:20:36 +04:00
# if XFS_BIG_INUMS
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM )
2011-07-08 16:35:13 +04:00
sfp - > i8count + + ;
2005-04-17 02:20:36 +04:00
# endif
dp - > i_d . di_size = new_isize ;
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 */
{
int add_datasize ; /* data size need for new ent */
char * buf ; /* buffer for old */
xfs_inode_t * dp ; /* incore directory inode */
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 */
int old_isize ; /* previous di_size */
xfs_dir2_sf_entry_t * oldsfep ; /* entry in original dir */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * oldsfp ; /* original shortform dir */
2005-04-17 02:20:36 +04:00
xfs_dir2_sf_entry_t * sfep ; /* entry in new dir */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* new shortform dir */
2013-08-12 14:50:09 +04:00
struct xfs_mount * mp ;
2005-04-17 02:20:36 +04:00
/*
* Copy the old directory to the stack buffer .
*/
dp = args - > dp ;
2013-08-12 14:50:09 +04:00
mp = dp - > i_mount ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
old_isize = ( int ) dp - > i_d . di_size ;
buf = kmem_alloc ( old_isize , KM_SLEEP ) ;
2011-07-08 16:35:13 +04:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) buf ;
2005-04-17 02:20:36 +04:00
memcpy ( oldsfp , sfp , old_isize ) ;
/*
* 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 .
*/
2013-09-30 03:37:04 +04:00
for ( offset = xfs_dir3_data_first_offset ( mp ) ,
2007-06-28 10:43:50 +04:00
oldsfep = xfs_dir2_sf_firstentry ( oldsfp ) ,
2013-08-12 14:50:09 +04:00
add_datasize = xfs_dir3_data_entsize ( mp , args - > namelen ) ,
2005-04-17 02:20:36 +04:00
eof = ( char * ) oldsfep = = & buf [ old_isize ] ;
! eof ;
2013-08-12 14:50:09 +04:00
offset = new_offset + xfs_dir3_data_entsize ( mp , oldsfep - > namelen ) ,
oldsfep = xfs_dir3_sf_nextentry ( mp , oldsfp , oldsfep ) ,
2005-04-17 02:20:36 +04:00
eof = ( char * ) oldsfep = = & buf [ old_isize ] ) {
2007-06-28 10:43:50 +04:00
new_offset = xfs_dir2_sf_get_offset ( oldsfep ) ;
2005-04-17 02:20:36 +04: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 ) ;
xfs_idata_realloc ( dp , new_isize , XFS_DATA_FORK ) ;
/*
* Reset the pointer since the buffer was reallocated .
*/
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04: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 10:43:50 +04:00
xfs_dir2_sf_put_offset ( sfep , offset ) ;
2005-04-17 02:20:36 +04:00
memcpy ( sfep - > name , args - > name , sfep - > namelen ) ;
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_put_ino ( mp , sfp , sfep , args - > inumber ) ;
2013-08-12 14:50:10 +04:00
xfs_dir3_sfe_put_ftype ( mp , sfp , sfep , args - > filetype ) ;
2011-07-08 16:35:13 +04:00
sfp - > count + + ;
2005-04-17 02:20:36 +04:00
# if XFS_BIG_INUMS
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM & & ! objchange )
2011-07-08 16:35:13 +04:00
sfp - > i8count + + ;
2005-04-17 02:20:36 +04:00
# endif
/*
* If there ' s more left to copy , do that .
*/
if ( ! eof ) {
2013-08-12 14:50:09 +04:00
sfep = xfs_dir3_sf_nextentry ( mp , sfp , sfep ) ;
2005-04-17 02:20:36 +04:00
memcpy ( sfep , oldsfep , old_isize - nbytes ) ;
}
2008-05-19 10:31:57 +04:00
kmem_free ( buf ) ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = new_isize ;
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 */
{
xfs_inode_t * dp ; /* incore directory inode */
int holefit ; /* found hole it will fit in */
int i ; /* entry number */
xfs_mount_t * mp ; /* filesystem mount point */
xfs_dir2_data_aoff_t offset ; /* data block offset */
xfs_dir2_sf_entry_t * sfep ; /* shortform entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-04-17 02:20:36 +04:00
int size ; /* entry's data size */
int used ; /* data bytes used */
dp = args - > dp ;
mp = dp - > i_mount ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2013-08-12 14:50:09 +04:00
size = xfs_dir3_data_entsize ( mp , args - > namelen ) ;
2013-09-30 03:37:04 +04:00
offset = xfs_dir3_data_first_offset ( mp ) ;
2007-06-28 10:43:50 +04:00
sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2005-04-17 02:20:36 +04: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 16:35:13 +04:00
for ( i = 0 ; i < sfp - > count ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( ! holefit )
2007-06-28 10:43:50 +04:00
holefit = offset + size < = xfs_dir2_sf_get_offset ( sfep ) ;
offset = xfs_dir2_sf_get_offset ( sfep ) +
2013-08-12 14:50:09 +04:00
xfs_dir3_data_entsize ( mp , sfep - > namelen ) ;
sfep = xfs_dir3_sf_nextentry ( mp , sfp , sfep ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Calculate data bytes used excluding the new entry , if this
* was a data block ( block form directory ) .
*/
used = offset +
2011-07-08 16:35:13 +04:00
( sfp - > count + 3 ) * ( uint ) sizeof ( xfs_dir2_leaf_entry_t ) +
2005-04-17 02:20:36 +04: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 .
*/
if ( used + ( holefit ? 0 : size ) > mp - > m_dirblksize )
return 0 ;
/*
* If changing the inode number size , do it the hard way .
*/
# if XFS_BIG_INUMS
if ( objchange ) {
return 2 ;
}
# else
ASSERT ( objchange = = 0 ) ;
# endif
/*
* If it won ' t fit at the end then do it the hard way ( use the hole ) .
*/
if ( used + size > mp - > m_dirblksize )
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 */
{
xfs_inode_t * dp ; /* incore directory inode */
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 */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2013-08-12 14:50:09 +04:00
struct xfs_mount * mp ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
2013-08-12 14:50:09 +04:00
mp = dp - > i_mount ;
2005-04-17 02:20:36 +04:00
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2013-09-30 03:37:04 +04:00
offset = xfs_dir3_data_first_offset ( mp ) ;
2011-07-08 16:35:03 +04:00
ino = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2005-04-17 02:20:36 +04:00
i8count = ino > XFS_DIR2_MAX_SHORT_INUM ;
2007-06-28 10:43:50 +04:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ;
2011-07-08 16:35:13 +04:00
i < sfp - > count ;
2013-08-12 14:50:09 +04:00
i + + , sfep = xfs_dir3_sf_nextentry ( mp , sfp , sfep ) ) {
2007-06-28 10:43:50 +04:00
ASSERT ( xfs_dir2_sf_get_offset ( sfep ) > = offset ) ;
2013-08-12 14:50:09 +04:00
ino = xfs_dir3_sfe_get_ino ( mp , sfp , sfep ) ;
2005-04-17 02:20:36 +04:00
i8count + = ino > XFS_DIR2_MAX_SHORT_INUM ;
offset =
2007-06-28 10:43:50 +04:00
xfs_dir2_sf_get_offset ( sfep ) +
2013-08-12 14:50:09 +04:00
xfs_dir3_data_entsize ( mp , sfep - > namelen ) ;
ASSERT ( xfs_dir3_sfe_get_ftype ( mp , sfp , sfep ) <
XFS_DIR3_FT_MAX ) ;
2005-04-17 02:20:36 +04:00
}
2011-07-08 16:35:13 +04:00
ASSERT ( i8count = = sfp - > i8count ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( XFS_BIG_INUMS | | i8count = = 0 ) ;
ASSERT ( ( char * ) sfep - ( char * ) sfp = = dp - > i_d . di_size ) ;
ASSERT ( offset +
2011-07-08 16:35:13 +04:00
( sfp - > count + 2 ) * ( uint ) sizeof ( xfs_dir2_leaf_entry_t ) +
2013-08-12 14:50:09 +04:00
( uint ) sizeof ( xfs_dir2_block_tail_t ) < = mp - > m_dirblksize ) ;
2005-04-17 02:20:36 +04:00
}
# endif /* DEBUG */
/*
* 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 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-04-17 02:20:36 +04:00
int size ; /* directory size */
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_create ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
ASSERT ( dp ! = NULL ) ;
ASSERT ( dp - > i_d . di_size = = 0 ) ;
/*
* If it ' s currently a zero - length extent file ,
* convert it to local format .
*/
if ( dp - > i_d . di_format = = XFS_DINODE_FMT_EXTENTS ) {
dp - > i_df . if_flags & = ~ XFS_IFEXTENTS ; /* just in case */
dp - > i_d . di_format = XFS_DINODE_FMT_LOCAL ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE ) ;
dp - > i_df . if_flags | = XFS_IFINLINE ;
}
ASSERT ( dp - > i_df . if_flags & XFS_IFINLINE ) ;
ASSERT ( dp - > i_df . if_bytes = = 0 ) ;
i8count = pino > XFS_DIR2_MAX_SHORT_INUM ;
2007-06-28 10:43:50 +04:00
size = xfs_dir2_sf_hdr_size ( i8count ) ;
2005-04-17 02:20:36 +04:00
/*
* Make a buffer for the data .
*/
xfs_idata_realloc ( dp , size , XFS_DATA_FORK ) ;
/*
* Fill in the header ,
*/
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
sfp - > i8count = i8count ;
2005-04-17 02:20:36 +04:00
/*
* Now can put in the inode number , since i8count is set .
*/
2011-07-08 16:35:03 +04:00
xfs_dir2_sf_put_parent_ino ( sfp , pino ) ;
2011-07-08 16:35:13 +04:00
sfp - > count = 0 ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = size ;
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 */
{
xfs_inode_t * dp ; /* incore directory inode */
int i ; /* entry index */
2008-05-21 10:58:22 +04:00
int error ;
2005-04-17 02:20:36 +04:00
xfs_dir2_sf_entry_t * sfep ; /* shortform directory entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2008-05-21 10:41:01 +04:00
enum xfs_dacmp cmp ; /* comparison result */
2008-05-21 10:58:22 +04:00
xfs_dir2_sf_entry_t * ci_sfep ; /* case-insens. entry */
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_lookup ( args ) ;
2005-04-17 02:20:36 +04:00
xfs_dir2_sf_check ( args ) ;
dp = args - > dp ;
ASSERT ( dp - > i_df . if_flags & XFS_IFINLINE ) ;
/*
* Bail out if the directory is way too short .
*/
if ( dp - > i_d . di_size < offsetof ( xfs_dir2_sf_hdr_t , parent ) ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) ) ;
return XFS_ERROR ( EIO ) ;
}
ASSERT ( dp - > i_df . if_bytes = = dp - > i_d . di_size ) ;
ASSERT ( dp - > i_df . if_u1 . if_data ! = NULL ) ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
ASSERT ( dp - > i_d . di_size > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Special case for .
*/
if ( args - > namelen = = 1 & & args - > name [ 0 ] = = ' . ' ) {
args - > inumber = dp - > i_ino ;
2008-05-21 10:41:01 +04:00
args - > cmpresult = XFS_CMP_EXACT ;
2013-08-12 14:50:10 +04:00
args - > filetype = XFS_DIR3_FT_DIR ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EEXIST ) ;
}
/*
* Special case for . .
*/
if ( args - > namelen = = 2 & &
args - > name [ 0 ] = = ' . ' & & args - > name [ 1 ] = = ' . ' ) {
2011-07-08 16:35:03 +04:00
args - > inumber = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2008-05-21 10:41:01 +04:00
args - > cmpresult = XFS_CMP_EXACT ;
2013-08-12 14:50:10 +04:00
args - > filetype = XFS_DIR3_FT_DIR ;
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( EEXIST ) ;
}
/*
* Loop over all the entries trying to match ours .
*/
2008-05-21 10:58:22 +04:00
ci_sfep = NULL ;
2011-07-08 16:35:13 +04:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ; i < sfp - > count ;
2013-08-12 14:50:09 +04:00
i + + , sfep = xfs_dir3_sf_nextentry ( dp - > i_mount , sfp , sfep ) ) {
2008-05-21 10:41:01 +04: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 .
*/
cmp = dp - > i_mount - > m_dirnameops - > compname ( args , sfep - > name ,
sfep - > namelen ) ;
if ( cmp ! = XFS_CMP_DIFFERENT & & cmp ! = args - > cmpresult ) {
args - > cmpresult = cmp ;
2013-08-12 14:50:09 +04:00
args - > inumber = xfs_dir3_sfe_get_ino ( dp - > i_mount ,
sfp , sfep ) ;
2013-08-12 14:50:10 +04:00
args - > filetype = xfs_dir3_sfe_get_ftype ( dp - > i_mount ,
sfp , sfep ) ;
2008-05-21 10:41:01 +04:00
if ( cmp = = XFS_CMP_EXACT )
return XFS_ERROR ( EEXIST ) ;
2008-05-21 10:58:22 +04:00
ci_sfep = sfep ;
2005-04-17 02:20:36 +04:00
}
}
2008-05-21 10:42:05 +04:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2008-05-21 10:41:01 +04:00
/*
* Here , we can only be doing a lookup ( not a rename or replace ) .
2008-05-21 10:58:22 +04:00
* If a case - insensitive match was not found , return ENOENT .
2008-05-21 10:41:01 +04:00
*/
2008-05-21 10:58:22 +04:00
if ( ! ci_sfep )
return XFS_ERROR ( ENOENT ) ;
/* otherwise process the CI match as required by the caller */
error = xfs_dir_cilookup_result ( args , ci_sfep - > name , ci_sfep - > namelen ) ;
return XFS_ERROR ( error ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Remove an entry from a shortform directory .
*/
int /* error */
xfs_dir2_sf_removename (
xfs_da_args_t * args )
{
int byteoff ; /* offset of removed entry */
xfs_inode_t * dp ; /* incore directory inode */
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 */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_removename ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
ASSERT ( dp - > i_df . if_flags & XFS_IFINLINE ) ;
oldsize = ( int ) dp - > i_d . di_size ;
/*
* Bail out if the directory is way too short .
*/
if ( oldsize < offsetof ( xfs_dir2_sf_hdr_t , parent ) ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) ) ;
return XFS_ERROR ( EIO ) ;
}
ASSERT ( dp - > i_df . if_bytes = = oldsize ) ;
ASSERT ( dp - > i_df . if_u1 . if_data ! = NULL ) ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
ASSERT ( oldsize > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Loop over the old directory entries .
* Find the one we ' re deleting .
*/
2011-07-08 16:35:13 +04:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ; i < sfp - > count ;
2013-08-12 14:50:09 +04:00
i + + , sfep = xfs_dir3_sf_nextentry ( dp - > i_mount , sfp , sfep ) ) {
2008-05-21 10:41:01 +04:00
if ( xfs_da_compname ( args , sfep - > name , sfep - > namelen ) = =
XFS_CMP_EXACT ) {
2013-08-12 14:50:09 +04:00
ASSERT ( xfs_dir3_sfe_get_ino ( dp - > i_mount , sfp , sfep ) = =
2011-07-08 16:35:03 +04:00
args - > inumber ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
/*
* Didn ' t find it .
*/
2011-07-08 16:35:13 +04:00
if ( i = = sfp - > count )
2005-04-17 02:20:36 +04:00
return XFS_ERROR ( ENOENT ) ;
/*
* Calculate sizes .
*/
byteoff = ( int ) ( ( char * ) sfep - ( char * ) sfp ) ;
2013-08-12 14:50:09 +04:00
entsize = xfs_dir3_sf_entsize ( dp - > i_mount , sfp , args - > namelen ) ;
2005-04-17 02:20:36 +04: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 16:35:13 +04:00
sfp - > count - - ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = newsize ;
/*
* Reallocate , making it smaller .
*/
xfs_idata_realloc ( dp , newsize - oldsize , XFS_DATA_FORK ) ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
# if XFS_BIG_INUMS
/*
* Are we changing inode number size ?
*/
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM ) {
2011-07-08 16:35:13 +04:00
if ( sfp - > i8count = = 1 )
2005-04-17 02:20:36 +04:00
xfs_dir2_sf_toino4 ( args ) ;
else
2011-07-08 16:35:13 +04:00
sfp - > i8count - - ;
2005-04-17 02:20:36 +04:00
}
# endif
xfs_dir2_sf_check ( args ) ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
return 0 ;
}
/*
* Replace the inode number of an entry in a shortform directory .
*/
int /* error */
xfs_dir2_sf_replace (
xfs_da_args_t * args ) /* operation arguments */
{
xfs_inode_t * dp ; /* incore directory inode */
int i ; /* entry index */
# if XFS_BIG_INUMS || defined(DEBUG)
xfs_ino_t ino = 0 ; /* entry old inode number */
# endif
# if XFS_BIG_INUMS
int i8elevated ; /* sf_toino8 set i8count=1 */
# endif
xfs_dir2_sf_entry_t * sfep ; /* shortform directory entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* shortform structure */
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_replace ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
ASSERT ( dp - > i_df . if_flags & XFS_IFINLINE ) ;
/*
* Bail out if the shortform directory is way too small .
*/
if ( dp - > i_d . di_size < offsetof ( xfs_dir2_sf_hdr_t , parent ) ) {
ASSERT ( XFS_FORCED_SHUTDOWN ( dp - > i_mount ) ) ;
return XFS_ERROR ( EIO ) ;
}
ASSERT ( dp - > i_df . if_bytes = = dp - > i_d . di_size ) ;
ASSERT ( dp - > i_df . if_u1 . if_data ! = NULL ) ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
ASSERT ( dp - > i_d . di_size > = xfs_dir2_sf_hdr_size ( sfp - > i8count ) ) ;
2005-04-17 02:20:36 +04:00
# if XFS_BIG_INUMS
/*
* New inode number is large , and need to convert to 8 - byte inodes .
*/
2011-07-08 16:35:13 +04:00
if ( args - > inumber > XFS_DIR2_MAX_SHORT_INUM & & sfp - > i8count = = 0 ) {
2005-04-17 02:20:36 +04:00
int error ; /* error return value */
int newsize ; /* new inode size */
newsize =
dp - > i_df . if_bytes +
2011-07-08 16:35:13 +04:00
( sfp - > count + 1 ) *
2005-04-17 02:20:36 +04:00
( ( uint ) sizeof ( xfs_dir2_ino8_t ) -
( uint ) sizeof ( xfs_dir2_ino4_t ) ) ;
/*
* Won ' t fit as shortform , convert to block then do replace .
*/
if ( newsize > XFS_IFORK_DSIZE ( dp ) ) {
error = xfs_dir2_sf_to_block ( args ) ;
if ( error ) {
return error ;
}
return xfs_dir2_block_replace ( args ) ;
}
/*
* Still fits , convert to 8 - byte now .
*/
xfs_dir2_sf_toino8 ( args ) ;
i8elevated = 1 ;
2011-07-08 16:35:13 +04:00
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
} else
i8elevated = 0 ;
# endif
ASSERT ( args - > namelen ! = 1 | | args - > name [ 0 ] ! = ' . ' ) ;
/*
* Replace . . ' s entry .
*/
if ( args - > namelen = = 2 & &
args - > name [ 0 ] = = ' . ' & & args - > name [ 1 ] = = ' . ' ) {
# if XFS_BIG_INUMS || defined(DEBUG)
2011-07-08 16:35:03 +04:00
ino = xfs_dir2_sf_get_parent_ino ( sfp ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( args - > inumber ! = ino ) ;
# endif
2011-07-08 16:35:03 +04:00
xfs_dir2_sf_put_parent_ino ( sfp , args - > inumber ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Normal entry , look for the name .
*/
else {
2013-08-12 14:50:09 +04:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ; i < sfp - > count ;
i + + , sfep = xfs_dir3_sf_nextentry ( dp - > i_mount , sfp , sfep ) ) {
2008-05-21 10:41:01 +04:00
if ( xfs_da_compname ( args , sfep - > name , sfep - > namelen ) = =
XFS_CMP_EXACT ) {
2005-04-17 02:20:36 +04:00
# if XFS_BIG_INUMS || defined(DEBUG)
2013-08-12 14:50:09 +04:00
ino = xfs_dir3_sfe_get_ino ( dp - > i_mount ,
sfp , sfep ) ;
2005-04-17 02:20:36 +04:00
ASSERT ( args - > inumber ! = ino ) ;
# endif
2013-08-12 14:50:09 +04:00
xfs_dir3_sfe_put_ino ( dp - > i_mount , sfp , sfep ,
args - > inumber ) ;
2013-08-12 14:50:10 +04:00
xfs_dir3_sfe_put_ftype ( dp - > i_mount , sfp , sfep ,
args - > filetype ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
/*
* Didn ' t find it .
*/
2011-07-08 16:35:13 +04:00
if ( i = = sfp - > count ) {
2008-05-21 10:42:05 +04:00
ASSERT ( args - > op_flags & XFS_DA_OP_OKNOENT ) ;
2005-04-17 02:20:36 +04:00
# if XFS_BIG_INUMS
if ( i8elevated )
xfs_dir2_sf_toino4 ( args ) ;
# endif
return XFS_ERROR ( ENOENT ) ;
}
}
# if XFS_BIG_INUMS
/*
* 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 16:35:13 +04:00
if ( sfp - > i8count = = 1 )
2005-04-17 02:20:36 +04:00
xfs_dir2_sf_toino4 ( args ) ;
else
2011-07-08 16:35:13 +04:00
sfp - > i8count - - ;
2005-04-17 02:20:36 +04: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 16:35:13 +04:00
ASSERT ( sfp - > i8count ! = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! i8elevated )
2011-07-08 16:35:13 +04:00
sfp - > i8count + + ;
2005-04-17 02:20:36 +04:00
}
# endif
xfs_dir2_sf_check ( args ) ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_DDATA ) ;
return 0 ;
}
# if XFS_BIG_INUMS
/*
* 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 */
{
char * buf ; /* old dir's buffer */
xfs_inode_t * dp ; /* incore directory inode */
int i ; /* entry index */
int newsize ; /* new inode size */
xfs_dir2_sf_entry_t * oldsfep ; /* old sf entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * oldsfp ; /* old sf directory */
2005-04-17 02:20:36 +04:00
int oldsize ; /* old inode size */
xfs_dir2_sf_entry_t * sfep ; /* new sf entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* new sf directory */
2013-08-12 14:50:10 +04:00
struct xfs_mount * mp ;
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_toino4 ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
2013-08-12 14:50:10 +04:00
mp = dp - > i_mount ;
2005-04-17 02:20:36 +04: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 ;
buf = kmem_alloc ( oldsize , KM_SLEEP ) ;
2011-07-08 16:35:13 +04:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
ASSERT ( oldsfp - > i8count = = 1 ) ;
2005-04-17 02:20:36 +04:00
memcpy ( buf , oldsfp , oldsize ) ;
/*
* Compute the new inode size .
*/
newsize =
oldsize -
2011-07-08 16:35:13 +04:00
( oldsfp - > count + 1 ) *
2005-04-17 02:20:36 +04:00
( ( uint ) sizeof ( xfs_dir2_ino8_t ) - ( uint ) sizeof ( xfs_dir2_ino4_t ) ) ;
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 16:35:13 +04:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) buf ;
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
/*
* Fill in the new header .
*/
2011-07-08 16:35:13 +04:00
sfp - > count = oldsfp - > count ;
sfp - > i8count = 0 ;
2011-07-08 16:35:03 +04:00
xfs_dir2_sf_put_parent_ino ( sfp , xfs_dir2_sf_get_parent_ino ( oldsfp ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Copy the entries field by field .
*/
2007-06-28 10:43:50 +04:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ,
oldsfep = xfs_dir2_sf_firstentry ( oldsfp ) ;
2011-07-08 16:35:13 +04:00
i < sfp - > count ;
2013-08-12 14:50:10 +04:00
i + + , sfep = xfs_dir3_sf_nextentry ( mp , sfp , sfep ) ,
oldsfep = xfs_dir3_sf_nextentry ( mp , oldsfp , oldsfep ) ) {
2005-04-17 02:20:36 +04:00
sfep - > namelen = oldsfep - > namelen ;
sfep - > offset = oldsfep - > offset ;
memcpy ( sfep - > name , oldsfep - > name , sfep - > namelen ) ;
2013-08-12 14:50:10 +04:00
xfs_dir3_sfe_put_ino ( mp , sfp , sfep ,
xfs_dir3_sfe_get_ino ( mp , oldsfp , oldsfep ) ) ;
xfs_dir3_sfe_put_ftype ( mp , sfp , sfep ,
xfs_dir3_sfe_get_ftype ( mp , oldsfp , oldsfep ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Clean up the inode .
*/
2008-05-19 10:31:57 +04:00
kmem_free ( buf ) ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = newsize ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
}
/*
* Convert from 4 - byte inode numbers to 8 - byte inode numbers .
* The new 8 - byte inode number is not there yet , we leave with the
* count 1 but no corresponding entry .
*/
static void
xfs_dir2_sf_toino8 (
xfs_da_args_t * args ) /* operation arguments */
{
char * buf ; /* old dir's buffer */
xfs_inode_t * dp ; /* incore directory inode */
int i ; /* entry index */
int newsize ; /* new inode size */
xfs_dir2_sf_entry_t * oldsfep ; /* old sf entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * oldsfp ; /* old sf directory */
2005-04-17 02:20:36 +04:00
int oldsize ; /* old inode size */
xfs_dir2_sf_entry_t * sfep ; /* new sf entry */
2011-07-08 16:35:13 +04:00
xfs_dir2_sf_hdr_t * sfp ; /* new sf directory */
2013-08-12 14:50:10 +04:00
struct xfs_mount * mp ;
2005-04-17 02:20:36 +04:00
2009-12-15 02:14:59 +03:00
trace_xfs_dir2_sf_toino8 ( args ) ;
2005-04-17 02:20:36 +04:00
dp = args - > dp ;
2013-08-12 14:50:10 +04:00
mp = dp - > i_mount ;
2005-04-17 02:20:36 +04: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 ;
buf = kmem_alloc ( oldsize , KM_SLEEP ) ;
2011-07-08 16:35:13 +04:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
ASSERT ( oldsfp - > i8count = = 0 ) ;
2005-04-17 02:20:36 +04:00
memcpy ( buf , oldsfp , oldsize ) ;
/*
* Compute the new inode size .
*/
newsize =
oldsize +
2011-07-08 16:35:13 +04:00
( oldsfp - > count + 1 ) *
2005-04-17 02:20:36 +04:00
( ( uint ) sizeof ( xfs_dir2_ino8_t ) - ( uint ) sizeof ( xfs_dir2_ino4_t ) ) ;
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 16:35:13 +04:00
oldsfp = ( xfs_dir2_sf_hdr_t * ) buf ;
sfp = ( xfs_dir2_sf_hdr_t * ) dp - > i_df . if_u1 . if_data ;
2005-04-17 02:20:36 +04:00
/*
* Fill in the new header .
*/
2011-07-08 16:35:13 +04:00
sfp - > count = oldsfp - > count ;
sfp - > i8count = 1 ;
2011-07-08 16:35:03 +04:00
xfs_dir2_sf_put_parent_ino ( sfp , xfs_dir2_sf_get_parent_ino ( oldsfp ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Copy the entries field by field .
*/
2007-06-28 10:43:50 +04:00
for ( i = 0 , sfep = xfs_dir2_sf_firstentry ( sfp ) ,
oldsfep = xfs_dir2_sf_firstentry ( oldsfp ) ;
2011-07-08 16:35:13 +04:00
i < sfp - > count ;
2013-08-12 14:50:10 +04:00
i + + , sfep = xfs_dir3_sf_nextentry ( mp , sfp , sfep ) ,
oldsfep = xfs_dir3_sf_nextentry ( mp , oldsfp , oldsfep ) ) {
2005-04-17 02:20:36 +04:00
sfep - > namelen = oldsfep - > namelen ;
sfep - > offset = oldsfep - > offset ;
memcpy ( sfep - > name , oldsfep - > name , sfep - > namelen ) ;
2013-08-12 14:50:10 +04:00
xfs_dir3_sfe_put_ino ( mp , sfp , sfep ,
xfs_dir3_sfe_get_ino ( mp , oldsfp , oldsfep ) ) ;
xfs_dir3_sfe_put_ftype ( mp , sfp , sfep ,
xfs_dir3_sfe_get_ftype ( mp , oldsfp , oldsfep ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Clean up the inode .
*/
2008-05-19 10:31:57 +04:00
kmem_free ( buf ) ;
2005-04-17 02:20:36 +04:00
dp - > i_d . di_size = newsize ;
xfs_trans_log_inode ( args - > trans , dp , XFS_ILOG_CORE | XFS_ILOG_DDATA ) ;
}
# endif /* XFS_BIG_INUMS */