1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include "xfs.h"
20#include "xfs_fs.h"
21#include "xfs_format.h"
22#include "xfs_log_format.h"
23#include "xfs_trans_resv.h"
24#include "xfs_bit.h"
25#include "xfs_sb.h"
26#include "xfs_ag.h"
27#include "xfs_mount.h"
28#include "xfs_da_format.h"
29#include "xfs_da_btree.h"
30#include "xfs_inode.h"
31#include "xfs_dir2.h"
32#include "xfs_dir2_priv.h"
33#include "xfs_error.h"
34#include "xfs_trace.h"
35#include "xfs_bmap.h"
36#include "xfs_trans.h"
37#include "xfs_dinode.h"
38
39
40
41
42static unsigned char xfs_dir3_filetype_table[] = {
43 DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK,
44 DT_FIFO, DT_SOCK, DT_LNK, DT_WHT,
45};
46
47unsigned char
48xfs_dir3_get_dtype(
49 struct xfs_mount *mp,
50 __uint8_t filetype)
51{
52 if (!xfs_sb_version_hasftype(&mp->m_sb))
53 return DT_UNKNOWN;
54
55 if (filetype >= XFS_DIR3_FT_MAX)
56 return DT_UNKNOWN;
57
58 return xfs_dir3_filetype_table[filetype];
59}
60
61
62
63
64
65
66const unsigned char xfs_mode_to_ftype[S_IFMT >> S_SHIFT] = {
67 [0] = XFS_DIR3_FT_UNKNOWN,
68 [S_IFREG >> S_SHIFT] = XFS_DIR3_FT_REG_FILE,
69 [S_IFDIR >> S_SHIFT] = XFS_DIR3_FT_DIR,
70 [S_IFCHR >> S_SHIFT] = XFS_DIR3_FT_CHRDEV,
71 [S_IFBLK >> S_SHIFT] = XFS_DIR3_FT_BLKDEV,
72 [S_IFIFO >> S_SHIFT] = XFS_DIR3_FT_FIFO,
73 [S_IFSOCK >> S_SHIFT] = XFS_DIR3_FT_SOCK,
74 [S_IFLNK >> S_SHIFT] = XFS_DIR3_FT_SYMLINK,
75};
76
77STATIC int
78xfs_dir2_sf_getdents(
79 xfs_inode_t *dp,
80 struct dir_context *ctx)
81{
82 int i;
83 xfs_mount_t *mp;
84 xfs_dir2_dataptr_t off;
85 xfs_dir2_sf_entry_t *sfep;
86 xfs_dir2_sf_hdr_t *sfp;
87 xfs_dir2_dataptr_t dot_offset;
88 xfs_dir2_dataptr_t dotdot_offset;
89 xfs_ino_t ino;
90
91 mp = dp->i_mount;
92
93 ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
94
95
96
97 if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
98 ASSERT(XFS_FORCED_SHUTDOWN(mp));
99 return XFS_ERROR(EIO);
100 }
101
102 ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
103 ASSERT(dp->i_df.if_u1.if_data != NULL);
104
105 sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
106
107 ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
108
109
110
111
112 if (xfs_dir2_dataptr_to_db(mp, ctx->pos) > mp->m_dirdatablk)
113 return 0;
114
115
116
117
118
119
120
121 dot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
122 dp->d_ops->data_dot_offset);
123 dotdot_offset = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
124 dp->d_ops->data_dotdot_offset);
125
126
127
128
129 if (ctx->pos <= dot_offset) {
130 ctx->pos = dot_offset & 0x7fffffff;
131 if (!dir_emit(ctx, ".", 1, dp->i_ino, DT_DIR))
132 return 0;
133 }
134
135
136
137
138 if (ctx->pos <= dotdot_offset) {
139 ino = dp->d_ops->sf_get_parent_ino(sfp);
140 ctx->pos = dotdot_offset & 0x7fffffff;
141 if (!dir_emit(ctx, "..", 2, ino, DT_DIR))
142 return 0;
143 }
144
145
146
147
148 sfep = xfs_dir2_sf_firstentry(sfp);
149 for (i = 0; i < sfp->count; i++) {
150 __uint8_t filetype;
151
152 off = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
153 xfs_dir2_sf_get_offset(sfep));
154
155 if (ctx->pos > off) {
156 sfep = dp->d_ops->sf_nextentry(sfp, sfep);
157 continue;
158 }
159
160 ino = dp->d_ops->sf_get_ino(sfp, sfep);
161 filetype = dp->d_ops->sf_get_ftype(sfep);
162 ctx->pos = off & 0x7fffffff;
163 if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,
164 xfs_dir3_get_dtype(mp, filetype)))
165 return 0;
166 sfep = dp->d_ops->sf_nextentry(sfp, sfep);
167 }
168
169 ctx->pos = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) &
170 0x7fffffff;
171 return 0;
172}
173
174
175
176
177STATIC int
178xfs_dir2_block_getdents(
179 xfs_inode_t *dp,
180 struct dir_context *ctx)
181{
182 xfs_dir2_data_hdr_t *hdr;
183 struct xfs_buf *bp;
184 xfs_dir2_block_tail_t *btp;
185 xfs_dir2_data_entry_t *dep;
186 xfs_dir2_data_unused_t *dup;
187 char *endptr;
188 int error;
189 xfs_mount_t *mp;
190 char *ptr;
191 int wantoff;
192 xfs_off_t cook;
193
194 mp = dp->i_mount;
195
196
197
198 if (xfs_dir2_dataptr_to_db(mp, ctx->pos) > mp->m_dirdatablk)
199 return 0;
200
201 error = xfs_dir3_block_read(NULL, dp, &bp);
202 if (error)
203 return error;
204
205
206
207
208
209 wantoff = xfs_dir2_dataptr_to_off(mp, ctx->pos);
210 hdr = bp->b_addr;
211 xfs_dir3_data_check(dp, bp);
212
213
214
215 btp = xfs_dir2_block_tail_p(mp, hdr);
216 ptr = (char *)dp->d_ops->data_entry_p(hdr);
217 endptr = (char *)xfs_dir2_block_leaf_p(btp);
218
219
220
221
222
223 while (ptr < endptr) {
224 __uint8_t filetype;
225
226 dup = (xfs_dir2_data_unused_t *)ptr;
227
228
229
230 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
231 ptr += be16_to_cpu(dup->length);
232 continue;
233 }
234
235 dep = (xfs_dir2_data_entry_t *)ptr;
236
237
238
239
240 ptr += dp->d_ops->data_entsize(dep->namelen);
241
242
243
244 if ((char *)dep - (char *)hdr < wantoff)
245 continue;
246
247 cook = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
248 (char *)dep - (char *)hdr);
249
250 ctx->pos = cook & 0x7fffffff;
251 filetype = dp->d_ops->data_get_ftype(dep);
252
253
254
255 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
256 be64_to_cpu(dep->inumber),
257 xfs_dir3_get_dtype(mp, filetype))) {
258 xfs_trans_brelse(NULL, bp);
259 return 0;
260 }
261 }
262
263
264
265
266
267 ctx->pos = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk + 1, 0) &
268 0x7fffffff;
269 xfs_trans_brelse(NULL, bp);
270 return 0;
271}
272
273struct xfs_dir2_leaf_map_info {
274 xfs_extlen_t map_blocks;
275 xfs_dablk_t map_off;
276 int map_size;
277 int map_valid;
278 int nmap;
279 xfs_dir2_db_t curdb;
280 int ra_current;
281 int ra_index;
282 int ra_offset;
283 int ra_want;
284 struct xfs_bmbt_irec map[];
285};
286
287STATIC int
288xfs_dir2_leaf_readbuf(
289 struct xfs_inode *dp,
290 size_t bufsize,
291 struct xfs_dir2_leaf_map_info *mip,
292 xfs_dir2_off_t *curoff,
293 struct xfs_buf **bpp)
294{
295 struct xfs_mount *mp = dp->i_mount;
296 struct xfs_buf *bp = *bpp;
297 struct xfs_bmbt_irec *map = mip->map;
298 struct blk_plug plug;
299 int error = 0;
300 int length;
301 int i;
302 int j;
303
304
305
306
307
308
309 if (bp) {
310 xfs_trans_brelse(NULL, bp);
311 bp = NULL;
312 mip->map_blocks -= mp->m_dirblkfsbs;
313
314
315
316
317 for (i = mp->m_dirblkfsbs; i > 0; ) {
318 j = min_t(int, map->br_blockcount, i);
319 map->br_blockcount -= j;
320 map->br_startblock += j;
321 map->br_startoff += j;
322
323
324
325
326 if (!map->br_blockcount && --mip->map_valid)
327 memmove(&map[0], &map[1],
328 sizeof(map[0]) * mip->map_valid);
329 i -= j;
330 }
331 }
332
333
334
335
336 mip->ra_want = howmany(bufsize + mp->m_dirblksize,
337 mp->m_sb.sb_blocksize) - 1;
338 ASSERT(mip->ra_want >= 0);
339
340
341
342
343
344 if (1 + mip->ra_want > mip->map_blocks &&
345 mip->map_off < xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET)) {
346
347
348
349
350 mip->nmap = mip->map_size - mip->map_valid;
351 error = xfs_bmapi_read(dp, mip->map_off,
352 xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET) -
353 mip->map_off,
354 &map[mip->map_valid], &mip->nmap, 0);
355
356
357
358
359
360
361 if (error)
362 goto out;
363
364
365
366
367
368
369 if (mip->nmap == mip->map_size - mip->map_valid) {
370 i = mip->map_valid + mip->nmap - 1;
371 mip->map_off = map[i].br_startoff + map[i].br_blockcount;
372 } else
373 mip->map_off = xfs_dir2_byte_to_da(mp,
374 XFS_DIR2_LEAF_OFFSET);
375
376
377
378
379
380 for (i = mip->map_valid; i < mip->map_valid + mip->nmap; ) {
381 if (map[i].br_startblock == HOLESTARTBLOCK) {
382 mip->nmap--;
383 length = mip->map_valid + mip->nmap - i;
384 if (length)
385 memmove(&map[i], &map[i + 1],
386 sizeof(map[i]) * length);
387 } else {
388 mip->map_blocks += map[i].br_blockcount;
389 i++;
390 }
391 }
392 mip->map_valid += mip->nmap;
393 }
394
395
396
397
398 if (!mip->map_valid) {
399 *curoff = xfs_dir2_da_to_byte(mp, mip->map_off);
400 goto out;
401 }
402
403
404
405
406 mip->curdb = xfs_dir2_da_to_db(mp, map->br_startoff);
407 error = xfs_dir3_data_read(NULL, dp, map->br_startoff,
408 map->br_blockcount >= mp->m_dirblkfsbs ?
409 XFS_FSB_TO_DADDR(mp, map->br_startblock) : -1, &bp);
410
411
412
413
414 if (error)
415 goto out;
416
417
418
419
420
421 if (mip->ra_current)
422 mip->ra_current -= mp->m_dirblkfsbs;
423
424
425
426
427 blk_start_plug(&plug);
428 for (mip->ra_index = mip->ra_offset = i = 0;
429 mip->ra_want > mip->ra_current && i < mip->map_blocks;
430 i += mp->m_dirblkfsbs) {
431 ASSERT(mip->ra_index < mip->map_valid);
432
433
434
435 if (i > mip->ra_current &&
436 map[mip->ra_index].br_blockcount >= mp->m_dirblkfsbs) {
437 xfs_dir3_data_readahead(NULL, dp,
438 map[mip->ra_index].br_startoff + mip->ra_offset,
439 XFS_FSB_TO_DADDR(mp,
440 map[mip->ra_index].br_startblock +
441 mip->ra_offset));
442 mip->ra_current = i;
443 }
444
445
446
447
448
449 else if (i > mip->ra_current) {
450 xfs_dir3_data_readahead(NULL, dp,
451 map[mip->ra_index].br_startoff +
452 mip->ra_offset, -1);
453 mip->ra_current = i;
454 }
455
456
457
458
459 for (j = 0; j < mp->m_dirblkfsbs; j++) {
460
461
462
463
464 length = min_t(int, mp->m_dirblkfsbs,
465 map[mip->ra_index].br_blockcount -
466 mip->ra_offset);
467 j += length;
468 mip->ra_offset += length;
469
470
471
472
473 if (mip->ra_offset == map[mip->ra_index].br_blockcount) {
474 mip->ra_offset = 0;
475 mip->ra_index++;
476 }
477 }
478 }
479 blk_finish_plug(&plug);
480
481out:
482 *bpp = bp;
483 return error;
484}
485
486
487
488
489
490STATIC int
491xfs_dir2_leaf_getdents(
492 xfs_inode_t *dp,
493 struct dir_context *ctx,
494 size_t bufsize)
495{
496 struct xfs_buf *bp = NULL;
497 xfs_dir2_data_hdr_t *hdr;
498 xfs_dir2_data_entry_t *dep;
499 xfs_dir2_data_unused_t *dup;
500 int error = 0;
501 int length;
502 xfs_mount_t *mp;
503 int byteoff;
504 xfs_dir2_off_t curoff;
505 xfs_dir2_off_t newoff;
506 char *ptr = NULL;
507 struct xfs_dir2_leaf_map_info *map_info;
508
509
510
511
512
513 if (ctx->pos >= XFS_DIR2_MAX_DATAPTR)
514 return 0;
515
516 mp = dp->i_mount;
517
518
519
520
521
522
523 length = howmany(bufsize + mp->m_dirblksize,
524 mp->m_sb.sb_blocksize);
525 map_info = kmem_zalloc(offsetof(struct xfs_dir2_leaf_map_info, map) +
526 (length * sizeof(struct xfs_bmbt_irec)),
527 KM_SLEEP | KM_NOFS);
528 map_info->map_size = length;
529
530
531
532
533
534 curoff = xfs_dir2_dataptr_to_byte(mp, ctx->pos);
535
536
537
538
539
540 map_info->map_off = xfs_dir2_db_to_da(mp,
541 xfs_dir2_byte_to_db(mp, curoff));
542
543
544
545
546
547 while (curoff < XFS_DIR2_LEAF_OFFSET) {
548 __uint8_t filetype;
549
550
551
552
553
554 if (!bp || ptr >= (char *)bp->b_addr + mp->m_dirblksize) {
555
556 error = xfs_dir2_leaf_readbuf(dp, bufsize, map_info,
557 &curoff, &bp);
558 if (error || !map_info->map_valid)
559 break;
560
561
562
563
564 newoff = xfs_dir2_db_off_to_byte(mp, map_info->curdb, 0);
565
566
567
568 if (curoff < newoff)
569 curoff = newoff;
570
571
572
573 else if (curoff > newoff)
574 ASSERT(xfs_dir2_byte_to_db(mp, curoff) ==
575 map_info->curdb);
576 hdr = bp->b_addr;
577 xfs_dir3_data_check(dp, bp);
578
579
580
581 ptr = (char *)dp->d_ops->data_entry_p(hdr);
582 byteoff = xfs_dir2_byte_to_off(mp, curoff);
583
584
585
586 if (byteoff == 0)
587 curoff += dp->d_ops->data_entry_offset;
588
589
590
591 else {
592 while ((char *)ptr - (char *)hdr < byteoff) {
593 dup = (xfs_dir2_data_unused_t *)ptr;
594
595 if (be16_to_cpu(dup->freetag)
596 == XFS_DIR2_DATA_FREE_TAG) {
597
598 length = be16_to_cpu(dup->length);
599 ptr += length;
600 continue;
601 }
602 dep = (xfs_dir2_data_entry_t *)ptr;
603 length =
604 dp->d_ops->data_entsize(dep->namelen);
605 ptr += length;
606 }
607
608
609
610 curoff =
611 xfs_dir2_db_off_to_byte(mp,
612 xfs_dir2_byte_to_db(mp, curoff),
613 (char *)ptr - (char *)hdr);
614 if (ptr >= (char *)hdr + mp->m_dirblksize) {
615 continue;
616 }
617 }
618 }
619
620
621
622
623 dup = (xfs_dir2_data_unused_t *)ptr;
624
625
626
627 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
628 length = be16_to_cpu(dup->length);
629 ptr += length;
630 curoff += length;
631 continue;
632 }
633
634 dep = (xfs_dir2_data_entry_t *)ptr;
635 length = dp->d_ops->data_entsize(dep->namelen);
636 filetype = dp->d_ops->data_get_ftype(dep);
637
638 ctx->pos = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
639 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
640 be64_to_cpu(dep->inumber),
641 xfs_dir3_get_dtype(mp, filetype)))
642 break;
643
644
645
646
647 ptr += length;
648 curoff += length;
649
650 bufsize = bufsize > length ? bufsize - length : 0;
651 }
652
653
654
655
656 if (curoff > xfs_dir2_dataptr_to_byte(mp, XFS_DIR2_MAX_DATAPTR))
657 ctx->pos = XFS_DIR2_MAX_DATAPTR & 0x7fffffff;
658 else
659 ctx->pos = xfs_dir2_byte_to_dataptr(mp, curoff) & 0x7fffffff;
660 kmem_free(map_info);
661 if (bp)
662 xfs_trans_brelse(NULL, bp);
663 return error;
664}
665
666
667
668
669int
670xfs_readdir(
671 xfs_inode_t *dp,
672 struct dir_context *ctx,
673 size_t bufsize)
674{
675 int rval;
676 int v;
677
678 trace_xfs_readdir(dp);
679
680 if (XFS_FORCED_SHUTDOWN(dp->i_mount))
681 return XFS_ERROR(EIO);
682
683 ASSERT(S_ISDIR(dp->i_d.di_mode));
684 XFS_STATS_INC(xs_dir_getdents);
685
686 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
687 rval = xfs_dir2_sf_getdents(dp, ctx);
688 else if ((rval = xfs_dir2_isblock(NULL, dp, &v)))
689 ;
690 else if (v)
691 rval = xfs_dir2_block_getdents(dp, ctx);
692 else
693 rval = xfs_dir2_leaf_getdents(dp, ctx, bufsize);
694 return rval;
695}
696