1153323Srodrigc/* 2159451Srodrigc * Copyright (c) 2000-2006 Silicon Graphics, Inc. 3159451Srodrigc * All Rights Reserved. 4153323Srodrigc * 5159451Srodrigc * This program is free software; you can redistribute it and/or 6159451Srodrigc * modify it under the terms of the GNU General Public License as 7153323Srodrigc * published by the Free Software Foundation. 8153323Srodrigc * 9159451Srodrigc * This program is distributed in the hope that it would be useful, 10159451Srodrigc * but WITHOUT ANY WARRANTY; without even the implied warranty of 11159451Srodrigc * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12159451Srodrigc * GNU General Public License for more details. 13153323Srodrigc * 14159451Srodrigc * You should have received a copy of the GNU General Public License 15159451Srodrigc * along with this program; if not, write the Free Software Foundation, 16159451Srodrigc * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17153323Srodrigc */ 18153323Srodrigc#include "xfs.h" 19159451Srodrigc#include "xfs_fs.h" 20153323Srodrigc#include "xfs_types.h" 21159451Srodrigc#include "xfs_bit.h" 22159451Srodrigc#include "xfs_log.h" 23153323Srodrigc#include "xfs_inum.h" 24153323Srodrigc#include "xfs_trans.h" 25153323Srodrigc#include "xfs_sb.h" 26153323Srodrigc#include "xfs_ag.h" 27153323Srodrigc#include "xfs_dir.h" 28153323Srodrigc#include "xfs_dir2.h" 29159451Srodrigc#include "xfs_da_btree.h" 30159451Srodrigc#include "xfs_bmap_btree.h" 31153323Srodrigc#include "xfs_alloc_btree.h" 32153323Srodrigc#include "xfs_ialloc_btree.h" 33153323Srodrigc#include "xfs_dir_sf.h" 34153323Srodrigc#include "xfs_dir2_sf.h" 35159451Srodrigc#include "xfs_attr_sf.h" 36153323Srodrigc#include "xfs_dinode.h" 37153323Srodrigc#include "xfs_inode.h" 38159451Srodrigc#include "xfs_btree.h" 39159451Srodrigc#include "xfs_dmapi.h" 40159451Srodrigc#include "xfs_mount.h" 41159451Srodrigc#include "xfs_ialloc.h" 42153323Srodrigc#include "xfs_itable.h" 43159451Srodrigc#include "xfs_inode_item.h" 44153323Srodrigc#include "xfs_extfree_item.h" 45153323Srodrigc#include "xfs_alloc.h" 46153323Srodrigc#include "xfs_bmap.h" 47153323Srodrigc#include "xfs_rtalloc.h" 48153323Srodrigc#include "xfs_error.h" 49153323Srodrigc#include "xfs_dir_leaf.h" 50159451Srodrigc#include "xfs_attr_leaf.h" 51153323Srodrigc#include "xfs_rw.h" 52153323Srodrigc#include "xfs_quota.h" 53153323Srodrigc#include "xfs_trans_space.h" 54153323Srodrigc#include "xfs_buf_item.h" 55153323Srodrigc 56153323Srodrigc 57159451Srodrigc#ifdef DEBUG 58153323SrodrigcSTATIC void 59153323Srodrigcxfs_bmap_check_leaf_extents(xfs_btree_cur_t *cur, xfs_inode_t *ip, int whichfork); 60153323Srodrigc#endif 61153323Srodrigc 62153323Srodrigckmem_zone_t *xfs_bmap_free_item_zone; 63153323Srodrigc 64153323Srodrigc/* 65153323Srodrigc * Prototypes for internal bmap routines. 66153323Srodrigc */ 67153323Srodrigc 68153323Srodrigc 69153323Srodrigc/* 70153323Srodrigc * Called from xfs_bmap_add_attrfork to handle extents format files. 71153323Srodrigc */ 72153323SrodrigcSTATIC int /* error */ 73153323Srodrigcxfs_bmap_add_attrfork_extents( 74153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 75153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 76153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated */ 77153323Srodrigc xfs_bmap_free_t *flist, /* blocks to free at commit */ 78153323Srodrigc int *flags); /* inode logging flags */ 79153323Srodrigc 80153323Srodrigc/* 81153323Srodrigc * Called from xfs_bmap_add_attrfork to handle local format files. 82153323Srodrigc */ 83153323SrodrigcSTATIC int /* error */ 84153323Srodrigcxfs_bmap_add_attrfork_local( 85153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 86153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 87153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated */ 88153323Srodrigc xfs_bmap_free_t *flist, /* blocks to free at commit */ 89153323Srodrigc int *flags); /* inode logging flags */ 90153323Srodrigc 91153323Srodrigc/* 92159451Srodrigc * Called by xfs_bmapi to update file extent records and the btree 93153323Srodrigc * after allocating space (or doing a delayed allocation). 94153323Srodrigc */ 95153323SrodrigcSTATIC int /* error */ 96153323Srodrigcxfs_bmap_add_extent( 97153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 98153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 99153323Srodrigc xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ 100159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 101153323Srodrigc xfs_fsblock_t *first, /* pointer to firstblock variable */ 102153323Srodrigc xfs_bmap_free_t *flist, /* list of extents to be freed */ 103153323Srodrigc int *logflagsp, /* inode logging flags */ 104159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 105153323Srodrigc int whichfork, /* data or attr fork */ 106153323Srodrigc int rsvd); /* OK to allocate reserved blocks */ 107153323Srodrigc 108153323Srodrigc/* 109153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting a delayed 110153323Srodrigc * allocation to a real allocation. 111153323Srodrigc */ 112153323SrodrigcSTATIC int /* error */ 113153323Srodrigcxfs_bmap_add_extent_delay_real( 114153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 115153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 116153323Srodrigc xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ 117159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 118153323Srodrigc xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */ 119153323Srodrigc xfs_fsblock_t *first, /* pointer to firstblock variable */ 120153323Srodrigc xfs_bmap_free_t *flist, /* list of extents to be freed */ 121153323Srodrigc int *logflagsp, /* inode logging flags */ 122159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 123153323Srodrigc int rsvd); /* OK to allocate reserved blocks */ 124153323Srodrigc 125153323Srodrigc/* 126153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting a hole 127153323Srodrigc * to a delayed allocation. 128153323Srodrigc */ 129153323SrodrigcSTATIC int /* error */ 130153323Srodrigcxfs_bmap_add_extent_hole_delay( 131153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 132153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 133153323Srodrigc xfs_btree_cur_t *cur, /* if null, not a btree */ 134159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 135153323Srodrigc int *logflagsp,/* inode logging flags */ 136159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 137153323Srodrigc int rsvd); /* OK to allocate reserved blocks */ 138153323Srodrigc 139153323Srodrigc/* 140153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting a hole 141153323Srodrigc * to a real allocation. 142153323Srodrigc */ 143153323SrodrigcSTATIC int /* error */ 144153323Srodrigcxfs_bmap_add_extent_hole_real( 145153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 146153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 147153323Srodrigc xfs_btree_cur_t *cur, /* if null, not a btree */ 148159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 149153323Srodrigc int *logflagsp, /* inode logging flags */ 150159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 151153323Srodrigc int whichfork); /* data or attr fork */ 152153323Srodrigc 153153323Srodrigc/* 154153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting an unwritten 155153323Srodrigc * allocation to a real allocation or vice versa. 156153323Srodrigc */ 157153323SrodrigcSTATIC int /* error */ 158153323Srodrigcxfs_bmap_add_extent_unwritten_real( 159153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 160153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 161153323Srodrigc xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ 162159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 163159451Srodrigc int *logflagsp, /* inode logging flags */ 164159451Srodrigc xfs_extdelta_t *delta); /* Change made to incore extents */ 165153323Srodrigc 166153323Srodrigc/* 167153323Srodrigc * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file. 168153323Srodrigc * It figures out where to ask the underlying allocator to put the new extent. 169153323Srodrigc */ 170153323SrodrigcSTATIC int /* error */ 171153323Srodrigcxfs_bmap_alloc( 172153323Srodrigc xfs_bmalloca_t *ap); /* bmap alloc argument struct */ 173153323Srodrigc 174153323Srodrigc/* 175153323Srodrigc * Transform a btree format file with only one leaf node, where the 176153323Srodrigc * extents list will fit in the inode, into an extents format file. 177159451Srodrigc * Since the file extents are already in-core, all we have to do is 178153323Srodrigc * give up the space for the btree root and pitch the leaf block. 179153323Srodrigc */ 180153323SrodrigcSTATIC int /* error */ 181153323Srodrigcxfs_bmap_btree_to_extents( 182153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 183153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 184153323Srodrigc xfs_btree_cur_t *cur, /* btree cursor */ 185153323Srodrigc int *logflagsp, /* inode logging flags */ 186153323Srodrigc int whichfork); /* data or attr fork */ 187153323Srodrigc 188159451Srodrigc#ifdef DEBUG 189159451Srodrigc#if 0 190153323Srodrigc/* 191153323Srodrigc * Check that the extents list for the inode ip is in the right order. 192153323Srodrigc */ 193153323SrodrigcSTATIC void 194153323Srodrigcxfs_bmap_check_extents( 195153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 196153323Srodrigc int whichfork); /* data or attr fork */ 197153323Srodrigc#endif 198159451Srodrigc#endif 199153323Srodrigc 200153323Srodrigc/* 201159451Srodrigc * Called by xfs_bmapi to update file extent records and the btree 202153323Srodrigc * after removing space (or undoing a delayed allocation). 203153323Srodrigc */ 204153323SrodrigcSTATIC int /* error */ 205153323Srodrigcxfs_bmap_del_extent( 206153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 207153323Srodrigc xfs_trans_t *tp, /* current trans pointer */ 208153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 209153323Srodrigc xfs_bmap_free_t *flist, /* list of extents to be freed */ 210153323Srodrigc xfs_btree_cur_t *cur, /* if null, not a btree */ 211159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 212153323Srodrigc int *logflagsp,/* inode logging flags */ 213159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 214153323Srodrigc int whichfork, /* data or attr fork */ 215153323Srodrigc int rsvd); /* OK to allocate reserved blocks */ 216153323Srodrigc 217153323Srodrigc/* 218153323Srodrigc * Remove the entry "free" from the free item list. Prev points to the 219153323Srodrigc * previous entry, unless "free" is the head of the list. 220153323Srodrigc */ 221153323SrodrigcSTATIC void 222153323Srodrigcxfs_bmap_del_free( 223153323Srodrigc xfs_bmap_free_t *flist, /* free item list header */ 224153323Srodrigc xfs_bmap_free_item_t *prev, /* previous item on list, if any */ 225153323Srodrigc xfs_bmap_free_item_t *free); /* list item to be freed */ 226153323Srodrigc 227153323Srodrigc/* 228153323Srodrigc * Convert an extents-format file into a btree-format file. 229153323Srodrigc * The new file will have a root block (in the inode) and a single child block. 230153323Srodrigc */ 231153323SrodrigcSTATIC int /* error */ 232153323Srodrigcxfs_bmap_extents_to_btree( 233153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 234153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 235153323Srodrigc xfs_fsblock_t *firstblock, /* first-block-allocated */ 236153323Srodrigc xfs_bmap_free_t *flist, /* blocks freed in xaction */ 237153323Srodrigc xfs_btree_cur_t **curp, /* cursor returned to caller */ 238153323Srodrigc int wasdel, /* converting a delayed alloc */ 239153323Srodrigc int *logflagsp, /* inode logging flags */ 240153323Srodrigc int whichfork); /* data or attr fork */ 241153323Srodrigc 242153323Srodrigc/* 243153323Srodrigc * Convert a local file to an extents file. 244153323Srodrigc * This code is sort of bogus, since the file data needs to get 245153323Srodrigc * logged so it won't be lost. The bmap-level manipulations are ok, though. 246153323Srodrigc */ 247153323SrodrigcSTATIC int /* error */ 248153323Srodrigcxfs_bmap_local_to_extents( 249153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 250153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 251153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated in xaction */ 252153323Srodrigc xfs_extlen_t total, /* total blocks needed by transaction */ 253153323Srodrigc int *logflagsp, /* inode logging flags */ 254153323Srodrigc int whichfork); /* data or attr fork */ 255153323Srodrigc 256153323Srodrigc/* 257153323Srodrigc * Search the extents list for the inode, for the extent containing bno. 258153323Srodrigc * If bno lies in a hole, point to the next entry. If bno lies past eof, 259153323Srodrigc * *eofp will be set, and *prevp will contain the last entry (null if none). 260153323Srodrigc * Else, *lastxp will be set to the index of the found 261153323Srodrigc * entry; *gotp will contain the entry. 262153323Srodrigc */ 263153323SrodrigcSTATIC xfs_bmbt_rec_t * /* pointer to found extent entry */ 264153323Srodrigcxfs_bmap_search_extents( 265153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 266153323Srodrigc xfs_fileoff_t bno, /* block number searched for */ 267153323Srodrigc int whichfork, /* data or attr fork */ 268153323Srodrigc int *eofp, /* out: end of file found */ 269153323Srodrigc xfs_extnum_t *lastxp, /* out: last extent index */ 270153323Srodrigc xfs_bmbt_irec_t *gotp, /* out: extent entry found */ 271153323Srodrigc xfs_bmbt_irec_t *prevp); /* out: previous extent entry found */ 272153323Srodrigc 273159451Srodrigc/* 274159451Srodrigc * Check the last inode extent to determine whether this allocation will result 275159451Srodrigc * in blocks being allocated at the end of the file. When we allocate new data 276159451Srodrigc * blocks at the end of the file which do not start at the previous data block, 277159451Srodrigc * we will try to align the new blocks at stripe unit boundaries. 278159451Srodrigc */ 279159451SrodrigcSTATIC int /* error */ 280159451Srodrigcxfs_bmap_isaeof( 281159451Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 282159451Srodrigc xfs_fileoff_t off, /* file offset in fsblocks */ 283159451Srodrigc int whichfork, /* data or attribute fork */ 284159451Srodrigc char *aeof); /* return value */ 285159451Srodrigc 286153323Srodrigc#ifdef XFS_BMAP_TRACE 287153323Srodrigc/* 288153323Srodrigc * Add a bmap trace buffer entry. Base routine for the others. 289153323Srodrigc */ 290153323SrodrigcSTATIC void 291153323Srodrigcxfs_bmap_trace_addentry( 292153323Srodrigc int opcode, /* operation */ 293153323Srodrigc char *fname, /* function name */ 294153323Srodrigc char *desc, /* operation description */ 295153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 296153323Srodrigc xfs_extnum_t idx, /* index of entry(ies) */ 297153323Srodrigc xfs_extnum_t cnt, /* count of entries, 1 or 2 */ 298153323Srodrigc xfs_bmbt_rec_t *r1, /* first record */ 299153323Srodrigc xfs_bmbt_rec_t *r2, /* second record or null */ 300153323Srodrigc int whichfork); /* data or attr fork */ 301153323Srodrigc 302153323Srodrigc/* 303159451Srodrigc * Add bmap trace entry prior to a call to xfs_iext_remove. 304153323Srodrigc */ 305153323SrodrigcSTATIC void 306153323Srodrigcxfs_bmap_trace_delete( 307153323Srodrigc char *fname, /* function name */ 308153323Srodrigc char *desc, /* operation description */ 309153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 310153323Srodrigc xfs_extnum_t idx, /* index of entry(entries) deleted */ 311153323Srodrigc xfs_extnum_t cnt, /* count of entries deleted, 1 or 2 */ 312153323Srodrigc int whichfork); /* data or attr fork */ 313153323Srodrigc 314153323Srodrigc/* 315159451Srodrigc * Add bmap trace entry prior to a call to xfs_iext_insert, or 316153323Srodrigc * reading in the extents list from the disk (in the btree). 317153323Srodrigc */ 318153323SrodrigcSTATIC void 319153323Srodrigcxfs_bmap_trace_insert( 320153323Srodrigc char *fname, /* function name */ 321153323Srodrigc char *desc, /* operation description */ 322153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 323153323Srodrigc xfs_extnum_t idx, /* index of entry(entries) inserted */ 324153323Srodrigc xfs_extnum_t cnt, /* count of entries inserted, 1 or 2 */ 325153323Srodrigc xfs_bmbt_irec_t *r1, /* inserted record 1 */ 326153323Srodrigc xfs_bmbt_irec_t *r2, /* inserted record 2 or null */ 327153323Srodrigc int whichfork); /* data or attr fork */ 328153323Srodrigc 329153323Srodrigc/* 330159451Srodrigc * Add bmap trace entry after updating an extent record in place. 331153323Srodrigc */ 332153323SrodrigcSTATIC void 333153323Srodrigcxfs_bmap_trace_post_update( 334153323Srodrigc char *fname, /* function name */ 335153323Srodrigc char *desc, /* operation description */ 336153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 337153323Srodrigc xfs_extnum_t idx, /* index of entry updated */ 338153323Srodrigc int whichfork); /* data or attr fork */ 339153323Srodrigc 340153323Srodrigc/* 341159451Srodrigc * Add bmap trace entry prior to updating an extent record in place. 342153323Srodrigc */ 343153323SrodrigcSTATIC void 344153323Srodrigcxfs_bmap_trace_pre_update( 345153323Srodrigc char *fname, /* function name */ 346153323Srodrigc char *desc, /* operation description */ 347153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 348153323Srodrigc xfs_extnum_t idx, /* index of entry to be updated */ 349153323Srodrigc int whichfork); /* data or attr fork */ 350153323Srodrigc 351153323Srodrigc#else 352153323Srodrigc#define xfs_bmap_trace_delete(f,d,ip,i,c,w) 353153323Srodrigc#define xfs_bmap_trace_insert(f,d,ip,i,c,r1,r2,w) 354153323Srodrigc#define xfs_bmap_trace_post_update(f,d,ip,i,w) 355153323Srodrigc#define xfs_bmap_trace_pre_update(f,d,ip,i,w) 356153323Srodrigc#endif /* XFS_BMAP_TRACE */ 357153323Srodrigc 358153323Srodrigc/* 359153323Srodrigc * Compute the worst-case number of indirect blocks that will be used 360153323Srodrigc * for ip's delayed extent of length "len". 361153323Srodrigc */ 362153323SrodrigcSTATIC xfs_filblks_t 363153323Srodrigcxfs_bmap_worst_indlen( 364153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 365153323Srodrigc xfs_filblks_t len); /* delayed extent length */ 366153323Srodrigc 367153323Srodrigc#ifdef DEBUG 368153323Srodrigc/* 369153323Srodrigc * Perform various validation checks on the values being returned 370153323Srodrigc * from xfs_bmapi(). 371153323Srodrigc */ 372153323SrodrigcSTATIC void 373153323Srodrigcxfs_bmap_validate_ret( 374153323Srodrigc xfs_fileoff_t bno, 375153323Srodrigc xfs_filblks_t len, 376153323Srodrigc int flags, 377153323Srodrigc xfs_bmbt_irec_t *mval, 378153323Srodrigc int nmap, 379153323Srodrigc int ret_nmap); 380153323Srodrigc#else 381153323Srodrigc#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap) 382153323Srodrigc#endif /* DEBUG */ 383153323Srodrigc 384153323Srodrigc#if defined(XFS_RW_TRACE) 385153323SrodrigcSTATIC void 386153323Srodrigcxfs_bunmap_trace( 387153323Srodrigc xfs_inode_t *ip, 388153323Srodrigc xfs_fileoff_t bno, 389153323Srodrigc xfs_filblks_t len, 390153323Srodrigc int flags, 391153323Srodrigc inst_t *ra); 392153323Srodrigc#else 393153323Srodrigc#define xfs_bunmap_trace(ip, bno, len, flags, ra) 394153323Srodrigc#endif /* XFS_RW_TRACE */ 395153323Srodrigc 396153323SrodrigcSTATIC int 397153323Srodrigcxfs_bmap_count_tree( 398153323Srodrigc xfs_mount_t *mp, 399153323Srodrigc xfs_trans_t *tp, 400159451Srodrigc xfs_ifork_t *ifp, 401153323Srodrigc xfs_fsblock_t blockno, 402153323Srodrigc int levelin, 403153323Srodrigc int *count); 404153323Srodrigc 405153323SrodrigcSTATIC int 406153323Srodrigcxfs_bmap_count_leaves( 407159451Srodrigc xfs_ifork_t *ifp, 408159451Srodrigc xfs_extnum_t idx, 409153323Srodrigc int numrecs, 410153323Srodrigc int *count); 411153323Srodrigc 412159451SrodrigcSTATIC int 413159451Srodrigcxfs_bmap_disk_count_leaves( 414159451Srodrigc xfs_ifork_t *ifp, 415159451Srodrigc xfs_mount_t *mp, 416159451Srodrigc xfs_extnum_t idx, 417159451Srodrigc xfs_bmbt_block_t *block, 418159451Srodrigc int numrecs, 419159451Srodrigc int *count); 420159451Srodrigc 421153323Srodrigc/* 422153323Srodrigc * Bmap internal routines. 423153323Srodrigc */ 424153323Srodrigc 425153323Srodrigc/* 426153323Srodrigc * Called from xfs_bmap_add_attrfork to handle btree format files. 427153323Srodrigc */ 428153323SrodrigcSTATIC int /* error */ 429153323Srodrigcxfs_bmap_add_attrfork_btree( 430153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 431153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 432153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated */ 433153323Srodrigc xfs_bmap_free_t *flist, /* blocks to free at commit */ 434153323Srodrigc int *flags) /* inode logging flags */ 435153323Srodrigc{ 436153323Srodrigc xfs_btree_cur_t *cur; /* btree cursor */ 437153323Srodrigc int error; /* error return value */ 438153323Srodrigc xfs_mount_t *mp; /* file system mount struct */ 439153323Srodrigc int stat; /* newroot status */ 440153323Srodrigc 441153323Srodrigc mp = ip->i_mount; 442153323Srodrigc if (ip->i_df.if_broot_bytes <= XFS_IFORK_DSIZE(ip)) 443153323Srodrigc *flags |= XFS_ILOG_DBROOT; 444153323Srodrigc else { 445153323Srodrigc cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip, 446153323Srodrigc XFS_DATA_FORK); 447153323Srodrigc cur->bc_private.b.flist = flist; 448153323Srodrigc cur->bc_private.b.firstblock = *firstblock; 449153323Srodrigc if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat))) 450153323Srodrigc goto error0; 451153323Srodrigc ASSERT(stat == 1); /* must be at least one entry */ 452153323Srodrigc if ((error = xfs_bmbt_newroot(cur, flags, &stat))) 453153323Srodrigc goto error0; 454153323Srodrigc if (stat == 0) { 455153323Srodrigc xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); 456153323Srodrigc return XFS_ERROR(ENOSPC); 457153323Srodrigc } 458153323Srodrigc *firstblock = cur->bc_private.b.firstblock; 459153323Srodrigc cur->bc_private.b.allocated = 0; 460153323Srodrigc xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); 461153323Srodrigc } 462153323Srodrigc return 0; 463153323Srodrigcerror0: 464153323Srodrigc xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 465153323Srodrigc return error; 466153323Srodrigc} 467153323Srodrigc 468153323Srodrigc/* 469153323Srodrigc * Called from xfs_bmap_add_attrfork to handle extents format files. 470153323Srodrigc */ 471153323SrodrigcSTATIC int /* error */ 472153323Srodrigcxfs_bmap_add_attrfork_extents( 473153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 474153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 475153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated */ 476153323Srodrigc xfs_bmap_free_t *flist, /* blocks to free at commit */ 477153323Srodrigc int *flags) /* inode logging flags */ 478153323Srodrigc{ 479153323Srodrigc xfs_btree_cur_t *cur; /* bmap btree cursor */ 480153323Srodrigc int error; /* error return value */ 481153323Srodrigc 482153323Srodrigc if (ip->i_d.di_nextents * sizeof(xfs_bmbt_rec_t) <= XFS_IFORK_DSIZE(ip)) 483153323Srodrigc return 0; 484153323Srodrigc cur = NULL; 485153323Srodrigc error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist, &cur, 0, 486153323Srodrigc flags, XFS_DATA_FORK); 487153323Srodrigc if (cur) { 488153323Srodrigc cur->bc_private.b.allocated = 0; 489153323Srodrigc xfs_btree_del_cursor(cur, 490153323Srodrigc error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); 491153323Srodrigc } 492153323Srodrigc return error; 493153323Srodrigc} 494153323Srodrigc 495153323Srodrigc/* 496153323Srodrigc * Called from xfs_bmap_add_attrfork to handle local format files. 497153323Srodrigc */ 498153323SrodrigcSTATIC int /* error */ 499153323Srodrigcxfs_bmap_add_attrfork_local( 500153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 501153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 502153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated */ 503153323Srodrigc xfs_bmap_free_t *flist, /* blocks to free at commit */ 504153323Srodrigc int *flags) /* inode logging flags */ 505153323Srodrigc{ 506153323Srodrigc xfs_da_args_t dargs; /* args for dir/attr code */ 507153323Srodrigc int error; /* error return value */ 508153323Srodrigc xfs_mount_t *mp; /* mount structure pointer */ 509153323Srodrigc 510153323Srodrigc if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip)) 511153323Srodrigc return 0; 512153323Srodrigc if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) { 513153323Srodrigc mp = ip->i_mount; 514153323Srodrigc memset(&dargs, 0, sizeof(dargs)); 515153323Srodrigc dargs.dp = ip; 516153323Srodrigc dargs.firstblock = firstblock; 517153323Srodrigc dargs.flist = flist; 518153323Srodrigc dargs.total = mp->m_dirblkfsbs; 519153323Srodrigc dargs.whichfork = XFS_DATA_FORK; 520153323Srodrigc dargs.trans = tp; 521153323Srodrigc error = XFS_DIR_SHORTFORM_TO_SINGLE(mp, &dargs); 522153323Srodrigc } else 523153323Srodrigc error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags, 524153323Srodrigc XFS_DATA_FORK); 525153323Srodrigc return error; 526153323Srodrigc} 527153323Srodrigc 528153323Srodrigc/* 529159451Srodrigc * Called by xfs_bmapi to update file extent records and the btree 530153323Srodrigc * after allocating space (or doing a delayed allocation). 531153323Srodrigc */ 532153323SrodrigcSTATIC int /* error */ 533153323Srodrigcxfs_bmap_add_extent( 534153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 535153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 536153323Srodrigc xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ 537159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 538153323Srodrigc xfs_fsblock_t *first, /* pointer to firstblock variable */ 539153323Srodrigc xfs_bmap_free_t *flist, /* list of extents to be freed */ 540153323Srodrigc int *logflagsp, /* inode logging flags */ 541159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 542153323Srodrigc int whichfork, /* data or attr fork */ 543153323Srodrigc int rsvd) /* OK to use reserved data blocks */ 544153323Srodrigc{ 545153323Srodrigc xfs_btree_cur_t *cur; /* btree cursor or null */ 546153323Srodrigc xfs_filblks_t da_new; /* new count del alloc blocks used */ 547153323Srodrigc xfs_filblks_t da_old; /* old count del alloc blocks used */ 548153323Srodrigc int error; /* error return value */ 549153323Srodrigc#ifdef XFS_BMAP_TRACE 550153323Srodrigc static char fname[] = "xfs_bmap_add_extent"; 551153323Srodrigc#endif 552153323Srodrigc xfs_ifork_t *ifp; /* inode fork ptr */ 553153323Srodrigc int logflags; /* returned value */ 554153323Srodrigc xfs_extnum_t nextents; /* number of extents in file now */ 555153323Srodrigc 556153323Srodrigc XFS_STATS_INC(xs_add_exlist); 557153323Srodrigc cur = *curp; 558153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 559153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 560153323Srodrigc ASSERT(idx <= nextents); 561153323Srodrigc da_old = da_new = 0; 562153323Srodrigc error = 0; 563153323Srodrigc /* 564153323Srodrigc * This is the first extent added to a new/empty file. 565153323Srodrigc * Special case this one, so other routines get to assume there are 566153323Srodrigc * already extents in the list. 567153323Srodrigc */ 568153323Srodrigc if (nextents == 0) { 569153323Srodrigc xfs_bmap_trace_insert(fname, "insert empty", ip, 0, 1, new, 570153323Srodrigc NULL, whichfork); 571159451Srodrigc xfs_iext_insert(ifp, 0, 1, new); 572153323Srodrigc ASSERT(cur == NULL); 573153323Srodrigc ifp->if_lastex = 0; 574153323Srodrigc if (!ISNULLSTARTBLOCK(new->br_startblock)) { 575153323Srodrigc XFS_IFORK_NEXT_SET(ip, whichfork, 1); 576153323Srodrigc logflags = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); 577153323Srodrigc } else 578153323Srodrigc logflags = 0; 579159451Srodrigc /* DELTA: single new extent */ 580159451Srodrigc if (delta) { 581159451Srodrigc if (delta->xed_startoff > new->br_startoff) 582159451Srodrigc delta->xed_startoff = new->br_startoff; 583159451Srodrigc if (delta->xed_blockcount < 584159451Srodrigc new->br_startoff + new->br_blockcount) 585159451Srodrigc delta->xed_blockcount = new->br_startoff + 586159451Srodrigc new->br_blockcount; 587159451Srodrigc } 588153323Srodrigc } 589153323Srodrigc /* 590153323Srodrigc * Any kind of new delayed allocation goes here. 591153323Srodrigc */ 592153323Srodrigc else if (ISNULLSTARTBLOCK(new->br_startblock)) { 593153323Srodrigc if (cur) 594153323Srodrigc ASSERT((cur->bc_private.b.flags & 595153323Srodrigc XFS_BTCUR_BPRV_WASDEL) == 0); 596153323Srodrigc if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, cur, new, 597159451Srodrigc &logflags, delta, rsvd))) 598153323Srodrigc goto done; 599153323Srodrigc } 600153323Srodrigc /* 601153323Srodrigc * Real allocation off the end of the file. 602153323Srodrigc */ 603153323Srodrigc else if (idx == nextents) { 604153323Srodrigc if (cur) 605153323Srodrigc ASSERT((cur->bc_private.b.flags & 606153323Srodrigc XFS_BTCUR_BPRV_WASDEL) == 0); 607153323Srodrigc if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new, 608159451Srodrigc &logflags, delta, whichfork))) 609153323Srodrigc goto done; 610153323Srodrigc } else { 611153323Srodrigc xfs_bmbt_irec_t prev; /* old extent at offset idx */ 612153323Srodrigc 613153323Srodrigc /* 614153323Srodrigc * Get the record referred to by idx. 615153323Srodrigc */ 616159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &prev); 617153323Srodrigc /* 618153323Srodrigc * If it's a real allocation record, and the new allocation ends 619153323Srodrigc * after the start of the referred to record, then we're filling 620153323Srodrigc * in a delayed or unwritten allocation with a real one, or 621153323Srodrigc * converting real back to unwritten. 622153323Srodrigc */ 623153323Srodrigc if (!ISNULLSTARTBLOCK(new->br_startblock) && 624153323Srodrigc new->br_startoff + new->br_blockcount > prev.br_startoff) { 625153323Srodrigc if (prev.br_state != XFS_EXT_UNWRITTEN && 626153323Srodrigc ISNULLSTARTBLOCK(prev.br_startblock)) { 627153323Srodrigc da_old = STARTBLOCKVAL(prev.br_startblock); 628153323Srodrigc if (cur) 629153323Srodrigc ASSERT(cur->bc_private.b.flags & 630153323Srodrigc XFS_BTCUR_BPRV_WASDEL); 631153323Srodrigc if ((error = xfs_bmap_add_extent_delay_real(ip, 632153323Srodrigc idx, &cur, new, &da_new, first, flist, 633159451Srodrigc &logflags, delta, rsvd))) 634153323Srodrigc goto done; 635153323Srodrigc } else if (new->br_state == XFS_EXT_NORM) { 636153323Srodrigc ASSERT(new->br_state == XFS_EXT_NORM); 637153323Srodrigc if ((error = xfs_bmap_add_extent_unwritten_real( 638159451Srodrigc ip, idx, &cur, new, &logflags, delta))) 639153323Srodrigc goto done; 640153323Srodrigc } else { 641153323Srodrigc ASSERT(new->br_state == XFS_EXT_UNWRITTEN); 642153323Srodrigc if ((error = xfs_bmap_add_extent_unwritten_real( 643159451Srodrigc ip, idx, &cur, new, &logflags, delta))) 644153323Srodrigc goto done; 645153323Srodrigc } 646153323Srodrigc ASSERT(*curp == cur || *curp == NULL); 647153323Srodrigc } 648153323Srodrigc /* 649153323Srodrigc * Otherwise we're filling in a hole with an allocation. 650153323Srodrigc */ 651153323Srodrigc else { 652153323Srodrigc if (cur) 653153323Srodrigc ASSERT((cur->bc_private.b.flags & 654153323Srodrigc XFS_BTCUR_BPRV_WASDEL) == 0); 655153323Srodrigc if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, 656159451Srodrigc new, &logflags, delta, whichfork))) 657153323Srodrigc goto done; 658153323Srodrigc } 659153323Srodrigc } 660153323Srodrigc 661153323Srodrigc ASSERT(*curp == cur || *curp == NULL); 662153323Srodrigc /* 663153323Srodrigc * Convert to a btree if necessary. 664153323Srodrigc */ 665153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS && 666153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max) { 667153323Srodrigc int tmp_logflags; /* partial log flag return val */ 668153323Srodrigc 669153323Srodrigc ASSERT(cur == NULL); 670153323Srodrigc error = xfs_bmap_extents_to_btree(ip->i_transp, ip, first, 671153323Srodrigc flist, &cur, da_old > 0, &tmp_logflags, whichfork); 672153323Srodrigc logflags |= tmp_logflags; 673153323Srodrigc if (error) 674153323Srodrigc goto done; 675153323Srodrigc } 676153323Srodrigc /* 677153323Srodrigc * Adjust for changes in reserved delayed indirect blocks. 678153323Srodrigc * Nothing to do for disk quotas here. 679153323Srodrigc */ 680153323Srodrigc if (da_old || da_new) { 681153323Srodrigc xfs_filblks_t nblks; 682153323Srodrigc 683153323Srodrigc nblks = da_new; 684153323Srodrigc if (cur) 685153323Srodrigc nblks += cur->bc_private.b.allocated; 686153323Srodrigc ASSERT(nblks <= da_old); 687153323Srodrigc if (nblks < da_old) 688153323Srodrigc xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, 689153323Srodrigc (int)(da_old - nblks), rsvd); 690153323Srodrigc } 691153323Srodrigc /* 692153323Srodrigc * Clear out the allocated field, done with it now in any case. 693153323Srodrigc */ 694153323Srodrigc if (cur) { 695153323Srodrigc cur->bc_private.b.allocated = 0; 696153323Srodrigc *curp = cur; 697153323Srodrigc } 698153323Srodrigcdone: 699159451Srodrigc#ifdef DEBUG 700153323Srodrigc if (!error) 701153323Srodrigc xfs_bmap_check_leaf_extents(*curp, ip, whichfork); 702153323Srodrigc#endif 703153323Srodrigc *logflagsp = logflags; 704153323Srodrigc return error; 705153323Srodrigc} 706153323Srodrigc 707153323Srodrigc/* 708153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting a delayed 709153323Srodrigc * allocation to a real allocation. 710153323Srodrigc */ 711153323SrodrigcSTATIC int /* error */ 712153323Srodrigcxfs_bmap_add_extent_delay_real( 713153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 714153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 715153323Srodrigc xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ 716159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 717153323Srodrigc xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */ 718153323Srodrigc xfs_fsblock_t *first, /* pointer to firstblock variable */ 719153323Srodrigc xfs_bmap_free_t *flist, /* list of extents to be freed */ 720153323Srodrigc int *logflagsp, /* inode logging flags */ 721159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 722153323Srodrigc int rsvd) /* OK to use reserved data block allocation */ 723153323Srodrigc{ 724153323Srodrigc xfs_btree_cur_t *cur; /* btree cursor */ 725153323Srodrigc int diff; /* temp value */ 726153323Srodrigc xfs_bmbt_rec_t *ep; /* extent entry for idx */ 727153323Srodrigc int error; /* error return value */ 728153323Srodrigc#ifdef XFS_BMAP_TRACE 729153323Srodrigc static char fname[] = "xfs_bmap_add_extent_delay_real"; 730153323Srodrigc#endif 731153323Srodrigc int i; /* temp state */ 732159451Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 733153323Srodrigc xfs_fileoff_t new_endoff; /* end offset of new entry */ 734153323Srodrigc xfs_bmbt_irec_t r[3]; /* neighbor extent entries */ 735153323Srodrigc /* left is 0, right is 1, prev is 2 */ 736153323Srodrigc int rval=0; /* return value (logging flags) */ 737153323Srodrigc int state = 0;/* state bits, accessed thru macros */ 738159451Srodrigc xfs_filblks_t temp=0; /* value for dnew calculations */ 739159451Srodrigc xfs_filblks_t temp2=0;/* value for dnew calculations */ 740153323Srodrigc int tmp_rval; /* partial logging flags */ 741153323Srodrigc enum { /* bit number definitions for state */ 742153323Srodrigc LEFT_CONTIG, RIGHT_CONTIG, 743153323Srodrigc LEFT_FILLING, RIGHT_FILLING, 744153323Srodrigc LEFT_DELAY, RIGHT_DELAY, 745153323Srodrigc LEFT_VALID, RIGHT_VALID 746153323Srodrigc }; 747153323Srodrigc 748153323Srodrigc#define LEFT r[0] 749153323Srodrigc#define RIGHT r[1] 750153323Srodrigc#define PREV r[2] 751153323Srodrigc#define MASK(b) (1 << (b)) 752153323Srodrigc#define MASK2(a,b) (MASK(a) | MASK(b)) 753153323Srodrigc#define MASK3(a,b,c) (MASK2(a,b) | MASK(c)) 754153323Srodrigc#define MASK4(a,b,c,d) (MASK3(a,b,c) | MASK(d)) 755153323Srodrigc#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b))) 756153323Srodrigc#define STATE_TEST(b) (state & MASK(b)) 757153323Srodrigc#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \ 758153323Srodrigc ((state &= ~MASK(b)), 0)) 759153323Srodrigc#define SWITCH_STATE \ 760153323Srodrigc (state & MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG)) 761153323Srodrigc 762153323Srodrigc /* 763153323Srodrigc * Set up a bunch of variables to make the tests simpler. 764153323Srodrigc */ 765153323Srodrigc cur = *curp; 766159451Srodrigc ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); 767159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 768153323Srodrigc xfs_bmbt_get_all(ep, &PREV); 769153323Srodrigc new_endoff = new->br_startoff + new->br_blockcount; 770153323Srodrigc ASSERT(PREV.br_startoff <= new->br_startoff); 771153323Srodrigc ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff); 772153323Srodrigc /* 773153323Srodrigc * Set flags determining what part of the previous delayed allocation 774153323Srodrigc * extent is being replaced by a real allocation. 775153323Srodrigc */ 776153323Srodrigc STATE_SET(LEFT_FILLING, PREV.br_startoff == new->br_startoff); 777153323Srodrigc STATE_SET(RIGHT_FILLING, 778153323Srodrigc PREV.br_startoff + PREV.br_blockcount == new_endoff); 779153323Srodrigc /* 780153323Srodrigc * Check and set flags if this segment has a left neighbor. 781153323Srodrigc * Don't set contiguous if the combined extent would be too large. 782153323Srodrigc */ 783153323Srodrigc if (STATE_SET_TEST(LEFT_VALID, idx > 0)) { 784159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &LEFT); 785153323Srodrigc STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(LEFT.br_startblock)); 786153323Srodrigc } 787153323Srodrigc STATE_SET(LEFT_CONTIG, 788153323Srodrigc STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) && 789153323Srodrigc LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff && 790153323Srodrigc LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock && 791153323Srodrigc LEFT.br_state == new->br_state && 792153323Srodrigc LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN); 793153323Srodrigc /* 794153323Srodrigc * Check and set flags if this segment has a right neighbor. 795153323Srodrigc * Don't set contiguous if the combined extent would be too large. 796153323Srodrigc * Also check for all-three-contiguous being too large. 797153323Srodrigc */ 798153323Srodrigc if (STATE_SET_TEST(RIGHT_VALID, 799153323Srodrigc idx < 800153323Srodrigc ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1)) { 801159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx + 1), &RIGHT); 802153323Srodrigc STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(RIGHT.br_startblock)); 803153323Srodrigc } 804153323Srodrigc STATE_SET(RIGHT_CONTIG, 805153323Srodrigc STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) && 806153323Srodrigc new_endoff == RIGHT.br_startoff && 807153323Srodrigc new->br_startblock + new->br_blockcount == 808153323Srodrigc RIGHT.br_startblock && 809153323Srodrigc new->br_state == RIGHT.br_state && 810153323Srodrigc new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN && 811153323Srodrigc ((state & MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING)) != 812153323Srodrigc MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING) || 813153323Srodrigc LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount 814153323Srodrigc <= MAXEXTLEN)); 815153323Srodrigc error = 0; 816153323Srodrigc /* 817153323Srodrigc * Switch out based on the FILLING and CONTIG state bits. 818153323Srodrigc */ 819153323Srodrigc switch (SWITCH_STATE) { 820153323Srodrigc 821153323Srodrigc case MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): 822153323Srodrigc /* 823153323Srodrigc * Filling in all of a previously delayed allocation extent. 824153323Srodrigc * The left and right neighbors are both contiguous with new. 825153323Srodrigc */ 826153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1, 827153323Srodrigc XFS_DATA_FORK); 828159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 829153323Srodrigc LEFT.br_blockcount + PREV.br_blockcount + 830153323Srodrigc RIGHT.br_blockcount); 831153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1, 832153323Srodrigc XFS_DATA_FORK); 833153323Srodrigc xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2, 834153323Srodrigc XFS_DATA_FORK); 835159451Srodrigc xfs_iext_remove(ifp, idx, 2); 836153323Srodrigc ip->i_df.if_lastex = idx - 1; 837153323Srodrigc ip->i_d.di_nextents--; 838153323Srodrigc if (cur == NULL) 839153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 840153323Srodrigc else { 841153323Srodrigc rval = XFS_ILOG_CORE; 842153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff, 843153323Srodrigc RIGHT.br_startblock, 844153323Srodrigc RIGHT.br_blockcount, &i))) 845153323Srodrigc goto done; 846153323Srodrigc ASSERT(i == 1); 847153323Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 848153323Srodrigc goto done; 849153323Srodrigc ASSERT(i == 1); 850153323Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 851153323Srodrigc goto done; 852153323Srodrigc ASSERT(i == 1); 853153323Srodrigc if ((error = xfs_bmbt_update(cur, LEFT.br_startoff, 854153323Srodrigc LEFT.br_startblock, 855153323Srodrigc LEFT.br_blockcount + 856153323Srodrigc PREV.br_blockcount + 857153323Srodrigc RIGHT.br_blockcount, LEFT.br_state))) 858153323Srodrigc goto done; 859153323Srodrigc } 860153323Srodrigc *dnew = 0; 861159451Srodrigc /* DELTA: Three in-core extents are replaced by one. */ 862159451Srodrigc temp = LEFT.br_startoff; 863159451Srodrigc temp2 = LEFT.br_blockcount + 864159451Srodrigc PREV.br_blockcount + 865159451Srodrigc RIGHT.br_blockcount; 866153323Srodrigc break; 867153323Srodrigc 868153323Srodrigc case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG): 869153323Srodrigc /* 870153323Srodrigc * Filling in all of a previously delayed allocation extent. 871153323Srodrigc * The left neighbor is contiguous, the right is not. 872153323Srodrigc */ 873153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1, 874153323Srodrigc XFS_DATA_FORK); 875159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 876153323Srodrigc LEFT.br_blockcount + PREV.br_blockcount); 877153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1, 878153323Srodrigc XFS_DATA_FORK); 879153323Srodrigc ip->i_df.if_lastex = idx - 1; 880153323Srodrigc xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1, 881153323Srodrigc XFS_DATA_FORK); 882159451Srodrigc xfs_iext_remove(ifp, idx, 1); 883153323Srodrigc if (cur == NULL) 884153323Srodrigc rval = XFS_ILOG_DEXT; 885153323Srodrigc else { 886153323Srodrigc rval = 0; 887153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, LEFT.br_startoff, 888153323Srodrigc LEFT.br_startblock, LEFT.br_blockcount, 889153323Srodrigc &i))) 890153323Srodrigc goto done; 891153323Srodrigc ASSERT(i == 1); 892153323Srodrigc if ((error = xfs_bmbt_update(cur, LEFT.br_startoff, 893153323Srodrigc LEFT.br_startblock, 894153323Srodrigc LEFT.br_blockcount + 895153323Srodrigc PREV.br_blockcount, LEFT.br_state))) 896153323Srodrigc goto done; 897153323Srodrigc } 898153323Srodrigc *dnew = 0; 899159451Srodrigc /* DELTA: Two in-core extents are replaced by one. */ 900159451Srodrigc temp = LEFT.br_startoff; 901159451Srodrigc temp2 = LEFT.br_blockcount + 902159451Srodrigc PREV.br_blockcount; 903153323Srodrigc break; 904153323Srodrigc 905153323Srodrigc case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG): 906153323Srodrigc /* 907153323Srodrigc * Filling in all of a previously delayed allocation extent. 908153323Srodrigc * The right neighbor is contiguous, the left is not. 909153323Srodrigc */ 910153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx, 911153323Srodrigc XFS_DATA_FORK); 912153323Srodrigc xfs_bmbt_set_startblock(ep, new->br_startblock); 913153323Srodrigc xfs_bmbt_set_blockcount(ep, 914153323Srodrigc PREV.br_blockcount + RIGHT.br_blockcount); 915153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx, 916153323Srodrigc XFS_DATA_FORK); 917153323Srodrigc ip->i_df.if_lastex = idx; 918153323Srodrigc xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1, 919153323Srodrigc XFS_DATA_FORK); 920159451Srodrigc xfs_iext_remove(ifp, idx + 1, 1); 921153323Srodrigc if (cur == NULL) 922153323Srodrigc rval = XFS_ILOG_DEXT; 923153323Srodrigc else { 924153323Srodrigc rval = 0; 925153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff, 926153323Srodrigc RIGHT.br_startblock, 927153323Srodrigc RIGHT.br_blockcount, &i))) 928153323Srodrigc goto done; 929153323Srodrigc ASSERT(i == 1); 930153323Srodrigc if ((error = xfs_bmbt_update(cur, PREV.br_startoff, 931153323Srodrigc new->br_startblock, 932153323Srodrigc PREV.br_blockcount + 933153323Srodrigc RIGHT.br_blockcount, PREV.br_state))) 934153323Srodrigc goto done; 935153323Srodrigc } 936153323Srodrigc *dnew = 0; 937159451Srodrigc /* DELTA: Two in-core extents are replaced by one. */ 938159451Srodrigc temp = PREV.br_startoff; 939159451Srodrigc temp2 = PREV.br_blockcount + 940159451Srodrigc RIGHT.br_blockcount; 941153323Srodrigc break; 942153323Srodrigc 943153323Srodrigc case MASK2(LEFT_FILLING, RIGHT_FILLING): 944153323Srodrigc /* 945153323Srodrigc * Filling in all of a previously delayed allocation extent. 946153323Srodrigc * Neither the left nor right neighbors are contiguous with 947153323Srodrigc * the new one. 948153323Srodrigc */ 949153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx, 950153323Srodrigc XFS_DATA_FORK); 951153323Srodrigc xfs_bmbt_set_startblock(ep, new->br_startblock); 952153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx, 953153323Srodrigc XFS_DATA_FORK); 954153323Srodrigc ip->i_df.if_lastex = idx; 955153323Srodrigc ip->i_d.di_nextents++; 956153323Srodrigc if (cur == NULL) 957153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 958153323Srodrigc else { 959153323Srodrigc rval = XFS_ILOG_CORE; 960153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, 961153323Srodrigc new->br_startblock, new->br_blockcount, 962153323Srodrigc &i))) 963153323Srodrigc goto done; 964153323Srodrigc ASSERT(i == 0); 965153323Srodrigc cur->bc_rec.b.br_state = XFS_EXT_NORM; 966153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 967153323Srodrigc goto done; 968153323Srodrigc ASSERT(i == 1); 969153323Srodrigc } 970153323Srodrigc *dnew = 0; 971159451Srodrigc /* DELTA: The in-core extent described by new changed type. */ 972159451Srodrigc temp = new->br_startoff; 973159451Srodrigc temp2 = new->br_blockcount; 974153323Srodrigc break; 975153323Srodrigc 976153323Srodrigc case MASK2(LEFT_FILLING, LEFT_CONTIG): 977153323Srodrigc /* 978153323Srodrigc * Filling in the first part of a previous delayed allocation. 979153323Srodrigc * The left neighbor is contiguous. 980153323Srodrigc */ 981153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1, 982153323Srodrigc XFS_DATA_FORK); 983159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 984153323Srodrigc LEFT.br_blockcount + new->br_blockcount); 985153323Srodrigc xfs_bmbt_set_startoff(ep, 986153323Srodrigc PREV.br_startoff + new->br_blockcount); 987153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1, 988153323Srodrigc XFS_DATA_FORK); 989153323Srodrigc temp = PREV.br_blockcount - new->br_blockcount; 990153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx, 991153323Srodrigc XFS_DATA_FORK); 992153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 993153323Srodrigc ip->i_df.if_lastex = idx - 1; 994153323Srodrigc if (cur == NULL) 995153323Srodrigc rval = XFS_ILOG_DEXT; 996153323Srodrigc else { 997153323Srodrigc rval = 0; 998153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, LEFT.br_startoff, 999153323Srodrigc LEFT.br_startblock, LEFT.br_blockcount, 1000153323Srodrigc &i))) 1001153323Srodrigc goto done; 1002153323Srodrigc ASSERT(i == 1); 1003153323Srodrigc if ((error = xfs_bmbt_update(cur, LEFT.br_startoff, 1004153323Srodrigc LEFT.br_startblock, 1005153323Srodrigc LEFT.br_blockcount + 1006153323Srodrigc new->br_blockcount, 1007153323Srodrigc LEFT.br_state))) 1008153323Srodrigc goto done; 1009153323Srodrigc } 1010153323Srodrigc temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 1011153323Srodrigc STARTBLOCKVAL(PREV.br_startblock)); 1012153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 1013153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx, 1014153323Srodrigc XFS_DATA_FORK); 1015153323Srodrigc *dnew = temp; 1016159451Srodrigc /* DELTA: The boundary between two in-core extents moved. */ 1017159451Srodrigc temp = LEFT.br_startoff; 1018159451Srodrigc temp2 = LEFT.br_blockcount + 1019159451Srodrigc PREV.br_blockcount; 1020153323Srodrigc break; 1021153323Srodrigc 1022153323Srodrigc case MASK(LEFT_FILLING): 1023153323Srodrigc /* 1024153323Srodrigc * Filling in the first part of a previous delayed allocation. 1025153323Srodrigc * The left neighbor is not contiguous. 1026153323Srodrigc */ 1027153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK); 1028153323Srodrigc xfs_bmbt_set_startoff(ep, new_endoff); 1029153323Srodrigc temp = PREV.br_blockcount - new->br_blockcount; 1030153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 1031153323Srodrigc xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL, 1032153323Srodrigc XFS_DATA_FORK); 1033159451Srodrigc xfs_iext_insert(ifp, idx, 1, new); 1034153323Srodrigc ip->i_df.if_lastex = idx; 1035153323Srodrigc ip->i_d.di_nextents++; 1036153323Srodrigc if (cur == NULL) 1037153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1038153323Srodrigc else { 1039153323Srodrigc rval = XFS_ILOG_CORE; 1040153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, 1041153323Srodrigc new->br_startblock, new->br_blockcount, 1042153323Srodrigc &i))) 1043153323Srodrigc goto done; 1044153323Srodrigc ASSERT(i == 0); 1045153323Srodrigc cur->bc_rec.b.br_state = XFS_EXT_NORM; 1046153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1047153323Srodrigc goto done; 1048153323Srodrigc ASSERT(i == 1); 1049153323Srodrigc } 1050153323Srodrigc if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && 1051153323Srodrigc ip->i_d.di_nextents > ip->i_df.if_ext_max) { 1052153323Srodrigc error = xfs_bmap_extents_to_btree(ip->i_transp, ip, 1053153323Srodrigc first, flist, &cur, 1, &tmp_rval, 1054153323Srodrigc XFS_DATA_FORK); 1055153323Srodrigc rval |= tmp_rval; 1056153323Srodrigc if (error) 1057153323Srodrigc goto done; 1058153323Srodrigc } 1059153323Srodrigc temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 1060153323Srodrigc STARTBLOCKVAL(PREV.br_startblock) - 1061153323Srodrigc (cur ? cur->bc_private.b.allocated : 0)); 1062159451Srodrigc ep = xfs_iext_get_ext(ifp, idx + 1); 1063153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 1064153323Srodrigc xfs_bmap_trace_post_update(fname, "LF", ip, idx + 1, 1065153323Srodrigc XFS_DATA_FORK); 1066153323Srodrigc *dnew = temp; 1067159451Srodrigc /* DELTA: One in-core extent is split in two. */ 1068159451Srodrigc temp = PREV.br_startoff; 1069159451Srodrigc temp2 = PREV.br_blockcount; 1070153323Srodrigc break; 1071153323Srodrigc 1072153323Srodrigc case MASK2(RIGHT_FILLING, RIGHT_CONTIG): 1073153323Srodrigc /* 1074153323Srodrigc * Filling in the last part of a previous delayed allocation. 1075153323Srodrigc * The right neighbor is contiguous with the new allocation. 1076153323Srodrigc */ 1077153323Srodrigc temp = PREV.br_blockcount - new->br_blockcount; 1078153323Srodrigc xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx, 1079153323Srodrigc XFS_DATA_FORK); 1080153323Srodrigc xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1, 1081153323Srodrigc XFS_DATA_FORK); 1082153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 1083159451Srodrigc xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1), 1084159451Srodrigc new->br_startoff, new->br_startblock, 1085153323Srodrigc new->br_blockcount + RIGHT.br_blockcount, 1086153323Srodrigc RIGHT.br_state); 1087153323Srodrigc xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1, 1088153323Srodrigc XFS_DATA_FORK); 1089153323Srodrigc ip->i_df.if_lastex = idx + 1; 1090153323Srodrigc if (cur == NULL) 1091153323Srodrigc rval = XFS_ILOG_DEXT; 1092153323Srodrigc else { 1093153323Srodrigc rval = 0; 1094153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff, 1095153323Srodrigc RIGHT.br_startblock, 1096153323Srodrigc RIGHT.br_blockcount, &i))) 1097153323Srodrigc goto done; 1098153323Srodrigc ASSERT(i == 1); 1099153323Srodrigc if ((error = xfs_bmbt_update(cur, new->br_startoff, 1100153323Srodrigc new->br_startblock, 1101153323Srodrigc new->br_blockcount + 1102153323Srodrigc RIGHT.br_blockcount, 1103153323Srodrigc RIGHT.br_state))) 1104153323Srodrigc goto done; 1105153323Srodrigc } 1106153323Srodrigc temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 1107153323Srodrigc STARTBLOCKVAL(PREV.br_startblock)); 1108153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 1109153323Srodrigc xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx, 1110153323Srodrigc XFS_DATA_FORK); 1111153323Srodrigc *dnew = temp; 1112159451Srodrigc /* DELTA: The boundary between two in-core extents moved. */ 1113159451Srodrigc temp = PREV.br_startoff; 1114159451Srodrigc temp2 = PREV.br_blockcount + 1115159451Srodrigc RIGHT.br_blockcount; 1116153323Srodrigc break; 1117153323Srodrigc 1118153323Srodrigc case MASK(RIGHT_FILLING): 1119153323Srodrigc /* 1120153323Srodrigc * Filling in the last part of a previous delayed allocation. 1121153323Srodrigc * The right neighbor is not contiguous. 1122153323Srodrigc */ 1123153323Srodrigc temp = PREV.br_blockcount - new->br_blockcount; 1124153323Srodrigc xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK); 1125153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 1126153323Srodrigc xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1, 1127153323Srodrigc new, NULL, XFS_DATA_FORK); 1128159451Srodrigc xfs_iext_insert(ifp, idx + 1, 1, new); 1129153323Srodrigc ip->i_df.if_lastex = idx + 1; 1130153323Srodrigc ip->i_d.di_nextents++; 1131153323Srodrigc if (cur == NULL) 1132153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1133153323Srodrigc else { 1134153323Srodrigc rval = XFS_ILOG_CORE; 1135153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, 1136153323Srodrigc new->br_startblock, new->br_blockcount, 1137153323Srodrigc &i))) 1138153323Srodrigc goto done; 1139153323Srodrigc ASSERT(i == 0); 1140153323Srodrigc cur->bc_rec.b.br_state = XFS_EXT_NORM; 1141153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1142153323Srodrigc goto done; 1143153323Srodrigc ASSERT(i == 1); 1144153323Srodrigc } 1145153323Srodrigc if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && 1146153323Srodrigc ip->i_d.di_nextents > ip->i_df.if_ext_max) { 1147153323Srodrigc error = xfs_bmap_extents_to_btree(ip->i_transp, ip, 1148153323Srodrigc first, flist, &cur, 1, &tmp_rval, 1149153323Srodrigc XFS_DATA_FORK); 1150153323Srodrigc rval |= tmp_rval; 1151153323Srodrigc if (error) 1152153323Srodrigc goto done; 1153153323Srodrigc } 1154153323Srodrigc temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 1155153323Srodrigc STARTBLOCKVAL(PREV.br_startblock) - 1156153323Srodrigc (cur ? cur->bc_private.b.allocated : 0)); 1157159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 1158153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 1159153323Srodrigc xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK); 1160153323Srodrigc *dnew = temp; 1161159451Srodrigc /* DELTA: One in-core extent is split in two. */ 1162159451Srodrigc temp = PREV.br_startoff; 1163159451Srodrigc temp2 = PREV.br_blockcount; 1164153323Srodrigc break; 1165153323Srodrigc 1166153323Srodrigc case 0: 1167153323Srodrigc /* 1168153323Srodrigc * Filling in the middle part of a previous delayed allocation. 1169153323Srodrigc * Contiguity is impossible here. 1170153323Srodrigc * This case is avoided almost all the time. 1171153323Srodrigc */ 1172153323Srodrigc temp = new->br_startoff - PREV.br_startoff; 1173153323Srodrigc xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK); 1174153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 1175153323Srodrigc r[0] = *new; 1176153323Srodrigc r[1].br_startoff = new_endoff; 1177153323Srodrigc temp2 = PREV.br_startoff + PREV.br_blockcount - new_endoff; 1178153323Srodrigc r[1].br_blockcount = temp2; 1179153323Srodrigc xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1], 1180153323Srodrigc XFS_DATA_FORK); 1181159451Srodrigc xfs_iext_insert(ifp, idx + 1, 2, &r[0]); 1182153323Srodrigc ip->i_df.if_lastex = idx + 1; 1183153323Srodrigc ip->i_d.di_nextents++; 1184153323Srodrigc if (cur == NULL) 1185153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1186153323Srodrigc else { 1187153323Srodrigc rval = XFS_ILOG_CORE; 1188153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, 1189153323Srodrigc new->br_startblock, new->br_blockcount, 1190153323Srodrigc &i))) 1191153323Srodrigc goto done; 1192153323Srodrigc ASSERT(i == 0); 1193153323Srodrigc cur->bc_rec.b.br_state = XFS_EXT_NORM; 1194153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1195153323Srodrigc goto done; 1196153323Srodrigc ASSERT(i == 1); 1197153323Srodrigc } 1198153323Srodrigc if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS && 1199153323Srodrigc ip->i_d.di_nextents > ip->i_df.if_ext_max) { 1200153323Srodrigc error = xfs_bmap_extents_to_btree(ip->i_transp, ip, 1201153323Srodrigc first, flist, &cur, 1, &tmp_rval, 1202153323Srodrigc XFS_DATA_FORK); 1203153323Srodrigc rval |= tmp_rval; 1204153323Srodrigc if (error) 1205153323Srodrigc goto done; 1206153323Srodrigc } 1207153323Srodrigc temp = xfs_bmap_worst_indlen(ip, temp); 1208153323Srodrigc temp2 = xfs_bmap_worst_indlen(ip, temp2); 1209153323Srodrigc diff = (int)(temp + temp2 - STARTBLOCKVAL(PREV.br_startblock) - 1210153323Srodrigc (cur ? cur->bc_private.b.allocated : 0)); 1211153323Srodrigc if (diff > 0 && 1212153323Srodrigc xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, -diff, rsvd)) { 1213153323Srodrigc /* 1214153323Srodrigc * Ick gross gag me with a spoon. 1215153323Srodrigc */ 1216153323Srodrigc ASSERT(0); /* want to see if this ever happens! */ 1217153323Srodrigc while (diff > 0) { 1218153323Srodrigc if (temp) { 1219153323Srodrigc temp--; 1220153323Srodrigc diff--; 1221153323Srodrigc if (!diff || 1222153323Srodrigc !xfs_mod_incore_sb(ip->i_mount, 1223153323Srodrigc XFS_SBS_FDBLOCKS, -diff, rsvd)) 1224153323Srodrigc break; 1225153323Srodrigc } 1226153323Srodrigc if (temp2) { 1227153323Srodrigc temp2--; 1228153323Srodrigc diff--; 1229153323Srodrigc if (!diff || 1230153323Srodrigc !xfs_mod_incore_sb(ip->i_mount, 1231153323Srodrigc XFS_SBS_FDBLOCKS, -diff, rsvd)) 1232153323Srodrigc break; 1233153323Srodrigc } 1234153323Srodrigc } 1235153323Srodrigc } 1236159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 1237153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 1238153323Srodrigc xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK); 1239153323Srodrigc xfs_bmap_trace_pre_update(fname, "0", ip, idx + 2, 1240153323Srodrigc XFS_DATA_FORK); 1241159451Srodrigc xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx + 2), 1242159451Srodrigc NULLSTARTBLOCK((int)temp2)); 1243153323Srodrigc xfs_bmap_trace_post_update(fname, "0", ip, idx + 2, 1244153323Srodrigc XFS_DATA_FORK); 1245153323Srodrigc *dnew = temp + temp2; 1246159451Srodrigc /* DELTA: One in-core extent is split in three. */ 1247159451Srodrigc temp = PREV.br_startoff; 1248159451Srodrigc temp2 = PREV.br_blockcount; 1249153323Srodrigc break; 1250153323Srodrigc 1251153323Srodrigc case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): 1252153323Srodrigc case MASK3(RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): 1253153323Srodrigc case MASK2(LEFT_FILLING, RIGHT_CONTIG): 1254153323Srodrigc case MASK2(RIGHT_FILLING, LEFT_CONTIG): 1255153323Srodrigc case MASK2(LEFT_CONTIG, RIGHT_CONTIG): 1256153323Srodrigc case MASK(LEFT_CONTIG): 1257153323Srodrigc case MASK(RIGHT_CONTIG): 1258153323Srodrigc /* 1259153323Srodrigc * These cases are all impossible. 1260153323Srodrigc */ 1261153323Srodrigc ASSERT(0); 1262153323Srodrigc } 1263153323Srodrigc *curp = cur; 1264159451Srodrigc if (delta) { 1265159451Srodrigc temp2 += temp; 1266159451Srodrigc if (delta->xed_startoff > temp) 1267159451Srodrigc delta->xed_startoff = temp; 1268159451Srodrigc if (delta->xed_blockcount < temp2) 1269159451Srodrigc delta->xed_blockcount = temp2; 1270159451Srodrigc } 1271153323Srodrigcdone: 1272153323Srodrigc *logflagsp = rval; 1273153323Srodrigc return error; 1274153323Srodrigc#undef LEFT 1275153323Srodrigc#undef RIGHT 1276153323Srodrigc#undef PREV 1277153323Srodrigc#undef MASK 1278153323Srodrigc#undef MASK2 1279153323Srodrigc#undef MASK3 1280153323Srodrigc#undef MASK4 1281153323Srodrigc#undef STATE_SET 1282153323Srodrigc#undef STATE_TEST 1283153323Srodrigc#undef STATE_SET_TEST 1284153323Srodrigc#undef SWITCH_STATE 1285153323Srodrigc} 1286153323Srodrigc 1287153323Srodrigc/* 1288153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting an unwritten 1289153323Srodrigc * allocation to a real allocation or vice versa. 1290153323Srodrigc */ 1291153323SrodrigcSTATIC int /* error */ 1292153323Srodrigcxfs_bmap_add_extent_unwritten_real( 1293153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 1294153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 1295153323Srodrigc xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ 1296159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 1297159451Srodrigc int *logflagsp, /* inode logging flags */ 1298159451Srodrigc xfs_extdelta_t *delta) /* Change made to incore extents */ 1299153323Srodrigc{ 1300153323Srodrigc xfs_btree_cur_t *cur; /* btree cursor */ 1301153323Srodrigc xfs_bmbt_rec_t *ep; /* extent entry for idx */ 1302153323Srodrigc int error; /* error return value */ 1303153323Srodrigc#ifdef XFS_BMAP_TRACE 1304153323Srodrigc static char fname[] = "xfs_bmap_add_extent_unwritten_real"; 1305153323Srodrigc#endif 1306153323Srodrigc int i; /* temp state */ 1307159451Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 1308153323Srodrigc xfs_fileoff_t new_endoff; /* end offset of new entry */ 1309153323Srodrigc xfs_exntst_t newext; /* new extent state */ 1310153323Srodrigc xfs_exntst_t oldext; /* old extent state */ 1311153323Srodrigc xfs_bmbt_irec_t r[3]; /* neighbor extent entries */ 1312153323Srodrigc /* left is 0, right is 1, prev is 2 */ 1313153323Srodrigc int rval=0; /* return value (logging flags) */ 1314153323Srodrigc int state = 0;/* state bits, accessed thru macros */ 1315159451Srodrigc xfs_filblks_t temp=0; 1316159451Srodrigc xfs_filblks_t temp2=0; 1317153323Srodrigc enum { /* bit number definitions for state */ 1318153323Srodrigc LEFT_CONTIG, RIGHT_CONTIG, 1319153323Srodrigc LEFT_FILLING, RIGHT_FILLING, 1320153323Srodrigc LEFT_DELAY, RIGHT_DELAY, 1321153323Srodrigc LEFT_VALID, RIGHT_VALID 1322153323Srodrigc }; 1323153323Srodrigc 1324153323Srodrigc#define LEFT r[0] 1325153323Srodrigc#define RIGHT r[1] 1326153323Srodrigc#define PREV r[2] 1327153323Srodrigc#define MASK(b) (1 << (b)) 1328153323Srodrigc#define MASK2(a,b) (MASK(a) | MASK(b)) 1329153323Srodrigc#define MASK3(a,b,c) (MASK2(a,b) | MASK(c)) 1330153323Srodrigc#define MASK4(a,b,c,d) (MASK3(a,b,c) | MASK(d)) 1331153323Srodrigc#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b))) 1332153323Srodrigc#define STATE_TEST(b) (state & MASK(b)) 1333153323Srodrigc#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \ 1334153323Srodrigc ((state &= ~MASK(b)), 0)) 1335153323Srodrigc#define SWITCH_STATE \ 1336153323Srodrigc (state & MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG)) 1337153323Srodrigc 1338153323Srodrigc /* 1339153323Srodrigc * Set up a bunch of variables to make the tests simpler. 1340153323Srodrigc */ 1341153323Srodrigc error = 0; 1342153323Srodrigc cur = *curp; 1343159451Srodrigc ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); 1344159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 1345153323Srodrigc xfs_bmbt_get_all(ep, &PREV); 1346153323Srodrigc newext = new->br_state; 1347153323Srodrigc oldext = (newext == XFS_EXT_UNWRITTEN) ? 1348153323Srodrigc XFS_EXT_NORM : XFS_EXT_UNWRITTEN; 1349153323Srodrigc ASSERT(PREV.br_state == oldext); 1350153323Srodrigc new_endoff = new->br_startoff + new->br_blockcount; 1351153323Srodrigc ASSERT(PREV.br_startoff <= new->br_startoff); 1352153323Srodrigc ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff); 1353153323Srodrigc /* 1354153323Srodrigc * Set flags determining what part of the previous oldext allocation 1355153323Srodrigc * extent is being replaced by a newext allocation. 1356153323Srodrigc */ 1357153323Srodrigc STATE_SET(LEFT_FILLING, PREV.br_startoff == new->br_startoff); 1358153323Srodrigc STATE_SET(RIGHT_FILLING, 1359153323Srodrigc PREV.br_startoff + PREV.br_blockcount == new_endoff); 1360153323Srodrigc /* 1361153323Srodrigc * Check and set flags if this segment has a left neighbor. 1362153323Srodrigc * Don't set contiguous if the combined extent would be too large. 1363153323Srodrigc */ 1364153323Srodrigc if (STATE_SET_TEST(LEFT_VALID, idx > 0)) { 1365159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &LEFT); 1366153323Srodrigc STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(LEFT.br_startblock)); 1367153323Srodrigc } 1368153323Srodrigc STATE_SET(LEFT_CONTIG, 1369153323Srodrigc STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) && 1370153323Srodrigc LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff && 1371153323Srodrigc LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock && 1372153323Srodrigc LEFT.br_state == newext && 1373153323Srodrigc LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN); 1374153323Srodrigc /* 1375153323Srodrigc * Check and set flags if this segment has a right neighbor. 1376153323Srodrigc * Don't set contiguous if the combined extent would be too large. 1377153323Srodrigc * Also check for all-three-contiguous being too large. 1378153323Srodrigc */ 1379153323Srodrigc if (STATE_SET_TEST(RIGHT_VALID, 1380153323Srodrigc idx < 1381153323Srodrigc ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1)) { 1382159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx + 1), &RIGHT); 1383153323Srodrigc STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(RIGHT.br_startblock)); 1384153323Srodrigc } 1385153323Srodrigc STATE_SET(RIGHT_CONTIG, 1386153323Srodrigc STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) && 1387153323Srodrigc new_endoff == RIGHT.br_startoff && 1388153323Srodrigc new->br_startblock + new->br_blockcount == 1389153323Srodrigc RIGHT.br_startblock && 1390153323Srodrigc newext == RIGHT.br_state && 1391153323Srodrigc new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN && 1392153323Srodrigc ((state & MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING)) != 1393153323Srodrigc MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING) || 1394153323Srodrigc LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount 1395153323Srodrigc <= MAXEXTLEN)); 1396153323Srodrigc /* 1397153323Srodrigc * Switch out based on the FILLING and CONTIG state bits. 1398153323Srodrigc */ 1399153323Srodrigc switch (SWITCH_STATE) { 1400153323Srodrigc 1401153323Srodrigc case MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): 1402153323Srodrigc /* 1403153323Srodrigc * Setting all of a previous oldext extent to newext. 1404153323Srodrigc * The left and right neighbors are both contiguous with new. 1405153323Srodrigc */ 1406153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1, 1407153323Srodrigc XFS_DATA_FORK); 1408159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 1409153323Srodrigc LEFT.br_blockcount + PREV.br_blockcount + 1410153323Srodrigc RIGHT.br_blockcount); 1411153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1, 1412153323Srodrigc XFS_DATA_FORK); 1413153323Srodrigc xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2, 1414153323Srodrigc XFS_DATA_FORK); 1415159451Srodrigc xfs_iext_remove(ifp, idx, 2); 1416153323Srodrigc ip->i_df.if_lastex = idx - 1; 1417153323Srodrigc ip->i_d.di_nextents -= 2; 1418153323Srodrigc if (cur == NULL) 1419153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1420153323Srodrigc else { 1421153323Srodrigc rval = XFS_ILOG_CORE; 1422153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff, 1423153323Srodrigc RIGHT.br_startblock, 1424153323Srodrigc RIGHT.br_blockcount, &i))) 1425153323Srodrigc goto done; 1426153323Srodrigc ASSERT(i == 1); 1427153323Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 1428153323Srodrigc goto done; 1429153323Srodrigc ASSERT(i == 1); 1430153323Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 1431153323Srodrigc goto done; 1432153323Srodrigc ASSERT(i == 1); 1433153323Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 1434153323Srodrigc goto done; 1435153323Srodrigc ASSERT(i == 1); 1436153323Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 1437153323Srodrigc goto done; 1438153323Srodrigc ASSERT(i == 1); 1439153323Srodrigc if ((error = xfs_bmbt_update(cur, LEFT.br_startoff, 1440153323Srodrigc LEFT.br_startblock, 1441153323Srodrigc LEFT.br_blockcount + PREV.br_blockcount + 1442153323Srodrigc RIGHT.br_blockcount, LEFT.br_state))) 1443153323Srodrigc goto done; 1444153323Srodrigc } 1445159451Srodrigc /* DELTA: Three in-core extents are replaced by one. */ 1446159451Srodrigc temp = LEFT.br_startoff; 1447159451Srodrigc temp2 = LEFT.br_blockcount + 1448159451Srodrigc PREV.br_blockcount + 1449159451Srodrigc RIGHT.br_blockcount; 1450153323Srodrigc break; 1451153323Srodrigc 1452153323Srodrigc case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG): 1453153323Srodrigc /* 1454153323Srodrigc * Setting all of a previous oldext extent to newext. 1455153323Srodrigc * The left neighbor is contiguous, the right is not. 1456153323Srodrigc */ 1457153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1, 1458153323Srodrigc XFS_DATA_FORK); 1459159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 1460153323Srodrigc LEFT.br_blockcount + PREV.br_blockcount); 1461153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1, 1462153323Srodrigc XFS_DATA_FORK); 1463153323Srodrigc ip->i_df.if_lastex = idx - 1; 1464153323Srodrigc xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1, 1465153323Srodrigc XFS_DATA_FORK); 1466159451Srodrigc xfs_iext_remove(ifp, idx, 1); 1467153323Srodrigc ip->i_d.di_nextents--; 1468153323Srodrigc if (cur == NULL) 1469153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1470153323Srodrigc else { 1471153323Srodrigc rval = XFS_ILOG_CORE; 1472153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff, 1473153323Srodrigc PREV.br_startblock, PREV.br_blockcount, 1474153323Srodrigc &i))) 1475153323Srodrigc goto done; 1476153323Srodrigc ASSERT(i == 1); 1477153323Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 1478153323Srodrigc goto done; 1479153323Srodrigc ASSERT(i == 1); 1480153323Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 1481153323Srodrigc goto done; 1482153323Srodrigc ASSERT(i == 1); 1483153323Srodrigc if ((error = xfs_bmbt_update(cur, LEFT.br_startoff, 1484153323Srodrigc LEFT.br_startblock, 1485153323Srodrigc LEFT.br_blockcount + PREV.br_blockcount, 1486153323Srodrigc LEFT.br_state))) 1487153323Srodrigc goto done; 1488153323Srodrigc } 1489159451Srodrigc /* DELTA: Two in-core extents are replaced by one. */ 1490159451Srodrigc temp = LEFT.br_startoff; 1491159451Srodrigc temp2 = LEFT.br_blockcount + 1492159451Srodrigc PREV.br_blockcount; 1493153323Srodrigc break; 1494153323Srodrigc 1495153323Srodrigc case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG): 1496153323Srodrigc /* 1497153323Srodrigc * Setting all of a previous oldext extent to newext. 1498153323Srodrigc * The right neighbor is contiguous, the left is not. 1499153323Srodrigc */ 1500153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx, 1501153323Srodrigc XFS_DATA_FORK); 1502153323Srodrigc xfs_bmbt_set_blockcount(ep, 1503153323Srodrigc PREV.br_blockcount + RIGHT.br_blockcount); 1504153323Srodrigc xfs_bmbt_set_state(ep, newext); 1505153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx, 1506153323Srodrigc XFS_DATA_FORK); 1507153323Srodrigc ip->i_df.if_lastex = idx; 1508153323Srodrigc xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1, 1509153323Srodrigc XFS_DATA_FORK); 1510159451Srodrigc xfs_iext_remove(ifp, idx + 1, 1); 1511153323Srodrigc ip->i_d.di_nextents--; 1512153323Srodrigc if (cur == NULL) 1513153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1514153323Srodrigc else { 1515153323Srodrigc rval = XFS_ILOG_CORE; 1516153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff, 1517153323Srodrigc RIGHT.br_startblock, 1518153323Srodrigc RIGHT.br_blockcount, &i))) 1519153323Srodrigc goto done; 1520153323Srodrigc ASSERT(i == 1); 1521153323Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 1522153323Srodrigc goto done; 1523153323Srodrigc ASSERT(i == 1); 1524153323Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 1525153323Srodrigc goto done; 1526153323Srodrigc ASSERT(i == 1); 1527153323Srodrigc if ((error = xfs_bmbt_update(cur, new->br_startoff, 1528153323Srodrigc new->br_startblock, 1529153323Srodrigc new->br_blockcount + RIGHT.br_blockcount, 1530153323Srodrigc newext))) 1531153323Srodrigc goto done; 1532153323Srodrigc } 1533159451Srodrigc /* DELTA: Two in-core extents are replaced by one. */ 1534159451Srodrigc temp = PREV.br_startoff; 1535159451Srodrigc temp2 = PREV.br_blockcount + 1536159451Srodrigc RIGHT.br_blockcount; 1537153323Srodrigc break; 1538153323Srodrigc 1539153323Srodrigc case MASK2(LEFT_FILLING, RIGHT_FILLING): 1540153323Srodrigc /* 1541153323Srodrigc * Setting all of a previous oldext extent to newext. 1542153323Srodrigc * Neither the left nor right neighbors are contiguous with 1543153323Srodrigc * the new one. 1544153323Srodrigc */ 1545153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx, 1546153323Srodrigc XFS_DATA_FORK); 1547153323Srodrigc xfs_bmbt_set_state(ep, newext); 1548153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx, 1549153323Srodrigc XFS_DATA_FORK); 1550153323Srodrigc ip->i_df.if_lastex = idx; 1551153323Srodrigc if (cur == NULL) 1552153323Srodrigc rval = XFS_ILOG_DEXT; 1553153323Srodrigc else { 1554153323Srodrigc rval = 0; 1555153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, 1556153323Srodrigc new->br_startblock, new->br_blockcount, 1557153323Srodrigc &i))) 1558153323Srodrigc goto done; 1559153323Srodrigc ASSERT(i == 1); 1560153323Srodrigc if ((error = xfs_bmbt_update(cur, new->br_startoff, 1561153323Srodrigc new->br_startblock, new->br_blockcount, 1562153323Srodrigc newext))) 1563153323Srodrigc goto done; 1564153323Srodrigc } 1565159451Srodrigc /* DELTA: The in-core extent described by new changed type. */ 1566159451Srodrigc temp = new->br_startoff; 1567159451Srodrigc temp2 = new->br_blockcount; 1568153323Srodrigc break; 1569153323Srodrigc 1570153323Srodrigc case MASK2(LEFT_FILLING, LEFT_CONTIG): 1571153323Srodrigc /* 1572153323Srodrigc * Setting the first part of a previous oldext extent to newext. 1573153323Srodrigc * The left neighbor is contiguous. 1574153323Srodrigc */ 1575153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1, 1576153323Srodrigc XFS_DATA_FORK); 1577159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 1578153323Srodrigc LEFT.br_blockcount + new->br_blockcount); 1579153323Srodrigc xfs_bmbt_set_startoff(ep, 1580153323Srodrigc PREV.br_startoff + new->br_blockcount); 1581153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1, 1582153323Srodrigc XFS_DATA_FORK); 1583153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx, 1584153323Srodrigc XFS_DATA_FORK); 1585153323Srodrigc xfs_bmbt_set_startblock(ep, 1586153323Srodrigc new->br_startblock + new->br_blockcount); 1587153323Srodrigc xfs_bmbt_set_blockcount(ep, 1588153323Srodrigc PREV.br_blockcount - new->br_blockcount); 1589153323Srodrigc xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx, 1590153323Srodrigc XFS_DATA_FORK); 1591153323Srodrigc ip->i_df.if_lastex = idx - 1; 1592153323Srodrigc if (cur == NULL) 1593153323Srodrigc rval = XFS_ILOG_DEXT; 1594153323Srodrigc else { 1595153323Srodrigc rval = 0; 1596153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff, 1597153323Srodrigc PREV.br_startblock, PREV.br_blockcount, 1598153323Srodrigc &i))) 1599153323Srodrigc goto done; 1600153323Srodrigc ASSERT(i == 1); 1601153323Srodrigc if ((error = xfs_bmbt_update(cur, 1602153323Srodrigc PREV.br_startoff + new->br_blockcount, 1603153323Srodrigc PREV.br_startblock + new->br_blockcount, 1604153323Srodrigc PREV.br_blockcount - new->br_blockcount, 1605153323Srodrigc oldext))) 1606153323Srodrigc goto done; 1607153323Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 1608153323Srodrigc goto done; 1609153323Srodrigc if (xfs_bmbt_update(cur, LEFT.br_startoff, 1610153323Srodrigc LEFT.br_startblock, 1611153323Srodrigc LEFT.br_blockcount + new->br_blockcount, 1612153323Srodrigc LEFT.br_state)) 1613153323Srodrigc goto done; 1614153323Srodrigc } 1615159451Srodrigc /* DELTA: The boundary between two in-core extents moved. */ 1616159451Srodrigc temp = LEFT.br_startoff; 1617159451Srodrigc temp2 = LEFT.br_blockcount + 1618159451Srodrigc PREV.br_blockcount; 1619153323Srodrigc break; 1620153323Srodrigc 1621153323Srodrigc case MASK(LEFT_FILLING): 1622153323Srodrigc /* 1623153323Srodrigc * Setting the first part of a previous oldext extent to newext. 1624153323Srodrigc * The left neighbor is not contiguous. 1625153323Srodrigc */ 1626153323Srodrigc xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK); 1627153323Srodrigc ASSERT(ep && xfs_bmbt_get_state(ep) == oldext); 1628153323Srodrigc xfs_bmbt_set_startoff(ep, new_endoff); 1629153323Srodrigc xfs_bmbt_set_blockcount(ep, 1630153323Srodrigc PREV.br_blockcount - new->br_blockcount); 1631153323Srodrigc xfs_bmbt_set_startblock(ep, 1632153323Srodrigc new->br_startblock + new->br_blockcount); 1633153323Srodrigc xfs_bmap_trace_post_update(fname, "LF", ip, idx, XFS_DATA_FORK); 1634153323Srodrigc xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL, 1635153323Srodrigc XFS_DATA_FORK); 1636159451Srodrigc xfs_iext_insert(ifp, idx, 1, new); 1637153323Srodrigc ip->i_df.if_lastex = idx; 1638153323Srodrigc ip->i_d.di_nextents++; 1639153323Srodrigc if (cur == NULL) 1640153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1641153323Srodrigc else { 1642153323Srodrigc rval = XFS_ILOG_CORE; 1643153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff, 1644153323Srodrigc PREV.br_startblock, PREV.br_blockcount, 1645153323Srodrigc &i))) 1646153323Srodrigc goto done; 1647153323Srodrigc ASSERT(i == 1); 1648153323Srodrigc if ((error = xfs_bmbt_update(cur, 1649153323Srodrigc PREV.br_startoff + new->br_blockcount, 1650153323Srodrigc PREV.br_startblock + new->br_blockcount, 1651153323Srodrigc PREV.br_blockcount - new->br_blockcount, 1652153323Srodrigc oldext))) 1653153323Srodrigc goto done; 1654153323Srodrigc cur->bc_rec.b = *new; 1655153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1656153323Srodrigc goto done; 1657153323Srodrigc ASSERT(i == 1); 1658153323Srodrigc } 1659159451Srodrigc /* DELTA: One in-core extent is split in two. */ 1660159451Srodrigc temp = PREV.br_startoff; 1661159451Srodrigc temp2 = PREV.br_blockcount; 1662153323Srodrigc break; 1663153323Srodrigc 1664153323Srodrigc case MASK2(RIGHT_FILLING, RIGHT_CONTIG): 1665153323Srodrigc /* 1666153323Srodrigc * Setting the last part of a previous oldext extent to newext. 1667153323Srodrigc * The right neighbor is contiguous with the new allocation. 1668153323Srodrigc */ 1669153323Srodrigc xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx, 1670153323Srodrigc XFS_DATA_FORK); 1671153323Srodrigc xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1, 1672153323Srodrigc XFS_DATA_FORK); 1673153323Srodrigc xfs_bmbt_set_blockcount(ep, 1674153323Srodrigc PREV.br_blockcount - new->br_blockcount); 1675153323Srodrigc xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx, 1676153323Srodrigc XFS_DATA_FORK); 1677159451Srodrigc xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, idx + 1), 1678159451Srodrigc new->br_startoff, new->br_startblock, 1679153323Srodrigc new->br_blockcount + RIGHT.br_blockcount, newext); 1680153323Srodrigc xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1, 1681153323Srodrigc XFS_DATA_FORK); 1682153323Srodrigc ip->i_df.if_lastex = idx + 1; 1683153323Srodrigc if (cur == NULL) 1684153323Srodrigc rval = XFS_ILOG_DEXT; 1685153323Srodrigc else { 1686153323Srodrigc rval = 0; 1687153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff, 1688153323Srodrigc PREV.br_startblock, 1689153323Srodrigc PREV.br_blockcount, &i))) 1690153323Srodrigc goto done; 1691153323Srodrigc ASSERT(i == 1); 1692153323Srodrigc if ((error = xfs_bmbt_update(cur, PREV.br_startoff, 1693153323Srodrigc PREV.br_startblock, 1694153323Srodrigc PREV.br_blockcount - new->br_blockcount, 1695153323Srodrigc oldext))) 1696153323Srodrigc goto done; 1697153323Srodrigc if ((error = xfs_bmbt_increment(cur, 0, &i))) 1698153323Srodrigc goto done; 1699153323Srodrigc if ((error = xfs_bmbt_update(cur, new->br_startoff, 1700153323Srodrigc new->br_startblock, 1701153323Srodrigc new->br_blockcount + RIGHT.br_blockcount, 1702153323Srodrigc newext))) 1703153323Srodrigc goto done; 1704153323Srodrigc } 1705159451Srodrigc /* DELTA: The boundary between two in-core extents moved. */ 1706159451Srodrigc temp = PREV.br_startoff; 1707159451Srodrigc temp2 = PREV.br_blockcount + 1708159451Srodrigc RIGHT.br_blockcount; 1709153323Srodrigc break; 1710153323Srodrigc 1711153323Srodrigc case MASK(RIGHT_FILLING): 1712153323Srodrigc /* 1713153323Srodrigc * Setting the last part of a previous oldext extent to newext. 1714153323Srodrigc * The right neighbor is not contiguous. 1715153323Srodrigc */ 1716153323Srodrigc xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK); 1717153323Srodrigc xfs_bmbt_set_blockcount(ep, 1718153323Srodrigc PREV.br_blockcount - new->br_blockcount); 1719153323Srodrigc xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK); 1720153323Srodrigc xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1, 1721153323Srodrigc new, NULL, XFS_DATA_FORK); 1722159451Srodrigc xfs_iext_insert(ifp, idx + 1, 1, new); 1723153323Srodrigc ip->i_df.if_lastex = idx + 1; 1724153323Srodrigc ip->i_d.di_nextents++; 1725153323Srodrigc if (cur == NULL) 1726153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1727153323Srodrigc else { 1728153323Srodrigc rval = XFS_ILOG_CORE; 1729153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff, 1730153323Srodrigc PREV.br_startblock, PREV.br_blockcount, 1731153323Srodrigc &i))) 1732153323Srodrigc goto done; 1733153323Srodrigc ASSERT(i == 1); 1734153323Srodrigc if ((error = xfs_bmbt_update(cur, PREV.br_startoff, 1735153323Srodrigc PREV.br_startblock, 1736153323Srodrigc PREV.br_blockcount - new->br_blockcount, 1737153323Srodrigc oldext))) 1738153323Srodrigc goto done; 1739153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, 1740153323Srodrigc new->br_startblock, new->br_blockcount, 1741153323Srodrigc &i))) 1742153323Srodrigc goto done; 1743153323Srodrigc ASSERT(i == 0); 1744153323Srodrigc cur->bc_rec.b.br_state = XFS_EXT_NORM; 1745153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1746153323Srodrigc goto done; 1747153323Srodrigc ASSERT(i == 1); 1748153323Srodrigc } 1749159451Srodrigc /* DELTA: One in-core extent is split in two. */ 1750159451Srodrigc temp = PREV.br_startoff; 1751159451Srodrigc temp2 = PREV.br_blockcount; 1752153323Srodrigc break; 1753153323Srodrigc 1754153323Srodrigc case 0: 1755153323Srodrigc /* 1756153323Srodrigc * Setting the middle part of a previous oldext extent to 1757153323Srodrigc * newext. Contiguity is impossible here. 1758153323Srodrigc * One extent becomes three extents. 1759153323Srodrigc */ 1760153323Srodrigc xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK); 1761153323Srodrigc xfs_bmbt_set_blockcount(ep, 1762153323Srodrigc new->br_startoff - PREV.br_startoff); 1763153323Srodrigc xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK); 1764153323Srodrigc r[0] = *new; 1765153323Srodrigc r[1].br_startoff = new_endoff; 1766153323Srodrigc r[1].br_blockcount = 1767153323Srodrigc PREV.br_startoff + PREV.br_blockcount - new_endoff; 1768153323Srodrigc r[1].br_startblock = new->br_startblock + new->br_blockcount; 1769153323Srodrigc r[1].br_state = oldext; 1770153323Srodrigc xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1], 1771153323Srodrigc XFS_DATA_FORK); 1772159451Srodrigc xfs_iext_insert(ifp, idx + 1, 2, &r[0]); 1773153323Srodrigc ip->i_df.if_lastex = idx + 1; 1774153323Srodrigc ip->i_d.di_nextents += 2; 1775153323Srodrigc if (cur == NULL) 1776153323Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_DEXT; 1777153323Srodrigc else { 1778153323Srodrigc rval = XFS_ILOG_CORE; 1779153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff, 1780153323Srodrigc PREV.br_startblock, PREV.br_blockcount, 1781153323Srodrigc &i))) 1782153323Srodrigc goto done; 1783153323Srodrigc ASSERT(i == 1); 1784153323Srodrigc /* new right extent - oldext */ 1785153323Srodrigc if ((error = xfs_bmbt_update(cur, r[1].br_startoff, 1786153323Srodrigc r[1].br_startblock, r[1].br_blockcount, 1787153323Srodrigc r[1].br_state))) 1788153323Srodrigc goto done; 1789153323Srodrigc /* new left extent - oldext */ 1790153323Srodrigc PREV.br_blockcount = 1791153323Srodrigc new->br_startoff - PREV.br_startoff; 1792153323Srodrigc cur->bc_rec.b = PREV; 1793153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1794153323Srodrigc goto done; 1795153323Srodrigc ASSERT(i == 1); 1796153323Srodrigc if ((error = xfs_bmbt_increment(cur, 0, &i))) 1797153323Srodrigc goto done; 1798153323Srodrigc ASSERT(i == 1); 1799153323Srodrigc /* new middle extent - newext */ 1800153323Srodrigc cur->bc_rec.b = *new; 1801153323Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 1802153323Srodrigc goto done; 1803153323Srodrigc ASSERT(i == 1); 1804153323Srodrigc } 1805159451Srodrigc /* DELTA: One in-core extent is split in three. */ 1806159451Srodrigc temp = PREV.br_startoff; 1807159451Srodrigc temp2 = PREV.br_blockcount; 1808153323Srodrigc break; 1809153323Srodrigc 1810153323Srodrigc case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): 1811153323Srodrigc case MASK3(RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): 1812153323Srodrigc case MASK2(LEFT_FILLING, RIGHT_CONTIG): 1813153323Srodrigc case MASK2(RIGHT_FILLING, LEFT_CONTIG): 1814153323Srodrigc case MASK2(LEFT_CONTIG, RIGHT_CONTIG): 1815153323Srodrigc case MASK(LEFT_CONTIG): 1816153323Srodrigc case MASK(RIGHT_CONTIG): 1817153323Srodrigc /* 1818153323Srodrigc * These cases are all impossible. 1819153323Srodrigc */ 1820153323Srodrigc ASSERT(0); 1821153323Srodrigc } 1822153323Srodrigc *curp = cur; 1823159451Srodrigc if (delta) { 1824159451Srodrigc temp2 += temp; 1825159451Srodrigc if (delta->xed_startoff > temp) 1826159451Srodrigc delta->xed_startoff = temp; 1827159451Srodrigc if (delta->xed_blockcount < temp2) 1828159451Srodrigc delta->xed_blockcount = temp2; 1829159451Srodrigc } 1830153323Srodrigcdone: 1831153323Srodrigc *logflagsp = rval; 1832153323Srodrigc return error; 1833153323Srodrigc#undef LEFT 1834153323Srodrigc#undef RIGHT 1835153323Srodrigc#undef PREV 1836153323Srodrigc#undef MASK 1837153323Srodrigc#undef MASK2 1838153323Srodrigc#undef MASK3 1839153323Srodrigc#undef MASK4 1840153323Srodrigc#undef STATE_SET 1841153323Srodrigc#undef STATE_TEST 1842153323Srodrigc#undef STATE_SET_TEST 1843153323Srodrigc#undef SWITCH_STATE 1844153323Srodrigc} 1845153323Srodrigc 1846153323Srodrigc/* 1847153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting a hole 1848153323Srodrigc * to a delayed allocation. 1849153323Srodrigc */ 1850153323Srodrigc/*ARGSUSED*/ 1851153323SrodrigcSTATIC int /* error */ 1852153323Srodrigcxfs_bmap_add_extent_hole_delay( 1853153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 1854153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 1855153323Srodrigc xfs_btree_cur_t *cur, /* if null, not a btree */ 1856159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 1857153323Srodrigc int *logflagsp, /* inode logging flags */ 1858159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 1859153323Srodrigc int rsvd) /* OK to allocate reserved blocks */ 1860153323Srodrigc{ 1861159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record for idx */ 1862153323Srodrigc#ifdef XFS_BMAP_TRACE 1863153323Srodrigc static char fname[] = "xfs_bmap_add_extent_hole_delay"; 1864153323Srodrigc#endif 1865159451Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 1866153323Srodrigc xfs_bmbt_irec_t left; /* left neighbor extent entry */ 1867153323Srodrigc xfs_filblks_t newlen=0; /* new indirect size */ 1868153323Srodrigc xfs_filblks_t oldlen=0; /* old indirect size */ 1869153323Srodrigc xfs_bmbt_irec_t right; /* right neighbor extent entry */ 1870153323Srodrigc int state; /* state bits, accessed thru macros */ 1871159451Srodrigc xfs_filblks_t temp=0; /* temp for indirect calculations */ 1872159451Srodrigc xfs_filblks_t temp2=0; 1873153323Srodrigc enum { /* bit number definitions for state */ 1874153323Srodrigc LEFT_CONTIG, RIGHT_CONTIG, 1875153323Srodrigc LEFT_DELAY, RIGHT_DELAY, 1876153323Srodrigc LEFT_VALID, RIGHT_VALID 1877153323Srodrigc }; 1878153323Srodrigc 1879153323Srodrigc#define MASK(b) (1 << (b)) 1880153323Srodrigc#define MASK2(a,b) (MASK(a) | MASK(b)) 1881153323Srodrigc#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b))) 1882153323Srodrigc#define STATE_TEST(b) (state & MASK(b)) 1883153323Srodrigc#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \ 1884153323Srodrigc ((state &= ~MASK(b)), 0)) 1885153323Srodrigc#define SWITCH_STATE (state & MASK2(LEFT_CONTIG, RIGHT_CONTIG)) 1886153323Srodrigc 1887159451Srodrigc ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); 1888159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 1889153323Srodrigc state = 0; 1890153323Srodrigc ASSERT(ISNULLSTARTBLOCK(new->br_startblock)); 1891153323Srodrigc /* 1892153323Srodrigc * Check and set flags if this segment has a left neighbor 1893153323Srodrigc */ 1894153323Srodrigc if (STATE_SET_TEST(LEFT_VALID, idx > 0)) { 1895159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &left); 1896153323Srodrigc STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(left.br_startblock)); 1897153323Srodrigc } 1898153323Srodrigc /* 1899153323Srodrigc * Check and set flags if the current (right) segment exists. 1900153323Srodrigc * If it doesn't exist, we're converting the hole at end-of-file. 1901153323Srodrigc */ 1902153323Srodrigc if (STATE_SET_TEST(RIGHT_VALID, 1903153323Srodrigc idx < 1904153323Srodrigc ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) { 1905153323Srodrigc xfs_bmbt_get_all(ep, &right); 1906153323Srodrigc STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(right.br_startblock)); 1907153323Srodrigc } 1908153323Srodrigc /* 1909153323Srodrigc * Set contiguity flags on the left and right neighbors. 1910153323Srodrigc * Don't let extents get too large, even if the pieces are contiguous. 1911153323Srodrigc */ 1912153323Srodrigc STATE_SET(LEFT_CONTIG, 1913153323Srodrigc STATE_TEST(LEFT_VALID) && STATE_TEST(LEFT_DELAY) && 1914153323Srodrigc left.br_startoff + left.br_blockcount == new->br_startoff && 1915153323Srodrigc left.br_blockcount + new->br_blockcount <= MAXEXTLEN); 1916153323Srodrigc STATE_SET(RIGHT_CONTIG, 1917153323Srodrigc STATE_TEST(RIGHT_VALID) && STATE_TEST(RIGHT_DELAY) && 1918153323Srodrigc new->br_startoff + new->br_blockcount == right.br_startoff && 1919153323Srodrigc new->br_blockcount + right.br_blockcount <= MAXEXTLEN && 1920153323Srodrigc (!STATE_TEST(LEFT_CONTIG) || 1921153323Srodrigc (left.br_blockcount + new->br_blockcount + 1922153323Srodrigc right.br_blockcount <= MAXEXTLEN))); 1923153323Srodrigc /* 1924153323Srodrigc * Switch out based on the contiguity flags. 1925153323Srodrigc */ 1926153323Srodrigc switch (SWITCH_STATE) { 1927153323Srodrigc 1928153323Srodrigc case MASK2(LEFT_CONTIG, RIGHT_CONTIG): 1929153323Srodrigc /* 1930153323Srodrigc * New allocation is contiguous with delayed allocations 1931153323Srodrigc * on the left and on the right. 1932159451Srodrigc * Merge all three into a single extent record. 1933153323Srodrigc */ 1934153323Srodrigc temp = left.br_blockcount + new->br_blockcount + 1935153323Srodrigc right.br_blockcount; 1936153323Srodrigc xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1, 1937153323Srodrigc XFS_DATA_FORK); 1938159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp); 1939153323Srodrigc oldlen = STARTBLOCKVAL(left.br_startblock) + 1940153323Srodrigc STARTBLOCKVAL(new->br_startblock) + 1941153323Srodrigc STARTBLOCKVAL(right.br_startblock); 1942153323Srodrigc newlen = xfs_bmap_worst_indlen(ip, temp); 1943159451Srodrigc xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1), 1944159451Srodrigc NULLSTARTBLOCK((int)newlen)); 1945153323Srodrigc xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1, 1946153323Srodrigc XFS_DATA_FORK); 1947153323Srodrigc xfs_bmap_trace_delete(fname, "LC|RC", ip, idx, 1, 1948153323Srodrigc XFS_DATA_FORK); 1949159451Srodrigc xfs_iext_remove(ifp, idx, 1); 1950153323Srodrigc ip->i_df.if_lastex = idx - 1; 1951159451Srodrigc /* DELTA: Two in-core extents were replaced by one. */ 1952159451Srodrigc temp2 = temp; 1953159451Srodrigc temp = left.br_startoff; 1954153323Srodrigc break; 1955153323Srodrigc 1956153323Srodrigc case MASK(LEFT_CONTIG): 1957153323Srodrigc /* 1958153323Srodrigc * New allocation is contiguous with a delayed allocation 1959153323Srodrigc * on the left. 1960153323Srodrigc * Merge the new allocation with the left neighbor. 1961153323Srodrigc */ 1962153323Srodrigc temp = left.br_blockcount + new->br_blockcount; 1963153323Srodrigc xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1, 1964153323Srodrigc XFS_DATA_FORK); 1965159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), temp); 1966153323Srodrigc oldlen = STARTBLOCKVAL(left.br_startblock) + 1967153323Srodrigc STARTBLOCKVAL(new->br_startblock); 1968153323Srodrigc newlen = xfs_bmap_worst_indlen(ip, temp); 1969159451Srodrigc xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, idx - 1), 1970159451Srodrigc NULLSTARTBLOCK((int)newlen)); 1971153323Srodrigc xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, 1972153323Srodrigc XFS_DATA_FORK); 1973153323Srodrigc ip->i_df.if_lastex = idx - 1; 1974159451Srodrigc /* DELTA: One in-core extent grew into a hole. */ 1975159451Srodrigc temp2 = temp; 1976159451Srodrigc temp = left.br_startoff; 1977153323Srodrigc break; 1978153323Srodrigc 1979153323Srodrigc case MASK(RIGHT_CONTIG): 1980153323Srodrigc /* 1981153323Srodrigc * New allocation is contiguous with a delayed allocation 1982153323Srodrigc * on the right. 1983153323Srodrigc * Merge the new allocation with the right neighbor. 1984153323Srodrigc */ 1985153323Srodrigc xfs_bmap_trace_pre_update(fname, "RC", ip, idx, XFS_DATA_FORK); 1986153323Srodrigc temp = new->br_blockcount + right.br_blockcount; 1987153323Srodrigc oldlen = STARTBLOCKVAL(new->br_startblock) + 1988153323Srodrigc STARTBLOCKVAL(right.br_startblock); 1989153323Srodrigc newlen = xfs_bmap_worst_indlen(ip, temp); 1990153323Srodrigc xfs_bmbt_set_allf(ep, new->br_startoff, 1991153323Srodrigc NULLSTARTBLOCK((int)newlen), temp, right.br_state); 1992153323Srodrigc xfs_bmap_trace_post_update(fname, "RC", ip, idx, XFS_DATA_FORK); 1993153323Srodrigc ip->i_df.if_lastex = idx; 1994159451Srodrigc /* DELTA: One in-core extent grew into a hole. */ 1995159451Srodrigc temp2 = temp; 1996159451Srodrigc temp = new->br_startoff; 1997153323Srodrigc break; 1998153323Srodrigc 1999153323Srodrigc case 0: 2000153323Srodrigc /* 2001153323Srodrigc * New allocation is not contiguous with another 2002153323Srodrigc * delayed allocation. 2003153323Srodrigc * Insert a new entry. 2004153323Srodrigc */ 2005153323Srodrigc oldlen = newlen = 0; 2006153323Srodrigc xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL, 2007153323Srodrigc XFS_DATA_FORK); 2008159451Srodrigc xfs_iext_insert(ifp, idx, 1, new); 2009153323Srodrigc ip->i_df.if_lastex = idx; 2010159451Srodrigc /* DELTA: A new in-core extent was added in a hole. */ 2011159451Srodrigc temp2 = new->br_blockcount; 2012159451Srodrigc temp = new->br_startoff; 2013153323Srodrigc break; 2014153323Srodrigc } 2015153323Srodrigc if (oldlen != newlen) { 2016153323Srodrigc ASSERT(oldlen > newlen); 2017153323Srodrigc xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, 2018153323Srodrigc (int)(oldlen - newlen), rsvd); 2019153323Srodrigc /* 2020153323Srodrigc * Nothing to do for disk quota accounting here. 2021153323Srodrigc */ 2022153323Srodrigc } 2023159451Srodrigc if (delta) { 2024159451Srodrigc temp2 += temp; 2025159451Srodrigc if (delta->xed_startoff > temp) 2026159451Srodrigc delta->xed_startoff = temp; 2027159451Srodrigc if (delta->xed_blockcount < temp2) 2028159451Srodrigc delta->xed_blockcount = temp2; 2029159451Srodrigc } 2030153323Srodrigc *logflagsp = 0; 2031153323Srodrigc return 0; 2032153323Srodrigc#undef MASK 2033153323Srodrigc#undef MASK2 2034153323Srodrigc#undef STATE_SET 2035153323Srodrigc#undef STATE_TEST 2036153323Srodrigc#undef STATE_SET_TEST 2037153323Srodrigc#undef SWITCH_STATE 2038153323Srodrigc} 2039153323Srodrigc 2040153323Srodrigc/* 2041153323Srodrigc * Called by xfs_bmap_add_extent to handle cases converting a hole 2042153323Srodrigc * to a real allocation. 2043153323Srodrigc */ 2044153323SrodrigcSTATIC int /* error */ 2045153323Srodrigcxfs_bmap_add_extent_hole_real( 2046153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 2047153323Srodrigc xfs_extnum_t idx, /* extent number to update/insert */ 2048153323Srodrigc xfs_btree_cur_t *cur, /* if null, not a btree */ 2049159451Srodrigc xfs_bmbt_irec_t *new, /* new data to add to file extents */ 2050153323Srodrigc int *logflagsp, /* inode logging flags */ 2051159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 2052153323Srodrigc int whichfork) /* data or attr fork */ 2053153323Srodrigc{ 2054153323Srodrigc xfs_bmbt_rec_t *ep; /* pointer to extent entry ins. point */ 2055153323Srodrigc int error; /* error return value */ 2056153323Srodrigc#ifdef XFS_BMAP_TRACE 2057153323Srodrigc static char fname[] = "xfs_bmap_add_extent_hole_real"; 2058153323Srodrigc#endif 2059153323Srodrigc int i; /* temp state */ 2060153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 2061153323Srodrigc xfs_bmbt_irec_t left; /* left neighbor extent entry */ 2062153323Srodrigc xfs_bmbt_irec_t right; /* right neighbor extent entry */ 2063159451Srodrigc int rval=0; /* return value (logging flags) */ 2064153323Srodrigc int state; /* state bits, accessed thru macros */ 2065159451Srodrigc xfs_filblks_t temp=0; 2066159451Srodrigc xfs_filblks_t temp2=0; 2067153323Srodrigc enum { /* bit number definitions for state */ 2068153323Srodrigc LEFT_CONTIG, RIGHT_CONTIG, 2069153323Srodrigc LEFT_DELAY, RIGHT_DELAY, 2070153323Srodrigc LEFT_VALID, RIGHT_VALID 2071153323Srodrigc }; 2072153323Srodrigc 2073153323Srodrigc#define MASK(b) (1 << (b)) 2074153323Srodrigc#define MASK2(a,b) (MASK(a) | MASK(b)) 2075153323Srodrigc#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b))) 2076153323Srodrigc#define STATE_TEST(b) (state & MASK(b)) 2077153323Srodrigc#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \ 2078153323Srodrigc ((state &= ~MASK(b)), 0)) 2079153323Srodrigc#define SWITCH_STATE (state & MASK2(LEFT_CONTIG, RIGHT_CONTIG)) 2080153323Srodrigc 2081153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 2082153323Srodrigc ASSERT(idx <= ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)); 2083159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 2084153323Srodrigc state = 0; 2085153323Srodrigc /* 2086153323Srodrigc * Check and set flags if this segment has a left neighbor. 2087153323Srodrigc */ 2088153323Srodrigc if (STATE_SET_TEST(LEFT_VALID, idx > 0)) { 2089159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx - 1), &left); 2090153323Srodrigc STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(left.br_startblock)); 2091153323Srodrigc } 2092153323Srodrigc /* 2093153323Srodrigc * Check and set flags if this segment has a current value. 2094153323Srodrigc * Not true if we're inserting into the "hole" at eof. 2095153323Srodrigc */ 2096153323Srodrigc if (STATE_SET_TEST(RIGHT_VALID, 2097153323Srodrigc idx < 2098153323Srodrigc ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) { 2099153323Srodrigc xfs_bmbt_get_all(ep, &right); 2100153323Srodrigc STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(right.br_startblock)); 2101153323Srodrigc } 2102153323Srodrigc /* 2103153323Srodrigc * We're inserting a real allocation between "left" and "right". 2104153323Srodrigc * Set the contiguity flags. Don't let extents get too large. 2105153323Srodrigc */ 2106153323Srodrigc STATE_SET(LEFT_CONTIG, 2107153323Srodrigc STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) && 2108153323Srodrigc left.br_startoff + left.br_blockcount == new->br_startoff && 2109153323Srodrigc left.br_startblock + left.br_blockcount == new->br_startblock && 2110153323Srodrigc left.br_state == new->br_state && 2111153323Srodrigc left.br_blockcount + new->br_blockcount <= MAXEXTLEN); 2112153323Srodrigc STATE_SET(RIGHT_CONTIG, 2113153323Srodrigc STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) && 2114153323Srodrigc new->br_startoff + new->br_blockcount == right.br_startoff && 2115153323Srodrigc new->br_startblock + new->br_blockcount == 2116153323Srodrigc right.br_startblock && 2117153323Srodrigc new->br_state == right.br_state && 2118153323Srodrigc new->br_blockcount + right.br_blockcount <= MAXEXTLEN && 2119153323Srodrigc (!STATE_TEST(LEFT_CONTIG) || 2120153323Srodrigc left.br_blockcount + new->br_blockcount + 2121153323Srodrigc right.br_blockcount <= MAXEXTLEN)); 2122153323Srodrigc 2123159451Srodrigc error = 0; 2124153323Srodrigc /* 2125153323Srodrigc * Select which case we're in here, and implement it. 2126153323Srodrigc */ 2127153323Srodrigc switch (SWITCH_STATE) { 2128153323Srodrigc 2129153323Srodrigc case MASK2(LEFT_CONTIG, RIGHT_CONTIG): 2130153323Srodrigc /* 2131153323Srodrigc * New allocation is contiguous with real allocations on the 2132153323Srodrigc * left and on the right. 2133159451Srodrigc * Merge all three into a single extent record. 2134153323Srodrigc */ 2135153323Srodrigc xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1, 2136153323Srodrigc whichfork); 2137159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 2138153323Srodrigc left.br_blockcount + new->br_blockcount + 2139153323Srodrigc right.br_blockcount); 2140153323Srodrigc xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1, 2141153323Srodrigc whichfork); 2142153323Srodrigc xfs_bmap_trace_delete(fname, "LC|RC", ip, 2143153323Srodrigc idx, 1, whichfork); 2144159451Srodrigc xfs_iext_remove(ifp, idx, 1); 2145153323Srodrigc ifp->if_lastex = idx - 1; 2146153323Srodrigc XFS_IFORK_NEXT_SET(ip, whichfork, 2147153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) - 1); 2148153323Srodrigc if (cur == NULL) { 2149159451Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); 2150159451Srodrigc } else { 2151159451Srodrigc rval = XFS_ILOG_CORE; 2152159451Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, 2153159451Srodrigc right.br_startoff, 2154159451Srodrigc right.br_startblock, 2155159451Srodrigc right.br_blockcount, &i))) 2156159451Srodrigc goto done; 2157159451Srodrigc ASSERT(i == 1); 2158159451Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 2159159451Srodrigc goto done; 2160159451Srodrigc ASSERT(i == 1); 2161159451Srodrigc if ((error = xfs_bmbt_decrement(cur, 0, &i))) 2162159451Srodrigc goto done; 2163159451Srodrigc ASSERT(i == 1); 2164159451Srodrigc if ((error = xfs_bmbt_update(cur, left.br_startoff, 2165159451Srodrigc left.br_startblock, 2166159451Srodrigc left.br_blockcount + 2167159451Srodrigc new->br_blockcount + 2168159451Srodrigc right.br_blockcount, 2169159451Srodrigc left.br_state))) 2170159451Srodrigc goto done; 2171153323Srodrigc } 2172159451Srodrigc /* DELTA: Two in-core extents were replaced by one. */ 2173159451Srodrigc temp = left.br_startoff; 2174159451Srodrigc temp2 = left.br_blockcount + 2175159451Srodrigc new->br_blockcount + 2176159451Srodrigc right.br_blockcount; 2177159451Srodrigc break; 2178153323Srodrigc 2179153323Srodrigc case MASK(LEFT_CONTIG): 2180153323Srodrigc /* 2181153323Srodrigc * New allocation is contiguous with a real allocation 2182153323Srodrigc * on the left. 2183153323Srodrigc * Merge the new allocation with the left neighbor. 2184153323Srodrigc */ 2185153323Srodrigc xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1, whichfork); 2186159451Srodrigc xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, idx - 1), 2187153323Srodrigc left.br_blockcount + new->br_blockcount); 2188153323Srodrigc xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, whichfork); 2189153323Srodrigc ifp->if_lastex = idx - 1; 2190153323Srodrigc if (cur == NULL) { 2191159451Srodrigc rval = XFS_ILOG_FEXT(whichfork); 2192159451Srodrigc } else { 2193159451Srodrigc rval = 0; 2194159451Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, 2195159451Srodrigc left.br_startoff, 2196159451Srodrigc left.br_startblock, 2197159451Srodrigc left.br_blockcount, &i))) 2198159451Srodrigc goto done; 2199159451Srodrigc ASSERT(i == 1); 2200159451Srodrigc if ((error = xfs_bmbt_update(cur, left.br_startoff, 2201159451Srodrigc left.br_startblock, 2202159451Srodrigc left.br_blockcount + 2203159451Srodrigc new->br_blockcount, 2204159451Srodrigc left.br_state))) 2205159451Srodrigc goto done; 2206153323Srodrigc } 2207159451Srodrigc /* DELTA: One in-core extent grew. */ 2208159451Srodrigc temp = left.br_startoff; 2209159451Srodrigc temp2 = left.br_blockcount + 2210159451Srodrigc new->br_blockcount; 2211159451Srodrigc break; 2212153323Srodrigc 2213153323Srodrigc case MASK(RIGHT_CONTIG): 2214153323Srodrigc /* 2215153323Srodrigc * New allocation is contiguous with a real allocation 2216153323Srodrigc * on the right. 2217153323Srodrigc * Merge the new allocation with the right neighbor. 2218153323Srodrigc */ 2219153323Srodrigc xfs_bmap_trace_pre_update(fname, "RC", ip, idx, whichfork); 2220153323Srodrigc xfs_bmbt_set_allf(ep, new->br_startoff, new->br_startblock, 2221153323Srodrigc new->br_blockcount + right.br_blockcount, 2222153323Srodrigc right.br_state); 2223153323Srodrigc xfs_bmap_trace_post_update(fname, "RC", ip, idx, whichfork); 2224153323Srodrigc ifp->if_lastex = idx; 2225153323Srodrigc if (cur == NULL) { 2226159451Srodrigc rval = XFS_ILOG_FEXT(whichfork); 2227159451Srodrigc } else { 2228159451Srodrigc rval = 0; 2229159451Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, 2230159451Srodrigc right.br_startoff, 2231159451Srodrigc right.br_startblock, 2232159451Srodrigc right.br_blockcount, &i))) 2233159451Srodrigc goto done; 2234159451Srodrigc ASSERT(i == 1); 2235159451Srodrigc if ((error = xfs_bmbt_update(cur, new->br_startoff, 2236159451Srodrigc new->br_startblock, 2237159451Srodrigc new->br_blockcount + 2238159451Srodrigc right.br_blockcount, 2239159451Srodrigc right.br_state))) 2240159451Srodrigc goto done; 2241153323Srodrigc } 2242159451Srodrigc /* DELTA: One in-core extent grew. */ 2243159451Srodrigc temp = new->br_startoff; 2244159451Srodrigc temp2 = new->br_blockcount + 2245159451Srodrigc right.br_blockcount; 2246159451Srodrigc break; 2247153323Srodrigc 2248153323Srodrigc case 0: 2249153323Srodrigc /* 2250153323Srodrigc * New allocation is not contiguous with another 2251153323Srodrigc * real allocation. 2252153323Srodrigc * Insert a new entry. 2253153323Srodrigc */ 2254153323Srodrigc xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL, 2255153323Srodrigc whichfork); 2256159451Srodrigc xfs_iext_insert(ifp, idx, 1, new); 2257153323Srodrigc ifp->if_lastex = idx; 2258153323Srodrigc XFS_IFORK_NEXT_SET(ip, whichfork, 2259153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) + 1); 2260153323Srodrigc if (cur == NULL) { 2261159451Srodrigc rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); 2262159451Srodrigc } else { 2263159451Srodrigc rval = XFS_ILOG_CORE; 2264159451Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, 2265159451Srodrigc new->br_startoff, 2266159451Srodrigc new->br_startblock, 2267159451Srodrigc new->br_blockcount, &i))) 2268159451Srodrigc goto done; 2269159451Srodrigc ASSERT(i == 0); 2270159451Srodrigc cur->bc_rec.b.br_state = new->br_state; 2271159451Srodrigc if ((error = xfs_bmbt_insert(cur, &i))) 2272159451Srodrigc goto done; 2273159451Srodrigc ASSERT(i == 1); 2274153323Srodrigc } 2275159451Srodrigc /* DELTA: A new extent was added in a hole. */ 2276159451Srodrigc temp = new->br_startoff; 2277159451Srodrigc temp2 = new->br_blockcount; 2278159451Srodrigc break; 2279153323Srodrigc } 2280159451Srodrigc if (delta) { 2281159451Srodrigc temp2 += temp; 2282159451Srodrigc if (delta->xed_startoff > temp) 2283159451Srodrigc delta->xed_startoff = temp; 2284159451Srodrigc if (delta->xed_blockcount < temp2) 2285159451Srodrigc delta->xed_blockcount = temp2; 2286159451Srodrigc } 2287159451Srodrigcdone: 2288159451Srodrigc *logflagsp = rval; 2289159451Srodrigc return error; 2290153323Srodrigc#undef MASK 2291153323Srodrigc#undef MASK2 2292153323Srodrigc#undef STATE_SET 2293153323Srodrigc#undef STATE_TEST 2294153323Srodrigc#undef STATE_SET_TEST 2295153323Srodrigc#undef SWITCH_STATE 2296153323Srodrigc} 2297153323Srodrigc 2298159451Srodrigc/* 2299159451Srodrigc * Adjust the size of the new extent based on di_extsize and rt extsize. 2300159451Srodrigc */ 2301159451SrodrigcSTATIC int 2302159451Srodrigcxfs_bmap_extsize_align( 2303159451Srodrigc xfs_mount_t *mp, 2304159451Srodrigc xfs_bmbt_irec_t *gotp, /* next extent pointer */ 2305159451Srodrigc xfs_bmbt_irec_t *prevp, /* previous extent pointer */ 2306159451Srodrigc xfs_extlen_t extsz, /* align to this extent size */ 2307159451Srodrigc int rt, /* is this a realtime inode? */ 2308159451Srodrigc int eof, /* is extent at end-of-file? */ 2309159451Srodrigc int delay, /* creating delalloc extent? */ 2310159451Srodrigc int convert, /* overwriting unwritten extent? */ 2311159451Srodrigc xfs_fileoff_t *offp, /* in/out: aligned offset */ 2312159451Srodrigc xfs_extlen_t *lenp) /* in/out: aligned length */ 2313159451Srodrigc{ 2314159451Srodrigc xfs_fileoff_t orig_off; /* original offset */ 2315159451Srodrigc xfs_extlen_t orig_alen; /* original length */ 2316159451Srodrigc xfs_fileoff_t orig_end; /* original off+len */ 2317159451Srodrigc xfs_fileoff_t nexto; /* next file offset */ 2318159451Srodrigc xfs_fileoff_t prevo; /* previous file offset */ 2319159451Srodrigc xfs_fileoff_t align_off; /* temp for offset */ 2320159451Srodrigc xfs_extlen_t align_alen; /* temp for length */ 2321159451Srodrigc xfs_extlen_t temp; /* temp for calculations */ 2322159451Srodrigc 2323159451Srodrigc if (convert) 2324159451Srodrigc return 0; 2325159451Srodrigc 2326159451Srodrigc orig_off = align_off = *offp; 2327159451Srodrigc orig_alen = align_alen = *lenp; 2328159451Srodrigc orig_end = orig_off + orig_alen; 2329159451Srodrigc 2330159451Srodrigc /* 2331159451Srodrigc * If this request overlaps an existing extent, then don't 2332159451Srodrigc * attempt to perform any additional alignment. 2333159451Srodrigc */ 2334159451Srodrigc if (!delay && !eof && 2335159451Srodrigc (orig_off >= gotp->br_startoff) && 2336159451Srodrigc (orig_end <= gotp->br_startoff + gotp->br_blockcount)) { 2337159451Srodrigc return 0; 2338159451Srodrigc } 2339159451Srodrigc 2340159451Srodrigc /* 2341159451Srodrigc * If the file offset is unaligned vs. the extent size 2342159451Srodrigc * we need to align it. This will be possible unless 2343159451Srodrigc * the file was previously written with a kernel that didn't 2344159451Srodrigc * perform this alignment, or if a truncate shot us in the 2345159451Srodrigc * foot. 2346159451Srodrigc */ 2347159451Srodrigc temp = do_mod(orig_off, extsz); 2348159451Srodrigc if (temp) { 2349159451Srodrigc align_alen += temp; 2350159451Srodrigc align_off -= temp; 2351159451Srodrigc } 2352159451Srodrigc /* 2353159451Srodrigc * Same adjustment for the end of the requested area. 2354159451Srodrigc */ 2355159451Srodrigc if ((temp = (align_alen % extsz))) { 2356159451Srodrigc align_alen += extsz - temp; 2357159451Srodrigc } 2358159451Srodrigc /* 2359159451Srodrigc * If the previous block overlaps with this proposed allocation 2360159451Srodrigc * then move the start forward without adjusting the length. 2361159451Srodrigc */ 2362159451Srodrigc if (prevp->br_startoff != NULLFILEOFF) { 2363159451Srodrigc if (prevp->br_startblock == HOLESTARTBLOCK) 2364159451Srodrigc prevo = prevp->br_startoff; 2365159451Srodrigc else 2366159451Srodrigc prevo = prevp->br_startoff + prevp->br_blockcount; 2367159451Srodrigc } else 2368159451Srodrigc prevo = 0; 2369159451Srodrigc if (align_off != orig_off && align_off < prevo) 2370159451Srodrigc align_off = prevo; 2371159451Srodrigc /* 2372159451Srodrigc * If the next block overlaps with this proposed allocation 2373159451Srodrigc * then move the start back without adjusting the length, 2374159451Srodrigc * but not before offset 0. 2375159451Srodrigc * This may of course make the start overlap previous block, 2376159451Srodrigc * and if we hit the offset 0 limit then the next block 2377159451Srodrigc * can still overlap too. 2378159451Srodrigc */ 2379159451Srodrigc if (!eof && gotp->br_startoff != NULLFILEOFF) { 2380159451Srodrigc if ((delay && gotp->br_startblock == HOLESTARTBLOCK) || 2381159451Srodrigc (!delay && gotp->br_startblock == DELAYSTARTBLOCK)) 2382159451Srodrigc nexto = gotp->br_startoff + gotp->br_blockcount; 2383159451Srodrigc else 2384159451Srodrigc nexto = gotp->br_startoff; 2385159451Srodrigc } else 2386159451Srodrigc nexto = NULLFILEOFF; 2387159451Srodrigc if (!eof && 2388159451Srodrigc align_off + align_alen != orig_end && 2389159451Srodrigc align_off + align_alen > nexto) 2390159451Srodrigc align_off = nexto > align_alen ? nexto - align_alen : 0; 2391159451Srodrigc /* 2392159451Srodrigc * If we're now overlapping the next or previous extent that 2393159451Srodrigc * means we can't fit an extsz piece in this hole. Just move 2394159451Srodrigc * the start forward to the first valid spot and set 2395159451Srodrigc * the length so we hit the end. 2396159451Srodrigc */ 2397159451Srodrigc if (align_off != orig_off && align_off < prevo) 2398159451Srodrigc align_off = prevo; 2399159451Srodrigc if (align_off + align_alen != orig_end && 2400159451Srodrigc align_off + align_alen > nexto && 2401159451Srodrigc nexto != NULLFILEOFF) { 2402159451Srodrigc ASSERT(nexto > prevo); 2403159451Srodrigc align_alen = nexto - align_off; 2404159451Srodrigc } 2405159451Srodrigc 2406159451Srodrigc /* 2407159451Srodrigc * If realtime, and the result isn't a multiple of the realtime 2408159451Srodrigc * extent size we need to remove blocks until it is. 2409159451Srodrigc */ 2410159451Srodrigc if (rt && (temp = (align_alen % mp->m_sb.sb_rextsize))) { 2411159451Srodrigc /* 2412159451Srodrigc * We're not covering the original request, or 2413159451Srodrigc * we won't be able to once we fix the length. 2414159451Srodrigc */ 2415159451Srodrigc if (orig_off < align_off || 2416159451Srodrigc orig_end > align_off + align_alen || 2417159451Srodrigc align_alen - temp < orig_alen) 2418159451Srodrigc return XFS_ERROR(EINVAL); 2419159451Srodrigc /* 2420159451Srodrigc * Try to fix it by moving the start up. 2421159451Srodrigc */ 2422159451Srodrigc if (align_off + temp <= orig_off) { 2423159451Srodrigc align_alen -= temp; 2424159451Srodrigc align_off += temp; 2425159451Srodrigc } 2426159451Srodrigc /* 2427159451Srodrigc * Try to fix it by moving the end in. 2428159451Srodrigc */ 2429159451Srodrigc else if (align_off + align_alen - temp >= orig_end) 2430159451Srodrigc align_alen -= temp; 2431159451Srodrigc /* 2432159451Srodrigc * Set the start to the minimum then trim the length. 2433159451Srodrigc */ 2434159451Srodrigc else { 2435159451Srodrigc align_alen -= orig_off - align_off; 2436159451Srodrigc align_off = orig_off; 2437159451Srodrigc align_alen -= align_alen % mp->m_sb.sb_rextsize; 2438159451Srodrigc } 2439159451Srodrigc /* 2440159451Srodrigc * Result doesn't cover the request, fail it. 2441159451Srodrigc */ 2442159451Srodrigc if (orig_off < align_off || orig_end > align_off + align_alen) 2443159451Srodrigc return XFS_ERROR(EINVAL); 2444159451Srodrigc } else { 2445159451Srodrigc ASSERT(orig_off >= align_off); 2446159451Srodrigc ASSERT(orig_end <= align_off + align_alen); 2447159451Srodrigc } 2448159451Srodrigc 2449159451Srodrigc#ifdef DEBUG 2450159451Srodrigc if (!eof && gotp->br_startoff != NULLFILEOFF) 2451159451Srodrigc ASSERT(align_off + align_alen <= gotp->br_startoff); 2452159451Srodrigc if (prevp->br_startoff != NULLFILEOFF) 2453159451Srodrigc ASSERT(align_off >= prevp->br_startoff + prevp->br_blockcount); 2454159451Srodrigc#endif 2455159451Srodrigc 2456159451Srodrigc *lenp = align_alen; 2457159451Srodrigc *offp = align_off; 2458159451Srodrigc return 0; 2459159451Srodrigc} 2460159451Srodrigc 2461153323Srodrigc#define XFS_ALLOC_GAP_UNITS 4 2462153323Srodrigc 2463159451SrodrigcSTATIC int 2464159451Srodrigcxfs_bmap_adjacent( 2465153323Srodrigc xfs_bmalloca_t *ap) /* bmap alloc argument struct */ 2466153323Srodrigc{ 2467153323Srodrigc xfs_fsblock_t adjust; /* adjustment to block numbers */ 2468153323Srodrigc xfs_agnumber_t fb_agno; /* ag number of ap->firstblock */ 2469153323Srodrigc xfs_mount_t *mp; /* mount point structure */ 2470153323Srodrigc int nullfb; /* true if ap->firstblock isn't set */ 2471153323Srodrigc int rt; /* true if inode is realtime */ 2472153323Srodrigc 2473153323Srodrigc#define ISVALID(x,y) \ 2474153323Srodrigc (rt ? \ 2475153323Srodrigc (x) < mp->m_sb.sb_rblocks : \ 2476153323Srodrigc XFS_FSB_TO_AGNO(mp, x) == XFS_FSB_TO_AGNO(mp, y) && \ 2477153323Srodrigc XFS_FSB_TO_AGNO(mp, x) < mp->m_sb.sb_agcount && \ 2478153323Srodrigc XFS_FSB_TO_AGBNO(mp, x) < mp->m_sb.sb_agblocks) 2479153323Srodrigc 2480153323Srodrigc mp = ap->ip->i_mount; 2481153323Srodrigc nullfb = ap->firstblock == NULLFSBLOCK; 2482153323Srodrigc rt = XFS_IS_REALTIME_INODE(ap->ip) && ap->userdata; 2483153323Srodrigc fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock); 2484153323Srodrigc /* 2485153323Srodrigc * If allocating at eof, and there's a previous real block, 2486153323Srodrigc * try to use it's last block as our starting point. 2487153323Srodrigc */ 2488153323Srodrigc if (ap->eof && ap->prevp->br_startoff != NULLFILEOFF && 2489153323Srodrigc !ISNULLSTARTBLOCK(ap->prevp->br_startblock) && 2490153323Srodrigc ISVALID(ap->prevp->br_startblock + ap->prevp->br_blockcount, 2491153323Srodrigc ap->prevp->br_startblock)) { 2492153323Srodrigc ap->rval = ap->prevp->br_startblock + ap->prevp->br_blockcount; 2493153323Srodrigc /* 2494153323Srodrigc * Adjust for the gap between prevp and us. 2495153323Srodrigc */ 2496153323Srodrigc adjust = ap->off - 2497153323Srodrigc (ap->prevp->br_startoff + ap->prevp->br_blockcount); 2498153323Srodrigc if (adjust && 2499153323Srodrigc ISVALID(ap->rval + adjust, ap->prevp->br_startblock)) 2500153323Srodrigc ap->rval += adjust; 2501153323Srodrigc } 2502153323Srodrigc /* 2503153323Srodrigc * If not at eof, then compare the two neighbor blocks. 2504153323Srodrigc * Figure out whether either one gives us a good starting point, 2505153323Srodrigc * and pick the better one. 2506153323Srodrigc */ 2507153323Srodrigc else if (!ap->eof) { 2508153323Srodrigc xfs_fsblock_t gotbno; /* right side block number */ 2509153323Srodrigc xfs_fsblock_t gotdiff=0; /* right side difference */ 2510153323Srodrigc xfs_fsblock_t prevbno; /* left side block number */ 2511153323Srodrigc xfs_fsblock_t prevdiff=0; /* left side difference */ 2512153323Srodrigc 2513153323Srodrigc /* 2514153323Srodrigc * If there's a previous (left) block, select a requested 2515153323Srodrigc * start block based on it. 2516153323Srodrigc */ 2517153323Srodrigc if (ap->prevp->br_startoff != NULLFILEOFF && 2518153323Srodrigc !ISNULLSTARTBLOCK(ap->prevp->br_startblock) && 2519153323Srodrigc (prevbno = ap->prevp->br_startblock + 2520153323Srodrigc ap->prevp->br_blockcount) && 2521153323Srodrigc ISVALID(prevbno, ap->prevp->br_startblock)) { 2522153323Srodrigc /* 2523153323Srodrigc * Calculate gap to end of previous block. 2524153323Srodrigc */ 2525153323Srodrigc adjust = prevdiff = ap->off - 2526153323Srodrigc (ap->prevp->br_startoff + 2527153323Srodrigc ap->prevp->br_blockcount); 2528153323Srodrigc /* 2529153323Srodrigc * Figure the startblock based on the previous block's 2530153323Srodrigc * end and the gap size. 2531153323Srodrigc * Heuristic! 2532153323Srodrigc * If the gap is large relative to the piece we're 2533153323Srodrigc * allocating, or using it gives us an invalid block 2534153323Srodrigc * number, then just use the end of the previous block. 2535153323Srodrigc */ 2536153323Srodrigc if (prevdiff <= XFS_ALLOC_GAP_UNITS * ap->alen && 2537153323Srodrigc ISVALID(prevbno + prevdiff, 2538153323Srodrigc ap->prevp->br_startblock)) 2539153323Srodrigc prevbno += adjust; 2540153323Srodrigc else 2541153323Srodrigc prevdiff += adjust; 2542153323Srodrigc /* 2543153323Srodrigc * If the firstblock forbids it, can't use it, 2544153323Srodrigc * must use default. 2545153323Srodrigc */ 2546153323Srodrigc if (!rt && !nullfb && 2547153323Srodrigc XFS_FSB_TO_AGNO(mp, prevbno) != fb_agno) 2548153323Srodrigc prevbno = NULLFSBLOCK; 2549153323Srodrigc } 2550153323Srodrigc /* 2551153323Srodrigc * No previous block or can't follow it, just default. 2552153323Srodrigc */ 2553153323Srodrigc else 2554153323Srodrigc prevbno = NULLFSBLOCK; 2555153323Srodrigc /* 2556153323Srodrigc * If there's a following (right) block, select a requested 2557153323Srodrigc * start block based on it. 2558153323Srodrigc */ 2559153323Srodrigc if (!ISNULLSTARTBLOCK(ap->gotp->br_startblock)) { 2560153323Srodrigc /* 2561153323Srodrigc * Calculate gap to start of next block. 2562153323Srodrigc */ 2563153323Srodrigc adjust = gotdiff = ap->gotp->br_startoff - ap->off; 2564153323Srodrigc /* 2565153323Srodrigc * Figure the startblock based on the next block's 2566153323Srodrigc * start and the gap size. 2567153323Srodrigc */ 2568153323Srodrigc gotbno = ap->gotp->br_startblock; 2569153323Srodrigc /* 2570153323Srodrigc * Heuristic! 2571153323Srodrigc * If the gap is large relative to the piece we're 2572153323Srodrigc * allocating, or using it gives us an invalid block 2573153323Srodrigc * number, then just use the start of the next block 2574153323Srodrigc * offset by our length. 2575153323Srodrigc */ 2576153323Srodrigc if (gotdiff <= XFS_ALLOC_GAP_UNITS * ap->alen && 2577153323Srodrigc ISVALID(gotbno - gotdiff, gotbno)) 2578153323Srodrigc gotbno -= adjust; 2579153323Srodrigc else if (ISVALID(gotbno - ap->alen, gotbno)) { 2580153323Srodrigc gotbno -= ap->alen; 2581153323Srodrigc gotdiff += adjust - ap->alen; 2582153323Srodrigc } else 2583153323Srodrigc gotdiff += adjust; 2584153323Srodrigc /* 2585153323Srodrigc * If the firstblock forbids it, can't use it, 2586153323Srodrigc * must use default. 2587153323Srodrigc */ 2588153323Srodrigc if (!rt && !nullfb && 2589153323Srodrigc XFS_FSB_TO_AGNO(mp, gotbno) != fb_agno) 2590153323Srodrigc gotbno = NULLFSBLOCK; 2591153323Srodrigc } 2592153323Srodrigc /* 2593153323Srodrigc * No next block, just default. 2594153323Srodrigc */ 2595153323Srodrigc else 2596153323Srodrigc gotbno = NULLFSBLOCK; 2597153323Srodrigc /* 2598153323Srodrigc * If both valid, pick the better one, else the only good 2599153323Srodrigc * one, else ap->rval is already set (to 0 or the inode block). 2600153323Srodrigc */ 2601153323Srodrigc if (prevbno != NULLFSBLOCK && gotbno != NULLFSBLOCK) 2602153323Srodrigc ap->rval = prevdiff <= gotdiff ? prevbno : gotbno; 2603153323Srodrigc else if (prevbno != NULLFSBLOCK) 2604153323Srodrigc ap->rval = prevbno; 2605153323Srodrigc else if (gotbno != NULLFSBLOCK) 2606153323Srodrigc ap->rval = gotbno; 2607153323Srodrigc } 2608159451Srodrigc#undef ISVALID 2609159451Srodrigc return 0; 2610159451Srodrigc} 2611159451Srodrigc 2612159451SrodrigcSTATIC int 2613159451Srodrigcxfs_bmap_rtalloc( 2614159451Srodrigc xfs_bmalloca_t *ap) /* bmap alloc argument struct */ 2615159451Srodrigc{ 2616159451Srodrigc xfs_alloctype_t atype = 0; /* type for allocation routines */ 2617159451Srodrigc int error; /* error return value */ 2618159451Srodrigc xfs_mount_t *mp; /* mount point structure */ 2619159451Srodrigc xfs_extlen_t prod = 0; /* product factor for allocators */ 2620159451Srodrigc xfs_extlen_t ralen = 0; /* realtime allocation length */ 2621159451Srodrigc xfs_extlen_t align; /* minimum allocation alignment */ 2622159451Srodrigc xfs_rtblock_t rtx; /* realtime extent number */ 2623159451Srodrigc xfs_rtblock_t rtb; 2624159451Srodrigc 2625159451Srodrigc mp = ap->ip->i_mount; 2626159451Srodrigc align = ap->ip->i_d.di_extsize ? 2627159451Srodrigc ap->ip->i_d.di_extsize : mp->m_sb.sb_rextsize; 2628159451Srodrigc prod = align / mp->m_sb.sb_rextsize; 2629159451Srodrigc error = xfs_bmap_extsize_align(mp, ap->gotp, ap->prevp, 2630159451Srodrigc align, 1, ap->eof, 0, 2631159451Srodrigc ap->conv, &ap->off, &ap->alen); 2632159451Srodrigc if (error) 2633159451Srodrigc return error; 2634159451Srodrigc ASSERT(ap->alen); 2635159451Srodrigc ASSERT(ap->alen % mp->m_sb.sb_rextsize == 0); 2636159451Srodrigc 2637153323Srodrigc /* 2638159451Srodrigc * If the offset & length are not perfectly aligned 2639159451Srodrigc * then kill prod, it will just get us in trouble. 2640159451Srodrigc */ 2641159451Srodrigc if (do_mod(ap->off, align) || ap->alen % align) 2642159451Srodrigc prod = 1; 2643159451Srodrigc /* 2644159451Srodrigc * Set ralen to be the actual requested length in rtextents. 2645159451Srodrigc */ 2646159451Srodrigc ralen = ap->alen / mp->m_sb.sb_rextsize; 2647159451Srodrigc /* 2648159451Srodrigc * If the old value was close enough to MAXEXTLEN that 2649159451Srodrigc * we rounded up to it, cut it back so it's valid again. 2650159451Srodrigc * Note that if it's a really large request (bigger than 2651159451Srodrigc * MAXEXTLEN), we don't hear about that number, and can't 2652159451Srodrigc * adjust the starting point to match it. 2653159451Srodrigc */ 2654159451Srodrigc if (ralen * mp->m_sb.sb_rextsize >= MAXEXTLEN) 2655159451Srodrigc ralen = MAXEXTLEN / mp->m_sb.sb_rextsize; 2656159451Srodrigc /* 2657159451Srodrigc * If it's an allocation to an empty file at offset 0, 2658159451Srodrigc * pick an extent that will space things out in the rt area. 2659159451Srodrigc */ 2660159451Srodrigc if (ap->eof && ap->off == 0) { 2661159451Srodrigc error = xfs_rtpick_extent(mp, ap->tp, ralen, &rtx); 2662159451Srodrigc if (error) 2663159451Srodrigc return error; 2664159451Srodrigc ap->rval = rtx * mp->m_sb.sb_rextsize; 2665159451Srodrigc } else { 2666159451Srodrigc ap->rval = 0; 2667159451Srodrigc } 2668159451Srodrigc 2669159451Srodrigc xfs_bmap_adjacent(ap); 2670159451Srodrigc 2671159451Srodrigc /* 2672159451Srodrigc * Realtime allocation, done through xfs_rtallocate_extent. 2673159451Srodrigc */ 2674159451Srodrigc atype = ap->rval == 0 ? XFS_ALLOCTYPE_ANY_AG : XFS_ALLOCTYPE_NEAR_BNO; 2675159451Srodrigc do_div(ap->rval, mp->m_sb.sb_rextsize); 2676159451Srodrigc rtb = ap->rval; 2677159451Srodrigc ap->alen = ralen; 2678159451Srodrigc if ((error = xfs_rtallocate_extent(ap->tp, ap->rval, 1, ap->alen, 2679159451Srodrigc &ralen, atype, ap->wasdel, prod, &rtb))) 2680159451Srodrigc return error; 2681159451Srodrigc if (rtb == NULLFSBLOCK && prod > 1 && 2682159451Srodrigc (error = xfs_rtallocate_extent(ap->tp, ap->rval, 1, 2683159451Srodrigc ap->alen, &ralen, atype, 2684159451Srodrigc ap->wasdel, 1, &rtb))) 2685159451Srodrigc return error; 2686159451Srodrigc ap->rval = rtb; 2687159451Srodrigc if (ap->rval != NULLFSBLOCK) { 2688159451Srodrigc ap->rval *= mp->m_sb.sb_rextsize; 2689159451Srodrigc ralen *= mp->m_sb.sb_rextsize; 2690159451Srodrigc ap->alen = ralen; 2691159451Srodrigc ap->ip->i_d.di_nblocks += ralen; 2692159451Srodrigc xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE); 2693159451Srodrigc if (ap->wasdel) 2694159451Srodrigc ap->ip->i_delayed_blks -= ralen; 2695159451Srodrigc /* 2696159451Srodrigc * Adjust the disk quota also. This was reserved 2697159451Srodrigc * earlier. 2698159451Srodrigc */ 2699159451Srodrigc XFS_TRANS_MOD_DQUOT_BYINO(mp, ap->tp, ap->ip, 2700159451Srodrigc ap->wasdel ? XFS_TRANS_DQ_DELRTBCOUNT : 2701159451Srodrigc XFS_TRANS_DQ_RTBCOUNT, (long) ralen); 2702159451Srodrigc } else { 2703159451Srodrigc ap->alen = 0; 2704159451Srodrigc } 2705159451Srodrigc return 0; 2706159451Srodrigc} 2707159451Srodrigc 2708159451SrodrigcSTATIC int 2709159451Srodrigcxfs_bmap_btalloc( 2710159451Srodrigc xfs_bmalloca_t *ap) /* bmap alloc argument struct */ 2711159451Srodrigc{ 2712159451Srodrigc xfs_mount_t *mp; /* mount point structure */ 2713159451Srodrigc xfs_alloctype_t atype = 0; /* type for allocation routines */ 2714159451Srodrigc xfs_extlen_t align; /* minimum allocation alignment */ 2715159451Srodrigc xfs_agnumber_t ag; 2716159451Srodrigc xfs_agnumber_t fb_agno; /* ag number of ap->firstblock */ 2717159451Srodrigc xfs_agnumber_t startag; 2718159451Srodrigc xfs_alloc_arg_t args; 2719159451Srodrigc xfs_extlen_t blen; 2720159451Srodrigc xfs_extlen_t delta; 2721159451Srodrigc xfs_extlen_t longest; 2722159451Srodrigc xfs_extlen_t need; 2723159451Srodrigc xfs_extlen_t nextminlen = 0; 2724159451Srodrigc xfs_perag_t *pag; 2725159451Srodrigc int nullfb; /* true if ap->firstblock isn't set */ 2726159451Srodrigc int isaligned; 2727159451Srodrigc int notinit; 2728159451Srodrigc int tryagain; 2729159451Srodrigc int error; 2730159451Srodrigc 2731159451Srodrigc mp = ap->ip->i_mount; 2732159451Srodrigc align = (ap->userdata && ap->ip->i_d.di_extsize && 2733159451Srodrigc (ap->ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE)) ? 2734159451Srodrigc ap->ip->i_d.di_extsize : 0; 2735159451Srodrigc if (unlikely(align)) { 2736159451Srodrigc error = xfs_bmap_extsize_align(mp, ap->gotp, ap->prevp, 2737159451Srodrigc align, 0, ap->eof, 0, ap->conv, 2738159451Srodrigc &ap->off, &ap->alen); 2739159451Srodrigc ASSERT(!error); 2740159451Srodrigc ASSERT(ap->alen); 2741159451Srodrigc } 2742159451Srodrigc nullfb = ap->firstblock == NULLFSBLOCK; 2743159451Srodrigc fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock); 2744159451Srodrigc if (nullfb) 2745159451Srodrigc ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino); 2746159451Srodrigc else 2747159451Srodrigc ap->rval = ap->firstblock; 2748159451Srodrigc 2749159451Srodrigc xfs_bmap_adjacent(ap); 2750159451Srodrigc 2751159451Srodrigc /* 2752153323Srodrigc * If allowed, use ap->rval; otherwise must use firstblock since 2753153323Srodrigc * it's in the right allocation group. 2754153323Srodrigc */ 2755159451Srodrigc if (nullfb || XFS_FSB_TO_AGNO(mp, ap->rval) == fb_agno) 2756153323Srodrigc ; 2757153323Srodrigc else 2758153323Srodrigc ap->rval = ap->firstblock; 2759153323Srodrigc /* 2760159451Srodrigc * Normal allocation, done through xfs_alloc_vextent. 2761153323Srodrigc */ 2762159451Srodrigc tryagain = isaligned = 0; 2763159451Srodrigc args.tp = ap->tp; 2764159451Srodrigc args.mp = mp; 2765159451Srodrigc args.fsbno = ap->rval; 2766159451Srodrigc args.maxlen = MIN(ap->alen, mp->m_sb.sb_agblocks); 2767159451Srodrigc blen = 0; 2768159451Srodrigc if (nullfb) { 2769159451Srodrigc args.type = XFS_ALLOCTYPE_START_BNO; 2770159451Srodrigc args.total = ap->total; 2771159451Srodrigc /* 2772159451Srodrigc * Find the longest available space. 2773159451Srodrigc * We're going to try for the whole allocation at once. 2774159451Srodrigc */ 2775159451Srodrigc startag = ag = XFS_FSB_TO_AGNO(mp, args.fsbno); 2776159451Srodrigc notinit = 0; 2777159451Srodrigc down_read(&mp->m_peraglock); 2778159451Srodrigc while (blen < ap->alen) { 2779159451Srodrigc pag = &mp->m_perag[ag]; 2780159451Srodrigc if (!pag->pagf_init && 2781159451Srodrigc (error = xfs_alloc_pagf_init(mp, args.tp, 2782159451Srodrigc ag, XFS_ALLOC_FLAG_TRYLOCK))) { 2783159451Srodrigc up_read(&mp->m_peraglock); 2784159451Srodrigc return error; 2785159451Srodrigc } 2786153323Srodrigc /* 2787159451Srodrigc * See xfs_alloc_fix_freelist... 2788153323Srodrigc */ 2789159451Srodrigc if (pag->pagf_init) { 2790159451Srodrigc need = XFS_MIN_FREELIST_PAG(pag, mp); 2791159451Srodrigc delta = need > pag->pagf_flcount ? 2792159451Srodrigc need - pag->pagf_flcount : 0; 2793159451Srodrigc longest = (pag->pagf_longest > delta) ? 2794159451Srodrigc (pag->pagf_longest - delta) : 2795159451Srodrigc (pag->pagf_flcount > 0 || 2796159451Srodrigc pag->pagf_longest > 0); 2797159451Srodrigc if (blen < longest) 2798159451Srodrigc blen = longest; 2799159451Srodrigc } else 2800159451Srodrigc notinit = 1; 2801159451Srodrigc if (++ag == mp->m_sb.sb_agcount) 2802159451Srodrigc ag = 0; 2803159451Srodrigc if (ag == startag) 2804159451Srodrigc break; 2805159451Srodrigc } 2806159451Srodrigc up_read(&mp->m_peraglock); 2807159451Srodrigc /* 2808159451Srodrigc * Since the above loop did a BUF_TRYLOCK, it is 2809159451Srodrigc * possible that there is space for this request. 2810159451Srodrigc */ 2811159451Srodrigc if (notinit || blen < ap->minlen) 2812159451Srodrigc args.minlen = ap->minlen; 2813159451Srodrigc /* 2814159451Srodrigc * If the best seen length is less than the request 2815159451Srodrigc * length, use the best as the minimum. 2816159451Srodrigc */ 2817159451Srodrigc else if (blen < ap->alen) 2818159451Srodrigc args.minlen = blen; 2819159451Srodrigc /* 2820159451Srodrigc * Otherwise we've seen an extent as big as alen, 2821159451Srodrigc * use that as the minimum. 2822159451Srodrigc */ 2823159451Srodrigc else 2824159451Srodrigc args.minlen = ap->alen; 2825159451Srodrigc } else if (ap->low) { 2826159451Srodrigc args.type = XFS_ALLOCTYPE_FIRST_AG; 2827159451Srodrigc args.total = args.minlen = ap->minlen; 2828159451Srodrigc } else { 2829159451Srodrigc args.type = XFS_ALLOCTYPE_NEAR_BNO; 2830159451Srodrigc args.total = ap->total; 2831159451Srodrigc args.minlen = ap->minlen; 2832153323Srodrigc } 2833159451Srodrigc if (unlikely(ap->userdata && ap->ip->i_d.di_extsize && 2834159451Srodrigc (ap->ip->i_d.di_flags & XFS_DIFLAG_EXTSIZE))) { 2835159451Srodrigc args.prod = ap->ip->i_d.di_extsize; 2836159451Srodrigc if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod))) 2837159451Srodrigc args.mod = (xfs_extlen_t)(args.prod - args.mod); 2838159451Srodrigc } else if (unlikely(mp->m_sb.sb_blocksize >= NBPP)) { 2839159451Srodrigc args.prod = 1; 2840159451Srodrigc args.mod = 0; 2841159451Srodrigc } else { 2842159451Srodrigc args.prod = NBPP >> mp->m_sb.sb_blocklog; 2843159451Srodrigc if ((args.mod = (xfs_extlen_t)(do_mod(ap->off, args.prod)))) 2844159451Srodrigc args.mod = (xfs_extlen_t)(args.prod - args.mod); 2845159451Srodrigc } 2846153323Srodrigc /* 2847159451Srodrigc * If we are not low on available data blocks, and the 2848159451Srodrigc * underlying logical volume manager is a stripe, and 2849159451Srodrigc * the file offset is zero then try to allocate data 2850159451Srodrigc * blocks on stripe unit boundary. 2851159451Srodrigc * NOTE: ap->aeof is only set if the allocation length 2852159451Srodrigc * is >= the stripe unit and the allocation offset is 2853159451Srodrigc * at the end of file. 2854153323Srodrigc */ 2855159451Srodrigc if (!ap->low && ap->aeof) { 2856159451Srodrigc if (!ap->off) { 2857159451Srodrigc args.alignment = mp->m_dalign; 2858159451Srodrigc atype = args.type; 2859159451Srodrigc isaligned = 1; 2860153323Srodrigc /* 2861159451Srodrigc * Adjust for alignment 2862153323Srodrigc */ 2863159451Srodrigc if (blen > args.alignment && blen <= ap->alen) 2864159451Srodrigc args.minlen = blen - args.alignment; 2865159451Srodrigc args.minalignslop = 0; 2866159451Srodrigc } else { 2867153323Srodrigc /* 2868159451Srodrigc * First try an exact bno allocation. 2869159451Srodrigc * If it fails then do a near or start bno 2870159451Srodrigc * allocation with alignment turned on. 2871153323Srodrigc */ 2872159451Srodrigc atype = args.type; 2873159451Srodrigc tryagain = 1; 2874159451Srodrigc args.type = XFS_ALLOCTYPE_THIS_BNO; 2875159451Srodrigc args.alignment = 1; 2876153323Srodrigc /* 2877159451Srodrigc * Compute the minlen+alignment for the 2878159451Srodrigc * next case. Set slop so that the value 2879159451Srodrigc * of minlen+alignment+slop doesn't go up 2880159451Srodrigc * between the calls. 2881153323Srodrigc */ 2882159451Srodrigc if (blen > mp->m_dalign && blen <= ap->alen) 2883159451Srodrigc nextminlen = blen - mp->m_dalign; 2884153323Srodrigc else 2885159451Srodrigc nextminlen = args.minlen; 2886159451Srodrigc if (nextminlen + mp->m_dalign > args.minlen + 1) 2887159451Srodrigc args.minalignslop = 2888159451Srodrigc nextminlen + mp->m_dalign - 2889159451Srodrigc args.minlen - 1; 2890159451Srodrigc else 2891159451Srodrigc args.minalignslop = 0; 2892153323Srodrigc } 2893159451Srodrigc } else { 2894159451Srodrigc args.alignment = 1; 2895159451Srodrigc args.minalignslop = 0; 2896159451Srodrigc } 2897159451Srodrigc args.minleft = ap->minleft; 2898159451Srodrigc args.wasdel = ap->wasdel; 2899159451Srodrigc args.isfl = 0; 2900159451Srodrigc args.userdata = ap->userdata; 2901159451Srodrigc if ((error = xfs_alloc_vextent(&args))) 2902159451Srodrigc return error; 2903159451Srodrigc if (tryagain && args.fsbno == NULLFSBLOCK) { 2904153323Srodrigc /* 2905159451Srodrigc * Exact allocation failed. Now try with alignment 2906159451Srodrigc * turned on. 2907153323Srodrigc */ 2908159451Srodrigc args.type = atype; 2909159451Srodrigc args.fsbno = ap->rval; 2910159451Srodrigc args.alignment = mp->m_dalign; 2911159451Srodrigc args.minlen = nextminlen; 2912159451Srodrigc args.minalignslop = 0; 2913159451Srodrigc isaligned = 1; 2914153323Srodrigc if ((error = xfs_alloc_vextent(&args))) 2915153323Srodrigc return error; 2916153323Srodrigc } 2917159451Srodrigc if (isaligned && args.fsbno == NULLFSBLOCK) { 2918159451Srodrigc /* 2919159451Srodrigc * allocation failed, so turn off alignment and 2920159451Srodrigc * try again. 2921159451Srodrigc */ 2922159451Srodrigc args.type = atype; 2923159451Srodrigc args.fsbno = ap->rval; 2924159451Srodrigc args.alignment = 0; 2925159451Srodrigc if ((error = xfs_alloc_vextent(&args))) 2926159451Srodrigc return error; 2927159451Srodrigc } 2928159451Srodrigc if (args.fsbno == NULLFSBLOCK && nullfb && 2929159451Srodrigc args.minlen > ap->minlen) { 2930159451Srodrigc args.minlen = ap->minlen; 2931159451Srodrigc args.type = XFS_ALLOCTYPE_START_BNO; 2932159451Srodrigc args.fsbno = ap->rval; 2933159451Srodrigc if ((error = xfs_alloc_vextent(&args))) 2934159451Srodrigc return error; 2935159451Srodrigc } 2936159451Srodrigc if (args.fsbno == NULLFSBLOCK && nullfb) { 2937159451Srodrigc args.fsbno = 0; 2938159451Srodrigc args.type = XFS_ALLOCTYPE_FIRST_AG; 2939159451Srodrigc args.total = ap->minlen; 2940159451Srodrigc args.minleft = 0; 2941159451Srodrigc if ((error = xfs_alloc_vextent(&args))) 2942159451Srodrigc return error; 2943159451Srodrigc ap->low = 1; 2944159451Srodrigc } 2945159451Srodrigc if (args.fsbno != NULLFSBLOCK) { 2946159451Srodrigc ap->firstblock = ap->rval = args.fsbno; 2947159451Srodrigc ASSERT(nullfb || fb_agno == args.agno || 2948159451Srodrigc (ap->low && fb_agno < args.agno)); 2949159451Srodrigc ap->alen = args.len; 2950159451Srodrigc ap->ip->i_d.di_nblocks += args.len; 2951159451Srodrigc xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE); 2952159451Srodrigc if (ap->wasdel) 2953159451Srodrigc ap->ip->i_delayed_blks -= args.len; 2954159451Srodrigc /* 2955159451Srodrigc * Adjust the disk quota also. This was reserved 2956159451Srodrigc * earlier. 2957159451Srodrigc */ 2958159451Srodrigc XFS_TRANS_MOD_DQUOT_BYINO(mp, ap->tp, ap->ip, 2959159451Srodrigc ap->wasdel ? XFS_TRANS_DQ_DELBCOUNT : 2960159451Srodrigc XFS_TRANS_DQ_BCOUNT, 2961159451Srodrigc (long) args.len); 2962159451Srodrigc } else { 2963159451Srodrigc ap->rval = NULLFSBLOCK; 2964159451Srodrigc ap->alen = 0; 2965159451Srodrigc } 2966153323Srodrigc return 0; 2967153323Srodrigc} 2968153323Srodrigc 2969153323Srodrigc/* 2970159451Srodrigc * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file. 2971159451Srodrigc * It figures out where to ask the underlying allocator to put the new extent. 2972159451Srodrigc */ 2973159451SrodrigcSTATIC int 2974159451Srodrigcxfs_bmap_alloc( 2975159451Srodrigc xfs_bmalloca_t *ap) /* bmap alloc argument struct */ 2976159451Srodrigc{ 2977159451Srodrigc if ((ap->ip->i_d.di_flags & XFS_DIFLAG_REALTIME) && ap->userdata) 2978159451Srodrigc return xfs_bmap_rtalloc(ap); 2979159451Srodrigc return xfs_bmap_btalloc(ap); 2980159451Srodrigc} 2981159451Srodrigc 2982159451Srodrigc/* 2983153323Srodrigc * Transform a btree format file with only one leaf node, where the 2984153323Srodrigc * extents list will fit in the inode, into an extents format file. 2985159451Srodrigc * Since the file extents are already in-core, all we have to do is 2986153323Srodrigc * give up the space for the btree root and pitch the leaf block. 2987153323Srodrigc */ 2988153323SrodrigcSTATIC int /* error */ 2989153323Srodrigcxfs_bmap_btree_to_extents( 2990153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 2991153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 2992153323Srodrigc xfs_btree_cur_t *cur, /* btree cursor */ 2993153323Srodrigc int *logflagsp, /* inode logging flags */ 2994153323Srodrigc int whichfork) /* data or attr fork */ 2995153323Srodrigc{ 2996153323Srodrigc /* REFERENCED */ 2997153323Srodrigc xfs_bmbt_block_t *cblock;/* child btree block */ 2998153323Srodrigc xfs_fsblock_t cbno; /* child block number */ 2999153323Srodrigc xfs_buf_t *cbp; /* child block's buffer */ 3000153323Srodrigc int error; /* error return value */ 3001153323Srodrigc xfs_ifork_t *ifp; /* inode fork data */ 3002153323Srodrigc xfs_mount_t *mp; /* mount point structure */ 3003153323Srodrigc xfs_bmbt_ptr_t *pp; /* ptr to block address */ 3004153323Srodrigc xfs_bmbt_block_t *rblock;/* root btree block */ 3005153323Srodrigc 3006153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3007153323Srodrigc ASSERT(ifp->if_flags & XFS_IFEXTENTS); 3008153323Srodrigc ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE); 3009153323Srodrigc rblock = ifp->if_broot; 3010159451Srodrigc ASSERT(be16_to_cpu(rblock->bb_level) == 1); 3011159451Srodrigc ASSERT(be16_to_cpu(rblock->bb_numrecs) == 1); 3012153323Srodrigc ASSERT(XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes) == 1); 3013153323Srodrigc mp = ip->i_mount; 3014153323Srodrigc pp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, ifp->if_broot_bytes); 3015153323Srodrigc *logflagsp = 0; 3016153323Srodrigc#ifdef DEBUG 3017153323Srodrigc if ((error = xfs_btree_check_lptr(cur, INT_GET(*pp, ARCH_CONVERT), 1))) 3018153323Srodrigc return error; 3019153323Srodrigc#endif 3020153323Srodrigc cbno = INT_GET(*pp, ARCH_CONVERT); 3021153323Srodrigc if ((error = xfs_btree_read_bufl(mp, tp, cbno, 0, &cbp, 3022153323Srodrigc XFS_BMAP_BTREE_REF))) 3023153323Srodrigc return error; 3024153323Srodrigc cblock = XFS_BUF_TO_BMBT_BLOCK(cbp); 3025153323Srodrigc if ((error = xfs_btree_check_lblock(cur, cblock, 0, cbp))) 3026153323Srodrigc return error; 3027153323Srodrigc xfs_bmap_add_free(cbno, 1, cur->bc_private.b.flist, mp); 3028153323Srodrigc ip->i_d.di_nblocks--; 3029153323Srodrigc XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, XFS_TRANS_DQ_BCOUNT, -1L); 3030153323Srodrigc xfs_trans_binval(tp, cbp); 3031153323Srodrigc if (cur->bc_bufs[0] == cbp) 3032153323Srodrigc cur->bc_bufs[0] = NULL; 3033153323Srodrigc xfs_iroot_realloc(ip, -1, whichfork); 3034153323Srodrigc ASSERT(ifp->if_broot == NULL); 3035153323Srodrigc ASSERT((ifp->if_flags & XFS_IFBROOT) == 0); 3036153323Srodrigc XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS); 3037153323Srodrigc *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); 3038153323Srodrigc return 0; 3039153323Srodrigc} 3040153323Srodrigc 3041153323Srodrigc/* 3042159451Srodrigc * Called by xfs_bmapi to update file extent records and the btree 3043153323Srodrigc * after removing space (or undoing a delayed allocation). 3044153323Srodrigc */ 3045153323SrodrigcSTATIC int /* error */ 3046153323Srodrigcxfs_bmap_del_extent( 3047153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3048153323Srodrigc xfs_trans_t *tp, /* current transaction pointer */ 3049153323Srodrigc xfs_extnum_t idx, /* extent number to update/delete */ 3050153323Srodrigc xfs_bmap_free_t *flist, /* list of extents to be freed */ 3051153323Srodrigc xfs_btree_cur_t *cur, /* if null, not a btree */ 3052159451Srodrigc xfs_bmbt_irec_t *del, /* data to remove from extents */ 3053153323Srodrigc int *logflagsp, /* inode logging flags */ 3054159451Srodrigc xfs_extdelta_t *delta, /* Change made to incore extents */ 3055153323Srodrigc int whichfork, /* data or attr fork */ 3056153323Srodrigc int rsvd) /* OK to allocate reserved blocks */ 3057153323Srodrigc{ 3058153323Srodrigc xfs_filblks_t da_new; /* new delay-alloc indirect blocks */ 3059153323Srodrigc xfs_filblks_t da_old; /* old delay-alloc indirect blocks */ 3060153323Srodrigc xfs_fsblock_t del_endblock=0; /* first block past del */ 3061153323Srodrigc xfs_fileoff_t del_endoff; /* first offset past del */ 3062153323Srodrigc int delay; /* current block is delayed allocated */ 3063153323Srodrigc int do_fx; /* free extent at end of routine */ 3064153323Srodrigc xfs_bmbt_rec_t *ep; /* current extent entry pointer */ 3065153323Srodrigc int error; /* error return value */ 3066153323Srodrigc int flags; /* inode logging flags */ 3067153323Srodrigc#ifdef XFS_BMAP_TRACE 3068153323Srodrigc static char fname[] = "xfs_bmap_del_extent"; 3069153323Srodrigc#endif 3070153323Srodrigc xfs_bmbt_irec_t got; /* current extent entry */ 3071153323Srodrigc xfs_fileoff_t got_endoff; /* first offset past got */ 3072153323Srodrigc int i; /* temp state */ 3073153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3074153323Srodrigc xfs_mount_t *mp; /* mount structure */ 3075153323Srodrigc xfs_filblks_t nblks; /* quota/sb block count */ 3076153323Srodrigc xfs_bmbt_irec_t new; /* new record to be inserted */ 3077153323Srodrigc /* REFERENCED */ 3078153323Srodrigc uint qfield; /* quota field to update */ 3079153323Srodrigc xfs_filblks_t temp; /* for indirect length calculations */ 3080153323Srodrigc xfs_filblks_t temp2; /* for indirect length calculations */ 3081153323Srodrigc 3082153323Srodrigc XFS_STATS_INC(xs_del_exlist); 3083153323Srodrigc mp = ip->i_mount; 3084153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3085159451Srodrigc ASSERT((idx >= 0) && (idx < ifp->if_bytes / 3086159451Srodrigc (uint)sizeof(xfs_bmbt_rec_t))); 3087153323Srodrigc ASSERT(del->br_blockcount > 0); 3088159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 3089153323Srodrigc xfs_bmbt_get_all(ep, &got); 3090153323Srodrigc ASSERT(got.br_startoff <= del->br_startoff); 3091153323Srodrigc del_endoff = del->br_startoff + del->br_blockcount; 3092153323Srodrigc got_endoff = got.br_startoff + got.br_blockcount; 3093153323Srodrigc ASSERT(got_endoff >= del_endoff); 3094153323Srodrigc delay = ISNULLSTARTBLOCK(got.br_startblock); 3095153323Srodrigc ASSERT(ISNULLSTARTBLOCK(del->br_startblock) == delay); 3096153323Srodrigc flags = 0; 3097153323Srodrigc qfield = 0; 3098153323Srodrigc error = 0; 3099153323Srodrigc /* 3100153323Srodrigc * If deleting a real allocation, must free up the disk space. 3101153323Srodrigc */ 3102153323Srodrigc if (!delay) { 3103153323Srodrigc flags = XFS_ILOG_CORE; 3104153323Srodrigc /* 3105153323Srodrigc * Realtime allocation. Free it and record di_nblocks update. 3106153323Srodrigc */ 3107153323Srodrigc if (whichfork == XFS_DATA_FORK && 3108153323Srodrigc (ip->i_d.di_flags & XFS_DIFLAG_REALTIME)) { 3109153323Srodrigc xfs_fsblock_t bno; 3110153323Srodrigc xfs_filblks_t len; 3111153323Srodrigc 3112153323Srodrigc ASSERT(do_mod(del->br_blockcount, 3113153323Srodrigc mp->m_sb.sb_rextsize) == 0); 3114153323Srodrigc ASSERT(do_mod(del->br_startblock, 3115153323Srodrigc mp->m_sb.sb_rextsize) == 0); 3116153323Srodrigc bno = del->br_startblock; 3117153323Srodrigc len = del->br_blockcount; 3118153323Srodrigc do_div(bno, mp->m_sb.sb_rextsize); 3119153323Srodrigc do_div(len, mp->m_sb.sb_rextsize); 3120153323Srodrigc if ((error = xfs_rtfree_extent(ip->i_transp, bno, 3121153323Srodrigc (xfs_extlen_t)len))) 3122153323Srodrigc goto done; 3123153323Srodrigc do_fx = 0; 3124153323Srodrigc nblks = len * mp->m_sb.sb_rextsize; 3125153323Srodrigc qfield = XFS_TRANS_DQ_RTBCOUNT; 3126153323Srodrigc } 3127153323Srodrigc /* 3128153323Srodrigc * Ordinary allocation. 3129153323Srodrigc */ 3130153323Srodrigc else { 3131153323Srodrigc do_fx = 1; 3132153323Srodrigc nblks = del->br_blockcount; 3133153323Srodrigc qfield = XFS_TRANS_DQ_BCOUNT; 3134153323Srodrigc } 3135153323Srodrigc /* 3136153323Srodrigc * Set up del_endblock and cur for later. 3137153323Srodrigc */ 3138153323Srodrigc del_endblock = del->br_startblock + del->br_blockcount; 3139153323Srodrigc if (cur) { 3140153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, got.br_startoff, 3141153323Srodrigc got.br_startblock, got.br_blockcount, 3142153323Srodrigc &i))) 3143153323Srodrigc goto done; 3144153323Srodrigc ASSERT(i == 1); 3145153323Srodrigc } 3146153323Srodrigc da_old = da_new = 0; 3147153323Srodrigc } else { 3148153323Srodrigc da_old = STARTBLOCKVAL(got.br_startblock); 3149153323Srodrigc da_new = 0; 3150153323Srodrigc nblks = 0; 3151153323Srodrigc do_fx = 0; 3152153323Srodrigc } 3153153323Srodrigc /* 3154153323Srodrigc * Set flag value to use in switch statement. 3155153323Srodrigc * Left-contig is 2, right-contig is 1. 3156153323Srodrigc */ 3157153323Srodrigc switch (((got.br_startoff == del->br_startoff) << 1) | 3158153323Srodrigc (got_endoff == del_endoff)) { 3159153323Srodrigc case 3: 3160153323Srodrigc /* 3161153323Srodrigc * Matches the whole extent. Delete the entry. 3162153323Srodrigc */ 3163153323Srodrigc xfs_bmap_trace_delete(fname, "3", ip, idx, 1, whichfork); 3164159451Srodrigc xfs_iext_remove(ifp, idx, 1); 3165153323Srodrigc ifp->if_lastex = idx; 3166153323Srodrigc if (delay) 3167153323Srodrigc break; 3168153323Srodrigc XFS_IFORK_NEXT_SET(ip, whichfork, 3169153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) - 1); 3170153323Srodrigc flags |= XFS_ILOG_CORE; 3171153323Srodrigc if (!cur) { 3172153323Srodrigc flags |= XFS_ILOG_FEXT(whichfork); 3173153323Srodrigc break; 3174153323Srodrigc } 3175153323Srodrigc if ((error = xfs_bmbt_delete(cur, &i))) 3176153323Srodrigc goto done; 3177153323Srodrigc ASSERT(i == 1); 3178153323Srodrigc break; 3179153323Srodrigc 3180153323Srodrigc case 2: 3181153323Srodrigc /* 3182153323Srodrigc * Deleting the first part of the extent. 3183153323Srodrigc */ 3184153323Srodrigc xfs_bmap_trace_pre_update(fname, "2", ip, idx, whichfork); 3185153323Srodrigc xfs_bmbt_set_startoff(ep, del_endoff); 3186153323Srodrigc temp = got.br_blockcount - del->br_blockcount; 3187153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 3188153323Srodrigc ifp->if_lastex = idx; 3189153323Srodrigc if (delay) { 3190153323Srodrigc temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 3191153323Srodrigc da_old); 3192153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 3193153323Srodrigc xfs_bmap_trace_post_update(fname, "2", ip, idx, 3194153323Srodrigc whichfork); 3195153323Srodrigc da_new = temp; 3196153323Srodrigc break; 3197153323Srodrigc } 3198153323Srodrigc xfs_bmbt_set_startblock(ep, del_endblock); 3199153323Srodrigc xfs_bmap_trace_post_update(fname, "2", ip, idx, whichfork); 3200153323Srodrigc if (!cur) { 3201153323Srodrigc flags |= XFS_ILOG_FEXT(whichfork); 3202153323Srodrigc break; 3203153323Srodrigc } 3204153323Srodrigc if ((error = xfs_bmbt_update(cur, del_endoff, del_endblock, 3205153323Srodrigc got.br_blockcount - del->br_blockcount, 3206153323Srodrigc got.br_state))) 3207153323Srodrigc goto done; 3208153323Srodrigc break; 3209153323Srodrigc 3210153323Srodrigc case 1: 3211153323Srodrigc /* 3212153323Srodrigc * Deleting the last part of the extent. 3213153323Srodrigc */ 3214153323Srodrigc temp = got.br_blockcount - del->br_blockcount; 3215153323Srodrigc xfs_bmap_trace_pre_update(fname, "1", ip, idx, whichfork); 3216153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 3217153323Srodrigc ifp->if_lastex = idx; 3218153323Srodrigc if (delay) { 3219153323Srodrigc temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), 3220153323Srodrigc da_old); 3221153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 3222153323Srodrigc xfs_bmap_trace_post_update(fname, "1", ip, idx, 3223153323Srodrigc whichfork); 3224153323Srodrigc da_new = temp; 3225153323Srodrigc break; 3226153323Srodrigc } 3227153323Srodrigc xfs_bmap_trace_post_update(fname, "1", ip, idx, whichfork); 3228153323Srodrigc if (!cur) { 3229153323Srodrigc flags |= XFS_ILOG_FEXT(whichfork); 3230153323Srodrigc break; 3231153323Srodrigc } 3232153323Srodrigc if ((error = xfs_bmbt_update(cur, got.br_startoff, 3233153323Srodrigc got.br_startblock, 3234153323Srodrigc got.br_blockcount - del->br_blockcount, 3235153323Srodrigc got.br_state))) 3236153323Srodrigc goto done; 3237153323Srodrigc break; 3238153323Srodrigc 3239153323Srodrigc case 0: 3240153323Srodrigc /* 3241153323Srodrigc * Deleting the middle of the extent. 3242153323Srodrigc */ 3243153323Srodrigc temp = del->br_startoff - got.br_startoff; 3244153323Srodrigc xfs_bmap_trace_pre_update(fname, "0", ip, idx, whichfork); 3245153323Srodrigc xfs_bmbt_set_blockcount(ep, temp); 3246153323Srodrigc new.br_startoff = del_endoff; 3247153323Srodrigc temp2 = got_endoff - del_endoff; 3248153323Srodrigc new.br_blockcount = temp2; 3249153323Srodrigc new.br_state = got.br_state; 3250153323Srodrigc if (!delay) { 3251153323Srodrigc new.br_startblock = del_endblock; 3252153323Srodrigc flags |= XFS_ILOG_CORE; 3253153323Srodrigc if (cur) { 3254153323Srodrigc if ((error = xfs_bmbt_update(cur, 3255153323Srodrigc got.br_startoff, 3256153323Srodrigc got.br_startblock, temp, 3257153323Srodrigc got.br_state))) 3258153323Srodrigc goto done; 3259153323Srodrigc if ((error = xfs_bmbt_increment(cur, 0, &i))) 3260153323Srodrigc goto done; 3261153323Srodrigc cur->bc_rec.b = new; 3262153323Srodrigc error = xfs_bmbt_insert(cur, &i); 3263153323Srodrigc if (error && error != ENOSPC) 3264153323Srodrigc goto done; 3265153323Srodrigc /* 3266153323Srodrigc * If get no-space back from btree insert, 3267153323Srodrigc * it tried a split, and we have a zero 3268153323Srodrigc * block reservation. 3269153323Srodrigc * Fix up our state and return the error. 3270153323Srodrigc */ 3271153323Srodrigc if (error == ENOSPC) { 3272153323Srodrigc /* 3273153323Srodrigc * Reset the cursor, don't trust 3274153323Srodrigc * it after any insert operation. 3275153323Srodrigc */ 3276153323Srodrigc if ((error = xfs_bmbt_lookup_eq(cur, 3277153323Srodrigc got.br_startoff, 3278153323Srodrigc got.br_startblock, 3279153323Srodrigc temp, &i))) 3280153323Srodrigc goto done; 3281153323Srodrigc ASSERT(i == 1); 3282153323Srodrigc /* 3283153323Srodrigc * Update the btree record back 3284153323Srodrigc * to the original value. 3285153323Srodrigc */ 3286153323Srodrigc if ((error = xfs_bmbt_update(cur, 3287153323Srodrigc got.br_startoff, 3288153323Srodrigc got.br_startblock, 3289153323Srodrigc got.br_blockcount, 3290153323Srodrigc got.br_state))) 3291153323Srodrigc goto done; 3292153323Srodrigc /* 3293153323Srodrigc * Reset the extent record back 3294153323Srodrigc * to the original value. 3295153323Srodrigc */ 3296153323Srodrigc xfs_bmbt_set_blockcount(ep, 3297153323Srodrigc got.br_blockcount); 3298153323Srodrigc flags = 0; 3299153323Srodrigc error = XFS_ERROR(ENOSPC); 3300153323Srodrigc goto done; 3301153323Srodrigc } 3302153323Srodrigc ASSERT(i == 1); 3303153323Srodrigc } else 3304153323Srodrigc flags |= XFS_ILOG_FEXT(whichfork); 3305153323Srodrigc XFS_IFORK_NEXT_SET(ip, whichfork, 3306153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) + 1); 3307153323Srodrigc } else { 3308153323Srodrigc ASSERT(whichfork == XFS_DATA_FORK); 3309153323Srodrigc temp = xfs_bmap_worst_indlen(ip, temp); 3310153323Srodrigc xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); 3311153323Srodrigc temp2 = xfs_bmap_worst_indlen(ip, temp2); 3312153323Srodrigc new.br_startblock = NULLSTARTBLOCK((int)temp2); 3313153323Srodrigc da_new = temp + temp2; 3314153323Srodrigc while (da_new > da_old) { 3315153323Srodrigc if (temp) { 3316153323Srodrigc temp--; 3317153323Srodrigc da_new--; 3318153323Srodrigc xfs_bmbt_set_startblock(ep, 3319153323Srodrigc NULLSTARTBLOCK((int)temp)); 3320153323Srodrigc } 3321153323Srodrigc if (da_new == da_old) 3322153323Srodrigc break; 3323153323Srodrigc if (temp2) { 3324153323Srodrigc temp2--; 3325153323Srodrigc da_new--; 3326153323Srodrigc new.br_startblock = 3327153323Srodrigc NULLSTARTBLOCK((int)temp2); 3328153323Srodrigc } 3329153323Srodrigc } 3330153323Srodrigc } 3331153323Srodrigc xfs_bmap_trace_post_update(fname, "0", ip, idx, whichfork); 3332153323Srodrigc xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 1, &new, NULL, 3333153323Srodrigc whichfork); 3334159451Srodrigc xfs_iext_insert(ifp, idx + 1, 1, &new); 3335153323Srodrigc ifp->if_lastex = idx + 1; 3336153323Srodrigc break; 3337153323Srodrigc } 3338153323Srodrigc /* 3339153323Srodrigc * If we need to, add to list of extents to delete. 3340153323Srodrigc */ 3341153323Srodrigc if (do_fx) 3342153323Srodrigc xfs_bmap_add_free(del->br_startblock, del->br_blockcount, flist, 3343153323Srodrigc mp); 3344153323Srodrigc /* 3345153323Srodrigc * Adjust inode # blocks in the file. 3346153323Srodrigc */ 3347153323Srodrigc if (nblks) 3348153323Srodrigc ip->i_d.di_nblocks -= nblks; 3349153323Srodrigc /* 3350153323Srodrigc * Adjust quota data. 3351153323Srodrigc */ 3352153323Srodrigc if (qfield) 3353153323Srodrigc XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, qfield, (long)-nblks); 3354153323Srodrigc 3355153323Srodrigc /* 3356153323Srodrigc * Account for change in delayed indirect blocks. 3357153323Srodrigc * Nothing to do for disk quota accounting here. 3358153323Srodrigc */ 3359153323Srodrigc ASSERT(da_old >= da_new); 3360153323Srodrigc if (da_old > da_new) 3361153323Srodrigc xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, (int)(da_old - da_new), 3362153323Srodrigc rsvd); 3363159451Srodrigc if (delta) { 3364159451Srodrigc /* DELTA: report the original extent. */ 3365159451Srodrigc if (delta->xed_startoff > got.br_startoff) 3366159451Srodrigc delta->xed_startoff = got.br_startoff; 3367159451Srodrigc if (delta->xed_blockcount < got.br_startoff+got.br_blockcount) 3368159451Srodrigc delta->xed_blockcount = got.br_startoff + 3369159451Srodrigc got.br_blockcount; 3370159451Srodrigc } 3371153323Srodrigcdone: 3372153323Srodrigc *logflagsp = flags; 3373153323Srodrigc return error; 3374153323Srodrigc} 3375153323Srodrigc 3376153323Srodrigc/* 3377153323Srodrigc * Remove the entry "free" from the free item list. Prev points to the 3378153323Srodrigc * previous entry, unless "free" is the head of the list. 3379153323Srodrigc */ 3380153323SrodrigcSTATIC void 3381153323Srodrigcxfs_bmap_del_free( 3382153323Srodrigc xfs_bmap_free_t *flist, /* free item list header */ 3383153323Srodrigc xfs_bmap_free_item_t *prev, /* previous item on list, if any */ 3384153323Srodrigc xfs_bmap_free_item_t *free) /* list item to be freed */ 3385153323Srodrigc{ 3386153323Srodrigc if (prev) 3387153323Srodrigc prev->xbfi_next = free->xbfi_next; 3388153323Srodrigc else 3389153323Srodrigc flist->xbf_first = free->xbfi_next; 3390153323Srodrigc flist->xbf_count--; 3391153323Srodrigc kmem_zone_free(xfs_bmap_free_item_zone, free); 3392153323Srodrigc} 3393153323Srodrigc 3394153323Srodrigc/* 3395153323Srodrigc * Convert an extents-format file into a btree-format file. 3396153323Srodrigc * The new file will have a root block (in the inode) and a single child block. 3397153323Srodrigc */ 3398153323SrodrigcSTATIC int /* error */ 3399153323Srodrigcxfs_bmap_extents_to_btree( 3400153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 3401153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3402153323Srodrigc xfs_fsblock_t *firstblock, /* first-block-allocated */ 3403153323Srodrigc xfs_bmap_free_t *flist, /* blocks freed in xaction */ 3404153323Srodrigc xfs_btree_cur_t **curp, /* cursor returned to caller */ 3405153323Srodrigc int wasdel, /* converting a delayed alloc */ 3406153323Srodrigc int *logflagsp, /* inode logging flags */ 3407153323Srodrigc int whichfork) /* data or attr fork */ 3408153323Srodrigc{ 3409153323Srodrigc xfs_bmbt_block_t *ablock; /* allocated (child) bt block */ 3410153323Srodrigc xfs_buf_t *abp; /* buffer for ablock */ 3411153323Srodrigc xfs_alloc_arg_t args; /* allocation arguments */ 3412153323Srodrigc xfs_bmbt_rec_t *arp; /* child record pointer */ 3413153323Srodrigc xfs_bmbt_block_t *block; /* btree root block */ 3414153323Srodrigc xfs_btree_cur_t *cur; /* bmap btree cursor */ 3415159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record pointer */ 3416153323Srodrigc int error; /* error return value */ 3417159451Srodrigc xfs_extnum_t i, cnt; /* extent record index */ 3418153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3419153323Srodrigc xfs_bmbt_key_t *kp; /* root block key pointer */ 3420153323Srodrigc xfs_mount_t *mp; /* mount structure */ 3421159451Srodrigc xfs_extnum_t nextents; /* number of file extents */ 3422153323Srodrigc xfs_bmbt_ptr_t *pp; /* root block address pointer */ 3423153323Srodrigc 3424153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3425153323Srodrigc ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS); 3426153323Srodrigc ASSERT(ifp->if_ext_max == 3427153323Srodrigc XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); 3428153323Srodrigc /* 3429153323Srodrigc * Make space in the inode incore. 3430153323Srodrigc */ 3431153323Srodrigc xfs_iroot_realloc(ip, 1, whichfork); 3432153323Srodrigc ifp->if_flags |= XFS_IFBROOT; 3433153323Srodrigc /* 3434153323Srodrigc * Fill in the root. 3435153323Srodrigc */ 3436153323Srodrigc block = ifp->if_broot; 3437159451Srodrigc block->bb_magic = cpu_to_be32(XFS_BMAP_MAGIC); 3438159451Srodrigc block->bb_level = cpu_to_be16(1); 3439159451Srodrigc block->bb_numrecs = cpu_to_be16(1); 3440159451Srodrigc block->bb_leftsib = cpu_to_be64(NULLDFSBNO); 3441159451Srodrigc block->bb_rightsib = cpu_to_be64(NULLDFSBNO); 3442153323Srodrigc /* 3443153323Srodrigc * Need a cursor. Can't allocate until bb_level is filled in. 3444153323Srodrigc */ 3445153323Srodrigc mp = ip->i_mount; 3446153323Srodrigc cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip, 3447153323Srodrigc whichfork); 3448153323Srodrigc cur->bc_private.b.firstblock = *firstblock; 3449153323Srodrigc cur->bc_private.b.flist = flist; 3450153323Srodrigc cur->bc_private.b.flags = wasdel ? XFS_BTCUR_BPRV_WASDEL : 0; 3451153323Srodrigc /* 3452153323Srodrigc * Convert to a btree with two levels, one record in root. 3453153323Srodrigc */ 3454153323Srodrigc XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_BTREE); 3455153323Srodrigc args.tp = tp; 3456153323Srodrigc args.mp = mp; 3457153323Srodrigc if (*firstblock == NULLFSBLOCK) { 3458153323Srodrigc args.type = XFS_ALLOCTYPE_START_BNO; 3459153323Srodrigc args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino); 3460153323Srodrigc } else if (flist->xbf_low) { 3461153323Srodrigc args.type = XFS_ALLOCTYPE_START_BNO; 3462153323Srodrigc args.fsbno = *firstblock; 3463153323Srodrigc } else { 3464153323Srodrigc args.type = XFS_ALLOCTYPE_NEAR_BNO; 3465153323Srodrigc args.fsbno = *firstblock; 3466153323Srodrigc } 3467153323Srodrigc args.minlen = args.maxlen = args.prod = 1; 3468153323Srodrigc args.total = args.minleft = args.alignment = args.mod = args.isfl = 3469153323Srodrigc args.minalignslop = 0; 3470153323Srodrigc args.wasdel = wasdel; 3471153323Srodrigc *logflagsp = 0; 3472153323Srodrigc if ((error = xfs_alloc_vextent(&args))) { 3473153323Srodrigc xfs_iroot_realloc(ip, -1, whichfork); 3474153323Srodrigc xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); 3475153323Srodrigc return error; 3476153323Srodrigc } 3477153323Srodrigc /* 3478153323Srodrigc * Allocation can't fail, the space was reserved. 3479153323Srodrigc */ 3480153323Srodrigc ASSERT(args.fsbno != NULLFSBLOCK); 3481153323Srodrigc ASSERT(*firstblock == NULLFSBLOCK || 3482153323Srodrigc args.agno == XFS_FSB_TO_AGNO(mp, *firstblock) || 3483153323Srodrigc (flist->xbf_low && 3484153323Srodrigc args.agno > XFS_FSB_TO_AGNO(mp, *firstblock))); 3485153323Srodrigc *firstblock = cur->bc_private.b.firstblock = args.fsbno; 3486153323Srodrigc cur->bc_private.b.allocated++; 3487153323Srodrigc ip->i_d.di_nblocks++; 3488153323Srodrigc XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, XFS_TRANS_DQ_BCOUNT, 1L); 3489153323Srodrigc abp = xfs_btree_get_bufl(mp, tp, args.fsbno, 0); 3490153323Srodrigc /* 3491153323Srodrigc * Fill in the child block. 3492153323Srodrigc */ 3493153323Srodrigc ablock = XFS_BUF_TO_BMBT_BLOCK(abp); 3494159451Srodrigc ablock->bb_magic = cpu_to_be32(XFS_BMAP_MAGIC); 3495159451Srodrigc ablock->bb_level = 0; 3496159451Srodrigc ablock->bb_leftsib = cpu_to_be64(NULLDFSBNO); 3497159451Srodrigc ablock->bb_rightsib = cpu_to_be64(NULLDFSBNO); 3498153323Srodrigc arp = XFS_BMAP_REC_IADDR(ablock, 1, cur); 3499153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 3500159451Srodrigc for (cnt = i = 0; i < nextents; i++) { 3501159451Srodrigc ep = xfs_iext_get_ext(ifp, i); 3502153323Srodrigc if (!ISNULLSTARTBLOCK(xfs_bmbt_get_startblock(ep))) { 3503153323Srodrigc arp->l0 = INT_GET(ep->l0, ARCH_CONVERT); 3504153323Srodrigc arp->l1 = INT_GET(ep->l1, ARCH_CONVERT); 3505153323Srodrigc arp++; cnt++; 3506153323Srodrigc } 3507153323Srodrigc } 3508159451Srodrigc ASSERT(cnt == XFS_IFORK_NEXTENTS(ip, whichfork)); 3509159451Srodrigc ablock->bb_numrecs = cpu_to_be16(cnt); 3510153323Srodrigc /* 3511153323Srodrigc * Fill in the root key and pointer. 3512153323Srodrigc */ 3513153323Srodrigc kp = XFS_BMAP_KEY_IADDR(block, 1, cur); 3514153323Srodrigc arp = XFS_BMAP_REC_IADDR(ablock, 1, cur); 3515153323Srodrigc INT_SET(kp->br_startoff, ARCH_CONVERT, xfs_bmbt_disk_get_startoff(arp)); 3516153323Srodrigc pp = XFS_BMAP_PTR_IADDR(block, 1, cur); 3517153323Srodrigc INT_SET(*pp, ARCH_CONVERT, args.fsbno); 3518153323Srodrigc /* 3519153323Srodrigc * Do all this logging at the end so that 3520153323Srodrigc * the root is at the right level. 3521153323Srodrigc */ 3522153323Srodrigc xfs_bmbt_log_block(cur, abp, XFS_BB_ALL_BITS); 3523159451Srodrigc xfs_bmbt_log_recs(cur, abp, 1, be16_to_cpu(ablock->bb_numrecs)); 3524153323Srodrigc ASSERT(*curp == NULL); 3525153323Srodrigc *curp = cur; 3526153323Srodrigc *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FBROOT(whichfork); 3527153323Srodrigc return 0; 3528153323Srodrigc} 3529153323Srodrigc 3530153323Srodrigc/* 3531159451Srodrigc * Helper routine to reset inode di_forkoff field when switching 3532159451Srodrigc * attribute fork from local to extent format - we reset it where 3533159451Srodrigc * possible to make space available for inline data fork extents. 3534153323Srodrigc */ 3535153323SrodrigcSTATIC void 3536159451Srodrigcxfs_bmap_forkoff_reset( 3537159451Srodrigc xfs_mount_t *mp, 3538159451Srodrigc xfs_inode_t *ip, 3539159451Srodrigc int whichfork) 3540153323Srodrigc{ 3541159451Srodrigc if (whichfork == XFS_ATTR_FORK && 3542159451Srodrigc (ip->i_d.di_format != XFS_DINODE_FMT_DEV) && 3543159451Srodrigc (ip->i_d.di_format != XFS_DINODE_FMT_UUID) && 3544159451Srodrigc ((mp->m_attroffset >> 3) > ip->i_d.di_forkoff)) { 3545159451Srodrigc ip->i_d.di_forkoff = mp->m_attroffset >> 3; 3546159451Srodrigc ip->i_df.if_ext_max = XFS_IFORK_DSIZE(ip) / 3547159451Srodrigc (uint)sizeof(xfs_bmbt_rec_t); 3548159451Srodrigc ip->i_afp->if_ext_max = XFS_IFORK_ASIZE(ip) / 3549159451Srodrigc (uint)sizeof(xfs_bmbt_rec_t); 3550159451Srodrigc } 3551153323Srodrigc} 3552153323Srodrigc 3553153323Srodrigc/* 3554153323Srodrigc * Convert a local file to an extents file. 3555153323Srodrigc * This code is out of bounds for data forks of regular files, 3556153323Srodrigc * since the file data needs to get logged so things will stay consistent. 3557153323Srodrigc * (The bmap-level manipulations are ok, though). 3558153323Srodrigc */ 3559153323SrodrigcSTATIC int /* error */ 3560153323Srodrigcxfs_bmap_local_to_extents( 3561153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 3562153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3563153323Srodrigc xfs_fsblock_t *firstblock, /* first block allocated in xaction */ 3564153323Srodrigc xfs_extlen_t total, /* total blocks needed by transaction */ 3565153323Srodrigc int *logflagsp, /* inode logging flags */ 3566153323Srodrigc int whichfork) /* data or attr fork */ 3567153323Srodrigc{ 3568153323Srodrigc int error; /* error return value */ 3569153323Srodrigc int flags; /* logging flags returned */ 3570153323Srodrigc#ifdef XFS_BMAP_TRACE 3571153323Srodrigc static char fname[] = "xfs_bmap_local_to_extents"; 3572153323Srodrigc#endif 3573153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3574153323Srodrigc 3575153323Srodrigc /* 3576153323Srodrigc * We don't want to deal with the case of keeping inode data inline yet. 3577153323Srodrigc * So sending the data fork of a regular inode is invalid. 3578153323Srodrigc */ 3579153323Srodrigc ASSERT(!((ip->i_d.di_mode & S_IFMT) == S_IFREG && 3580153323Srodrigc whichfork == XFS_DATA_FORK)); 3581153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3582153323Srodrigc ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL); 3583153323Srodrigc flags = 0; 3584153323Srodrigc error = 0; 3585153323Srodrigc if (ifp->if_bytes) { 3586153323Srodrigc xfs_alloc_arg_t args; /* allocation arguments */ 3587159451Srodrigc xfs_buf_t *bp; /* buffer for extent block */ 3588159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record pointer */ 3589153323Srodrigc 3590153323Srodrigc args.tp = tp; 3591153323Srodrigc args.mp = ip->i_mount; 3592159451Srodrigc ASSERT((ifp->if_flags & 3593159451Srodrigc (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE); 3594153323Srodrigc /* 3595153323Srodrigc * Allocate a block. We know we need only one, since the 3596153323Srodrigc * file currently fits in an inode. 3597153323Srodrigc */ 3598153323Srodrigc if (*firstblock == NULLFSBLOCK) { 3599153323Srodrigc args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino); 3600153323Srodrigc args.type = XFS_ALLOCTYPE_START_BNO; 3601153323Srodrigc } else { 3602153323Srodrigc args.fsbno = *firstblock; 3603153323Srodrigc args.type = XFS_ALLOCTYPE_NEAR_BNO; 3604153323Srodrigc } 3605153323Srodrigc args.total = total; 3606153323Srodrigc args.mod = args.minleft = args.alignment = args.wasdel = 3607153323Srodrigc args.isfl = args.minalignslop = 0; 3608153323Srodrigc args.minlen = args.maxlen = args.prod = 1; 3609153323Srodrigc if ((error = xfs_alloc_vextent(&args))) 3610153323Srodrigc goto done; 3611153323Srodrigc /* 3612153323Srodrigc * Can't fail, the space was reserved. 3613153323Srodrigc */ 3614153323Srodrigc ASSERT(args.fsbno != NULLFSBLOCK); 3615153323Srodrigc ASSERT(args.len == 1); 3616153323Srodrigc *firstblock = args.fsbno; 3617153323Srodrigc bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0); 3618153323Srodrigc memcpy((char *)XFS_BUF_PTR(bp), ifp->if_u1.if_data, 3619153323Srodrigc ifp->if_bytes); 3620153323Srodrigc xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1); 3621159451Srodrigc xfs_bmap_forkoff_reset(args.mp, ip, whichfork); 3622153323Srodrigc xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); 3623159451Srodrigc xfs_iext_add(ifp, 0, 1); 3624159451Srodrigc ep = xfs_iext_get_ext(ifp, 0); 3625153323Srodrigc xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM); 3626153323Srodrigc xfs_bmap_trace_post_update(fname, "new", ip, 0, whichfork); 3627153323Srodrigc XFS_IFORK_NEXT_SET(ip, whichfork, 1); 3628153323Srodrigc ip->i_d.di_nblocks = 1; 3629153323Srodrigc XFS_TRANS_MOD_DQUOT_BYINO(args.mp, tp, ip, 3630153323Srodrigc XFS_TRANS_DQ_BCOUNT, 1L); 3631153323Srodrigc flags |= XFS_ILOG_FEXT(whichfork); 3632159451Srodrigc } else { 3633153323Srodrigc ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0); 3634159451Srodrigc xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork); 3635159451Srodrigc } 3636153323Srodrigc ifp->if_flags &= ~XFS_IFINLINE; 3637153323Srodrigc ifp->if_flags |= XFS_IFEXTENTS; 3638153323Srodrigc XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS); 3639153323Srodrigc flags |= XFS_ILOG_CORE; 3640153323Srodrigcdone: 3641153323Srodrigc *logflagsp = flags; 3642153323Srodrigc return error; 3643153323Srodrigc} 3644153323Srodrigc 3645159451Srodrigc/* 3646159451Srodrigc * Search the extent records for the entry containing block bno. 3647159451Srodrigc * If bno lies in a hole, point to the next entry. If bno lies 3648159451Srodrigc * past eof, *eofp will be set, and *prevp will contain the last 3649159451Srodrigc * entry (null if none). Else, *lastxp will be set to the index 3650159451Srodrigc * of the found entry; *gotp will contain the entry. 3651159451Srodrigc */ 3652153323Srodrigcxfs_bmbt_rec_t * /* pointer to found extent entry */ 3653159451Srodrigcxfs_bmap_search_multi_extents( 3654159451Srodrigc xfs_ifork_t *ifp, /* inode fork pointer */ 3655153323Srodrigc xfs_fileoff_t bno, /* block number searched for */ 3656153323Srodrigc int *eofp, /* out: end of file found */ 3657153323Srodrigc xfs_extnum_t *lastxp, /* out: last extent index */ 3658153323Srodrigc xfs_bmbt_irec_t *gotp, /* out: extent entry found */ 3659153323Srodrigc xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */ 3660153323Srodrigc{ 3661159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record pointer */ 3662159451Srodrigc xfs_extnum_t lastx; /* last extent index */ 3663153323Srodrigc 3664159451Srodrigc /* 3665159451Srodrigc * Initialize the extent entry structure to catch access to 3666159451Srodrigc * uninitialized br_startblock field. 3667159451Srodrigc */ 3668159451Srodrigc gotp->br_startoff = 0xffa5a5a5a5a5a5a5LL; 3669159451Srodrigc gotp->br_blockcount = 0xa55a5a5a5a5a5a5aLL; 3670159451Srodrigc gotp->br_state = XFS_EXT_INVALID; 3671159451Srodrigc#if XFS_BIG_BLKNOS 3672159451Srodrigc gotp->br_startblock = 0xffffa5a5a5a5a5a5LL; 3673159451Srodrigc#else 3674159451Srodrigc gotp->br_startblock = 0xffffa5a5; 3675159451Srodrigc#endif 3676153323Srodrigc prevp->br_startoff = NULLFILEOFF; 3677159451Srodrigc 3678159451Srodrigc ep = xfs_iext_bno_to_ext(ifp, bno, &lastx); 3679159451Srodrigc if (lastx > 0) { 3680159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, lastx - 1), prevp); 3681159451Srodrigc } 3682159451Srodrigc if (lastx < (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) { 3683159451Srodrigc xfs_bmbt_get_all(ep, gotp); 3684153323Srodrigc *eofp = 0; 3685153323Srodrigc } else { 3686159451Srodrigc if (lastx > 0) { 3687159451Srodrigc *gotp = *prevp; 3688153323Srodrigc } 3689159451Srodrigc *eofp = 1; 3690159451Srodrigc ep = NULL; 3691153323Srodrigc } 3692153323Srodrigc *lastxp = lastx; 3693153323Srodrigc return ep; 3694153323Srodrigc} 3695153323Srodrigc 3696153323Srodrigc/* 3697153323Srodrigc * Search the extents list for the inode, for the extent containing bno. 3698153323Srodrigc * If bno lies in a hole, point to the next entry. If bno lies past eof, 3699153323Srodrigc * *eofp will be set, and *prevp will contain the last entry (null if none). 3700153323Srodrigc * Else, *lastxp will be set to the index of the found 3701153323Srodrigc * entry; *gotp will contain the entry. 3702153323Srodrigc */ 3703153323SrodrigcSTATIC xfs_bmbt_rec_t * /* pointer to found extent entry */ 3704153323Srodrigcxfs_bmap_search_extents( 3705153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3706153323Srodrigc xfs_fileoff_t bno, /* block number searched for */ 3707153323Srodrigc int whichfork, /* data or attr fork */ 3708153323Srodrigc int *eofp, /* out: end of file found */ 3709153323Srodrigc xfs_extnum_t *lastxp, /* out: last extent index */ 3710153323Srodrigc xfs_bmbt_irec_t *gotp, /* out: extent entry found */ 3711153323Srodrigc xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */ 3712153323Srodrigc{ 3713153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3714159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record pointer */ 3715159451Srodrigc int rt; /* realtime flag */ 3716153323Srodrigc 3717153323Srodrigc XFS_STATS_INC(xs_look_exlist); 3718153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3719153323Srodrigc 3720159451Srodrigc ep = xfs_bmap_search_multi_extents(ifp, bno, eofp, lastxp, gotp, prevp); 3721159451Srodrigc 3722159451Srodrigc rt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); 3723159451Srodrigc if (unlikely(!rt && !gotp->br_startblock && (*lastxp != NULLEXTNUM))) { 3724159451Srodrigc cmn_err(CE_PANIC,"Access to block zero: fs: <%s> inode: %lld " 3725159451Srodrigc "start_block : %llx start_off : %llx blkcnt : %llx " 3726159451Srodrigc "extent-state : %x \n", 3727159451Srodrigc (ip->i_mount)->m_fsname, (long long)ip->i_ino, 3728159451Srodrigc (unsigned long long)gotp->br_startblock, 3729159451Srodrigc (unsigned long long)gotp->br_startoff, 3730159451Srodrigc (unsigned long long)gotp->br_blockcount, 3731159451Srodrigc gotp->br_state); 3732159451Srodrigc } 3733159451Srodrigc return ep; 3734153323Srodrigc} 3735153323Srodrigc 3736153323Srodrigc 3737153323Srodrigc#ifdef XFS_BMAP_TRACE 3738153323Srodrigcktrace_t *xfs_bmap_trace_buf; 3739153323Srodrigc 3740153323Srodrigc/* 3741153323Srodrigc * Add a bmap trace buffer entry. Base routine for the others. 3742153323Srodrigc */ 3743153323SrodrigcSTATIC void 3744153323Srodrigcxfs_bmap_trace_addentry( 3745153323Srodrigc int opcode, /* operation */ 3746153323Srodrigc char *fname, /* function name */ 3747153323Srodrigc char *desc, /* operation description */ 3748153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3749153323Srodrigc xfs_extnum_t idx, /* index of entry(ies) */ 3750153323Srodrigc xfs_extnum_t cnt, /* count of entries, 1 or 2 */ 3751153323Srodrigc xfs_bmbt_rec_t *r1, /* first record */ 3752153323Srodrigc xfs_bmbt_rec_t *r2, /* second record or null */ 3753153323Srodrigc int whichfork) /* data or attr fork */ 3754153323Srodrigc{ 3755153323Srodrigc xfs_bmbt_rec_t tr2; 3756153323Srodrigc 3757153323Srodrigc ASSERT(cnt == 1 || cnt == 2); 3758153323Srodrigc ASSERT(r1 != NULL); 3759153323Srodrigc if (cnt == 1) { 3760153323Srodrigc ASSERT(r2 == NULL); 3761153323Srodrigc r2 = &tr2; 3762153323Srodrigc memset(&tr2, 0, sizeof(tr2)); 3763153323Srodrigc } else 3764153323Srodrigc ASSERT(r2 != NULL); 3765153323Srodrigc ktrace_enter(xfs_bmap_trace_buf, 3766153323Srodrigc (void *)(__psint_t)(opcode | (whichfork << 16)), 3767153323Srodrigc (void *)fname, (void *)desc, (void *)ip, 3768153323Srodrigc (void *)(__psint_t)idx, 3769153323Srodrigc (void *)(__psint_t)cnt, 3770153323Srodrigc (void *)(__psunsigned_t)(ip->i_ino >> 32), 3771153323Srodrigc (void *)(__psunsigned_t)(unsigned)ip->i_ino, 3772153323Srodrigc (void *)(__psunsigned_t)(r1->l0 >> 32), 3773153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r1->l0), 3774153323Srodrigc (void *)(__psunsigned_t)(r1->l1 >> 32), 3775153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r1->l1), 3776153323Srodrigc (void *)(__psunsigned_t)(r2->l0 >> 32), 3777153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r2->l0), 3778153323Srodrigc (void *)(__psunsigned_t)(r2->l1 >> 32), 3779153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r2->l1) 3780153323Srodrigc ); 3781153323Srodrigc ASSERT(ip->i_xtrace); 3782153323Srodrigc ktrace_enter(ip->i_xtrace, 3783153323Srodrigc (void *)(__psint_t)(opcode | (whichfork << 16)), 3784153323Srodrigc (void *)fname, (void *)desc, (void *)ip, 3785153323Srodrigc (void *)(__psint_t)idx, 3786153323Srodrigc (void *)(__psint_t)cnt, 3787153323Srodrigc (void *)(__psunsigned_t)(ip->i_ino >> 32), 3788153323Srodrigc (void *)(__psunsigned_t)(unsigned)ip->i_ino, 3789153323Srodrigc (void *)(__psunsigned_t)(r1->l0 >> 32), 3790153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r1->l0), 3791153323Srodrigc (void *)(__psunsigned_t)(r1->l1 >> 32), 3792153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r1->l1), 3793153323Srodrigc (void *)(__psunsigned_t)(r2->l0 >> 32), 3794153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r2->l0), 3795153323Srodrigc (void *)(__psunsigned_t)(r2->l1 >> 32), 3796153323Srodrigc (void *)(__psunsigned_t)(unsigned)(r2->l1) 3797153323Srodrigc ); 3798153323Srodrigc} 3799153323Srodrigc 3800153323Srodrigc/* 3801159451Srodrigc * Add bmap trace entry prior to a call to xfs_iext_remove. 3802153323Srodrigc */ 3803153323SrodrigcSTATIC void 3804153323Srodrigcxfs_bmap_trace_delete( 3805153323Srodrigc char *fname, /* function name */ 3806153323Srodrigc char *desc, /* operation description */ 3807153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3808153323Srodrigc xfs_extnum_t idx, /* index of entry(entries) deleted */ 3809153323Srodrigc xfs_extnum_t cnt, /* count of entries deleted, 1 or 2 */ 3810153323Srodrigc int whichfork) /* data or attr fork */ 3811153323Srodrigc{ 3812153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3813153323Srodrigc 3814153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3815153323Srodrigc xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_DELETE, fname, desc, ip, idx, 3816159451Srodrigc cnt, xfs_iext_get_ext(ifp, idx), 3817159451Srodrigc cnt == 2 ? xfs_iext_get_ext(ifp, idx + 1) : NULL, 3818153323Srodrigc whichfork); 3819153323Srodrigc} 3820153323Srodrigc 3821153323Srodrigc/* 3822159451Srodrigc * Add bmap trace entry prior to a call to xfs_iext_insert, or 3823153323Srodrigc * reading in the extents list from the disk (in the btree). 3824153323Srodrigc */ 3825153323SrodrigcSTATIC void 3826153323Srodrigcxfs_bmap_trace_insert( 3827153323Srodrigc char *fname, /* function name */ 3828153323Srodrigc char *desc, /* operation description */ 3829153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3830153323Srodrigc xfs_extnum_t idx, /* index of entry(entries) inserted */ 3831153323Srodrigc xfs_extnum_t cnt, /* count of entries inserted, 1 or 2 */ 3832153323Srodrigc xfs_bmbt_irec_t *r1, /* inserted record 1 */ 3833153323Srodrigc xfs_bmbt_irec_t *r2, /* inserted record 2 or null */ 3834153323Srodrigc int whichfork) /* data or attr fork */ 3835153323Srodrigc{ 3836153323Srodrigc xfs_bmbt_rec_t tr1; /* compressed record 1 */ 3837153323Srodrigc xfs_bmbt_rec_t tr2; /* compressed record 2 if needed */ 3838153323Srodrigc 3839153323Srodrigc xfs_bmbt_set_all(&tr1, r1); 3840153323Srodrigc if (cnt == 2) { 3841153323Srodrigc ASSERT(r2 != NULL); 3842153323Srodrigc xfs_bmbt_set_all(&tr2, r2); 3843153323Srodrigc } else { 3844153323Srodrigc ASSERT(cnt == 1); 3845153323Srodrigc ASSERT(r2 == NULL); 3846153323Srodrigc } 3847153323Srodrigc xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_INSERT, fname, desc, ip, idx, 3848153323Srodrigc cnt, &tr1, cnt == 2 ? &tr2 : NULL, whichfork); 3849153323Srodrigc} 3850153323Srodrigc 3851153323Srodrigc/* 3852159451Srodrigc * Add bmap trace entry after updating an extent record in place. 3853153323Srodrigc */ 3854153323SrodrigcSTATIC void 3855153323Srodrigcxfs_bmap_trace_post_update( 3856153323Srodrigc char *fname, /* function name */ 3857153323Srodrigc char *desc, /* operation description */ 3858153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3859153323Srodrigc xfs_extnum_t idx, /* index of entry updated */ 3860153323Srodrigc int whichfork) /* data or attr fork */ 3861153323Srodrigc{ 3862153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3863153323Srodrigc 3864153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3865153323Srodrigc xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_POST_UP, fname, desc, ip, idx, 3866159451Srodrigc 1, xfs_iext_get_ext(ifp, idx), NULL, whichfork); 3867153323Srodrigc} 3868153323Srodrigc 3869153323Srodrigc/* 3870159451Srodrigc * Add bmap trace entry prior to updating an extent record in place. 3871153323Srodrigc */ 3872153323SrodrigcSTATIC void 3873153323Srodrigcxfs_bmap_trace_pre_update( 3874153323Srodrigc char *fname, /* function name */ 3875153323Srodrigc char *desc, /* operation description */ 3876153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3877153323Srodrigc xfs_extnum_t idx, /* index of entry to be updated */ 3878153323Srodrigc int whichfork) /* data or attr fork */ 3879153323Srodrigc{ 3880153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 3881153323Srodrigc 3882153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 3883153323Srodrigc xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_PRE_UP, fname, desc, ip, idx, 1, 3884159451Srodrigc xfs_iext_get_ext(ifp, idx), NULL, whichfork); 3885153323Srodrigc} 3886153323Srodrigc#endif /* XFS_BMAP_TRACE */ 3887153323Srodrigc 3888153323Srodrigc/* 3889153323Srodrigc * Compute the worst-case number of indirect blocks that will be used 3890153323Srodrigc * for ip's delayed extent of length "len". 3891153323Srodrigc */ 3892153323SrodrigcSTATIC xfs_filblks_t 3893153323Srodrigcxfs_bmap_worst_indlen( 3894153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3895153323Srodrigc xfs_filblks_t len) /* delayed extent length */ 3896153323Srodrigc{ 3897153323Srodrigc int level; /* btree level number */ 3898153323Srodrigc int maxrecs; /* maximum record count at this level */ 3899153323Srodrigc xfs_mount_t *mp; /* mount structure */ 3900153323Srodrigc xfs_filblks_t rval; /* return value */ 3901153323Srodrigc 3902153323Srodrigc mp = ip->i_mount; 3903153323Srodrigc maxrecs = mp->m_bmap_dmxr[0]; 3904153323Srodrigc for (level = 0, rval = 0; 3905153323Srodrigc level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK); 3906153323Srodrigc level++) { 3907153323Srodrigc len += maxrecs - 1; 3908153323Srodrigc do_div(len, maxrecs); 3909153323Srodrigc rval += len; 3910153323Srodrigc if (len == 1) 3911153323Srodrigc return rval + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 3912153323Srodrigc level - 1; 3913153323Srodrigc if (level == 0) 3914153323Srodrigc maxrecs = mp->m_bmap_dmxr[1]; 3915153323Srodrigc } 3916153323Srodrigc return rval; 3917153323Srodrigc} 3918153323Srodrigc 3919153323Srodrigc#if defined(XFS_RW_TRACE) 3920153323SrodrigcSTATIC void 3921153323Srodrigcxfs_bunmap_trace( 3922153323Srodrigc xfs_inode_t *ip, 3923153323Srodrigc xfs_fileoff_t bno, 3924153323Srodrigc xfs_filblks_t len, 3925153323Srodrigc int flags, 3926153323Srodrigc inst_t *ra) 3927153323Srodrigc{ 3928153323Srodrigc if (ip->i_rwtrace == NULL) 3929153323Srodrigc return; 3930153323Srodrigc ktrace_enter(ip->i_rwtrace, 3931159451Srodrigc (void *)(__psint_t)XFS_BUNMAP, 3932153323Srodrigc (void *)ip, 3933153323Srodrigc (void *)(__psint_t)((ip->i_d.di_size >> 32) & 0xffffffff), 3934153323Srodrigc (void *)(__psint_t)(ip->i_d.di_size & 0xffffffff), 3935153323Srodrigc (void *)(__psint_t)(((xfs_dfiloff_t)bno >> 32) & 0xffffffff), 3936153323Srodrigc (void *)(__psint_t)((xfs_dfiloff_t)bno & 0xffffffff), 3937153323Srodrigc (void *)(__psint_t)len, 3938153323Srodrigc (void *)(__psint_t)flags, 3939153323Srodrigc (void *)current_cpu(), 3940153323Srodrigc (void *)ra, 3941153323Srodrigc (void *)0, 3942153323Srodrigc (void *)0, 3943153323Srodrigc (void *)0, 3944153323Srodrigc (void *)0, 3945153323Srodrigc (void *)0, 3946153323Srodrigc (void *)0); 3947153323Srodrigc} 3948153323Srodrigc#endif 3949153323Srodrigc 3950153323Srodrigc/* 3951153323Srodrigc * Convert inode from non-attributed to attributed. 3952153323Srodrigc * Must not be in a transaction, ip must not be locked. 3953153323Srodrigc */ 3954153323Srodrigcint /* error code */ 3955153323Srodrigcxfs_bmap_add_attrfork( 3956153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 3957159451Srodrigc int size, /* space new attribute needs */ 3958159451Srodrigc int rsvd) /* xact may use reserved blks */ 3959153323Srodrigc{ 3960159451Srodrigc xfs_fsblock_t firstblock; /* 1st block/ag allocated */ 3961159451Srodrigc xfs_bmap_free_t flist; /* freed extent records */ 3962159451Srodrigc xfs_mount_t *mp; /* mount structure */ 3963159451Srodrigc xfs_trans_t *tp; /* transaction pointer */ 3964159451Srodrigc unsigned long s; /* spinlock spl value */ 3965153323Srodrigc int blks; /* space reservation */ 3966159451Srodrigc int version = 1; /* superblock attr version */ 3967153323Srodrigc int committed; /* xaction was committed */ 3968159451Srodrigc int logflags; /* logging flags */ 3969153323Srodrigc int error; /* error return value */ 3970153323Srodrigc 3971159451Srodrigc ASSERT(XFS_IFORK_Q(ip) == 0); 3972153323Srodrigc ASSERT(ip->i_df.if_ext_max == 3973153323Srodrigc XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); 3974159451Srodrigc 3975153323Srodrigc mp = ip->i_mount; 3976153323Srodrigc ASSERT(!XFS_NOT_DQATTACHED(mp, ip)); 3977153323Srodrigc tp = xfs_trans_alloc(mp, XFS_TRANS_ADDAFORK); 3978153323Srodrigc blks = XFS_ADDAFORK_SPACE_RES(mp); 3979153323Srodrigc if (rsvd) 3980153323Srodrigc tp->t_flags |= XFS_TRANS_RESERVE; 3981153323Srodrigc if ((error = xfs_trans_reserve(tp, blks, XFS_ADDAFORK_LOG_RES(mp), 0, 3982153323Srodrigc XFS_TRANS_PERM_LOG_RES, XFS_ADDAFORK_LOG_COUNT))) 3983153323Srodrigc goto error0; 3984153323Srodrigc xfs_ilock(ip, XFS_ILOCK_EXCL); 3985153323Srodrigc error = XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, blks, 0, rsvd ? 3986153323Srodrigc XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES : 3987153323Srodrigc XFS_QMOPT_RES_REGBLKS); 3988153323Srodrigc if (error) { 3989153323Srodrigc xfs_iunlock(ip, XFS_ILOCK_EXCL); 3990153323Srodrigc xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES); 3991153323Srodrigc return error; 3992153323Srodrigc } 3993153323Srodrigc if (XFS_IFORK_Q(ip)) 3994153323Srodrigc goto error1; 3995153323Srodrigc if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) { 3996153323Srodrigc /* 3997153323Srodrigc * For inodes coming from pre-6.2 filesystems. 3998153323Srodrigc */ 3999153323Srodrigc ASSERT(ip->i_d.di_aformat == 0); 4000153323Srodrigc ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS; 4001153323Srodrigc } 4002153323Srodrigc ASSERT(ip->i_d.di_anextents == 0); 4003153323Srodrigc VN_HOLD(XFS_ITOV(ip)); 4004153323Srodrigc xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); 4005153323Srodrigc xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); 4006153323Srodrigc switch (ip->i_d.di_format) { 4007153323Srodrigc case XFS_DINODE_FMT_DEV: 4008153323Srodrigc ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; 4009153323Srodrigc break; 4010153323Srodrigc case XFS_DINODE_FMT_UUID: 4011153323Srodrigc ip->i_d.di_forkoff = roundup(sizeof(uuid_t), 8) >> 3; 4012153323Srodrigc break; 4013153323Srodrigc case XFS_DINODE_FMT_LOCAL: 4014153323Srodrigc case XFS_DINODE_FMT_EXTENTS: 4015153323Srodrigc case XFS_DINODE_FMT_BTREE: 4016159451Srodrigc ip->i_d.di_forkoff = xfs_attr_shortform_bytesfit(ip, size); 4017159451Srodrigc if (!ip->i_d.di_forkoff) 4018159451Srodrigc ip->i_d.di_forkoff = mp->m_attroffset >> 3; 4019159451Srodrigc else if (mp->m_flags & XFS_MOUNT_ATTR2) 4020159451Srodrigc version = 2; 4021153323Srodrigc break; 4022153323Srodrigc default: 4023153323Srodrigc ASSERT(0); 4024153323Srodrigc error = XFS_ERROR(EINVAL); 4025153323Srodrigc goto error1; 4026153323Srodrigc } 4027153323Srodrigc ip->i_df.if_ext_max = 4028153323Srodrigc XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); 4029153323Srodrigc ASSERT(ip->i_afp == NULL); 4030153323Srodrigc ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP); 4031153323Srodrigc ip->i_afp->if_ext_max = 4032153323Srodrigc XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); 4033153323Srodrigc ip->i_afp->if_flags = XFS_IFEXTENTS; 4034153323Srodrigc logflags = 0; 4035153323Srodrigc XFS_BMAP_INIT(&flist, &firstblock); 4036153323Srodrigc switch (ip->i_d.di_format) { 4037153323Srodrigc case XFS_DINODE_FMT_LOCAL: 4038153323Srodrigc error = xfs_bmap_add_attrfork_local(tp, ip, &firstblock, &flist, 4039153323Srodrigc &logflags); 4040153323Srodrigc break; 4041153323Srodrigc case XFS_DINODE_FMT_EXTENTS: 4042153323Srodrigc error = xfs_bmap_add_attrfork_extents(tp, ip, &firstblock, 4043153323Srodrigc &flist, &logflags); 4044153323Srodrigc break; 4045153323Srodrigc case XFS_DINODE_FMT_BTREE: 4046153323Srodrigc error = xfs_bmap_add_attrfork_btree(tp, ip, &firstblock, &flist, 4047153323Srodrigc &logflags); 4048153323Srodrigc break; 4049153323Srodrigc default: 4050153323Srodrigc error = 0; 4051153323Srodrigc break; 4052153323Srodrigc } 4053153323Srodrigc if (logflags) 4054153323Srodrigc xfs_trans_log_inode(tp, ip, logflags); 4055153323Srodrigc if (error) 4056153323Srodrigc goto error2; 4057159451Srodrigc if (!XFS_SB_VERSION_HASATTR(&mp->m_sb) || 4058159451Srodrigc (!XFS_SB_VERSION_HASATTR2(&mp->m_sb) && version == 2)) { 4059159451Srodrigc __int64_t sbfields = 0; 4060159451Srodrigc 4061153323Srodrigc s = XFS_SB_LOCK(mp); 4062153323Srodrigc if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) { 4063153323Srodrigc XFS_SB_VERSION_ADDATTR(&mp->m_sb); 4064159451Srodrigc sbfields |= XFS_SB_VERSIONNUM; 4065159451Srodrigc } 4066159451Srodrigc if (!XFS_SB_VERSION_HASATTR2(&mp->m_sb) && version == 2) { 4067159451Srodrigc XFS_SB_VERSION_ADDATTR2(&mp->m_sb); 4068159451Srodrigc sbfields |= (XFS_SB_VERSIONNUM | XFS_SB_FEATURES2); 4069159451Srodrigc } 4070159451Srodrigc if (sbfields) { 4071153323Srodrigc XFS_SB_UNLOCK(mp, s); 4072159451Srodrigc xfs_mod_sb(tp, sbfields); 4073153323Srodrigc } else 4074153323Srodrigc XFS_SB_UNLOCK(mp, s); 4075153323Srodrigc } 4076153323Srodrigc if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed))) 4077153323Srodrigc goto error2; 4078153323Srodrigc error = xfs_trans_commit(tp, XFS_TRANS_PERM_LOG_RES, NULL); 4079153323Srodrigc ASSERT(ip->i_df.if_ext_max == 4080153323Srodrigc XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); 4081153323Srodrigc return error; 4082153323Srodrigcerror2: 4083153323Srodrigc xfs_bmap_cancel(&flist); 4084153323Srodrigcerror1: 4085153323Srodrigc ASSERT(ismrlocked(&ip->i_lock,MR_UPDATE)); 4086153323Srodrigc xfs_iunlock(ip, XFS_ILOCK_EXCL); 4087153323Srodrigcerror0: 4088153323Srodrigc xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); 4089153323Srodrigc ASSERT(ip->i_df.if_ext_max == 4090153323Srodrigc XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); 4091153323Srodrigc return error; 4092153323Srodrigc} 4093153323Srodrigc 4094153323Srodrigc/* 4095153323Srodrigc * Add the extent to the list of extents to be free at transaction end. 4096153323Srodrigc * The list is maintained sorted (by block number). 4097153323Srodrigc */ 4098153323Srodrigc/* ARGSUSED */ 4099153323Srodrigcvoid 4100153323Srodrigcxfs_bmap_add_free( 4101153323Srodrigc xfs_fsblock_t bno, /* fs block number of extent */ 4102153323Srodrigc xfs_filblks_t len, /* length of extent */ 4103153323Srodrigc xfs_bmap_free_t *flist, /* list of extents */ 4104153323Srodrigc xfs_mount_t *mp) /* mount point structure */ 4105153323Srodrigc{ 4106153323Srodrigc xfs_bmap_free_item_t *cur; /* current (next) element */ 4107153323Srodrigc xfs_bmap_free_item_t *new; /* new element */ 4108153323Srodrigc xfs_bmap_free_item_t *prev; /* previous element */ 4109153323Srodrigc#ifdef DEBUG 4110153323Srodrigc xfs_agnumber_t agno; 4111153323Srodrigc xfs_agblock_t agbno; 4112153323Srodrigc 4113153323Srodrigc ASSERT(bno != NULLFSBLOCK); 4114153323Srodrigc ASSERT(len > 0); 4115153323Srodrigc ASSERT(len <= MAXEXTLEN); 4116153323Srodrigc ASSERT(!ISNULLSTARTBLOCK(bno)); 4117153323Srodrigc agno = XFS_FSB_TO_AGNO(mp, bno); 4118153323Srodrigc agbno = XFS_FSB_TO_AGBNO(mp, bno); 4119153323Srodrigc ASSERT(agno < mp->m_sb.sb_agcount); 4120153323Srodrigc ASSERT(agbno < mp->m_sb.sb_agblocks); 4121153323Srodrigc ASSERT(len < mp->m_sb.sb_agblocks); 4122153323Srodrigc ASSERT(agbno + len <= mp->m_sb.sb_agblocks); 4123153323Srodrigc#endif 4124153323Srodrigc ASSERT(xfs_bmap_free_item_zone != NULL); 4125153323Srodrigc new = kmem_zone_alloc(xfs_bmap_free_item_zone, KM_SLEEP); 4126153323Srodrigc new->xbfi_startblock = bno; 4127153323Srodrigc new->xbfi_blockcount = (xfs_extlen_t)len; 4128153323Srodrigc for (prev = NULL, cur = flist->xbf_first; 4129153323Srodrigc cur != NULL; 4130153323Srodrigc prev = cur, cur = cur->xbfi_next) { 4131153323Srodrigc if (cur->xbfi_startblock >= bno) 4132153323Srodrigc break; 4133153323Srodrigc } 4134153323Srodrigc if (prev) 4135153323Srodrigc prev->xbfi_next = new; 4136153323Srodrigc else 4137153323Srodrigc flist->xbf_first = new; 4138153323Srodrigc new->xbfi_next = cur; 4139153323Srodrigc flist->xbf_count++; 4140153323Srodrigc} 4141153323Srodrigc 4142153323Srodrigc/* 4143153323Srodrigc * Compute and fill in the value of the maximum depth of a bmap btree 4144153323Srodrigc * in this filesystem. Done once, during mount. 4145153323Srodrigc */ 4146153323Srodrigcvoid 4147153323Srodrigcxfs_bmap_compute_maxlevels( 4148153323Srodrigc xfs_mount_t *mp, /* file system mount structure */ 4149153323Srodrigc int whichfork) /* data or attr fork */ 4150153323Srodrigc{ 4151153323Srodrigc int level; /* btree level */ 4152153323Srodrigc uint maxblocks; /* max blocks at this level */ 4153153323Srodrigc uint maxleafents; /* max leaf entries possible */ 4154153323Srodrigc int maxrootrecs; /* max records in root block */ 4155153323Srodrigc int minleafrecs; /* min records in leaf block */ 4156153323Srodrigc int minnoderecs; /* min records in node block */ 4157153323Srodrigc int sz; /* root block size */ 4158153323Srodrigc 4159153323Srodrigc /* 4160153323Srodrigc * The maximum number of extents in a file, hence the maximum 4161153323Srodrigc * number of leaf entries, is controlled by the type of di_nextents 4162153323Srodrigc * (a signed 32-bit number, xfs_extnum_t), or by di_anextents 4163153323Srodrigc * (a signed 16-bit number, xfs_aextnum_t). 4164153323Srodrigc */ 4165159451Srodrigc if (whichfork == XFS_DATA_FORK) { 4166159451Srodrigc maxleafents = MAXEXTNUM; 4167159451Srodrigc sz = (mp->m_flags & XFS_MOUNT_ATTR2) ? 4168159451Srodrigc XFS_BMDR_SPACE_CALC(MINDBTPTRS) : mp->m_attroffset; 4169159451Srodrigc } else { 4170159451Srodrigc maxleafents = MAXAEXTNUM; 4171159451Srodrigc sz = (mp->m_flags & XFS_MOUNT_ATTR2) ? 4172159451Srodrigc XFS_BMDR_SPACE_CALC(MINABTPTRS) : 4173159451Srodrigc mp->m_sb.sb_inodesize - mp->m_attroffset; 4174159451Srodrigc } 4175159451Srodrigc maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0); 4176153323Srodrigc minleafrecs = mp->m_bmap_dmnr[0]; 4177153323Srodrigc minnoderecs = mp->m_bmap_dmnr[1]; 4178153323Srodrigc maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs; 4179153323Srodrigc for (level = 1; maxblocks > 1; level++) { 4180153323Srodrigc if (maxblocks <= maxrootrecs) 4181153323Srodrigc maxblocks = 1; 4182153323Srodrigc else 4183153323Srodrigc maxblocks = (maxblocks + minnoderecs - 1) / minnoderecs; 4184153323Srodrigc } 4185153323Srodrigc mp->m_bm_maxlevels[whichfork] = level; 4186153323Srodrigc} 4187153323Srodrigc 4188153323Srodrigc/* 4189153323Srodrigc * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi 4190153323Srodrigc * caller. Frees all the extents that need freeing, which must be done 4191153323Srodrigc * last due to locking considerations. We never free any extents in 4192153323Srodrigc * the first transaction. This is to allow the caller to make the first 4193153323Srodrigc * transaction a synchronous one so that the pointers to the data being 4194153323Srodrigc * broken in this transaction will be permanent before the data is actually 4195153323Srodrigc * freed. This is necessary to prevent blocks from being reallocated 4196153323Srodrigc * and written to before the free and reallocation are actually permanent. 4197153323Srodrigc * We do not just make the first transaction synchronous here, because 4198153323Srodrigc * there are more efficient ways to gain the same protection in some cases 4199153323Srodrigc * (see the file truncation code). 4200153323Srodrigc * 4201153323Srodrigc * Return 1 if the given transaction was committed and a new one 4202153323Srodrigc * started, and 0 otherwise in the committed parameter. 4203153323Srodrigc */ 4204153323Srodrigc/*ARGSUSED*/ 4205153323Srodrigcint /* error */ 4206153323Srodrigcxfs_bmap_finish( 4207153323Srodrigc xfs_trans_t **tp, /* transaction pointer addr */ 4208153323Srodrigc xfs_bmap_free_t *flist, /* i/o: list extents to free */ 4209153323Srodrigc xfs_fsblock_t firstblock, /* controlled ag for allocs */ 4210153323Srodrigc int *committed) /* xact committed or not */ 4211153323Srodrigc{ 4212153323Srodrigc xfs_efd_log_item_t *efd; /* extent free data */ 4213153323Srodrigc xfs_efi_log_item_t *efi; /* extent free intention */ 4214153323Srodrigc int error; /* error return value */ 4215159451Srodrigc xfs_bmap_free_item_t *free; /* free extent item */ 4216153323Srodrigc unsigned int logres; /* new log reservation */ 4217153323Srodrigc unsigned int logcount; /* new log count */ 4218153323Srodrigc xfs_mount_t *mp; /* filesystem mount structure */ 4219153323Srodrigc xfs_bmap_free_item_t *next; /* next item on free list */ 4220153323Srodrigc xfs_trans_t *ntp; /* new transaction pointer */ 4221153323Srodrigc 4222153323Srodrigc ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES); 4223153323Srodrigc if (flist->xbf_count == 0) { 4224153323Srodrigc *committed = 0; 4225153323Srodrigc return 0; 4226153323Srodrigc } 4227153323Srodrigc ntp = *tp; 4228153323Srodrigc efi = xfs_trans_get_efi(ntp, flist->xbf_count); 4229153323Srodrigc for (free = flist->xbf_first; free; free = free->xbfi_next) 4230153323Srodrigc xfs_trans_log_efi_extent(ntp, efi, free->xbfi_startblock, 4231153323Srodrigc free->xbfi_blockcount); 4232153323Srodrigc logres = ntp->t_log_res; 4233153323Srodrigc logcount = ntp->t_log_count; 4234153323Srodrigc ntp = xfs_trans_dup(*tp); 4235153323Srodrigc error = xfs_trans_commit(*tp, 0, NULL); 4236153323Srodrigc *tp = ntp; 4237153323Srodrigc *committed = 1; 4238153323Srodrigc /* 4239153323Srodrigc * We have a new transaction, so we should return committed=1, 4240153323Srodrigc * even though we're returning an error. 4241153323Srodrigc */ 4242153323Srodrigc if (error) { 4243153323Srodrigc return error; 4244153323Srodrigc } 4245153323Srodrigc if ((error = xfs_trans_reserve(ntp, 0, logres, 0, XFS_TRANS_PERM_LOG_RES, 4246153323Srodrigc logcount))) 4247153323Srodrigc return error; 4248153323Srodrigc efd = xfs_trans_get_efd(ntp, efi, flist->xbf_count); 4249153323Srodrigc for (free = flist->xbf_first; free != NULL; free = next) { 4250153323Srodrigc next = free->xbfi_next; 4251153323Srodrigc if ((error = xfs_free_extent(ntp, free->xbfi_startblock, 4252153323Srodrigc free->xbfi_blockcount))) { 4253153323Srodrigc /* 4254153323Srodrigc * The bmap free list will be cleaned up at a 4255153323Srodrigc * higher level. The EFI will be canceled when 4256153323Srodrigc * this transaction is aborted. 4257153323Srodrigc * Need to force shutdown here to make sure it 4258153323Srodrigc * happens, since this transaction may not be 4259153323Srodrigc * dirty yet. 4260153323Srodrigc */ 4261153323Srodrigc mp = ntp->t_mountp; 4262153323Srodrigc if (!XFS_FORCED_SHUTDOWN(mp)) 4263153323Srodrigc xfs_force_shutdown(mp, 4264153323Srodrigc (error == EFSCORRUPTED) ? 4265153323Srodrigc XFS_CORRUPT_INCORE : 4266153323Srodrigc XFS_METADATA_IO_ERROR); 4267153323Srodrigc return error; 4268153323Srodrigc } 4269153323Srodrigc xfs_trans_log_efd_extent(ntp, efd, free->xbfi_startblock, 4270153323Srodrigc free->xbfi_blockcount); 4271153323Srodrigc xfs_bmap_del_free(flist, NULL, free); 4272153323Srodrigc } 4273153323Srodrigc return 0; 4274153323Srodrigc} 4275153323Srodrigc 4276153323Srodrigc/* 4277153323Srodrigc * Free up any items left in the list. 4278153323Srodrigc */ 4279153323Srodrigcvoid 4280153323Srodrigcxfs_bmap_cancel( 4281153323Srodrigc xfs_bmap_free_t *flist) /* list of bmap_free_items */ 4282153323Srodrigc{ 4283153323Srodrigc xfs_bmap_free_item_t *free; /* free list item */ 4284153323Srodrigc xfs_bmap_free_item_t *next; 4285153323Srodrigc 4286153323Srodrigc if (flist->xbf_count == 0) 4287153323Srodrigc return; 4288153323Srodrigc ASSERT(flist->xbf_first != NULL); 4289153323Srodrigc for (free = flist->xbf_first; free; free = next) { 4290153323Srodrigc next = free->xbfi_next; 4291153323Srodrigc xfs_bmap_del_free(flist, NULL, free); 4292153323Srodrigc } 4293153323Srodrigc ASSERT(flist->xbf_count == 0); 4294153323Srodrigc} 4295153323Srodrigc 4296153323Srodrigc/* 4297153323Srodrigc * Returns the file-relative block number of the first unused block(s) 4298153323Srodrigc * in the file with at least "len" logically contiguous blocks free. 4299153323Srodrigc * This is the lowest-address hole if the file has holes, else the first block 4300153323Srodrigc * past the end of file. 4301153323Srodrigc * Return 0 if the file is currently local (in-inode). 4302153323Srodrigc */ 4303153323Srodrigcint /* error */ 4304153323Srodrigcxfs_bmap_first_unused( 4305153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 4306153323Srodrigc xfs_inode_t *ip, /* incore inode */ 4307153323Srodrigc xfs_extlen_t len, /* size of hole to find */ 4308153323Srodrigc xfs_fileoff_t *first_unused, /* unused block */ 4309153323Srodrigc int whichfork) /* data or attr fork */ 4310153323Srodrigc{ 4311153323Srodrigc xfs_bmbt_rec_t *ep; /* pointer to an extent entry */ 4312153323Srodrigc int error; /* error return value */ 4313159451Srodrigc int idx; /* extent record index */ 4314153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 4315153323Srodrigc xfs_fileoff_t lastaddr; /* last block number seen */ 4316153323Srodrigc xfs_fileoff_t lowest; /* lowest useful block */ 4317153323Srodrigc xfs_fileoff_t max; /* starting useful block */ 4318153323Srodrigc xfs_fileoff_t off; /* offset for this block */ 4319153323Srodrigc xfs_extnum_t nextents; /* number of extent entries */ 4320153323Srodrigc 4321153323Srodrigc ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE || 4322153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS || 4323153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL); 4324153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) { 4325153323Srodrigc *first_unused = 0; 4326153323Srodrigc return 0; 4327153323Srodrigc } 4328153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4329153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 4330153323Srodrigc (error = xfs_iread_extents(tp, ip, whichfork))) 4331153323Srodrigc return error; 4332153323Srodrigc lowest = *first_unused; 4333153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 4334159451Srodrigc for (idx = 0, lastaddr = 0, max = lowest; idx < nextents; idx++) { 4335159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 4336153323Srodrigc off = xfs_bmbt_get_startoff(ep); 4337153323Srodrigc /* 4338153323Srodrigc * See if the hole before this extent will work. 4339153323Srodrigc */ 4340153323Srodrigc if (off >= lowest + len && off - max >= len) { 4341153323Srodrigc *first_unused = max; 4342153323Srodrigc return 0; 4343153323Srodrigc } 4344153323Srodrigc lastaddr = off + xfs_bmbt_get_blockcount(ep); 4345153323Srodrigc max = XFS_FILEOFF_MAX(lastaddr, lowest); 4346153323Srodrigc } 4347153323Srodrigc *first_unused = max; 4348153323Srodrigc return 0; 4349153323Srodrigc} 4350153323Srodrigc 4351153323Srodrigc/* 4352153323Srodrigc * Returns the file-relative block number of the last block + 1 before 4353153323Srodrigc * last_block (input value) in the file. 4354159451Srodrigc * This is not based on i_size, it is based on the extent records. 4355159451Srodrigc * Returns 0 for local files, as they do not have extent records. 4356153323Srodrigc */ 4357153323Srodrigcint /* error */ 4358153323Srodrigcxfs_bmap_last_before( 4359153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 4360153323Srodrigc xfs_inode_t *ip, /* incore inode */ 4361153323Srodrigc xfs_fileoff_t *last_block, /* last block */ 4362153323Srodrigc int whichfork) /* data or attr fork */ 4363153323Srodrigc{ 4364153323Srodrigc xfs_fileoff_t bno; /* input file offset */ 4365153323Srodrigc int eof; /* hit end of file */ 4366153323Srodrigc xfs_bmbt_rec_t *ep; /* pointer to last extent */ 4367153323Srodrigc int error; /* error return value */ 4368153323Srodrigc xfs_bmbt_irec_t got; /* current extent value */ 4369153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 4370153323Srodrigc xfs_extnum_t lastx; /* last extent used */ 4371153323Srodrigc xfs_bmbt_irec_t prev; /* previous extent value */ 4372153323Srodrigc 4373153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE && 4374153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && 4375153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL) 4376153323Srodrigc return XFS_ERROR(EIO); 4377153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) { 4378153323Srodrigc *last_block = 0; 4379153323Srodrigc return 0; 4380153323Srodrigc } 4381153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4382153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 4383153323Srodrigc (error = xfs_iread_extents(tp, ip, whichfork))) 4384153323Srodrigc return error; 4385153323Srodrigc bno = *last_block - 1; 4386153323Srodrigc ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, 4387153323Srodrigc &prev); 4388153323Srodrigc if (eof || xfs_bmbt_get_startoff(ep) > bno) { 4389153323Srodrigc if (prev.br_startoff == NULLFILEOFF) 4390153323Srodrigc *last_block = 0; 4391153323Srodrigc else 4392153323Srodrigc *last_block = prev.br_startoff + prev.br_blockcount; 4393153323Srodrigc } 4394153323Srodrigc /* 4395153323Srodrigc * Otherwise *last_block is already the right answer. 4396153323Srodrigc */ 4397153323Srodrigc return 0; 4398153323Srodrigc} 4399153323Srodrigc 4400153323Srodrigc/* 4401153323Srodrigc * Returns the file-relative block number of the first block past eof in 4402159451Srodrigc * the file. This is not based on i_size, it is based on the extent records. 4403159451Srodrigc * Returns 0 for local files, as they do not have extent records. 4404153323Srodrigc */ 4405153323Srodrigcint /* error */ 4406153323Srodrigcxfs_bmap_last_offset( 4407153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 4408153323Srodrigc xfs_inode_t *ip, /* incore inode */ 4409153323Srodrigc xfs_fileoff_t *last_block, /* last block */ 4410153323Srodrigc int whichfork) /* data or attr fork */ 4411153323Srodrigc{ 4412153323Srodrigc xfs_bmbt_rec_t *ep; /* pointer to last extent */ 4413153323Srodrigc int error; /* error return value */ 4414153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 4415153323Srodrigc xfs_extnum_t nextents; /* number of extent entries */ 4416153323Srodrigc 4417153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE && 4418153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && 4419153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL) 4420153323Srodrigc return XFS_ERROR(EIO); 4421153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) { 4422153323Srodrigc *last_block = 0; 4423153323Srodrigc return 0; 4424153323Srodrigc } 4425153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4426153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 4427153323Srodrigc (error = xfs_iread_extents(tp, ip, whichfork))) 4428153323Srodrigc return error; 4429153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 4430153323Srodrigc if (!nextents) { 4431153323Srodrigc *last_block = 0; 4432153323Srodrigc return 0; 4433153323Srodrigc } 4434159451Srodrigc ep = xfs_iext_get_ext(ifp, nextents - 1); 4435153323Srodrigc *last_block = xfs_bmbt_get_startoff(ep) + xfs_bmbt_get_blockcount(ep); 4436153323Srodrigc return 0; 4437153323Srodrigc} 4438153323Srodrigc 4439153323Srodrigc/* 4440153323Srodrigc * Returns whether the selected fork of the inode has exactly one 4441153323Srodrigc * block or not. For the data fork we check this matches di_size, 4442153323Srodrigc * implying the file's range is 0..bsize-1. 4443153323Srodrigc */ 4444153323Srodrigcint /* 1=>1 block, 0=>otherwise */ 4445153323Srodrigcxfs_bmap_one_block( 4446153323Srodrigc xfs_inode_t *ip, /* incore inode */ 4447153323Srodrigc int whichfork) /* data or attr fork */ 4448153323Srodrigc{ 4449153323Srodrigc xfs_bmbt_rec_t *ep; /* ptr to fork's extent */ 4450153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 4451153323Srodrigc int rval; /* return value */ 4452153323Srodrigc xfs_bmbt_irec_t s; /* internal version of extent */ 4453153323Srodrigc 4454153323Srodrigc#ifndef DEBUG 4455153323Srodrigc if (whichfork == XFS_DATA_FORK) 4456153323Srodrigc return ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize; 4457153323Srodrigc#endif /* !DEBUG */ 4458153323Srodrigc if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1) 4459153323Srodrigc return 0; 4460153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS) 4461153323Srodrigc return 0; 4462153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4463153323Srodrigc ASSERT(ifp->if_flags & XFS_IFEXTENTS); 4464159451Srodrigc ep = xfs_iext_get_ext(ifp, 0); 4465153323Srodrigc xfs_bmbt_get_all(ep, &s); 4466153323Srodrigc rval = s.br_startoff == 0 && s.br_blockcount == 1; 4467153323Srodrigc if (rval && whichfork == XFS_DATA_FORK) 4468153323Srodrigc ASSERT(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize); 4469153323Srodrigc return rval; 4470153323Srodrigc} 4471153323Srodrigc 4472153323Srodrigc/* 4473153323Srodrigc * Read in the extents to if_extents. 4474153323Srodrigc * All inode fields are set up by caller, we just traverse the btree 4475153323Srodrigc * and copy the records in. If the file system cannot contain unwritten 4476153323Srodrigc * extents, the records are checked for no "state" flags. 4477153323Srodrigc */ 4478153323Srodrigcint /* error */ 4479153323Srodrigcxfs_bmap_read_extents( 4480153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 4481153323Srodrigc xfs_inode_t *ip, /* incore inode */ 4482153323Srodrigc int whichfork) /* data or attr fork */ 4483153323Srodrigc{ 4484153323Srodrigc xfs_bmbt_block_t *block; /* current btree block */ 4485153323Srodrigc xfs_fsblock_t bno; /* block # of "block" */ 4486153323Srodrigc xfs_buf_t *bp; /* buffer for "block" */ 4487153323Srodrigc int error; /* error return value */ 4488153323Srodrigc xfs_exntfmt_t exntf; /* XFS_EXTFMT_NOSTATE, if checking */ 4489153323Srodrigc#ifdef XFS_BMAP_TRACE 4490153323Srodrigc static char fname[] = "xfs_bmap_read_extents"; 4491153323Srodrigc#endif 4492153323Srodrigc xfs_extnum_t i, j; /* index into the extents list */ 4493153323Srodrigc xfs_ifork_t *ifp; /* fork structure */ 4494153323Srodrigc int level; /* btree level, for checking */ 4495153323Srodrigc xfs_mount_t *mp; /* file system mount structure */ 4496153323Srodrigc xfs_bmbt_ptr_t *pp; /* pointer to block address */ 4497153323Srodrigc /* REFERENCED */ 4498153323Srodrigc xfs_extnum_t room; /* number of entries there's room for */ 4499153323Srodrigc 4500153323Srodrigc bno = NULLFSBLOCK; 4501153323Srodrigc mp = ip->i_mount; 4502153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4503153323Srodrigc exntf = (whichfork != XFS_DATA_FORK) ? XFS_EXTFMT_NOSTATE : 4504153323Srodrigc XFS_EXTFMT_INODE(ip); 4505153323Srodrigc block = ifp->if_broot; 4506153323Srodrigc /* 4507153323Srodrigc * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out. 4508153323Srodrigc */ 4509159451Srodrigc level = be16_to_cpu(block->bb_level); 4510159451Srodrigc ASSERT(level > 0); 4511153323Srodrigc pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes); 4512153323Srodrigc ASSERT(INT_GET(*pp, ARCH_CONVERT) != NULLDFSBNO); 4513153323Srodrigc ASSERT(XFS_FSB_TO_AGNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agcount); 4514153323Srodrigc ASSERT(XFS_FSB_TO_AGBNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agblocks); 4515153323Srodrigc bno = INT_GET(*pp, ARCH_CONVERT); 4516153323Srodrigc /* 4517153323Srodrigc * Go down the tree until leaf level is reached, following the first 4518153323Srodrigc * pointer (leftmost) at each level. 4519153323Srodrigc */ 4520153323Srodrigc while (level-- > 0) { 4521153323Srodrigc if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp, 4522153323Srodrigc XFS_BMAP_BTREE_REF))) 4523153323Srodrigc return error; 4524153323Srodrigc block = XFS_BUF_TO_BMBT_BLOCK(bp); 4525153323Srodrigc XFS_WANT_CORRUPTED_GOTO( 4526153323Srodrigc XFS_BMAP_SANITY_CHECK(mp, block, level), 4527153323Srodrigc error0); 4528153323Srodrigc if (level == 0) 4529153323Srodrigc break; 4530153323Srodrigc pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, block, 4531153323Srodrigc 1, mp->m_bmap_dmxr[1]); 4532153323Srodrigc XFS_WANT_CORRUPTED_GOTO( 4533153323Srodrigc XFS_FSB_SANITY_CHECK(mp, INT_GET(*pp, ARCH_CONVERT)), 4534153323Srodrigc error0); 4535153323Srodrigc bno = INT_GET(*pp, ARCH_CONVERT); 4536153323Srodrigc xfs_trans_brelse(tp, bp); 4537153323Srodrigc } 4538153323Srodrigc /* 4539153323Srodrigc * Here with bp and block set to the leftmost leaf node in the tree. 4540153323Srodrigc */ 4541159451Srodrigc room = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 4542153323Srodrigc i = 0; 4543153323Srodrigc /* 4544159451Srodrigc * Loop over all leaf nodes. Copy information to the extent records. 4545153323Srodrigc */ 4546153323Srodrigc for (;;) { 4547159451Srodrigc xfs_bmbt_rec_t *frp, *trp; 4548153323Srodrigc xfs_fsblock_t nextbno; 4549153323Srodrigc xfs_extnum_t num_recs; 4550159451Srodrigc xfs_extnum_t start; 4551153323Srodrigc 4552153323Srodrigc 4553159451Srodrigc num_recs = be16_to_cpu(block->bb_numrecs); 4554153323Srodrigc if (unlikely(i + num_recs > room)) { 4555153323Srodrigc ASSERT(i + num_recs <= room); 4556159451Srodrigc xfs_fs_repair_cmn_err(CE_WARN, ip->i_mount, 4557159451Srodrigc "corrupt dinode %Lu, (btree extents).", 4558153323Srodrigc (unsigned long long) ip->i_ino); 4559153323Srodrigc XFS_ERROR_REPORT("xfs_bmap_read_extents(1)", 4560153323Srodrigc XFS_ERRLEVEL_LOW, 4561153323Srodrigc ip->i_mount); 4562153323Srodrigc goto error0; 4563153323Srodrigc } 4564153323Srodrigc XFS_WANT_CORRUPTED_GOTO( 4565153323Srodrigc XFS_BMAP_SANITY_CHECK(mp, block, 0), 4566153323Srodrigc error0); 4567153323Srodrigc /* 4568153323Srodrigc * Read-ahead the next leaf block, if any. 4569153323Srodrigc */ 4570159451Srodrigc nextbno = be64_to_cpu(block->bb_rightsib); 4571153323Srodrigc if (nextbno != NULLFSBLOCK) 4572153323Srodrigc xfs_btree_reada_bufl(mp, nextbno, 1); 4573153323Srodrigc /* 4574159451Srodrigc * Copy records into the extent records. 4575153323Srodrigc */ 4576153323Srodrigc frp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, 4577153323Srodrigc block, 1, mp->m_bmap_dmxr[0]); 4578159451Srodrigc start = i; 4579159451Srodrigc for (j = 0; j < num_recs; j++, i++, frp++) { 4580159451Srodrigc trp = xfs_iext_get_ext(ifp, i); 4581153323Srodrigc trp->l0 = INT_GET(frp->l0, ARCH_CONVERT); 4582153323Srodrigc trp->l1 = INT_GET(frp->l1, ARCH_CONVERT); 4583153323Srodrigc } 4584153323Srodrigc if (exntf == XFS_EXTFMT_NOSTATE) { 4585153323Srodrigc /* 4586153323Srodrigc * Check all attribute bmap btree records and 4587153323Srodrigc * any "older" data bmap btree records for a 4588153323Srodrigc * set bit in the "extent flag" position. 4589153323Srodrigc */ 4590159451Srodrigc if (unlikely(xfs_check_nostate_extents(ifp, 4591159451Srodrigc start, num_recs))) { 4592153323Srodrigc XFS_ERROR_REPORT("xfs_bmap_read_extents(2)", 4593153323Srodrigc XFS_ERRLEVEL_LOW, 4594153323Srodrigc ip->i_mount); 4595153323Srodrigc goto error0; 4596153323Srodrigc } 4597153323Srodrigc } 4598153323Srodrigc xfs_trans_brelse(tp, bp); 4599153323Srodrigc bno = nextbno; 4600153323Srodrigc /* 4601153323Srodrigc * If we've reached the end, stop. 4602153323Srodrigc */ 4603153323Srodrigc if (bno == NULLFSBLOCK) 4604153323Srodrigc break; 4605153323Srodrigc if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp, 4606153323Srodrigc XFS_BMAP_BTREE_REF))) 4607153323Srodrigc return error; 4608153323Srodrigc block = XFS_BUF_TO_BMBT_BLOCK(bp); 4609153323Srodrigc } 4610159451Srodrigc ASSERT(i == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))); 4611153323Srodrigc ASSERT(i == XFS_IFORK_NEXTENTS(ip, whichfork)); 4612153323Srodrigc xfs_bmap_trace_exlist(fname, ip, i, whichfork); 4613153323Srodrigc return 0; 4614153323Srodrigcerror0: 4615153323Srodrigc xfs_trans_brelse(tp, bp); 4616153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 4617153323Srodrigc} 4618153323Srodrigc 4619153323Srodrigc#ifdef XFS_BMAP_TRACE 4620153323Srodrigc/* 4621159451Srodrigc * Add bmap trace insert entries for all the contents of the extent records. 4622153323Srodrigc */ 4623153323Srodrigcvoid 4624153323Srodrigcxfs_bmap_trace_exlist( 4625153323Srodrigc char *fname, /* function name */ 4626153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 4627153323Srodrigc xfs_extnum_t cnt, /* count of entries in the list */ 4628153323Srodrigc int whichfork) /* data or attr fork */ 4629153323Srodrigc{ 4630159451Srodrigc xfs_bmbt_rec_t *ep; /* current extent record */ 4631159451Srodrigc xfs_extnum_t idx; /* extent record index */ 4632153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 4633159451Srodrigc xfs_bmbt_irec_t s; /* file extent record */ 4634153323Srodrigc 4635153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4636159451Srodrigc ASSERT(cnt == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))); 4637159451Srodrigc for (idx = 0; idx < cnt; idx++) { 4638159451Srodrigc ep = xfs_iext_get_ext(ifp, idx); 4639153323Srodrigc xfs_bmbt_get_all(ep, &s); 4640153323Srodrigc xfs_bmap_trace_insert(fname, "exlist", ip, idx, 1, &s, NULL, 4641153323Srodrigc whichfork); 4642153323Srodrigc } 4643153323Srodrigc} 4644153323Srodrigc#endif 4645153323Srodrigc 4646153323Srodrigc#ifdef DEBUG 4647153323Srodrigc/* 4648153323Srodrigc * Validate that the bmbt_irecs being returned from bmapi are valid 4649153323Srodrigc * given the callers original parameters. Specifically check the 4650153323Srodrigc * ranges of the returned irecs to ensure that they only extent beyond 4651153323Srodrigc * the given parameters if the XFS_BMAPI_ENTIRE flag was set. 4652153323Srodrigc */ 4653153323SrodrigcSTATIC void 4654153323Srodrigcxfs_bmap_validate_ret( 4655153323Srodrigc xfs_fileoff_t bno, 4656153323Srodrigc xfs_filblks_t len, 4657153323Srodrigc int flags, 4658153323Srodrigc xfs_bmbt_irec_t *mval, 4659153323Srodrigc int nmap, 4660153323Srodrigc int ret_nmap) 4661153323Srodrigc{ 4662153323Srodrigc int i; /* index to map values */ 4663153323Srodrigc 4664153323Srodrigc ASSERT(ret_nmap <= nmap); 4665153323Srodrigc 4666153323Srodrigc for (i = 0; i < ret_nmap; i++) { 4667153323Srodrigc ASSERT(mval[i].br_blockcount > 0); 4668153323Srodrigc if (!(flags & XFS_BMAPI_ENTIRE)) { 4669153323Srodrigc ASSERT(mval[i].br_startoff >= bno); 4670153323Srodrigc ASSERT(mval[i].br_blockcount <= len); 4671153323Srodrigc ASSERT(mval[i].br_startoff + mval[i].br_blockcount <= 4672153323Srodrigc bno + len); 4673153323Srodrigc } else { 4674153323Srodrigc ASSERT(mval[i].br_startoff < bno + len); 4675153323Srodrigc ASSERT(mval[i].br_startoff + mval[i].br_blockcount > 4676153323Srodrigc bno); 4677153323Srodrigc } 4678153323Srodrigc ASSERT(i == 0 || 4679153323Srodrigc mval[i - 1].br_startoff + mval[i - 1].br_blockcount == 4680153323Srodrigc mval[i].br_startoff); 4681153323Srodrigc if ((flags & XFS_BMAPI_WRITE) && !(flags & XFS_BMAPI_DELAY)) 4682153323Srodrigc ASSERT(mval[i].br_startblock != DELAYSTARTBLOCK && 4683153323Srodrigc mval[i].br_startblock != HOLESTARTBLOCK); 4684153323Srodrigc ASSERT(mval[i].br_state == XFS_EXT_NORM || 4685153323Srodrigc mval[i].br_state == XFS_EXT_UNWRITTEN); 4686153323Srodrigc } 4687153323Srodrigc} 4688153323Srodrigc#endif /* DEBUG */ 4689153323Srodrigc 4690153323Srodrigc 4691153323Srodrigc/* 4692153323Srodrigc * Map file blocks to filesystem blocks. 4693153323Srodrigc * File range is given by the bno/len pair. 4694153323Srodrigc * Adds blocks to file if a write ("flags & XFS_BMAPI_WRITE" set) 4695153323Srodrigc * into a hole or past eof. 4696153323Srodrigc * Only allocates blocks from a single allocation group, 4697153323Srodrigc * to avoid locking problems. 4698153323Srodrigc * The returned value in "firstblock" from the first call in a transaction 4699153323Srodrigc * must be remembered and presented to subsequent calls in "firstblock". 4700153323Srodrigc * An upper bound for the number of blocks to be allocated is supplied to 4701153323Srodrigc * the first call in "total"; if no allocation group has that many free 4702153323Srodrigc * blocks then the call will fail (return NULLFSBLOCK in "firstblock"). 4703153323Srodrigc */ 4704153323Srodrigcint /* error */ 4705153323Srodrigcxfs_bmapi( 4706153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 4707153323Srodrigc xfs_inode_t *ip, /* incore inode */ 4708153323Srodrigc xfs_fileoff_t bno, /* starting file offs. mapped */ 4709153323Srodrigc xfs_filblks_t len, /* length to map in file */ 4710153323Srodrigc int flags, /* XFS_BMAPI_... */ 4711153323Srodrigc xfs_fsblock_t *firstblock, /* first allocated block 4712153323Srodrigc controls a.g. for allocs */ 4713153323Srodrigc xfs_extlen_t total, /* total blocks needed */ 4714153323Srodrigc xfs_bmbt_irec_t *mval, /* output: map values */ 4715153323Srodrigc int *nmap, /* i/o: mval size/count */ 4716159451Srodrigc xfs_bmap_free_t *flist, /* i/o: list extents to free */ 4717159451Srodrigc xfs_extdelta_t *delta) /* o: change made to incore extents */ 4718153323Srodrigc{ 4719153323Srodrigc xfs_fsblock_t abno; /* allocated block number */ 4720153323Srodrigc xfs_extlen_t alen; /* allocated extent length */ 4721153323Srodrigc xfs_fileoff_t aoff; /* allocated file offset */ 4722153323Srodrigc xfs_bmalloca_t bma; /* args for xfs_bmap_alloc */ 4723153323Srodrigc xfs_btree_cur_t *cur; /* bmap btree cursor */ 4724153323Srodrigc xfs_fileoff_t end; /* end of mapped file region */ 4725159451Srodrigc int eof; /* we've hit the end of extents */ 4726159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record pointer */ 4727153323Srodrigc int error; /* error return */ 4728159451Srodrigc xfs_bmbt_irec_t got; /* current file extent record */ 4729153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 4730153323Srodrigc xfs_extlen_t indlen; /* indirect blocks length */ 4731153323Srodrigc xfs_extnum_t lastx; /* last useful extent number */ 4732153323Srodrigc int logflags; /* flags for transaction logging */ 4733153323Srodrigc xfs_extlen_t minleft; /* min blocks left after allocation */ 4734153323Srodrigc xfs_extlen_t minlen; /* min allocation size */ 4735153323Srodrigc xfs_mount_t *mp; /* xfs mount structure */ 4736153323Srodrigc int n; /* current extent index */ 4737153323Srodrigc int nallocs; /* number of extents alloc\'d */ 4738153323Srodrigc xfs_extnum_t nextents; /* number of extents in file */ 4739153323Srodrigc xfs_fileoff_t obno; /* old block number (offset) */ 4740159451Srodrigc xfs_bmbt_irec_t prev; /* previous file extent record */ 4741153323Srodrigc int tmp_logflags; /* temp flags holder */ 4742159451Srodrigc int whichfork; /* data or attr fork */ 4743159451Srodrigc char inhole; /* current location is hole in file */ 4744153323Srodrigc char wasdelay; /* old extent was delayed */ 4745153323Srodrigc char wr; /* this is a write request */ 4746159451Srodrigc char rt; /* this is a realtime file */ 4747153323Srodrigc#ifdef DEBUG 4748153323Srodrigc xfs_fileoff_t orig_bno; /* original block number value */ 4749153323Srodrigc int orig_flags; /* original flags arg value */ 4750153323Srodrigc xfs_filblks_t orig_len; /* original value of len arg */ 4751153323Srodrigc xfs_bmbt_irec_t *orig_mval; /* original value of mval */ 4752153323Srodrigc int orig_nmap; /* original value of *nmap */ 4753153323Srodrigc 4754153323Srodrigc orig_bno = bno; 4755153323Srodrigc orig_len = len; 4756153323Srodrigc orig_flags = flags; 4757153323Srodrigc orig_mval = mval; 4758153323Srodrigc orig_nmap = *nmap; 4759153323Srodrigc#endif 4760153323Srodrigc ASSERT(*nmap >= 1); 4761153323Srodrigc ASSERT(*nmap <= XFS_BMAP_MAX_NMAP || !(flags & XFS_BMAPI_WRITE)); 4762153323Srodrigc whichfork = (flags & XFS_BMAPI_ATTRFORK) ? 4763153323Srodrigc XFS_ATTR_FORK : XFS_DATA_FORK; 4764153323Srodrigc mp = ip->i_mount; 4765153323Srodrigc if (unlikely(XFS_TEST_ERROR( 4766153323Srodrigc (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && 4767153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE && 4768153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL), 4769153323Srodrigc mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) { 4770153323Srodrigc XFS_ERROR_REPORT("xfs_bmapi", XFS_ERRLEVEL_LOW, mp); 4771153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 4772153323Srodrigc } 4773153323Srodrigc if (XFS_FORCED_SHUTDOWN(mp)) 4774153323Srodrigc return XFS_ERROR(EIO); 4775159451Srodrigc rt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); 4776153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 4777153323Srodrigc ASSERT(ifp->if_ext_max == 4778153323Srodrigc XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); 4779153323Srodrigc if ((wr = (flags & XFS_BMAPI_WRITE)) != 0) 4780153323Srodrigc XFS_STATS_INC(xs_blk_mapw); 4781153323Srodrigc else 4782153323Srodrigc XFS_STATS_INC(xs_blk_mapr); 4783153323Srodrigc /* 4784159451Srodrigc * IGSTATE flag is used to combine extents which 4785153323Srodrigc * differ only due to the state of the extents. 4786153323Srodrigc * This technique is used from xfs_getbmap() 4787153323Srodrigc * when the caller does not wish to see the 4788153323Srodrigc * separation (which is the default). 4789153323Srodrigc * 4790153323Srodrigc * This technique is also used when writing a 4791153323Srodrigc * buffer which has been partially written, 4792153323Srodrigc * (usually by being flushed during a chunkread), 4793153323Srodrigc * to ensure one write takes place. This also 4794153323Srodrigc * prevents a change in the xfs inode extents at 4795153323Srodrigc * this time, intentionally. This change occurs 4796153323Srodrigc * on completion of the write operation, in 4797153323Srodrigc * xfs_strat_comp(), where the xfs_bmapi() call 4798153323Srodrigc * is transactioned, and the extents combined. 4799153323Srodrigc */ 4800159451Srodrigc if ((flags & XFS_BMAPI_IGSTATE) && wr) /* if writing unwritten space */ 4801159451Srodrigc wr = 0; /* no allocations are allowed */ 4802159451Srodrigc ASSERT(wr || !(flags & XFS_BMAPI_DELAY)); 4803153323Srodrigc logflags = 0; 4804153323Srodrigc nallocs = 0; 4805153323Srodrigc cur = NULL; 4806153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) { 4807153323Srodrigc ASSERT(wr && tp); 4808153323Srodrigc if ((error = xfs_bmap_local_to_extents(tp, ip, 4809153323Srodrigc firstblock, total, &logflags, whichfork))) 4810153323Srodrigc goto error0; 4811153323Srodrigc } 4812153323Srodrigc if (wr && *firstblock == NULLFSBLOCK) { 4813153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE) 4814159451Srodrigc minleft = be16_to_cpu(ifp->if_broot->bb_level) + 1; 4815153323Srodrigc else 4816153323Srodrigc minleft = 1; 4817153323Srodrigc } else 4818153323Srodrigc minleft = 0; 4819153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 4820153323Srodrigc (error = xfs_iread_extents(tp, ip, whichfork))) 4821153323Srodrigc goto error0; 4822153323Srodrigc ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, 4823153323Srodrigc &prev); 4824153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 4825153323Srodrigc n = 0; 4826153323Srodrigc end = bno + len; 4827153323Srodrigc obno = bno; 4828153323Srodrigc bma.ip = NULL; 4829159451Srodrigc if (delta) { 4830159451Srodrigc delta->xed_startoff = NULLFILEOFF; 4831159451Srodrigc delta->xed_blockcount = 0; 4832159451Srodrigc } 4833153323Srodrigc while (bno < end && n < *nmap) { 4834153323Srodrigc /* 4835153323Srodrigc * Reading past eof, act as though there's a hole 4836153323Srodrigc * up to end. 4837153323Srodrigc */ 4838153323Srodrigc if (eof && !wr) 4839153323Srodrigc got.br_startoff = end; 4840153323Srodrigc inhole = eof || got.br_startoff > bno; 4841159451Srodrigc wasdelay = wr && !inhole && !(flags & XFS_BMAPI_DELAY) && 4842153323Srodrigc ISNULLSTARTBLOCK(got.br_startblock); 4843153323Srodrigc /* 4844153323Srodrigc * First, deal with the hole before the allocated space 4845153323Srodrigc * that we found, if any. 4846153323Srodrigc */ 4847153323Srodrigc if (wr && (inhole || wasdelay)) { 4848153323Srodrigc /* 4849153323Srodrigc * For the wasdelay case, we could also just 4850153323Srodrigc * allocate the stuff asked for in this bmap call 4851153323Srodrigc * but that wouldn't be as good. 4852153323Srodrigc */ 4853159451Srodrigc if (wasdelay && !(flags & XFS_BMAPI_EXACT)) { 4854153323Srodrigc alen = (xfs_extlen_t)got.br_blockcount; 4855153323Srodrigc aoff = got.br_startoff; 4856153323Srodrigc if (lastx != NULLEXTNUM && lastx) { 4857159451Srodrigc ep = xfs_iext_get_ext(ifp, lastx - 1); 4858153323Srodrigc xfs_bmbt_get_all(ep, &prev); 4859153323Srodrigc } 4860153323Srodrigc } else if (wasdelay) { 4861153323Srodrigc alen = (xfs_extlen_t) 4862153323Srodrigc XFS_FILBLKS_MIN(len, 4863153323Srodrigc (got.br_startoff + 4864153323Srodrigc got.br_blockcount) - bno); 4865153323Srodrigc aoff = bno; 4866153323Srodrigc } else { 4867153323Srodrigc alen = (xfs_extlen_t) 4868153323Srodrigc XFS_FILBLKS_MIN(len, MAXEXTLEN); 4869153323Srodrigc if (!eof) 4870153323Srodrigc alen = (xfs_extlen_t) 4871153323Srodrigc XFS_FILBLKS_MIN(alen, 4872153323Srodrigc got.br_startoff - bno); 4873153323Srodrigc aoff = bno; 4874153323Srodrigc } 4875159451Srodrigc minlen = (flags & XFS_BMAPI_CONTIG) ? alen : 1; 4876159451Srodrigc if (flags & XFS_BMAPI_DELAY) { 4877159451Srodrigc xfs_extlen_t extsz; 4878159451Srodrigc 4879159451Srodrigc /* Figure out the extent size, adjust alen */ 4880159451Srodrigc if (rt) { 4881159451Srodrigc if (!(extsz = ip->i_d.di_extsize)) 4882159451Srodrigc extsz = mp->m_sb.sb_rextsize; 4883159451Srodrigc } else { 4884159451Srodrigc extsz = ip->i_d.di_extsize; 4885159451Srodrigc } 4886159451Srodrigc if (extsz) { 4887159451Srodrigc error = xfs_bmap_extsize_align(mp, 4888159451Srodrigc &got, &prev, extsz, 4889159451Srodrigc rt, eof, 4890159451Srodrigc flags&XFS_BMAPI_DELAY, 4891159451Srodrigc flags&XFS_BMAPI_CONVERT, 4892159451Srodrigc &aoff, &alen); 4893159451Srodrigc ASSERT(!error); 4894159451Srodrigc } 4895159451Srodrigc 4896159451Srodrigc if (rt) 4897159451Srodrigc extsz = alen / mp->m_sb.sb_rextsize; 4898159451Srodrigc 4899153323Srodrigc /* 4900153323Srodrigc * Make a transaction-less quota reservation for 4901153323Srodrigc * delayed allocation blocks. This number gets 4902159451Srodrigc * adjusted later. We return if we haven't 4903159451Srodrigc * allocated blocks already inside this loop. 4904153323Srodrigc */ 4905159451Srodrigc if ((error = XFS_TRANS_RESERVE_QUOTA_NBLKS( 4906159451Srodrigc mp, NULL, ip, (long)alen, 0, 4907159451Srodrigc rt ? XFS_QMOPT_RES_RTBLKS : 4908159451Srodrigc XFS_QMOPT_RES_REGBLKS))) { 4909153323Srodrigc if (n == 0) { 4910153323Srodrigc *nmap = 0; 4911153323Srodrigc ASSERT(cur == NULL); 4912159451Srodrigc return error; 4913153323Srodrigc } 4914153323Srodrigc break; 4915153323Srodrigc } 4916159451Srodrigc 4917159451Srodrigc /* 4918159451Srodrigc * Split changing sb for alen and indlen since 4919159451Srodrigc * they could be coming from different places. 4920159451Srodrigc */ 4921159451Srodrigc indlen = (xfs_extlen_t) 4922159451Srodrigc xfs_bmap_worst_indlen(ip, alen); 4923159451Srodrigc ASSERT(indlen > 0); 4924159451Srodrigc 4925159451Srodrigc if (rt) { 4926159451Srodrigc error = xfs_mod_incore_sb(mp, 4927159451Srodrigc XFS_SBS_FREXTENTS, 4928159451Srodrigc -(extsz), (flags & 4929159451Srodrigc XFS_BMAPI_RSVBLOCKS)); 4930159451Srodrigc } else { 4931159451Srodrigc error = xfs_mod_incore_sb(mp, 4932159451Srodrigc XFS_SBS_FDBLOCKS, 4933159451Srodrigc -(alen), (flags & 4934159451Srodrigc XFS_BMAPI_RSVBLOCKS)); 4935159451Srodrigc } 4936159451Srodrigc if (!error) { 4937159451Srodrigc error = xfs_mod_incore_sb(mp, 4938159451Srodrigc XFS_SBS_FDBLOCKS, 4939159451Srodrigc -(indlen), (flags & 4940159451Srodrigc XFS_BMAPI_RSVBLOCKS)); 4941159451Srodrigc if (error && rt) 4942159451Srodrigc xfs_mod_incore_sb(mp, 4943159451Srodrigc XFS_SBS_FREXTENTS, 4944159451Srodrigc extsz, (flags & 4945159451Srodrigc XFS_BMAPI_RSVBLOCKS)); 4946159451Srodrigc else if (error) 4947159451Srodrigc xfs_mod_incore_sb(mp, 4948159451Srodrigc XFS_SBS_FDBLOCKS, 4949159451Srodrigc alen, (flags & 4950159451Srodrigc XFS_BMAPI_RSVBLOCKS)); 4951159451Srodrigc } 4952159451Srodrigc 4953159451Srodrigc if (error) { 4954159451Srodrigc if (XFS_IS_QUOTA_ON(mp)) 4955159451Srodrigc /* unreserve the blocks now */ 4956159451Srodrigc (void) 4957159451Srodrigc XFS_TRANS_UNRESERVE_QUOTA_NBLKS( 4958159451Srodrigc mp, NULL, ip, 4959159451Srodrigc (long)alen, 0, rt ? 4960159451Srodrigc XFS_QMOPT_RES_RTBLKS : 4961159451Srodrigc XFS_QMOPT_RES_REGBLKS); 4962153323Srodrigc break; 4963153323Srodrigc } 4964159451Srodrigc 4965153323Srodrigc ip->i_delayed_blks += alen; 4966153323Srodrigc abno = NULLSTARTBLOCK(indlen); 4967153323Srodrigc } else { 4968153323Srodrigc /* 4969153323Srodrigc * If first time, allocate and fill in 4970153323Srodrigc * once-only bma fields. 4971153323Srodrigc */ 4972153323Srodrigc if (bma.ip == NULL) { 4973153323Srodrigc bma.tp = tp; 4974153323Srodrigc bma.ip = ip; 4975153323Srodrigc bma.prevp = &prev; 4976153323Srodrigc bma.gotp = &got; 4977153323Srodrigc bma.total = total; 4978153323Srodrigc bma.userdata = 0; 4979153323Srodrigc } 4980153323Srodrigc /* Indicate if this is the first user data 4981153323Srodrigc * in the file, or just any user data. 4982153323Srodrigc */ 4983159451Srodrigc if (!(flags & XFS_BMAPI_METADATA)) { 4984153323Srodrigc bma.userdata = (aoff == 0) ? 4985153323Srodrigc XFS_ALLOC_INITIAL_USER_DATA : 4986153323Srodrigc XFS_ALLOC_USERDATA; 4987153323Srodrigc } 4988153323Srodrigc /* 4989153323Srodrigc * Fill in changeable bma fields. 4990153323Srodrigc */ 4991153323Srodrigc bma.eof = eof; 4992153323Srodrigc bma.firstblock = *firstblock; 4993153323Srodrigc bma.alen = alen; 4994153323Srodrigc bma.off = aoff; 4995170124Skan bma.conv = (flags & XFS_BMAPI_CONVERT) != 0; 4996153323Srodrigc bma.wasdel = wasdelay; 4997153323Srodrigc bma.minlen = minlen; 4998153323Srodrigc bma.low = flist->xbf_low; 4999153323Srodrigc bma.minleft = minleft; 5000153323Srodrigc /* 5001153323Srodrigc * Only want to do the alignment at the 5002153323Srodrigc * eof if it is userdata and allocation length 5003153323Srodrigc * is larger than a stripe unit. 5004153323Srodrigc */ 5005153323Srodrigc if (mp->m_dalign && alen >= mp->m_dalign && 5006159451Srodrigc (!(flags & XFS_BMAPI_METADATA)) && 5007159451Srodrigc (whichfork == XFS_DATA_FORK)) { 5008153323Srodrigc if ((error = xfs_bmap_isaeof(ip, aoff, 5009153323Srodrigc whichfork, &bma.aeof))) 5010153323Srodrigc goto error0; 5011153323Srodrigc } else 5012153323Srodrigc bma.aeof = 0; 5013153323Srodrigc /* 5014153323Srodrigc * Call allocator. 5015153323Srodrigc */ 5016153323Srodrigc if ((error = xfs_bmap_alloc(&bma))) 5017153323Srodrigc goto error0; 5018153323Srodrigc /* 5019153323Srodrigc * Copy out result fields. 5020153323Srodrigc */ 5021153323Srodrigc abno = bma.rval; 5022153323Srodrigc if ((flist->xbf_low = bma.low)) 5023153323Srodrigc minleft = 0; 5024153323Srodrigc alen = bma.alen; 5025153323Srodrigc aoff = bma.off; 5026153323Srodrigc ASSERT(*firstblock == NULLFSBLOCK || 5027153323Srodrigc XFS_FSB_TO_AGNO(mp, *firstblock) == 5028153323Srodrigc XFS_FSB_TO_AGNO(mp, bma.firstblock) || 5029153323Srodrigc (flist->xbf_low && 5030153323Srodrigc XFS_FSB_TO_AGNO(mp, *firstblock) < 5031153323Srodrigc XFS_FSB_TO_AGNO(mp, bma.firstblock))); 5032153323Srodrigc *firstblock = bma.firstblock; 5033153323Srodrigc if (cur) 5034153323Srodrigc cur->bc_private.b.firstblock = 5035153323Srodrigc *firstblock; 5036153323Srodrigc if (abno == NULLFSBLOCK) 5037153323Srodrigc break; 5038153323Srodrigc if ((ifp->if_flags & XFS_IFBROOT) && !cur) { 5039153323Srodrigc cur = xfs_btree_init_cursor(mp, 5040153323Srodrigc tp, NULL, 0, XFS_BTNUM_BMAP, 5041153323Srodrigc ip, whichfork); 5042153323Srodrigc cur->bc_private.b.firstblock = 5043153323Srodrigc *firstblock; 5044153323Srodrigc cur->bc_private.b.flist = flist; 5045153323Srodrigc } 5046153323Srodrigc /* 5047153323Srodrigc * Bump the number of extents we've allocated 5048153323Srodrigc * in this call. 5049153323Srodrigc */ 5050153323Srodrigc nallocs++; 5051153323Srodrigc } 5052153323Srodrigc if (cur) 5053153323Srodrigc cur->bc_private.b.flags = 5054153323Srodrigc wasdelay ? XFS_BTCUR_BPRV_WASDEL : 0; 5055153323Srodrigc got.br_startoff = aoff; 5056153323Srodrigc got.br_startblock = abno; 5057153323Srodrigc got.br_blockcount = alen; 5058153323Srodrigc got.br_state = XFS_EXT_NORM; /* assume normal */ 5059153323Srodrigc /* 5060153323Srodrigc * Determine state of extent, and the filesystem. 5061153323Srodrigc * A wasdelay extent has been initialized, so 5062153323Srodrigc * shouldn't be flagged as unwritten. 5063153323Srodrigc */ 5064153323Srodrigc if (wr && XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) { 5065153323Srodrigc if (!wasdelay && (flags & XFS_BMAPI_PREALLOC)) 5066153323Srodrigc got.br_state = XFS_EXT_UNWRITTEN; 5067153323Srodrigc } 5068153323Srodrigc error = xfs_bmap_add_extent(ip, lastx, &cur, &got, 5069159451Srodrigc firstblock, flist, &tmp_logflags, delta, 5070159451Srodrigc whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); 5071153323Srodrigc logflags |= tmp_logflags; 5072153323Srodrigc if (error) 5073153323Srodrigc goto error0; 5074153323Srodrigc lastx = ifp->if_lastex; 5075159451Srodrigc ep = xfs_iext_get_ext(ifp, lastx); 5076153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 5077153323Srodrigc xfs_bmbt_get_all(ep, &got); 5078153323Srodrigc ASSERT(got.br_startoff <= aoff); 5079153323Srodrigc ASSERT(got.br_startoff + got.br_blockcount >= 5080153323Srodrigc aoff + alen); 5081153323Srodrigc#ifdef DEBUG 5082159451Srodrigc if (flags & XFS_BMAPI_DELAY) { 5083153323Srodrigc ASSERT(ISNULLSTARTBLOCK(got.br_startblock)); 5084153323Srodrigc ASSERT(STARTBLOCKVAL(got.br_startblock) > 0); 5085153323Srodrigc } 5086153323Srodrigc ASSERT(got.br_state == XFS_EXT_NORM || 5087153323Srodrigc got.br_state == XFS_EXT_UNWRITTEN); 5088153323Srodrigc#endif 5089153323Srodrigc /* 5090153323Srodrigc * Fall down into the found allocated space case. 5091153323Srodrigc */ 5092153323Srodrigc } else if (inhole) { 5093153323Srodrigc /* 5094153323Srodrigc * Reading in a hole. 5095153323Srodrigc */ 5096153323Srodrigc mval->br_startoff = bno; 5097153323Srodrigc mval->br_startblock = HOLESTARTBLOCK; 5098153323Srodrigc mval->br_blockcount = 5099153323Srodrigc XFS_FILBLKS_MIN(len, got.br_startoff - bno); 5100153323Srodrigc mval->br_state = XFS_EXT_NORM; 5101153323Srodrigc bno += mval->br_blockcount; 5102153323Srodrigc len -= mval->br_blockcount; 5103153323Srodrigc mval++; 5104153323Srodrigc n++; 5105153323Srodrigc continue; 5106153323Srodrigc } 5107153323Srodrigc /* 5108153323Srodrigc * Then deal with the allocated space we found. 5109153323Srodrigc */ 5110153323Srodrigc ASSERT(ep != NULL); 5111159451Srodrigc if (!(flags & XFS_BMAPI_ENTIRE) && 5112159451Srodrigc (got.br_startoff + got.br_blockcount > obno)) { 5113153323Srodrigc if (obno > bno) 5114153323Srodrigc bno = obno; 5115153323Srodrigc ASSERT((bno >= obno) || (n == 0)); 5116153323Srodrigc ASSERT(bno < end); 5117153323Srodrigc mval->br_startoff = bno; 5118153323Srodrigc if (ISNULLSTARTBLOCK(got.br_startblock)) { 5119159451Srodrigc ASSERT(!wr || (flags & XFS_BMAPI_DELAY)); 5120153323Srodrigc mval->br_startblock = DELAYSTARTBLOCK; 5121153323Srodrigc } else 5122153323Srodrigc mval->br_startblock = 5123153323Srodrigc got.br_startblock + 5124153323Srodrigc (bno - got.br_startoff); 5125153323Srodrigc /* 5126153323Srodrigc * Return the minimum of what we got and what we 5127153323Srodrigc * asked for for the length. We can use the len 5128153323Srodrigc * variable here because it is modified below 5129153323Srodrigc * and we could have been there before coming 5130153323Srodrigc * here if the first part of the allocation 5131153323Srodrigc * didn't overlap what was asked for. 5132153323Srodrigc */ 5133153323Srodrigc mval->br_blockcount = 5134153323Srodrigc XFS_FILBLKS_MIN(end - bno, got.br_blockcount - 5135153323Srodrigc (bno - got.br_startoff)); 5136153323Srodrigc mval->br_state = got.br_state; 5137153323Srodrigc ASSERT(mval->br_blockcount <= len); 5138153323Srodrigc } else { 5139153323Srodrigc *mval = got; 5140153323Srodrigc if (ISNULLSTARTBLOCK(mval->br_startblock)) { 5141159451Srodrigc ASSERT(!wr || (flags & XFS_BMAPI_DELAY)); 5142153323Srodrigc mval->br_startblock = DELAYSTARTBLOCK; 5143153323Srodrigc } 5144153323Srodrigc } 5145153323Srodrigc 5146153323Srodrigc /* 5147153323Srodrigc * Check if writing previously allocated but 5148153323Srodrigc * unwritten extents. 5149153323Srodrigc */ 5150153323Srodrigc if (wr && mval->br_state == XFS_EXT_UNWRITTEN && 5151153323Srodrigc ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_DELAY)) == 0)) { 5152153323Srodrigc /* 5153153323Srodrigc * Modify (by adding) the state flag, if writing. 5154153323Srodrigc */ 5155153323Srodrigc ASSERT(mval->br_blockcount <= len); 5156153323Srodrigc if ((ifp->if_flags & XFS_IFBROOT) && !cur) { 5157153323Srodrigc cur = xfs_btree_init_cursor(mp, 5158153323Srodrigc tp, NULL, 0, XFS_BTNUM_BMAP, 5159153323Srodrigc ip, whichfork); 5160153323Srodrigc cur->bc_private.b.firstblock = 5161153323Srodrigc *firstblock; 5162153323Srodrigc cur->bc_private.b.flist = flist; 5163153323Srodrigc } 5164153323Srodrigc mval->br_state = XFS_EXT_NORM; 5165153323Srodrigc error = xfs_bmap_add_extent(ip, lastx, &cur, mval, 5166159451Srodrigc firstblock, flist, &tmp_logflags, delta, 5167159451Srodrigc whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); 5168153323Srodrigc logflags |= tmp_logflags; 5169153323Srodrigc if (error) 5170153323Srodrigc goto error0; 5171153323Srodrigc lastx = ifp->if_lastex; 5172159451Srodrigc ep = xfs_iext_get_ext(ifp, lastx); 5173153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 5174153323Srodrigc xfs_bmbt_get_all(ep, &got); 5175153323Srodrigc /* 5176153323Srodrigc * We may have combined previously unwritten 5177153323Srodrigc * space with written space, so generate 5178153323Srodrigc * another request. 5179153323Srodrigc */ 5180153323Srodrigc if (mval->br_blockcount < len) 5181153323Srodrigc continue; 5182153323Srodrigc } 5183153323Srodrigc 5184159451Srodrigc ASSERT((flags & XFS_BMAPI_ENTIRE) || 5185153323Srodrigc ((mval->br_startoff + mval->br_blockcount) <= end)); 5186159451Srodrigc ASSERT((flags & XFS_BMAPI_ENTIRE) || 5187159451Srodrigc (mval->br_blockcount <= len) || 5188153323Srodrigc (mval->br_startoff < obno)); 5189153323Srodrigc bno = mval->br_startoff + mval->br_blockcount; 5190153323Srodrigc len = end - bno; 5191153323Srodrigc if (n > 0 && mval->br_startoff == mval[-1].br_startoff) { 5192153323Srodrigc ASSERT(mval->br_startblock == mval[-1].br_startblock); 5193153323Srodrigc ASSERT(mval->br_blockcount > mval[-1].br_blockcount); 5194153323Srodrigc ASSERT(mval->br_state == mval[-1].br_state); 5195153323Srodrigc mval[-1].br_blockcount = mval->br_blockcount; 5196153323Srodrigc mval[-1].br_state = mval->br_state; 5197153323Srodrigc } else if (n > 0 && mval->br_startblock != DELAYSTARTBLOCK && 5198153323Srodrigc mval[-1].br_startblock != DELAYSTARTBLOCK && 5199153323Srodrigc mval[-1].br_startblock != HOLESTARTBLOCK && 5200153323Srodrigc mval->br_startblock == 5201153323Srodrigc mval[-1].br_startblock + mval[-1].br_blockcount && 5202159451Srodrigc ((flags & XFS_BMAPI_IGSTATE) || 5203159451Srodrigc mval[-1].br_state == mval->br_state)) { 5204153323Srodrigc ASSERT(mval->br_startoff == 5205153323Srodrigc mval[-1].br_startoff + mval[-1].br_blockcount); 5206153323Srodrigc mval[-1].br_blockcount += mval->br_blockcount; 5207153323Srodrigc } else if (n > 0 && 5208153323Srodrigc mval->br_startblock == DELAYSTARTBLOCK && 5209153323Srodrigc mval[-1].br_startblock == DELAYSTARTBLOCK && 5210153323Srodrigc mval->br_startoff == 5211153323Srodrigc mval[-1].br_startoff + mval[-1].br_blockcount) { 5212153323Srodrigc mval[-1].br_blockcount += mval->br_blockcount; 5213153323Srodrigc mval[-1].br_state = mval->br_state; 5214153323Srodrigc } else if (!((n == 0) && 5215153323Srodrigc ((mval->br_startoff + mval->br_blockcount) <= 5216153323Srodrigc obno))) { 5217153323Srodrigc mval++; 5218153323Srodrigc n++; 5219153323Srodrigc } 5220153323Srodrigc /* 5221153323Srodrigc * If we're done, stop now. Stop when we've allocated 5222153323Srodrigc * XFS_BMAP_MAX_NMAP extents no matter what. Otherwise 5223153323Srodrigc * the transaction may get too big. 5224153323Srodrigc */ 5225153323Srodrigc if (bno >= end || n >= *nmap || nallocs >= *nmap) 5226153323Srodrigc break; 5227153323Srodrigc /* 5228153323Srodrigc * Else go on to the next record. 5229153323Srodrigc */ 5230159451Srodrigc ep = xfs_iext_get_ext(ifp, ++lastx); 5231153323Srodrigc if (lastx >= nextents) { 5232153323Srodrigc eof = 1; 5233153323Srodrigc prev = got; 5234153323Srodrigc } else 5235153323Srodrigc xfs_bmbt_get_all(ep, &got); 5236153323Srodrigc } 5237153323Srodrigc ifp->if_lastex = lastx; 5238153323Srodrigc *nmap = n; 5239153323Srodrigc /* 5240153323Srodrigc * Transform from btree to extents, give it cur. 5241153323Srodrigc */ 5242153323Srodrigc if (tp && XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE && 5243153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max) { 5244153323Srodrigc ASSERT(wr && cur); 5245153323Srodrigc error = xfs_bmap_btree_to_extents(tp, ip, cur, 5246153323Srodrigc &tmp_logflags, whichfork); 5247153323Srodrigc logflags |= tmp_logflags; 5248153323Srodrigc if (error) 5249153323Srodrigc goto error0; 5250153323Srodrigc } 5251153323Srodrigc ASSERT(ifp->if_ext_max == 5252153323Srodrigc XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); 5253153323Srodrigc ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE || 5254153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max); 5255153323Srodrigc error = 0; 5256159451Srodrigc if (delta && delta->xed_startoff != NULLFILEOFF) { 5257159451Srodrigc /* A change was actually made. 5258159451Srodrigc * Note that delta->xed_blockount is an offset at this 5259159451Srodrigc * point and needs to be converted to a block count. 5260159451Srodrigc */ 5261159451Srodrigc ASSERT(delta->xed_blockcount > delta->xed_startoff); 5262159451Srodrigc delta->xed_blockcount -= delta->xed_startoff; 5263159451Srodrigc } 5264153323Srodrigcerror0: 5265153323Srodrigc /* 5266153323Srodrigc * Log everything. Do this after conversion, there's no point in 5267159451Srodrigc * logging the extent records if we've converted to btree format. 5268153323Srodrigc */ 5269153323Srodrigc if ((logflags & XFS_ILOG_FEXT(whichfork)) && 5270153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS) 5271153323Srodrigc logflags &= ~XFS_ILOG_FEXT(whichfork); 5272153323Srodrigc else if ((logflags & XFS_ILOG_FBROOT(whichfork)) && 5273153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE) 5274153323Srodrigc logflags &= ~XFS_ILOG_FBROOT(whichfork); 5275153323Srodrigc /* 5276153323Srodrigc * Log whatever the flags say, even if error. Otherwise we might miss 5277153323Srodrigc * detecting a case where the data is changed, there's an error, 5278153323Srodrigc * and it's not logged so we don't shutdown when we should. 5279153323Srodrigc */ 5280153323Srodrigc if (logflags) { 5281153323Srodrigc ASSERT(tp && wr); 5282153323Srodrigc xfs_trans_log_inode(tp, ip, logflags); 5283153323Srodrigc } 5284153323Srodrigc if (cur) { 5285153323Srodrigc if (!error) { 5286153323Srodrigc ASSERT(*firstblock == NULLFSBLOCK || 5287153323Srodrigc XFS_FSB_TO_AGNO(mp, *firstblock) == 5288153323Srodrigc XFS_FSB_TO_AGNO(mp, 5289153323Srodrigc cur->bc_private.b.firstblock) || 5290153323Srodrigc (flist->xbf_low && 5291153323Srodrigc XFS_FSB_TO_AGNO(mp, *firstblock) < 5292153323Srodrigc XFS_FSB_TO_AGNO(mp, 5293153323Srodrigc cur->bc_private.b.firstblock))); 5294153323Srodrigc *firstblock = cur->bc_private.b.firstblock; 5295153323Srodrigc } 5296153323Srodrigc xfs_btree_del_cursor(cur, 5297153323Srodrigc error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); 5298153323Srodrigc } 5299153323Srodrigc if (!error) 5300153323Srodrigc xfs_bmap_validate_ret(orig_bno, orig_len, orig_flags, orig_mval, 5301153323Srodrigc orig_nmap, *nmap); 5302153323Srodrigc return error; 5303153323Srodrigc} 5304153323Srodrigc 5305153323Srodrigc/* 5306153323Srodrigc * Map file blocks to filesystem blocks, simple version. 5307153323Srodrigc * One block (extent) only, read-only. 5308153323Srodrigc * For flags, only the XFS_BMAPI_ATTRFORK flag is examined. 5309153323Srodrigc * For the other flag values, the effect is as if XFS_BMAPI_METADATA 5310153323Srodrigc * was set and all the others were clear. 5311153323Srodrigc */ 5312153323Srodrigcint /* error */ 5313153323Srodrigcxfs_bmapi_single( 5314153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 5315153323Srodrigc xfs_inode_t *ip, /* incore inode */ 5316153323Srodrigc int whichfork, /* data or attr fork */ 5317153323Srodrigc xfs_fsblock_t *fsb, /* output: mapped block */ 5318153323Srodrigc xfs_fileoff_t bno) /* starting file offs. mapped */ 5319153323Srodrigc{ 5320159451Srodrigc int eof; /* we've hit the end of extents */ 5321153323Srodrigc int error; /* error return */ 5322159451Srodrigc xfs_bmbt_irec_t got; /* current file extent record */ 5323153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 5324153323Srodrigc xfs_extnum_t lastx; /* last useful extent number */ 5325159451Srodrigc xfs_bmbt_irec_t prev; /* previous file extent record */ 5326153323Srodrigc 5327153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 5328153323Srodrigc if (unlikely( 5329153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE && 5330153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS)) { 5331153323Srodrigc XFS_ERROR_REPORT("xfs_bmapi_single", XFS_ERRLEVEL_LOW, 5332153323Srodrigc ip->i_mount); 5333153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 5334153323Srodrigc } 5335153323Srodrigc if (XFS_FORCED_SHUTDOWN(ip->i_mount)) 5336153323Srodrigc return XFS_ERROR(EIO); 5337153323Srodrigc XFS_STATS_INC(xs_blk_mapr); 5338153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 5339153323Srodrigc (error = xfs_iread_extents(tp, ip, whichfork))) 5340153323Srodrigc return error; 5341153323Srodrigc (void)xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, 5342153323Srodrigc &prev); 5343153323Srodrigc /* 5344153323Srodrigc * Reading past eof, act as though there's a hole 5345153323Srodrigc * up to end. 5346153323Srodrigc */ 5347153323Srodrigc if (eof || got.br_startoff > bno) { 5348153323Srodrigc *fsb = NULLFSBLOCK; 5349153323Srodrigc return 0; 5350153323Srodrigc } 5351153323Srodrigc ASSERT(!ISNULLSTARTBLOCK(got.br_startblock)); 5352153323Srodrigc ASSERT(bno < got.br_startoff + got.br_blockcount); 5353153323Srodrigc *fsb = got.br_startblock + (bno - got.br_startoff); 5354153323Srodrigc ifp->if_lastex = lastx; 5355153323Srodrigc return 0; 5356153323Srodrigc} 5357153323Srodrigc 5358153323Srodrigc/* 5359153323Srodrigc * Unmap (remove) blocks from a file. 5360153323Srodrigc * If nexts is nonzero then the number of extents to remove is limited to 5361153323Srodrigc * that value. If not all extents in the block range can be removed then 5362153323Srodrigc * *done is set. 5363153323Srodrigc */ 5364153323Srodrigcint /* error */ 5365153323Srodrigcxfs_bunmapi( 5366153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 5367153323Srodrigc struct xfs_inode *ip, /* incore inode */ 5368153323Srodrigc xfs_fileoff_t bno, /* starting offset to unmap */ 5369153323Srodrigc xfs_filblks_t len, /* length to unmap in file */ 5370153323Srodrigc int flags, /* misc flags */ 5371153323Srodrigc xfs_extnum_t nexts, /* number of extents max */ 5372153323Srodrigc xfs_fsblock_t *firstblock, /* first allocated block 5373153323Srodrigc controls a.g. for allocs */ 5374153323Srodrigc xfs_bmap_free_t *flist, /* i/o: list extents to free */ 5375159451Srodrigc xfs_extdelta_t *delta, /* o: change made to incore 5376159451Srodrigc extents */ 5377153323Srodrigc int *done) /* set if not done yet */ 5378153323Srodrigc{ 5379153323Srodrigc xfs_btree_cur_t *cur; /* bmap btree cursor */ 5380153323Srodrigc xfs_bmbt_irec_t del; /* extent being deleted */ 5381153323Srodrigc int eof; /* is deleting at eof */ 5382159451Srodrigc xfs_bmbt_rec_t *ep; /* extent record pointer */ 5383153323Srodrigc int error; /* error return value */ 5384153323Srodrigc xfs_extnum_t extno; /* extent number in list */ 5385159451Srodrigc xfs_bmbt_irec_t got; /* current extent record */ 5386153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 5387153323Srodrigc int isrt; /* freeing in rt area */ 5388153323Srodrigc xfs_extnum_t lastx; /* last extent index used */ 5389153323Srodrigc int logflags; /* transaction logging flags */ 5390153323Srodrigc xfs_extlen_t mod; /* rt extent offset */ 5391153323Srodrigc xfs_mount_t *mp; /* mount structure */ 5392159451Srodrigc xfs_extnum_t nextents; /* number of file extents */ 5393159451Srodrigc xfs_bmbt_irec_t prev; /* previous extent record */ 5394153323Srodrigc xfs_fileoff_t start; /* first file offset deleted */ 5395153323Srodrigc int tmp_logflags; /* partial logging flags */ 5396153323Srodrigc int wasdel; /* was a delayed alloc extent */ 5397153323Srodrigc int whichfork; /* data or attribute fork */ 5398153323Srodrigc int rsvd; /* OK to allocate reserved blocks */ 5399153323Srodrigc xfs_fsblock_t sum; 5400153323Srodrigc 5401153323Srodrigc xfs_bunmap_trace(ip, bno, len, flags, (inst_t *)__return_address); 5402153323Srodrigc whichfork = (flags & XFS_BMAPI_ATTRFORK) ? 5403153323Srodrigc XFS_ATTR_FORK : XFS_DATA_FORK; 5404153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 5405153323Srodrigc if (unlikely( 5406153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && 5407153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) { 5408153323Srodrigc XFS_ERROR_REPORT("xfs_bunmapi", XFS_ERRLEVEL_LOW, 5409153323Srodrigc ip->i_mount); 5410153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 5411153323Srodrigc } 5412153323Srodrigc mp = ip->i_mount; 5413153323Srodrigc if (XFS_FORCED_SHUTDOWN(mp)) 5414153323Srodrigc return XFS_ERROR(EIO); 5415153323Srodrigc rsvd = (flags & XFS_BMAPI_RSVBLOCKS) != 0; 5416153323Srodrigc ASSERT(len > 0); 5417153323Srodrigc ASSERT(nexts >= 0); 5418153323Srodrigc ASSERT(ifp->if_ext_max == 5419153323Srodrigc XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); 5420153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 5421153323Srodrigc (error = xfs_iread_extents(tp, ip, whichfork))) 5422153323Srodrigc return error; 5423153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 5424153323Srodrigc if (nextents == 0) { 5425153323Srodrigc *done = 1; 5426153323Srodrigc return 0; 5427153323Srodrigc } 5428153323Srodrigc XFS_STATS_INC(xs_blk_unmap); 5429159451Srodrigc isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); 5430153323Srodrigc start = bno; 5431153323Srodrigc bno = start + len - 1; 5432153323Srodrigc ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, 5433153323Srodrigc &prev); 5434159451Srodrigc if (delta) { 5435159451Srodrigc delta->xed_startoff = NULLFILEOFF; 5436159451Srodrigc delta->xed_blockcount = 0; 5437159451Srodrigc } 5438153323Srodrigc /* 5439153323Srodrigc * Check to see if the given block number is past the end of the 5440153323Srodrigc * file, back up to the last block if so... 5441153323Srodrigc */ 5442153323Srodrigc if (eof) { 5443159451Srodrigc ep = xfs_iext_get_ext(ifp, --lastx); 5444153323Srodrigc xfs_bmbt_get_all(ep, &got); 5445153323Srodrigc bno = got.br_startoff + got.br_blockcount - 1; 5446153323Srodrigc } 5447153323Srodrigc logflags = 0; 5448153323Srodrigc if (ifp->if_flags & XFS_IFBROOT) { 5449153323Srodrigc ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE); 5450153323Srodrigc cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip, 5451153323Srodrigc whichfork); 5452153323Srodrigc cur->bc_private.b.firstblock = *firstblock; 5453153323Srodrigc cur->bc_private.b.flist = flist; 5454153323Srodrigc cur->bc_private.b.flags = 0; 5455153323Srodrigc } else 5456153323Srodrigc cur = NULL; 5457153323Srodrigc extno = 0; 5458153323Srodrigc while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 && 5459153323Srodrigc (nexts == 0 || extno < nexts)) { 5460153323Srodrigc /* 5461153323Srodrigc * Is the found extent after a hole in which bno lives? 5462153323Srodrigc * Just back up to the previous extent, if so. 5463153323Srodrigc */ 5464153323Srodrigc if (got.br_startoff > bno) { 5465153323Srodrigc if (--lastx < 0) 5466153323Srodrigc break; 5467159451Srodrigc ep = xfs_iext_get_ext(ifp, lastx); 5468153323Srodrigc xfs_bmbt_get_all(ep, &got); 5469153323Srodrigc } 5470153323Srodrigc /* 5471153323Srodrigc * Is the last block of this extent before the range 5472153323Srodrigc * we're supposed to delete? If so, we're done. 5473153323Srodrigc */ 5474153323Srodrigc bno = XFS_FILEOFF_MIN(bno, 5475153323Srodrigc got.br_startoff + got.br_blockcount - 1); 5476153323Srodrigc if (bno < start) 5477153323Srodrigc break; 5478153323Srodrigc /* 5479153323Srodrigc * Then deal with the (possibly delayed) allocated space 5480153323Srodrigc * we found. 5481153323Srodrigc */ 5482153323Srodrigc ASSERT(ep != NULL); 5483153323Srodrigc del = got; 5484153323Srodrigc wasdel = ISNULLSTARTBLOCK(del.br_startblock); 5485153323Srodrigc if (got.br_startoff < start) { 5486153323Srodrigc del.br_startoff = start; 5487153323Srodrigc del.br_blockcount -= start - got.br_startoff; 5488153323Srodrigc if (!wasdel) 5489153323Srodrigc del.br_startblock += start - got.br_startoff; 5490153323Srodrigc } 5491153323Srodrigc if (del.br_startoff + del.br_blockcount > bno + 1) 5492153323Srodrigc del.br_blockcount = bno + 1 - del.br_startoff; 5493153323Srodrigc sum = del.br_startblock + del.br_blockcount; 5494153323Srodrigc if (isrt && 5495153323Srodrigc (mod = do_mod(sum, mp->m_sb.sb_rextsize))) { 5496153323Srodrigc /* 5497153323Srodrigc * Realtime extent not lined up at the end. 5498153323Srodrigc * The extent could have been split into written 5499153323Srodrigc * and unwritten pieces, or we could just be 5500153323Srodrigc * unmapping part of it. But we can't really 5501153323Srodrigc * get rid of part of a realtime extent. 5502153323Srodrigc */ 5503153323Srodrigc if (del.br_state == XFS_EXT_UNWRITTEN || 5504153323Srodrigc !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) { 5505153323Srodrigc /* 5506153323Srodrigc * This piece is unwritten, or we're not 5507153323Srodrigc * using unwritten extents. Skip over it. 5508153323Srodrigc */ 5509153323Srodrigc ASSERT(bno >= mod); 5510153323Srodrigc bno -= mod > del.br_blockcount ? 5511153323Srodrigc del.br_blockcount : mod; 5512153323Srodrigc if (bno < got.br_startoff) { 5513153323Srodrigc if (--lastx >= 0) 5514159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext( 5515159451Srodrigc ifp, lastx), &got); 5516153323Srodrigc } 5517153323Srodrigc continue; 5518153323Srodrigc } 5519153323Srodrigc /* 5520153323Srodrigc * It's written, turn it unwritten. 5521153323Srodrigc * This is better than zeroing it. 5522153323Srodrigc */ 5523153323Srodrigc ASSERT(del.br_state == XFS_EXT_NORM); 5524153323Srodrigc ASSERT(xfs_trans_get_block_res(tp) > 0); 5525153323Srodrigc /* 5526153323Srodrigc * If this spans a realtime extent boundary, 5527153323Srodrigc * chop it back to the start of the one we end at. 5528153323Srodrigc */ 5529153323Srodrigc if (del.br_blockcount > mod) { 5530153323Srodrigc del.br_startoff += del.br_blockcount - mod; 5531153323Srodrigc del.br_startblock += del.br_blockcount - mod; 5532153323Srodrigc del.br_blockcount = mod; 5533153323Srodrigc } 5534153323Srodrigc del.br_state = XFS_EXT_UNWRITTEN; 5535153323Srodrigc error = xfs_bmap_add_extent(ip, lastx, &cur, &del, 5536159451Srodrigc firstblock, flist, &logflags, delta, 5537159451Srodrigc XFS_DATA_FORK, 0); 5538153323Srodrigc if (error) 5539153323Srodrigc goto error0; 5540153323Srodrigc goto nodelete; 5541153323Srodrigc } 5542153323Srodrigc if (isrt && (mod = do_mod(del.br_startblock, mp->m_sb.sb_rextsize))) { 5543153323Srodrigc /* 5544153323Srodrigc * Realtime extent is lined up at the end but not 5545153323Srodrigc * at the front. We'll get rid of full extents if 5546153323Srodrigc * we can. 5547153323Srodrigc */ 5548153323Srodrigc mod = mp->m_sb.sb_rextsize - mod; 5549153323Srodrigc if (del.br_blockcount > mod) { 5550153323Srodrigc del.br_blockcount -= mod; 5551153323Srodrigc del.br_startoff += mod; 5552153323Srodrigc del.br_startblock += mod; 5553153323Srodrigc } else if ((del.br_startoff == start && 5554153323Srodrigc (del.br_state == XFS_EXT_UNWRITTEN || 5555153323Srodrigc xfs_trans_get_block_res(tp) == 0)) || 5556153323Srodrigc !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) { 5557153323Srodrigc /* 5558153323Srodrigc * Can't make it unwritten. There isn't 5559153323Srodrigc * a full extent here so just skip it. 5560153323Srodrigc */ 5561153323Srodrigc ASSERT(bno >= del.br_blockcount); 5562153323Srodrigc bno -= del.br_blockcount; 5563153323Srodrigc if (bno < got.br_startoff) { 5564153323Srodrigc if (--lastx >= 0) 5565153323Srodrigc xfs_bmbt_get_all(--ep, &got); 5566153323Srodrigc } 5567153323Srodrigc continue; 5568153323Srodrigc } else if (del.br_state == XFS_EXT_UNWRITTEN) { 5569153323Srodrigc /* 5570153323Srodrigc * This one is already unwritten. 5571153323Srodrigc * It must have a written left neighbor. 5572153323Srodrigc * Unwrite the killed part of that one and 5573153323Srodrigc * try again. 5574153323Srodrigc */ 5575153323Srodrigc ASSERT(lastx > 0); 5576159451Srodrigc xfs_bmbt_get_all(xfs_iext_get_ext(ifp, 5577159451Srodrigc lastx - 1), &prev); 5578153323Srodrigc ASSERT(prev.br_state == XFS_EXT_NORM); 5579153323Srodrigc ASSERT(!ISNULLSTARTBLOCK(prev.br_startblock)); 5580153323Srodrigc ASSERT(del.br_startblock == 5581153323Srodrigc prev.br_startblock + prev.br_blockcount); 5582153323Srodrigc if (prev.br_startoff < start) { 5583153323Srodrigc mod = start - prev.br_startoff; 5584153323Srodrigc prev.br_blockcount -= mod; 5585153323Srodrigc prev.br_startblock += mod; 5586153323Srodrigc prev.br_startoff = start; 5587153323Srodrigc } 5588153323Srodrigc prev.br_state = XFS_EXT_UNWRITTEN; 5589153323Srodrigc error = xfs_bmap_add_extent(ip, lastx - 1, &cur, 5590153323Srodrigc &prev, firstblock, flist, &logflags, 5591159451Srodrigc delta, XFS_DATA_FORK, 0); 5592153323Srodrigc if (error) 5593153323Srodrigc goto error0; 5594153323Srodrigc goto nodelete; 5595153323Srodrigc } else { 5596153323Srodrigc ASSERT(del.br_state == XFS_EXT_NORM); 5597153323Srodrigc del.br_state = XFS_EXT_UNWRITTEN; 5598153323Srodrigc error = xfs_bmap_add_extent(ip, lastx, &cur, 5599153323Srodrigc &del, firstblock, flist, &logflags, 5600159451Srodrigc delta, XFS_DATA_FORK, 0); 5601153323Srodrigc if (error) 5602153323Srodrigc goto error0; 5603153323Srodrigc goto nodelete; 5604153323Srodrigc } 5605153323Srodrigc } 5606153323Srodrigc if (wasdel) { 5607153323Srodrigc ASSERT(STARTBLOCKVAL(del.br_startblock) > 0); 5608159451Srodrigc /* Update realtime/data freespace, unreserve quota */ 5609159451Srodrigc if (isrt) { 5610159451Srodrigc xfs_filblks_t rtexts; 5611159451Srodrigc 5612159451Srodrigc rtexts = XFS_FSB_TO_B(mp, del.br_blockcount); 5613159451Srodrigc do_div(rtexts, mp->m_sb.sb_rextsize); 5614159451Srodrigc xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS, 5615159451Srodrigc (int)rtexts, rsvd); 5616159451Srodrigc (void)XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, 5617159451Srodrigc NULL, ip, -((long)del.br_blockcount), 0, 5618159451Srodrigc XFS_QMOPT_RES_RTBLKS); 5619159451Srodrigc } else { 5620159451Srodrigc xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, 5621159451Srodrigc (int)del.br_blockcount, rsvd); 5622159451Srodrigc (void)XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, 5623159451Srodrigc NULL, ip, -((long)del.br_blockcount), 0, 5624153323Srodrigc XFS_QMOPT_RES_REGBLKS); 5625159451Srodrigc } 5626153323Srodrigc ip->i_delayed_blks -= del.br_blockcount; 5627153323Srodrigc if (cur) 5628153323Srodrigc cur->bc_private.b.flags |= 5629153323Srodrigc XFS_BTCUR_BPRV_WASDEL; 5630153323Srodrigc } else if (cur) 5631153323Srodrigc cur->bc_private.b.flags &= ~XFS_BTCUR_BPRV_WASDEL; 5632153323Srodrigc /* 5633153323Srodrigc * If it's the case where the directory code is running 5634153323Srodrigc * with no block reservation, and the deleted block is in 5635153323Srodrigc * the middle of its extent, and the resulting insert 5636153323Srodrigc * of an extent would cause transformation to btree format, 5637153323Srodrigc * then reject it. The calling code will then swap 5638153323Srodrigc * blocks around instead. 5639153323Srodrigc * We have to do this now, rather than waiting for the 5640153323Srodrigc * conversion to btree format, since the transaction 5641153323Srodrigc * will be dirty. 5642153323Srodrigc */ 5643153323Srodrigc if (!wasdel && xfs_trans_get_block_res(tp) == 0 && 5644153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS && 5645153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) >= ifp->if_ext_max && 5646153323Srodrigc del.br_startoff > got.br_startoff && 5647153323Srodrigc del.br_startoff + del.br_blockcount < 5648153323Srodrigc got.br_startoff + got.br_blockcount) { 5649153323Srodrigc error = XFS_ERROR(ENOSPC); 5650153323Srodrigc goto error0; 5651153323Srodrigc } 5652153323Srodrigc error = xfs_bmap_del_extent(ip, tp, lastx, flist, cur, &del, 5653159451Srodrigc &tmp_logflags, delta, whichfork, rsvd); 5654153323Srodrigc logflags |= tmp_logflags; 5655153323Srodrigc if (error) 5656153323Srodrigc goto error0; 5657153323Srodrigc bno = del.br_startoff - 1; 5658153323Srodrigcnodelete: 5659153323Srodrigc lastx = ifp->if_lastex; 5660153323Srodrigc /* 5661153323Srodrigc * If not done go on to the next (previous) record. 5662153323Srodrigc * Reset ep in case the extents array was re-alloced. 5663153323Srodrigc */ 5664159451Srodrigc ep = xfs_iext_get_ext(ifp, lastx); 5665153323Srodrigc if (bno != (xfs_fileoff_t)-1 && bno >= start) { 5666153323Srodrigc if (lastx >= XFS_IFORK_NEXTENTS(ip, whichfork) || 5667153323Srodrigc xfs_bmbt_get_startoff(ep) > bno) { 5668159451Srodrigc if (--lastx >= 0) 5669159451Srodrigc ep = xfs_iext_get_ext(ifp, lastx); 5670153323Srodrigc } 5671153323Srodrigc if (lastx >= 0) 5672153323Srodrigc xfs_bmbt_get_all(ep, &got); 5673153323Srodrigc extno++; 5674153323Srodrigc } 5675153323Srodrigc } 5676153323Srodrigc ifp->if_lastex = lastx; 5677153323Srodrigc *done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0; 5678153323Srodrigc ASSERT(ifp->if_ext_max == 5679153323Srodrigc XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); 5680153323Srodrigc /* 5681153323Srodrigc * Convert to a btree if necessary. 5682153323Srodrigc */ 5683153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS && 5684153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max) { 5685153323Srodrigc ASSERT(cur == NULL); 5686153323Srodrigc error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist, 5687153323Srodrigc &cur, 0, &tmp_logflags, whichfork); 5688153323Srodrigc logflags |= tmp_logflags; 5689153323Srodrigc if (error) 5690153323Srodrigc goto error0; 5691153323Srodrigc } 5692153323Srodrigc /* 5693153323Srodrigc * transform from btree to extents, give it cur 5694153323Srodrigc */ 5695153323Srodrigc else if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE && 5696153323Srodrigc XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max) { 5697153323Srodrigc ASSERT(cur != NULL); 5698153323Srodrigc error = xfs_bmap_btree_to_extents(tp, ip, cur, &tmp_logflags, 5699153323Srodrigc whichfork); 5700153323Srodrigc logflags |= tmp_logflags; 5701153323Srodrigc if (error) 5702153323Srodrigc goto error0; 5703153323Srodrigc } 5704153323Srodrigc /* 5705153323Srodrigc * transform from extents to local? 5706153323Srodrigc */ 5707153323Srodrigc ASSERT(ifp->if_ext_max == 5708153323Srodrigc XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); 5709153323Srodrigc error = 0; 5710159451Srodrigc if (delta && delta->xed_startoff != NULLFILEOFF) { 5711159451Srodrigc /* A change was actually made. 5712159451Srodrigc * Note that delta->xed_blockount is an offset at this 5713159451Srodrigc * point and needs to be converted to a block count. 5714159451Srodrigc */ 5715159451Srodrigc ASSERT(delta->xed_blockcount > delta->xed_startoff); 5716159451Srodrigc delta->xed_blockcount -= delta->xed_startoff; 5717159451Srodrigc } 5718153323Srodrigcerror0: 5719153323Srodrigc /* 5720153323Srodrigc * Log everything. Do this after conversion, there's no point in 5721159451Srodrigc * logging the extent records if we've converted to btree format. 5722153323Srodrigc */ 5723153323Srodrigc if ((logflags & XFS_ILOG_FEXT(whichfork)) && 5724153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS) 5725153323Srodrigc logflags &= ~XFS_ILOG_FEXT(whichfork); 5726153323Srodrigc else if ((logflags & XFS_ILOG_FBROOT(whichfork)) && 5727153323Srodrigc XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE) 5728153323Srodrigc logflags &= ~XFS_ILOG_FBROOT(whichfork); 5729153323Srodrigc /* 5730153323Srodrigc * Log inode even in the error case, if the transaction 5731153323Srodrigc * is dirty we'll need to shut down the filesystem. 5732153323Srodrigc */ 5733153323Srodrigc if (logflags) 5734153323Srodrigc xfs_trans_log_inode(tp, ip, logflags); 5735153323Srodrigc if (cur) { 5736153323Srodrigc if (!error) { 5737153323Srodrigc *firstblock = cur->bc_private.b.firstblock; 5738153323Srodrigc cur->bc_private.b.allocated = 0; 5739153323Srodrigc } 5740153323Srodrigc xfs_btree_del_cursor(cur, 5741153323Srodrigc error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); 5742153323Srodrigc } 5743153323Srodrigc return error; 5744153323Srodrigc} 5745153323Srodrigc 5746159451Srodrigc#undef copy_to_user 5747159451Srodrigcstatic __inline__ int 5748159451Srodrigccopy_to_user(void *dst, void *src, int len) { 5749159451Srodrigc memcpy(dst,src,len); 5750159451Srodrigc return 0; 5751159451Srodrigc} 5752159451Srodrigc#undef copy_from_user 5753159451Srodrigcstatic __inline__ int 5754159451Srodrigccopy_from_user(void *dst, void *src, int len) { 5755159451Srodrigc memcpy(dst,src,len); 5756159451Srodrigc return 0; 5757159451Srodrigc} 5758153323Srodrigc/* 5759153323Srodrigc * Fcntl interface to xfs_bmapi. 5760153323Srodrigc */ 5761153323Srodrigcint /* error code */ 5762153323Srodrigcxfs_getbmap( 5763153323Srodrigc bhv_desc_t *bdp, /* XFS behavior descriptor*/ 5764153323Srodrigc struct getbmap *bmv, /* user bmap structure */ 5765159451Srodrigc void __user *ap, /* pointer to user's array */ 5766153323Srodrigc int interface) /* interface flags */ 5767153323Srodrigc{ 5768153323Srodrigc __int64_t bmvend; /* last block requested */ 5769153323Srodrigc int error; /* return value */ 5770153323Srodrigc __int64_t fixlen; /* length for -1 case */ 5771153323Srodrigc int i; /* extent number */ 5772153323Srodrigc xfs_inode_t *ip; /* xfs incore inode pointer */ 5773153323Srodrigc xfs_vnode_t *vp; /* corresponding vnode */ 5774153323Srodrigc int lock; /* lock state */ 5775153323Srodrigc xfs_bmbt_irec_t *map; /* buffer for user's data */ 5776153323Srodrigc xfs_mount_t *mp; /* file system mount point */ 5777153323Srodrigc int nex; /* # of user extents can do */ 5778153323Srodrigc int nexleft; /* # of user extents left */ 5779153323Srodrigc int subnex; /* # of bmapi's can do */ 5780153323Srodrigc int nmap; /* number of map entries */ 5781153323Srodrigc struct getbmap out; /* output structure */ 5782153323Srodrigc int whichfork; /* data or attr fork */ 5783153323Srodrigc int prealloced; /* this is a file with 5784153323Srodrigc * preallocated data space */ 5785153323Srodrigc int sh_unwritten; /* true, if unwritten */ 5786153323Srodrigc /* extents listed separately */ 5787153323Srodrigc int bmapi_flags; /* flags for xfs_bmapi */ 5788153323Srodrigc __int32_t oflags; /* getbmapx bmv_oflags field */ 5789153323Srodrigc 5790153323Srodrigc vp = BHV_TO_VNODE(bdp); 5791153323Srodrigc ip = XFS_BHVTOI(bdp); 5792153323Srodrigc mp = ip->i_mount; 5793153323Srodrigc 5794153323Srodrigc whichfork = interface & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK; 5795153323Srodrigc sh_unwritten = (interface & BMV_IF_PREALLOC) != 0; 5796153323Srodrigc 5797153323Srodrigc /* If the BMV_IF_NO_DMAPI_READ interface bit specified, do not 5798153323Srodrigc * generate a DMAPI read event. Otherwise, if the DM_EVENT_READ 5799153323Srodrigc * bit is set for the file, generate a read event in order 5800153323Srodrigc * that the DMAPI application may do its thing before we return 5801153323Srodrigc * the extents. Usually this means restoring user file data to 5802153323Srodrigc * regions of the file that look like holes. 5803153323Srodrigc * 5804153323Srodrigc * The "old behavior" (from XFS_IOC_GETBMAP) is to not specify 5805153323Srodrigc * BMV_IF_NO_DMAPI_READ so that read events are generated. 5806153323Srodrigc * If this were not true, callers of ioctl( XFS_IOC_GETBMAP ) 5807153323Srodrigc * could misinterpret holes in a DMAPI file as true holes, 5808153323Srodrigc * when in fact they may represent offline user data. 5809153323Srodrigc */ 5810153323Srodrigc if ( (interface & BMV_IF_NO_DMAPI_READ) == 0 5811153323Srodrigc && DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ) 5812153323Srodrigc && whichfork == XFS_DATA_FORK) { 5813153323Srodrigc 5814153323Srodrigc error = XFS_SEND_DATA(mp, DM_EVENT_READ, vp, 0, 0, 0, NULL); 5815153323Srodrigc if (error) 5816153323Srodrigc return XFS_ERROR(error); 5817153323Srodrigc } 5818153323Srodrigc 5819153323Srodrigc if (whichfork == XFS_ATTR_FORK) { 5820153323Srodrigc if (XFS_IFORK_Q(ip)) { 5821153323Srodrigc if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS && 5822153323Srodrigc ip->i_d.di_aformat != XFS_DINODE_FMT_BTREE && 5823153323Srodrigc ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL) 5824153323Srodrigc return XFS_ERROR(EINVAL); 5825153323Srodrigc } else if (unlikely( 5826153323Srodrigc ip->i_d.di_aformat != 0 && 5827153323Srodrigc ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS)) { 5828153323Srodrigc XFS_ERROR_REPORT("xfs_getbmap", XFS_ERRLEVEL_LOW, 5829153323Srodrigc ip->i_mount); 5830153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 5831153323Srodrigc } 5832153323Srodrigc } else if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS && 5833153323Srodrigc ip->i_d.di_format != XFS_DINODE_FMT_BTREE && 5834153323Srodrigc ip->i_d.di_format != XFS_DINODE_FMT_LOCAL) 5835153323Srodrigc return XFS_ERROR(EINVAL); 5836153323Srodrigc if (whichfork == XFS_DATA_FORK) { 5837159451Srodrigc if ((ip->i_d.di_extsize && (ip->i_d.di_flags & 5838159451Srodrigc (XFS_DIFLAG_REALTIME|XFS_DIFLAG_EXTSIZE))) || 5839159451Srodrigc ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC|XFS_DIFLAG_APPEND)){ 5840153323Srodrigc prealloced = 1; 5841153323Srodrigc fixlen = XFS_MAXIOFFSET(mp); 5842153323Srodrigc } else { 5843153323Srodrigc prealloced = 0; 5844153323Srodrigc fixlen = ip->i_d.di_size; 5845153323Srodrigc } 5846153323Srodrigc } else { 5847153323Srodrigc prealloced = 0; 5848153323Srodrigc fixlen = 1LL << 32; 5849153323Srodrigc } 5850153323Srodrigc 5851153323Srodrigc if (bmv->bmv_length == -1) { 5852153323Srodrigc fixlen = XFS_FSB_TO_BB(mp, XFS_B_TO_FSB(mp, fixlen)); 5853153323Srodrigc bmv->bmv_length = MAX( (__int64_t)(fixlen - bmv->bmv_offset), 5854153323Srodrigc (__int64_t)0); 5855153323Srodrigc } else if (bmv->bmv_length < 0) 5856153323Srodrigc return XFS_ERROR(EINVAL); 5857153323Srodrigc if (bmv->bmv_length == 0) { 5858153323Srodrigc bmv->bmv_entries = 0; 5859153323Srodrigc return 0; 5860153323Srodrigc } 5861153323Srodrigc nex = bmv->bmv_count - 1; 5862153323Srodrigc if (nex <= 0) 5863153323Srodrigc return XFS_ERROR(EINVAL); 5864153323Srodrigc bmvend = bmv->bmv_offset + bmv->bmv_length; 5865153323Srodrigc 5866153323Srodrigc xfs_ilock(ip, XFS_IOLOCK_SHARED); 5867153323Srodrigc 5868153323Srodrigc if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) { 5869153323Srodrigc /* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */ 5870153323Srodrigc XVOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error); 5871153323Srodrigc } 5872153323Srodrigc 5873153323Srodrigc ASSERT(whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0); 5874153323Srodrigc 5875153323Srodrigc lock = xfs_ilock_map_shared(ip); 5876153323Srodrigc 5877153323Srodrigc /* 5878153323Srodrigc * Don't let nex be bigger than the number of extents 5879153323Srodrigc * we can have assuming alternating holes and real extents. 5880153323Srodrigc */ 5881153323Srodrigc if (nex > XFS_IFORK_NEXTENTS(ip, whichfork) * 2 + 1) 5882153323Srodrigc nex = XFS_IFORK_NEXTENTS(ip, whichfork) * 2 + 1; 5883153323Srodrigc 5884153323Srodrigc bmapi_flags = XFS_BMAPI_AFLAG(whichfork) | 5885153323Srodrigc ((sh_unwritten) ? 0 : XFS_BMAPI_IGSTATE); 5886153323Srodrigc 5887153323Srodrigc /* 5888153323Srodrigc * Allocate enough space to handle "subnex" maps at a time. 5889153323Srodrigc */ 5890153323Srodrigc subnex = 16; 5891153323Srodrigc map = kmem_alloc(subnex * sizeof(*map), KM_SLEEP); 5892153323Srodrigc 5893153323Srodrigc bmv->bmv_entries = 0; 5894153323Srodrigc 5895153323Srodrigc if (XFS_IFORK_NEXTENTS(ip, whichfork) == 0) { 5896153323Srodrigc error = 0; 5897153323Srodrigc goto unlock_and_return; 5898153323Srodrigc } 5899153323Srodrigc 5900153323Srodrigc nexleft = nex; 5901153323Srodrigc 5902153323Srodrigc do { 5903153323Srodrigc nmap = (nexleft > subnex) ? subnex : nexleft; 5904153323Srodrigc error = xfs_bmapi(NULL, ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset), 5905153323Srodrigc XFS_BB_TO_FSB(mp, bmv->bmv_length), 5906159451Srodrigc bmapi_flags, NULL, 0, map, &nmap, 5907159451Srodrigc NULL, NULL); 5908153323Srodrigc if (error) 5909153323Srodrigc goto unlock_and_return; 5910153323Srodrigc ASSERT(nmap <= subnex); 5911153323Srodrigc 5912153323Srodrigc for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) { 5913153323Srodrigc nexleft--; 5914153323Srodrigc oflags = (map[i].br_state == XFS_EXT_UNWRITTEN) ? 5915153323Srodrigc BMV_OF_PREALLOC : 0; 5916153323Srodrigc out.bmv_offset = XFS_FSB_TO_BB(mp, map[i].br_startoff); 5917153323Srodrigc out.bmv_length = XFS_FSB_TO_BB(mp, map[i].br_blockcount); 5918153323Srodrigc ASSERT(map[i].br_startblock != DELAYSTARTBLOCK); 5919159451Srodrigc if (map[i].br_startblock == HOLESTARTBLOCK && 5920159451Srodrigc ((prealloced && out.bmv_offset + out.bmv_length == bmvend) || 5921159451Srodrigc whichfork == XFS_ATTR_FORK )) { 5922159451Srodrigc /* 5923159451Srodrigc * came to hole at end of file or the end of 5924159451Srodrigc attribute fork 5925159451Srodrigc */ 5926153323Srodrigc goto unlock_and_return; 5927153323Srodrigc } else { 5928153323Srodrigc out.bmv_block = 5929153323Srodrigc (map[i].br_startblock == HOLESTARTBLOCK) ? 5930153323Srodrigc -1 : 5931153323Srodrigc XFS_FSB_TO_DB(ip, map[i].br_startblock); 5932153323Srodrigc 5933153323Srodrigc /* return either getbmap/getbmapx structure. */ 5934153323Srodrigc if (interface & BMV_IF_EXTENDED) { 5935153323Srodrigc struct getbmapx outx; 5936153323Srodrigc 5937153323Srodrigc GETBMAP_CONVERT(out,outx); 5938153323Srodrigc outx.bmv_oflags = oflags; 5939153323Srodrigc outx.bmv_unused1 = outx.bmv_unused2 = 0; 5940153323Srodrigc if (copy_to_user(ap, &outx, 5941153323Srodrigc sizeof(outx))) { 5942153323Srodrigc error = XFS_ERROR(EFAULT); 5943153323Srodrigc goto unlock_and_return; 5944153323Srodrigc } 5945153323Srodrigc } else { 5946153323Srodrigc if (copy_to_user(ap, &out, 5947153323Srodrigc sizeof(out))) { 5948153323Srodrigc error = XFS_ERROR(EFAULT); 5949153323Srodrigc goto unlock_and_return; 5950153323Srodrigc } 5951153323Srodrigc } 5952153323Srodrigc bmv->bmv_offset = 5953153323Srodrigc out.bmv_offset + out.bmv_length; 5954153323Srodrigc bmv->bmv_length = MAX((__int64_t)0, 5955153323Srodrigc (__int64_t)(bmvend - bmv->bmv_offset)); 5956153323Srodrigc bmv->bmv_entries++; 5957153323Srodrigc ap = (interface & BMV_IF_EXTENDED) ? 5958159451Srodrigc (void __user *) 5959159451Srodrigc ((struct getbmapx __user *)ap + 1) : 5960159451Srodrigc (void __user *) 5961159451Srodrigc ((struct getbmap __user *)ap + 1); 5962153323Srodrigc } 5963153323Srodrigc } 5964153323Srodrigc } while (nmap && nexleft && bmv->bmv_length); 5965153323Srodrigc 5966153323Srodrigcunlock_and_return: 5967153323Srodrigc xfs_iunlock_map_shared(ip, lock); 5968153323Srodrigc xfs_iunlock(ip, XFS_IOLOCK_SHARED); 5969153323Srodrigc 5970153323Srodrigc kmem_free(map, subnex * sizeof(*map)); 5971153323Srodrigc 5972153323Srodrigc return error; 5973153323Srodrigc} 5974153323Srodrigc 5975153323Srodrigc/* 5976153323Srodrigc * Check the last inode extent to determine whether this allocation will result 5977153323Srodrigc * in blocks being allocated at the end of the file. When we allocate new data 5978153323Srodrigc * blocks at the end of the file which do not start at the previous data block, 5979153323Srodrigc * we will try to align the new blocks at stripe unit boundaries. 5980153323Srodrigc */ 5981159451SrodrigcSTATIC int /* error */ 5982153323Srodrigcxfs_bmap_isaeof( 5983153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 5984153323Srodrigc xfs_fileoff_t off, /* file offset in fsblocks */ 5985153323Srodrigc int whichfork, /* data or attribute fork */ 5986153323Srodrigc char *aeof) /* return value */ 5987153323Srodrigc{ 5988153323Srodrigc int error; /* error return value */ 5989153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 5990159451Srodrigc xfs_bmbt_rec_t *lastrec; /* extent record pointer */ 5991159451Srodrigc xfs_extnum_t nextents; /* number of file extents */ 5992159451Srodrigc xfs_bmbt_irec_t s; /* expanded extent record */ 5993153323Srodrigc 5994153323Srodrigc ASSERT(whichfork == XFS_DATA_FORK); 5995153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 5996153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 5997153323Srodrigc (error = xfs_iread_extents(NULL, ip, whichfork))) 5998153323Srodrigc return error; 5999153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 6000153323Srodrigc if (nextents == 0) { 6001153323Srodrigc *aeof = 1; 6002153323Srodrigc return 0; 6003153323Srodrigc } 6004153323Srodrigc /* 6005153323Srodrigc * Go to the last extent 6006153323Srodrigc */ 6007159451Srodrigc lastrec = xfs_iext_get_ext(ifp, nextents - 1); 6008153323Srodrigc xfs_bmbt_get_all(lastrec, &s); 6009153323Srodrigc /* 6010153323Srodrigc * Check we are allocating in the last extent (for delayed allocations) 6011153323Srodrigc * or past the last extent for non-delayed allocations. 6012153323Srodrigc */ 6013153323Srodrigc *aeof = (off >= s.br_startoff && 6014153323Srodrigc off < s.br_startoff + s.br_blockcount && 6015153323Srodrigc ISNULLSTARTBLOCK(s.br_startblock)) || 6016153323Srodrigc off >= s.br_startoff + s.br_blockcount; 6017153323Srodrigc return 0; 6018153323Srodrigc} 6019153323Srodrigc 6020153323Srodrigc/* 6021153323Srodrigc * Check if the endoff is outside the last extent. If so the caller will grow 6022153323Srodrigc * the allocation to a stripe unit boundary. 6023153323Srodrigc */ 6024153323Srodrigcint /* error */ 6025153323Srodrigcxfs_bmap_eof( 6026153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 6027153323Srodrigc xfs_fileoff_t endoff, /* file offset in fsblocks */ 6028153323Srodrigc int whichfork, /* data or attribute fork */ 6029153323Srodrigc int *eof) /* result value */ 6030153323Srodrigc{ 6031153323Srodrigc xfs_fsblock_t blockcount; /* extent block count */ 6032153323Srodrigc int error; /* error return value */ 6033153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 6034159451Srodrigc xfs_bmbt_rec_t *lastrec; /* extent record pointer */ 6035159451Srodrigc xfs_extnum_t nextents; /* number of file extents */ 6036153323Srodrigc xfs_fileoff_t startoff; /* extent starting file offset */ 6037153323Srodrigc 6038153323Srodrigc ASSERT(whichfork == XFS_DATA_FORK); 6039153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 6040153323Srodrigc if (!(ifp->if_flags & XFS_IFEXTENTS) && 6041153323Srodrigc (error = xfs_iread_extents(NULL, ip, whichfork))) 6042153323Srodrigc return error; 6043153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 6044153323Srodrigc if (nextents == 0) { 6045153323Srodrigc *eof = 1; 6046153323Srodrigc return 0; 6047153323Srodrigc } 6048153323Srodrigc /* 6049153323Srodrigc * Go to the last extent 6050153323Srodrigc */ 6051159451Srodrigc lastrec = xfs_iext_get_ext(ifp, nextents - 1); 6052153323Srodrigc startoff = xfs_bmbt_get_startoff(lastrec); 6053153323Srodrigc blockcount = xfs_bmbt_get_blockcount(lastrec); 6054153323Srodrigc *eof = endoff >= startoff + blockcount; 6055153323Srodrigc return 0; 6056153323Srodrigc} 6057153323Srodrigc 6058159451Srodrigc#ifdef DEBUG 6059159451Srodrigc#if 0 6060153323Srodrigc/* 6061153323Srodrigc * Check that the extents list for the inode ip is in the right order. 6062153323Srodrigc */ 6063153323SrodrigcSTATIC void 6064153323Srodrigcxfs_bmap_check_extents( 6065153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 6066153323Srodrigc int whichfork) /* data or attr fork */ 6067153323Srodrigc{ 6068153323Srodrigc xfs_bmbt_rec_t *ep; /* current extent entry */ 6069159451Srodrigc xfs_extnum_t idx; /* extent record index */ 6070153323Srodrigc xfs_ifork_t *ifp; /* inode fork pointer */ 6071153323Srodrigc xfs_extnum_t nextents; /* number of extents in list */ 6072159451Srodrigc xfs_bmbt_rec_t *nextp; /* next extent entry */ 6073153323Srodrigc 6074153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 6075153323Srodrigc ASSERT(ifp->if_flags & XFS_IFEXTENTS); 6076153323Srodrigc nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); 6077159451Srodrigc ep = xfs_iext_get_ext(ifp, 0); 6078159451Srodrigc for (idx = 0; idx < nextents - 1; idx++) { 6079159451Srodrigc nextp = xfs_iext_get_ext(ifp, idx + 1); 6080153323Srodrigc xfs_btree_check_rec(XFS_BTNUM_BMAP, (void *)ep, 6081159451Srodrigc (void *)(nextp)); 6082159451Srodrigc ep = nextp; 6083153323Srodrigc } 6084153323Srodrigc} 6085159451Srodrigc#endif 6086153323Srodrigc 6087153323SrodrigcSTATIC 6088153323Srodrigcxfs_buf_t * 6089153323Srodrigcxfs_bmap_get_bp( 6090153323Srodrigc xfs_btree_cur_t *cur, 6091153323Srodrigc xfs_fsblock_t bno) 6092153323Srodrigc{ 6093153323Srodrigc int i; 6094153323Srodrigc xfs_buf_t *bp; 6095153323Srodrigc 6096153323Srodrigc if (!cur) 6097153323Srodrigc return(NULL); 6098153323Srodrigc 6099153323Srodrigc bp = NULL; 6100153323Srodrigc for(i = 0; i < XFS_BTREE_MAXLEVELS; i++) { 6101153323Srodrigc bp = cur->bc_bufs[i]; 6102153323Srodrigc if (!bp) break; 6103153323Srodrigc if (XFS_BUF_ADDR(bp) == bno) 6104153323Srodrigc break; /* Found it */ 6105153323Srodrigc } 6106153323Srodrigc if (i == XFS_BTREE_MAXLEVELS) 6107153323Srodrigc bp = NULL; 6108153323Srodrigc 6109153323Srodrigc if (!bp) { /* Chase down all the log items to see if the bp is there */ 6110153323Srodrigc xfs_log_item_chunk_t *licp; 6111153323Srodrigc xfs_trans_t *tp; 6112153323Srodrigc 6113153323Srodrigc tp = cur->bc_tp; 6114153323Srodrigc licp = &tp->t_items; 6115153323Srodrigc while (!bp && licp != NULL) { 6116153323Srodrigc if (XFS_LIC_ARE_ALL_FREE(licp)) { 6117153323Srodrigc licp = licp->lic_next; 6118153323Srodrigc continue; 6119153323Srodrigc } 6120153323Srodrigc for (i = 0; i < licp->lic_unused; i++) { 6121153323Srodrigc xfs_log_item_desc_t *lidp; 6122153323Srodrigc xfs_log_item_t *lip; 6123153323Srodrigc xfs_buf_log_item_t *bip; 6124153323Srodrigc xfs_buf_t *lbp; 6125153323Srodrigc 6126153323Srodrigc if (XFS_LIC_ISFREE(licp, i)) { 6127153323Srodrigc continue; 6128153323Srodrigc } 6129153323Srodrigc 6130153323Srodrigc lidp = XFS_LIC_SLOT(licp, i); 6131153323Srodrigc lip = lidp->lid_item; 6132153323Srodrigc if (lip->li_type != XFS_LI_BUF) 6133153323Srodrigc continue; 6134153323Srodrigc 6135153323Srodrigc bip = (xfs_buf_log_item_t *)lip; 6136153323Srodrigc lbp = bip->bli_buf; 6137153323Srodrigc 6138153323Srodrigc if (XFS_BUF_ADDR(lbp) == bno) { 6139153323Srodrigc bp = lbp; 6140153323Srodrigc break; /* Found it */ 6141153323Srodrigc } 6142153323Srodrigc } 6143153323Srodrigc licp = licp->lic_next; 6144153323Srodrigc } 6145153323Srodrigc } 6146153323Srodrigc return(bp); 6147153323Srodrigc} 6148153323Srodrigc 6149159451SrodrigcSTATIC void 6150153323Srodrigcxfs_check_block( 6151153323Srodrigc xfs_bmbt_block_t *block, 6152153323Srodrigc xfs_mount_t *mp, 6153153323Srodrigc int root, 6154153323Srodrigc short sz) 6155153323Srodrigc{ 6156153323Srodrigc int i, j, dmxr; 6157153323Srodrigc xfs_bmbt_ptr_t *pp, *thispa; /* pointer to block address */ 6158153323Srodrigc xfs_bmbt_key_t *prevp, *keyp; 6159153323Srodrigc 6160159451Srodrigc ASSERT(be16_to_cpu(block->bb_level) > 0); 6161153323Srodrigc 6162153323Srodrigc prevp = NULL; 6163159451Srodrigc for( i = 1; i <= be16_to_cpu(block->bb_numrecs); i++) { 6164153323Srodrigc dmxr = mp->m_bmap_dmxr[0]; 6165153323Srodrigc 6166153323Srodrigc if (root) { 6167153323Srodrigc keyp = XFS_BMAP_BROOT_KEY_ADDR(block, i, sz); 6168153323Srodrigc } else { 6169153323Srodrigc keyp = XFS_BTREE_KEY_ADDR(mp->m_sb.sb_blocksize, 6170153323Srodrigc xfs_bmbt, block, i, dmxr); 6171153323Srodrigc } 6172153323Srodrigc 6173153323Srodrigc if (prevp) { 6174153323Srodrigc xfs_btree_check_key(XFS_BTNUM_BMAP, prevp, keyp); 6175153323Srodrigc } 6176153323Srodrigc prevp = keyp; 6177153323Srodrigc 6178153323Srodrigc /* 6179153323Srodrigc * Compare the block numbers to see if there are dups. 6180153323Srodrigc */ 6181153323Srodrigc 6182153323Srodrigc if (root) { 6183153323Srodrigc pp = XFS_BMAP_BROOT_PTR_ADDR(block, i, sz); 6184153323Srodrigc } else { 6185153323Srodrigc pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, 6186153323Srodrigc xfs_bmbt, block, i, dmxr); 6187153323Srodrigc } 6188159451Srodrigc for (j = i+1; j <= be16_to_cpu(block->bb_numrecs); j++) { 6189153323Srodrigc if (root) { 6190153323Srodrigc thispa = XFS_BMAP_BROOT_PTR_ADDR(block, j, sz); 6191153323Srodrigc } else { 6192153323Srodrigc thispa = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, 6193153323Srodrigc xfs_bmbt, block, j, dmxr); 6194153323Srodrigc } 6195153323Srodrigc if (INT_GET(*thispa, ARCH_CONVERT) == 6196153323Srodrigc INT_GET(*pp, ARCH_CONVERT)) { 6197153323Srodrigc cmn_err(CE_WARN, "%s: thispa(%d) == pp(%d) %Ld", 6198153323Srodrigc __FUNCTION__, j, i, 6199153323Srodrigc INT_GET(*thispa, ARCH_CONVERT)); 6200153323Srodrigc panic("%s: ptrs are equal in node\n", 6201153323Srodrigc __FUNCTION__); 6202153323Srodrigc } 6203153323Srodrigc } 6204153323Srodrigc } 6205153323Srodrigc} 6206153323Srodrigc 6207153323Srodrigc/* 6208153323Srodrigc * Check that the extents for the inode ip are in the right order in all 6209153323Srodrigc * btree leaves. 6210153323Srodrigc */ 6211153323Srodrigc 6212153323SrodrigcSTATIC void 6213153323Srodrigcxfs_bmap_check_leaf_extents( 6214153323Srodrigc xfs_btree_cur_t *cur, /* btree cursor or null */ 6215153323Srodrigc xfs_inode_t *ip, /* incore inode pointer */ 6216153323Srodrigc int whichfork) /* data or attr fork */ 6217153323Srodrigc{ 6218153323Srodrigc xfs_bmbt_block_t *block; /* current btree block */ 6219153323Srodrigc xfs_fsblock_t bno; /* block # of "block" */ 6220153323Srodrigc xfs_buf_t *bp; /* buffer for "block" */ 6221153323Srodrigc int error; /* error return value */ 6222159451Srodrigc xfs_extnum_t i=0, j; /* index into the extents list */ 6223153323Srodrigc xfs_ifork_t *ifp; /* fork structure */ 6224153323Srodrigc int level; /* btree level, for checking */ 6225153323Srodrigc xfs_mount_t *mp; /* file system mount structure */ 6226153323Srodrigc xfs_bmbt_ptr_t *pp; /* pointer to block address */ 6227159451Srodrigc xfs_bmbt_rec_t *ep; /* pointer to current extent */ 6228159451Srodrigc xfs_bmbt_rec_t *lastp; /* pointer to previous extent */ 6229159451Srodrigc xfs_bmbt_rec_t *nextp; /* pointer to next extent */ 6230153323Srodrigc int bp_release = 0; 6231153323Srodrigc 6232153323Srodrigc if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE) { 6233153323Srodrigc return; 6234153323Srodrigc } 6235153323Srodrigc 6236153323Srodrigc bno = NULLFSBLOCK; 6237153323Srodrigc mp = ip->i_mount; 6238153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 6239153323Srodrigc block = ifp->if_broot; 6240153323Srodrigc /* 6241153323Srodrigc * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out. 6242153323Srodrigc */ 6243159451Srodrigc level = be16_to_cpu(block->bb_level); 6244159451Srodrigc ASSERT(level > 0); 6245153323Srodrigc xfs_check_block(block, mp, 1, ifp->if_broot_bytes); 6246153323Srodrigc pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes); 6247153323Srodrigc ASSERT(INT_GET(*pp, ARCH_CONVERT) != NULLDFSBNO); 6248153323Srodrigc ASSERT(XFS_FSB_TO_AGNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agcount); 6249153323Srodrigc ASSERT(XFS_FSB_TO_AGBNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agblocks); 6250153323Srodrigc bno = INT_GET(*pp, ARCH_CONVERT); 6251153323Srodrigc /* 6252153323Srodrigc * Go down the tree until leaf level is reached, following the first 6253153323Srodrigc * pointer (leftmost) at each level. 6254153323Srodrigc */ 6255153323Srodrigc while (level-- > 0) { 6256153323Srodrigc /* See if buf is in cur first */ 6257153323Srodrigc bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno)); 6258153323Srodrigc if (bp) { 6259153323Srodrigc bp_release = 0; 6260153323Srodrigc } else { 6261153323Srodrigc bp_release = 1; 6262153323Srodrigc } 6263153323Srodrigc if (!bp && (error = xfs_btree_read_bufl(mp, NULL, bno, 0, &bp, 6264153323Srodrigc XFS_BMAP_BTREE_REF))) 6265153323Srodrigc goto error_norelse; 6266153323Srodrigc block = XFS_BUF_TO_BMBT_BLOCK(bp); 6267153323Srodrigc XFS_WANT_CORRUPTED_GOTO( 6268153323Srodrigc XFS_BMAP_SANITY_CHECK(mp, block, level), 6269153323Srodrigc error0); 6270153323Srodrigc if (level == 0) 6271153323Srodrigc break; 6272153323Srodrigc 6273153323Srodrigc /* 6274153323Srodrigc * Check this block for basic sanity (increasing keys and 6275153323Srodrigc * no duplicate blocks). 6276153323Srodrigc */ 6277153323Srodrigc 6278153323Srodrigc xfs_check_block(block, mp, 0, 0); 6279153323Srodrigc pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, block, 6280153323Srodrigc 1, mp->m_bmap_dmxr[1]); 6281153323Srodrigc XFS_WANT_CORRUPTED_GOTO(XFS_FSB_SANITY_CHECK(mp, INT_GET(*pp, ARCH_CONVERT)), error0); 6282153323Srodrigc bno = INT_GET(*pp, ARCH_CONVERT); 6283153323Srodrigc if (bp_release) { 6284153323Srodrigc bp_release = 0; 6285153323Srodrigc xfs_trans_brelse(NULL, bp); 6286153323Srodrigc } 6287153323Srodrigc } 6288153323Srodrigc 6289153323Srodrigc /* 6290153323Srodrigc * Here with bp and block set to the leftmost leaf node in the tree. 6291153323Srodrigc */ 6292153323Srodrigc i = 0; 6293153323Srodrigc 6294153323Srodrigc /* 6295153323Srodrigc * Loop over all leaf nodes checking that all extents are in the right order. 6296153323Srodrigc */ 6297153323Srodrigc lastp = NULL; 6298153323Srodrigc for (;;) { 6299153323Srodrigc xfs_fsblock_t nextbno; 6300153323Srodrigc xfs_extnum_t num_recs; 6301153323Srodrigc 6302153323Srodrigc 6303159451Srodrigc num_recs = be16_to_cpu(block->bb_numrecs); 6304153323Srodrigc 6305153323Srodrigc /* 6306153323Srodrigc * Read-ahead the next leaf block, if any. 6307153323Srodrigc */ 6308153323Srodrigc 6309159451Srodrigc nextbno = be64_to_cpu(block->bb_rightsib); 6310153323Srodrigc 6311153323Srodrigc /* 6312153323Srodrigc * Check all the extents to make sure they are OK. 6313153323Srodrigc * If we had a previous block, the last entry should 6314153323Srodrigc * conform with the first entry in this one. 6315153323Srodrigc */ 6316153323Srodrigc 6317159451Srodrigc ep = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, 6318153323Srodrigc block, 1, mp->m_bmap_dmxr[0]); 6319159451Srodrigc for (j = 1; j < num_recs; j++) { 6320159451Srodrigc nextp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, 6321159451Srodrigc block, j + 1, mp->m_bmap_dmxr[0]); 6322153323Srodrigc if (lastp) { 6323153323Srodrigc xfs_btree_check_rec(XFS_BTNUM_BMAP, 6324153323Srodrigc (void *)lastp, (void *)ep); 6325153323Srodrigc } 6326153323Srodrigc xfs_btree_check_rec(XFS_BTNUM_BMAP, (void *)ep, 6327159451Srodrigc (void *)(nextp)); 6328159451Srodrigc lastp = ep; 6329159451Srodrigc ep = nextp; 6330153323Srodrigc } 6331153323Srodrigc 6332153323Srodrigc i += num_recs; 6333153323Srodrigc if (bp_release) { 6334153323Srodrigc bp_release = 0; 6335153323Srodrigc xfs_trans_brelse(NULL, bp); 6336153323Srodrigc } 6337153323Srodrigc bno = nextbno; 6338153323Srodrigc /* 6339153323Srodrigc * If we've reached the end, stop. 6340153323Srodrigc */ 6341153323Srodrigc if (bno == NULLFSBLOCK) 6342153323Srodrigc break; 6343153323Srodrigc 6344153323Srodrigc bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno)); 6345153323Srodrigc if (bp) { 6346153323Srodrigc bp_release = 0; 6347153323Srodrigc } else { 6348153323Srodrigc bp_release = 1; 6349153323Srodrigc } 6350153323Srodrigc if (!bp && (error = xfs_btree_read_bufl(mp, NULL, bno, 0, &bp, 6351153323Srodrigc XFS_BMAP_BTREE_REF))) 6352153323Srodrigc goto error_norelse; 6353153323Srodrigc block = XFS_BUF_TO_BMBT_BLOCK(bp); 6354153323Srodrigc } 6355153323Srodrigc if (bp_release) { 6356153323Srodrigc bp_release = 0; 6357153323Srodrigc xfs_trans_brelse(NULL, bp); 6358153323Srodrigc } 6359153323Srodrigc return; 6360153323Srodrigc 6361153323Srodrigcerror0: 6362153323Srodrigc cmn_err(CE_WARN, "%s: at error0", __FUNCTION__); 6363153323Srodrigc if (bp_release) 6364153323Srodrigc xfs_trans_brelse(NULL, bp); 6365153323Srodrigcerror_norelse: 6366153323Srodrigc cmn_err(CE_WARN, "%s: BAD after btree leaves for %d extents", 6367159451Srodrigc __FUNCTION__, i); 6368153323Srodrigc panic("%s: CORRUPTED BTREE OR SOMETHING", __FUNCTION__); 6369153323Srodrigc return; 6370153323Srodrigc} 6371153323Srodrigc#endif 6372153323Srodrigc 6373153323Srodrigc/* 6374153323Srodrigc * Count fsblocks of the given fork. 6375153323Srodrigc */ 6376153323Srodrigcint /* error */ 6377153323Srodrigcxfs_bmap_count_blocks( 6378153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 6379153323Srodrigc xfs_inode_t *ip, /* incore inode */ 6380153323Srodrigc int whichfork, /* data or attr fork */ 6381153323Srodrigc int *count) /* out: count of blocks */ 6382153323Srodrigc{ 6383153323Srodrigc xfs_bmbt_block_t *block; /* current btree block */ 6384153323Srodrigc xfs_fsblock_t bno; /* block # of "block" */ 6385153323Srodrigc xfs_ifork_t *ifp; /* fork structure */ 6386153323Srodrigc int level; /* btree level, for checking */ 6387153323Srodrigc xfs_mount_t *mp; /* file system mount structure */ 6388153323Srodrigc xfs_bmbt_ptr_t *pp; /* pointer to block address */ 6389153323Srodrigc 6390153323Srodrigc bno = NULLFSBLOCK; 6391153323Srodrigc mp = ip->i_mount; 6392153323Srodrigc ifp = XFS_IFORK_PTR(ip, whichfork); 6393153323Srodrigc if ( XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS ) { 6394159451Srodrigc if (unlikely(xfs_bmap_count_leaves(ifp, 0, 6395153323Srodrigc ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t), 6396153323Srodrigc count) < 0)) { 6397153323Srodrigc XFS_ERROR_REPORT("xfs_bmap_count_blocks(1)", 6398153323Srodrigc XFS_ERRLEVEL_LOW, mp); 6399153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 6400153323Srodrigc } 6401153323Srodrigc return 0; 6402153323Srodrigc } 6403153323Srodrigc 6404153323Srodrigc /* 6405153323Srodrigc * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out. 6406153323Srodrigc */ 6407153323Srodrigc block = ifp->if_broot; 6408159451Srodrigc level = be16_to_cpu(block->bb_level); 6409159451Srodrigc ASSERT(level > 0); 6410153323Srodrigc pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes); 6411153323Srodrigc ASSERT(INT_GET(*pp, ARCH_CONVERT) != NULLDFSBNO); 6412153323Srodrigc ASSERT(XFS_FSB_TO_AGNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agcount); 6413153323Srodrigc ASSERT(XFS_FSB_TO_AGBNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agblocks); 6414153323Srodrigc bno = INT_GET(*pp, ARCH_CONVERT); 6415153323Srodrigc 6416159451Srodrigc if (unlikely(xfs_bmap_count_tree(mp, tp, ifp, bno, level, count) < 0)) { 6417153323Srodrigc XFS_ERROR_REPORT("xfs_bmap_count_blocks(2)", XFS_ERRLEVEL_LOW, 6418153323Srodrigc mp); 6419153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 6420153323Srodrigc } 6421153323Srodrigc 6422153323Srodrigc return 0; 6423153323Srodrigc} 6424153323Srodrigc 6425153323Srodrigc/* 6426153323Srodrigc * Recursively walks each level of a btree 6427153323Srodrigc * to count total fsblocks is use. 6428153323Srodrigc */ 6429153323Srodrigcint /* error */ 6430153323Srodrigcxfs_bmap_count_tree( 6431153323Srodrigc xfs_mount_t *mp, /* file system mount point */ 6432153323Srodrigc xfs_trans_t *tp, /* transaction pointer */ 6433159451Srodrigc xfs_ifork_t *ifp, /* inode fork pointer */ 6434153323Srodrigc xfs_fsblock_t blockno, /* file system block number */ 6435153323Srodrigc int levelin, /* level in btree */ 6436153323Srodrigc int *count) /* Count of blocks */ 6437153323Srodrigc{ 6438153323Srodrigc int error; 6439153323Srodrigc xfs_buf_t *bp, *nbp; 6440153323Srodrigc int level = levelin; 6441153323Srodrigc xfs_bmbt_ptr_t *pp; 6442153323Srodrigc xfs_fsblock_t bno = blockno; 6443153323Srodrigc xfs_fsblock_t nextbno; 6444153323Srodrigc xfs_bmbt_block_t *block, *nextblock; 6445153323Srodrigc int numrecs; 6446153323Srodrigc 6447153323Srodrigc if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp, XFS_BMAP_BTREE_REF))) 6448153323Srodrigc return error; 6449153323Srodrigc *count += 1; 6450153323Srodrigc block = XFS_BUF_TO_BMBT_BLOCK(bp); 6451153323Srodrigc 6452153323Srodrigc if (--level) { 6453153323Srodrigc /* Not at node above leafs, count this level of nodes */ 6454159451Srodrigc nextbno = be64_to_cpu(block->bb_rightsib); 6455153323Srodrigc while (nextbno != NULLFSBLOCK) { 6456153323Srodrigc if ((error = xfs_btree_read_bufl(mp, tp, nextbno, 6457153323Srodrigc 0, &nbp, XFS_BMAP_BTREE_REF))) 6458153323Srodrigc return error; 6459153323Srodrigc *count += 1; 6460153323Srodrigc nextblock = XFS_BUF_TO_BMBT_BLOCK(nbp); 6461159451Srodrigc nextbno = be64_to_cpu(nextblock->bb_rightsib); 6462153323Srodrigc xfs_trans_brelse(tp, nbp); 6463153323Srodrigc } 6464153323Srodrigc 6465153323Srodrigc /* Dive to the next level */ 6466153323Srodrigc pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, 6467153323Srodrigc xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]); 6468153323Srodrigc bno = INT_GET(*pp, ARCH_CONVERT); 6469153323Srodrigc if (unlikely((error = 6470159451Srodrigc xfs_bmap_count_tree(mp, tp, ifp, bno, level, count)) < 0)) { 6471153323Srodrigc xfs_trans_brelse(tp, bp); 6472153323Srodrigc XFS_ERROR_REPORT("xfs_bmap_count_tree(1)", 6473153323Srodrigc XFS_ERRLEVEL_LOW, mp); 6474153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 6475153323Srodrigc } 6476153323Srodrigc xfs_trans_brelse(tp, bp); 6477153323Srodrigc } else { 6478153323Srodrigc /* count all level 1 nodes and their leaves */ 6479153323Srodrigc for (;;) { 6480159451Srodrigc nextbno = be64_to_cpu(block->bb_rightsib); 6481159451Srodrigc numrecs = be16_to_cpu(block->bb_numrecs); 6482159451Srodrigc if (unlikely(xfs_bmap_disk_count_leaves(ifp, mp, 6483159451Srodrigc 0, block, numrecs, count) < 0)) { 6484153323Srodrigc xfs_trans_brelse(tp, bp); 6485153323Srodrigc XFS_ERROR_REPORT("xfs_bmap_count_tree(2)", 6486153323Srodrigc XFS_ERRLEVEL_LOW, mp); 6487153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 6488153323Srodrigc } 6489153323Srodrigc xfs_trans_brelse(tp, bp); 6490153323Srodrigc if (nextbno == NULLFSBLOCK) 6491153323Srodrigc break; 6492153323Srodrigc bno = nextbno; 6493153323Srodrigc if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp, 6494153323Srodrigc XFS_BMAP_BTREE_REF))) 6495153323Srodrigc return error; 6496153323Srodrigc *count += 1; 6497153323Srodrigc block = XFS_BUF_TO_BMBT_BLOCK(bp); 6498153323Srodrigc } 6499153323Srodrigc } 6500153323Srodrigc return 0; 6501153323Srodrigc} 6502153323Srodrigc 6503153323Srodrigc/* 6504159451Srodrigc * Count leaf blocks given a range of extent records. 6505153323Srodrigc */ 6506153323Srodrigcint 6507153323Srodrigcxfs_bmap_count_leaves( 6508159451Srodrigc xfs_ifork_t *ifp, 6509159451Srodrigc xfs_extnum_t idx, 6510153323Srodrigc int numrecs, 6511153323Srodrigc int *count) 6512153323Srodrigc{ 6513153323Srodrigc int b; 6514159451Srodrigc xfs_bmbt_rec_t *frp; 6515153323Srodrigc 6516159451Srodrigc for (b = 0; b < numrecs; b++) { 6517159451Srodrigc frp = xfs_iext_get_ext(ifp, idx + b); 6518159451Srodrigc *count += xfs_bmbt_get_blockcount(frp); 6519159451Srodrigc } 6520159451Srodrigc return 0; 6521159451Srodrigc} 6522159451Srodrigc 6523159451Srodrigc/* 6524159451Srodrigc * Count leaf blocks given a range of extent records originally 6525159451Srodrigc * in btree format. 6526159451Srodrigc */ 6527159451Srodrigcint 6528159451Srodrigcxfs_bmap_disk_count_leaves( 6529159451Srodrigc xfs_ifork_t *ifp, 6530159451Srodrigc xfs_mount_t *mp, 6531159451Srodrigc xfs_extnum_t idx, 6532159451Srodrigc xfs_bmbt_block_t *block, 6533159451Srodrigc int numrecs, 6534159451Srodrigc int *count) 6535159451Srodrigc{ 6536159451Srodrigc int b; 6537159451Srodrigc xfs_bmbt_rec_t *frp; 6538159451Srodrigc 6539159451Srodrigc for (b = 1; b <= numrecs; b++) { 6540159451Srodrigc frp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, 6541159451Srodrigc xfs_bmbt, block, idx + b, mp->m_bmap_dmxr[0]); 6542153323Srodrigc *count += xfs_bmbt_disk_get_blockcount(frp); 6543159451Srodrigc } 6544153323Srodrigc return 0; 6545153323Srodrigc} 6546153323Srodrigc 6547153323Srodrigc 6548