162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "xfs.h" 862306a36Sopenharmony_ci#include "xfs_fs.h" 962306a36Sopenharmony_ci#include "xfs_shared.h" 1062306a36Sopenharmony_ci#include "xfs_format.h" 1162306a36Sopenharmony_ci#include "xfs_log_format.h" 1262306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1362306a36Sopenharmony_ci#include "xfs_mount.h" 1462306a36Sopenharmony_ci#include "xfs_inode.h" 1562306a36Sopenharmony_ci#include "xfs_trans.h" 1662306a36Sopenharmony_ci#include "xfs_inode_item.h" 1762306a36Sopenharmony_ci#include "xfs_btree.h" 1862306a36Sopenharmony_ci#include "xfs_bmap_btree.h" 1962306a36Sopenharmony_ci#include "xfs_bmap.h" 2062306a36Sopenharmony_ci#include "xfs_error.h" 2162306a36Sopenharmony_ci#include "xfs_trace.h" 2262306a36Sopenharmony_ci#include "xfs_da_format.h" 2362306a36Sopenharmony_ci#include "xfs_da_btree.h" 2462306a36Sopenharmony_ci#include "xfs_dir2_priv.h" 2562306a36Sopenharmony_ci#include "xfs_attr_leaf.h" 2662306a36Sopenharmony_ci#include "xfs_types.h" 2762306a36Sopenharmony_ci#include "xfs_errortag.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct kmem_cache *xfs_ifork_cache; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_civoid 3262306a36Sopenharmony_cixfs_init_local_fork( 3362306a36Sopenharmony_ci struct xfs_inode *ip, 3462306a36Sopenharmony_ci int whichfork, 3562306a36Sopenharmony_ci const void *data, 3662306a36Sopenharmony_ci int64_t size) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 3962306a36Sopenharmony_ci int mem_size = size; 4062306a36Sopenharmony_ci bool zero_terminate; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* 4362306a36Sopenharmony_ci * If we are using the local fork to store a symlink body we need to 4462306a36Sopenharmony_ci * zero-terminate it so that we can pass it back to the VFS directly. 4562306a36Sopenharmony_ci * Overallocate the in-memory fork by one for that and add a zero 4662306a36Sopenharmony_ci * to terminate it below. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci zero_terminate = S_ISLNK(VFS_I(ip)->i_mode); 4962306a36Sopenharmony_ci if (zero_terminate) 5062306a36Sopenharmony_ci mem_size++; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (size) { 5362306a36Sopenharmony_ci ifp->if_u1.if_data = kmem_alloc(mem_size, KM_NOFS); 5462306a36Sopenharmony_ci memcpy(ifp->if_u1.if_data, data, size); 5562306a36Sopenharmony_ci if (zero_terminate) 5662306a36Sopenharmony_ci ifp->if_u1.if_data[size] = '\0'; 5762306a36Sopenharmony_ci } else { 5862306a36Sopenharmony_ci ifp->if_u1.if_data = NULL; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ifp->if_bytes = size; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci * The file is in-lined in the on-disk inode. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ciSTATIC int 6862306a36Sopenharmony_cixfs_iformat_local( 6962306a36Sopenharmony_ci struct xfs_inode *ip, 7062306a36Sopenharmony_ci struct xfs_dinode *dip, 7162306a36Sopenharmony_ci int whichfork, 7262306a36Sopenharmony_ci int size) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * If the size is unreasonable, then something 7662306a36Sopenharmony_ci * is wrong and we just bail out rather than crash in 7762306a36Sopenharmony_ci * kmem_alloc() or memcpy() below. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci if (unlikely(size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) { 8062306a36Sopenharmony_ci xfs_warn(ip->i_mount, 8162306a36Sopenharmony_ci "corrupt inode %llu (bad size %d for local fork, size = %zd).", 8262306a36Sopenharmony_ci (unsigned long long) ip->i_ino, size, 8362306a36Sopenharmony_ci XFS_DFORK_SIZE(dip, ip->i_mount, whichfork)); 8462306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, 8562306a36Sopenharmony_ci "xfs_iformat_local", dip, sizeof(*dip), 8662306a36Sopenharmony_ci __this_address); 8762306a36Sopenharmony_ci return -EFSCORRUPTED; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci xfs_init_local_fork(ip, whichfork, XFS_DFORK_PTR(dip, whichfork), size); 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * The file consists of a set of extents all of which fit into the on-disk 9662306a36Sopenharmony_ci * inode. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ciSTATIC int 9962306a36Sopenharmony_cixfs_iformat_extents( 10062306a36Sopenharmony_ci struct xfs_inode *ip, 10162306a36Sopenharmony_ci struct xfs_dinode *dip, 10262306a36Sopenharmony_ci int whichfork) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 10562306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 10662306a36Sopenharmony_ci int state = xfs_bmap_fork_to_state(whichfork); 10762306a36Sopenharmony_ci xfs_extnum_t nex = xfs_dfork_nextents(dip, whichfork); 10862306a36Sopenharmony_ci int size = nex * sizeof(xfs_bmbt_rec_t); 10962306a36Sopenharmony_ci struct xfs_iext_cursor icur; 11062306a36Sopenharmony_ci struct xfs_bmbt_rec *dp; 11162306a36Sopenharmony_ci struct xfs_bmbt_irec new; 11262306a36Sopenharmony_ci int i; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * If the number of extents is unreasonable, then something is wrong and 11662306a36Sopenharmony_ci * we just bail out rather than crash in kmem_alloc() or memcpy() below. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) { 11962306a36Sopenharmony_ci xfs_warn(ip->i_mount, "corrupt inode %llu ((a)extents = %llu).", 12062306a36Sopenharmony_ci ip->i_ino, nex); 12162306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, 12262306a36Sopenharmony_ci "xfs_iformat_extents(1)", dip, sizeof(*dip), 12362306a36Sopenharmony_ci __this_address); 12462306a36Sopenharmony_ci return -EFSCORRUPTED; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ifp->if_bytes = 0; 12862306a36Sopenharmony_ci ifp->if_u1.if_root = NULL; 12962306a36Sopenharmony_ci ifp->if_height = 0; 13062306a36Sopenharmony_ci if (size) { 13162306a36Sopenharmony_ci dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci xfs_iext_first(ifp, &icur); 13462306a36Sopenharmony_ci for (i = 0; i < nex; i++, dp++) { 13562306a36Sopenharmony_ci xfs_failaddr_t fa; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci xfs_bmbt_disk_get_all(dp, &new); 13862306a36Sopenharmony_ci fa = xfs_bmap_validate_extent(ip, whichfork, &new); 13962306a36Sopenharmony_ci if (fa) { 14062306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, 14162306a36Sopenharmony_ci "xfs_iformat_extents(2)", 14262306a36Sopenharmony_ci dp, sizeof(*dp), fa); 14362306a36Sopenharmony_ci return xfs_bmap_complain_bad_rec(ip, whichfork, 14462306a36Sopenharmony_ci fa, &new); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci xfs_iext_insert(ip, &icur, &new, state); 14862306a36Sopenharmony_ci trace_xfs_read_extent(ip, &icur, state, _THIS_IP_); 14962306a36Sopenharmony_ci xfs_iext_next(ifp, &icur); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * The file has too many extents to fit into 15762306a36Sopenharmony_ci * the inode, so they are in B-tree format. 15862306a36Sopenharmony_ci * Allocate a buffer for the root of the B-tree 15962306a36Sopenharmony_ci * and copy the root into it. The i_extents 16062306a36Sopenharmony_ci * field will remain NULL until all of the 16162306a36Sopenharmony_ci * extents are read in (when they are needed). 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ciSTATIC int 16462306a36Sopenharmony_cixfs_iformat_btree( 16562306a36Sopenharmony_ci struct xfs_inode *ip, 16662306a36Sopenharmony_ci struct xfs_dinode *dip, 16762306a36Sopenharmony_ci int whichfork) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 17062306a36Sopenharmony_ci xfs_bmdr_block_t *dfp; 17162306a36Sopenharmony_ci struct xfs_ifork *ifp; 17262306a36Sopenharmony_ci /* REFERENCED */ 17362306a36Sopenharmony_ci int nrecs; 17462306a36Sopenharmony_ci int size; 17562306a36Sopenharmony_ci int level; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 17862306a36Sopenharmony_ci dfp = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork); 17962306a36Sopenharmony_ci size = XFS_BMAP_BROOT_SPACE(mp, dfp); 18062306a36Sopenharmony_ci nrecs = be16_to_cpu(dfp->bb_numrecs); 18162306a36Sopenharmony_ci level = be16_to_cpu(dfp->bb_level); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * blow out if -- fork has less extents than can fit in 18562306a36Sopenharmony_ci * fork (fork shouldn't be a btree format), root btree 18662306a36Sopenharmony_ci * block has more records than can fit into the fork, 18762306a36Sopenharmony_ci * or the number of extents is greater than the number of 18862306a36Sopenharmony_ci * blocks. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci if (unlikely(ifp->if_nextents <= XFS_IFORK_MAXEXT(ip, whichfork) || 19162306a36Sopenharmony_ci nrecs == 0 || 19262306a36Sopenharmony_ci XFS_BMDR_SPACE_CALC(nrecs) > 19362306a36Sopenharmony_ci XFS_DFORK_SIZE(dip, mp, whichfork) || 19462306a36Sopenharmony_ci ifp->if_nextents > ip->i_nblocks) || 19562306a36Sopenharmony_ci level == 0 || level > XFS_BM_MAXLEVELS(mp, whichfork)) { 19662306a36Sopenharmony_ci xfs_warn(mp, "corrupt inode %llu (btree).", 19762306a36Sopenharmony_ci (unsigned long long) ip->i_ino); 19862306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, 19962306a36Sopenharmony_ci "xfs_iformat_btree", dfp, size, 20062306a36Sopenharmony_ci __this_address); 20162306a36Sopenharmony_ci return -EFSCORRUPTED; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ifp->if_broot_bytes = size; 20562306a36Sopenharmony_ci ifp->if_broot = kmem_alloc(size, KM_NOFS); 20662306a36Sopenharmony_ci ASSERT(ifp->if_broot != NULL); 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * Copy and convert from the on-disk structure 20962306a36Sopenharmony_ci * to the in-memory structure. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci xfs_bmdr_to_bmbt(ip, dfp, XFS_DFORK_SIZE(dip, ip->i_mount, whichfork), 21262306a36Sopenharmony_ci ifp->if_broot, size); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci ifp->if_bytes = 0; 21562306a36Sopenharmony_ci ifp->if_u1.if_root = NULL; 21662306a36Sopenharmony_ci ifp->if_height = 0; 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciint 22162306a36Sopenharmony_cixfs_iformat_data_fork( 22262306a36Sopenharmony_ci struct xfs_inode *ip, 22362306a36Sopenharmony_ci struct xfs_dinode *dip) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct inode *inode = VFS_I(ip); 22662306a36Sopenharmony_ci int error; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Initialize the extent count early, as the per-format routines may 23062306a36Sopenharmony_ci * depend on it. Use release semantics to set needextents /after/ we 23162306a36Sopenharmony_ci * set the format. This ensures that we can use acquire semantics on 23262306a36Sopenharmony_ci * needextents in xfs_need_iread_extents() and be guaranteed to see a 23362306a36Sopenharmony_ci * valid format value after that load. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci ip->i_df.if_format = dip->di_format; 23662306a36Sopenharmony_ci ip->i_df.if_nextents = xfs_dfork_data_extents(dip); 23762306a36Sopenharmony_ci smp_store_release(&ip->i_df.if_needextents, 23862306a36Sopenharmony_ci ip->i_df.if_format == XFS_DINODE_FMT_BTREE ? 1 : 0); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci switch (inode->i_mode & S_IFMT) { 24162306a36Sopenharmony_ci case S_IFIFO: 24262306a36Sopenharmony_ci case S_IFCHR: 24362306a36Sopenharmony_ci case S_IFBLK: 24462306a36Sopenharmony_ci case S_IFSOCK: 24562306a36Sopenharmony_ci ip->i_disk_size = 0; 24662306a36Sopenharmony_ci inode->i_rdev = xfs_to_linux_dev_t(xfs_dinode_get_rdev(dip)); 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci case S_IFREG: 24962306a36Sopenharmony_ci case S_IFLNK: 25062306a36Sopenharmony_ci case S_IFDIR: 25162306a36Sopenharmony_ci switch (ip->i_df.if_format) { 25262306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 25362306a36Sopenharmony_ci error = xfs_iformat_local(ip, dip, XFS_DATA_FORK, 25462306a36Sopenharmony_ci be64_to_cpu(dip->di_size)); 25562306a36Sopenharmony_ci if (!error) 25662306a36Sopenharmony_ci error = xfs_ifork_verify_local_data(ip); 25762306a36Sopenharmony_ci return error; 25862306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 25962306a36Sopenharmony_ci return xfs_iformat_extents(ip, dip, XFS_DATA_FORK); 26062306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 26162306a36Sopenharmony_ci return xfs_iformat_btree(ip, dip, XFS_DATA_FORK); 26262306a36Sopenharmony_ci default: 26362306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, 26462306a36Sopenharmony_ci dip, sizeof(*dip), __this_address); 26562306a36Sopenharmony_ci return -EFSCORRUPTED; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci default: 26962306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip, 27062306a36Sopenharmony_ci sizeof(*dip), __this_address); 27162306a36Sopenharmony_ci return -EFSCORRUPTED; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic uint16_t 27662306a36Sopenharmony_cixfs_dfork_attr_shortform_size( 27762306a36Sopenharmony_ci struct xfs_dinode *dip) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct xfs_attr_shortform *atp = 28062306a36Sopenharmony_ci (struct xfs_attr_shortform *)XFS_DFORK_APTR(dip); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return be16_to_cpu(atp->hdr.totsize); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_civoid 28662306a36Sopenharmony_cixfs_ifork_init_attr( 28762306a36Sopenharmony_ci struct xfs_inode *ip, 28862306a36Sopenharmony_ci enum xfs_dinode_fmt format, 28962306a36Sopenharmony_ci xfs_extnum_t nextents) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * Initialize the extent count early, as the per-format routines may 29362306a36Sopenharmony_ci * depend on it. Use release semantics to set needextents /after/ we 29462306a36Sopenharmony_ci * set the format. This ensures that we can use acquire semantics on 29562306a36Sopenharmony_ci * needextents in xfs_need_iread_extents() and be guaranteed to see a 29662306a36Sopenharmony_ci * valid format value after that load. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci ip->i_af.if_format = format; 29962306a36Sopenharmony_ci ip->i_af.if_nextents = nextents; 30062306a36Sopenharmony_ci smp_store_release(&ip->i_af.if_needextents, 30162306a36Sopenharmony_ci ip->i_af.if_format == XFS_DINODE_FMT_BTREE ? 1 : 0); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_civoid 30562306a36Sopenharmony_cixfs_ifork_zap_attr( 30662306a36Sopenharmony_ci struct xfs_inode *ip) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci xfs_idestroy_fork(&ip->i_af); 30962306a36Sopenharmony_ci memset(&ip->i_af, 0, sizeof(struct xfs_ifork)); 31062306a36Sopenharmony_ci ip->i_af.if_format = XFS_DINODE_FMT_EXTENTS; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciint 31462306a36Sopenharmony_cixfs_iformat_attr_fork( 31562306a36Sopenharmony_ci struct xfs_inode *ip, 31662306a36Sopenharmony_ci struct xfs_dinode *dip) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci xfs_extnum_t naextents = xfs_dfork_attr_extents(dip); 31962306a36Sopenharmony_ci int error = 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * Initialize the extent count early, as the per-format routines may 32362306a36Sopenharmony_ci * depend on it. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci xfs_ifork_init_attr(ip, dip->di_aformat, naextents); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci switch (ip->i_af.if_format) { 32862306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 32962306a36Sopenharmony_ci error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, 33062306a36Sopenharmony_ci xfs_dfork_attr_shortform_size(dip)); 33162306a36Sopenharmony_ci if (!error) 33262306a36Sopenharmony_ci error = xfs_ifork_verify_local_attr(ip); 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 33562306a36Sopenharmony_ci error = xfs_iformat_extents(ip, dip, XFS_ATTR_FORK); 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 33862306a36Sopenharmony_ci error = xfs_iformat_btree(ip, dip, XFS_ATTR_FORK); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci default: 34162306a36Sopenharmony_ci xfs_inode_verifier_error(ip, error, __func__, dip, 34262306a36Sopenharmony_ci sizeof(*dip), __this_address); 34362306a36Sopenharmony_ci error = -EFSCORRUPTED; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (error) 34862306a36Sopenharmony_ci xfs_ifork_zap_attr(ip); 34962306a36Sopenharmony_ci return error; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* 35362306a36Sopenharmony_ci * Reallocate the space for if_broot based on the number of records 35462306a36Sopenharmony_ci * being added or deleted as indicated in rec_diff. Move the records 35562306a36Sopenharmony_ci * and pointers in if_broot to fit the new size. When shrinking this 35662306a36Sopenharmony_ci * will eliminate holes between the records and pointers created by 35762306a36Sopenharmony_ci * the caller. When growing this will create holes to be filled in 35862306a36Sopenharmony_ci * by the caller. 35962306a36Sopenharmony_ci * 36062306a36Sopenharmony_ci * The caller must not request to add more records than would fit in 36162306a36Sopenharmony_ci * the on-disk inode root. If the if_broot is currently NULL, then 36262306a36Sopenharmony_ci * if we are adding records, one will be allocated. The caller must also 36362306a36Sopenharmony_ci * not request that the number of records go below zero, although 36462306a36Sopenharmony_ci * it can go to zero. 36562306a36Sopenharmony_ci * 36662306a36Sopenharmony_ci * ip -- the inode whose if_broot area is changing 36762306a36Sopenharmony_ci * ext_diff -- the change in the number of records, positive or negative, 36862306a36Sopenharmony_ci * requested for the if_broot array. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_civoid 37162306a36Sopenharmony_cixfs_iroot_realloc( 37262306a36Sopenharmony_ci xfs_inode_t *ip, 37362306a36Sopenharmony_ci int rec_diff, 37462306a36Sopenharmony_ci int whichfork) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 37762306a36Sopenharmony_ci int cur_max; 37862306a36Sopenharmony_ci struct xfs_ifork *ifp; 37962306a36Sopenharmony_ci struct xfs_btree_block *new_broot; 38062306a36Sopenharmony_ci int new_max; 38162306a36Sopenharmony_ci size_t new_size; 38262306a36Sopenharmony_ci char *np; 38362306a36Sopenharmony_ci char *op; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Handle the degenerate case quietly. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci if (rec_diff == 0) { 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 39362306a36Sopenharmony_ci if (rec_diff > 0) { 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * If there wasn't any memory allocated before, just 39662306a36Sopenharmony_ci * allocate it now and get out. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci if (ifp->if_broot_bytes == 0) { 39962306a36Sopenharmony_ci new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, rec_diff); 40062306a36Sopenharmony_ci ifp->if_broot = kmem_alloc(new_size, KM_NOFS); 40162306a36Sopenharmony_ci ifp->if_broot_bytes = (int)new_size; 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * If there is already an existing if_broot, then we need 40762306a36Sopenharmony_ci * to realloc() it and shift the pointers to their new 40862306a36Sopenharmony_ci * location. The records don't change location because 40962306a36Sopenharmony_ci * they are kept butted up against the btree block header. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0); 41262306a36Sopenharmony_ci new_max = cur_max + rec_diff; 41362306a36Sopenharmony_ci new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, new_max); 41462306a36Sopenharmony_ci ifp->if_broot = krealloc(ifp->if_broot, new_size, 41562306a36Sopenharmony_ci GFP_NOFS | __GFP_NOFAIL); 41662306a36Sopenharmony_ci op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1, 41762306a36Sopenharmony_ci ifp->if_broot_bytes); 41862306a36Sopenharmony_ci np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1, 41962306a36Sopenharmony_ci (int)new_size); 42062306a36Sopenharmony_ci ifp->if_broot_bytes = (int)new_size; 42162306a36Sopenharmony_ci ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <= 42262306a36Sopenharmony_ci xfs_inode_fork_size(ip, whichfork)); 42362306a36Sopenharmony_ci memmove(np, op, cur_max * (uint)sizeof(xfs_fsblock_t)); 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * rec_diff is less than 0. In this case, we are shrinking the 42962306a36Sopenharmony_ci * if_broot buffer. It must already exist. If we go to zero 43062306a36Sopenharmony_ci * records, just get rid of the root and clear the status bit. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci ASSERT((ifp->if_broot != NULL) && (ifp->if_broot_bytes > 0)); 43362306a36Sopenharmony_ci cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0); 43462306a36Sopenharmony_ci new_max = cur_max + rec_diff; 43562306a36Sopenharmony_ci ASSERT(new_max >= 0); 43662306a36Sopenharmony_ci if (new_max > 0) 43762306a36Sopenharmony_ci new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, new_max); 43862306a36Sopenharmony_ci else 43962306a36Sopenharmony_ci new_size = 0; 44062306a36Sopenharmony_ci if (new_size > 0) { 44162306a36Sopenharmony_ci new_broot = kmem_alloc(new_size, KM_NOFS); 44262306a36Sopenharmony_ci /* 44362306a36Sopenharmony_ci * First copy over the btree block header. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci memcpy(new_broot, ifp->if_broot, 44662306a36Sopenharmony_ci XFS_BMBT_BLOCK_LEN(ip->i_mount)); 44762306a36Sopenharmony_ci } else { 44862306a36Sopenharmony_ci new_broot = NULL; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 45262306a36Sopenharmony_ci * Only copy the records and pointers if there are any. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (new_max > 0) { 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * First copy the records. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci op = (char *)XFS_BMBT_REC_ADDR(mp, ifp->if_broot, 1); 45962306a36Sopenharmony_ci np = (char *)XFS_BMBT_REC_ADDR(mp, new_broot, 1); 46062306a36Sopenharmony_ci memcpy(np, op, new_max * (uint)sizeof(xfs_bmbt_rec_t)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * Then copy the pointers. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1, 46662306a36Sopenharmony_ci ifp->if_broot_bytes); 46762306a36Sopenharmony_ci np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, new_broot, 1, 46862306a36Sopenharmony_ci (int)new_size); 46962306a36Sopenharmony_ci memcpy(np, op, new_max * (uint)sizeof(xfs_fsblock_t)); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci kmem_free(ifp->if_broot); 47262306a36Sopenharmony_ci ifp->if_broot = new_broot; 47362306a36Sopenharmony_ci ifp->if_broot_bytes = (int)new_size; 47462306a36Sopenharmony_ci if (ifp->if_broot) 47562306a36Sopenharmony_ci ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <= 47662306a36Sopenharmony_ci xfs_inode_fork_size(ip, whichfork)); 47762306a36Sopenharmony_ci return; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/* 48262306a36Sopenharmony_ci * This is called when the amount of space needed for if_data 48362306a36Sopenharmony_ci * is increased or decreased. The change in size is indicated by 48462306a36Sopenharmony_ci * the number of bytes that need to be added or deleted in the 48562306a36Sopenharmony_ci * byte_diff parameter. 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * If the amount of space needed has decreased below the size of the 48862306a36Sopenharmony_ci * inline buffer, then switch to using the inline buffer. Otherwise, 48962306a36Sopenharmony_ci * use kmem_realloc() or kmem_alloc() to adjust the size of the buffer 49062306a36Sopenharmony_ci * to what is needed. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * ip -- the inode whose if_data area is changing 49362306a36Sopenharmony_ci * byte_diff -- the change in the number of bytes, positive or negative, 49462306a36Sopenharmony_ci * requested for the if_data array. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_civoid 49762306a36Sopenharmony_cixfs_idata_realloc( 49862306a36Sopenharmony_ci struct xfs_inode *ip, 49962306a36Sopenharmony_ci int64_t byte_diff, 50062306a36Sopenharmony_ci int whichfork) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 50362306a36Sopenharmony_ci int64_t new_size = ifp->if_bytes + byte_diff; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ASSERT(new_size >= 0); 50662306a36Sopenharmony_ci ASSERT(new_size <= xfs_inode_fork_size(ip, whichfork)); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (byte_diff == 0) 50962306a36Sopenharmony_ci return; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (new_size == 0) { 51262306a36Sopenharmony_ci kmem_free(ifp->if_u1.if_data); 51362306a36Sopenharmony_ci ifp->if_u1.if_data = NULL; 51462306a36Sopenharmony_ci ifp->if_bytes = 0; 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ifp->if_u1.if_data = krealloc(ifp->if_u1.if_data, new_size, 51962306a36Sopenharmony_ci GFP_NOFS | __GFP_NOFAIL); 52062306a36Sopenharmony_ci ifp->if_bytes = new_size; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_civoid 52462306a36Sopenharmony_cixfs_idestroy_fork( 52562306a36Sopenharmony_ci struct xfs_ifork *ifp) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci if (ifp->if_broot != NULL) { 52862306a36Sopenharmony_ci kmem_free(ifp->if_broot); 52962306a36Sopenharmony_ci ifp->if_broot = NULL; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci switch (ifp->if_format) { 53362306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 53462306a36Sopenharmony_ci kmem_free(ifp->if_u1.if_data); 53562306a36Sopenharmony_ci ifp->if_u1.if_data = NULL; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 53862306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 53962306a36Sopenharmony_ci if (ifp->if_height) 54062306a36Sopenharmony_ci xfs_iext_destroy(ifp); 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/* 54662306a36Sopenharmony_ci * Convert in-core extents to on-disk form 54762306a36Sopenharmony_ci * 54862306a36Sopenharmony_ci * In the case of the data fork, the in-core and on-disk fork sizes can be 54962306a36Sopenharmony_ci * different due to delayed allocation extents. We only copy on-disk extents 55062306a36Sopenharmony_ci * here, so callers must always use the physical fork size to determine the 55162306a36Sopenharmony_ci * size of the buffer passed to this routine. We will return the size actually 55262306a36Sopenharmony_ci * used. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ciint 55562306a36Sopenharmony_cixfs_iextents_copy( 55662306a36Sopenharmony_ci struct xfs_inode *ip, 55762306a36Sopenharmony_ci struct xfs_bmbt_rec *dp, 55862306a36Sopenharmony_ci int whichfork) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci int state = xfs_bmap_fork_to_state(whichfork); 56162306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 56262306a36Sopenharmony_ci struct xfs_iext_cursor icur; 56362306a36Sopenharmony_ci struct xfs_bmbt_irec rec; 56462306a36Sopenharmony_ci int64_t copied = 0; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED)); 56762306a36Sopenharmony_ci ASSERT(ifp->if_bytes > 0); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci for_each_xfs_iext(ifp, &icur, &rec) { 57062306a36Sopenharmony_ci if (isnullstartblock(rec.br_startblock)) 57162306a36Sopenharmony_ci continue; 57262306a36Sopenharmony_ci ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL); 57362306a36Sopenharmony_ci xfs_bmbt_disk_set_all(dp, &rec); 57462306a36Sopenharmony_ci trace_xfs_write_extent(ip, &icur, state, _RET_IP_); 57562306a36Sopenharmony_ci copied += sizeof(struct xfs_bmbt_rec); 57662306a36Sopenharmony_ci dp++; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ASSERT(copied > 0); 58062306a36Sopenharmony_ci ASSERT(copied <= ifp->if_bytes); 58162306a36Sopenharmony_ci return copied; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* 58562306a36Sopenharmony_ci * Each of the following cases stores data into the same region 58662306a36Sopenharmony_ci * of the on-disk inode, so only one of them can be valid at 58762306a36Sopenharmony_ci * any given time. While it is possible to have conflicting formats 58862306a36Sopenharmony_ci * and log flags, e.g. having XFS_ILOG_?DATA set when the fork is 58962306a36Sopenharmony_ci * in EXTENTS format, this can only happen when the fork has 59062306a36Sopenharmony_ci * changed formats after being modified but before being flushed. 59162306a36Sopenharmony_ci * In these cases, the format always takes precedence, because the 59262306a36Sopenharmony_ci * format indicates the current state of the fork. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_civoid 59562306a36Sopenharmony_cixfs_iflush_fork( 59662306a36Sopenharmony_ci struct xfs_inode *ip, 59762306a36Sopenharmony_ci struct xfs_dinode *dip, 59862306a36Sopenharmony_ci struct xfs_inode_log_item *iip, 59962306a36Sopenharmony_ci int whichfork) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci char *cp; 60262306a36Sopenharmony_ci struct xfs_ifork *ifp; 60362306a36Sopenharmony_ci xfs_mount_t *mp; 60462306a36Sopenharmony_ci static const short brootflag[2] = 60562306a36Sopenharmony_ci { XFS_ILOG_DBROOT, XFS_ILOG_ABROOT }; 60662306a36Sopenharmony_ci static const short dataflag[2] = 60762306a36Sopenharmony_ci { XFS_ILOG_DDATA, XFS_ILOG_ADATA }; 60862306a36Sopenharmony_ci static const short extflag[2] = 60962306a36Sopenharmony_ci { XFS_ILOG_DEXT, XFS_ILOG_AEXT }; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (!iip) 61262306a36Sopenharmony_ci return; 61362306a36Sopenharmony_ci ifp = xfs_ifork_ptr(ip, whichfork); 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * This can happen if we gave up in iformat in an error path, 61662306a36Sopenharmony_ci * for the attribute fork. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci if (!ifp) { 61962306a36Sopenharmony_ci ASSERT(whichfork == XFS_ATTR_FORK); 62062306a36Sopenharmony_ci return; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci cp = XFS_DFORK_PTR(dip, whichfork); 62362306a36Sopenharmony_ci mp = ip->i_mount; 62462306a36Sopenharmony_ci switch (ifp->if_format) { 62562306a36Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 62662306a36Sopenharmony_ci if ((iip->ili_fields & dataflag[whichfork]) && 62762306a36Sopenharmony_ci (ifp->if_bytes > 0)) { 62862306a36Sopenharmony_ci ASSERT(ifp->if_u1.if_data != NULL); 62962306a36Sopenharmony_ci ASSERT(ifp->if_bytes <= xfs_inode_fork_size(ip, whichfork)); 63062306a36Sopenharmony_ci memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 63562306a36Sopenharmony_ci if ((iip->ili_fields & extflag[whichfork]) && 63662306a36Sopenharmony_ci (ifp->if_bytes > 0)) { 63762306a36Sopenharmony_ci ASSERT(ifp->if_nextents > 0); 63862306a36Sopenharmony_ci (void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp, 63962306a36Sopenharmony_ci whichfork); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 64462306a36Sopenharmony_ci if ((iip->ili_fields & brootflag[whichfork]) && 64562306a36Sopenharmony_ci (ifp->if_broot_bytes > 0)) { 64662306a36Sopenharmony_ci ASSERT(ifp->if_broot != NULL); 64762306a36Sopenharmony_ci ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <= 64862306a36Sopenharmony_ci xfs_inode_fork_size(ip, whichfork)); 64962306a36Sopenharmony_ci xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes, 65062306a36Sopenharmony_ci (xfs_bmdr_block_t *)cp, 65162306a36Sopenharmony_ci XFS_DFORK_SIZE(dip, mp, whichfork)); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci case XFS_DINODE_FMT_DEV: 65662306a36Sopenharmony_ci if (iip->ili_fields & XFS_ILOG_DEV) { 65762306a36Sopenharmony_ci ASSERT(whichfork == XFS_DATA_FORK); 65862306a36Sopenharmony_ci xfs_dinode_put_rdev(dip, 65962306a36Sopenharmony_ci linux_to_xfs_dev_t(VFS_I(ip)->i_rdev)); 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci default: 66462306a36Sopenharmony_ci ASSERT(0); 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/* Convert bmap state flags to an inode fork. */ 67062306a36Sopenharmony_cistruct xfs_ifork * 67162306a36Sopenharmony_cixfs_iext_state_to_fork( 67262306a36Sopenharmony_ci struct xfs_inode *ip, 67362306a36Sopenharmony_ci int state) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci if (state & BMAP_COWFORK) 67662306a36Sopenharmony_ci return ip->i_cowfp; 67762306a36Sopenharmony_ci else if (state & BMAP_ATTRFORK) 67862306a36Sopenharmony_ci return &ip->i_af; 67962306a36Sopenharmony_ci return &ip->i_df; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/* 68362306a36Sopenharmony_ci * Initialize an inode's copy-on-write fork. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_civoid 68662306a36Sopenharmony_cixfs_ifork_init_cow( 68762306a36Sopenharmony_ci struct xfs_inode *ip) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci if (ip->i_cowfp) 69062306a36Sopenharmony_ci return; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci ip->i_cowfp = kmem_cache_zalloc(xfs_ifork_cache, 69362306a36Sopenharmony_ci GFP_NOFS | __GFP_NOFAIL); 69462306a36Sopenharmony_ci ip->i_cowfp->if_format = XFS_DINODE_FMT_EXTENTS; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/* Verify the inline contents of the data fork of an inode. */ 69862306a36Sopenharmony_ciint 69962306a36Sopenharmony_cixfs_ifork_verify_local_data( 70062306a36Sopenharmony_ci struct xfs_inode *ip) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci xfs_failaddr_t fa = NULL; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci switch (VFS_I(ip)->i_mode & S_IFMT) { 70562306a36Sopenharmony_ci case S_IFDIR: 70662306a36Sopenharmony_ci fa = xfs_dir2_sf_verify(ip); 70762306a36Sopenharmony_ci break; 70862306a36Sopenharmony_ci case S_IFLNK: 70962306a36Sopenharmony_ci fa = xfs_symlink_shortform_verify(ip); 71062306a36Sopenharmony_ci break; 71162306a36Sopenharmony_ci default: 71262306a36Sopenharmony_ci break; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (fa) { 71662306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, "data fork", 71762306a36Sopenharmony_ci ip->i_df.if_u1.if_data, ip->i_df.if_bytes, fa); 71862306a36Sopenharmony_ci return -EFSCORRUPTED; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/* Verify the inline contents of the attr fork of an inode. */ 72562306a36Sopenharmony_ciint 72662306a36Sopenharmony_cixfs_ifork_verify_local_attr( 72762306a36Sopenharmony_ci struct xfs_inode *ip) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct xfs_ifork *ifp = &ip->i_af; 73062306a36Sopenharmony_ci xfs_failaddr_t fa; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (!xfs_inode_has_attr_fork(ip)) 73362306a36Sopenharmony_ci fa = __this_address; 73462306a36Sopenharmony_ci else 73562306a36Sopenharmony_ci fa = xfs_attr_shortform_verify(ip); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (fa) { 73862306a36Sopenharmony_ci xfs_inode_verifier_error(ip, -EFSCORRUPTED, "attr fork", 73962306a36Sopenharmony_ci ifp->if_u1.if_data, ifp->if_bytes, fa); 74062306a36Sopenharmony_ci return -EFSCORRUPTED; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ciint 74762306a36Sopenharmony_cixfs_iext_count_may_overflow( 74862306a36Sopenharmony_ci struct xfs_inode *ip, 74962306a36Sopenharmony_ci int whichfork, 75062306a36Sopenharmony_ci int nr_to_add) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); 75362306a36Sopenharmony_ci uint64_t max_exts; 75462306a36Sopenharmony_ci uint64_t nr_exts; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (whichfork == XFS_COW_FORK) 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci max_exts = xfs_iext_max_nextents(xfs_inode_has_large_extent_counts(ip), 76062306a36Sopenharmony_ci whichfork); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS)) 76362306a36Sopenharmony_ci max_exts = 10; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci nr_exts = ifp->if_nextents + nr_to_add; 76662306a36Sopenharmony_ci if (nr_exts < ifp->if_nextents || nr_exts > max_exts) 76762306a36Sopenharmony_ci return -EFBIG; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* 77362306a36Sopenharmony_ci * Upgrade this inode's extent counter fields to be able to handle a potential 77462306a36Sopenharmony_ci * increase in the extent count by nr_to_add. Normally this is the same 77562306a36Sopenharmony_ci * quantity that caused xfs_iext_count_may_overflow() to return -EFBIG. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ciint 77862306a36Sopenharmony_cixfs_iext_count_upgrade( 77962306a36Sopenharmony_ci struct xfs_trans *tp, 78062306a36Sopenharmony_ci struct xfs_inode *ip, 78162306a36Sopenharmony_ci uint nr_to_add) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci ASSERT(nr_to_add <= XFS_MAX_EXTCNT_UPGRADE_NR); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!xfs_has_large_extent_counts(ip->i_mount) || 78662306a36Sopenharmony_ci xfs_inode_has_large_extent_counts(ip) || 78762306a36Sopenharmony_ci XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS)) 78862306a36Sopenharmony_ci return -EFBIG; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci ip->i_diflags2 |= XFS_DIFLAG2_NREXT64; 79162306a36Sopenharmony_ci xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 795