hc
2024-09-20 a36159eec6ca17402b0e146b86efaf76568dc353
kernel/fs/xfs/xfs_dir2_readdir.c
....@@ -6,20 +6,18 @@
66 */
77 #include "xfs.h"
88 #include "xfs_fs.h"
9
+#include "xfs_shared.h"
910 #include "xfs_format.h"
1011 #include "xfs_log_format.h"
1112 #include "xfs_trans_resv.h"
12
-#include "xfs_bit.h"
1313 #include "xfs_mount.h"
14
-#include "xfs_da_format.h"
15
-#include "xfs_da_btree.h"
1614 #include "xfs_inode.h"
1715 #include "xfs_dir2.h"
1816 #include "xfs_dir2_priv.h"
19
-#include "xfs_error.h"
2017 #include "xfs_trace.h"
2118 #include "xfs_bmap.h"
2219 #include "xfs_trans.h"
20
+#include "xfs_error.h"
2321
2422 /*
2523 * Directory file type support functions
....@@ -50,6 +48,7 @@
5048 {
5149 int i; /* shortform entry number */
5250 struct xfs_inode *dp = args->dp; /* incore directory inode */
51
+ struct xfs_mount *mp = dp->i_mount;
5352 xfs_dir2_dataptr_t off; /* current entry's offset */
5453 xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
5554 xfs_dir2_sf_hdr_t *sfp; /* shortform structure */
....@@ -71,15 +70,15 @@
7170 return 0;
7271
7372 /*
74
- * Precalculate offsets for . and .. as we will always need them.
75
- *
76
- * XXX(hch): the second argument is sometimes 0 and sometimes
77
- * geo->datablk
73
+ * Precalculate offsets for "." and ".." as we will always need them.
74
+ * This relies on the fact that directories always start with the
75
+ * entries for "." and "..".
7876 */
7977 dot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
80
- dp->d_ops->data_dot_offset);
78
+ geo->data_entry_offset);
8179 dotdot_offset = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
82
- dp->d_ops->data_dotdot_offset);
80
+ geo->data_entry_offset +
81
+ xfs_dir2_data_entsize(mp, sizeof(".") - 1));
8382
8483 /*
8584 * Put . entry unless we're starting past it.
....@@ -94,7 +93,7 @@
9493 * Put .. entry unless we're starting past it.
9594 */
9695 if (ctx->pos <= dotdot_offset) {
97
- ino = dp->d_ops->sf_get_parent_ino(sfp);
96
+ ino = xfs_dir2_sf_get_parent_ino(sfp);
9897 ctx->pos = dotdot_offset & 0x7fffffff;
9998 if (!dir_emit(ctx, "..", 2, ino, DT_DIR))
10099 return 0;
....@@ -111,17 +110,21 @@
111110 xfs_dir2_sf_get_offset(sfep));
112111
113112 if (ctx->pos > off) {
114
- sfep = dp->d_ops->sf_nextentry(sfp, sfep);
113
+ sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
115114 continue;
116115 }
117116
118
- ino = dp->d_ops->sf_get_ino(sfp, sfep);
119
- filetype = dp->d_ops->sf_get_ftype(sfep);
117
+ ino = xfs_dir2_sf_get_ino(mp, sfp, sfep);
118
+ filetype = xfs_dir2_sf_get_ftype(mp, sfep);
120119 ctx->pos = off & 0x7fffffff;
120
+ if (XFS_IS_CORRUPT(dp->i_mount,
121
+ !xfs_dir2_namecheck(sfep->name,
122
+ sfep->namelen)))
123
+ return -EFSCORRUPTED;
121124 if (!dir_emit(ctx, (char *)sfep->name, sfep->namelen, ino,
122
- xfs_dir3_get_dtype(dp->i_mount, filetype)))
125
+ xfs_dir3_get_dtype(mp, filetype)))
123126 return 0;
124
- sfep = dp->d_ops->sf_nextentry(sfp, sfep);
127
+ sfep = xfs_dir2_sf_nextentry(mp, sfp, sfep);
125128 }
126129
127130 ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
....@@ -138,17 +141,14 @@
138141 struct dir_context *ctx)
139142 {
140143 struct xfs_inode *dp = args->dp; /* incore directory inode */
141
- xfs_dir2_data_hdr_t *hdr; /* block header */
142144 struct xfs_buf *bp; /* buffer for block */
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 */
146145 int error; /* error return value */
147
- char *ptr; /* current data entry */
148146 int wantoff; /* starting block offset */
149147 xfs_off_t cook;
150148 struct xfs_da_geometry *geo = args->geo;
151149 int lock_mode;
150
+ unsigned int offset, next_offset;
151
+ unsigned int end;
152152
153153 /*
154154 * If the block number in the offset is out of range, we're done.
....@@ -167,56 +167,57 @@
167167 * We'll skip entries before this.
168168 */
169169 wantoff = xfs_dir2_dataptr_to_off(geo, ctx->pos);
170
- hdr = bp->b_addr;
171170 xfs_dir3_data_check(dp, bp);
172
- /*
173
- * Set up values for the loop.
174
- */
175
- ptr = (char *)dp->d_ops->data_entry_p(hdr);
176
- endptr = xfs_dir3_data_endp(geo, hdr);
177171
178172 /*
179173 * Loop over the data portion of the block.
180174 * Each object is a real entry (dep) or an unused one (dup).
181175 */
182
- while (ptr < endptr) {
176
+ end = xfs_dir3_data_end_offset(geo, bp->b_addr);
177
+ for (offset = geo->data_entry_offset;
178
+ offset < end;
179
+ offset = next_offset) {
180
+ struct xfs_dir2_data_unused *dup = bp->b_addr + offset;
181
+ struct xfs_dir2_data_entry *dep = bp->b_addr + offset;
183182 uint8_t filetype;
184183
185
- dup = (xfs_dir2_data_unused_t *)ptr;
186184 /*
187185 * Unused, skip it.
188186 */
189187 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
190
- ptr += be16_to_cpu(dup->length);
188
+ next_offset = offset + be16_to_cpu(dup->length);
191189 continue;
192190 }
193
-
194
- dep = (xfs_dir2_data_entry_t *)ptr;
195191
196192 /*
197193 * Bump pointer for the next iteration.
198194 */
199
- ptr += dp->d_ops->data_entsize(dep->namelen);
195
+ next_offset = offset +
196
+ xfs_dir2_data_entsize(dp->i_mount, dep->namelen);
197
+
200198 /*
201199 * The entry is before the desired starting point, skip it.
202200 */
203
- if ((char *)dep - (char *)hdr < wantoff)
201
+ if (offset < wantoff)
204202 continue;
205203
206
- cook = xfs_dir2_db_off_to_dataptr(geo, geo->datablk,
207
- (char *)dep - (char *)hdr);
204
+ cook = xfs_dir2_db_off_to_dataptr(geo, geo->datablk, offset);
208205
209206 ctx->pos = cook & 0x7fffffff;
210
- filetype = dp->d_ops->data_get_ftype(dep);
207
+ filetype = xfs_dir2_data_get_ftype(dp->i_mount, dep);
211208 /*
212209 * If it didn't fit, set the final offset to here & return.
213210 */
211
+ if (XFS_IS_CORRUPT(dp->i_mount,
212
+ !xfs_dir2_namecheck(dep->name,
213
+ dep->namelen))) {
214
+ error = -EFSCORRUPTED;
215
+ goto out_rele;
216
+ }
214217 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
215218 be64_to_cpu(dep->inumber),
216
- xfs_dir3_get_dtype(dp->i_mount, filetype))) {
217
- xfs_trans_brelse(args->trans, bp);
218
- return 0;
219
- }
219
+ xfs_dir3_get_dtype(dp->i_mount, filetype)))
220
+ goto out_rele;
220221 }
221222
222223 /*
....@@ -225,8 +226,9 @@
225226 */
226227 ctx->pos = xfs_dir2_db_off_to_dataptr(geo, geo->datablk + 1, 0) &
227228 0x7fffffff;
229
+out_rele:
228230 xfs_trans_brelse(args->trans, bp);
229
- return 0;
231
+ return error;
230232 }
231233
232234 /*
....@@ -279,7 +281,7 @@
279281 new_off = xfs_dir2_da_to_byte(geo, map.br_startoff);
280282 if (new_off > *cur_off)
281283 *cur_off = new_off;
282
- error = xfs_dir3_data_read(args->trans, dp, map.br_startoff, -1, &bp);
284
+ error = xfs_dir3_data_read(args->trans, dp, map.br_startoff, 0, &bp);
283285 if (error)
284286 goto out;
285287
....@@ -314,7 +316,8 @@
314316 break;
315317 }
316318 if (next_ra > *ra_blk) {
317
- xfs_dir3_data_readahead(dp, next_ra, -2);
319
+ xfs_dir3_data_readahead(dp, next_ra,
320
+ XFS_DABUF_MAP_HOLE_OK);
318321 *ra_blk = next_ra;
319322 }
320323 ra_want -= geo->fsbcount;
....@@ -346,17 +349,17 @@
346349 size_t bufsize)
347350 {
348351 struct xfs_inode *dp = args->dp;
352
+ struct xfs_mount *mp = dp->i_mount;
349353 struct xfs_buf *bp = NULL; /* data block buffer */
350
- xfs_dir2_data_hdr_t *hdr; /* data block header */
351354 xfs_dir2_data_entry_t *dep; /* data entry */
352355 xfs_dir2_data_unused_t *dup; /* unused entry */
353
- char *ptr = NULL; /* pointer to current data */
354356 struct xfs_da_geometry *geo = args->geo;
355357 xfs_dablk_t rablk = 0; /* current readahead block */
356358 xfs_dir2_off_t curoff; /* current overall offset */
357359 int length; /* temporary length value */
358360 int byteoff; /* offset in current block */
359361 int lock_mode;
362
+ unsigned int offset = 0;
360363 int error = 0; /* error return value */
361364
362365 /*
....@@ -383,7 +386,7 @@
383386 * If we have no buffer, or we're off the end of the
384387 * current buffer, need to get another one.
385388 */
386
- if (!bp || ptr >= (char *)bp->b_addr + geo->blksize) {
389
+ if (!bp || offset >= geo->blksize) {
387390 if (bp) {
388391 xfs_trans_brelse(args->trans, bp);
389392 bp = NULL;
....@@ -396,36 +399,35 @@
396399 if (error || !bp)
397400 break;
398401
399
- hdr = bp->b_addr;
400402 xfs_dir3_data_check(dp, bp);
401403 /*
402404 * Find our position in the block.
403405 */
404
- ptr = (char *)dp->d_ops->data_entry_p(hdr);
406
+ offset = geo->data_entry_offset;
405407 byteoff = xfs_dir2_byte_to_off(geo, curoff);
406408 /*
407409 * Skip past the header.
408410 */
409411 if (byteoff == 0)
410
- curoff += dp->d_ops->data_entry_offset;
412
+ curoff += geo->data_entry_offset;
411413 /*
412414 * Skip past entries until we reach our offset.
413415 */
414416 else {
415
- while ((char *)ptr - (char *)hdr < byteoff) {
416
- dup = (xfs_dir2_data_unused_t *)ptr;
417
+ while (offset < byteoff) {
418
+ dup = bp->b_addr + offset;
417419
418420 if (be16_to_cpu(dup->freetag)
419421 == XFS_DIR2_DATA_FREE_TAG) {
420422
421423 length = be16_to_cpu(dup->length);
422
- ptr += length;
424
+ offset += length;
423425 continue;
424426 }
425
- dep = (xfs_dir2_data_entry_t *)ptr;
426
- length =
427
- dp->d_ops->data_entsize(dep->namelen);
428
- ptr += length;
427
+ dep = bp->b_addr + offset;
428
+ length = xfs_dir2_data_entsize(mp,
429
+ dep->namelen);
430
+ offset += length;
429431 }
430432 /*
431433 * Now set our real offset.
....@@ -433,32 +435,38 @@
433435 curoff =
434436 xfs_dir2_db_off_to_byte(geo,
435437 xfs_dir2_byte_to_db(geo, curoff),
436
- (char *)ptr - (char *)hdr);
437
- if (ptr >= (char *)hdr + geo->blksize) {
438
+ offset);
439
+ if (offset >= geo->blksize)
438440 continue;
439
- }
440441 }
441442 }
443
+
442444 /*
443
- * We have a pointer to an entry.
444
- * Is it a live one?
445
+ * We have a pointer to an entry. Is it a live one?
445446 */
446
- dup = (xfs_dir2_data_unused_t *)ptr;
447
+ dup = bp->b_addr + offset;
448
+
447449 /*
448450 * No, it's unused, skip over it.
449451 */
450452 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
451453 length = be16_to_cpu(dup->length);
452
- ptr += length;
454
+ offset += length;
453455 curoff += length;
454456 continue;
455457 }
456458
457
- dep = (xfs_dir2_data_entry_t *)ptr;
458
- length = dp->d_ops->data_entsize(dep->namelen);
459
- filetype = dp->d_ops->data_get_ftype(dep);
459
+ dep = bp->b_addr + offset;
460
+ length = xfs_dir2_data_entsize(mp, dep->namelen);
461
+ filetype = xfs_dir2_data_get_ftype(mp, dep);
460462
461463 ctx->pos = xfs_dir2_byte_to_dataptr(curoff) & 0x7fffffff;
464
+ if (XFS_IS_CORRUPT(dp->i_mount,
465
+ !xfs_dir2_namecheck(dep->name,
466
+ dep->namelen))) {
467
+ error = -EFSCORRUPTED;
468
+ break;
469
+ }
462470 if (!dir_emit(ctx, (char *)dep->name, dep->namelen,
463471 be64_to_cpu(dep->inumber),
464472 xfs_dir3_get_dtype(dp->i_mount, filetype)))
....@@ -467,7 +475,7 @@
467475 /*
468476 * Advance to next entry in the block.
469477 */
470
- ptr += length;
478
+ offset += length;
471479 curoff += length;
472480 /* bufsize may have just been a guess; don't go negative */
473481 bufsize = bufsize > length ? bufsize - length : 0;
....@@ -516,7 +524,7 @@
516524 args.geo = dp->i_mount->m_dir_geo;
517525 args.trans = tp;
518526
519
- if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
527
+ if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
520528 rval = xfs_dir2_sf_getdents(&args, ctx);
521529 else if ((rval = xfs_dir2_isblock(&args, &v)))
522530 ;