diff options
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/dir.c | 158 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/inline.c | 164 |
3 files changed, 134 insertions, 190 deletions
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index f8d56e4254e0..3c7d288ae94c 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c | |||
@@ -29,8 +29,7 @@ | |||
29 | #include "ext4.h" | 29 | #include "ext4.h" |
30 | #include "xattr.h" | 30 | #include "xattr.h" |
31 | 31 | ||
32 | static int ext4_dx_readdir(struct file *filp, | 32 | static int ext4_dx_readdir(struct file *, struct dir_context *); |
33 | void *dirent, filldir_t filldir); | ||
34 | 33 | ||
35 | /** | 34 | /** |
36 | * Check if the given dir-inode refers to an htree-indexed directory | 35 | * Check if the given dir-inode refers to an htree-indexed directory |
@@ -103,60 +102,56 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, | |||
103 | return 1; | 102 | return 1; |
104 | } | 103 | } |
105 | 104 | ||
106 | static int ext4_readdir(struct file *filp, | 105 | static int ext4_readdir(struct file *file, struct dir_context *ctx) |
107 | void *dirent, filldir_t filldir) | ||
108 | { | 106 | { |
109 | int error = 0; | ||
110 | unsigned int offset; | 107 | unsigned int offset; |
111 | int i, stored; | 108 | int i, stored; |
112 | struct ext4_dir_entry_2 *de; | 109 | struct ext4_dir_entry_2 *de; |
113 | int err; | 110 | int err; |
114 | struct inode *inode = file_inode(filp); | 111 | struct inode *inode = file_inode(file); |
115 | struct super_block *sb = inode->i_sb; | 112 | struct super_block *sb = inode->i_sb; |
116 | int ret = 0; | ||
117 | int dir_has_error = 0; | 113 | int dir_has_error = 0; |
118 | 114 | ||
119 | if (is_dx_dir(inode)) { | 115 | if (is_dx_dir(inode)) { |
120 | err = ext4_dx_readdir(filp, dirent, filldir); | 116 | err = ext4_dx_readdir(file, ctx); |
121 | if (err != ERR_BAD_DX_DIR) { | 117 | if (err != ERR_BAD_DX_DIR) { |
122 | ret = err; | 118 | return err; |
123 | goto out; | ||
124 | } | 119 | } |
125 | /* | 120 | /* |
126 | * We don't set the inode dirty flag since it's not | 121 | * We don't set the inode dirty flag since it's not |
127 | * critical that it get flushed back to the disk. | 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 | EXT4_INODE_INDEX); | 125 | EXT4_INODE_INDEX); |
131 | } | 126 | } |
132 | 127 | ||
133 | if (ext4_has_inline_data(inode)) { | 128 | if (ext4_has_inline_data(inode)) { |
134 | int has_inline_data = 1; | 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 | &has_inline_data); | 131 | &has_inline_data); |
137 | if (has_inline_data) | 132 | if (has_inline_data) |
138 | return ret; | 133 | return ret; |
139 | } | 134 | } |
140 | 135 | ||
141 | stored = 0; | 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 | struct ext4_map_blocks map; | 140 | struct ext4_map_blocks map; |
146 | struct buffer_head *bh = NULL; | 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 | map.m_len = 1; | 144 | map.m_len = 1; |
150 | err = ext4_map_blocks(NULL, inode, &map, 0); | 145 | err = ext4_map_blocks(NULL, inode, &map, 0); |
151 | if (err > 0) { | 146 | if (err > 0) { |
152 | pgoff_t index = map.m_pblk >> | 147 | pgoff_t index = map.m_pblk >> |
153 | (PAGE_CACHE_SHIFT - inode->i_blkbits); | 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 | page_cache_sync_readahead( | 150 | page_cache_sync_readahead( |
156 | sb->s_bdev->bd_inode->i_mapping, | 151 | sb->s_bdev->bd_inode->i_mapping, |
157 | &filp->f_ra, filp, | 152 | &file->f_ra, file, |
158 | index, 1); | 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 | bh = ext4_bread(NULL, inode, map.m_lblk, 0, &err); | 155 | bh = ext4_bread(NULL, inode, map.m_lblk, 0, &err); |
161 | } | 156 | } |
162 | 157 | ||
@@ -166,16 +161,16 @@ static int ext4_readdir(struct file *filp, | |||
166 | */ | 161 | */ |
167 | if (!bh) { | 162 | if (!bh) { |
168 | if (!dir_has_error) { | 163 | if (!dir_has_error) { |
169 | EXT4_ERROR_FILE(filp, 0, | 164 | EXT4_ERROR_FILE(file, 0, |
170 | "directory contains a " | 165 | "directory contains a " |
171 | "hole at offset %llu", | 166 | "hole at offset %llu", |
172 | (unsigned long long) filp->f_pos); | 167 | (unsigned long long) ctx->pos); |
173 | dir_has_error = 1; | 168 | dir_has_error = 1; |
174 | } | 169 | } |
175 | /* corrupt size? Maybe no more blocks to read */ | 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 | break; | 172 | break; |
178 | filp->f_pos += sb->s_blocksize - offset; | 173 | ctx->pos += sb->s_blocksize - offset; |
179 | continue; | 174 | continue; |
180 | } | 175 | } |
181 | 176 | ||
@@ -183,21 +178,20 @@ static int ext4_readdir(struct file *filp, | |||
183 | if (!buffer_verified(bh) && | 178 | if (!buffer_verified(bh) && |
184 | !ext4_dirent_csum_verify(inode, | 179 | !ext4_dirent_csum_verify(inode, |
185 | (struct ext4_dir_entry *)bh->b_data)) { | 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 | "at offset %llu", | 182 | "at offset %llu", |
188 | (unsigned long long)filp->f_pos); | 183 | (unsigned long long)ctx->pos); |
189 | filp->f_pos += sb->s_blocksize - offset; | 184 | ctx->pos += sb->s_blocksize - offset; |
190 | brelse(bh); | 185 | brelse(bh); |
191 | continue; | 186 | continue; |
192 | } | 187 | } |
193 | set_buffer_verified(bh); | 188 | set_buffer_verified(bh); |
194 | 189 | ||
195 | revalidate: | ||
196 | /* If the dir block has changed since the last call to | 190 | /* If the dir block has changed since the last call to |
197 | * readdir(2), then we might be pointing to an invalid | 191 | * readdir(2), then we might be pointing to an invalid |
198 | * dirent right now. Scan from the start of the block | 192 | * dirent right now. Scan from the start of the block |
199 | * to make sure. */ | 193 | * to make sure. */ |
200 | if (filp->f_version != inode->i_version) { | 194 | if (file->f_version != inode->i_version) { |
201 | for (i = 0; i < sb->s_blocksize && i < offset; ) { | 195 | for (i = 0; i < sb->s_blocksize && i < offset; ) { |
202 | de = (struct ext4_dir_entry_2 *) | 196 | de = (struct ext4_dir_entry_2 *) |
203 | (bh->b_data + i); | 197 | (bh->b_data + i); |
@@ -214,57 +208,46 @@ revalidate: | |||
214 | sb->s_blocksize); | 208 | sb->s_blocksize); |
215 | } | 209 | } |
216 | offset = i; | 210 | offset = i; |
217 | filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | 211 | ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1)) |
218 | | offset; | 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 | && offset < sb->s_blocksize) { | 217 | && offset < sb->s_blocksize) { |
224 | de = (struct ext4_dir_entry_2 *) (bh->b_data + offset); | 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 | bh->b_data, bh->b_size, | 220 | bh->b_data, bh->b_size, |
227 | offset)) { | 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 | (sb->s_blocksize - 1)) + 1; | 226 | (sb->s_blocksize - 1)) + 1; |
233 | brelse(bh); | 227 | break; |
234 | ret = stored; | ||
235 | goto out; | ||
236 | } | 228 | } |
237 | offset += ext4_rec_len_from_disk(de->rec_len, | 229 | offset += ext4_rec_len_from_disk(de->rec_len, |
238 | sb->s_blocksize); | 230 | sb->s_blocksize); |
239 | if (le32_to_cpu(de->inode)) { | 231 | if (le32_to_cpu(de->inode)) { |
240 | /* We might block in the next section | 232 | if (!dir_emit(ctx, de->name, |
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, | ||
250 | de->name_len, | 233 | de->name_len, |
251 | filp->f_pos, | ||
252 | le32_to_cpu(de->inode), | 234 | le32_to_cpu(de->inode), |
253 | get_dtype(sb, de->file_type)); | 235 | get_dtype(sb, de->file_type))) { |
254 | if (error) | 236 | brelse(bh); |
255 | break; | 237 | return 0; |
256 | if (version != filp->f_version) | 238 | } |
257 | goto revalidate; | ||
258 | stored++; | ||
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 | sb->s_blocksize); | 241 | sb->s_blocksize); |
262 | } | 242 | } |
263 | offset = 0; | 243 | offset = 0; |
264 | brelse(bh); | 244 | brelse(bh); |
245 | if (ctx->pos < inode->i_size) { | ||
246 | if (!dir_relax(inode)) | ||
247 | return 0; | ||
248 | } | ||
265 | } | 249 | } |
266 | out: | 250 | return 0; |
267 | return ret; | ||
268 | } | 251 | } |
269 | 252 | ||
270 | static inline int is_32bit_api(void) | 253 | static inline int is_32bit_api(void) |
@@ -492,16 +475,12 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, | |||
492 | * for all entres on the fname linked list. (Normally there is only | 475 | * for all entres on the fname linked list. (Normally there is only |
493 | * one entry on the linked list, unless there are 62 bit hash collisions.) | 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, | 478 | static int call_filldir(struct file *file, struct dir_context *ctx, |
496 | filldir_t filldir, struct fname *fname) | 479 | struct fname *fname) |
497 | { | 480 | { |
498 | struct dir_private_info *info = filp->private_data; | 481 | struct dir_private_info *info = file->private_data; |
499 | loff_t curr_pos; | 482 | struct inode *inode = file_inode(file); |
500 | struct inode *inode = file_inode(filp); | 483 | struct super_block *sb = inode->i_sb; |
501 | struct super_block *sb; | ||
502 | int error; | ||
503 | |||
504 | sb = inode->i_sb; | ||
505 | 484 | ||
506 | if (!fname) { | 485 | if (!fname) { |
507 | ext4_msg(sb, KERN_ERR, "%s:%d: inode #%lu: comm %s: " | 486 | ext4_msg(sb, KERN_ERR, "%s:%d: inode #%lu: comm %s: " |
@@ -509,47 +488,44 @@ static int call_filldir(struct file *filp, void *dirent, | |||
509 | inode->i_ino, current->comm); | 488 | inode->i_ino, current->comm); |
510 | return 0; | 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 | while (fname) { | 492 | while (fname) { |
514 | error = filldir(dirent, fname->name, | 493 | if (!dir_emit(ctx, fname->name, |
515 | fname->name_len, curr_pos, | 494 | fname->name_len, |
516 | fname->inode, | 495 | fname->inode, |
517 | get_dtype(sb, fname->file_type)); | 496 | get_dtype(sb, fname->file_type))) { |
518 | if (error) { | ||
519 | filp->f_pos = curr_pos; | ||
520 | info->extra_fname = fname; | 497 | info->extra_fname = fname; |
521 | return error; | 498 | return 1; |
522 | } | 499 | } |
523 | fname = fname->next; | 500 | fname = fname->next; |
524 | } | 501 | } |
525 | return 0; | 502 | return 0; |
526 | } | 503 | } |
527 | 504 | ||
528 | static int ext4_dx_readdir(struct file *filp, | 505 | static int ext4_dx_readdir(struct file *file, struct dir_context *ctx) |
529 | void *dirent, filldir_t filldir) | ||
530 | { | 506 | { |
531 | struct dir_private_info *info = filp->private_data; | 507 | struct dir_private_info *info = file->private_data; |
532 | struct inode *inode = file_inode(filp); | 508 | struct inode *inode = file_inode(file); |
533 | struct fname *fname; | 509 | struct fname *fname; |
534 | int ret; | 510 | int ret; |
535 | 511 | ||
536 | if (!info) { | 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 | if (!info) | 514 | if (!info) |
539 | return -ENOMEM; | 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 | return 0; /* EOF */ | 520 | return 0; /* EOF */ |
545 | 521 | ||
546 | /* Some one has messed with f_pos; reset the world */ | 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 | free_rb_tree_fname(&info->root); | 524 | free_rb_tree_fname(&info->root); |
549 | info->curr_node = NULL; | 525 | info->curr_node = NULL; |
550 | info->extra_fname = NULL; | 526 | info->extra_fname = NULL; |
551 | info->curr_hash = pos2maj_hash(filp, filp->f_pos); | 527 | info->curr_hash = pos2maj_hash(file, ctx->pos); |
552 | info->curr_minor_hash = pos2min_hash(filp, filp->f_pos); | 528 | info->curr_minor_hash = pos2min_hash(file, ctx->pos); |
553 | } | 529 | } |
554 | 530 | ||
555 | /* | 531 | /* |
@@ -557,7 +533,7 @@ static int ext4_dx_readdir(struct file *filp, | |||
557 | * chain, return them first. | 533 | * chain, return them first. |
558 | */ | 534 | */ |
559 | if (info->extra_fname) { | 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 | goto finished; | 537 | goto finished; |
562 | info->extra_fname = NULL; | 538 | info->extra_fname = NULL; |
563 | goto next_node; | 539 | goto next_node; |
@@ -571,17 +547,17 @@ static int ext4_dx_readdir(struct file *filp, | |||
571 | * cached entries. | 547 | * cached entries. |
572 | */ | 548 | */ |
573 | if ((!info->curr_node) || | 549 | if ((!info->curr_node) || |
574 | (filp->f_version != inode->i_version)) { | 550 | (file->f_version != inode->i_version)) { |
575 | info->curr_node = NULL; | 551 | info->curr_node = NULL; |
576 | free_rb_tree_fname(&info->root); | 552 | free_rb_tree_fname(&info->root); |
577 | filp->f_version = inode->i_version; | 553 | file->f_version = inode->i_version; |
578 | ret = ext4_htree_fill_tree(filp, info->curr_hash, | 554 | ret = ext4_htree_fill_tree(file, info->curr_hash, |
579 | info->curr_minor_hash, | 555 | info->curr_minor_hash, |
580 | &info->next_hash); | 556 | &info->next_hash); |
581 | if (ret < 0) | 557 | if (ret < 0) |
582 | return ret; | 558 | return ret; |
583 | if (ret == 0) { | 559 | if (ret == 0) { |
584 | filp->f_pos = ext4_get_htree_eof(filp); | 560 | ctx->pos = ext4_get_htree_eof(file); |
585 | break; | 561 | break; |
586 | } | 562 | } |
587 | info->curr_node = rb_first(&info->root); | 563 | info->curr_node = rb_first(&info->root); |
@@ -590,7 +566,7 @@ static int ext4_dx_readdir(struct file *filp, | |||
590 | fname = rb_entry(info->curr_node, struct fname, rb_hash); | 566 | fname = rb_entry(info->curr_node, struct fname, rb_hash); |
591 | info->curr_hash = fname->hash; | 567 | info->curr_hash = fname->hash; |
592 | info->curr_minor_hash = fname->minor_hash; | 568 | info->curr_minor_hash = fname->minor_hash; |
593 | if (call_filldir(filp, dirent, filldir, fname)) | 569 | if (call_filldir(file, ctx, fname)) |
594 | break; | 570 | break; |
595 | next_node: | 571 | next_node: |
596 | info->curr_node = rb_next(info->curr_node); | 572 | info->curr_node = rb_next(info->curr_node); |
@@ -601,7 +577,7 @@ static int ext4_dx_readdir(struct file *filp, | |||
601 | info->curr_minor_hash = fname->minor_hash; | 577 | info->curr_minor_hash = fname->minor_hash; |
602 | } else { | 578 | } else { |
603 | if (info->next_hash == ~0) { | 579 | if (info->next_hash == ~0) { |
604 | filp->f_pos = ext4_get_htree_eof(filp); | 580 | ctx->pos = ext4_get_htree_eof(file); |
605 | break; | 581 | break; |
606 | } | 582 | } |
607 | info->curr_hash = info->next_hash; | 583 | info->curr_hash = info->next_hash; |
@@ -609,7 +585,7 @@ static int ext4_dx_readdir(struct file *filp, | |||
609 | } | 585 | } |
610 | } | 586 | } |
611 | finished: | 587 | finished: |
612 | info->last_pos = filp->f_pos; | 588 | info->last_pos = ctx->pos; |
613 | return 0; | 589 | return 0; |
614 | } | 590 | } |
615 | 591 | ||
@@ -624,7 +600,7 @@ static int ext4_release_dir(struct inode *inode, struct file *filp) | |||
624 | const struct file_operations ext4_dir_operations = { | 600 | const struct file_operations ext4_dir_operations = { |
625 | .llseek = ext4_dir_llseek, | 601 | .llseek = ext4_dir_llseek, |
626 | .read = generic_read_dir, | 602 | .read = generic_read_dir, |
627 | .readdir = ext4_readdir, | 603 | .iterate = ext4_readdir, |
628 | .unlocked_ioctl = ext4_ioctl, | 604 | .unlocked_ioctl = ext4_ioctl, |
629 | #ifdef CONFIG_COMPAT | 605 | #ifdef CONFIG_COMPAT |
630 | .compat_ioctl = ext4_compat_ioctl, | 606 | .compat_ioctl = ext4_compat_ioctl, |
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5aae3d12d400..4af03ea84aa3 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2515,7 +2515,7 @@ extern int ext4_try_create_inline_dir(handle_t *handle, | |||
2515 | struct inode *parent, | 2515 | struct inode *parent, |
2516 | struct inode *inode); | 2516 | struct inode *inode); |
2517 | extern int ext4_read_inline_dir(struct file *filp, | 2517 | extern int ext4_read_inline_dir(struct file *filp, |
2518 | void *dirent, filldir_t filldir, | 2518 | struct dir_context *ctx, |
2519 | int *has_inline_data); | 2519 | int *has_inline_data); |
2520 | extern int htree_inlinedir_to_tree(struct file *dir_file, | 2520 | extern int htree_inlinedir_to_tree(struct file *dir_file, |
2521 | struct inode *dir, ext4_lblk_t block, | 2521 | struct inode *dir, ext4_lblk_t block, |
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 3e2bf873e8a8..1a346a6bdc8f 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c | |||
@@ -1404,16 +1404,15 @@ out: | |||
1404 | * offset as if '.' and '..' really take place. | 1404 | * offset as if '.' and '..' really take place. |
1405 | * | 1405 | * |
1406 | */ | 1406 | */ |
1407 | int ext4_read_inline_dir(struct file *filp, | 1407 | int ext4_read_inline_dir(struct file *file, |
1408 | void *dirent, filldir_t filldir, | 1408 | struct dir_context *ctx, |
1409 | int *has_inline_data) | 1409 | int *has_inline_data) |
1410 | { | 1410 | { |
1411 | int error = 0; | ||
1412 | unsigned int offset, parent_ino; | 1411 | unsigned int offset, parent_ino; |
1413 | int i, stored; | 1412 | int i; |
1414 | struct ext4_dir_entry_2 *de; | 1413 | struct ext4_dir_entry_2 *de; |
1415 | struct super_block *sb; | 1414 | struct super_block *sb; |
1416 | struct inode *inode = file_inode(filp); | 1415 | struct inode *inode = file_inode(file); |
1417 | int ret, inline_size = 0; | 1416 | int ret, inline_size = 0; |
1418 | struct ext4_iloc iloc; | 1417 | struct ext4_iloc iloc; |
1419 | void *dir_buf = NULL; | 1418 | void *dir_buf = NULL; |
@@ -1444,9 +1443,8 @@ int ext4_read_inline_dir(struct file *filp, | |||
1444 | goto out; | 1443 | goto out; |
1445 | 1444 | ||
1446 | sb = inode->i_sb; | 1445 | sb = inode->i_sb; |
1447 | stored = 0; | ||
1448 | parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode); | 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 | * dotdot_offset and dotdot_size is the real offset and | 1450 | * dotdot_offset and dotdot_size is the real offset and |
@@ -1460,104 +1458,74 @@ int ext4_read_inline_dir(struct file *filp, | |||
1460 | extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE; | 1458 | extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE; |
1461 | extra_size = extra_offset + inline_size; | 1459 | extra_size = extra_offset + inline_size; |
1462 | 1460 | ||
1463 | while (!error && !stored && filp->f_pos < extra_size) { | 1461 | /* |
1464 | revalidate: | 1462 | * If the version has changed since the last call to |
1465 | /* | 1463 | * readdir(2), then we might be pointing to an invalid |
1466 | * If the version has changed since the last call to | 1464 | * dirent right now. Scan from the start of the inline |
1467 | * readdir(2), then we might be pointing to an invalid | 1465 | * dir to make sure. |
1468 | * dirent right now. Scan from the start of the inline | 1466 | */ |
1469 | * dir to make sure. | 1467 | if (file->f_version != inode->i_version) { |
1470 | */ | 1468 | for (i = 0; i < extra_size && i < offset;) { |
1471 | if (filp->f_version != inode->i_version) { | 1469 | /* |
1472 | for (i = 0; i < extra_size && i < offset;) { | 1470 | * "." is with offset 0 and |
1473 | /* | 1471 | * ".." is dotdot_offset. |
1474 | * "." is with offset 0 and | 1472 | */ |
1475 | * ".." is dotdot_offset. | 1473 | if (!i) { |
1476 | */ | 1474 | i = dotdot_offset; |
1477 | if (!i) { | 1475 | continue; |
1478 | i = dotdot_offset; | 1476 | } else if (i == dotdot_offset) { |
1479 | continue; | 1477 | i = dotdot_size; |
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; | ||
1514 | continue; | 1478 | continue; |
1515 | } | 1479 | } |
1480 | /* for other entry, the real offset in | ||
1481 | * the buf has to be tuned accordingly. | ||
1482 | */ | ||
1483 | de = (struct ext4_dir_entry_2 *) | ||
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 | } | ||
1516 | 1501 | ||
1517 | if (filp->f_pos == dotdot_offset) { | 1502 | while (ctx->pos < extra_size) { |
1518 | error = filldir(dirent, "..", 2, | 1503 | if (ctx->pos == 0) { |
1519 | dotdot_offset, | 1504 | if (!dir_emit(ctx, ".", 1, inode->i_ino, DT_DIR)) |
1520 | parent_ino, DT_DIR); | 1505 | goto out; |
1521 | if (error) | 1506 | ctx->pos = dotdot_offset; |
1522 | break; | 1507 | continue; |
1523 | stored++; | 1508 | } |
1524 | 1509 | ||
1525 | filp->f_pos = dotdot_size; | 1510 | if (ctx->pos == dotdot_offset) { |
1526 | continue; | 1511 | if (!dir_emit(ctx, "..", 2, parent_ino, DT_DIR)) |
1527 | } | 1512 | goto out; |
1513 | ctx->pos = dotdot_size; | ||
1514 | continue; | ||
1515 | } | ||
1528 | 1516 | ||
1529 | de = (struct ext4_dir_entry_2 *) | 1517 | de = (struct ext4_dir_entry_2 *) |
1530 | (dir_buf + filp->f_pos - extra_offset); | 1518 | (dir_buf + ctx->pos - extra_offset); |
1531 | if (ext4_check_dir_entry(inode, filp, de, | 1519 | if (ext4_check_dir_entry(inode, file, de, iloc.bh, dir_buf, |
1532 | iloc.bh, dir_buf, | 1520 | extra_size, ctx->pos)) |
1533 | extra_size, filp->f_pos)) { | 1521 | goto out; |
1534 | ret = stored; | 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))) | ||
1535 | goto out; | 1526 | 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; | ||
1546 | |||
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); | ||
1560 | } | 1527 | } |
1528 | ctx->pos += ext4_rec_len_from_disk(de->rec_len, extra_size); | ||
1561 | } | 1529 | } |
1562 | out: | 1530 | out: |
1563 | kfree(dir_buf); | 1531 | kfree(dir_buf); |