Blame view

fs/xfs/xfs_dir2_readdir.c 13.4 KB
0b61f8a40   Dave Chinner   xfs: convert to S...
1
  // SPDX-License-Identifier: GPL-2.0
4a8af273d   Dave Chinner   xfs: move getdent...
2
3
4
5
  /*
   * Copyright (c) 2000-2005 Silicon Graphics, Inc.
   * Copyright (c) 2013 Red Hat, Inc.
   * All Rights Reserved.
4a8af273d   Dave Chinner   xfs: move getdent...
6
7
8
   */
  #include "xfs.h"
  #include "xfs_fs.h"
5467b34bd   Darrick J. Wong   xfs: move xfs_ino...
9
  #include "xfs_shared.h"
a4fbe6ab1   Dave Chinner   xfs: decouple ino...
10
  #include "xfs_format.h"
239880ef6   Dave Chinner   xfs: decouple log...
11
12
  #include "xfs_log_format.h"
  #include "xfs_trans_resv.h"
4a8af273d   Dave Chinner   xfs: move getdent...
13
  #include "xfs_mount.h"
4a8af273d   Dave Chinner   xfs: move getdent...
14
  #include "xfs_inode.h"
2b9ab5ab9   Dave Chinner   xfs: reshuffle di...
15
  #include "xfs_dir2.h"
4a8af273d   Dave Chinner   xfs: move getdent...
16
  #include "xfs_dir2_priv.h"
4a8af273d   Dave Chinner   xfs: move getdent...
17
18
  #include "xfs_trace.h"
  #include "xfs_bmap.h"
239880ef6   Dave Chinner   xfs: decouple log...
19
  #include "xfs_trans.h"
04df34ac6   Darrick J. Wong   xfs: namecheck di...
20
  #include "xfs_error.h"
4a8af273d   Dave Chinner   xfs: move getdent...
21

0cb97766f   Dave Chinner   xfs: Add read-onl...
22
23
24
25
26
27
28
  /*
   * Directory file type support functions
   */
  static unsigned char xfs_dir3_filetype_table[] = {
  	DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK,
  	DT_FIFO, DT_SOCK, DT_LNK, DT_WHT,
  };
a5c46e5e8   Darrick J. Wong   xfs: scrub direct...
29
  unsigned char
0cb97766f   Dave Chinner   xfs: Add read-onl...
30
31
  xfs_dir3_get_dtype(
  	struct xfs_mount	*mp,
c8ce540db   Darrick J. Wong   xfs: remove doubl...
32
  	uint8_t			filetype)
0cb97766f   Dave Chinner   xfs: Add read-onl...
33
34
35
36
37
38
39
40
41
  {
  	if (!xfs_sb_version_hasftype(&mp->m_sb))
  		return DT_UNKNOWN;
  
  	if (filetype >= XFS_DIR3_FT_MAX)
  		return DT_UNKNOWN;
  
  	return xfs_dir3_filetype_table[filetype];
  }
0cb97766f   Dave Chinner   xfs: Add read-onl...
42

4a8af273d   Dave Chinner   xfs: move getdent...
43
44
  STATIC int
  xfs_dir2_sf_getdents(
53f82db00   Dave Chinner   xfs: reduce direc...
45
  	struct xfs_da_args	*args,
4a8af273d   Dave Chinner   xfs: move getdent...
46
47
48
  	struct dir_context	*ctx)
  {
  	int			i;		/* shortform entry number */
53f82db00   Dave Chinner   xfs: reduce direc...
49
  	struct xfs_inode	*dp = args->dp;	/* incore directory inode */
50f6bb6b7   Christoph Hellwig   xfs: devirtualize...
50
  	struct xfs_mount	*mp = dp->i_mount;
4a8af273d   Dave Chinner   xfs: move getdent...
51
52
53
54
55
56
  	xfs_dir2_dataptr_t	off;		/* current entry's offset */
  	xfs_dir2_sf_entry_t	*sfep;		/* shortform directory entry */
  	xfs_dir2_sf_hdr_t	*sfp;		/* shortform structure */
  	xfs_dir2_dataptr_t	dot_offset;
  	xfs_dir2_dataptr_t	dotdot_offset;
  	xfs_ino_t		ino;
53f82db00   Dave Chinner   xfs: reduce direc...
57
  	struct xfs_da_geometry	*geo = args->geo;
4a8af273d   Dave Chinner   xfs: move getdent...
58
59
  
  	ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
4a8af273d   Dave Chinner   xfs: move getdent...
60
61
62
63
  	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
  	ASSERT(dp->i_df.if_u1.if_data != NULL);
  
  	sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
4a8af273d   Dave Chinner   xfs: move getdent...
64
65
66
  	/*
  	 * If the block number in the offset is out of range, we're done.
  	 */
7dda6e864   Dave Chinner   xfs: convert dire...
67
  	if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)
4a8af273d   Dave Chinner   xfs: move getdent...
68
69
70
  		return 0;
  
  	/*
168231047   Christoph Hellwig   xfs: remove the d...
71
72
73
  	 * Precalculate offsets for "." and ".." as we will always need them.
  	 * This relies on the fact that directories always start with the
  	 * entries for "." and "..".
4a8af273d   Dave Chinner   xfs: move getdent...
74
  	 */
7dda6e864   Dave Chinner   xfs: convert dire...
75
  	dot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
d73e1cee8   Christoph Hellwig   xfs: move the dir...
76
  			geo->data_entry_offset);
7dda6e864   Dave Chinner   xfs: convert dire...
77
  	dotdot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
d73e1cee8   Christoph Hellwig   xfs: move the dir...
78
  			geo->data_entry_offset +
fdbb8c5b8   Christoph Hellwig   xfs: devirtualize...
79
  			xfs_dir2_data_entsize(mp, sizeof(".") - 1));
4a8af273d   Dave Chinner   xfs: move getdent...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  
  	/*
  	 * Put . entry unless we're starting past it.
  	 */
  	if (ctx->pos <= dot_offset) {
  		ctx->pos = dot_offset & 0x7fffffff;
  		if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR))
  			return 0;
  	}
  
  	/*
  	 * Put .. entry unless we're starting past it.
  	 */
  	if (ctx->pos <= dotdot_offset) {
84915e1bd   Christoph Hellwig   xfs: devirtualize...
94
  		ino = xfs_dir2_sf_get_parent_ino(sfp);
4a8af273d   Dave Chinner   xfs: move getdent...
95
96
97
98
99
100
101
102
103
104
  		ctx->pos = dotdot_offset & 0x7fffffff;
  		if (!dir_emit(ctx, "..", 2, ino, DT_DIR))
  			return 0;
  	}
  
  	/*
  	 * Loop while there are more entries and put'ing works.
  	 */
  	sfep = xfs_dir2_sf_firstentry(sfp);
  	for (i = 0; i < sfp->count; i++) {
c8ce540db   Darrick J. Wong   xfs: remove doubl...
105
  		uint8_t filetype;
0cb97766f   Dave Chinner   xfs: Add read-onl...
106

7dda6e864   Dave Chinner   xfs: convert dire...
107
  		off = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
4a8af273d   Dave Chinner   xfs: move getdent...
108
109
110
  				xfs_dir2_sf_get_offset(sfep));
  
  		if (ctx->pos > off) {
50f6bb6b7   Christoph Hellwig   xfs: devirtualize...
111
  			sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
4a8af273d   Dave Chinner   xfs: move getdent...
112
113
  			continue;
  		}
93b1e96a4   Christoph Hellwig   xfs: devirtualize...
114
  		ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
4501ed2a3   Christoph Hellwig   xfs: devirtualize...
115
  		filetype = xfs_dir2_sf_get_ftype(mp, sfep);
4a8af273d   Dave Chinner   xfs: move getdent...
116
  		ctx->pos = off & 0x7fffffff;
a71895c5d   Darrick J. Wong   xfs: convert open...
117
118
119
  		if (XFS_IS_CORRUPT(dp->i_mount,
  				   !xfs_dir2_namecheck(sfep->name,
  						       sfep->namelen)))
04df34ac6   Darrick J. Wong   xfs: namecheck di...
120
  			return -EFSCORRUPTED;
0cb97766f   Dave Chinner   xfs: Add read-onl...
121
  		if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,
50f6bb6b7   Christoph Hellwig   xfs: devirtualize...
122
  			    xfs_dir3_get_dtype(mp, filetype)))
4a8af273d   Dave Chinner   xfs: move getdent...
123
  			return 0;
50f6bb6b7   Christoph Hellwig   xfs: devirtualize...
124
  		sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
4a8af273d   Dave Chinner   xfs: move getdent...
125
  	}
7dda6e864   Dave Chinner   xfs: convert dire...
126
  	ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
53f82db00   Dave Chinner   xfs: reduce direc...
127
  								0x7fffffff;
4a8af273d   Dave Chinner   xfs: move getdent...
128
129
130
131
132
133
134
135
  	return 0;
  }
  
  /*
   * Readdir for block directories.
   */
  STATIC int
  xfs_dir2_block_getdents(
53f82db00   Dave Chinner   xfs: reduce direc...
136
  	struct xfs_da_args	*args,
4a8af273d   Dave Chinner   xfs: move getdent...
137
138
  	struct dir_context	*ctx)
  {
53f82db00   Dave Chinner   xfs: reduce direc...
139
  	struct xfs_inode	*dp = args->dp;	/* incore directory inode */
4a8af273d   Dave Chinner   xfs: move getdent...
140
  	struct xfs_buf		*bp;		/* buffer for block */
4a8af273d   Dave Chinner   xfs: move getdent...
141
  	int			error;		/* error return value */
4a8af273d   Dave Chinner   xfs: move getdent...
142
143
  	int			wantoff;	/* starting block offset */
  	xfs_off_t		cook;
53f82db00   Dave Chinner   xfs: reduce direc...
144
  	struct xfs_da_geometry	*geo = args->geo;
dbad7c993   Dave Chinner   xfs: stop holding...
145
  	int			lock_mode;
3d28e7e27   Tommi Rantala   xfs: fix regressi...
146
  	unsigned int		offset, next_offset;
263dde869   Christoph Hellwig   xfs: cleanup xfs_...
147
  	unsigned int		end;
4a8af273d   Dave Chinner   xfs: move getdent...
148

4a8af273d   Dave Chinner   xfs: move getdent...
149
150
151
  	/*
  	 * If the block number in the offset is out of range, we're done.
  	 */
7dda6e864   Dave Chinner   xfs: convert dire...
152
  	if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)
4a8af273d   Dave Chinner   xfs: move getdent...
153
  		return 0;
dbad7c993   Dave Chinner   xfs: stop holding...
154
  	lock_mode = xfs_ilock_data_map_shared(dp);
acb9553ca   Darrick J. Wong   xfs: pass along t...
155
  	error = xfs_dir3_block_read(args->trans, dp, &bp);
dbad7c993   Dave Chinner   xfs: stop holding...
156
  	xfs_iunlock(dp, lock_mode);
4a8af273d   Dave Chinner   xfs: move getdent...
157
158
159
160
161
162
163
  	if (error)
  		return error;
  
  	/*
  	 * Extract the byte offset we start at from the seek pointer.
  	 * We'll skip entries before this.
  	 */
30028030b   Dave Chinner   xfs: convert dire...
164
  	wantoff = xfs_dir2_dataptr_to_off(geo, ctx->pos);
4a8af273d   Dave Chinner   xfs: move getdent...
165
  	xfs_dir3_data_check(dp, bp);
4a8af273d   Dave Chinner   xfs: move getdent...
166
167
168
169
170
  
  	/*
  	 * Loop over the data portion of the block.
  	 * Each object is a real entry (dep) or an unused one (dup).
  	 */
5c072127d   Christoph Hellwig   xfs: replace xfs_...
171
  	end = xfs_dir3_data_end_offset(geo, bp->b_addr);
3d28e7e27   Tommi Rantala   xfs: fix regressi...
172
173
174
  	for (offset = geo->data_entry_offset;
  	     offset < end;
  	     offset = next_offset) {
263dde869   Christoph Hellwig   xfs: cleanup xfs_...
175
176
  		struct xfs_dir2_data_unused	*dup = bp->b_addr + offset;
  		struct xfs_dir2_data_entry	*dep = bp->b_addr + offset;
c8ce540db   Darrick J. Wong   xfs: remove doubl...
177
  		uint8_t filetype;
0cb97766f   Dave Chinner   xfs: Add read-onl...
178

4a8af273d   Dave Chinner   xfs: move getdent...
179
180
181
182
  		/*
  		 * Unused, skip it.
  		 */
  		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
3d28e7e27   Tommi Rantala   xfs: fix regressi...
183
  			next_offset = offset + be16_to_cpu(dup->length);
4a8af273d   Dave Chinner   xfs: move getdent...
184
185
  			continue;
  		}
4a8af273d   Dave Chinner   xfs: move getdent...
186
187
188
  		/*
  		 * Bump pointer for the next iteration.
  		 */
3d28e7e27   Tommi Rantala   xfs: fix regressi...
189
190
  		next_offset = offset +
  			xfs_dir2_data_entsize(dp->i_mount, dep->namelen);
263dde869   Christoph Hellwig   xfs: cleanup xfs_...
191

4a8af273d   Dave Chinner   xfs: move getdent...
192
193
194
  		/*
  		 * The entry is before the desired starting point, skip it.
  		 */
263dde869   Christoph Hellwig   xfs: cleanup xfs_...
195
  		if (offset < wantoff)
4a8af273d   Dave Chinner   xfs: move getdent...
196
  			continue;
263dde869   Christoph Hellwig   xfs: cleanup xfs_...
197
  		cook = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);
4a8af273d   Dave Chinner   xfs: move getdent...
198
199
  
  		ctx->pos = cook & 0x7fffffff;
59b8b4650   Christoph Hellwig   xfs: devirtualize...
200
  		filetype = xfs_dir2_data_get_ftype(dp->i_mount, dep);
4a8af273d   Dave Chinner   xfs: move getdent...
201
202
203
  		/*
  		 * If it didn't fit, set the final offset to here & return.
  		 */
a71895c5d   Darrick J. Wong   xfs: convert open...
204
205
206
  		if (XFS_IS_CORRUPT(dp->i_mount,
  				   !xfs_dir2_namecheck(dep->name,
  						       dep->namelen))) {
04df34ac6   Darrick J. Wong   xfs: namecheck di...
207
208
209
  			error = -EFSCORRUPTED;
  			goto out_rele;
  		}
4a8af273d   Dave Chinner   xfs: move getdent...
210
  		if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
0cb97766f   Dave Chinner   xfs: Add read-onl...
211
  			    be64_to_cpu(dep->inumber),
04df34ac6   Darrick J. Wong   xfs: namecheck di...
212
213
  			    xfs_dir3_get_dtype(dp->i_mount, filetype)))
  			goto out_rele;
4a8af273d   Dave Chinner   xfs: move getdent...
214
215
216
217
218
219
  	}
  
  	/*
  	 * Reached the end of the block.
  	 * Set the offset to a non-existent block 1 and return.
  	 */
7dda6e864   Dave Chinner   xfs: convert dire...
220
  	ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
53f82db00   Dave Chinner   xfs: reduce direc...
221
  								0x7fffffff;
04df34ac6   Darrick J. Wong   xfs: namecheck di...
222
  out_rele:
acb9553ca   Darrick J. Wong   xfs: pass along t...
223
  	xfs_trans_brelse(args->trans, bp);
04df34ac6   Darrick J. Wong   xfs: namecheck di...
224
  	return error;
4a8af273d   Dave Chinner   xfs: move getdent...
225
  }
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
226
227
228
229
230
  /*
   * Read a directory block and initiate readahead for blocks beyond that.
   * We maintain a sliding readahead window of the remaining space in the
   * buffer rounded up to the nearest block.
   */
4a8af273d   Dave Chinner   xfs: move getdent...
231
232
  STATIC int
  xfs_dir2_leaf_readbuf(
53f82db00   Dave Chinner   xfs: reduce direc...
233
  	struct xfs_da_args	*args,
4a8af273d   Dave Chinner   xfs: move getdent...
234
  	size_t			bufsize,
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
235
236
237
  	xfs_dir2_off_t		*cur_off,
  	xfs_dablk_t		*ra_blk,
  	struct xfs_buf		**bpp)
4a8af273d   Dave Chinner   xfs: move getdent...
238
  {
53f82db00   Dave Chinner   xfs: reduce direc...
239
  	struct xfs_inode	*dp = args->dp;
9f5418010   Dave Chinner   xfs: concurrent r...
240
  	struct xfs_buf		*bp = NULL;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
241
242
243
  	struct xfs_da_geometry	*geo = args->geo;
  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(dp, XFS_DATA_FORK);
  	struct xfs_bmbt_irec	map;
4a8af273d   Dave Chinner   xfs: move getdent...
244
  	struct blk_plug		plug;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
245
246
247
248
  	xfs_dir2_off_t		new_off;
  	xfs_dablk_t		next_ra;
  	xfs_dablk_t		map_off;
  	xfs_dablk_t		last_da;
b2b1712a6   Christoph Hellwig   xfs: introduce th...
249
  	struct xfs_iext_cursor	icur;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
250
  	int			ra_want;
4a8af273d   Dave Chinner   xfs: move getdent...
251
  	int			error = 0;
4a8af273d   Dave Chinner   xfs: move getdent...
252

d205a7d0e   Darrick J. Wong   xfs: refactor dir...
253
254
  	if (!(ifp->if_flags & XFS_IFEXTENTS)) {
  		error = xfs_iread_extents(args->trans, dp, XFS_DATA_FORK);
4a8af273d   Dave Chinner   xfs: move getdent...
255
  		if (error)
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
256
  			goto out;
4a8af273d   Dave Chinner   xfs: move getdent...
257
258
259
  	}
  
  	/*
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
260
261
262
  	 * Look for mapped directory blocks at or above the current offset.
  	 * Truncate down to the nearest directory block to start the scanning
  	 * operation.
4a8af273d   Dave Chinner   xfs: move getdent...
263
  	 */
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
264
265
  	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
  	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *cur_off));
b2b1712a6   Christoph Hellwig   xfs: introduce th...
266
  	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &icur, &map))
4a8af273d   Dave Chinner   xfs: move getdent...
267
  		goto out;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
268
269
270
  	if (map.br_startoff >= last_da)
  		goto out;
  	xfs_trim_extent(&map, map_off, last_da - map_off);
4a8af273d   Dave Chinner   xfs: move getdent...
271

d205a7d0e   Darrick J. Wong   xfs: refactor dir...
272
273
274
275
  	/* Read the directory block of that first mapping. */
  	new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
  	if (new_off > *cur_off)
  		*cur_off = new_off;
cd2c9f1b5   Christoph Hellwig   xfs: remove the m...
276
  	error = xfs_dir3_data_read(args->trans, dp, map.br_startoff, 0, &bp);
4a8af273d   Dave Chinner   xfs: move getdent...
277
  	if (error)
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
278
  		goto out;
4a8af273d   Dave Chinner   xfs: move getdent...
279
280
  
  	/*
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
281
282
283
  	 * Start readahead for the next bufsize's worth of dir data blocks.
  	 * We may have already issued readahead for some of that range;
  	 * ra_blk tracks the last block we tried to read(ahead).
4a8af273d   Dave Chinner   xfs: move getdent...
284
  	 */
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
285
286
287
288
289
290
291
292
293
  	ra_want = howmany(bufsize + geo->blksize, (1 << geo->fsblog));
  	if (*ra_blk >= last_da)
  		goto out;
  	else if (*ra_blk == 0)
  		*ra_blk = map.br_startoff;
  	next_ra = map.br_startoff + geo->fsbcount;
  	if (next_ra >= last_da)
  		goto out_no_ra;
  	if (map.br_blockcount < geo->fsbcount &&
b2b1712a6   Christoph Hellwig   xfs: introduce th...
294
  	    !xfs_iext_next_extent(ifp, &icur, &map))
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
295
296
297
298
299
300
  		goto out_no_ra;
  	if (map.br_startoff >= last_da)
  		goto out_no_ra;
  	xfs_trim_extent(&map, next_ra, last_da - next_ra);
  
  	/* Start ra for each dir (not fs) block that has a mapping. */
4a8af273d   Dave Chinner   xfs: move getdent...
301
  	blk_start_plug(&plug);
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
302
303
304
305
306
307
308
309
310
  	while (ra_want > 0) {
  		next_ra = roundup((xfs_dablk_t)map.br_startoff, geo->fsbcount);
  		while (ra_want > 0 &&
  		       next_ra < map.br_startoff + map.br_blockcount) {
  			if (next_ra >= last_da) {
  				*ra_blk = last_da;
  				break;
  			}
  			if (next_ra > *ra_blk) {
06566fda4   Christoph Hellwig   xfs: remove the m...
311
312
  				xfs_dir3_data_readahead(dp, next_ra,
  							XFS_DABUF_MAP_HOLE_OK);
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
313
  				*ra_blk = next_ra;
4a8af273d   Dave Chinner   xfs: move getdent...
314
  			}
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
315
316
317
  			ra_want -= geo->fsbcount;
  			next_ra += geo->fsbcount;
  		}
b2b1712a6   Christoph Hellwig   xfs: introduce th...
318
  		if (!xfs_iext_next_extent(ifp, &icur, &map)) {
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
319
320
  			*ra_blk = last_da;
  			break;
4a8af273d   Dave Chinner   xfs: move getdent...
321
322
323
324
325
326
327
  		}
  	}
  	blk_finish_plug(&plug);
  
  out:
  	*bpp = bp;
  	return error;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
328
329
330
  out_no_ra:
  	*ra_blk = last_da;
  	goto out;
4a8af273d   Dave Chinner   xfs: move getdent...
331
332
333
334
335
336
337
338
  }
  
  /*
   * Getdents (readdir) for leaf and node directories.
   * This reads the data blocks only, so is the same for both forms.
   */
  STATIC int
  xfs_dir2_leaf_getdents(
53f82db00   Dave Chinner   xfs: reduce direc...
339
  	struct xfs_da_args	*args,
4a8af273d   Dave Chinner   xfs: move getdent...
340
341
342
  	struct dir_context	*ctx,
  	size_t			bufsize)
  {
53f82db00   Dave Chinner   xfs: reduce direc...
343
  	struct xfs_inode	*dp = args->dp;
fdbb8c5b8   Christoph Hellwig   xfs: devirtualize...
344
  	struct xfs_mount	*mp = dp->i_mount;
4a8af273d   Dave Chinner   xfs: move getdent...
345
  	struct xfs_buf		*bp = NULL;	/* data block buffer */
4a8af273d   Dave Chinner   xfs: move getdent...
346
347
  	xfs_dir2_data_entry_t	*dep;		/* data entry */
  	xfs_dir2_data_unused_t	*dup;		/* unused entry */
53f82db00   Dave Chinner   xfs: reduce direc...
348
  	struct xfs_da_geometry	*geo = args->geo;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
349
350
351
352
353
  	xfs_dablk_t		rablk = 0;	/* current readahead block */
  	xfs_dir2_off_t		curoff;		/* current overall offset */
  	int			length;		/* temporary length value */
  	int			byteoff;	/* offset in current block */
  	int			lock_mode;
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
354
  	unsigned int		offset = 0;
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
355
  	int			error = 0;	/* error return value */
4a8af273d   Dave Chinner   xfs: move getdent...
356
357
358
359
360
361
362
  
  	/*
  	 * If the offset is at or past the largest allowed value,
  	 * give up right away.
  	 */
  	if (ctx->pos >= XFS_DIR2_MAX_DATAPTR)
  		return 0;
4a8af273d   Dave Chinner   xfs: move getdent...
363
  	/*
4a8af273d   Dave Chinner   xfs: move getdent...
364
365
366
  	 * Inside the loop we keep the main offset value as a byte offset
  	 * in the directory file.
  	 */
259940537   Eric Sandeen   xfs: remove unuse...
367
  	curoff = xfs_dir2_dataptr_to_byte(ctx->pos);
4a8af273d   Dave Chinner   xfs: move getdent...
368
369
  
  	/*
4a8af273d   Dave Chinner   xfs: move getdent...
370
371
372
373
  	 * Loop over directory entries until we reach the end offset.
  	 * Get more blocks and readahead as necessary.
  	 */
  	while (curoff < XFS_DIR2_LEAF_OFFSET) {
c8ce540db   Darrick J. Wong   xfs: remove doubl...
374
  		uint8_t filetype;
0cb97766f   Dave Chinner   xfs: Add read-onl...
375

4a8af273d   Dave Chinner   xfs: move getdent...
376
377
378
379
  		/*
  		 * If we have no buffer, or we're off the end of the
  		 * current buffer, need to get another one.
  		 */
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
380
  		if (!bp || offset >= geo->blksize) {
9f5418010   Dave Chinner   xfs: concurrent r...
381
  			if (bp) {
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
382
  				xfs_trans_brelse(args->trans, bp);
9f5418010   Dave Chinner   xfs: concurrent r...
383
  				bp = NULL;
9f5418010   Dave Chinner   xfs: concurrent r...
384
  			}
4a8af273d   Dave Chinner   xfs: move getdent...
385

dbad7c993   Dave Chinner   xfs: stop holding...
386
  			lock_mode = xfs_ilock_data_map_shared(dp);
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
387
388
  			error = xfs_dir2_leaf_readbuf(args, bufsize, &curoff,
  					&rablk, &bp);
dbad7c993   Dave Chinner   xfs: stop holding...
389
  			xfs_iunlock(dp, lock_mode);
d205a7d0e   Darrick J. Wong   xfs: refactor dir...
390
  			if (error || !bp)
4a8af273d   Dave Chinner   xfs: move getdent...
391
  				break;
4a8af273d   Dave Chinner   xfs: move getdent...
392
393
394
395
  			xfs_dir3_data_check(dp, bp);
  			/*
  			 * Find our position in the block.
  			 */
d73e1cee8   Christoph Hellwig   xfs: move the dir...
396
  			offset = geo->data_entry_offset;
53f82db00   Dave Chinner   xfs: reduce direc...
397
  			byteoff = xfs_dir2_byte_to_off(geo, curoff);
4a8af273d   Dave Chinner   xfs: move getdent...
398
399
400
401
  			/*
  			 * Skip past the header.
  			 */
  			if (byteoff == 0)
d73e1cee8   Christoph Hellwig   xfs: move the dir...
402
  				curoff += geo->data_entry_offset;
4a8af273d   Dave Chinner   xfs: move getdent...
403
404
405
406
  			/*
  			 * Skip past entries until we reach our offset.
  			 */
  			else {
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
407
408
  				while (offset < byteoff) {
  					dup = bp->b_addr + offset;
4a8af273d   Dave Chinner   xfs: move getdent...
409
410
411
412
413
  
  					if (be16_to_cpu(dup->freetag)
  						  == XFS_DIR2_DATA_FREE_TAG) {
  
  						length = be16_to_cpu(dup->length);
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
414
  						offset += length;
4a8af273d   Dave Chinner   xfs: move getdent...
415
416
  						continue;
  					}
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
417
  					dep = bp->b_addr + offset;
fdbb8c5b8   Christoph Hellwig   xfs: devirtualize...
418
419
  					length = xfs_dir2_data_entsize(mp,
  							dep->namelen);
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
420
  					offset += length;
4a8af273d   Dave Chinner   xfs: move getdent...
421
422
423
424
425
  				}
  				/*
  				 * Now set our real offset.
  				 */
  				curoff =
30028030b   Dave Chinner   xfs: convert dire...
426
427
  					xfs_dir2_db_off_to_byte(geo,
  					    xfs_dir2_byte_to_db(geo, curoff),
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
428
429
  					    offset);
  				if (offset >= geo->blksize)
4a8af273d   Dave Chinner   xfs: move getdent...
430
  					continue;
4a8af273d   Dave Chinner   xfs: move getdent...
431
432
  			}
  		}
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
433

4a8af273d   Dave Chinner   xfs: move getdent...
434
  		/*
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
435
  		 * We have a pointer to an entry.  Is it a live one?
4a8af273d   Dave Chinner   xfs: move getdent...
436
  		 */
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
437
  		dup = bp->b_addr + offset;
4a8af273d   Dave Chinner   xfs: move getdent...
438
439
440
441
442
  		/*
  		 * No, it's unused, skip over it.
  		 */
  		if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
  			length = be16_to_cpu(dup->length);
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
443
  			offset += length;
4a8af273d   Dave Chinner   xfs: move getdent...
444
445
446
  			curoff += length;
  			continue;
  		}
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
447
  		dep = bp->b_addr + offset;
fdbb8c5b8   Christoph Hellwig   xfs: devirtualize...
448
  		length = xfs_dir2_data_entsize(mp, dep->namelen);
59b8b4650   Christoph Hellwig   xfs: devirtualize...
449
  		filetype = xfs_dir2_data_get_ftype(mp, dep);
4a8af273d   Dave Chinner   xfs: move getdent...
450

259940537   Eric Sandeen   xfs: remove unuse...
451
  		ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
a71895c5d   Darrick J. Wong   xfs: convert open...
452
453
454
  		if (XFS_IS_CORRUPT(dp->i_mount,
  				   !xfs_dir2_namecheck(dep->name,
  						       dep->namelen))) {
04df34ac6   Darrick J. Wong   xfs: namecheck di...
455
456
457
  			error = -EFSCORRUPTED;
  			break;
  		}
4a8af273d   Dave Chinner   xfs: move getdent...
458
  		if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
0cb97766f   Dave Chinner   xfs: Add read-onl...
459
  			    be64_to_cpu(dep->inumber),
53f82db00   Dave Chinner   xfs: reduce direc...
460
  			    xfs_dir3_get_dtype(dp->i_mount, filetype)))
4a8af273d   Dave Chinner   xfs: move getdent...
461
462
463
464
465
  			break;
  
  		/*
  		 * Advance to next entry in the block.
  		 */
2f4369a86   Christoph Hellwig   xfs: cleanup xfs_...
466
  		offset += length;
4a8af273d   Dave Chinner   xfs: move getdent...
467
468
469
470
471
472
473
474
  		curoff += length;
  		/* bufsize may have just been a guess; don't go negative */
  		bufsize = bufsize > length ? bufsize - length : 0;
  	}
  
  	/*
  	 * All done.  Set output offset value to current offset.
  	 */
259940537   Eric Sandeen   xfs: remove unuse...
475
  	if (curoff > xfs_dir2_dataptr_to_byte(XFS_DIR2_MAX_DATAPTR))
4a8af273d   Dave Chinner   xfs: move getdent...
476
477
  		ctx->pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;
  	else
259940537   Eric Sandeen   xfs: remove unuse...
478
  		ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
4a8af273d   Dave Chinner   xfs: move getdent...
479
  	if (bp)
acb9553ca   Darrick J. Wong   xfs: pass along t...
480
  		xfs_trans_brelse(args->trans, bp);
4a8af273d   Dave Chinner   xfs: move getdent...
481
482
483
484
485
  	return error;
  }
  
  /*
   * Read a directory.
acb9553ca   Darrick J. Wong   xfs: pass along t...
486
487
488
489
490
   *
   * If supplied, the transaction collects locked dir buffers to avoid
   * nested buffer deadlocks.  This function does not dirty the
   * transaction.  The caller should ensure that the inode is locked
   * before calling this function.
4a8af273d   Dave Chinner   xfs: move getdent...
491
492
493
   */
  int
  xfs_readdir(
acb9553ca   Darrick J. Wong   xfs: pass along t...
494
  	struct xfs_trans	*tp,
53f82db00   Dave Chinner   xfs: reduce direc...
495
496
497
  	struct xfs_inode	*dp,
  	struct dir_context	*ctx,
  	size_t			bufsize)
4a8af273d   Dave Chinner   xfs: move getdent...
498
  {
35f46c5f0   Dave Chinner   xfs: fix xfs_da_a...
499
  	struct xfs_da_args	args = { NULL };
53f82db00   Dave Chinner   xfs: reduce direc...
500
501
  	int			rval;
  	int			v;
4a8af273d   Dave Chinner   xfs: move getdent...
502
503
504
505
  
  	trace_xfs_readdir(dp);
  
  	if (XFS_FORCED_SHUTDOWN(dp->i_mount))
2451337dd   Dave Chinner   xfs: global error...
506
  		return -EIO;
4a8af273d   Dave Chinner   xfs: move getdent...
507

c19b3b05a   Dave Chinner   xfs: mode di_mode...
508
  	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
ff6d6af23   Bill O'Donnell   xfs: per-filesyst...
509
  	XFS_STATS_INC(dp->i_mount, xs_dir_getdents);
4a8af273d   Dave Chinner   xfs: move getdent...
510

53f82db00   Dave Chinner   xfs: reduce direc...
511
512
  	args.dp = dp;
  	args.geo = dp->i_mount->m_dir_geo;
acb9553ca   Darrick J. Wong   xfs: pass along t...
513
  	args.trans = tp;
53f82db00   Dave Chinner   xfs: reduce direc...
514

f7e67b20e   Christoph Hellwig   xfs: move the for...
515
  	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
53f82db00   Dave Chinner   xfs: reduce direc...
516
517
  		rval = xfs_dir2_sf_getdents(&args, ctx);
  	else if ((rval = xfs_dir2_isblock(&args, &v)))
4a8af273d   Dave Chinner   xfs: move getdent...
518
519
  		;
  	else if (v)
53f82db00   Dave Chinner   xfs: reduce direc...
520
  		rval = xfs_dir2_block_getdents(&args, ctx);
4a8af273d   Dave Chinner   xfs: move getdent...
521
  	else
53f82db00   Dave Chinner   xfs: reduce direc...
522
  		rval = xfs_dir2_leaf_getdents(&args, ctx, bufsize);
40194ecc6   Ben Myers   xfs: reinstate th...
523

4a8af273d   Dave Chinner   xfs: move getdent...
524
525
  	return rval;
  }