1153323Srodrigc/* 2159451Srodrigc * Copyright (c) 2000-2001,2005 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_log.h" 22153323Srodrigc#include "xfs_inum.h" 23153323Srodrigc#include "xfs_trans.h" 24153323Srodrigc#include "xfs_sb.h" 25153323Srodrigc#include "xfs_dir.h" 26153323Srodrigc#include "xfs_dir2.h" 27153323Srodrigc#include "xfs_dmapi.h" 28153323Srodrigc#include "xfs_mount.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_alloc.h" 34153323Srodrigc#include "xfs_btree.h" 35153323Srodrigc#include "xfs_dir_sf.h" 36153323Srodrigc#include "xfs_dir2_sf.h" 37159451Srodrigc#include "xfs_attr_sf.h" 38153323Srodrigc#include "xfs_dinode.h" 39153323Srodrigc#include "xfs_inode.h" 40153323Srodrigc#include "xfs_bmap.h" 41153323Srodrigc#include "xfs_dir_leaf.h" 42153323Srodrigc#include "xfs_error.h" 43153323Srodrigc 44153323Srodrigc/* 45153323Srodrigc * xfs_dir.c 46153323Srodrigc * 47153323Srodrigc * Provide the external interfaces to manage directories. 48153323Srodrigc */ 49153323Srodrigc 50153323Srodrigc/*======================================================================== 51153323Srodrigc * Function prototypes for the kernel. 52153323Srodrigc *========================================================================*/ 53153323Srodrigc 54153323Srodrigc/* 55153323Srodrigc * Functions for the dirops interfaces. 56153323Srodrigc */ 57153323Srodrigcstatic void xfs_dir_mount(struct xfs_mount *mp); 58153323Srodrigc 59153323Srodrigcstatic int xfs_dir_isempty(struct xfs_inode *dp); 60153323Srodrigc 61153323Srodrigcstatic int xfs_dir_init(struct xfs_trans *trans, 62153323Srodrigc struct xfs_inode *dir, 63153323Srodrigc struct xfs_inode *parent_dir); 64153323Srodrigc 65153323Srodrigcstatic int xfs_dir_createname(struct xfs_trans *trans, 66153323Srodrigc struct xfs_inode *dp, 67153323Srodrigc char *name_string, 68153323Srodrigc int name_len, 69153323Srodrigc xfs_ino_t inode_number, 70153323Srodrigc xfs_fsblock_t *firstblock, 71153323Srodrigc xfs_bmap_free_t *flist, 72153323Srodrigc xfs_extlen_t total); 73153323Srodrigc 74153323Srodrigcstatic int xfs_dir_lookup(struct xfs_trans *tp, 75153323Srodrigc struct xfs_inode *dp, 76153323Srodrigc char *name_string, 77153323Srodrigc int name_length, 78153323Srodrigc xfs_ino_t *inode_number); 79153323Srodrigc 80153323Srodrigcstatic int xfs_dir_removename(struct xfs_trans *trans, 81153323Srodrigc struct xfs_inode *dp, 82153323Srodrigc char *name_string, 83153323Srodrigc int name_length, 84153323Srodrigc xfs_ino_t ino, 85153323Srodrigc xfs_fsblock_t *firstblock, 86153323Srodrigc xfs_bmap_free_t *flist, 87153323Srodrigc xfs_extlen_t total); 88153323Srodrigc 89153323Srodrigcstatic int xfs_dir_getdents(struct xfs_trans *tp, 90153323Srodrigc struct xfs_inode *dp, 91153323Srodrigc struct uio *uiop, 92153323Srodrigc int *eofp); 93153323Srodrigc 94153323Srodrigcstatic int xfs_dir_replace(struct xfs_trans *tp, 95153323Srodrigc struct xfs_inode *dp, 96153323Srodrigc char *name_string, 97153323Srodrigc int name_length, 98153323Srodrigc xfs_ino_t inode_number, 99153323Srodrigc xfs_fsblock_t *firstblock, 100153323Srodrigc xfs_bmap_free_t *flist, 101153323Srodrigc xfs_extlen_t total); 102153323Srodrigc 103153323Srodrigcstatic int xfs_dir_canenter(struct xfs_trans *tp, 104153323Srodrigc struct xfs_inode *dp, 105153323Srodrigc char *name_string, 106153323Srodrigc int name_length); 107153323Srodrigc 108153323Srodrigcstatic int xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, 109153323Srodrigc xfs_dinode_t *dip); 110153323Srodrigc 111153323Srodrigcxfs_dirops_t xfsv1_dirops = { 112153323Srodrigc .xd_mount = xfs_dir_mount, 113153323Srodrigc .xd_isempty = xfs_dir_isempty, 114153323Srodrigc .xd_init = xfs_dir_init, 115153323Srodrigc .xd_createname = xfs_dir_createname, 116153323Srodrigc .xd_lookup = xfs_dir_lookup, 117153323Srodrigc .xd_removename = xfs_dir_removename, 118153323Srodrigc .xd_getdents = xfs_dir_getdents, 119153323Srodrigc .xd_replace = xfs_dir_replace, 120153323Srodrigc .xd_canenter = xfs_dir_canenter, 121153323Srodrigc .xd_shortform_validate_ondisk = xfs_dir_shortform_validate_ondisk, 122153323Srodrigc .xd_shortform_to_single = xfs_dir_shortform_to_leaf, 123153323Srodrigc}; 124153323Srodrigc 125153323Srodrigc/* 126153323Srodrigc * Internal routines when dirsize == XFS_LBSIZE(mp). 127153323Srodrigc */ 128153323SrodrigcSTATIC int xfs_dir_leaf_lookup(xfs_da_args_t *args); 129153323SrodrigcSTATIC int xfs_dir_leaf_removename(xfs_da_args_t *args, int *number_entries, 130153323Srodrigc int *total_namebytes); 131153323SrodrigcSTATIC int xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, 132153323Srodrigc uio_t *uio, int *eofp, 133153323Srodrigc xfs_dirent_t *dbp, 134153323Srodrigc xfs_dir_put_t put); 135153323SrodrigcSTATIC int xfs_dir_leaf_replace(xfs_da_args_t *args); 136153323Srodrigc 137153323Srodrigc/* 138153323Srodrigc * Internal routines when dirsize > XFS_LBSIZE(mp). 139153323Srodrigc */ 140153323SrodrigcSTATIC int xfs_dir_node_addname(xfs_da_args_t *args); 141153323SrodrigcSTATIC int xfs_dir_node_lookup(xfs_da_args_t *args); 142153323SrodrigcSTATIC int xfs_dir_node_removename(xfs_da_args_t *args); 143153323SrodrigcSTATIC int xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, 144153323Srodrigc uio_t *uio, int *eofp, 145153323Srodrigc xfs_dirent_t *dbp, 146153323Srodrigc xfs_dir_put_t put); 147153323SrodrigcSTATIC int xfs_dir_node_replace(xfs_da_args_t *args); 148153323Srodrigc 149153323Srodrigc#if defined(XFS_DIR_TRACE) 150153323Srodrigcktrace_t *xfs_dir_trace_buf; 151153323Srodrigc#endif 152153323Srodrigc 153153323Srodrigc 154153323Srodrigc/*======================================================================== 155153323Srodrigc * Overall external interface routines. 156153323Srodrigc *========================================================================*/ 157153323Srodrigc 158153323Srodrigcxfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot; 159153323Srodrigc 160153323Srodrigc/* 161153323Srodrigc * One-time startup routine called from xfs_init(). 162153323Srodrigc */ 163153323Srodrigcvoid 164153323Srodrigcxfs_dir_startup(void) 165153323Srodrigc{ 166153323Srodrigc xfs_dir_hash_dot = xfs_da_hashname(".", 1); 167153323Srodrigc xfs_dir_hash_dotdot = xfs_da_hashname("..", 2); 168153323Srodrigc} 169153323Srodrigc 170153323Srodrigc/* 171153323Srodrigc * Initialize directory-related fields in the mount structure. 172153323Srodrigc */ 173153323Srodrigcstatic void 174153323Srodrigcxfs_dir_mount(xfs_mount_t *mp) 175153323Srodrigc{ 176153323Srodrigc uint shortcount, leafcount, count; 177153323Srodrigc 178153323Srodrigc mp->m_dirversion = 1; 179159451Srodrigc if (!(mp->m_flags & XFS_MOUNT_ATTR2)) { 180159451Srodrigc shortcount = (mp->m_attroffset - 181159451Srodrigc (uint)sizeof(xfs_dir_sf_hdr_t)) / 182159451Srodrigc (uint)sizeof(xfs_dir_sf_entry_t); 183159451Srodrigc leafcount = (XFS_LBSIZE(mp) - 184159451Srodrigc (uint)sizeof(xfs_dir_leaf_hdr_t)) / 185159451Srodrigc ((uint)sizeof(xfs_dir_leaf_entry_t) + 186159451Srodrigc (uint)sizeof(xfs_dir_leaf_name_t)); 187159451Srodrigc } else { 188159451Srodrigc shortcount = (XFS_BMDR_SPACE_CALC(MINABTPTRS) - 189159451Srodrigc (uint)sizeof(xfs_dir_sf_hdr_t)) / 190159451Srodrigc (uint)sizeof(xfs_dir_sf_entry_t); 191159451Srodrigc leafcount = (XFS_LBSIZE(mp) - 192159451Srodrigc (uint)sizeof(xfs_dir_leaf_hdr_t)) / 193159451Srodrigc ((uint)sizeof(xfs_dir_leaf_entry_t) + 194159451Srodrigc (uint)sizeof(xfs_dir_leaf_name_t)); 195159451Srodrigc } 196153323Srodrigc count = shortcount > leafcount ? shortcount : leafcount; 197153323Srodrigc mp->m_dircook_elog = xfs_da_log2_roundup(count + 1); 198153323Srodrigc ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog); 199153323Srodrigc mp->m_dir_node_ents = mp->m_attr_node_ents = 200153323Srodrigc (XFS_LBSIZE(mp) - (uint)sizeof(xfs_da_node_hdr_t)) / 201153323Srodrigc (uint)sizeof(xfs_da_node_entry_t); 202153323Srodrigc mp->m_dir_magicpct = (XFS_LBSIZE(mp) * 37) / 100; 203153323Srodrigc mp->m_dirblksize = mp->m_sb.sb_blocksize; 204153323Srodrigc mp->m_dirblkfsbs = 1; 205153323Srodrigc} 206153323Srodrigc 207153323Srodrigc/* 208153323Srodrigc * Return 1 if directory contains only "." and "..". 209153323Srodrigc */ 210153323Srodrigcstatic int 211153323Srodrigcxfs_dir_isempty(xfs_inode_t *dp) 212153323Srodrigc{ 213153323Srodrigc xfs_dir_sf_hdr_t *hdr; 214153323Srodrigc 215153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 216153323Srodrigc if (dp->i_d.di_size == 0) 217153323Srodrigc return(1); 218153323Srodrigc if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp)) 219153323Srodrigc return(0); 220153323Srodrigc hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data; 221153323Srodrigc return(hdr->count == 0); 222153323Srodrigc} 223153323Srodrigc 224153323Srodrigc/* 225153323Srodrigc * Initialize a directory with its "." and ".." entries. 226153323Srodrigc */ 227153323Srodrigcstatic int 228153323Srodrigcxfs_dir_init(xfs_trans_t *trans, xfs_inode_t *dir, xfs_inode_t *parent_dir) 229153323Srodrigc{ 230153323Srodrigc xfs_da_args_t args; 231153323Srodrigc int error; 232153323Srodrigc 233153323Srodrigc memset((char *)&args, 0, sizeof(args)); 234153323Srodrigc args.dp = dir; 235153323Srodrigc args.trans = trans; 236153323Srodrigc 237153323Srodrigc ASSERT((dir->i_d.di_mode & S_IFMT) == S_IFDIR); 238153323Srodrigc if ((error = xfs_dir_ino_validate(trans->t_mountp, parent_dir->i_ino))) 239153323Srodrigc return error; 240153323Srodrigc 241153323Srodrigc return(xfs_dir_shortform_create(&args, parent_dir->i_ino)); 242153323Srodrigc} 243153323Srodrigc 244153323Srodrigc/* 245153323Srodrigc * Generic handler routine to add a name to a directory. 246153323Srodrigc * Transitions directory from shortform to Btree as necessary. 247153323Srodrigc */ 248153323Srodrigcstatic int /* error */ 249153323Srodrigcxfs_dir_createname(xfs_trans_t *trans, xfs_inode_t *dp, char *name, 250153323Srodrigc int namelen, xfs_ino_t inum, xfs_fsblock_t *firstblock, 251153323Srodrigc xfs_bmap_free_t *flist, xfs_extlen_t total) 252153323Srodrigc{ 253153323Srodrigc xfs_da_args_t args; 254153323Srodrigc int retval, newsize, done; 255153323Srodrigc 256153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 257153323Srodrigc 258153323Srodrigc if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum))) 259153323Srodrigc return (retval); 260153323Srodrigc 261153323Srodrigc XFS_STATS_INC(xs_dir_create); 262153323Srodrigc /* 263153323Srodrigc * Fill in the arg structure for this request. 264153323Srodrigc */ 265153323Srodrigc args.name = name; 266153323Srodrigc args.namelen = namelen; 267153323Srodrigc args.hashval = xfs_da_hashname(name, namelen); 268153323Srodrigc args.inumber = inum; 269153323Srodrigc args.dp = dp; 270153323Srodrigc args.firstblock = firstblock; 271153323Srodrigc args.flist = flist; 272153323Srodrigc args.total = total; 273153323Srodrigc args.whichfork = XFS_DATA_FORK; 274153323Srodrigc args.trans = trans; 275153323Srodrigc args.justcheck = 0; 276153323Srodrigc args.addname = args.oknoent = 1; 277153323Srodrigc 278153323Srodrigc /* 279153323Srodrigc * Decide on what work routines to call based on the inode size. 280153323Srodrigc */ 281153323Srodrigc done = 0; 282153323Srodrigc if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { 283153323Srodrigc newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen); 284153323Srodrigc if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) { 285153323Srodrigc retval = xfs_dir_shortform_addname(&args); 286153323Srodrigc done = 1; 287153323Srodrigc } else { 288153323Srodrigc if (total == 0) 289153323Srodrigc return XFS_ERROR(ENOSPC); 290153323Srodrigc retval = xfs_dir_shortform_to_leaf(&args); 291153323Srodrigc done = retval != 0; 292153323Srodrigc } 293153323Srodrigc } 294153323Srodrigc if (!done && xfs_bmap_one_block(dp, XFS_DATA_FORK)) { 295153323Srodrigc retval = xfs_dir_leaf_addname(&args); 296153323Srodrigc done = retval != ENOSPC; 297153323Srodrigc if (!done) { 298153323Srodrigc if (total == 0) 299153323Srodrigc return XFS_ERROR(ENOSPC); 300153323Srodrigc retval = xfs_dir_leaf_to_node(&args); 301153323Srodrigc done = retval != 0; 302153323Srodrigc } 303153323Srodrigc } 304153323Srodrigc if (!done) { 305153323Srodrigc retval = xfs_dir_node_addname(&args); 306153323Srodrigc } 307153323Srodrigc return(retval); 308153323Srodrigc} 309153323Srodrigc 310153323Srodrigc/* 311153323Srodrigc * Generic handler routine to check if a name can be added to a directory, 312153323Srodrigc * without adding any blocks to the directory. 313153323Srodrigc */ 314153323Srodrigcstatic int /* error */ 315153323Srodrigcxfs_dir_canenter(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen) 316153323Srodrigc{ 317153323Srodrigc xfs_da_args_t args; 318153323Srodrigc int retval, newsize; 319153323Srodrigc 320153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 321153323Srodrigc /* 322153323Srodrigc * Fill in the arg structure for this request. 323153323Srodrigc */ 324153323Srodrigc args.name = name; 325153323Srodrigc args.namelen = namelen; 326153323Srodrigc args.hashval = xfs_da_hashname(name, namelen); 327153323Srodrigc args.inumber = 0; 328153323Srodrigc args.dp = dp; 329153323Srodrigc args.firstblock = NULL; 330153323Srodrigc args.flist = NULL; 331153323Srodrigc args.total = 0; 332153323Srodrigc args.whichfork = XFS_DATA_FORK; 333153323Srodrigc args.trans = trans; 334153323Srodrigc args.justcheck = args.addname = args.oknoent = 1; 335153323Srodrigc 336153323Srodrigc /* 337153323Srodrigc * Decide on what work routines to call based on the inode size. 338153323Srodrigc */ 339153323Srodrigc if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { 340153323Srodrigc newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen); 341153323Srodrigc if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) 342153323Srodrigc retval = 0; 343153323Srodrigc else 344153323Srodrigc retval = XFS_ERROR(ENOSPC); 345153323Srodrigc } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { 346153323Srodrigc retval = xfs_dir_leaf_addname(&args); 347153323Srodrigc } else { 348153323Srodrigc retval = xfs_dir_node_addname(&args); 349153323Srodrigc } 350153323Srodrigc return(retval); 351153323Srodrigc} 352153323Srodrigc 353153323Srodrigc/* 354153323Srodrigc * Generic handler routine to remove a name from a directory. 355153323Srodrigc * Transitions directory from Btree to shortform as necessary. 356153323Srodrigc */ 357153323Srodrigcstatic int /* error */ 358153323Srodrigcxfs_dir_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name, 359153323Srodrigc int namelen, xfs_ino_t ino, xfs_fsblock_t *firstblock, 360153323Srodrigc xfs_bmap_free_t *flist, xfs_extlen_t total) 361153323Srodrigc{ 362153323Srodrigc xfs_da_args_t args; 363153323Srodrigc int count, totallen, newsize, retval; 364153323Srodrigc 365153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 366153323Srodrigc XFS_STATS_INC(xs_dir_remove); 367153323Srodrigc /* 368153323Srodrigc * Fill in the arg structure for this request. 369153323Srodrigc */ 370153323Srodrigc args.name = name; 371153323Srodrigc args.namelen = namelen; 372153323Srodrigc args.hashval = xfs_da_hashname(name, namelen); 373153323Srodrigc args.inumber = ino; 374153323Srodrigc args.dp = dp; 375153323Srodrigc args.firstblock = firstblock; 376153323Srodrigc args.flist = flist; 377153323Srodrigc args.total = total; 378153323Srodrigc args.whichfork = XFS_DATA_FORK; 379153323Srodrigc args.trans = trans; 380153323Srodrigc args.justcheck = args.addname = args.oknoent = 0; 381153323Srodrigc 382153323Srodrigc /* 383153323Srodrigc * Decide on what work routines to call based on the inode size. 384153323Srodrigc */ 385153323Srodrigc if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { 386153323Srodrigc retval = xfs_dir_shortform_removename(&args); 387153323Srodrigc } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { 388170124Skan count = totallen = 0; 389153323Srodrigc retval = xfs_dir_leaf_removename(&args, &count, &totallen); 390153323Srodrigc if (retval == 0) { 391153323Srodrigc newsize = XFS_DIR_SF_ALLFIT(count, totallen); 392153323Srodrigc if (newsize <= XFS_IFORK_DSIZE(dp)) { 393153323Srodrigc retval = xfs_dir_leaf_to_shortform(&args); 394153323Srodrigc } 395153323Srodrigc } 396153323Srodrigc } else { 397153323Srodrigc retval = xfs_dir_node_removename(&args); 398153323Srodrigc } 399153323Srodrigc return(retval); 400153323Srodrigc} 401153323Srodrigc 402153323Srodrigcstatic int /* error */ 403153323Srodrigcxfs_dir_lookup(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen, 404153323Srodrigc xfs_ino_t *inum) 405153323Srodrigc{ 406153323Srodrigc xfs_da_args_t args; 407153323Srodrigc int retval; 408153323Srodrigc 409153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 410153323Srodrigc 411153323Srodrigc XFS_STATS_INC(xs_dir_lookup); 412153323Srodrigc /* 413153323Srodrigc * Fill in the arg structure for this request. 414153323Srodrigc */ 415153323Srodrigc args.name = name; 416153323Srodrigc args.namelen = namelen; 417153323Srodrigc args.hashval = xfs_da_hashname(name, namelen); 418153323Srodrigc args.inumber = 0; 419153323Srodrigc args.dp = dp; 420153323Srodrigc args.firstblock = NULL; 421153323Srodrigc args.flist = NULL; 422153323Srodrigc args.total = 0; 423153323Srodrigc args.whichfork = XFS_DATA_FORK; 424153323Srodrigc args.trans = trans; 425153323Srodrigc args.justcheck = args.addname = 0; 426153323Srodrigc args.oknoent = 1; 427153323Srodrigc 428153323Srodrigc /* 429153323Srodrigc * Decide on what work routines to call based on the inode size. 430153323Srodrigc */ 431153323Srodrigc if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { 432153323Srodrigc retval = xfs_dir_shortform_lookup(&args); 433153323Srodrigc } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { 434153323Srodrigc retval = xfs_dir_leaf_lookup(&args); 435153323Srodrigc } else { 436153323Srodrigc retval = xfs_dir_node_lookup(&args); 437153323Srodrigc } 438153323Srodrigc if (retval == EEXIST) 439153323Srodrigc retval = 0; 440153323Srodrigc *inum = args.inumber; 441153323Srodrigc return(retval); 442153323Srodrigc} 443153323Srodrigc 444153323Srodrigc/* 445153323Srodrigc * Implement readdir. 446153323Srodrigc */ 447153323Srodrigcstatic int /* error */ 448153323Srodrigcxfs_dir_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp) 449153323Srodrigc{ 450153323Srodrigc xfs_dirent_t *dbp; 451153323Srodrigc int alignment, retval; 452153323Srodrigc xfs_dir_put_t put; 453153323Srodrigc 454153323Srodrigc XFS_STATS_INC(xs_dir_getdents); 455153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 456153323Srodrigc 457153323Srodrigc /* 458153323Srodrigc * If our caller has given us a single contiguous memory buffer, 459153323Srodrigc * just work directly within that buffer. If it's in user memory, 460153323Srodrigc * lock it down first. 461153323Srodrigc */ 462153323Srodrigc alignment = sizeof(xfs_off_t) - 1; 463153323Srodrigc if ((uio->uio_iovcnt == 1) && 464153323Srodrigc (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) && 465153323Srodrigc ((uio->uio_iov[0].iov_len & alignment) == 0)) { 466153323Srodrigc dbp = NULL; 467153323Srodrigc put = xfs_dir_put_dirent64_direct; 468153323Srodrigc } else { 469153323Srodrigc dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP); 470153323Srodrigc put = xfs_dir_put_dirent64_uio; 471153323Srodrigc } 472153323Srodrigc 473153323Srodrigc /* 474153323Srodrigc * Decide on what work routines to call based on the inode size. 475153323Srodrigc */ 476153323Srodrigc *eofp = 0; 477153323Srodrigc 478153323Srodrigc if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { 479153323Srodrigc retval = xfs_dir_shortform_getdents(dp, uio, eofp, dbp, put); 480153323Srodrigc } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { 481153323Srodrigc retval = xfs_dir_leaf_getdents(trans, dp, uio, eofp, dbp, put); 482153323Srodrigc } else { 483153323Srodrigc retval = xfs_dir_node_getdents(trans, dp, uio, eofp, dbp, put); 484153323Srodrigc } 485153323Srodrigc if (dbp != NULL) 486153323Srodrigc kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN); 487153323Srodrigc 488153323Srodrigc return(retval); 489153323Srodrigc} 490153323Srodrigc 491153323Srodrigcstatic int /* error */ 492153323Srodrigcxfs_dir_replace(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen, 493153323Srodrigc xfs_ino_t inum, xfs_fsblock_t *firstblock, 494153323Srodrigc xfs_bmap_free_t *flist, xfs_extlen_t total) 495153323Srodrigc{ 496153323Srodrigc xfs_da_args_t args; 497153323Srodrigc int retval; 498153323Srodrigc 499153323Srodrigc ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); 500153323Srodrigc 501153323Srodrigc if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum))) 502153323Srodrigc return retval; 503153323Srodrigc 504153323Srodrigc /* 505153323Srodrigc * Fill in the arg structure for this request. 506153323Srodrigc */ 507153323Srodrigc args.name = name; 508153323Srodrigc args.namelen = namelen; 509153323Srodrigc args.hashval = xfs_da_hashname(name, namelen); 510153323Srodrigc args.inumber = inum; 511153323Srodrigc args.dp = dp; 512153323Srodrigc args.firstblock = firstblock; 513153323Srodrigc args.flist = flist; 514153323Srodrigc args.total = total; 515153323Srodrigc args.whichfork = XFS_DATA_FORK; 516153323Srodrigc args.trans = trans; 517153323Srodrigc args.justcheck = args.addname = args.oknoent = 0; 518153323Srodrigc 519153323Srodrigc /* 520153323Srodrigc * Decide on what work routines to call based on the inode size. 521153323Srodrigc */ 522153323Srodrigc if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { 523153323Srodrigc retval = xfs_dir_shortform_replace(&args); 524153323Srodrigc } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { 525153323Srodrigc retval = xfs_dir_leaf_replace(&args); 526153323Srodrigc } else { 527153323Srodrigc retval = xfs_dir_node_replace(&args); 528153323Srodrigc } 529153323Srodrigc 530153323Srodrigc return(retval); 531153323Srodrigc} 532153323Srodrigc 533153323Srodrigcstatic int 534153323Srodrigcxfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, xfs_dinode_t *dp) 535153323Srodrigc{ 536153323Srodrigc xfs_ino_t ino; 537153323Srodrigc int namelen_sum; 538153323Srodrigc int count; 539153323Srodrigc xfs_dir_shortform_t *sf; 540153323Srodrigc xfs_dir_sf_entry_t *sfe; 541153323Srodrigc int i; 542153323Srodrigc 543153323Srodrigc 544153323Srodrigc 545153323Srodrigc if ((INT_GET(dp->di_core.di_mode, ARCH_CONVERT) & S_IFMT) != S_IFDIR) { 546153323Srodrigc return 0; 547153323Srodrigc } 548153323Srodrigc if (INT_GET(dp->di_core.di_format, ARCH_CONVERT) != XFS_DINODE_FMT_LOCAL) { 549153323Srodrigc return 0; 550153323Srodrigc } 551153323Srodrigc if (INT_GET(dp->di_core.di_size, ARCH_CONVERT) < sizeof(sf->hdr)) { 552153323Srodrigc xfs_fs_cmn_err(CE_WARN, mp, "Invalid shortform size: dp 0x%p", 553153323Srodrigc dp); 554153323Srodrigc return 1; 555153323Srodrigc } 556153323Srodrigc sf = (xfs_dir_shortform_t *)(&dp->di_u.di_dirsf); 557159451Srodrigc ino = XFS_GET_DIR_INO8(sf->hdr.parent); 558153323Srodrigc if (xfs_dir_ino_validate(mp, ino)) 559153323Srodrigc return 1; 560153323Srodrigc 561153323Srodrigc count = sf->hdr.count; 562153323Srodrigc if ((count < 0) || ((count * 10) > XFS_LITINO(mp))) { 563153323Srodrigc xfs_fs_cmn_err(CE_WARN, mp, 564153323Srodrigc "Invalid shortform count: dp 0x%p", dp); 565153323Srodrigc return(1); 566153323Srodrigc } 567153323Srodrigc 568153323Srodrigc if (count == 0) { 569153323Srodrigc return 0; 570153323Srodrigc } 571153323Srodrigc 572153323Srodrigc namelen_sum = 0; 573153323Srodrigc sfe = &sf->list[0]; 574153323Srodrigc for (i = sf->hdr.count - 1; i >= 0; i--) { 575159451Srodrigc ino = XFS_GET_DIR_INO8(sfe->inumber); 576153323Srodrigc xfs_dir_ino_validate(mp, ino); 577153323Srodrigc if (sfe->namelen >= XFS_LITINO(mp)) { 578153323Srodrigc xfs_fs_cmn_err(CE_WARN, mp, 579153323Srodrigc "Invalid shortform namelen: dp 0x%p", dp); 580153323Srodrigc return 1; 581153323Srodrigc } 582153323Srodrigc namelen_sum += sfe->namelen; 583153323Srodrigc sfe = XFS_DIR_SF_NEXTENTRY(sfe); 584153323Srodrigc } 585153323Srodrigc if (namelen_sum >= XFS_LITINO(mp)) { 586153323Srodrigc xfs_fs_cmn_err(CE_WARN, mp, 587153323Srodrigc "Invalid shortform namelen: dp 0x%p", dp); 588153323Srodrigc return 1; 589153323Srodrigc } 590153323Srodrigc 591153323Srodrigc return 0; 592153323Srodrigc} 593153323Srodrigc 594153323Srodrigc/*======================================================================== 595153323Srodrigc * External routines when dirsize == XFS_LBSIZE(dp->i_mount). 596153323Srodrigc *========================================================================*/ 597153323Srodrigc 598153323Srodrigc/* 599153323Srodrigc * Add a name to the leaf directory structure 600153323Srodrigc * This is the external routine. 601153323Srodrigc */ 602153323Srodrigcint 603153323Srodrigcxfs_dir_leaf_addname(xfs_da_args_t *args) 604153323Srodrigc{ 605153323Srodrigc int index, retval; 606153323Srodrigc xfs_dabuf_t *bp; 607153323Srodrigc 608153323Srodrigc retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, 609153323Srodrigc XFS_DATA_FORK); 610153323Srodrigc if (retval) 611153323Srodrigc return(retval); 612153323Srodrigc ASSERT(bp != NULL); 613153323Srodrigc 614153323Srodrigc retval = xfs_dir_leaf_lookup_int(bp, args, &index); 615153323Srodrigc if (retval == ENOENT) 616153323Srodrigc retval = xfs_dir_leaf_add(bp, args, index); 617153323Srodrigc xfs_da_buf_done(bp); 618153323Srodrigc return(retval); 619153323Srodrigc} 620153323Srodrigc 621153323Srodrigc/* 622153323Srodrigc * Remove a name from the leaf directory structure 623153323Srodrigc * This is the external routine. 624153323Srodrigc */ 625153323SrodrigcSTATIC int 626153323Srodrigcxfs_dir_leaf_removename(xfs_da_args_t *args, int *count, int *totallen) 627153323Srodrigc{ 628153323Srodrigc xfs_dir_leafblock_t *leaf; 629153323Srodrigc int index, retval; 630153323Srodrigc xfs_dabuf_t *bp; 631153323Srodrigc 632153323Srodrigc retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, 633153323Srodrigc XFS_DATA_FORK); 634153323Srodrigc if (retval) 635153323Srodrigc return(retval); 636153323Srodrigc ASSERT(bp != NULL); 637153323Srodrigc leaf = bp->data; 638159451Srodrigc ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); 639153323Srodrigc retval = xfs_dir_leaf_lookup_int(bp, args, &index); 640153323Srodrigc if (retval == EEXIST) { 641153323Srodrigc (void)xfs_dir_leaf_remove(args->trans, bp, index); 642153323Srodrigc *count = INT_GET(leaf->hdr.count, ARCH_CONVERT); 643153323Srodrigc *totallen = INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); 644153323Srodrigc retval = 0; 645153323Srodrigc } 646153323Srodrigc xfs_da_buf_done(bp); 647153323Srodrigc return(retval); 648153323Srodrigc} 649153323Srodrigc 650153323Srodrigc/* 651153323Srodrigc * Look up a name in a leaf directory structure. 652153323Srodrigc * This is the external routine. 653153323Srodrigc */ 654153323SrodrigcSTATIC int 655153323Srodrigcxfs_dir_leaf_lookup(xfs_da_args_t *args) 656153323Srodrigc{ 657153323Srodrigc int index, retval; 658153323Srodrigc xfs_dabuf_t *bp; 659153323Srodrigc 660153323Srodrigc retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, 661153323Srodrigc XFS_DATA_FORK); 662153323Srodrigc if (retval) 663153323Srodrigc return(retval); 664153323Srodrigc ASSERT(bp != NULL); 665153323Srodrigc retval = xfs_dir_leaf_lookup_int(bp, args, &index); 666153323Srodrigc xfs_da_brelse(args->trans, bp); 667153323Srodrigc return(retval); 668153323Srodrigc} 669153323Srodrigc 670153323Srodrigc/* 671153323Srodrigc * Copy out directory entries for getdents(), for leaf directories. 672153323Srodrigc */ 673153323SrodrigcSTATIC int 674153323Srodrigcxfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, 675153323Srodrigc int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put) 676153323Srodrigc{ 677153323Srodrigc xfs_dabuf_t *bp; 678153323Srodrigc int retval, eob; 679153323Srodrigc 680153323Srodrigc retval = xfs_da_read_buf(dp->i_transp, dp, 0, -1, &bp, XFS_DATA_FORK); 681153323Srodrigc if (retval) 682153323Srodrigc return(retval); 683153323Srodrigc ASSERT(bp != NULL); 684153323Srodrigc retval = xfs_dir_leaf_getdents_int(bp, dp, 0, uio, &eob, dbp, put, -1); 685153323Srodrigc xfs_da_brelse(trans, bp); 686153323Srodrigc *eofp = (eob == 0); 687153323Srodrigc return(retval); 688153323Srodrigc} 689153323Srodrigc 690153323Srodrigc/* 691153323Srodrigc * Look up a name in a leaf directory structure, replace the inode number. 692153323Srodrigc * This is the external routine. 693153323Srodrigc */ 694153323SrodrigcSTATIC int 695153323Srodrigcxfs_dir_leaf_replace(xfs_da_args_t *args) 696153323Srodrigc{ 697153323Srodrigc int index, retval; 698153323Srodrigc xfs_dabuf_t *bp; 699153323Srodrigc xfs_ino_t inum; 700153323Srodrigc xfs_dir_leafblock_t *leaf; 701153323Srodrigc xfs_dir_leaf_entry_t *entry; 702153323Srodrigc xfs_dir_leaf_name_t *namest; 703153323Srodrigc 704153323Srodrigc inum = args->inumber; 705153323Srodrigc retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, 706153323Srodrigc XFS_DATA_FORK); 707153323Srodrigc if (retval) 708153323Srodrigc return(retval); 709153323Srodrigc ASSERT(bp != NULL); 710153323Srodrigc retval = xfs_dir_leaf_lookup_int(bp, args, &index); 711153323Srodrigc if (retval == EEXIST) { 712153323Srodrigc leaf = bp->data; 713153323Srodrigc entry = &leaf->entries[index]; 714153323Srodrigc namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); 715153323Srodrigc /* XXX - replace assert? */ 716159451Srodrigc XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber); 717153323Srodrigc xfs_da_log_buf(args->trans, bp, 718153323Srodrigc XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber))); 719153323Srodrigc xfs_da_buf_done(bp); 720153323Srodrigc retval = 0; 721153323Srodrigc } else 722153323Srodrigc xfs_da_brelse(args->trans, bp); 723153323Srodrigc return(retval); 724153323Srodrigc} 725153323Srodrigc 726153323Srodrigc 727153323Srodrigc/*======================================================================== 728153323Srodrigc * External routines when dirsize > XFS_LBSIZE(mp). 729153323Srodrigc *========================================================================*/ 730153323Srodrigc 731153323Srodrigc/* 732153323Srodrigc * Add a name to a Btree-format directory. 733153323Srodrigc * 734153323Srodrigc * This will involve walking down the Btree, and may involve splitting 735153323Srodrigc * leaf nodes and even splitting intermediate nodes up to and including 736153323Srodrigc * the root node (a special case of an intermediate node). 737153323Srodrigc */ 738153323SrodrigcSTATIC int 739153323Srodrigcxfs_dir_node_addname(xfs_da_args_t *args) 740153323Srodrigc{ 741153323Srodrigc xfs_da_state_t *state; 742153323Srodrigc xfs_da_state_blk_t *blk; 743153323Srodrigc int retval, error; 744153323Srodrigc 745153323Srodrigc /* 746153323Srodrigc * Fill in bucket of arguments/results/context to carry around. 747153323Srodrigc */ 748153323Srodrigc state = xfs_da_state_alloc(); 749153323Srodrigc state->args = args; 750153323Srodrigc state->mp = args->dp->i_mount; 751153323Srodrigc state->blocksize = state->mp->m_sb.sb_blocksize; 752153323Srodrigc state->node_ents = state->mp->m_dir_node_ents; 753153323Srodrigc 754153323Srodrigc /* 755153323Srodrigc * Search to see if name already exists, and get back a pointer 756153323Srodrigc * to where it should go. 757153323Srodrigc */ 758153323Srodrigc error = xfs_da_node_lookup_int(state, &retval); 759153323Srodrigc if (error) 760153323Srodrigc retval = error; 761153323Srodrigc if (retval != ENOENT) 762153323Srodrigc goto error; 763153323Srodrigc blk = &state->path.blk[ state->path.active-1 ]; 764153323Srodrigc ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); 765153323Srodrigc retval = xfs_dir_leaf_add(blk->bp, args, blk->index); 766153323Srodrigc if (retval == 0) { 767153323Srodrigc /* 768153323Srodrigc * Addition succeeded, update Btree hashvals. 769153323Srodrigc */ 770153323Srodrigc if (!args->justcheck) 771153323Srodrigc xfs_da_fixhashpath(state, &state->path); 772153323Srodrigc } else { 773153323Srodrigc /* 774153323Srodrigc * Addition failed, split as many Btree elements as required. 775153323Srodrigc */ 776153323Srodrigc if (args->total == 0) { 777153323Srodrigc ASSERT(retval == ENOSPC); 778153323Srodrigc goto error; 779153323Srodrigc } 780153323Srodrigc retval = xfs_da_split(state); 781153323Srodrigc } 782153323Srodrigcerror: 783153323Srodrigc xfs_da_state_free(state); 784153323Srodrigc 785153323Srodrigc return(retval); 786153323Srodrigc} 787153323Srodrigc 788153323Srodrigc/* 789153323Srodrigc * Remove a name from a B-tree directory. 790153323Srodrigc * 791153323Srodrigc * This will involve walking down the Btree, and may involve joining 792153323Srodrigc * leaf nodes and even joining intermediate nodes up to and including 793153323Srodrigc * the root node (a special case of an intermediate node). 794153323Srodrigc */ 795153323SrodrigcSTATIC int 796153323Srodrigcxfs_dir_node_removename(xfs_da_args_t *args) 797153323Srodrigc{ 798153323Srodrigc xfs_da_state_t *state; 799153323Srodrigc xfs_da_state_blk_t *blk; 800153323Srodrigc int retval, error; 801153323Srodrigc 802153323Srodrigc state = xfs_da_state_alloc(); 803153323Srodrigc state->args = args; 804153323Srodrigc state->mp = args->dp->i_mount; 805153323Srodrigc state->blocksize = state->mp->m_sb.sb_blocksize; 806153323Srodrigc state->node_ents = state->mp->m_dir_node_ents; 807153323Srodrigc 808153323Srodrigc /* 809153323Srodrigc * Search to see if name exists, and get back a pointer to it. 810153323Srodrigc */ 811153323Srodrigc error = xfs_da_node_lookup_int(state, &retval); 812153323Srodrigc if (error) 813153323Srodrigc retval = error; 814153323Srodrigc if (retval != EEXIST) { 815153323Srodrigc xfs_da_state_free(state); 816153323Srodrigc return(retval); 817153323Srodrigc } 818153323Srodrigc 819153323Srodrigc /* 820153323Srodrigc * Remove the name and update the hashvals in the tree. 821153323Srodrigc */ 822153323Srodrigc blk = &state->path.blk[ state->path.active-1 ]; 823153323Srodrigc ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); 824153323Srodrigc retval = xfs_dir_leaf_remove(args->trans, blk->bp, blk->index); 825153323Srodrigc xfs_da_fixhashpath(state, &state->path); 826153323Srodrigc 827153323Srodrigc /* 828153323Srodrigc * Check to see if the tree needs to be collapsed. 829153323Srodrigc */ 830153323Srodrigc error = 0; 831153323Srodrigc if (retval) { 832153323Srodrigc error = xfs_da_join(state); 833153323Srodrigc } 834153323Srodrigc 835153323Srodrigc xfs_da_state_free(state); 836153323Srodrigc if (error) 837153323Srodrigc return(error); 838153323Srodrigc return(0); 839153323Srodrigc} 840153323Srodrigc 841153323Srodrigc/* 842153323Srodrigc * Look up a filename in a int directory. 843153323Srodrigc * Use an internal routine to actually do all the work. 844153323Srodrigc */ 845153323SrodrigcSTATIC int 846153323Srodrigcxfs_dir_node_lookup(xfs_da_args_t *args) 847153323Srodrigc{ 848153323Srodrigc xfs_da_state_t *state; 849153323Srodrigc int retval, error, i; 850153323Srodrigc 851153323Srodrigc state = xfs_da_state_alloc(); 852153323Srodrigc state->args = args; 853153323Srodrigc state->mp = args->dp->i_mount; 854153323Srodrigc state->blocksize = state->mp->m_sb.sb_blocksize; 855153323Srodrigc state->node_ents = state->mp->m_dir_node_ents; 856153323Srodrigc 857153323Srodrigc /* 858153323Srodrigc * Search to see if name exists, 859153323Srodrigc * and get back a pointer to it. 860153323Srodrigc */ 861153323Srodrigc error = xfs_da_node_lookup_int(state, &retval); 862153323Srodrigc if (error) { 863153323Srodrigc retval = error; 864153323Srodrigc } 865153323Srodrigc 866153323Srodrigc /* 867153323Srodrigc * If not in a transaction, we have to release all the buffers. 868153323Srodrigc */ 869153323Srodrigc for (i = 0; i < state->path.active; i++) { 870153323Srodrigc xfs_da_brelse(args->trans, state->path.blk[i].bp); 871153323Srodrigc state->path.blk[i].bp = NULL; 872153323Srodrigc } 873153323Srodrigc 874153323Srodrigc xfs_da_state_free(state); 875153323Srodrigc return(retval); 876153323Srodrigc} 877153323Srodrigc 878153323SrodrigcSTATIC int 879153323Srodrigcxfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, 880153323Srodrigc int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put) 881153323Srodrigc{ 882153323Srodrigc xfs_da_intnode_t *node; 883153323Srodrigc xfs_da_node_entry_t *btree; 884153323Srodrigc xfs_dir_leafblock_t *leaf = NULL; 885153323Srodrigc xfs_dablk_t bno, nextbno; 886153323Srodrigc xfs_dahash_t cookhash; 887153323Srodrigc xfs_mount_t *mp; 888153323Srodrigc int error, eob, i; 889153323Srodrigc xfs_dabuf_t *bp; 890153323Srodrigc xfs_daddr_t nextda; 891153323Srodrigc 892153323Srodrigc /* 893153323Srodrigc * Pick up our context. 894153323Srodrigc */ 895153323Srodrigc mp = dp->i_mount; 896153323Srodrigc bp = NULL; 897153323Srodrigc bno = XFS_DA_COOKIE_BNO(mp, uio->uio_offset); 898153323Srodrigc cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset); 899153323Srodrigc 900153323Srodrigc xfs_dir_trace_g_du("node: start", dp, uio); 901153323Srodrigc 902153323Srodrigc /* 903153323Srodrigc * Re-find our place, even if we're confused about what our place is. 904153323Srodrigc * 905153323Srodrigc * First we check the block number from the magic cookie, it is a 906153323Srodrigc * cache of where we ended last time. If we find a leaf block, and 907153323Srodrigc * the starting hashval in that block is less than our desired 908153323Srodrigc * hashval, then we run with it. 909153323Srodrigc */ 910153323Srodrigc if (bno > 0) { 911153323Srodrigc error = xfs_da_read_buf(trans, dp, bno, -2, &bp, XFS_DATA_FORK); 912153323Srodrigc if ((error != 0) && (error != EFSCORRUPTED)) 913153323Srodrigc return(error); 914153323Srodrigc if (bp) 915153323Srodrigc leaf = bp->data; 916159451Srodrigc if (bp && be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC) { 917153323Srodrigc xfs_dir_trace_g_dub("node: block not a leaf", 918153323Srodrigc dp, uio, bno); 919153323Srodrigc xfs_da_brelse(trans, bp); 920153323Srodrigc bp = NULL; 921153323Srodrigc } 922153323Srodrigc if (bp && INT_GET(leaf->entries[0].hashval, ARCH_CONVERT) > cookhash) { 923153323Srodrigc xfs_dir_trace_g_dub("node: leaf hash too large", 924153323Srodrigc dp, uio, bno); 925153323Srodrigc xfs_da_brelse(trans, bp); 926153323Srodrigc bp = NULL; 927153323Srodrigc } 928153323Srodrigc if (bp && 929153323Srodrigc cookhash > INT_GET(leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT)) { 930153323Srodrigc xfs_dir_trace_g_dub("node: leaf hash too small", 931153323Srodrigc dp, uio, bno); 932153323Srodrigc xfs_da_brelse(trans, bp); 933153323Srodrigc bp = NULL; 934153323Srodrigc } 935153323Srodrigc } 936153323Srodrigc 937153323Srodrigc /* 938153323Srodrigc * If we did not find a leaf block from the blockno in the cookie, 939153323Srodrigc * or we there was no blockno in the cookie (eg: first time thru), 940153323Srodrigc * the we start at the top of the Btree and re-find our hashval. 941153323Srodrigc */ 942153323Srodrigc if (bp == NULL) { 943153323Srodrigc xfs_dir_trace_g_du("node: start at root" , dp, uio); 944153323Srodrigc bno = 0; 945153323Srodrigc for (;;) { 946153323Srodrigc error = xfs_da_read_buf(trans, dp, bno, -1, &bp, 947153323Srodrigc XFS_DATA_FORK); 948153323Srodrigc if (error) 949153323Srodrigc return(error); 950153323Srodrigc if (bp == NULL) 951153323Srodrigc return(XFS_ERROR(EFSCORRUPTED)); 952153323Srodrigc node = bp->data; 953159451Srodrigc if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) 954153323Srodrigc break; 955153323Srodrigc btree = &node->btree[0]; 956153323Srodrigc xfs_dir_trace_g_dun("node: node detail", dp, uio, node); 957159451Srodrigc for (i = 0; i < be16_to_cpu(node->hdr.count); btree++, i++) { 958159451Srodrigc if (be32_to_cpu(btree->hashval) >= cookhash) { 959159451Srodrigc bno = be32_to_cpu(btree->before); 960153323Srodrigc break; 961153323Srodrigc } 962153323Srodrigc } 963159451Srodrigc if (i == be16_to_cpu(node->hdr.count)) { 964153323Srodrigc xfs_da_brelse(trans, bp); 965153323Srodrigc xfs_dir_trace_g_du("node: hash beyond EOF", 966153323Srodrigc dp, uio); 967153323Srodrigc uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0, 968153323Srodrigc XFS_DA_MAXHASH); 969153323Srodrigc *eofp = 1; 970153323Srodrigc return(0); 971153323Srodrigc } 972153323Srodrigc xfs_dir_trace_g_dub("node: going to block", 973153323Srodrigc dp, uio, bno); 974153323Srodrigc xfs_da_brelse(trans, bp); 975153323Srodrigc } 976153323Srodrigc } 977153323Srodrigc ASSERT(cookhash != XFS_DA_MAXHASH); 978153323Srodrigc 979153323Srodrigc /* 980153323Srodrigc * We've dropped down to the (first) leaf block that contains the 981153323Srodrigc * hashval we are interested in. Continue rolling upward thru the 982153323Srodrigc * leaf blocks until we fill up our buffer. 983153323Srodrigc */ 984153323Srodrigc for (;;) { 985153323Srodrigc leaf = bp->data; 986159451Srodrigc if (unlikely(be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC)) { 987153323Srodrigc xfs_dir_trace_g_dul("node: not a leaf", dp, uio, leaf); 988153323Srodrigc xfs_da_brelse(trans, bp); 989153323Srodrigc XFS_CORRUPTION_ERROR("xfs_dir_node_getdents(1)", 990153323Srodrigc XFS_ERRLEVEL_LOW, mp, leaf); 991153323Srodrigc return XFS_ERROR(EFSCORRUPTED); 992153323Srodrigc } 993153323Srodrigc xfs_dir_trace_g_dul("node: leaf detail", dp, uio, leaf); 994159451Srodrigc if ((nextbno = be32_to_cpu(leaf->hdr.info.forw))) { 995153323Srodrigc nextda = xfs_da_reada_buf(trans, dp, nextbno, 996153323Srodrigc XFS_DATA_FORK); 997153323Srodrigc } else 998153323Srodrigc nextda = -1; 999153323Srodrigc error = xfs_dir_leaf_getdents_int(bp, dp, bno, uio, &eob, dbp, 1000153323Srodrigc put, nextda); 1001153323Srodrigc xfs_da_brelse(trans, bp); 1002153323Srodrigc bno = nextbno; 1003153323Srodrigc if (eob) { 1004153323Srodrigc xfs_dir_trace_g_dub("node: E-O-B", dp, uio, bno); 1005153323Srodrigc *eofp = 0; 1006153323Srodrigc return(error); 1007153323Srodrigc } 1008153323Srodrigc if (bno == 0) 1009153323Srodrigc break; 1010153323Srodrigc error = xfs_da_read_buf(trans, dp, bno, nextda, &bp, 1011153323Srodrigc XFS_DATA_FORK); 1012153323Srodrigc if (error) 1013153323Srodrigc return(error); 1014153323Srodrigc if (unlikely(bp == NULL)) { 1015153323Srodrigc XFS_ERROR_REPORT("xfs_dir_node_getdents(2)", 1016153323Srodrigc XFS_ERRLEVEL_LOW, mp); 1017153323Srodrigc return(XFS_ERROR(EFSCORRUPTED)); 1018153323Srodrigc } 1019153323Srodrigc } 1020153323Srodrigc *eofp = 1; 1021153323Srodrigc xfs_dir_trace_g_du("node: E-O-F", dp, uio); 1022153323Srodrigc return(0); 1023153323Srodrigc} 1024153323Srodrigc 1025153323Srodrigc/* 1026153323Srodrigc * Look up a filename in an int directory, replace the inode number. 1027153323Srodrigc * Use an internal routine to actually do the lookup. 1028153323Srodrigc */ 1029153323SrodrigcSTATIC int 1030153323Srodrigcxfs_dir_node_replace(xfs_da_args_t *args) 1031153323Srodrigc{ 1032153323Srodrigc xfs_da_state_t *state; 1033153323Srodrigc xfs_da_state_blk_t *blk; 1034153323Srodrigc xfs_dir_leafblock_t *leaf; 1035153323Srodrigc xfs_dir_leaf_entry_t *entry; 1036153323Srodrigc xfs_dir_leaf_name_t *namest; 1037153323Srodrigc xfs_ino_t inum; 1038153323Srodrigc int retval, error, i; 1039153323Srodrigc xfs_dabuf_t *bp; 1040153323Srodrigc 1041153323Srodrigc state = xfs_da_state_alloc(); 1042153323Srodrigc state->args = args; 1043153323Srodrigc state->mp = args->dp->i_mount; 1044153323Srodrigc state->blocksize = state->mp->m_sb.sb_blocksize; 1045153323Srodrigc state->node_ents = state->mp->m_dir_node_ents; 1046153323Srodrigc inum = args->inumber; 1047153323Srodrigc 1048153323Srodrigc /* 1049153323Srodrigc * Search to see if name exists, 1050153323Srodrigc * and get back a pointer to it. 1051153323Srodrigc */ 1052153323Srodrigc error = xfs_da_node_lookup_int(state, &retval); 1053153323Srodrigc if (error) { 1054153323Srodrigc retval = error; 1055153323Srodrigc } 1056153323Srodrigc 1057153323Srodrigc if (retval == EEXIST) { 1058153323Srodrigc blk = &state->path.blk[state->path.active - 1]; 1059153323Srodrigc ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); 1060153323Srodrigc bp = blk->bp; 1061153323Srodrigc leaf = bp->data; 1062153323Srodrigc entry = &leaf->entries[blk->index]; 1063153323Srodrigc namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); 1064153323Srodrigc /* XXX - replace assert ? */ 1065159451Srodrigc XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber); 1066153323Srodrigc xfs_da_log_buf(args->trans, bp, 1067153323Srodrigc XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber))); 1068153323Srodrigc xfs_da_buf_done(bp); 1069153323Srodrigc blk->bp = NULL; 1070153323Srodrigc retval = 0; 1071153323Srodrigc } else { 1072153323Srodrigc i = state->path.active - 1; 1073153323Srodrigc xfs_da_brelse(args->trans, state->path.blk[i].bp); 1074153323Srodrigc state->path.blk[i].bp = NULL; 1075153323Srodrigc } 1076153323Srodrigc for (i = 0; i < state->path.active - 1; i++) { 1077153323Srodrigc xfs_da_brelse(args->trans, state->path.blk[i].bp); 1078153323Srodrigc state->path.blk[i].bp = NULL; 1079153323Srodrigc } 1080153323Srodrigc 1081153323Srodrigc xfs_da_state_free(state); 1082153323Srodrigc return(retval); 1083153323Srodrigc} 1084153323Srodrigc 1085153323Srodrigc#if defined(XFS_DIR_TRACE) 1086153323Srodrigc/* 1087153323Srodrigc * Add a trace buffer entry for an inode and a uio. 1088153323Srodrigc */ 1089153323Srodrigcvoid 1090153323Srodrigcxfs_dir_trace_g_du(char *where, xfs_inode_t *dp, uio_t *uio) 1091153323Srodrigc{ 1092153323Srodrigc xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DU, where, 1093153323Srodrigc (void *)dp, (void *)dp->i_mount, 1094153323Srodrigc (void *)((unsigned long)(uio->uio_offset >> 32)), 1095153323Srodrigc (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), 1096153323Srodrigc (void *)(unsigned long)uio->uio_resid, 1097153323Srodrigc NULL, NULL, NULL, NULL, NULL, NULL, NULL); 1098153323Srodrigc} 1099153323Srodrigc 1100153323Srodrigc/* 1101153323Srodrigc * Add a trace buffer entry for an inode and a uio. 1102153323Srodrigc */ 1103153323Srodrigcvoid 1104153323Srodrigcxfs_dir_trace_g_dub(char *where, xfs_inode_t *dp, uio_t *uio, xfs_dablk_t bno) 1105153323Srodrigc{ 1106153323Srodrigc xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUB, where, 1107153323Srodrigc (void *)dp, (void *)dp->i_mount, 1108153323Srodrigc (void *)((unsigned long)(uio->uio_offset >> 32)), 1109153323Srodrigc (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), 1110153323Srodrigc (void *)(unsigned long)uio->uio_resid, 1111153323Srodrigc (void *)(unsigned long)bno, 1112153323Srodrigc NULL, NULL, NULL, NULL, NULL, NULL); 1113153323Srodrigc} 1114153323Srodrigc 1115153323Srodrigc/* 1116153323Srodrigc * Add a trace buffer entry for an inode and a uio. 1117153323Srodrigc */ 1118153323Srodrigcvoid 1119153323Srodrigcxfs_dir_trace_g_dun(char *where, xfs_inode_t *dp, uio_t *uio, 1120153323Srodrigc xfs_da_intnode_t *node) 1121153323Srodrigc{ 1122159451Srodrigc int last = be16_to_cpu(node->hdr.count) - 1; 1123153323Srodrigc 1124153323Srodrigc xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUN, where, 1125153323Srodrigc (void *)dp, (void *)dp->i_mount, 1126153323Srodrigc (void *)((unsigned long)(uio->uio_offset >> 32)), 1127153323Srodrigc (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), 1128153323Srodrigc (void *)(unsigned long)uio->uio_resid, 1129159451Srodrigc (void *)(unsigned long)be32_to_cpu(node->hdr.info.forw), 1130153323Srodrigc (void *)(unsigned long) 1131159451Srodrigc be16_to_cpu(node->hdr.count), 1132153323Srodrigc (void *)(unsigned long) 1133159451Srodrigc be32_to_cpu(node->btree[0].hashval), 1134153323Srodrigc (void *)(unsigned long) 1135159451Srodrigc be32_to_cpu(node->btree[last].hashval), 1136153323Srodrigc NULL, NULL, NULL); 1137153323Srodrigc} 1138153323Srodrigc 1139153323Srodrigc/* 1140153323Srodrigc * Add a trace buffer entry for an inode and a uio. 1141153323Srodrigc */ 1142153323Srodrigcvoid 1143153323Srodrigcxfs_dir_trace_g_dul(char *where, xfs_inode_t *dp, uio_t *uio, 1144153323Srodrigc xfs_dir_leafblock_t *leaf) 1145153323Srodrigc{ 1146153323Srodrigc int last = INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1; 1147153323Srodrigc 1148153323Srodrigc xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUL, where, 1149153323Srodrigc (void *)dp, (void *)dp->i_mount, 1150153323Srodrigc (void *)((unsigned long)(uio->uio_offset >> 32)), 1151153323Srodrigc (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), 1152153323Srodrigc (void *)(unsigned long)uio->uio_resid, 1153159451Srodrigc (void *)(unsigned long)be32_to_cpu(leaf->hdr.info.forw), 1154153323Srodrigc (void *)(unsigned long) 1155153323Srodrigc INT_GET(leaf->hdr.count, ARCH_CONVERT), 1156153323Srodrigc (void *)(unsigned long) 1157153323Srodrigc INT_GET(leaf->entries[0].hashval, ARCH_CONVERT), 1158153323Srodrigc (void *)(unsigned long) 1159153323Srodrigc INT_GET(leaf->entries[last].hashval, ARCH_CONVERT), 1160153323Srodrigc NULL, NULL, NULL); 1161153323Srodrigc} 1162153323Srodrigc 1163153323Srodrigc/* 1164153323Srodrigc * Add a trace buffer entry for an inode and a uio. 1165153323Srodrigc */ 1166153323Srodrigcvoid 1167153323Srodrigcxfs_dir_trace_g_due(char *where, xfs_inode_t *dp, uio_t *uio, 1168153323Srodrigc xfs_dir_leaf_entry_t *entry) 1169153323Srodrigc{ 1170153323Srodrigc xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUE, where, 1171153323Srodrigc (void *)dp, (void *)dp->i_mount, 1172153323Srodrigc (void *)((unsigned long)(uio->uio_offset >> 32)), 1173153323Srodrigc (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), 1174153323Srodrigc (void *)(unsigned long)uio->uio_resid, 1175153323Srodrigc (void *)(unsigned long) 1176153323Srodrigc INT_GET(entry->hashval, ARCH_CONVERT), 1177153323Srodrigc NULL, NULL, NULL, NULL, NULL, NULL); 1178153323Srodrigc} 1179153323Srodrigc 1180153323Srodrigc/* 1181153323Srodrigc * Add a trace buffer entry for an inode and a uio. 1182153323Srodrigc */ 1183153323Srodrigcvoid 1184153323Srodrigcxfs_dir_trace_g_duc(char *where, xfs_inode_t *dp, uio_t *uio, xfs_off_t cookie) 1185153323Srodrigc{ 1186153323Srodrigc xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUC, where, 1187153323Srodrigc (void *)dp, (void *)dp->i_mount, 1188153323Srodrigc (void *)((unsigned long)(uio->uio_offset >> 32)), 1189153323Srodrigc (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), 1190153323Srodrigc (void *)(unsigned long)uio->uio_resid, 1191153323Srodrigc (void *)((unsigned long)(cookie >> 32)), 1192153323Srodrigc (void *)((unsigned long)(cookie & 0xFFFFFFFF)), 1193153323Srodrigc NULL, NULL, NULL, NULL, NULL); 1194153323Srodrigc} 1195153323Srodrigc 1196153323Srodrigc/* 1197153323Srodrigc * Add a trace buffer entry for the arguments given to the routine, 1198153323Srodrigc * generic form. 1199153323Srodrigc */ 1200153323Srodrigcvoid 1201153323Srodrigcxfs_dir_trace_enter(int type, char *where, 1202153323Srodrigc void * a0, void * a1, 1203153323Srodrigc void * a2, void * a3, 1204153323Srodrigc void * a4, void * a5, 1205153323Srodrigc void * a6, void * a7, 1206153323Srodrigc void * a8, void * a9, 1207153323Srodrigc void * a10, void * a11) 1208153323Srodrigc{ 1209153323Srodrigc ASSERT(xfs_dir_trace_buf); 1210153323Srodrigc ktrace_enter(xfs_dir_trace_buf, (void *)(unsigned long)type, 1211153323Srodrigc (void *)where, 1212153323Srodrigc (void *)a0, (void *)a1, (void *)a2, 1213153323Srodrigc (void *)a3, (void *)a4, (void *)a5, 1214153323Srodrigc (void *)a6, (void *)a7, (void *)a8, 1215153323Srodrigc (void *)a9, (void *)a10, (void *)a11, 1216153323Srodrigc NULL, NULL); 1217153323Srodrigc} 1218153323Srodrigc#endif /* XFS_DIR_TRACE */ 1219