18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. 48c2ecf20Sopenharmony_ci * All Rights Reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include "xfs.h" 78c2ecf20Sopenharmony_ci#include "xfs_fs.h" 88c2ecf20Sopenharmony_ci#include "xfs_shared.h" 98c2ecf20Sopenharmony_ci#include "xfs_format.h" 108c2ecf20Sopenharmony_ci#include "xfs_log_format.h" 118c2ecf20Sopenharmony_ci#include "xfs_trans_resv.h" 128c2ecf20Sopenharmony_ci#include "xfs_mount.h" 138c2ecf20Sopenharmony_ci#include "xfs_inode.h" 148c2ecf20Sopenharmony_ci#include "xfs_trans.h" 158c2ecf20Sopenharmony_ci#include "xfs_dir2.h" 168c2ecf20Sopenharmony_ci#include "xfs_dir2_priv.h" 178c2ecf20Sopenharmony_ci#include "xfs_trace.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * Prototypes for internal functions. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistatic void xfs_dir2_sf_addname_easy(xfs_da_args_t *args, 238c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep, 248c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t offset, 258c2ecf20Sopenharmony_ci int new_isize); 268c2ecf20Sopenharmony_cistatic void xfs_dir2_sf_addname_hard(xfs_da_args_t *args, int objchange, 278c2ecf20Sopenharmony_ci int new_isize); 288c2ecf20Sopenharmony_cistatic int xfs_dir2_sf_addname_pick(xfs_da_args_t *args, int objchange, 298c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t **sfepp, 308c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t *offsetp); 318c2ecf20Sopenharmony_ci#ifdef DEBUG 328c2ecf20Sopenharmony_cistatic void xfs_dir2_sf_check(xfs_da_args_t *args); 338c2ecf20Sopenharmony_ci#else 348c2ecf20Sopenharmony_ci#define xfs_dir2_sf_check(args) 358c2ecf20Sopenharmony_ci#endif /* DEBUG */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void xfs_dir2_sf_toino4(xfs_da_args_t *args); 388c2ecf20Sopenharmony_cistatic void xfs_dir2_sf_toino8(xfs_da_args_t *args); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ciint 418c2ecf20Sopenharmony_cixfs_dir2_sf_entsize( 428c2ecf20Sopenharmony_ci struct xfs_mount *mp, 438c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 448c2ecf20Sopenharmony_ci int len) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci int count = len; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci count += sizeof(struct xfs_dir2_sf_entry); /* namelen + offset */ 498c2ecf20Sopenharmony_ci count += hdr->i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE; /* ino # */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (xfs_sb_version_hasftype(&mp->m_sb)) 528c2ecf20Sopenharmony_ci count += sizeof(uint8_t); 538c2ecf20Sopenharmony_ci return count; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct xfs_dir2_sf_entry * 578c2ecf20Sopenharmony_cixfs_dir2_sf_nextentry( 588c2ecf20Sopenharmony_ci struct xfs_mount *mp, 598c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 608c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return (void *)sfep + xfs_dir2_sf_entsize(mp, hdr, sfep->namelen); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * In short-form directory entries the inode numbers are stored at variable 678c2ecf20Sopenharmony_ci * offset behind the entry name. If the entry stores a filetype value, then it 688c2ecf20Sopenharmony_ci * sits between the name and the inode number. The actual inode numbers can 698c2ecf20Sopenharmony_ci * come in two formats as well, either 4 bytes or 8 bytes wide. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_cixfs_ino_t 728c2ecf20Sopenharmony_cixfs_dir2_sf_get_ino( 738c2ecf20Sopenharmony_ci struct xfs_mount *mp, 748c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 758c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci uint8_t *from = sfep->name + sfep->namelen; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (xfs_sb_version_hasftype(&mp->m_sb)) 808c2ecf20Sopenharmony_ci from++; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (!hdr->i8count) 838c2ecf20Sopenharmony_ci return get_unaligned_be32(from); 848c2ecf20Sopenharmony_ci return get_unaligned_be64(from) & XFS_MAXINUMBER; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_civoid 888c2ecf20Sopenharmony_cixfs_dir2_sf_put_ino( 898c2ecf20Sopenharmony_ci struct xfs_mount *mp, 908c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 918c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep, 928c2ecf20Sopenharmony_ci xfs_ino_t ino) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci uint8_t *to = sfep->name + sfep->namelen; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ASSERT(ino <= XFS_MAXINUMBER); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (xfs_sb_version_hasftype(&mp->m_sb)) 998c2ecf20Sopenharmony_ci to++; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (hdr->i8count) 1028c2ecf20Sopenharmony_ci put_unaligned_be64(ino, to); 1038c2ecf20Sopenharmony_ci else 1048c2ecf20Sopenharmony_ci put_unaligned_be32(ino, to); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cixfs_ino_t 1088c2ecf20Sopenharmony_cixfs_dir2_sf_get_parent_ino( 1098c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci if (!hdr->i8count) 1128c2ecf20Sopenharmony_ci return get_unaligned_be32(hdr->parent); 1138c2ecf20Sopenharmony_ci return get_unaligned_be64(hdr->parent) & XFS_MAXINUMBER; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid 1178c2ecf20Sopenharmony_cixfs_dir2_sf_put_parent_ino( 1188c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 1198c2ecf20Sopenharmony_ci xfs_ino_t ino) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci ASSERT(ino <= XFS_MAXINUMBER); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (hdr->i8count) 1248c2ecf20Sopenharmony_ci put_unaligned_be64(ino, hdr->parent); 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci put_unaligned_be32(ino, hdr->parent); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * The file type field is stored at the end of the name for filetype enabled 1318c2ecf20Sopenharmony_ci * shortform directories, or not at all otherwise. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ciuint8_t 1348c2ecf20Sopenharmony_cixfs_dir2_sf_get_ftype( 1358c2ecf20Sopenharmony_ci struct xfs_mount *mp, 1368c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci if (xfs_sb_version_hasftype(&mp->m_sb)) { 1398c2ecf20Sopenharmony_ci uint8_t ftype = sfep->name[sfep->namelen]; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (ftype < XFS_DIR3_FT_MAX) 1428c2ecf20Sopenharmony_ci return ftype; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return XFS_DIR3_FT_UNKNOWN; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid 1498c2ecf20Sopenharmony_cixfs_dir2_sf_put_ftype( 1508c2ecf20Sopenharmony_ci struct xfs_mount *mp, 1518c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep, 1528c2ecf20Sopenharmony_ci uint8_t ftype) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci ASSERT(ftype < XFS_DIR3_FT_MAX); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (xfs_sb_version_hasftype(&mp->m_sb)) 1578c2ecf20Sopenharmony_ci sfep->name[sfep->namelen] = ftype; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * Given a block directory (dp/block), calculate its size as a shortform (sf) 1628c2ecf20Sopenharmony_ci * directory and a header for the sf directory, if it will fit it the 1638c2ecf20Sopenharmony_ci * space currently present in the inode. If it won't fit, the output 1648c2ecf20Sopenharmony_ci * size is too big (but not accurate). 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ciint /* size for sf form */ 1678c2ecf20Sopenharmony_cixfs_dir2_block_sfsize( 1688c2ecf20Sopenharmony_ci xfs_inode_t *dp, /* incore inode pointer */ 1698c2ecf20Sopenharmony_ci xfs_dir2_data_hdr_t *hdr, /* block directory data */ 1708c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfhp) /* output: header for sf form */ 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci xfs_dir2_dataptr_t addr; /* data entry address */ 1738c2ecf20Sopenharmony_ci xfs_dir2_leaf_entry_t *blp; /* leaf area of the block */ 1748c2ecf20Sopenharmony_ci xfs_dir2_block_tail_t *btp; /* tail area of the block */ 1758c2ecf20Sopenharmony_ci int count; /* shortform entry count */ 1768c2ecf20Sopenharmony_ci xfs_dir2_data_entry_t *dep; /* data entry in the block */ 1778c2ecf20Sopenharmony_ci int i; /* block entry index */ 1788c2ecf20Sopenharmony_ci int i8count; /* count of big-inode entries */ 1798c2ecf20Sopenharmony_ci int isdot; /* entry is "." */ 1808c2ecf20Sopenharmony_ci int isdotdot; /* entry is ".." */ 1818c2ecf20Sopenharmony_ci xfs_mount_t *mp; /* mount structure pointer */ 1828c2ecf20Sopenharmony_ci int namelen; /* total name bytes */ 1838c2ecf20Sopenharmony_ci xfs_ino_t parent = 0; /* parent inode number */ 1848c2ecf20Sopenharmony_ci int size=0; /* total computed size */ 1858c2ecf20Sopenharmony_ci int has_ftype; 1868c2ecf20Sopenharmony_ci struct xfs_da_geometry *geo; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mp = dp->i_mount; 1898c2ecf20Sopenharmony_ci geo = mp->m_dir_geo; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * if there is a filetype field, add the extra byte to the namelen 1938c2ecf20Sopenharmony_ci * for each entry that we see. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci has_ftype = xfs_sb_version_hasftype(&mp->m_sb) ? 1 : 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci count = i8count = namelen = 0; 1988c2ecf20Sopenharmony_ci btp = xfs_dir2_block_tail_p(geo, hdr); 1998c2ecf20Sopenharmony_ci blp = xfs_dir2_block_leaf_p(btp); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Iterate over the block's data entries by using the leaf pointers. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci for (i = 0; i < be32_to_cpu(btp->count); i++) { 2058c2ecf20Sopenharmony_ci if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR) 2068c2ecf20Sopenharmony_ci continue; 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * Calculate the pointer to the entry at hand. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci dep = (xfs_dir2_data_entry_t *)((char *)hdr + 2118c2ecf20Sopenharmony_ci xfs_dir2_dataptr_to_off(geo, addr)); 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Detect . and .., so we can special-case them. 2148c2ecf20Sopenharmony_ci * . is not included in sf directories. 2158c2ecf20Sopenharmony_ci * .. is included by just the parent inode number. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci isdot = dep->namelen == 1 && dep->name[0] == '.'; 2188c2ecf20Sopenharmony_ci isdotdot = 2198c2ecf20Sopenharmony_ci dep->namelen == 2 && 2208c2ecf20Sopenharmony_ci dep->name[0] == '.' && dep->name[1] == '.'; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (!isdot) 2238c2ecf20Sopenharmony_ci i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* take into account the file type field */ 2268c2ecf20Sopenharmony_ci if (!isdot && !isdotdot) { 2278c2ecf20Sopenharmony_ci count++; 2288c2ecf20Sopenharmony_ci namelen += dep->namelen + has_ftype; 2298c2ecf20Sopenharmony_ci } else if (isdotdot) 2308c2ecf20Sopenharmony_ci parent = be64_to_cpu(dep->inumber); 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * Calculate the new size, see if we should give up yet. 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci size = xfs_dir2_sf_hdr_size(i8count) + /* header */ 2358c2ecf20Sopenharmony_ci count * 3 * sizeof(u8) + /* namelen + offset */ 2368c2ecf20Sopenharmony_ci namelen + /* name */ 2378c2ecf20Sopenharmony_ci (i8count ? /* inumber */ 2388c2ecf20Sopenharmony_ci count * XFS_INO64_SIZE : 2398c2ecf20Sopenharmony_ci count * XFS_INO32_SIZE); 2408c2ecf20Sopenharmony_ci if (size > XFS_IFORK_DSIZE(dp)) 2418c2ecf20Sopenharmony_ci return size; /* size value is a failure */ 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * Create the output header, if it worked. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci sfhp->count = count; 2478c2ecf20Sopenharmony_ci sfhp->i8count = i8count; 2488c2ecf20Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfhp, parent); 2498c2ecf20Sopenharmony_ci return size; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* 2538c2ecf20Sopenharmony_ci * Convert a block format directory to shortform. 2548c2ecf20Sopenharmony_ci * Caller has already checked that it will fit, and built us a header. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ciint /* error */ 2578c2ecf20Sopenharmony_cixfs_dir2_block_to_sf( 2588c2ecf20Sopenharmony_ci struct xfs_da_args *args, /* operation arguments */ 2598c2ecf20Sopenharmony_ci struct xfs_buf *bp, 2608c2ecf20Sopenharmony_ci int size, /* shortform directory size */ 2618c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *sfhp) /* shortform directory hdr */ 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 2648c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 2658c2ecf20Sopenharmony_ci int error; /* error return value */ 2668c2ecf20Sopenharmony_ci int logflags; /* inode logging flags */ 2678c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep; /* shortform entry */ 2688c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; /* shortform directory header */ 2698c2ecf20Sopenharmony_ci unsigned int offset = args->geo->data_entry_offset; 2708c2ecf20Sopenharmony_ci unsigned int end; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci trace_xfs_dir2_block_to_sf(args); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Allocate a temporary destination buffer the size of the inode to 2768c2ecf20Sopenharmony_ci * format the data into. Once we have formatted the data, we can free 2778c2ecf20Sopenharmony_ci * the block and copy the formatted data into the inode literal area. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci sfp = kmem_alloc(mp->m_sb.sb_inodesize, 0); 2808c2ecf20Sopenharmony_ci memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count)); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * Loop over the active and unused entries. Stop when we reach the 2848c2ecf20Sopenharmony_ci * leaf/tail portion of the block. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci end = xfs_dir3_data_end_offset(args->geo, bp->b_addr); 2878c2ecf20Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 2888c2ecf20Sopenharmony_ci while (offset < end) { 2898c2ecf20Sopenharmony_ci struct xfs_dir2_data_unused *dup = bp->b_addr + offset; 2908c2ecf20Sopenharmony_ci struct xfs_dir2_data_entry *dep = bp->b_addr + offset; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * If it's unused, just skip over it. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 2968c2ecf20Sopenharmony_ci offset += be16_to_cpu(dup->length); 2978c2ecf20Sopenharmony_ci continue; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Skip . 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci if (dep->namelen == 1 && dep->name[0] == '.') 3048c2ecf20Sopenharmony_ci ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino); 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * Skip .., but make sure the inode number is right. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci else if (dep->namelen == 2 && 3098c2ecf20Sopenharmony_ci dep->name[0] == '.' && dep->name[1] == '.') 3108c2ecf20Sopenharmony_ci ASSERT(be64_to_cpu(dep->inumber) == 3118c2ecf20Sopenharmony_ci xfs_dir2_sf_get_parent_ino(sfp)); 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * Normal entry, copy it into shortform. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci else { 3168c2ecf20Sopenharmony_ci sfep->namelen = dep->namelen; 3178c2ecf20Sopenharmony_ci xfs_dir2_sf_put_offset(sfep, offset); 3188c2ecf20Sopenharmony_ci memcpy(sfep->name, dep->name, dep->namelen); 3198c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 3208c2ecf20Sopenharmony_ci be64_to_cpu(dep->inumber)); 3218c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, 3228c2ecf20Sopenharmony_ci xfs_dir2_data_get_ftype(mp, dep)); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci offset += xfs_dir2_data_entsize(mp, dep->namelen); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci ASSERT((char *)sfep - (char *)sfp == size); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* now we are done with the block, we can shrink the inode */ 3318c2ecf20Sopenharmony_ci logflags = XFS_ILOG_CORE; 3328c2ecf20Sopenharmony_ci error = xfs_dir2_shrink_inode(args, args->geo->datablk, bp); 3338c2ecf20Sopenharmony_ci if (error) { 3348c2ecf20Sopenharmony_ci ASSERT(error != -ENOSPC); 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * The buffer is now unconditionally gone, whether 3408c2ecf20Sopenharmony_ci * xfs_dir2_shrink_inode worked or not. 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * Convert the inode to local format and copy the data in. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_bytes == 0); 3458c2ecf20Sopenharmony_ci xfs_init_local_fork(dp, XFS_DATA_FORK, sfp, size); 3468c2ecf20Sopenharmony_ci dp->i_df.if_format = XFS_DINODE_FMT_LOCAL; 3478c2ecf20Sopenharmony_ci dp->i_d.di_size = size; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci logflags |= XFS_ILOG_DDATA; 3508c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 3518c2ecf20Sopenharmony_ciout: 3528c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, logflags); 3538c2ecf20Sopenharmony_ci kmem_free(sfp); 3548c2ecf20Sopenharmony_ci return error; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci * Add a name to a shortform directory. 3598c2ecf20Sopenharmony_ci * There are two algorithms, "easy" and "hard" which we decide on 3608c2ecf20Sopenharmony_ci * before changing anything. 3618c2ecf20Sopenharmony_ci * Convert to block form if necessary, if the new entry won't fit. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ciint /* error */ 3648c2ecf20Sopenharmony_cixfs_dir2_sf_addname( 3658c2ecf20Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci xfs_inode_t *dp; /* incore directory inode */ 3688c2ecf20Sopenharmony_ci int error; /* error return value */ 3698c2ecf20Sopenharmony_ci int incr_isize; /* total change in size */ 3708c2ecf20Sopenharmony_ci int new_isize; /* di_size after adding name */ 3718c2ecf20Sopenharmony_ci int objchange; /* changing to 8-byte inodes */ 3728c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t offset = 0; /* offset for new entry */ 3738c2ecf20Sopenharmony_ci int pick; /* which algorithm to use */ 3748c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 3758c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep = NULL; /* shortform entry */ 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_addname(args); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ASSERT(xfs_dir2_sf_lookup(args) == -ENOENT); 3808c2ecf20Sopenharmony_ci dp = args->dp; 3818c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_flags & XFS_IFINLINE); 3828c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size >= offsetof(struct xfs_dir2_sf_hdr, parent)); 3838c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); 3848c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 3858c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 3868c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * Compute entry (and change in) size. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci incr_isize = xfs_dir2_sf_entsize(dp->i_mount, sfp, args->namelen); 3918c2ecf20Sopenharmony_ci objchange = 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Do we have to change to 8 byte inodes? 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) { 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * Yes, adjust the inode size. old count + (parent + new) 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci incr_isize += (sfp->count + 2) * XFS_INO64_DIFF; 4018c2ecf20Sopenharmony_ci objchange = 1; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci new_isize = (int)dp->i_d.di_size + incr_isize; 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Won't fit as shortform any more (due to size), 4078c2ecf20Sopenharmony_ci * or the pick routine says it won't (due to offset values). 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci if (new_isize > XFS_IFORK_DSIZE(dp) || 4108c2ecf20Sopenharmony_ci (pick = 4118c2ecf20Sopenharmony_ci xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) { 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * Just checking or no space reservation, it doesn't fit. 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0) 4168c2ecf20Sopenharmony_ci return -ENOSPC; 4178c2ecf20Sopenharmony_ci /* 4188c2ecf20Sopenharmony_ci * Convert to block form then add the name. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci error = xfs_dir2_sf_to_block(args); 4218c2ecf20Sopenharmony_ci if (error) 4228c2ecf20Sopenharmony_ci return error; 4238c2ecf20Sopenharmony_ci return xfs_dir2_block_addname(args); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci /* 4268c2ecf20Sopenharmony_ci * Just checking, it fits. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci if (args->op_flags & XFS_DA_OP_JUSTCHECK) 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * Do it the easy way - just add it at the end. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci if (pick == 1) 4348c2ecf20Sopenharmony_ci xfs_dir2_sf_addname_easy(args, sfep, offset, new_isize); 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * Do it the hard way - look for a place to insert the new entry. 4378c2ecf20Sopenharmony_ci * Convert to 8 byte inode numbers first if necessary. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci else { 4408c2ecf20Sopenharmony_ci ASSERT(pick == 2); 4418c2ecf20Sopenharmony_ci if (objchange) 4428c2ecf20Sopenharmony_ci xfs_dir2_sf_toino8(args); 4438c2ecf20Sopenharmony_ci xfs_dir2_sf_addname_hard(args, objchange, new_isize); 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* 4508c2ecf20Sopenharmony_ci * Add the new entry the "easy" way. 4518c2ecf20Sopenharmony_ci * This is copying the old directory and adding the new entry at the end. 4528c2ecf20Sopenharmony_ci * Since it's sorted by "offset" we need room after the last offset 4538c2ecf20Sopenharmony_ci * that's already there, and then room to convert to a block directory. 4548c2ecf20Sopenharmony_ci * This is already checked by the pick routine. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_cistatic void 4578c2ecf20Sopenharmony_cixfs_dir2_sf_addname_easy( 4588c2ecf20Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 4598c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep, /* pointer to new entry */ 4608c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t offset, /* offset to use for new ent */ 4618c2ecf20Sopenharmony_ci int new_isize) /* new directory size */ 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 4648c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 4658c2ecf20Sopenharmony_ci int byteoff; /* byte offset in sf dir */ 4668c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 4698c2ecf20Sopenharmony_ci byteoff = (int)((char *)sfep - (char *)sfp); 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Grow the in-inode space. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, xfs_dir2_sf_entsize(mp, sfp, args->namelen), 4748c2ecf20Sopenharmony_ci XFS_DATA_FORK); 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * Need to set up again due to realloc of the inode data. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 4798c2ecf20Sopenharmony_ci sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff); 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * Fill in the new entry. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci sfep->namelen = args->namelen; 4848c2ecf20Sopenharmony_ci xfs_dir2_sf_put_offset(sfep, offset); 4858c2ecf20Sopenharmony_ci memcpy(sfep->name, args->name, sfep->namelen); 4868c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber); 4878c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, args->filetype); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * Update the header and inode. 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci sfp->count++; 4938c2ecf20Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) 4948c2ecf20Sopenharmony_ci sfp->i8count++; 4958c2ecf20Sopenharmony_ci dp->i_d.di_size = new_isize; 4968c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* 5008c2ecf20Sopenharmony_ci * Add the new entry the "hard" way. 5018c2ecf20Sopenharmony_ci * The caller has already converted to 8 byte inode numbers if necessary, 5028c2ecf20Sopenharmony_ci * in which case we need to leave the i8count at 1. 5038c2ecf20Sopenharmony_ci * Find a hole that the new entry will fit into, and copy 5048c2ecf20Sopenharmony_ci * the first part of the entries, the new entry, and the last part of 5058c2ecf20Sopenharmony_ci * the entries. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci/* ARGSUSED */ 5088c2ecf20Sopenharmony_cistatic void 5098c2ecf20Sopenharmony_cixfs_dir2_sf_addname_hard( 5108c2ecf20Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 5118c2ecf20Sopenharmony_ci int objchange, /* changing inode number size */ 5128c2ecf20Sopenharmony_ci int new_isize) /* new directory size */ 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 5158c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 5168c2ecf20Sopenharmony_ci int add_datasize; /* data size need for new ent */ 5178c2ecf20Sopenharmony_ci char *buf; /* buffer for old */ 5188c2ecf20Sopenharmony_ci int eof; /* reached end of old dir */ 5198c2ecf20Sopenharmony_ci int nbytes; /* temp for byte copies */ 5208c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t new_offset; /* next offset value */ 5218c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t offset; /* current offset value */ 5228c2ecf20Sopenharmony_ci int old_isize; /* previous di_size */ 5238c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *oldsfep; /* entry in original dir */ 5248c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *oldsfp; /* original shortform dir */ 5258c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* entry in new dir */ 5268c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* new shortform dir */ 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * Copy the old directory to the stack buffer. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 5328c2ecf20Sopenharmony_ci old_isize = (int)dp->i_d.di_size; 5338c2ecf20Sopenharmony_ci buf = kmem_alloc(old_isize, 0); 5348c2ecf20Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)buf; 5358c2ecf20Sopenharmony_ci memcpy(oldsfp, sfp, old_isize); 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * Loop over the old directory finding the place we're going 5388c2ecf20Sopenharmony_ci * to insert the new entry. 5398c2ecf20Sopenharmony_ci * If it's going to end up at the end then oldsfep will point there. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci for (offset = args->geo->data_first_offset, 5428c2ecf20Sopenharmony_ci oldsfep = xfs_dir2_sf_firstentry(oldsfp), 5438c2ecf20Sopenharmony_ci add_datasize = xfs_dir2_data_entsize(mp, args->namelen), 5448c2ecf20Sopenharmony_ci eof = (char *)oldsfep == &buf[old_isize]; 5458c2ecf20Sopenharmony_ci !eof; 5468c2ecf20Sopenharmony_ci offset = new_offset + xfs_dir2_data_entsize(mp, oldsfep->namelen), 5478c2ecf20Sopenharmony_ci oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep), 5488c2ecf20Sopenharmony_ci eof = (char *)oldsfep == &buf[old_isize]) { 5498c2ecf20Sopenharmony_ci new_offset = xfs_dir2_sf_get_offset(oldsfep); 5508c2ecf20Sopenharmony_ci if (offset + add_datasize <= new_offset) 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci /* 5548c2ecf20Sopenharmony_ci * Get rid of the old directory, then allocate space for 5558c2ecf20Sopenharmony_ci * the new one. We do this so xfs_idata_realloc won't copy 5568c2ecf20Sopenharmony_ci * the data. 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, -old_isize, XFS_DATA_FORK); 5598c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, new_isize, XFS_DATA_FORK); 5608c2ecf20Sopenharmony_ci /* 5618c2ecf20Sopenharmony_ci * Reset the pointer since the buffer was reallocated. 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 5648c2ecf20Sopenharmony_ci /* 5658c2ecf20Sopenharmony_ci * Copy the first part of the directory, including the header. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci nbytes = (int)((char *)oldsfep - (char *)oldsfp); 5688c2ecf20Sopenharmony_ci memcpy(sfp, oldsfp, nbytes); 5698c2ecf20Sopenharmony_ci sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + nbytes); 5708c2ecf20Sopenharmony_ci /* 5718c2ecf20Sopenharmony_ci * Fill in the new entry, and update the header counts. 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci sfep->namelen = args->namelen; 5748c2ecf20Sopenharmony_ci xfs_dir2_sf_put_offset(sfep, offset); 5758c2ecf20Sopenharmony_ci memcpy(sfep->name, args->name, sfep->namelen); 5768c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber); 5778c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, args->filetype); 5788c2ecf20Sopenharmony_ci sfp->count++; 5798c2ecf20Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange) 5808c2ecf20Sopenharmony_ci sfp->i8count++; 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * If there's more left to copy, do that. 5838c2ecf20Sopenharmony_ci */ 5848c2ecf20Sopenharmony_ci if (!eof) { 5858c2ecf20Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 5868c2ecf20Sopenharmony_ci memcpy(sfep, oldsfep, old_isize - nbytes); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci kmem_free(buf); 5898c2ecf20Sopenharmony_ci dp->i_d.di_size = new_isize; 5908c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/* 5948c2ecf20Sopenharmony_ci * Decide if the new entry will fit at all. 5958c2ecf20Sopenharmony_ci * If it will fit, pick between adding the new entry to the end (easy) 5968c2ecf20Sopenharmony_ci * or somewhere else (hard). 5978c2ecf20Sopenharmony_ci * Return 0 (won't fit), 1 (easy), 2 (hard). 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci/*ARGSUSED*/ 6008c2ecf20Sopenharmony_cistatic int /* pick result */ 6018c2ecf20Sopenharmony_cixfs_dir2_sf_addname_pick( 6028c2ecf20Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 6038c2ecf20Sopenharmony_ci int objchange, /* inode # size changes */ 6048c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t **sfepp, /* out(1): new entry ptr */ 6058c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t *offsetp) /* out(1): new offset */ 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 6088c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 6098c2ecf20Sopenharmony_ci int holefit; /* found hole it will fit in */ 6108c2ecf20Sopenharmony_ci int i; /* entry number */ 6118c2ecf20Sopenharmony_ci xfs_dir2_data_aoff_t offset; /* data block offset */ 6128c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform entry */ 6138c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 6148c2ecf20Sopenharmony_ci int size; /* entry's data size */ 6158c2ecf20Sopenharmony_ci int used; /* data bytes used */ 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 6188c2ecf20Sopenharmony_ci size = xfs_dir2_data_entsize(mp, args->namelen); 6198c2ecf20Sopenharmony_ci offset = args->geo->data_first_offset; 6208c2ecf20Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 6218c2ecf20Sopenharmony_ci holefit = 0; 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * Loop over sf entries. 6248c2ecf20Sopenharmony_ci * Keep track of data offset and whether we've seen a place 6258c2ecf20Sopenharmony_ci * to insert the new entry. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci for (i = 0; i < sfp->count; i++) { 6288c2ecf20Sopenharmony_ci if (!holefit) 6298c2ecf20Sopenharmony_ci holefit = offset + size <= xfs_dir2_sf_get_offset(sfep); 6308c2ecf20Sopenharmony_ci offset = xfs_dir2_sf_get_offset(sfep) + 6318c2ecf20Sopenharmony_ci xfs_dir2_data_entsize(mp, sfep->namelen); 6328c2ecf20Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * Calculate data bytes used excluding the new entry, if this 6368c2ecf20Sopenharmony_ci * was a data block (block form directory). 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci used = offset + 6398c2ecf20Sopenharmony_ci (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 6408c2ecf20Sopenharmony_ci (uint)sizeof(xfs_dir2_block_tail_t); 6418c2ecf20Sopenharmony_ci /* 6428c2ecf20Sopenharmony_ci * If it won't fit in a block form then we can't insert it, 6438c2ecf20Sopenharmony_ci * we'll go back, convert to block, then try the insert and convert 6448c2ecf20Sopenharmony_ci * to leaf. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci if (used + (holefit ? 0 : size) > args->geo->blksize) 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci /* 6498c2ecf20Sopenharmony_ci * If changing the inode number size, do it the hard way. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci if (objchange) 6528c2ecf20Sopenharmony_ci return 2; 6538c2ecf20Sopenharmony_ci /* 6548c2ecf20Sopenharmony_ci * If it won't fit at the end then do it the hard way (use the hole). 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_ci if (used + size > args->geo->blksize) 6578c2ecf20Sopenharmony_ci return 2; 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * Do it the easy way. 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_ci *sfepp = sfep; 6628c2ecf20Sopenharmony_ci *offsetp = offset; 6638c2ecf20Sopenharmony_ci return 1; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci#ifdef DEBUG 6678c2ecf20Sopenharmony_ci/* 6688c2ecf20Sopenharmony_ci * Check consistency of shortform directory, assert if bad. 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic void 6718c2ecf20Sopenharmony_cixfs_dir2_sf_check( 6728c2ecf20Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 6758c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 6768c2ecf20Sopenharmony_ci int i; /* entry number */ 6778c2ecf20Sopenharmony_ci int i8count; /* number of big inode#s */ 6788c2ecf20Sopenharmony_ci xfs_ino_t ino; /* entry inode number */ 6798c2ecf20Sopenharmony_ci int offset; /* data offset */ 6808c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform dir entry */ 6818c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 6848c2ecf20Sopenharmony_ci offset = args->geo->data_first_offset; 6858c2ecf20Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 6868c2ecf20Sopenharmony_ci i8count = ino > XFS_DIR2_MAX_SHORT_INUM; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); 6898c2ecf20Sopenharmony_ci i < sfp->count; 6908c2ecf20Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 6918c2ecf20Sopenharmony_ci ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset); 6928c2ecf20Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 6938c2ecf20Sopenharmony_ci i8count += ino > XFS_DIR2_MAX_SHORT_INUM; 6948c2ecf20Sopenharmony_ci offset = 6958c2ecf20Sopenharmony_ci xfs_dir2_sf_get_offset(sfep) + 6968c2ecf20Sopenharmony_ci xfs_dir2_data_entsize(mp, sfep->namelen); 6978c2ecf20Sopenharmony_ci ASSERT(xfs_dir2_sf_get_ftype(mp, sfep) < XFS_DIR3_FT_MAX); 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci ASSERT(i8count == sfp->i8count); 7008c2ecf20Sopenharmony_ci ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size); 7018c2ecf20Sopenharmony_ci ASSERT(offset + 7028c2ecf20Sopenharmony_ci (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 7038c2ecf20Sopenharmony_ci (uint)sizeof(xfs_dir2_block_tail_t) <= args->geo->blksize); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci#endif /* DEBUG */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci/* Verify the consistency of an inline directory. */ 7088c2ecf20Sopenharmony_cixfs_failaddr_t 7098c2ecf20Sopenharmony_cixfs_dir2_sf_verify( 7108c2ecf20Sopenharmony_ci struct xfs_inode *ip) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 7138c2ecf20Sopenharmony_ci struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); 7148c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; 7158c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *sfep; 7168c2ecf20Sopenharmony_ci struct xfs_dir2_sf_entry *next_sfep; 7178c2ecf20Sopenharmony_ci char *endp; 7188c2ecf20Sopenharmony_ci xfs_ino_t ino; 7198c2ecf20Sopenharmony_ci int i; 7208c2ecf20Sopenharmony_ci int i8count; 7218c2ecf20Sopenharmony_ci int offset; 7228c2ecf20Sopenharmony_ci int64_t size; 7238c2ecf20Sopenharmony_ci int error; 7248c2ecf20Sopenharmony_ci uint8_t filetype; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data; 7298c2ecf20Sopenharmony_ci size = ifp->if_bytes; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * Give up if the directory is way too short. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci if (size <= offsetof(struct xfs_dir2_sf_hdr, parent) || 7358c2ecf20Sopenharmony_ci size < xfs_dir2_sf_hdr_size(sfp->i8count)) 7368c2ecf20Sopenharmony_ci return __this_address; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci endp = (char *)sfp + size; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* Check .. entry */ 7418c2ecf20Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 7428c2ecf20Sopenharmony_ci i8count = ino > XFS_DIR2_MAX_SHORT_INUM; 7438c2ecf20Sopenharmony_ci error = xfs_dir_ino_validate(mp, ino); 7448c2ecf20Sopenharmony_ci if (error) 7458c2ecf20Sopenharmony_ci return __this_address; 7468c2ecf20Sopenharmony_ci offset = mp->m_dir_geo->data_first_offset; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* Check all reported entries */ 7498c2ecf20Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 7508c2ecf20Sopenharmony_ci for (i = 0; i < sfp->count; i++) { 7518c2ecf20Sopenharmony_ci /* 7528c2ecf20Sopenharmony_ci * struct xfs_dir2_sf_entry has a variable length. 7538c2ecf20Sopenharmony_ci * Check the fixed-offset parts of the structure are 7548c2ecf20Sopenharmony_ci * within the data buffer. 7558c2ecf20Sopenharmony_ci */ 7568c2ecf20Sopenharmony_ci if (((char *)sfep + sizeof(*sfep)) >= endp) 7578c2ecf20Sopenharmony_ci return __this_address; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* Don't allow names with known bad length. */ 7608c2ecf20Sopenharmony_ci if (sfep->namelen == 0) 7618c2ecf20Sopenharmony_ci return __this_address; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* 7648c2ecf20Sopenharmony_ci * Check that the variable-length part of the structure is 7658c2ecf20Sopenharmony_ci * within the data buffer. The next entry starts after the 7668c2ecf20Sopenharmony_ci * name component, so nextentry is an acceptable test. 7678c2ecf20Sopenharmony_ci */ 7688c2ecf20Sopenharmony_ci next_sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 7698c2ecf20Sopenharmony_ci if (endp < (char *)next_sfep) 7708c2ecf20Sopenharmony_ci return __this_address; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* Check that the offsets always increase. */ 7738c2ecf20Sopenharmony_ci if (xfs_dir2_sf_get_offset(sfep) < offset) 7748c2ecf20Sopenharmony_ci return __this_address; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* Check the inode number. */ 7778c2ecf20Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 7788c2ecf20Sopenharmony_ci i8count += ino > XFS_DIR2_MAX_SHORT_INUM; 7798c2ecf20Sopenharmony_ci error = xfs_dir_ino_validate(mp, ino); 7808c2ecf20Sopenharmony_ci if (error) 7818c2ecf20Sopenharmony_ci return __this_address; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* Check the file type. */ 7848c2ecf20Sopenharmony_ci filetype = xfs_dir2_sf_get_ftype(mp, sfep); 7858c2ecf20Sopenharmony_ci if (filetype >= XFS_DIR3_FT_MAX) 7868c2ecf20Sopenharmony_ci return __this_address; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci offset = xfs_dir2_sf_get_offset(sfep) + 7898c2ecf20Sopenharmony_ci xfs_dir2_data_entsize(mp, sfep->namelen); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci sfep = next_sfep; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci if (i8count != sfp->i8count) 7948c2ecf20Sopenharmony_ci return __this_address; 7958c2ecf20Sopenharmony_ci if ((void *)sfep != (void *)endp) 7968c2ecf20Sopenharmony_ci return __this_address; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* Make sure this whole thing ought to be in local format. */ 7998c2ecf20Sopenharmony_ci if (offset + (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 8008c2ecf20Sopenharmony_ci (uint)sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) 8018c2ecf20Sopenharmony_ci return __this_address; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return NULL; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci/* 8078c2ecf20Sopenharmony_ci * Create a new (shortform) directory. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ciint /* error, always 0 */ 8108c2ecf20Sopenharmony_cixfs_dir2_sf_create( 8118c2ecf20Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 8128c2ecf20Sopenharmony_ci xfs_ino_t pino) /* parent inode number */ 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci xfs_inode_t *dp; /* incore directory inode */ 8158c2ecf20Sopenharmony_ci int i8count; /* parent inode is an 8-byte number */ 8168c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 8178c2ecf20Sopenharmony_ci int size; /* directory size */ 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_create(args); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci dp = args->dp; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci ASSERT(dp != NULL); 8248c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size == 0); 8258c2ecf20Sopenharmony_ci /* 8268c2ecf20Sopenharmony_ci * If it's currently a zero-length extent file, 8278c2ecf20Sopenharmony_ci * convert it to local format. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_ci if (dp->i_df.if_format == XFS_DINODE_FMT_EXTENTS) { 8308c2ecf20Sopenharmony_ci dp->i_df.if_flags &= ~XFS_IFEXTENTS; /* just in case */ 8318c2ecf20Sopenharmony_ci dp->i_df.if_format = XFS_DINODE_FMT_LOCAL; 8328c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); 8338c2ecf20Sopenharmony_ci dp->i_df.if_flags |= XFS_IFINLINE; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_flags & XFS_IFINLINE); 8368c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_bytes == 0); 8378c2ecf20Sopenharmony_ci i8count = pino > XFS_DIR2_MAX_SHORT_INUM; 8388c2ecf20Sopenharmony_ci size = xfs_dir2_sf_hdr_size(i8count); 8398c2ecf20Sopenharmony_ci /* 8408c2ecf20Sopenharmony_ci * Make a buffer for the data. 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, size, XFS_DATA_FORK); 8438c2ecf20Sopenharmony_ci /* 8448c2ecf20Sopenharmony_ci * Fill in the header, 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 8478c2ecf20Sopenharmony_ci sfp->i8count = i8count; 8488c2ecf20Sopenharmony_ci /* 8498c2ecf20Sopenharmony_ci * Now can put in the inode number, since i8count is set. 8508c2ecf20Sopenharmony_ci */ 8518c2ecf20Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, pino); 8528c2ecf20Sopenharmony_ci sfp->count = 0; 8538c2ecf20Sopenharmony_ci dp->i_d.di_size = size; 8548c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 8558c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* 8608c2ecf20Sopenharmony_ci * Lookup an entry in a shortform directory. 8618c2ecf20Sopenharmony_ci * Returns EEXIST if found, ENOENT if not found. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ciint /* error */ 8648c2ecf20Sopenharmony_cixfs_dir2_sf_lookup( 8658c2ecf20Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 8688c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 8698c2ecf20Sopenharmony_ci int i; /* entry index */ 8708c2ecf20Sopenharmony_ci int error; 8718c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 8728c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 8738c2ecf20Sopenharmony_ci enum xfs_dacmp cmp; /* comparison result */ 8748c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *ci_sfep; /* case-insens. entry */ 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_lookup(args); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_flags & XFS_IFINLINE); 8818c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size >= offsetof(struct xfs_dir2_sf_hdr, parent)); 8828c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); 8838c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 8848c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 8858c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); 8868c2ecf20Sopenharmony_ci /* 8878c2ecf20Sopenharmony_ci * Special case for . 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci if (args->namelen == 1 && args->name[0] == '.') { 8908c2ecf20Sopenharmony_ci args->inumber = dp->i_ino; 8918c2ecf20Sopenharmony_ci args->cmpresult = XFS_CMP_EXACT; 8928c2ecf20Sopenharmony_ci args->filetype = XFS_DIR3_FT_DIR; 8938c2ecf20Sopenharmony_ci return -EEXIST; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci /* 8968c2ecf20Sopenharmony_ci * Special case for .. 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_ci if (args->namelen == 2 && 8998c2ecf20Sopenharmony_ci args->name[0] == '.' && args->name[1] == '.') { 9008c2ecf20Sopenharmony_ci args->inumber = xfs_dir2_sf_get_parent_ino(sfp); 9018c2ecf20Sopenharmony_ci args->cmpresult = XFS_CMP_EXACT; 9028c2ecf20Sopenharmony_ci args->filetype = XFS_DIR3_FT_DIR; 9038c2ecf20Sopenharmony_ci return -EEXIST; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci /* 9068c2ecf20Sopenharmony_ci * Loop over all the entries trying to match ours. 9078c2ecf20Sopenharmony_ci */ 9088c2ecf20Sopenharmony_ci ci_sfep = NULL; 9098c2ecf20Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; 9108c2ecf20Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 9118c2ecf20Sopenharmony_ci /* 9128c2ecf20Sopenharmony_ci * Compare name and if it's an exact match, return the inode 9138c2ecf20Sopenharmony_ci * number. If it's the first case-insensitive match, store the 9148c2ecf20Sopenharmony_ci * inode number and continue looking for an exact match. 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci cmp = xfs_dir2_compname(args, sfep->name, sfep->namelen); 9178c2ecf20Sopenharmony_ci if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { 9188c2ecf20Sopenharmony_ci args->cmpresult = cmp; 9198c2ecf20Sopenharmony_ci args->inumber = xfs_dir2_sf_get_ino(mp, sfp, sfep); 9208c2ecf20Sopenharmony_ci args->filetype = xfs_dir2_sf_get_ftype(mp, sfep); 9218c2ecf20Sopenharmony_ci if (cmp == XFS_CMP_EXACT) 9228c2ecf20Sopenharmony_ci return -EEXIST; 9238c2ecf20Sopenharmony_ci ci_sfep = sfep; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * Here, we can only be doing a lookup (not a rename or replace). 9298c2ecf20Sopenharmony_ci * If a case-insensitive match was not found, return -ENOENT. 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci if (!ci_sfep) 9328c2ecf20Sopenharmony_ci return -ENOENT; 9338c2ecf20Sopenharmony_ci /* otherwise process the CI match as required by the caller */ 9348c2ecf20Sopenharmony_ci error = xfs_dir_cilookup_result(args, ci_sfep->name, ci_sfep->namelen); 9358c2ecf20Sopenharmony_ci return error; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci/* 9398c2ecf20Sopenharmony_ci * Remove an entry from a shortform directory. 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_ciint /* error */ 9428c2ecf20Sopenharmony_cixfs_dir2_sf_removename( 9438c2ecf20Sopenharmony_ci xfs_da_args_t *args) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 9468c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 9478c2ecf20Sopenharmony_ci int byteoff; /* offset of removed entry */ 9488c2ecf20Sopenharmony_ci int entsize; /* this entry's size */ 9498c2ecf20Sopenharmony_ci int i; /* shortform entry index */ 9508c2ecf20Sopenharmony_ci int newsize; /* new inode size */ 9518c2ecf20Sopenharmony_ci int oldsize; /* old inode size */ 9528c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 9538c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_removename(args); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_flags & XFS_IFINLINE); 9588c2ecf20Sopenharmony_ci oldsize = (int)dp->i_d.di_size; 9598c2ecf20Sopenharmony_ci ASSERT(oldsize >= offsetof(struct xfs_dir2_sf_hdr, parent)); 9608c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_bytes == oldsize); 9618c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 9628c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 9638c2ecf20Sopenharmony_ci ASSERT(oldsize >= xfs_dir2_sf_hdr_size(sfp->i8count)); 9648c2ecf20Sopenharmony_ci /* 9658c2ecf20Sopenharmony_ci * Loop over the old directory entries. 9668c2ecf20Sopenharmony_ci * Find the one we're deleting. 9678c2ecf20Sopenharmony_ci */ 9688c2ecf20Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; 9698c2ecf20Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 9708c2ecf20Sopenharmony_ci if (xfs_da_compname(args, sfep->name, sfep->namelen) == 9718c2ecf20Sopenharmony_ci XFS_CMP_EXACT) { 9728c2ecf20Sopenharmony_ci ASSERT(xfs_dir2_sf_get_ino(mp, sfp, sfep) == 9738c2ecf20Sopenharmony_ci args->inumber); 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci /* 9788c2ecf20Sopenharmony_ci * Didn't find it. 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_ci if (i == sfp->count) 9818c2ecf20Sopenharmony_ci return -ENOENT; 9828c2ecf20Sopenharmony_ci /* 9838c2ecf20Sopenharmony_ci * Calculate sizes. 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_ci byteoff = (int)((char *)sfep - (char *)sfp); 9868c2ecf20Sopenharmony_ci entsize = xfs_dir2_sf_entsize(mp, sfp, args->namelen); 9878c2ecf20Sopenharmony_ci newsize = oldsize - entsize; 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * Copy the part if any after the removed entry, sliding it down. 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_ci if (byteoff + entsize < oldsize) 9928c2ecf20Sopenharmony_ci memmove((char *)sfp + byteoff, (char *)sfp + byteoff + entsize, 9938c2ecf20Sopenharmony_ci oldsize - (byteoff + entsize)); 9948c2ecf20Sopenharmony_ci /* 9958c2ecf20Sopenharmony_ci * Fix up the header and file size. 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ci sfp->count--; 9988c2ecf20Sopenharmony_ci dp->i_d.di_size = newsize; 9998c2ecf20Sopenharmony_ci /* 10008c2ecf20Sopenharmony_ci * Reallocate, making it smaller. 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK); 10038c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * Are we changing inode number size? 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) { 10088c2ecf20Sopenharmony_ci if (sfp->i8count == 1) 10098c2ecf20Sopenharmony_ci xfs_dir2_sf_toino4(args); 10108c2ecf20Sopenharmony_ci else 10118c2ecf20Sopenharmony_ci sfp->i8count--; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 10148c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci/* 10198c2ecf20Sopenharmony_ci * Check whether the sf dir replace operation need more blocks. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_cistatic bool 10228c2ecf20Sopenharmony_cixfs_dir2_sf_replace_needblock( 10238c2ecf20Sopenharmony_ci struct xfs_inode *dp, 10248c2ecf20Sopenharmony_ci xfs_ino_t inum) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci int newsize; 10278c2ecf20Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (dp->i_df.if_format != XFS_DINODE_FMT_LOCAL) 10308c2ecf20Sopenharmony_ci return false; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data; 10338c2ecf20Sopenharmony_ci newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return inum > XFS_DIR2_MAX_SHORT_INUM && 10368c2ecf20Sopenharmony_ci sfp->i8count == 0 && newsize > XFS_IFORK_DSIZE(dp); 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci/* 10408c2ecf20Sopenharmony_ci * Replace the inode number of an entry in a shortform directory. 10418c2ecf20Sopenharmony_ci */ 10428c2ecf20Sopenharmony_ciint /* error */ 10438c2ecf20Sopenharmony_cixfs_dir2_sf_replace( 10448c2ecf20Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 10478c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 10488c2ecf20Sopenharmony_ci int i; /* entry index */ 10498c2ecf20Sopenharmony_ci xfs_ino_t ino=0; /* entry old inode number */ 10508c2ecf20Sopenharmony_ci int i8elevated; /* sf_toino8 set i8count=1 */ 10518c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 10528c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_replace(args); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_flags & XFS_IFINLINE); 10578c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size >= offsetof(struct xfs_dir2_sf_hdr, parent)); 10588c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); 10598c2ecf20Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 10608c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 10618c2ecf20Sopenharmony_ci ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* 10648c2ecf20Sopenharmony_ci * New inode number is large, and need to convert to 8-byte inodes. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) { 10678c2ecf20Sopenharmony_ci int error; /* error return value */ 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* 10708c2ecf20Sopenharmony_ci * Won't fit as shortform, convert to block then do replace. 10718c2ecf20Sopenharmony_ci */ 10728c2ecf20Sopenharmony_ci if (xfs_dir2_sf_replace_needblock(dp, args->inumber)) { 10738c2ecf20Sopenharmony_ci error = xfs_dir2_sf_to_block(args); 10748c2ecf20Sopenharmony_ci if (error) 10758c2ecf20Sopenharmony_ci return error; 10768c2ecf20Sopenharmony_ci return xfs_dir2_block_replace(args); 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci /* 10798c2ecf20Sopenharmony_ci * Still fits, convert to 8-byte now. 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci xfs_dir2_sf_toino8(args); 10828c2ecf20Sopenharmony_ci i8elevated = 1; 10838c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 10848c2ecf20Sopenharmony_ci } else 10858c2ecf20Sopenharmony_ci i8elevated = 0; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci ASSERT(args->namelen != 1 || args->name[0] != '.'); 10888c2ecf20Sopenharmony_ci /* 10898c2ecf20Sopenharmony_ci * Replace ..'s entry. 10908c2ecf20Sopenharmony_ci */ 10918c2ecf20Sopenharmony_ci if (args->namelen == 2 && 10928c2ecf20Sopenharmony_ci args->name[0] == '.' && args->name[1] == '.') { 10938c2ecf20Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 10948c2ecf20Sopenharmony_ci ASSERT(args->inumber != ino); 10958c2ecf20Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, args->inumber); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci /* 10988c2ecf20Sopenharmony_ci * Normal entry, look for the name. 10998c2ecf20Sopenharmony_ci */ 11008c2ecf20Sopenharmony_ci else { 11018c2ecf20Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; 11028c2ecf20Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 11038c2ecf20Sopenharmony_ci if (xfs_da_compname(args, sfep->name, sfep->namelen) == 11048c2ecf20Sopenharmony_ci XFS_CMP_EXACT) { 11058c2ecf20Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 11068c2ecf20Sopenharmony_ci ASSERT(args->inumber != ino); 11078c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 11088c2ecf20Sopenharmony_ci args->inumber); 11098c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, args->filetype); 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci /* 11148c2ecf20Sopenharmony_ci * Didn't find it. 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci if (i == sfp->count) { 11178c2ecf20Sopenharmony_ci ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); 11188c2ecf20Sopenharmony_ci if (i8elevated) 11198c2ecf20Sopenharmony_ci xfs_dir2_sf_toino4(args); 11208c2ecf20Sopenharmony_ci return -ENOENT; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci /* 11248c2ecf20Sopenharmony_ci * See if the old number was large, the new number is small. 11258c2ecf20Sopenharmony_ci */ 11268c2ecf20Sopenharmony_ci if (ino > XFS_DIR2_MAX_SHORT_INUM && 11278c2ecf20Sopenharmony_ci args->inumber <= XFS_DIR2_MAX_SHORT_INUM) { 11288c2ecf20Sopenharmony_ci /* 11298c2ecf20Sopenharmony_ci * And the old count was one, so need to convert to small. 11308c2ecf20Sopenharmony_ci */ 11318c2ecf20Sopenharmony_ci if (sfp->i8count == 1) 11328c2ecf20Sopenharmony_ci xfs_dir2_sf_toino4(args); 11338c2ecf20Sopenharmony_ci else 11348c2ecf20Sopenharmony_ci sfp->i8count--; 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci /* 11378c2ecf20Sopenharmony_ci * See if the old number was small, the new number is large. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci if (ino <= XFS_DIR2_MAX_SHORT_INUM && 11408c2ecf20Sopenharmony_ci args->inumber > XFS_DIR2_MAX_SHORT_INUM) { 11418c2ecf20Sopenharmony_ci /* 11428c2ecf20Sopenharmony_ci * add to the i8count unless we just converted to 8-byte 11438c2ecf20Sopenharmony_ci * inodes (which does an implied i8count = 1) 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci ASSERT(sfp->i8count != 0); 11468c2ecf20Sopenharmony_ci if (!i8elevated) 11478c2ecf20Sopenharmony_ci sfp->i8count++; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci xfs_dir2_sf_check(args); 11508c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA); 11518c2ecf20Sopenharmony_ci return 0; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci/* 11558c2ecf20Sopenharmony_ci * Convert from 8-byte inode numbers to 4-byte inode numbers. 11568c2ecf20Sopenharmony_ci * The last 8-byte inode number is gone, but the count is still 1. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_cistatic void 11598c2ecf20Sopenharmony_cixfs_dir2_sf_toino4( 11608c2ecf20Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 11638c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 11648c2ecf20Sopenharmony_ci char *buf; /* old dir's buffer */ 11658c2ecf20Sopenharmony_ci int i; /* entry index */ 11668c2ecf20Sopenharmony_ci int newsize; /* new inode size */ 11678c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ 11688c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *oldsfp; /* old sf directory */ 11698c2ecf20Sopenharmony_ci int oldsize; /* old inode size */ 11708c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* new sf entry */ 11718c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* new sf directory */ 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_toino4(args); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* 11768c2ecf20Sopenharmony_ci * Copy the old directory to the buffer. 11778c2ecf20Sopenharmony_ci * Then nuke it from the inode, and add the new buffer to the inode. 11788c2ecf20Sopenharmony_ci * Don't want xfs_idata_realloc copying the data here. 11798c2ecf20Sopenharmony_ci */ 11808c2ecf20Sopenharmony_ci oldsize = dp->i_df.if_bytes; 11818c2ecf20Sopenharmony_ci buf = kmem_alloc(oldsize, 0); 11828c2ecf20Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 11838c2ecf20Sopenharmony_ci ASSERT(oldsfp->i8count == 1); 11848c2ecf20Sopenharmony_ci memcpy(buf, oldsfp, oldsize); 11858c2ecf20Sopenharmony_ci /* 11868c2ecf20Sopenharmony_ci * Compute the new inode size. 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci newsize = oldsize - (oldsfp->count + 1) * XFS_INO64_DIFF; 11898c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); 11908c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * Reset our pointers, the data has moved. 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)buf; 11958c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 11968c2ecf20Sopenharmony_ci /* 11978c2ecf20Sopenharmony_ci * Fill in the new header. 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_ci sfp->count = oldsfp->count; 12008c2ecf20Sopenharmony_ci sfp->i8count = 0; 12018c2ecf20Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp)); 12028c2ecf20Sopenharmony_ci /* 12038c2ecf20Sopenharmony_ci * Copy the entries field by field. 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), 12068c2ecf20Sopenharmony_ci oldsfep = xfs_dir2_sf_firstentry(oldsfp); 12078c2ecf20Sopenharmony_ci i < sfp->count; 12088c2ecf20Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep), 12098c2ecf20Sopenharmony_ci oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep)) { 12108c2ecf20Sopenharmony_ci sfep->namelen = oldsfep->namelen; 12118c2ecf20Sopenharmony_ci memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset)); 12128c2ecf20Sopenharmony_ci memcpy(sfep->name, oldsfep->name, sfep->namelen); 12138c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 12148c2ecf20Sopenharmony_ci xfs_dir2_sf_get_ino(mp, oldsfp, oldsfep)); 12158c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, 12168c2ecf20Sopenharmony_ci xfs_dir2_sf_get_ftype(mp, oldsfep)); 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci /* 12198c2ecf20Sopenharmony_ci * Clean up the inode. 12208c2ecf20Sopenharmony_ci */ 12218c2ecf20Sopenharmony_ci kmem_free(buf); 12228c2ecf20Sopenharmony_ci dp->i_d.di_size = newsize; 12238c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci/* 12278c2ecf20Sopenharmony_ci * Convert existing entries from 4-byte inode numbers to 8-byte inode numbers. 12288c2ecf20Sopenharmony_ci * The new entry w/ an 8-byte inode number is not there yet; we leave with 12298c2ecf20Sopenharmony_ci * i8count set to 1, but no corresponding 8-byte entry. 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_cistatic void 12328c2ecf20Sopenharmony_cixfs_dir2_sf_toino8( 12338c2ecf20Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct xfs_inode *dp = args->dp; 12368c2ecf20Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 12378c2ecf20Sopenharmony_ci char *buf; /* old dir's buffer */ 12388c2ecf20Sopenharmony_ci int i; /* entry index */ 12398c2ecf20Sopenharmony_ci int newsize; /* new inode size */ 12408c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ 12418c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *oldsfp; /* old sf directory */ 12428c2ecf20Sopenharmony_ci int oldsize; /* old inode size */ 12438c2ecf20Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* new sf entry */ 12448c2ecf20Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* new sf directory */ 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci trace_xfs_dir2_sf_toino8(args); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci /* 12498c2ecf20Sopenharmony_ci * Copy the old directory to the buffer. 12508c2ecf20Sopenharmony_ci * Then nuke it from the inode, and add the new buffer to the inode. 12518c2ecf20Sopenharmony_ci * Don't want xfs_idata_realloc copying the data here. 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_ci oldsize = dp->i_df.if_bytes; 12548c2ecf20Sopenharmony_ci buf = kmem_alloc(oldsize, 0); 12558c2ecf20Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 12568c2ecf20Sopenharmony_ci ASSERT(oldsfp->i8count == 0); 12578c2ecf20Sopenharmony_ci memcpy(buf, oldsfp, oldsize); 12588c2ecf20Sopenharmony_ci /* 12598c2ecf20Sopenharmony_ci * Compute the new inode size (nb: entry count + 1 for parent) 12608c2ecf20Sopenharmony_ci */ 12618c2ecf20Sopenharmony_ci newsize = oldsize + (oldsfp->count + 1) * XFS_INO64_DIFF; 12628c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); 12638c2ecf20Sopenharmony_ci xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); 12648c2ecf20Sopenharmony_ci /* 12658c2ecf20Sopenharmony_ci * Reset our pointers, the data has moved. 12668c2ecf20Sopenharmony_ci */ 12678c2ecf20Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)buf; 12688c2ecf20Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 12698c2ecf20Sopenharmony_ci /* 12708c2ecf20Sopenharmony_ci * Fill in the new header. 12718c2ecf20Sopenharmony_ci */ 12728c2ecf20Sopenharmony_ci sfp->count = oldsfp->count; 12738c2ecf20Sopenharmony_ci sfp->i8count = 1; 12748c2ecf20Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp)); 12758c2ecf20Sopenharmony_ci /* 12768c2ecf20Sopenharmony_ci * Copy the entries field by field. 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), 12798c2ecf20Sopenharmony_ci oldsfep = xfs_dir2_sf_firstentry(oldsfp); 12808c2ecf20Sopenharmony_ci i < sfp->count; 12818c2ecf20Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep), 12828c2ecf20Sopenharmony_ci oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep)) { 12838c2ecf20Sopenharmony_ci sfep->namelen = oldsfep->namelen; 12848c2ecf20Sopenharmony_ci memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset)); 12858c2ecf20Sopenharmony_ci memcpy(sfep->name, oldsfep->name, sfep->namelen); 12868c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 12878c2ecf20Sopenharmony_ci xfs_dir2_sf_get_ino(mp, oldsfp, oldsfep)); 12888c2ecf20Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, 12898c2ecf20Sopenharmony_ci xfs_dir2_sf_get_ftype(mp, oldsfep)); 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * Clean up the inode. 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_ci kmem_free(buf); 12958c2ecf20Sopenharmony_ci dp->i_d.di_size = newsize; 12968c2ecf20Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 12978c2ecf20Sopenharmony_ci} 1298