Commit 725bebb27882ae617d50776cc8b6cacd84481c91
1 parent
4deb398a1b
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
[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
fs/ext4/dir.c
... | ... | @@ -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, |
fs/ext4/ext4.h
... | ... | @@ -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, |
fs/ext4/inline.c
... | ... | @@ -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); |