aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_dir2_readdir.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-08-12 06:49:36 -0400
committerBen Myers <bpm@sgi.com>2013-08-12 17:39:56 -0400
commit4a8af273de63d9656559ba5289e91f40aae0441f (patch)
treeac3d22e894df58c38326bada04073d283c90dee5 /fs/xfs/xfs_dir2_readdir.c
parent1fd7115eda5661e872463694fc4a12821c4f914a (diff)
xfs: move getdents code into it's own file
The directory readdir code is not used by userspace, but it is intermingled with files that are shared with userspace. This makes it difficult to compare the differences between the userspac eand kernel files are the userspace files don't have the getdents code in them. Move all the kernel getdents code to a separate file to bring the shared content between userspace and kernel files closer together. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Mark Tinguely <tinguely@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_dir2_readdir.c')
-rw-r--r--fs/xfs/xfs_dir2_readdir.c645
1 files changed, 645 insertions, 0 deletions
diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c
new file mode 100644
index 000000000000..2bc4b324ff76
--- /dev/null
+++ b/fs/xfs/xfs_dir2_readdir.c
@@ -0,0 +1,645 @@
1/*
2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3 * Copyright (c) 2013 Red Hat, Inc.
4 * All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it would be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include "xfs.h"
20#include "xfs_fs.h"
21#include "xfs_types.h"
22#include "xfs_bit.h"
23#include "xfs_log.h"
24#include "xfs_trans.h"
25#include "xfs_sb.h"
26#include "xfs_ag.h"
27#include "xfs_mount.h"
28#include "xfs_da_btree.h"
29#include "xfs_bmap_btree.h"
30#include "xfs_dinode.h"
31#include "xfs_inode.h"
32#include "xfs_dir2_format.h"
33#include "xfs_dir2_priv.h"
34#include "xfs_error.h"
35#include "xfs_trace.h"
36#include "xfs_bmap.h"
37
38STATIC int
39xfs_dir2_sf_getdents(
40 xfs_inode_t *dp, /* incore directory inode */
41 struct dir_context *ctx)
42{
43 int i; /* shortform entry number */
44 xfs_mount_t *mp; /* filesystem mount point */
45 xfs_dir2_dataptr_t off; /* current entry's offset */
46 xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
47 xfs_dir2_sf_hdr_t *sfp; /* shortform structure */
48 xfs_dir2_dataptr_t dot_offset;
49 xfs_dir2_dataptr_t dotdot_offset;
50 xfs_ino_t ino;
51
52 mp = dp->i_mount;
53
54 ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
55 /*
56 * Give up if the directory is way too short.
57 */
58 if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
59 ASSERT(XFS_FORCED_SHUTDOWN(mp));
60 return XFS_ERROR(EIO);
61 }
62
63 ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
64 ASSERT(dp->i_df.if_u1.if_data != NULL);
65
66 sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
67
68 ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
69
70 /*
71 * If the block number in the offset is out of range, we're done.
72 */
73 if (xfs_dir2_dataptr_to_db(mp, ctx->pos) > mp->m_dirdatablk)
74 return 0;
75
76 /*
77 * Precalculate offsets for . and .. as we will always need them.
78 *
79 * XXX(hch): the second argument is sometimes 0 and sometimes
80 * mp->m_dirdatablk.
81 */
82 dot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
83 XFS_DIR3_DATA_DOT_OFFSET(mp));
84 dotdot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
85 XFS_DIR3_DATA_DOTDOT_OFFSET(mp));
86
87 /*
88 * Put . entry unless we're starting past it.
89 */
90 if (ctx->pos <= dot_offset) {
91 ctx->pos = dot_offset & 0x7fffffff;
92 if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR))
93 return 0;
94 }
95
96 /*
97 * Put .. entry unless we're starting past it.
98 */
99 if (ctx->pos <= dotdot_offset) {
100 ino = xfs_dir2_sf_get_parent_ino(sfp);
101 ctx->pos = dotdot_offset & 0x7fffffff;
102 if (!dir_emit(ctx, "..", 2, ino, DT_DIR))
103 return 0;
104 }
105
106 /*
107 * Loop while there are more entries and put'ing works.
108 */
109 sfep = xfs_dir2_sf_firstentry(sfp);
110 for (i = 0; i < sfp->count; i++) {
111 off = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
112 xfs_dir2_sf_get_offset(sfep));
113
114 if (ctx->pos > off) {
115 sfep = xfs_dir2_sf_nextentry(sfp, sfep);
116 continue;
117 }
118
119 ino = xfs_dir2_sfe_get_ino(sfp, sfep);
120 ctx->pos = off & 0x7fffffff;
121 if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen,
122 ino, DT_UNKNOWN))
123 return 0;
124 sfep = xfs_dir2_sf_nextentry(sfp, sfep);
125 }
126
127 ctx->pos = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) &
128 0x7fffffff;
129 return 0;
130}
131
132/*
133 * Readdir for block directories.
134 */
135STATIC int
136xfs_dir2_block_getdents(
137 xfs_inode_t *dp, /* incore inode */
138 struct dir_context *ctx)
139{
140 xfs_dir2_data_hdr_t *hdr; /* block header */
141 struct xfs_buf *bp; /* buffer for block */
142 xfs_dir2_block_tail_t *btp; /* block tail */
143 xfs_dir2_data_entry_t *dep; /* block data entry */
144 xfs_dir2_data_unused_t *dup; /* block unused entry */
145 char *endptr; /* end of the data entries */
146 int error; /* error return value */
147 xfs_mount_t *mp; /* filesystem mount point */
148 char *ptr; /* current data entry */
149 int wantoff; /* starting block offset */
150 xfs_off_t cook;
151
152 mp = dp->i_mount;
153 /*
154 * If the block number in the offset is out of range, we're done.
155 */
156 if (xfs_dir2_dataptr_to_db(mp, ctx->pos) > mp->m_dirdatablk)
157 return 0;
158
159 error = xfs_dir3_block_read(NULL, dp, &bp);
160 if (error)
161 return error;
162
163 /*
164 * Extract the byte offset we start at from the seek pointer.
165 * We'll skip entries before this.
166 */
167 wantoff = xfs_dir2_dataptr_to_off(mp, ctx->pos);
168 hdr = bp->b_addr;
169 xfs_dir3_data_check(dp, bp);
170 /*
171 * Set up values for the loop.
172 */
173 btp = xfs_dir2_block_tail_p(mp, hdr);
174 ptr = (char *)xfs_dir3_data_entry_p(hdr);
175 endptr = (char *)xfs_dir2_block_leaf_p(btp);
176
177 /*
178 * Loop over the data portion of the block.
179 * Each object is a real entry (dep) or an unused one (dup).
180 */
181 while (ptr < endptr) {
182 dup = (xfs_dir2_data_unused_t *)ptr;
183 /*
184 * Unused, skip it.
185 */
186 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
187 ptr += be16_to_cpu(dup->length);
188 continue;
189 }
190
191 dep = (xfs_dir2_data_entry_t *)ptr;
192
193 /*
194 * Bump pointer for the next iteration.
195 */
196 ptr += xfs_dir2_data_entsize(dep->namelen);
197 /*
198 * The entry is before the desired starting point, skip it.
199 */
200 if ((char *)dep - (char *)hdr < wantoff)
201 continue;
202
203 cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
204 (char *)dep - (char *)hdr);
205
206 ctx->pos = cook & 0x7fffffff;
207 /*
208 * If it didn't fit, set the final offset to here & return.
209 */
210 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
211 be64_to_cpu(dep->inumber), DT_UNKNOWN)) {
212 xfs_trans_brelse(NULL, bp);
213 return 0;
214 }
215 }
216
217 /*
218 * Reached the end of the block.
219 * Set the offset to a non-existent block 1 and return.
220 */
221 ctx->pos = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) &
222 0x7fffffff;
223 xfs_trans_brelse(NULL, bp);
224 return 0;
225}
226
227struct xfs_dir2_leaf_map_info {
228 xfs_extlen_t map_blocks; /* number of fsbs in map */
229 xfs_dablk_t map_off; /* last mapped file offset */
230 int map_size; /* total entries in *map */
231 int map_valid; /* valid entries in *map */
232 int nmap; /* mappings to ask xfs_bmapi */
233 xfs_dir2_db_t curdb; /* db for current block */
234 int ra_current; /* number of read-ahead blks */
235 int ra_index; /* *map index for read-ahead */
236 int ra_offset; /* map entry offset for ra */
237 int ra_want; /* readahead count wanted */
238 struct xfs_bmbt_irec map[]; /* map vector for blocks */
239};
240
241STATIC int
242xfs_dir2_leaf_readbuf(
243 struct xfs_inode *dp,
244 size_t bufsize,
245 struct xfs_dir2_leaf_map_info *mip,
246 xfs_dir2_off_t *curoff,
247 struct xfs_buf **bpp)
248{
249 struct xfs_mount *mp = dp->i_mount;
250 struct xfs_buf *bp = *bpp;
251 struct xfs_bmbt_irec *map = mip->map;
252 struct blk_plug plug;
253 int error = 0;
254 int length;
255 int i;
256 int j;
257
258 /*
259 * If we have a buffer, we need to release it and
260 * take it out of the mapping.
261 */
262
263 if (bp) {
264 xfs_trans_brelse(NULL, bp);
265 bp = NULL;
266 mip->map_blocks -= mp->m_dirblkfsbs;
267 /*
268 * Loop to get rid of the extents for the
269 * directory block.
270 */
271 for (i = mp->m_dirblkfsbs; i > 0; ) {
272 j = min_t(int, map->br_blockcount, i);
273 map->br_blockcount -= j;
274 map->br_startblock += j;
275 map->br_startoff += j;
276 /*
277 * If mapping is done, pitch it from
278 * the table.
279 */
280 if (!map->br_blockcount && --mip->map_valid)
281 memmove(&map[0], &map[1],
282 sizeof(map[0]) * mip->map_valid);
283 i -= j;
284 }
285 }
286
287 /*
288 * Recalculate the readahead blocks wanted.
289 */
290 mip->ra_want = howmany(bufsize + mp->m_dirblksize,
291 mp->m_sb.sb_blocksize) - 1;
292 ASSERT(mip->ra_want >= 0);
293
294 /*
295 * If we don't have as many as we want, and we haven't
296 * run out of data blocks, get some more mappings.
297 */
298 if (1 + mip->ra_want > mip->map_blocks &&
299 mip->map_off < xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET)) {
300 /*
301 * Get more bmaps, fill in after the ones
302 * we already have in the table.
303 */
304 mip->nmap = mip->map_size - mip->map_valid;
305 error = xfs_bmapi_read(dp, mip->map_off,
306 xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET) -
307 mip->map_off,
308 &map[mip->map_valid], &mip->nmap, 0);
309
310 /*
311 * Don't know if we should ignore this or try to return an
312 * error. The trouble with returning errors is that readdir
313 * will just stop without actually passing the error through.
314 */
315 if (error)
316 goto out; /* XXX */
317
318 /*
319 * If we got all the mappings we asked for, set the final map
320 * offset based on the last bmap value received. Otherwise,
321 * we've reached the end.
322 */
323 if (mip->nmap == mip->map_size - mip->map_valid) {
324 i = mip->map_valid + mip->nmap - 1;
325 mip->map_off = map[i].br_startoff + map[i].br_blockcount;
326 } else
327 mip->map_off = xfs_dir2_byte_to_da(mp,
328 XFS_DIR2_LEAF_OFFSET);
329
330 /*
331 * Look for holes in the mapping, and eliminate them. Count up
332 * the valid blocks.
333 */
334 for (i = mip->map_valid; i < mip->map_valid + mip->nmap; ) {
335 if (map[i].br_startblock == HOLESTARTBLOCK) {
336 mip->nmap--;
337 length = mip->map_valid + mip->nmap - i;
338 if (length)
339 memmove(&map[i], &map[i + 1],
340 sizeof(map[i]) * length);
341 } else {
342 mip->map_blocks += map[i].br_blockcount;
343 i++;
344 }
345 }
346 mip->map_valid += mip->nmap;
347 }
348
349 /*
350 * No valid mappings, so no more data blocks.
351 */
352 if (!mip->map_valid) {
353 *curoff = xfs_dir2_da_to_byte(mp, mip->map_off);
354 goto out;
355 }
356
357 /*
358 * Read the directory block starting at the first mapping.
359 */
360 mip->curdb = xfs_dir2_da_to_db(mp, map->br_startoff);
361 error = xfs_dir3_data_read(NULL, dp, map->br_startoff,
362 map->br_blockcount >= mp->m_dirblkfsbs ?
363 XFS_FSB_TO_DADDR(mp, map->br_startblock) : -1, &bp);
364
365 /*
366 * Should just skip over the data block instead of giving up.
367 */
368 if (error)
369 goto out; /* XXX */
370
371 /*
372 * Adjust the current amount of read-ahead: we just read a block that
373 * was previously ra.
374 */
375 if (mip->ra_current)
376 mip->ra_current -= mp->m_dirblkfsbs;
377
378 /*
379 * Do we need more readahead?
380 */
381 blk_start_plug(&plug);
382 for (mip->ra_index = mip->ra_offset = i = 0;
383 mip->ra_want > mip->ra_current && i < mip->map_blocks;
384 i += mp->m_dirblkfsbs) {
385 ASSERT(mip->ra_index < mip->map_valid);
386 /*
387 * Read-ahead a contiguous directory block.
388 */
389 if (i > mip->ra_current &&
390 map[mip->ra_index].br_blockcount >= mp->m_dirblkfsbs) {
391 xfs_dir3_data_readahead(NULL, dp,
392 map[mip->ra_index].br_startoff + mip->ra_offset,
393 XFS_FSB_TO_DADDR(mp,
394 map[mip->ra_index].br_startblock +
395 mip->ra_offset));
396 mip->ra_current = i;
397 }
398
399 /*
400 * Read-ahead a non-contiguous directory block. This doesn't
401 * use our mapping, but this is a very rare case.
402 */
403 else if (i > mip->ra_current) {
404 xfs_dir3_data_readahead(NULL, dp,
405 map[mip->ra_index].br_startoff +
406 mip->ra_offset, -1);
407 mip->ra_current = i;
408 }
409
410 /*
411 * Advance offset through the mapping table.
412 */
413 for (j = 0; j < mp->m_dirblkfsbs; j++) {
414 /*
415 * The rest of this extent but not more than a dir
416 * block.
417 */
418 length = min_t(int, mp->m_dirblkfsbs,
419 map[mip->ra_index].br_blockcount -
420 mip->ra_offset);
421 j += length;
422 mip->ra_offset += length;
423
424 /*
425 * Advance to the next mapping if this one is used up.
426 */
427 if (mip->ra_offset == map[mip->ra_index].br_blockcount) {
428 mip->ra_offset = 0;
429 mip->ra_index++;
430 }
431 }
432 }
433 blk_finish_plug(&plug);
434
435out:
436 *bpp = bp;
437 return error;
438}
439
440/*
441 * Getdents (readdir) for leaf and node directories.
442 * This reads the data blocks only, so is the same for both forms.
443 */
444STATIC int
445xfs_dir2_leaf_getdents(
446 xfs_inode_t *dp, /* incore directory inode */
447 struct dir_context *ctx,
448 size_t bufsize)
449{
450 struct xfs_buf *bp = NULL; /* data block buffer */
451 xfs_dir2_data_hdr_t *hdr; /* data block header */
452 xfs_dir2_data_entry_t *dep; /* data entry */
453 xfs_dir2_data_unused_t *dup; /* unused entry */
454 int error = 0; /* error return value */
455 int length; /* temporary length value */
456 xfs_mount_t *mp; /* filesystem mount point */
457 int byteoff; /* offset in current block */
458 xfs_dir2_off_t curoff; /* current overall offset */
459 xfs_dir2_off_t newoff; /* new curoff after new blk */
460 char *ptr = NULL; /* pointer to current data */
461 struct xfs_dir2_leaf_map_info *map_info;
462
463 /*
464 * If the offset is at or past the largest allowed value,
465 * give up right away.
466 */
467 if (ctx->pos >= XFS_DIR2_MAX_DATAPTR)
468 return 0;
469
470 mp = dp->i_mount;
471
472 /*
473 * Set up to bmap a number of blocks based on the caller's
474 * buffer size, the directory block size, and the filesystem
475 * block size.
476 */
477 length = howmany(bufsize + mp->m_dirblksize,
478 mp->m_sb.sb_blocksize);
479 map_info = kmem_zalloc(offsetof(struct xfs_dir2_leaf_map_info, map) +
480 (length * sizeof(struct xfs_bmbt_irec)),
481 KM_SLEEP | KM_NOFS);
482 map_info->map_size = length;
483
484 /*
485 * Inside the loop we keep the main offset value as a byte offset
486 * in the directory file.
487 */
488 curoff = xfs_dir2_dataptr_to_byte(mp, ctx->pos);
489
490 /*
491 * Force this conversion through db so we truncate the offset
492 * down to get the start of the data block.
493 */
494 map_info->map_off = xfs_dir2_db_to_da(mp,
495 xfs_dir2_byte_to_db(mp, curoff));
496
497 /*
498 * Loop over directory entries until we reach the end offset.
499 * Get more blocks and readahead as necessary.
500 */
501 while (curoff < XFS_DIR2_LEAF_OFFSET) {
502 /*
503 * If we have no buffer, or we're off the end of the
504 * current buffer, need to get another one.
505 */
506 if (!bp || ptr >= (char *)bp->b_addr + mp->m_dirblksize) {
507
508 error = xfs_dir2_leaf_readbuf(dp, bufsize, map_info,
509 &curoff, &bp);
510 if (error || !map_info->map_valid)
511 break;
512
513 /*
514 * Having done a read, we need to set a new offset.
515 */
516 newoff = xfs_dir2_db_off_to_byte(mp, map_info->curdb, 0);
517 /*
518 * Start of the current block.
519 */
520 if (curoff < newoff)
521 curoff = newoff;
522 /*
523 * Make sure we're in the right block.
524 */
525 else if (curoff > newoff)
526 ASSERT(xfs_dir2_byte_to_db(mp, curoff) ==
527 map_info->curdb);
528 hdr = bp->b_addr;
529 xfs_dir3_data_check(dp, bp);
530 /*
531 * Find our position in the block.
532 */
533 ptr = (char *)xfs_dir3_data_entry_p(hdr);
534 byteoff = xfs_dir2_byte_to_off(mp, curoff);
535 /*
536 * Skip past the header.
537 */
538 if (byteoff == 0)
539 curoff += xfs_dir3_data_entry_offset(hdr);
540 /*
541 * Skip past entries until we reach our offset.
542 */
543 else {
544 while ((char *)ptr - (char *)hdr < byteoff) {
545 dup = (xfs_dir2_data_unused_t *)ptr;
546
547 if (be16_to_cpu(dup->freetag)
548 == XFS_DIR2_DATA_FREE_TAG) {
549
550 length = be16_to_cpu(dup->length);
551 ptr += length;
552 continue;
553 }
554 dep = (xfs_dir2_data_entry_t *)ptr;
555 length =
556 xfs_dir2_data_entsize(dep->namelen);
557 ptr += length;
558 }
559 /*
560 * Now set our real offset.
561 */
562 curoff =
563 xfs_dir2_db_off_to_byte(mp,
564 xfs_dir2_byte_to_db(mp, curoff),
565 (char *)ptr - (char *)hdr);
566 if (ptr >= (char *)hdr + mp->m_dirblksize) {
567 continue;
568 }
569 }
570 }
571 /*
572 * We have a pointer to an entry.
573 * Is it a live one?
574 */
575 dup = (xfs_dir2_data_unused_t *)ptr;
576 /*
577 * No, it's unused, skip over it.
578 */
579 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
580 length = be16_to_cpu(dup->length);
581 ptr += length;
582 curoff += length;
583 continue;
584 }
585
586 dep = (xfs_dir2_data_entry_t *)ptr;
587 length = xfs_dir2_data_entsize(dep->namelen);
588
589 ctx->pos = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
590 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
591 be64_to_cpu(dep->inumber), DT_UNKNOWN))
592 break;
593
594 /*
595 * Advance to next entry in the block.
596 */
597 ptr += length;
598 curoff += length;
599 /* bufsize may have just been a guess; don't go negative */
600 bufsize = bufsize > length ? bufsize - length : 0;
601 }
602
603 /*
604 * All done. Set output offset value to current offset.
605 */
606 if (curoff > xfs_dir2_dataptr_to_byte(mp, XFS_DIR2_MAX_DATAPTR))
607 ctx->pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;
608 else
609 ctx->pos = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
610 kmem_free(map_info);
611 if (bp)
612 xfs_trans_brelse(NULL, bp);
613 return error;
614}
615
616/*
617 * Read a directory.
618 */
619int
620xfs_readdir(
621 xfs_inode_t *dp,
622 struct dir_context *ctx,
623 size_t bufsize)
624{
625 int rval; /* return value */
626 int v; /* type-checking value */
627
628 trace_xfs_readdir(dp);
629
630 if (XFS_FORCED_SHUTDOWN(dp->i_mount))
631 return XFS_ERROR(EIO);
632
633 ASSERT(S_ISDIR(dp->i_d.di_mode));
634 XFS_STATS_INC(xs_dir_getdents);
635
636 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
637 rval = xfs_dir2_sf_getdents(dp, ctx);
638 else if ((rval = xfs_dir2_isblock(NULL, dp, &v)))
639 ;
640 else if (v)
641 rval = xfs_dir2_block_getdents(dp, ctx);
642 else
643 rval = xfs_dir2_leaf_getdents(dp, ctx, bufsize);
644 return rval;
645}