162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_trans.h" 1562306a36Sopenharmony_ci#include "xfs_dir2.h" 1662306a36Sopenharmony_ci#include "xfs_dir2_priv.h" 1762306a36Sopenharmony_ci#include "xfs_trace.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Prototypes for internal functions. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic void xfs_dir2_sf_addname_easy(xfs_da_args_t *args, 2362306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep, 2462306a36Sopenharmony_ci xfs_dir2_data_aoff_t offset, 2562306a36Sopenharmony_ci int new_isize); 2662306a36Sopenharmony_cistatic void xfs_dir2_sf_addname_hard(xfs_da_args_t *args, int objchange, 2762306a36Sopenharmony_ci int new_isize); 2862306a36Sopenharmony_cistatic int xfs_dir2_sf_addname_pick(xfs_da_args_t *args, int objchange, 2962306a36Sopenharmony_ci xfs_dir2_sf_entry_t **sfepp, 3062306a36Sopenharmony_ci xfs_dir2_data_aoff_t *offsetp); 3162306a36Sopenharmony_ci#ifdef DEBUG 3262306a36Sopenharmony_cistatic void xfs_dir2_sf_check(xfs_da_args_t *args); 3362306a36Sopenharmony_ci#else 3462306a36Sopenharmony_ci#define xfs_dir2_sf_check(args) 3562306a36Sopenharmony_ci#endif /* DEBUG */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void xfs_dir2_sf_toino4(xfs_da_args_t *args); 3862306a36Sopenharmony_cistatic void xfs_dir2_sf_toino8(xfs_da_args_t *args); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciint 4162306a36Sopenharmony_cixfs_dir2_sf_entsize( 4262306a36Sopenharmony_ci struct xfs_mount *mp, 4362306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 4462306a36Sopenharmony_ci int len) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int count = len; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci count += sizeof(struct xfs_dir2_sf_entry); /* namelen + offset */ 4962306a36Sopenharmony_ci count += hdr->i8count ? XFS_INO64_SIZE : XFS_INO32_SIZE; /* ino # */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (xfs_has_ftype(mp)) 5262306a36Sopenharmony_ci count += sizeof(uint8_t); 5362306a36Sopenharmony_ci return count; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct xfs_dir2_sf_entry * 5762306a36Sopenharmony_cixfs_dir2_sf_nextentry( 5862306a36Sopenharmony_ci struct xfs_mount *mp, 5962306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 6062306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci return (void *)sfep + xfs_dir2_sf_entsize(mp, hdr, sfep->namelen); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * In short-form directory entries the inode numbers are stored at variable 6762306a36Sopenharmony_ci * offset behind the entry name. If the entry stores a filetype value, then it 6862306a36Sopenharmony_ci * sits between the name and the inode number. The actual inode numbers can 6962306a36Sopenharmony_ci * come in two formats as well, either 4 bytes or 8 bytes wide. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cixfs_ino_t 7262306a36Sopenharmony_cixfs_dir2_sf_get_ino( 7362306a36Sopenharmony_ci struct xfs_mount *mp, 7462306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 7562306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci uint8_t *from = sfep->name + sfep->namelen; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (xfs_has_ftype(mp)) 8062306a36Sopenharmony_ci from++; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!hdr->i8count) 8362306a36Sopenharmony_ci return get_unaligned_be32(from); 8462306a36Sopenharmony_ci return get_unaligned_be64(from) & XFS_MAXINUMBER; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_civoid 8862306a36Sopenharmony_cixfs_dir2_sf_put_ino( 8962306a36Sopenharmony_ci struct xfs_mount *mp, 9062306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 9162306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep, 9262306a36Sopenharmony_ci xfs_ino_t ino) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci uint8_t *to = sfep->name + sfep->namelen; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ASSERT(ino <= XFS_MAXINUMBER); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (xfs_has_ftype(mp)) 9962306a36Sopenharmony_ci to++; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (hdr->i8count) 10262306a36Sopenharmony_ci put_unaligned_be64(ino, to); 10362306a36Sopenharmony_ci else 10462306a36Sopenharmony_ci put_unaligned_be32(ino, to); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cixfs_ino_t 10862306a36Sopenharmony_cixfs_dir2_sf_get_parent_ino( 10962306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci if (!hdr->i8count) 11262306a36Sopenharmony_ci return get_unaligned_be32(hdr->parent); 11362306a36Sopenharmony_ci return get_unaligned_be64(hdr->parent) & XFS_MAXINUMBER; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid 11762306a36Sopenharmony_cixfs_dir2_sf_put_parent_ino( 11862306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *hdr, 11962306a36Sopenharmony_ci xfs_ino_t ino) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci ASSERT(ino <= XFS_MAXINUMBER); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (hdr->i8count) 12462306a36Sopenharmony_ci put_unaligned_be64(ino, hdr->parent); 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci put_unaligned_be32(ino, hdr->parent); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* 13062306a36Sopenharmony_ci * The file type field is stored at the end of the name for filetype enabled 13162306a36Sopenharmony_ci * shortform directories, or not at all otherwise. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ciuint8_t 13462306a36Sopenharmony_cixfs_dir2_sf_get_ftype( 13562306a36Sopenharmony_ci struct xfs_mount *mp, 13662306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci if (xfs_has_ftype(mp)) { 13962306a36Sopenharmony_ci uint8_t ftype = sfep->name[sfep->namelen]; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ftype < XFS_DIR3_FT_MAX) 14262306a36Sopenharmony_ci return ftype; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return XFS_DIR3_FT_UNKNOWN; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid 14962306a36Sopenharmony_cixfs_dir2_sf_put_ftype( 15062306a36Sopenharmony_ci struct xfs_mount *mp, 15162306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep, 15262306a36Sopenharmony_ci uint8_t ftype) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci ASSERT(ftype < XFS_DIR3_FT_MAX); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (xfs_has_ftype(mp)) 15762306a36Sopenharmony_ci sfep->name[sfep->namelen] = ftype; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* 16162306a36Sopenharmony_ci * Given a block directory (dp/block), calculate its size as a shortform (sf) 16262306a36Sopenharmony_ci * directory and a header for the sf directory, if it will fit it the 16362306a36Sopenharmony_ci * space currently present in the inode. If it won't fit, the output 16462306a36Sopenharmony_ci * size is too big (but not accurate). 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ciint /* size for sf form */ 16762306a36Sopenharmony_cixfs_dir2_block_sfsize( 16862306a36Sopenharmony_ci xfs_inode_t *dp, /* incore inode pointer */ 16962306a36Sopenharmony_ci xfs_dir2_data_hdr_t *hdr, /* block directory data */ 17062306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfhp) /* output: header for sf form */ 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci xfs_dir2_dataptr_t addr; /* data entry address */ 17362306a36Sopenharmony_ci xfs_dir2_leaf_entry_t *blp; /* leaf area of the block */ 17462306a36Sopenharmony_ci xfs_dir2_block_tail_t *btp; /* tail area of the block */ 17562306a36Sopenharmony_ci int count; /* shortform entry count */ 17662306a36Sopenharmony_ci xfs_dir2_data_entry_t *dep; /* data entry in the block */ 17762306a36Sopenharmony_ci int i; /* block entry index */ 17862306a36Sopenharmony_ci int i8count; /* count of big-inode entries */ 17962306a36Sopenharmony_ci int isdot; /* entry is "." */ 18062306a36Sopenharmony_ci int isdotdot; /* entry is ".." */ 18162306a36Sopenharmony_ci xfs_mount_t *mp; /* mount structure pointer */ 18262306a36Sopenharmony_ci int namelen; /* total name bytes */ 18362306a36Sopenharmony_ci xfs_ino_t parent = 0; /* parent inode number */ 18462306a36Sopenharmony_ci int size=0; /* total computed size */ 18562306a36Sopenharmony_ci int has_ftype; 18662306a36Sopenharmony_ci struct xfs_da_geometry *geo; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci mp = dp->i_mount; 18962306a36Sopenharmony_ci geo = mp->m_dir_geo; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * if there is a filetype field, add the extra byte to the namelen 19362306a36Sopenharmony_ci * for each entry that we see. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci has_ftype = xfs_has_ftype(mp) ? 1 : 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci count = i8count = namelen = 0; 19862306a36Sopenharmony_ci btp = xfs_dir2_block_tail_p(geo, hdr); 19962306a36Sopenharmony_ci blp = xfs_dir2_block_leaf_p(btp); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Iterate over the block's data entries by using the leaf pointers. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci for (i = 0; i < be32_to_cpu(btp->count); i++) { 20562306a36Sopenharmony_ci if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR) 20662306a36Sopenharmony_ci continue; 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * Calculate the pointer to the entry at hand. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci dep = (xfs_dir2_data_entry_t *)((char *)hdr + 21162306a36Sopenharmony_ci xfs_dir2_dataptr_to_off(geo, addr)); 21262306a36Sopenharmony_ci /* 21362306a36Sopenharmony_ci * Detect . and .., so we can special-case them. 21462306a36Sopenharmony_ci * . is not included in sf directories. 21562306a36Sopenharmony_ci * .. is included by just the parent inode number. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci isdot = dep->namelen == 1 && dep->name[0] == '.'; 21862306a36Sopenharmony_ci isdotdot = 21962306a36Sopenharmony_ci dep->namelen == 2 && 22062306a36Sopenharmony_ci dep->name[0] == '.' && dep->name[1] == '.'; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!isdot) 22362306a36Sopenharmony_ci i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* take into account the file type field */ 22662306a36Sopenharmony_ci if (!isdot && !isdotdot) { 22762306a36Sopenharmony_ci count++; 22862306a36Sopenharmony_ci namelen += dep->namelen + has_ftype; 22962306a36Sopenharmony_ci } else if (isdotdot) 23062306a36Sopenharmony_ci parent = be64_to_cpu(dep->inumber); 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Calculate the new size, see if we should give up yet. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci size = xfs_dir2_sf_hdr_size(i8count) + /* header */ 23562306a36Sopenharmony_ci count * 3 * sizeof(u8) + /* namelen + offset */ 23662306a36Sopenharmony_ci namelen + /* name */ 23762306a36Sopenharmony_ci (i8count ? /* inumber */ 23862306a36Sopenharmony_ci count * XFS_INO64_SIZE : 23962306a36Sopenharmony_ci count * XFS_INO32_SIZE); 24062306a36Sopenharmony_ci if (size > xfs_inode_data_fork_size(dp)) 24162306a36Sopenharmony_ci return size; /* size value is a failure */ 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * Create the output header, if it worked. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci sfhp->count = count; 24762306a36Sopenharmony_ci sfhp->i8count = i8count; 24862306a36Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfhp, parent); 24962306a36Sopenharmony_ci return size; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* 25362306a36Sopenharmony_ci * Convert a block format directory to shortform. 25462306a36Sopenharmony_ci * Caller has already checked that it will fit, and built us a header. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ciint /* error */ 25762306a36Sopenharmony_cixfs_dir2_block_to_sf( 25862306a36Sopenharmony_ci struct xfs_da_args *args, /* operation arguments */ 25962306a36Sopenharmony_ci struct xfs_buf *bp, 26062306a36Sopenharmony_ci int size, /* shortform directory size */ 26162306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *sfhp) /* shortform directory hdr */ 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 26462306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 26562306a36Sopenharmony_ci int error; /* error return value */ 26662306a36Sopenharmony_ci int logflags; /* inode logging flags */ 26762306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep; /* shortform entry */ 26862306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; /* shortform directory header */ 26962306a36Sopenharmony_ci unsigned int offset = args->geo->data_entry_offset; 27062306a36Sopenharmony_ci unsigned int end; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci trace_xfs_dir2_block_to_sf(args); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Allocate a temporary destination buffer the size of the inode to 27662306a36Sopenharmony_ci * format the data into. Once we have formatted the data, we can free 27762306a36Sopenharmony_ci * the block and copy the formatted data into the inode literal area. 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci sfp = kmem_alloc(mp->m_sb.sb_inodesize, 0); 28062306a36Sopenharmony_ci memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count)); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * Loop over the active and unused entries. Stop when we reach the 28462306a36Sopenharmony_ci * leaf/tail portion of the block. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci end = xfs_dir3_data_end_offset(args->geo, bp->b_addr); 28762306a36Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 28862306a36Sopenharmony_ci while (offset < end) { 28962306a36Sopenharmony_ci struct xfs_dir2_data_unused *dup = bp->b_addr + offset; 29062306a36Sopenharmony_ci struct xfs_dir2_data_entry *dep = bp->b_addr + offset; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * If it's unused, just skip over it. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { 29662306a36Sopenharmony_ci offset += be16_to_cpu(dup->length); 29762306a36Sopenharmony_ci continue; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * Skip . 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci if (dep->namelen == 1 && dep->name[0] == '.') 30462306a36Sopenharmony_ci ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino); 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * Skip .., but make sure the inode number is right. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci else if (dep->namelen == 2 && 30962306a36Sopenharmony_ci dep->name[0] == '.' && dep->name[1] == '.') 31062306a36Sopenharmony_ci ASSERT(be64_to_cpu(dep->inumber) == 31162306a36Sopenharmony_ci xfs_dir2_sf_get_parent_ino(sfp)); 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Normal entry, copy it into shortform. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci else { 31662306a36Sopenharmony_ci sfep->namelen = dep->namelen; 31762306a36Sopenharmony_ci xfs_dir2_sf_put_offset(sfep, offset); 31862306a36Sopenharmony_ci memcpy(sfep->name, dep->name, dep->namelen); 31962306a36Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 32062306a36Sopenharmony_ci be64_to_cpu(dep->inumber)); 32162306a36Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, 32262306a36Sopenharmony_ci xfs_dir2_data_get_ftype(mp, dep)); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci offset += xfs_dir2_data_entsize(mp, dep->namelen); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci ASSERT((char *)sfep - (char *)sfp == size); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* now we are done with the block, we can shrink the inode */ 33162306a36Sopenharmony_ci logflags = XFS_ILOG_CORE; 33262306a36Sopenharmony_ci error = xfs_dir2_shrink_inode(args, args->geo->datablk, bp); 33362306a36Sopenharmony_ci if (error) { 33462306a36Sopenharmony_ci ASSERT(error != -ENOSPC); 33562306a36Sopenharmony_ci goto out; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* 33962306a36Sopenharmony_ci * The buffer is now unconditionally gone, whether 34062306a36Sopenharmony_ci * xfs_dir2_shrink_inode worked or not. 34162306a36Sopenharmony_ci * 34262306a36Sopenharmony_ci * Convert the inode to local format and copy the data in. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == 0); 34562306a36Sopenharmony_ci xfs_init_local_fork(dp, XFS_DATA_FORK, sfp, size); 34662306a36Sopenharmony_ci dp->i_df.if_format = XFS_DINODE_FMT_LOCAL; 34762306a36Sopenharmony_ci dp->i_disk_size = size; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci logflags |= XFS_ILOG_DDATA; 35062306a36Sopenharmony_ci xfs_dir2_sf_check(args); 35162306a36Sopenharmony_ciout: 35262306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, logflags); 35362306a36Sopenharmony_ci kmem_free(sfp); 35462306a36Sopenharmony_ci return error; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* 35862306a36Sopenharmony_ci * Add a name to a shortform directory. 35962306a36Sopenharmony_ci * There are two algorithms, "easy" and "hard" which we decide on 36062306a36Sopenharmony_ci * before changing anything. 36162306a36Sopenharmony_ci * Convert to block form if necessary, if the new entry won't fit. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ciint /* error */ 36462306a36Sopenharmony_cixfs_dir2_sf_addname( 36562306a36Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci xfs_inode_t *dp; /* incore directory inode */ 36862306a36Sopenharmony_ci int error; /* error return value */ 36962306a36Sopenharmony_ci int incr_isize; /* total change in size */ 37062306a36Sopenharmony_ci int new_isize; /* size after adding name */ 37162306a36Sopenharmony_ci int objchange; /* changing to 8-byte inodes */ 37262306a36Sopenharmony_ci xfs_dir2_data_aoff_t offset = 0; /* offset for new entry */ 37362306a36Sopenharmony_ci int pick; /* which algorithm to use */ 37462306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 37562306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep = NULL; /* shortform entry */ 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci trace_xfs_dir2_sf_addname(args); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ASSERT(xfs_dir2_sf_lookup(args) == -ENOENT); 38062306a36Sopenharmony_ci dp = args->dp; 38162306a36Sopenharmony_ci ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL); 38262306a36Sopenharmony_ci ASSERT(dp->i_disk_size >= offsetof(struct xfs_dir2_sf_hdr, parent)); 38362306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_disk_size); 38462306a36Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 38562306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 38662306a36Sopenharmony_ci ASSERT(dp->i_disk_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * Compute entry (and change in) size. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci incr_isize = xfs_dir2_sf_entsize(dp->i_mount, sfp, args->namelen); 39162306a36Sopenharmony_ci objchange = 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * Do we have to change to 8 byte inodes? 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) { 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Yes, adjust the inode size. old count + (parent + new) 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci incr_isize += (sfp->count + 2) * XFS_INO64_DIFF; 40162306a36Sopenharmony_ci objchange = 1; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci new_isize = (int)dp->i_disk_size + incr_isize; 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * Won't fit as shortform any more (due to size), 40762306a36Sopenharmony_ci * or the pick routine says it won't (due to offset values). 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci if (new_isize > xfs_inode_data_fork_size(dp) || 41062306a36Sopenharmony_ci (pick = 41162306a36Sopenharmony_ci xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) { 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Just checking or no space reservation, it doesn't fit. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0) 41662306a36Sopenharmony_ci return -ENOSPC; 41762306a36Sopenharmony_ci /* 41862306a36Sopenharmony_ci * Convert to block form then add the name. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci error = xfs_dir2_sf_to_block(args); 42162306a36Sopenharmony_ci if (error) 42262306a36Sopenharmony_ci return error; 42362306a36Sopenharmony_ci return xfs_dir2_block_addname(args); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * Just checking, it fits. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci if (args->op_flags & XFS_DA_OP_JUSTCHECK) 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * Do it the easy way - just add it at the end. 43262306a36Sopenharmony_ci */ 43362306a36Sopenharmony_ci if (pick == 1) 43462306a36Sopenharmony_ci xfs_dir2_sf_addname_easy(args, sfep, offset, new_isize); 43562306a36Sopenharmony_ci /* 43662306a36Sopenharmony_ci * Do it the hard way - look for a place to insert the new entry. 43762306a36Sopenharmony_ci * Convert to 8 byte inode numbers first if necessary. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci else { 44062306a36Sopenharmony_ci ASSERT(pick == 2); 44162306a36Sopenharmony_ci if (objchange) 44262306a36Sopenharmony_ci xfs_dir2_sf_toino8(args); 44362306a36Sopenharmony_ci xfs_dir2_sf_addname_hard(args, objchange, new_isize); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * Add the new entry the "easy" way. 45162306a36Sopenharmony_ci * This is copying the old directory and adding the new entry at the end. 45262306a36Sopenharmony_ci * Since it's sorted by "offset" we need room after the last offset 45362306a36Sopenharmony_ci * that's already there, and then room to convert to a block directory. 45462306a36Sopenharmony_ci * This is already checked by the pick routine. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic void 45762306a36Sopenharmony_cixfs_dir2_sf_addname_easy( 45862306a36Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 45962306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep, /* pointer to new entry */ 46062306a36Sopenharmony_ci xfs_dir2_data_aoff_t offset, /* offset to use for new ent */ 46162306a36Sopenharmony_ci int new_isize) /* new directory size */ 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 46462306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 46562306a36Sopenharmony_ci int byteoff; /* byte offset in sf dir */ 46662306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 46962306a36Sopenharmony_ci byteoff = (int)((char *)sfep - (char *)sfp); 47062306a36Sopenharmony_ci /* 47162306a36Sopenharmony_ci * Grow the in-inode space. 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci xfs_idata_realloc(dp, xfs_dir2_sf_entsize(mp, sfp, args->namelen), 47462306a36Sopenharmony_ci XFS_DATA_FORK); 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Need to set up again due to realloc of the inode data. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 47962306a36Sopenharmony_ci sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff); 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * Fill in the new entry. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci sfep->namelen = args->namelen; 48462306a36Sopenharmony_ci xfs_dir2_sf_put_offset(sfep, offset); 48562306a36Sopenharmony_ci memcpy(sfep->name, args->name, sfep->namelen); 48662306a36Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber); 48762306a36Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, args->filetype); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * Update the header and inode. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci sfp->count++; 49362306a36Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) 49462306a36Sopenharmony_ci sfp->i8count++; 49562306a36Sopenharmony_ci dp->i_disk_size = new_isize; 49662306a36Sopenharmony_ci xfs_dir2_sf_check(args); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* 50062306a36Sopenharmony_ci * Add the new entry the "hard" way. 50162306a36Sopenharmony_ci * The caller has already converted to 8 byte inode numbers if necessary, 50262306a36Sopenharmony_ci * in which case we need to leave the i8count at 1. 50362306a36Sopenharmony_ci * Find a hole that the new entry will fit into, and copy 50462306a36Sopenharmony_ci * the first part of the entries, the new entry, and the last part of 50562306a36Sopenharmony_ci * the entries. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci/* ARGSUSED */ 50862306a36Sopenharmony_cistatic void 50962306a36Sopenharmony_cixfs_dir2_sf_addname_hard( 51062306a36Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 51162306a36Sopenharmony_ci int objchange, /* changing inode number size */ 51262306a36Sopenharmony_ci int new_isize) /* new directory size */ 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 51562306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 51662306a36Sopenharmony_ci int add_datasize; /* data size need for new ent */ 51762306a36Sopenharmony_ci char *buf; /* buffer for old */ 51862306a36Sopenharmony_ci int eof; /* reached end of old dir */ 51962306a36Sopenharmony_ci int nbytes; /* temp for byte copies */ 52062306a36Sopenharmony_ci xfs_dir2_data_aoff_t new_offset; /* next offset value */ 52162306a36Sopenharmony_ci xfs_dir2_data_aoff_t offset; /* current offset value */ 52262306a36Sopenharmony_ci int old_isize; /* previous size */ 52362306a36Sopenharmony_ci xfs_dir2_sf_entry_t *oldsfep; /* entry in original dir */ 52462306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *oldsfp; /* original shortform dir */ 52562306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* entry in new dir */ 52662306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* new shortform dir */ 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* 52962306a36Sopenharmony_ci * Copy the old directory to the stack buffer. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 53262306a36Sopenharmony_ci old_isize = (int)dp->i_disk_size; 53362306a36Sopenharmony_ci buf = kmem_alloc(old_isize, 0); 53462306a36Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)buf; 53562306a36Sopenharmony_ci memcpy(oldsfp, sfp, old_isize); 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * Loop over the old directory finding the place we're going 53862306a36Sopenharmony_ci * to insert the new entry. 53962306a36Sopenharmony_ci * If it's going to end up at the end then oldsfep will point there. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci for (offset = args->geo->data_first_offset, 54262306a36Sopenharmony_ci oldsfep = xfs_dir2_sf_firstentry(oldsfp), 54362306a36Sopenharmony_ci add_datasize = xfs_dir2_data_entsize(mp, args->namelen), 54462306a36Sopenharmony_ci eof = (char *)oldsfep == &buf[old_isize]; 54562306a36Sopenharmony_ci !eof; 54662306a36Sopenharmony_ci offset = new_offset + xfs_dir2_data_entsize(mp, oldsfep->namelen), 54762306a36Sopenharmony_ci oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep), 54862306a36Sopenharmony_ci eof = (char *)oldsfep == &buf[old_isize]) { 54962306a36Sopenharmony_ci new_offset = xfs_dir2_sf_get_offset(oldsfep); 55062306a36Sopenharmony_ci if (offset + add_datasize <= new_offset) 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * Get rid of the old directory, then allocate space for 55562306a36Sopenharmony_ci * the new one. We do this so xfs_idata_realloc won't copy 55662306a36Sopenharmony_ci * the data. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci xfs_idata_realloc(dp, -old_isize, XFS_DATA_FORK); 55962306a36Sopenharmony_ci xfs_idata_realloc(dp, new_isize, XFS_DATA_FORK); 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * Reset the pointer since the buffer was reallocated. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 56462306a36Sopenharmony_ci /* 56562306a36Sopenharmony_ci * Copy the first part of the directory, including the header. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci nbytes = (int)((char *)oldsfep - (char *)oldsfp); 56862306a36Sopenharmony_ci memcpy(sfp, oldsfp, nbytes); 56962306a36Sopenharmony_ci sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + nbytes); 57062306a36Sopenharmony_ci /* 57162306a36Sopenharmony_ci * Fill in the new entry, and update the header counts. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci sfep->namelen = args->namelen; 57462306a36Sopenharmony_ci xfs_dir2_sf_put_offset(sfep, offset); 57562306a36Sopenharmony_ci memcpy(sfep->name, args->name, sfep->namelen); 57662306a36Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, args->inumber); 57762306a36Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, args->filetype); 57862306a36Sopenharmony_ci sfp->count++; 57962306a36Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange) 58062306a36Sopenharmony_ci sfp->i8count++; 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * If there's more left to copy, do that. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci if (!eof) { 58562306a36Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 58662306a36Sopenharmony_ci memcpy(sfep, oldsfep, old_isize - nbytes); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci kmem_free(buf); 58962306a36Sopenharmony_ci dp->i_disk_size = new_isize; 59062306a36Sopenharmony_ci xfs_dir2_sf_check(args); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/* 59462306a36Sopenharmony_ci * Decide if the new entry will fit at all. 59562306a36Sopenharmony_ci * If it will fit, pick between adding the new entry to the end (easy) 59662306a36Sopenharmony_ci * or somewhere else (hard). 59762306a36Sopenharmony_ci * Return 0 (won't fit), 1 (easy), 2 (hard). 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ci/*ARGSUSED*/ 60062306a36Sopenharmony_cistatic int /* pick result */ 60162306a36Sopenharmony_cixfs_dir2_sf_addname_pick( 60262306a36Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 60362306a36Sopenharmony_ci int objchange, /* inode # size changes */ 60462306a36Sopenharmony_ci xfs_dir2_sf_entry_t **sfepp, /* out(1): new entry ptr */ 60562306a36Sopenharmony_ci xfs_dir2_data_aoff_t *offsetp) /* out(1): new offset */ 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 60862306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 60962306a36Sopenharmony_ci int holefit; /* found hole it will fit in */ 61062306a36Sopenharmony_ci int i; /* entry number */ 61162306a36Sopenharmony_ci xfs_dir2_data_aoff_t offset; /* data block offset */ 61262306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform entry */ 61362306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 61462306a36Sopenharmony_ci int size; /* entry's data size */ 61562306a36Sopenharmony_ci int used; /* data bytes used */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 61862306a36Sopenharmony_ci size = xfs_dir2_data_entsize(mp, args->namelen); 61962306a36Sopenharmony_ci offset = args->geo->data_first_offset; 62062306a36Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 62162306a36Sopenharmony_ci holefit = 0; 62262306a36Sopenharmony_ci /* 62362306a36Sopenharmony_ci * Loop over sf entries. 62462306a36Sopenharmony_ci * Keep track of data offset and whether we've seen a place 62562306a36Sopenharmony_ci * to insert the new entry. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci for (i = 0; i < sfp->count; i++) { 62862306a36Sopenharmony_ci if (!holefit) 62962306a36Sopenharmony_ci holefit = offset + size <= xfs_dir2_sf_get_offset(sfep); 63062306a36Sopenharmony_ci offset = xfs_dir2_sf_get_offset(sfep) + 63162306a36Sopenharmony_ci xfs_dir2_data_entsize(mp, sfep->namelen); 63262306a36Sopenharmony_ci sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * Calculate data bytes used excluding the new entry, if this 63662306a36Sopenharmony_ci * was a data block (block form directory). 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci used = offset + 63962306a36Sopenharmony_ci (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 64062306a36Sopenharmony_ci (uint)sizeof(xfs_dir2_block_tail_t); 64162306a36Sopenharmony_ci /* 64262306a36Sopenharmony_ci * If it won't fit in a block form then we can't insert it, 64362306a36Sopenharmony_ci * we'll go back, convert to block, then try the insert and convert 64462306a36Sopenharmony_ci * to leaf. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci if (used + (holefit ? 0 : size) > args->geo->blksize) 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci /* 64962306a36Sopenharmony_ci * If changing the inode number size, do it the hard way. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci if (objchange) 65262306a36Sopenharmony_ci return 2; 65362306a36Sopenharmony_ci /* 65462306a36Sopenharmony_ci * If it won't fit at the end then do it the hard way (use the hole). 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_ci if (used + size > args->geo->blksize) 65762306a36Sopenharmony_ci return 2; 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * Do it the easy way. 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci *sfepp = sfep; 66262306a36Sopenharmony_ci *offsetp = offset; 66362306a36Sopenharmony_ci return 1; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci#ifdef DEBUG 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * Check consistency of shortform directory, assert if bad. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_cistatic void 67162306a36Sopenharmony_cixfs_dir2_sf_check( 67262306a36Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 67562306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 67662306a36Sopenharmony_ci int i; /* entry number */ 67762306a36Sopenharmony_ci int i8count; /* number of big inode#s */ 67862306a36Sopenharmony_ci xfs_ino_t ino; /* entry inode number */ 67962306a36Sopenharmony_ci int offset; /* data offset */ 68062306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform dir entry */ 68162306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 68462306a36Sopenharmony_ci offset = args->geo->data_first_offset; 68562306a36Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 68662306a36Sopenharmony_ci i8count = ino > XFS_DIR2_MAX_SHORT_INUM; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); 68962306a36Sopenharmony_ci i < sfp->count; 69062306a36Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 69162306a36Sopenharmony_ci ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset); 69262306a36Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 69362306a36Sopenharmony_ci i8count += ino > XFS_DIR2_MAX_SHORT_INUM; 69462306a36Sopenharmony_ci offset = 69562306a36Sopenharmony_ci xfs_dir2_sf_get_offset(sfep) + 69662306a36Sopenharmony_ci xfs_dir2_data_entsize(mp, sfep->namelen); 69762306a36Sopenharmony_ci ASSERT(xfs_dir2_sf_get_ftype(mp, sfep) < XFS_DIR3_FT_MAX); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci ASSERT(i8count == sfp->i8count); 70062306a36Sopenharmony_ci ASSERT((char *)sfep - (char *)sfp == dp->i_disk_size); 70162306a36Sopenharmony_ci ASSERT(offset + 70262306a36Sopenharmony_ci (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 70362306a36Sopenharmony_ci (uint)sizeof(xfs_dir2_block_tail_t) <= args->geo->blksize); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci#endif /* DEBUG */ 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci/* Verify the consistency of an inline directory. */ 70862306a36Sopenharmony_cixfs_failaddr_t 70962306a36Sopenharmony_cixfs_dir2_sf_verify( 71062306a36Sopenharmony_ci struct xfs_inode *ip) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 71362306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); 71462306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; 71562306a36Sopenharmony_ci struct xfs_dir2_sf_entry *sfep; 71662306a36Sopenharmony_ci struct xfs_dir2_sf_entry *next_sfep; 71762306a36Sopenharmony_ci char *endp; 71862306a36Sopenharmony_ci xfs_ino_t ino; 71962306a36Sopenharmony_ci int i; 72062306a36Sopenharmony_ci int i8count; 72162306a36Sopenharmony_ci int offset; 72262306a36Sopenharmony_ci int64_t size; 72362306a36Sopenharmony_ci int error; 72462306a36Sopenharmony_ci uint8_t filetype; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data; 72962306a36Sopenharmony_ci size = ifp->if_bytes; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Give up if the directory is way too short. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (size <= offsetof(struct xfs_dir2_sf_hdr, parent) || 73562306a36Sopenharmony_ci size < xfs_dir2_sf_hdr_size(sfp->i8count)) 73662306a36Sopenharmony_ci return __this_address; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci endp = (char *)sfp + size; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Check .. entry */ 74162306a36Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 74262306a36Sopenharmony_ci i8count = ino > XFS_DIR2_MAX_SHORT_INUM; 74362306a36Sopenharmony_ci error = xfs_dir_ino_validate(mp, ino); 74462306a36Sopenharmony_ci if (error) 74562306a36Sopenharmony_ci return __this_address; 74662306a36Sopenharmony_ci offset = mp->m_dir_geo->data_first_offset; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Check all reported entries */ 74962306a36Sopenharmony_ci sfep = xfs_dir2_sf_firstentry(sfp); 75062306a36Sopenharmony_ci for (i = 0; i < sfp->count; i++) { 75162306a36Sopenharmony_ci /* 75262306a36Sopenharmony_ci * struct xfs_dir2_sf_entry has a variable length. 75362306a36Sopenharmony_ci * Check the fixed-offset parts of the structure are 75462306a36Sopenharmony_ci * within the data buffer. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_ci if (((char *)sfep + sizeof(*sfep)) >= endp) 75762306a36Sopenharmony_ci return __this_address; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* Don't allow names with known bad length. */ 76062306a36Sopenharmony_ci if (sfep->namelen == 0) 76162306a36Sopenharmony_ci return __this_address; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* 76462306a36Sopenharmony_ci * Check that the variable-length part of the structure is 76562306a36Sopenharmony_ci * within the data buffer. The next entry starts after the 76662306a36Sopenharmony_ci * name component, so nextentry is an acceptable test. 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci next_sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep); 76962306a36Sopenharmony_ci if (endp < (char *)next_sfep) 77062306a36Sopenharmony_ci return __this_address; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Check that the offsets always increase. */ 77362306a36Sopenharmony_ci if (xfs_dir2_sf_get_offset(sfep) < offset) 77462306a36Sopenharmony_ci return __this_address; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* Check the inode number. */ 77762306a36Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 77862306a36Sopenharmony_ci i8count += ino > XFS_DIR2_MAX_SHORT_INUM; 77962306a36Sopenharmony_ci error = xfs_dir_ino_validate(mp, ino); 78062306a36Sopenharmony_ci if (error) 78162306a36Sopenharmony_ci return __this_address; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Check the file type. */ 78462306a36Sopenharmony_ci filetype = xfs_dir2_sf_get_ftype(mp, sfep); 78562306a36Sopenharmony_ci if (filetype >= XFS_DIR3_FT_MAX) 78662306a36Sopenharmony_ci return __this_address; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci offset = xfs_dir2_sf_get_offset(sfep) + 78962306a36Sopenharmony_ci xfs_dir2_data_entsize(mp, sfep->namelen); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci sfep = next_sfep; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci if (i8count != sfp->i8count) 79462306a36Sopenharmony_ci return __this_address; 79562306a36Sopenharmony_ci if ((void *)sfep != (void *)endp) 79662306a36Sopenharmony_ci return __this_address; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* Make sure this whole thing ought to be in local format. */ 79962306a36Sopenharmony_ci if (offset + (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + 80062306a36Sopenharmony_ci (uint)sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) 80162306a36Sopenharmony_ci return __this_address; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return NULL; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci/* 80762306a36Sopenharmony_ci * Create a new (shortform) directory. 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_ciint /* error, always 0 */ 81062306a36Sopenharmony_cixfs_dir2_sf_create( 81162306a36Sopenharmony_ci xfs_da_args_t *args, /* operation arguments */ 81262306a36Sopenharmony_ci xfs_ino_t pino) /* parent inode number */ 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci xfs_inode_t *dp; /* incore directory inode */ 81562306a36Sopenharmony_ci int i8count; /* parent inode is an 8-byte number */ 81662306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 81762306a36Sopenharmony_ci int size; /* directory size */ 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci trace_xfs_dir2_sf_create(args); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci dp = args->dp; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci ASSERT(dp != NULL); 82462306a36Sopenharmony_ci ASSERT(dp->i_disk_size == 0); 82562306a36Sopenharmony_ci /* 82662306a36Sopenharmony_ci * If it's currently a zero-length extent file, 82762306a36Sopenharmony_ci * convert it to local format. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci if (dp->i_df.if_format == XFS_DINODE_FMT_EXTENTS) { 83062306a36Sopenharmony_ci dp->i_df.if_format = XFS_DINODE_FMT_LOCAL; 83162306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL); 83462306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == 0); 83562306a36Sopenharmony_ci i8count = pino > XFS_DIR2_MAX_SHORT_INUM; 83662306a36Sopenharmony_ci size = xfs_dir2_sf_hdr_size(i8count); 83762306a36Sopenharmony_ci /* 83862306a36Sopenharmony_ci * Make a buffer for the data. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci xfs_idata_realloc(dp, size, XFS_DATA_FORK); 84162306a36Sopenharmony_ci /* 84262306a36Sopenharmony_ci * Fill in the header, 84362306a36Sopenharmony_ci */ 84462306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 84562306a36Sopenharmony_ci sfp->i8count = i8count; 84662306a36Sopenharmony_ci /* 84762306a36Sopenharmony_ci * Now can put in the inode number, since i8count is set. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, pino); 85062306a36Sopenharmony_ci sfp->count = 0; 85162306a36Sopenharmony_ci dp->i_disk_size = size; 85262306a36Sopenharmony_ci xfs_dir2_sf_check(args); 85362306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/* 85862306a36Sopenharmony_ci * Lookup an entry in a shortform directory. 85962306a36Sopenharmony_ci * Returns EEXIST if found, ENOENT if not found. 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_ciint /* error */ 86262306a36Sopenharmony_cixfs_dir2_sf_lookup( 86362306a36Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 86662306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 86762306a36Sopenharmony_ci int i; /* entry index */ 86862306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 86962306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 87062306a36Sopenharmony_ci enum xfs_dacmp cmp; /* comparison result */ 87162306a36Sopenharmony_ci xfs_dir2_sf_entry_t *ci_sfep; /* case-insens. entry */ 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci trace_xfs_dir2_sf_lookup(args); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci xfs_dir2_sf_check(args); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL); 87862306a36Sopenharmony_ci ASSERT(dp->i_disk_size >= offsetof(struct xfs_dir2_sf_hdr, parent)); 87962306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_disk_size); 88062306a36Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 88162306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 88262306a36Sopenharmony_ci ASSERT(dp->i_disk_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); 88362306a36Sopenharmony_ci /* 88462306a36Sopenharmony_ci * Special case for . 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_ci if (args->namelen == 1 && args->name[0] == '.') { 88762306a36Sopenharmony_ci args->inumber = dp->i_ino; 88862306a36Sopenharmony_ci args->cmpresult = XFS_CMP_EXACT; 88962306a36Sopenharmony_ci args->filetype = XFS_DIR3_FT_DIR; 89062306a36Sopenharmony_ci return -EEXIST; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci /* 89362306a36Sopenharmony_ci * Special case for .. 89462306a36Sopenharmony_ci */ 89562306a36Sopenharmony_ci if (args->namelen == 2 && 89662306a36Sopenharmony_ci args->name[0] == '.' && args->name[1] == '.') { 89762306a36Sopenharmony_ci args->inumber = xfs_dir2_sf_get_parent_ino(sfp); 89862306a36Sopenharmony_ci args->cmpresult = XFS_CMP_EXACT; 89962306a36Sopenharmony_ci args->filetype = XFS_DIR3_FT_DIR; 90062306a36Sopenharmony_ci return -EEXIST; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * Loop over all the entries trying to match ours. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci ci_sfep = NULL; 90662306a36Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; 90762306a36Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 90862306a36Sopenharmony_ci /* 90962306a36Sopenharmony_ci * Compare name and if it's an exact match, return the inode 91062306a36Sopenharmony_ci * number. If it's the first case-insensitive match, store the 91162306a36Sopenharmony_ci * inode number and continue looking for an exact match. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci cmp = xfs_dir2_compname(args, sfep->name, sfep->namelen); 91462306a36Sopenharmony_ci if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { 91562306a36Sopenharmony_ci args->cmpresult = cmp; 91662306a36Sopenharmony_ci args->inumber = xfs_dir2_sf_get_ino(mp, sfp, sfep); 91762306a36Sopenharmony_ci args->filetype = xfs_dir2_sf_get_ftype(mp, sfep); 91862306a36Sopenharmony_ci if (cmp == XFS_CMP_EXACT) 91962306a36Sopenharmony_ci return -EEXIST; 92062306a36Sopenharmony_ci ci_sfep = sfep; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); 92462306a36Sopenharmony_ci /* 92562306a36Sopenharmony_ci * Here, we can only be doing a lookup (not a rename or replace). 92662306a36Sopenharmony_ci * If a case-insensitive match was not found, return -ENOENT. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci if (!ci_sfep) 92962306a36Sopenharmony_ci return -ENOENT; 93062306a36Sopenharmony_ci /* otherwise process the CI match as required by the caller */ 93162306a36Sopenharmony_ci return xfs_dir_cilookup_result(args, ci_sfep->name, ci_sfep->namelen); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci/* 93562306a36Sopenharmony_ci * Remove an entry from a shortform directory. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ciint /* error */ 93862306a36Sopenharmony_cixfs_dir2_sf_removename( 93962306a36Sopenharmony_ci xfs_da_args_t *args) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 94262306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 94362306a36Sopenharmony_ci int byteoff; /* offset of removed entry */ 94462306a36Sopenharmony_ci int entsize; /* this entry's size */ 94562306a36Sopenharmony_ci int i; /* shortform entry index */ 94662306a36Sopenharmony_ci int newsize; /* new inode size */ 94762306a36Sopenharmony_ci int oldsize; /* old inode size */ 94862306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 94962306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci trace_xfs_dir2_sf_removename(args); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL); 95462306a36Sopenharmony_ci oldsize = (int)dp->i_disk_size; 95562306a36Sopenharmony_ci ASSERT(oldsize >= offsetof(struct xfs_dir2_sf_hdr, parent)); 95662306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == oldsize); 95762306a36Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 95862306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 95962306a36Sopenharmony_ci ASSERT(oldsize >= xfs_dir2_sf_hdr_size(sfp->i8count)); 96062306a36Sopenharmony_ci /* 96162306a36Sopenharmony_ci * Loop over the old directory entries. 96262306a36Sopenharmony_ci * Find the one we're deleting. 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; 96562306a36Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 96662306a36Sopenharmony_ci if (xfs_da_compname(args, sfep->name, sfep->namelen) == 96762306a36Sopenharmony_ci XFS_CMP_EXACT) { 96862306a36Sopenharmony_ci ASSERT(xfs_dir2_sf_get_ino(mp, sfp, sfep) == 96962306a36Sopenharmony_ci args->inumber); 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci /* 97462306a36Sopenharmony_ci * Didn't find it. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_ci if (i == sfp->count) 97762306a36Sopenharmony_ci return -ENOENT; 97862306a36Sopenharmony_ci /* 97962306a36Sopenharmony_ci * Calculate sizes. 98062306a36Sopenharmony_ci */ 98162306a36Sopenharmony_ci byteoff = (int)((char *)sfep - (char *)sfp); 98262306a36Sopenharmony_ci entsize = xfs_dir2_sf_entsize(mp, sfp, args->namelen); 98362306a36Sopenharmony_ci newsize = oldsize - entsize; 98462306a36Sopenharmony_ci /* 98562306a36Sopenharmony_ci * Copy the part if any after the removed entry, sliding it down. 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci if (byteoff + entsize < oldsize) 98862306a36Sopenharmony_ci memmove((char *)sfp + byteoff, (char *)sfp + byteoff + entsize, 98962306a36Sopenharmony_ci oldsize - (byteoff + entsize)); 99062306a36Sopenharmony_ci /* 99162306a36Sopenharmony_ci * Fix up the header and file size. 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci sfp->count--; 99462306a36Sopenharmony_ci dp->i_disk_size = newsize; 99562306a36Sopenharmony_ci /* 99662306a36Sopenharmony_ci * Reallocate, making it smaller. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK); 99962306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 100062306a36Sopenharmony_ci /* 100162306a36Sopenharmony_ci * Are we changing inode number size? 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) { 100462306a36Sopenharmony_ci if (sfp->i8count == 1) 100562306a36Sopenharmony_ci xfs_dir2_sf_toino4(args); 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci sfp->i8count--; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci xfs_dir2_sf_check(args); 101062306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 101162306a36Sopenharmony_ci return 0; 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci/* 101562306a36Sopenharmony_ci * Check whether the sf dir replace operation need more blocks. 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_cistatic bool 101862306a36Sopenharmony_cixfs_dir2_sf_replace_needblock( 101962306a36Sopenharmony_ci struct xfs_inode *dp, 102062306a36Sopenharmony_ci xfs_ino_t inum) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci int newsize; 102362306a36Sopenharmony_ci struct xfs_dir2_sf_hdr *sfp; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (dp->i_df.if_format != XFS_DINODE_FMT_LOCAL) 102662306a36Sopenharmony_ci return false; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci sfp = (struct xfs_dir2_sf_hdr *)dp->i_df.if_u1.if_data; 102962306a36Sopenharmony_ci newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci return inum > XFS_DIR2_MAX_SHORT_INUM && 103262306a36Sopenharmony_ci sfp->i8count == 0 && newsize > xfs_inode_data_fork_size(dp); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci/* 103662306a36Sopenharmony_ci * Replace the inode number of an entry in a shortform directory. 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_ciint /* error */ 103962306a36Sopenharmony_cixfs_dir2_sf_replace( 104062306a36Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 104362306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 104462306a36Sopenharmony_ci int i; /* entry index */ 104562306a36Sopenharmony_ci xfs_ino_t ino=0; /* entry old inode number */ 104662306a36Sopenharmony_ci int i8elevated; /* sf_toino8 set i8count=1 */ 104762306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ 104862306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci trace_xfs_dir2_sf_replace(args); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ASSERT(dp->i_df.if_format == XFS_DINODE_FMT_LOCAL); 105362306a36Sopenharmony_ci ASSERT(dp->i_disk_size >= offsetof(struct xfs_dir2_sf_hdr, parent)); 105462306a36Sopenharmony_ci ASSERT(dp->i_df.if_bytes == dp->i_disk_size); 105562306a36Sopenharmony_ci ASSERT(dp->i_df.if_u1.if_data != NULL); 105662306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 105762306a36Sopenharmony_ci ASSERT(dp->i_disk_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* 106062306a36Sopenharmony_ci * New inode number is large, and need to convert to 8-byte inodes. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_ci if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) { 106362306a36Sopenharmony_ci int error; /* error return value */ 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* 106662306a36Sopenharmony_ci * Won't fit as shortform, convert to block then do replace. 106762306a36Sopenharmony_ci */ 106862306a36Sopenharmony_ci if (xfs_dir2_sf_replace_needblock(dp, args->inumber)) { 106962306a36Sopenharmony_ci error = xfs_dir2_sf_to_block(args); 107062306a36Sopenharmony_ci if (error) 107162306a36Sopenharmony_ci return error; 107262306a36Sopenharmony_ci return xfs_dir2_block_replace(args); 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci /* 107562306a36Sopenharmony_ci * Still fits, convert to 8-byte now. 107662306a36Sopenharmony_ci */ 107762306a36Sopenharmony_ci xfs_dir2_sf_toino8(args); 107862306a36Sopenharmony_ci i8elevated = 1; 107962306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 108062306a36Sopenharmony_ci } else 108162306a36Sopenharmony_ci i8elevated = 0; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci ASSERT(args->namelen != 1 || args->name[0] != '.'); 108462306a36Sopenharmony_ci /* 108562306a36Sopenharmony_ci * Replace ..'s entry. 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_ci if (args->namelen == 2 && 108862306a36Sopenharmony_ci args->name[0] == '.' && args->name[1] == '.') { 108962306a36Sopenharmony_ci ino = xfs_dir2_sf_get_parent_ino(sfp); 109062306a36Sopenharmony_ci ASSERT(args->inumber != ino); 109162306a36Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, args->inumber); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci /* 109462306a36Sopenharmony_ci * Normal entry, look for the name. 109562306a36Sopenharmony_ci */ 109662306a36Sopenharmony_ci else { 109762306a36Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; 109862306a36Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep)) { 109962306a36Sopenharmony_ci if (xfs_da_compname(args, sfep->name, sfep->namelen) == 110062306a36Sopenharmony_ci XFS_CMP_EXACT) { 110162306a36Sopenharmony_ci ino = xfs_dir2_sf_get_ino(mp, sfp, sfep); 110262306a36Sopenharmony_ci ASSERT(args->inumber != ino); 110362306a36Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 110462306a36Sopenharmony_ci args->inumber); 110562306a36Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, args->filetype); 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * Didn't find it. 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ci if (i == sfp->count) { 111362306a36Sopenharmony_ci ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); 111462306a36Sopenharmony_ci if (i8elevated) 111562306a36Sopenharmony_ci xfs_dir2_sf_toino4(args); 111662306a36Sopenharmony_ci return -ENOENT; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci /* 112062306a36Sopenharmony_ci * See if the old number was large, the new number is small. 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci if (ino > XFS_DIR2_MAX_SHORT_INUM && 112362306a36Sopenharmony_ci args->inumber <= XFS_DIR2_MAX_SHORT_INUM) { 112462306a36Sopenharmony_ci /* 112562306a36Sopenharmony_ci * And the old count was one, so need to convert to small. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci if (sfp->i8count == 1) 112862306a36Sopenharmony_ci xfs_dir2_sf_toino4(args); 112962306a36Sopenharmony_ci else 113062306a36Sopenharmony_ci sfp->i8count--; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * See if the old number was small, the new number is large. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci if (ino <= XFS_DIR2_MAX_SHORT_INUM && 113662306a36Sopenharmony_ci args->inumber > XFS_DIR2_MAX_SHORT_INUM) { 113762306a36Sopenharmony_ci /* 113862306a36Sopenharmony_ci * add to the i8count unless we just converted to 8-byte 113962306a36Sopenharmony_ci * inodes (which does an implied i8count = 1) 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci ASSERT(sfp->i8count != 0); 114262306a36Sopenharmony_ci if (!i8elevated) 114362306a36Sopenharmony_ci sfp->i8count++; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci xfs_dir2_sf_check(args); 114662306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA); 114762306a36Sopenharmony_ci return 0; 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci/* 115162306a36Sopenharmony_ci * Convert from 8-byte inode numbers to 4-byte inode numbers. 115262306a36Sopenharmony_ci * The last 8-byte inode number is gone, but the count is still 1. 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_cistatic void 115562306a36Sopenharmony_cixfs_dir2_sf_toino4( 115662306a36Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 115962306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 116062306a36Sopenharmony_ci char *buf; /* old dir's buffer */ 116162306a36Sopenharmony_ci int i; /* entry index */ 116262306a36Sopenharmony_ci int newsize; /* new inode size */ 116362306a36Sopenharmony_ci xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ 116462306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *oldsfp; /* old sf directory */ 116562306a36Sopenharmony_ci int oldsize; /* old inode size */ 116662306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* new sf entry */ 116762306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* new sf directory */ 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci trace_xfs_dir2_sf_toino4(args); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* 117262306a36Sopenharmony_ci * Copy the old directory to the buffer. 117362306a36Sopenharmony_ci * Then nuke it from the inode, and add the new buffer to the inode. 117462306a36Sopenharmony_ci * Don't want xfs_idata_realloc copying the data here. 117562306a36Sopenharmony_ci */ 117662306a36Sopenharmony_ci oldsize = dp->i_df.if_bytes; 117762306a36Sopenharmony_ci buf = kmem_alloc(oldsize, 0); 117862306a36Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 117962306a36Sopenharmony_ci ASSERT(oldsfp->i8count == 1); 118062306a36Sopenharmony_ci memcpy(buf, oldsfp, oldsize); 118162306a36Sopenharmony_ci /* 118262306a36Sopenharmony_ci * Compute the new inode size. 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_ci newsize = oldsize - (oldsfp->count + 1) * XFS_INO64_DIFF; 118562306a36Sopenharmony_ci xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); 118662306a36Sopenharmony_ci xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); 118762306a36Sopenharmony_ci /* 118862306a36Sopenharmony_ci * Reset our pointers, the data has moved. 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)buf; 119162306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 119262306a36Sopenharmony_ci /* 119362306a36Sopenharmony_ci * Fill in the new header. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci sfp->count = oldsfp->count; 119662306a36Sopenharmony_ci sfp->i8count = 0; 119762306a36Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp)); 119862306a36Sopenharmony_ci /* 119962306a36Sopenharmony_ci * Copy the entries field by field. 120062306a36Sopenharmony_ci */ 120162306a36Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), 120262306a36Sopenharmony_ci oldsfep = xfs_dir2_sf_firstentry(oldsfp); 120362306a36Sopenharmony_ci i < sfp->count; 120462306a36Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep), 120562306a36Sopenharmony_ci oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep)) { 120662306a36Sopenharmony_ci sfep->namelen = oldsfep->namelen; 120762306a36Sopenharmony_ci memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset)); 120862306a36Sopenharmony_ci memcpy(sfep->name, oldsfep->name, sfep->namelen); 120962306a36Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 121062306a36Sopenharmony_ci xfs_dir2_sf_get_ino(mp, oldsfp, oldsfep)); 121162306a36Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, 121262306a36Sopenharmony_ci xfs_dir2_sf_get_ftype(mp, oldsfep)); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci /* 121562306a36Sopenharmony_ci * Clean up the inode. 121662306a36Sopenharmony_ci */ 121762306a36Sopenharmony_ci kmem_free(buf); 121862306a36Sopenharmony_ci dp->i_disk_size = newsize; 121962306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci/* 122362306a36Sopenharmony_ci * Convert existing entries from 4-byte inode numbers to 8-byte inode numbers. 122462306a36Sopenharmony_ci * The new entry w/ an 8-byte inode number is not there yet; we leave with 122562306a36Sopenharmony_ci * i8count set to 1, but no corresponding 8-byte entry. 122662306a36Sopenharmony_ci */ 122762306a36Sopenharmony_cistatic void 122862306a36Sopenharmony_cixfs_dir2_sf_toino8( 122962306a36Sopenharmony_ci xfs_da_args_t *args) /* operation arguments */ 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct xfs_inode *dp = args->dp; 123262306a36Sopenharmony_ci struct xfs_mount *mp = dp->i_mount; 123362306a36Sopenharmony_ci char *buf; /* old dir's buffer */ 123462306a36Sopenharmony_ci int i; /* entry index */ 123562306a36Sopenharmony_ci int newsize; /* new inode size */ 123662306a36Sopenharmony_ci xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ 123762306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *oldsfp; /* old sf directory */ 123862306a36Sopenharmony_ci int oldsize; /* old inode size */ 123962306a36Sopenharmony_ci xfs_dir2_sf_entry_t *sfep; /* new sf entry */ 124062306a36Sopenharmony_ci xfs_dir2_sf_hdr_t *sfp; /* new sf directory */ 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci trace_xfs_dir2_sf_toino8(args); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci /* 124562306a36Sopenharmony_ci * Copy the old directory to the buffer. 124662306a36Sopenharmony_ci * Then nuke it from the inode, and add the new buffer to the inode. 124762306a36Sopenharmony_ci * Don't want xfs_idata_realloc copying the data here. 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_ci oldsize = dp->i_df.if_bytes; 125062306a36Sopenharmony_ci buf = kmem_alloc(oldsize, 0); 125162306a36Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 125262306a36Sopenharmony_ci ASSERT(oldsfp->i8count == 0); 125362306a36Sopenharmony_ci memcpy(buf, oldsfp, oldsize); 125462306a36Sopenharmony_ci /* 125562306a36Sopenharmony_ci * Compute the new inode size (nb: entry count + 1 for parent) 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci newsize = oldsize + (oldsfp->count + 1) * XFS_INO64_DIFF; 125862306a36Sopenharmony_ci xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); 125962306a36Sopenharmony_ci xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); 126062306a36Sopenharmony_ci /* 126162306a36Sopenharmony_ci * Reset our pointers, the data has moved. 126262306a36Sopenharmony_ci */ 126362306a36Sopenharmony_ci oldsfp = (xfs_dir2_sf_hdr_t *)buf; 126462306a36Sopenharmony_ci sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 126562306a36Sopenharmony_ci /* 126662306a36Sopenharmony_ci * Fill in the new header. 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_ci sfp->count = oldsfp->count; 126962306a36Sopenharmony_ci sfp->i8count = 1; 127062306a36Sopenharmony_ci xfs_dir2_sf_put_parent_ino(sfp, xfs_dir2_sf_get_parent_ino(oldsfp)); 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * Copy the entries field by field. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ci for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), 127562306a36Sopenharmony_ci oldsfep = xfs_dir2_sf_firstentry(oldsfp); 127662306a36Sopenharmony_ci i < sfp->count; 127762306a36Sopenharmony_ci i++, sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep), 127862306a36Sopenharmony_ci oldsfep = xfs_dir2_sf_nextentry(mp, oldsfp, oldsfep)) { 127962306a36Sopenharmony_ci sfep->namelen = oldsfep->namelen; 128062306a36Sopenharmony_ci memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset)); 128162306a36Sopenharmony_ci memcpy(sfep->name, oldsfep->name, sfep->namelen); 128262306a36Sopenharmony_ci xfs_dir2_sf_put_ino(mp, sfp, sfep, 128362306a36Sopenharmony_ci xfs_dir2_sf_get_ino(mp, oldsfp, oldsfep)); 128462306a36Sopenharmony_ci xfs_dir2_sf_put_ftype(mp, sfep, 128562306a36Sopenharmony_ci xfs_dir2_sf_get_ftype(mp, oldsfep)); 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci /* 128862306a36Sopenharmony_ci * Clean up the inode. 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_ci kmem_free(buf); 129162306a36Sopenharmony_ci dp->i_disk_size = newsize; 129262306a36Sopenharmony_ci xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); 129362306a36Sopenharmony_ci} 1294