Commit 725bebb27882ae617d50776cc8b6cacd84481c91

Authored by Al Viro
1 parent 4deb398a1b

[readdir] convert ext4

and trim the living hell out bogosities in inline dir case

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

Showing 3 changed files with 134 additions and 190 deletions Side-by-side Diff

... ... @@ -29,8 +29,7 @@
29 29 #include "ext4.h"
30 30 #include "xattr.h"
31 31  
32   -static int ext4_dx_readdir(struct file *filp,
33   - void *dirent, filldir_t filldir);
  32 +static int ext4_dx_readdir(struct file *, struct dir_context *);
34 33  
35 34 /**
36 35 * Check if the given dir-inode refers to an htree-indexed directory
37 36  
38 37  
39 38  
40 39  
41 40  
42 41  
43 42  
44 43  
45 44  
46 45  
47 46  
48 47  
49 48  
... ... @@ -103,60 +102,56 @@
103 102 return 1;
104 103 }
105 104  
106   -static int ext4_readdir(struct file *filp,
107   - void *dirent, filldir_t filldir)
  105 +static int ext4_readdir(struct file *file, struct dir_context *ctx)
108 106 {
109   - int error = 0;
110 107 unsigned int offset;
111 108 int i, stored;
112 109 struct ext4_dir_entry_2 *de;
113 110 int err;
114   - struct inode *inode = file_inode(filp);
  111 + struct inode *inode = file_inode(file);
115 112 struct super_block *sb = inode->i_sb;
116   - int ret = 0;
117 113 int dir_has_error = 0;
118 114  
119 115 if (is_dx_dir(inode)) {
120   - err = ext4_dx_readdir(filp, dirent, filldir);
  116 + err = ext4_dx_readdir(file, ctx);
121 117 if (err != ERR_BAD_DX_DIR) {
122   - ret = err;
123   - goto out;
  118 + return err;
124 119 }
125 120 /*
126 121 * We don't set the inode dirty flag since it's not
127 122 * critical that it get flushed back to the disk.
128 123 */
129   - ext4_clear_inode_flag(file_inode(filp),
  124 + ext4_clear_inode_flag(file_inode(file),
130 125 EXT4_INODE_INDEX);
131 126 }
132 127  
133 128 if (ext4_has_inline_data(inode)) {
134 129 int has_inline_data = 1;
135   - ret = ext4_read_inline_dir(filp, dirent, filldir,
  130 + int ret = ext4_read_inline_dir(file, ctx,
136 131 &has_inline_data);
137 132 if (has_inline_data)
138 133 return ret;
139 134 }
140 135  
141 136 stored = 0;
142   - offset = filp->f_pos & (sb->s_blocksize - 1);
  137 + offset = ctx->pos & (sb->s_blocksize - 1);
143 138  
144   - while (!error && !stored && filp->f_pos < inode->i_size) {
  139 + while (ctx->pos < inode->i_size) {
145 140 struct ext4_map_blocks map;
146 141 struct buffer_head *bh = NULL;
147 142  
148   - map.m_lblk = filp->f_pos >> EXT4_BLOCK_SIZE_BITS(sb);
  143 + map.m_lblk = ctx->pos >> EXT4_BLOCK_SIZE_BITS(sb);
149 144 map.m_len = 1;
150 145 err = ext4_map_blocks(NULL, inode, &map, 0);
151 146 if (err > 0) {
152 147 pgoff_t index = map.m_pblk >>
153 148 (PAGE_CACHE_SHIFT - inode->i_blkbits);
154   - if (!ra_has_index(&filp->f_ra, index))
  149 + if (!ra_has_index(&file->f_ra, index))
155 150 page_cache_sync_readahead(
156 151 sb->s_bdev->bd_inode->i_mapping,
157   - &filp->f_ra, filp,
  152 + &file->f_ra, file,
158 153 index, 1);
159   - filp->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT;
  154 + file->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT;
160 155 bh = ext4_bread(NULL, inode, map.m_lblk, 0, &err);
161 156 }
162 157  
163 158  
164 159  
165 160  
... ... @@ -166,16 +161,16 @@
166 161 */
167 162 if (!bh) {
168 163 if (!dir_has_error) {
169   - EXT4_ERROR_FILE(filp, 0,
  164 + EXT4_ERROR_FILE(file, 0,
170 165 "directory contains a "
171 166 "hole at offset %llu",
172   - (unsigned long long) filp->f_pos);
  167 + (unsigned long long) ctx->pos);
173 168 dir_has_error = 1;
174 169 }
175 170 /* corrupt size? Maybe no more blocks to read */
176   - if (filp->f_pos > inode->i_blocks << 9)
  171 + if (ctx->pos > inode->i_blocks << 9)
177 172 break;
178   - filp->f_pos += sb->s_blocksize - offset;
  173 + ctx->pos += sb->s_blocksize - offset;
179 174 continue;
180 175 }
181 176  
182 177  
183 178  
184 179  
... ... @@ -183,21 +178,20 @@
183 178 if (!buffer_verified(bh) &&
184 179 !ext4_dirent_csum_verify(inode,
185 180 (struct ext4_dir_entry *)bh->b_data)) {
186   - EXT4_ERROR_FILE(filp, 0, "directory fails checksum "
  181 + EXT4_ERROR_FILE(file, 0, "directory fails checksum "
187 182 "at offset %llu",
188   - (unsigned long long)filp->f_pos);
189   - filp->f_pos += sb->s_blocksize - offset;
  183 + (unsigned long long)ctx->pos);
  184 + ctx->pos += sb->s_blocksize - offset;
190 185 brelse(bh);
191 186 continue;
192 187 }
193 188 set_buffer_verified(bh);
194 189  
195   -revalidate:
196 190 /* If the dir block has changed since the last call to
197 191 * readdir(2), then we might be pointing to an invalid
198 192 * dirent right now. Scan from the start of the block
199 193 * to make sure. */
200   - if (filp->f_version != inode->i_version) {
  194 + if (file->f_version != inode->i_version) {
201 195 for (i = 0; i < sb->s_blocksize && i < offset; ) {
202 196 de = (struct ext4_dir_entry_2 *)
203 197 (bh->b_data + i);
204 198  
205 199  
206 200  
207 201  
208 202  
209 203  
210 204  
211 205  
212 206  
213 207  
214 208  
215 209  
... ... @@ -214,57 +208,46 @@
214 208 sb->s_blocksize);
215 209 }
216 210 offset = i;
217   - filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
  211 + ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1))
218 212 | offset;
219   - filp->f_version = inode->i_version;
  213 + file->f_version = inode->i_version;
220 214 }
221 215  
222   - while (!error && filp->f_pos < inode->i_size
  216 + while (ctx->pos < inode->i_size
223 217 && offset < sb->s_blocksize) {
224 218 de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
225   - if (ext4_check_dir_entry(inode, filp, de, bh,
  219 + if (ext4_check_dir_entry(inode, file, de, bh,
226 220 bh->b_data, bh->b_size,
227 221 offset)) {
228 222 /*
229   - * On error, skip the f_pos to the next block
  223 + * On error, skip to the next block
230 224 */
231   - filp->f_pos = (filp->f_pos |
  225 + ctx->pos = (ctx->pos |
232 226 (sb->s_blocksize - 1)) + 1;
233   - brelse(bh);
234   - ret = stored;
235   - goto out;
  227 + break;
236 228 }
237 229 offset += ext4_rec_len_from_disk(de->rec_len,
238 230 sb->s_blocksize);
239 231 if (le32_to_cpu(de->inode)) {
240   - /* We might block in the next section
241   - * if the data destination is
242   - * currently swapped out. So, use a
243   - * version stamp to detect whether or
244   - * not the directory has been modified
245   - * during the copy operation.
246   - */
247   - u64 version = filp->f_version;
248   -
249   - error = filldir(dirent, de->name,
  232 + if (!dir_emit(ctx, de->name,
250 233 de->name_len,
251   - filp->f_pos,
252 234 le32_to_cpu(de->inode),
253   - get_dtype(sb, de->file_type));
254   - if (error)
255   - break;
256   - if (version != filp->f_version)
257   - goto revalidate;
258   - stored++;
  235 + get_dtype(sb, de->file_type))) {
  236 + brelse(bh);
  237 + return 0;
  238 + }
259 239 }
260   - filp->f_pos += ext4_rec_len_from_disk(de->rec_len,
  240 + ctx->pos += ext4_rec_len_from_disk(de->rec_len,
261 241 sb->s_blocksize);
262 242 }
263 243 offset = 0;
264 244 brelse(bh);
  245 + if (ctx->pos < inode->i_size) {
  246 + if (!dir_relax(inode))
  247 + return 0;
  248 + }
265 249 }
266   -out:
267   - return ret;
  250 + return 0;
268 251 }
269 252  
270 253 static inline int is_32bit_api(void)
271 254  
272 255  
273 256  
274 257  
275 258  
276 259  
277 260  
278 261  
279 262  
280 263  
281 264  
282 265  
283 266  
... ... @@ -492,64 +475,57 @@
492 475 * for all entres on the fname linked list. (Normally there is only
493 476 * one entry on the linked list, unless there are 62 bit hash collisions.)
494 477 */
495   -static int call_filldir(struct file *filp, void *dirent,
496   - filldir_t filldir, struct fname *fname)
  478 +static int call_filldir(struct file *file, struct dir_context *ctx,
  479 + struct fname *fname)
497 480 {
498   - struct dir_private_info *info = filp->private_data;
499   - loff_t curr_pos;
500   - struct inode *inode = file_inode(filp);
501   - struct super_block *sb;
502   - int error;
  481 + struct dir_private_info *info = file->private_data;
  482 + struct inode *inode = file_inode(file);
  483 + struct super_block *sb = inode->i_sb;
503 484  
504   - sb = inode->i_sb;
505   -
506 485 if (!fname) {
507 486 ext4_msg(sb, KERN_ERR, "%s:%d: inode #%lu: comm %s: "
508 487 "called with null fname?!?", __func__, __LINE__,
509 488 inode->i_ino, current->comm);
510 489 return 0;
511 490 }
512   - curr_pos = hash2pos(filp, fname->hash, fname->minor_hash);
  491 + ctx->pos = hash2pos(file, fname->hash, fname->minor_hash);
513 492 while (fname) {
514   - error = filldir(dirent, fname->name,
515   - fname->name_len, curr_pos,
  493 + if (!dir_emit(ctx, fname->name,
  494 + fname->name_len,
516 495 fname->inode,
517   - get_dtype(sb, fname->file_type));
518   - if (error) {
519   - filp->f_pos = curr_pos;
  496 + get_dtype(sb, fname->file_type))) {
520 497 info->extra_fname = fname;
521   - return error;
  498 + return 1;
522 499 }
523 500 fname = fname->next;
524 501 }
525 502 return 0;
526 503 }
527 504  
528   -static int ext4_dx_readdir(struct file *filp,
529   - void *dirent, filldir_t filldir)
  505 +static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
530 506 {
531   - struct dir_private_info *info = filp->private_data;
532   - struct inode *inode = file_inode(filp);
  507 + struct dir_private_info *info = file->private_data;
  508 + struct inode *inode = file_inode(file);
533 509 struct fname *fname;
534 510 int ret;
535 511  
536 512 if (!info) {
537   - info = ext4_htree_create_dir_info(filp, filp->f_pos);
  513 + info = ext4_htree_create_dir_info(file, ctx->pos);
538 514 if (!info)
539 515 return -ENOMEM;
540   - filp->private_data = info;
  516 + file->private_data = info;
541 517 }
542 518  
543   - if (filp->f_pos == ext4_get_htree_eof(filp))
  519 + if (ctx->pos == ext4_get_htree_eof(file))
544 520 return 0; /* EOF */
545 521  
546 522 /* Some one has messed with f_pos; reset the world */
547   - if (info->last_pos != filp->f_pos) {
  523 + if (info->last_pos != ctx->pos) {
548 524 free_rb_tree_fname(&info->root);
549 525 info->curr_node = NULL;
550 526 info->extra_fname = NULL;
551   - info->curr_hash = pos2maj_hash(filp, filp->f_pos);
552   - info->curr_minor_hash = pos2min_hash(filp, filp->f_pos);
  527 + info->curr_hash = pos2maj_hash(file, ctx->pos);
  528 + info->curr_minor_hash = pos2min_hash(file, ctx->pos);
553 529 }
554 530  
555 531 /*
... ... @@ -557,7 +533,7 @@
557 533 * chain, return them first.
558 534 */
559 535 if (info->extra_fname) {
560   - if (call_filldir(filp, dirent, filldir, info->extra_fname))
  536 + if (call_filldir(file, ctx, info->extra_fname))
561 537 goto finished;
562 538 info->extra_fname = NULL;
563 539 goto next_node;
564 540  
565 541  
... ... @@ -571,17 +547,17 @@
571 547 * cached entries.
572 548 */
573 549 if ((!info->curr_node) ||
574   - (filp->f_version != inode->i_version)) {
  550 + (file->f_version != inode->i_version)) {
575 551 info->curr_node = NULL;
576 552 free_rb_tree_fname(&info->root);
577   - filp->f_version = inode->i_version;
578   - ret = ext4_htree_fill_tree(filp, info->curr_hash,
  553 + file->f_version = inode->i_version;
  554 + ret = ext4_htree_fill_tree(file, info->curr_hash,
579 555 info->curr_minor_hash,
580 556 &info->next_hash);
581 557 if (ret < 0)
582 558 return ret;
583 559 if (ret == 0) {
584   - filp->f_pos = ext4_get_htree_eof(filp);
  560 + ctx->pos = ext4_get_htree_eof(file);
585 561 break;
586 562 }
587 563 info->curr_node = rb_first(&info->root);
... ... @@ -590,7 +566,7 @@
590 566 fname = rb_entry(info->curr_node, struct fname, rb_hash);
591 567 info->curr_hash = fname->hash;
592 568 info->curr_minor_hash = fname->minor_hash;
593   - if (call_filldir(filp, dirent, filldir, fname))
  569 + if (call_filldir(file, ctx, fname))
594 570 break;
595 571 next_node:
596 572 info->curr_node = rb_next(info->curr_node);
... ... @@ -601,7 +577,7 @@
601 577 info->curr_minor_hash = fname->minor_hash;
602 578 } else {
603 579 if (info->next_hash == ~0) {
604   - filp->f_pos = ext4_get_htree_eof(filp);
  580 + ctx->pos = ext4_get_htree_eof(file);
605 581 break;
606 582 }
607 583 info->curr_hash = info->next_hash;
... ... @@ -609,7 +585,7 @@
609 585 }
610 586 }
611 587 finished:
612   - info->last_pos = filp->f_pos;
  588 + info->last_pos = ctx->pos;
613 589 return 0;
614 590 }
615 591  
... ... @@ -624,7 +600,7 @@
624 600 const struct file_operations ext4_dir_operations = {
625 601 .llseek = ext4_dir_llseek,
626 602 .read = generic_read_dir,
627   - .readdir = ext4_readdir,
  603 + .iterate = ext4_readdir,
628 604 .unlocked_ioctl = ext4_ioctl,
629 605 #ifdef CONFIG_COMPAT
630 606 .compat_ioctl = ext4_compat_ioctl,
... ... @@ -2515,7 +2515,7 @@
2515 2515 struct inode *parent,
2516 2516 struct inode *inode);
2517 2517 extern int ext4_read_inline_dir(struct file *filp,
2518   - void *dirent, filldir_t filldir,
  2518 + struct dir_context *ctx,
2519 2519 int *has_inline_data);
2520 2520 extern int htree_inlinedir_to_tree(struct file *dir_file,
2521 2521 struct inode *dir, ext4_lblk_t block,
... ... @@ -1404,16 +1404,15 @@
1404 1404 * offset as if '.' and '..' really take place.
1405 1405 *
1406 1406 */
1407   -int ext4_read_inline_dir(struct file *filp,
1408   - void *dirent, filldir_t filldir,
  1407 +int ext4_read_inline_dir(struct file *file,
  1408 + struct dir_context *ctx,
1409 1409 int *has_inline_data)
1410 1410 {
1411   - int error = 0;
1412 1411 unsigned int offset, parent_ino;
1413   - int i, stored;
  1412 + int i;
1414 1413 struct ext4_dir_entry_2 *de;
1415 1414 struct super_block *sb;
1416   - struct inode *inode = file_inode(filp);
  1415 + struct inode *inode = file_inode(file);
1417 1416 int ret, inline_size = 0;
1418 1417 struct ext4_iloc iloc;
1419 1418 void *dir_buf = NULL;
1420 1419  
... ... @@ -1444,9 +1443,8 @@
1444 1443 goto out;
1445 1444  
1446 1445 sb = inode->i_sb;
1447   - stored = 0;
1448 1446 parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode);
1449   - offset = filp->f_pos;
  1447 + offset = ctx->pos;
1450 1448  
1451 1449 /*
1452 1450 * dotdot_offset and dotdot_size is the real offset and
1453 1451  
1454 1452  
1455 1453  
1456 1454  
1457 1455  
1458 1456  
... ... @@ -1460,104 +1458,74 @@
1460 1458 extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE;
1461 1459 extra_size = extra_offset + inline_size;
1462 1460  
1463   - while (!error && !stored && filp->f_pos < extra_size) {
1464   -revalidate:
1465   - /*
1466   - * If the version has changed since the last call to
1467   - * readdir(2), then we might be pointing to an invalid
1468   - * dirent right now. Scan from the start of the inline
1469   - * dir to make sure.
1470   - */
1471   - if (filp->f_version != inode->i_version) {
1472   - for (i = 0; i < extra_size && i < offset;) {
1473   - /*
1474   - * "." is with offset 0 and
1475   - * ".." is dotdot_offset.
1476   - */
1477   - if (!i) {
1478   - i = dotdot_offset;
1479   - continue;
1480   - } else if (i == dotdot_offset) {
1481   - i = dotdot_size;
1482   - continue;
1483   - }
1484   - /* for other entry, the real offset in
1485   - * the buf has to be tuned accordingly.
1486   - */
1487   - de = (struct ext4_dir_entry_2 *)
1488   - (dir_buf + i - extra_offset);
1489   - /* It's too expensive to do a full
1490   - * dirent test each time round this
1491   - * loop, but we do have to test at
1492   - * least that it is non-zero. A
1493   - * failure will be detected in the
1494   - * dirent test below. */
1495   - if (ext4_rec_len_from_disk(de->rec_len,
1496   - extra_size) < EXT4_DIR_REC_LEN(1))
1497   - break;
1498   - i += ext4_rec_len_from_disk(de->rec_len,
1499   - extra_size);
1500   - }
1501   - offset = i;
1502   - filp->f_pos = offset;
1503   - filp->f_version = inode->i_version;
1504   - }
1505   -
1506   - while (!error && filp->f_pos < extra_size) {
1507   - if (filp->f_pos == 0) {
1508   - error = filldir(dirent, ".", 1, 0, inode->i_ino,
1509   - DT_DIR);
1510   - if (error)
1511   - break;
1512   - stored++;
1513   - filp->f_pos = dotdot_offset;
  1461 + /*
  1462 + * If the version has changed since the last call to
  1463 + * readdir(2), then we might be pointing to an invalid
  1464 + * dirent right now. Scan from the start of the inline
  1465 + * dir to make sure.
  1466 + */
  1467 + if (file->f_version != inode->i_version) {
  1468 + for (i = 0; i < extra_size && i < offset;) {
  1469 + /*
  1470 + * "." is with offset 0 and
  1471 + * ".." is dotdot_offset.
  1472 + */
  1473 + if (!i) {
  1474 + i = dotdot_offset;
1514 1475 continue;
1515   - }
1516   -
1517   - if (filp->f_pos == dotdot_offset) {
1518   - error = filldir(dirent, "..", 2,
1519   - dotdot_offset,
1520   - parent_ino, DT_DIR);
1521   - if (error)
1522   - break;
1523   - stored++;
1524   -
1525   - filp->f_pos = dotdot_size;
  1476 + } else if (i == dotdot_offset) {
  1477 + i = dotdot_size;
1526 1478 continue;
1527 1479 }
1528   -
  1480 + /* for other entry, the real offset in
  1481 + * the buf has to be tuned accordingly.
  1482 + */
1529 1483 de = (struct ext4_dir_entry_2 *)
1530   - (dir_buf + filp->f_pos - extra_offset);
1531   - if (ext4_check_dir_entry(inode, filp, de,
1532   - iloc.bh, dir_buf,
1533   - extra_size, filp->f_pos)) {
1534   - ret = stored;
  1484 + (dir_buf + i - extra_offset);
  1485 + /* It's too expensive to do a full
  1486 + * dirent test each time round this
  1487 + * loop, but we do have to test at
  1488 + * least that it is non-zero. A
  1489 + * failure will be detected in the
  1490 + * dirent test below. */
  1491 + if (ext4_rec_len_from_disk(de->rec_len, extra_size)
  1492 + < EXT4_DIR_REC_LEN(1))
  1493 + break;
  1494 + i += ext4_rec_len_from_disk(de->rec_len,
  1495 + extra_size);
  1496 + }
  1497 + offset = i;
  1498 + ctx->pos = offset;
  1499 + file->f_version = inode->i_version;
  1500 + }
  1501 +
  1502 + while (ctx->pos < extra_size) {
  1503 + if (ctx->pos == 0) {
  1504 + if (!dir_emit(ctx, ".", 1, inode->i_ino, DT_DIR))
1535 1505 goto out;
1536   - }
1537   - if (le32_to_cpu(de->inode)) {
1538   - /* We might block in the next section
1539   - * if the data destination is
1540   - * currently swapped out. So, use a
1541   - * version stamp to detect whether or
1542   - * not the directory has been modified
1543   - * during the copy operation.
1544   - */
1545   - u64 version = filp->f_version;
  1506 + ctx->pos = dotdot_offset;
  1507 + continue;
  1508 + }
1546 1509  
1547   - error = filldir(dirent, de->name,
1548   - de->name_len,
1549   - filp->f_pos,
1550   - le32_to_cpu(de->inode),
1551   - get_dtype(sb, de->file_type));
1552   - if (error)
1553   - break;
1554   - if (version != filp->f_version)
1555   - goto revalidate;
1556   - stored++;
1557   - }
1558   - filp->f_pos += ext4_rec_len_from_disk(de->rec_len,
1559   - extra_size);
  1510 + if (ctx->pos == dotdot_offset) {
  1511 + if (!dir_emit(ctx, "..", 2, parent_ino, DT_DIR))
  1512 + goto out;
  1513 + ctx->pos = dotdot_size;
  1514 + continue;
1560 1515 }
  1516 +
  1517 + de = (struct ext4_dir_entry_2 *)
  1518 + (dir_buf + ctx->pos - extra_offset);
  1519 + if (ext4_check_dir_entry(inode, file, de, iloc.bh, dir_buf,
  1520 + extra_size, ctx->pos))
  1521 + goto out;
  1522 + if (le32_to_cpu(de->inode)) {
  1523 + if (!dir_emit(ctx, de->name, de->name_len,
  1524 + le32_to_cpu(de->inode),
  1525 + get_dtype(sb, de->file_type)))
  1526 + goto out;
  1527 + }
  1528 + ctx->pos += ext4_rec_len_from_disk(de->rec_len, extra_size);
1561 1529 }
1562 1530 out:
1563 1531 kfree(dir_buf);