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