diff options
Diffstat (limited to 'fs/ext4/dir.c')
-rw-r--r-- | fs/ext4/dir.c | 158 |
1 files changed, 67 insertions, 91 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, |