diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-05-15 21:02:48 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-06-29 04:46:49 -0400 |
commit | 5ded75ec4c576577cae7d556e671d11d0c80c2fc (patch) | |
tree | 964c5d165f74933df96d6fa16f9bd866a7308727 | |
parent | 5f99f4e79abc64ed9d93a4b0158b21c64ff7f478 (diff) |
[readdir] convert ext3
new helper: dir_relax(inode). Call when you are in location that will
_not_ be invalidated by directory modifications (block boundary, in case
of ext*). Returns whether the directory has survived (dropping i_mutex
allows rmdir to kill the sucker; if it returns false to us, ->iterate()
is obviously done)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/ext3/dir.c | 157 | ||||
-rw-r--r-- | include/linux/fs.h | 6 |
2 files changed, 70 insertions, 93 deletions
diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index 87eccbbca255..f522425aaa24 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c | |||
@@ -28,8 +28,7 @@ static unsigned char ext3_filetype_table[] = { | |||
28 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK | 28 | DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK |
29 | }; | 29 | }; |
30 | 30 | ||
31 | static int ext3_dx_readdir(struct file * filp, | 31 | static int ext3_dx_readdir(struct file *, struct dir_context *); |
32 | void * dirent, filldir_t filldir); | ||
33 | 32 | ||
34 | static unsigned char get_dtype(struct super_block *sb, int filetype) | 33 | static unsigned char get_dtype(struct super_block *sb, int filetype) |
35 | { | 34 | { |
@@ -91,36 +90,30 @@ int ext3_check_dir_entry (const char * function, struct inode * dir, | |||
91 | return error_msg == NULL ? 1 : 0; | 90 | return error_msg == NULL ? 1 : 0; |
92 | } | 91 | } |
93 | 92 | ||
94 | static int ext3_readdir(struct file * filp, | 93 | static int ext3_readdir(struct file *file, struct dir_context *ctx) |
95 | void * dirent, filldir_t filldir) | ||
96 | { | 94 | { |
97 | int error = 0; | ||
98 | unsigned long offset; | 95 | unsigned long offset; |
99 | int i, stored; | 96 | int i; |
100 | struct ext3_dir_entry_2 *de; | 97 | struct ext3_dir_entry_2 *de; |
101 | int err; | 98 | int err; |
102 | struct inode *inode = file_inode(filp); | 99 | struct inode *inode = file_inode(file); |
103 | struct super_block *sb = inode->i_sb; | 100 | struct super_block *sb = inode->i_sb; |
104 | int ret = 0; | ||
105 | int dir_has_error = 0; | 101 | int dir_has_error = 0; |
106 | 102 | ||
107 | if (is_dx_dir(inode)) { | 103 | if (is_dx_dir(inode)) { |
108 | err = ext3_dx_readdir(filp, dirent, filldir); | 104 | err = ext3_dx_readdir(file, ctx); |
109 | if (err != ERR_BAD_DX_DIR) { | 105 | if (err != ERR_BAD_DX_DIR) |
110 | ret = err; | 106 | return err; |
111 | goto out; | ||
112 | } | ||
113 | /* | 107 | /* |
114 | * We don't set the inode dirty flag since it's not | 108 | * We don't set the inode dirty flag since it's not |
115 | * critical that it get flushed back to the disk. | 109 | * critical that it get flushed back to the disk. |
116 | */ | 110 | */ |
117 | EXT3_I(file_inode(filp))->i_flags &= ~EXT3_INDEX_FL; | 111 | EXT3_I(inode)->i_flags &= ~EXT3_INDEX_FL; |
118 | } | 112 | } |
119 | stored = 0; | 113 | offset = ctx->pos & (sb->s_blocksize - 1); |
120 | offset = filp->f_pos & (sb->s_blocksize - 1); | ||
121 | 114 | ||
122 | while (!error && !stored && filp->f_pos < inode->i_size) { | 115 | while (ctx->pos < inode->i_size) { |
123 | unsigned long blk = filp->f_pos >> EXT3_BLOCK_SIZE_BITS(sb); | 116 | unsigned long blk = ctx->pos >> EXT3_BLOCK_SIZE_BITS(sb); |
124 | struct buffer_head map_bh; | 117 | struct buffer_head map_bh; |
125 | struct buffer_head *bh = NULL; | 118 | struct buffer_head *bh = NULL; |
126 | 119 | ||
@@ -129,12 +122,12 @@ static int ext3_readdir(struct file * filp, | |||
129 | if (err > 0) { | 122 | if (err > 0) { |
130 | pgoff_t index = map_bh.b_blocknr >> | 123 | pgoff_t index = map_bh.b_blocknr >> |
131 | (PAGE_CACHE_SHIFT - inode->i_blkbits); | 124 | (PAGE_CACHE_SHIFT - inode->i_blkbits); |
132 | if (!ra_has_index(&filp->f_ra, index)) | 125 | if (!ra_has_index(&file->f_ra, index)) |
133 | page_cache_sync_readahead( | 126 | page_cache_sync_readahead( |
134 | sb->s_bdev->bd_inode->i_mapping, | 127 | sb->s_bdev->bd_inode->i_mapping, |
135 | &filp->f_ra, filp, | 128 | &file->f_ra, file, |
136 | index, 1); | 129 | index, 1); |
137 | filp->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT; | 130 | file->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT; |
138 | bh = ext3_bread(NULL, inode, blk, 0, &err); | 131 | bh = ext3_bread(NULL, inode, blk, 0, &err); |
139 | } | 132 | } |
140 | 133 | ||
@@ -146,22 +139,21 @@ static int ext3_readdir(struct file * filp, | |||
146 | if (!dir_has_error) { | 139 | if (!dir_has_error) { |
147 | ext3_error(sb, __func__, "directory #%lu " | 140 | ext3_error(sb, __func__, "directory #%lu " |
148 | "contains a hole at offset %lld", | 141 | "contains a hole at offset %lld", |
149 | inode->i_ino, filp->f_pos); | 142 | inode->i_ino, ctx->pos); |
150 | dir_has_error = 1; | 143 | dir_has_error = 1; |
151 | } | 144 | } |
152 | /* corrupt size? Maybe no more blocks to read */ | 145 | /* corrupt size? Maybe no more blocks to read */ |
153 | if (filp->f_pos > inode->i_blocks << 9) | 146 | if (ctx->pos > inode->i_blocks << 9) |
154 | break; | 147 | break; |
155 | filp->f_pos += sb->s_blocksize - offset; | 148 | ctx->pos += sb->s_blocksize - offset; |
156 | continue; | 149 | continue; |
157 | } | 150 | } |
158 | 151 | ||
159 | revalidate: | ||
160 | /* If the dir block has changed since the last call to | 152 | /* If the dir block has changed since the last call to |
161 | * readdir(2), then we might be pointing to an invalid | 153 | * readdir(2), then we might be pointing to an invalid |
162 | * dirent right now. Scan from the start of the block | 154 | * dirent right now. Scan from the start of the block |
163 | * to make sure. */ | 155 | * to make sure. */ |
164 | if (filp->f_version != inode->i_version) { | 156 | if (offset && file->f_version != inode->i_version) { |
165 | for (i = 0; i < sb->s_blocksize && i < offset; ) { | 157 | for (i = 0; i < sb->s_blocksize && i < offset; ) { |
166 | de = (struct ext3_dir_entry_2 *) | 158 | de = (struct ext3_dir_entry_2 *) |
167 | (bh->b_data + i); | 159 | (bh->b_data + i); |
@@ -177,53 +169,40 @@ revalidate: | |||
177 | i += ext3_rec_len_from_disk(de->rec_len); | 169 | i += ext3_rec_len_from_disk(de->rec_len); |
178 | } | 170 | } |
179 | offset = i; | 171 | offset = i; |
180 | filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) | 172 | ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1)) |
181 | | offset; | 173 | | offset; |
182 | filp->f_version = inode->i_version; | 174 | file->f_version = inode->i_version; |
183 | } | 175 | } |
184 | 176 | ||
185 | while (!error && filp->f_pos < inode->i_size | 177 | while (ctx->pos < inode->i_size |
186 | && offset < sb->s_blocksize) { | 178 | && offset < sb->s_blocksize) { |
187 | de = (struct ext3_dir_entry_2 *) (bh->b_data + offset); | 179 | de = (struct ext3_dir_entry_2 *) (bh->b_data + offset); |
188 | if (!ext3_check_dir_entry ("ext3_readdir", inode, de, | 180 | if (!ext3_check_dir_entry ("ext3_readdir", inode, de, |
189 | bh, offset)) { | 181 | bh, offset)) { |
190 | /* On error, skip the f_pos to the | 182 | /* On error, skip the to the |
191 | next block. */ | 183 | next block. */ |
192 | filp->f_pos = (filp->f_pos | | 184 | ctx->pos = (ctx->pos | |
193 | (sb->s_blocksize - 1)) + 1; | 185 | (sb->s_blocksize - 1)) + 1; |
194 | brelse (bh); | 186 | break; |
195 | ret = stored; | ||
196 | goto out; | ||
197 | } | 187 | } |
198 | offset += ext3_rec_len_from_disk(de->rec_len); | 188 | offset += ext3_rec_len_from_disk(de->rec_len); |
199 | if (le32_to_cpu(de->inode)) { | 189 | if (le32_to_cpu(de->inode)) { |
200 | /* We might block in the next section | 190 | if (!dir_emit(ctx, de->name, de->name_len, |
201 | * if the data destination is | 191 | le32_to_cpu(de->inode), |
202 | * currently swapped out. So, use a | 192 | get_dtype(sb, de->file_type))) { |
203 | * version stamp to detect whether or | 193 | brelse(bh); |
204 | * not the directory has been modified | 194 | return 0; |
205 | * during the copy operation. | 195 | } |
206 | */ | ||
207 | u64 version = filp->f_version; | ||
208 | |||
209 | error = filldir(dirent, de->name, | ||
210 | de->name_len, | ||
211 | filp->f_pos, | ||
212 | le32_to_cpu(de->inode), | ||
213 | get_dtype(sb, de->file_type)); | ||
214 | if (error) | ||
215 | break; | ||
216 | if (version != filp->f_version) | ||
217 | goto revalidate; | ||
218 | stored ++; | ||
219 | } | 196 | } |
220 | filp->f_pos += ext3_rec_len_from_disk(de->rec_len); | 197 | ctx->pos += ext3_rec_len_from_disk(de->rec_len); |
221 | } | 198 | } |
222 | offset = 0; | 199 | offset = 0; |
223 | brelse (bh); | 200 | brelse (bh); |
201 | if (ctx->pos < inode->i_size) | ||
202 | if (!dir_relax(inode)) | ||
203 | return 0; | ||
224 | } | 204 | } |
225 | out: | 205 | return 0; |
226 | return ret; | ||
227 | } | 206 | } |
228 | 207 | ||
229 | static inline int is_32bit_api(void) | 208 | static inline int is_32bit_api(void) |
@@ -452,62 +431,54 @@ int ext3_htree_store_dirent(struct file *dir_file, __u32 hash, | |||
452 | * for all entres on the fname linked list. (Normally there is only | 431 | * for all entres on the fname linked list. (Normally there is only |
453 | * one entry on the linked list, unless there are 62 bit hash collisions.) | 432 | * one entry on the linked list, unless there are 62 bit hash collisions.) |
454 | */ | 433 | */ |
455 | static int call_filldir(struct file * filp, void * dirent, | 434 | static bool call_filldir(struct file *file, struct dir_context *ctx, |
456 | filldir_t filldir, struct fname *fname) | 435 | struct fname *fname) |
457 | { | 436 | { |
458 | struct dir_private_info *info = filp->private_data; | 437 | struct dir_private_info *info = file->private_data; |
459 | loff_t curr_pos; | 438 | struct inode *inode = file_inode(file); |
460 | struct inode *inode = file_inode(filp); | 439 | struct super_block *sb = inode->i_sb; |
461 | struct super_block * sb; | ||
462 | int error; | ||
463 | |||
464 | sb = inode->i_sb; | ||
465 | 440 | ||
466 | if (!fname) { | 441 | if (!fname) { |
467 | printk("call_filldir: called with null fname?!?\n"); | 442 | printk("call_filldir: called with null fname?!?\n"); |
468 | return 0; | 443 | return true; |
469 | } | 444 | } |
470 | curr_pos = hash2pos(filp, fname->hash, fname->minor_hash); | 445 | ctx->pos = hash2pos(file, fname->hash, fname->minor_hash); |
471 | while (fname) { | 446 | while (fname) { |
472 | error = filldir(dirent, fname->name, | 447 | if (!dir_emit(ctx, fname->name, fname->name_len, |
473 | fname->name_len, curr_pos, | ||
474 | fname->inode, | 448 | fname->inode, |
475 | get_dtype(sb, fname->file_type)); | 449 | get_dtype(sb, fname->file_type))) { |
476 | if (error) { | ||
477 | filp->f_pos = curr_pos; | ||
478 | info->extra_fname = fname; | 450 | info->extra_fname = fname; |
479 | return error; | 451 | return false; |
480 | } | 452 | } |
481 | fname = fname->next; | 453 | fname = fname->next; |
482 | } | 454 | } |
483 | return 0; | 455 | return true; |
484 | } | 456 | } |
485 | 457 | ||
486 | static int ext3_dx_readdir(struct file * filp, | 458 | static int ext3_dx_readdir(struct file *file, struct dir_context *ctx) |
487 | void * dirent, filldir_t filldir) | ||
488 | { | 459 | { |
489 | struct dir_private_info *info = filp->private_data; | 460 | struct dir_private_info *info = file->private_data; |
490 | struct inode *inode = file_inode(filp); | 461 | struct inode *inode = file_inode(file); |
491 | struct fname *fname; | 462 | struct fname *fname; |
492 | int ret; | 463 | int ret; |
493 | 464 | ||
494 | if (!info) { | 465 | if (!info) { |
495 | info = ext3_htree_create_dir_info(filp, filp->f_pos); | 466 | info = ext3_htree_create_dir_info(file, ctx->pos); |
496 | if (!info) | 467 | if (!info) |
497 | return -ENOMEM; | 468 | return -ENOMEM; |
498 | filp->private_data = info; | 469 | file->private_data = info; |
499 | } | 470 | } |
500 | 471 | ||
501 | if (filp->f_pos == ext3_get_htree_eof(filp)) | 472 | if (ctx->pos == ext3_get_htree_eof(file)) |
502 | return 0; /* EOF */ | 473 | return 0; /* EOF */ |
503 | 474 | ||
504 | /* Some one has messed with f_pos; reset the world */ | 475 | /* Some one has messed with f_pos; reset the world */ |
505 | if (info->last_pos != filp->f_pos) { | 476 | if (info->last_pos != ctx->pos) { |
506 | free_rb_tree_fname(&info->root); | 477 | free_rb_tree_fname(&info->root); |
507 | info->curr_node = NULL; | 478 | info->curr_node = NULL; |
508 | info->extra_fname = NULL; | 479 | info->extra_fname = NULL; |
509 | info->curr_hash = pos2maj_hash(filp, filp->f_pos); | 480 | info->curr_hash = pos2maj_hash(file, ctx->pos); |
510 | info->curr_minor_hash = pos2min_hash(filp, filp->f_pos); | 481 | info->curr_minor_hash = pos2min_hash(file, ctx->pos); |
511 | } | 482 | } |
512 | 483 | ||
513 | /* | 484 | /* |
@@ -515,7 +486,7 @@ static int ext3_dx_readdir(struct file * filp, | |||
515 | * chain, return them first. | 486 | * chain, return them first. |
516 | */ | 487 | */ |
517 | if (info->extra_fname) { | 488 | if (info->extra_fname) { |
518 | if (call_filldir(filp, dirent, filldir, info->extra_fname)) | 489 | if (!call_filldir(file, ctx, info->extra_fname)) |
519 | goto finished; | 490 | goto finished; |
520 | info->extra_fname = NULL; | 491 | info->extra_fname = NULL; |
521 | goto next_node; | 492 | goto next_node; |
@@ -529,17 +500,17 @@ static int ext3_dx_readdir(struct file * filp, | |||
529 | * cached entries. | 500 | * cached entries. |
530 | */ | 501 | */ |
531 | if ((!info->curr_node) || | 502 | if ((!info->curr_node) || |
532 | (filp->f_version != inode->i_version)) { | 503 | (file->f_version != inode->i_version)) { |
533 | info->curr_node = NULL; | 504 | info->curr_node = NULL; |
534 | free_rb_tree_fname(&info->root); | 505 | free_rb_tree_fname(&info->root); |
535 | filp->f_version = inode->i_version; | 506 | file->f_version = inode->i_version; |
536 | ret = ext3_htree_fill_tree(filp, info->curr_hash, | 507 | ret = ext3_htree_fill_tree(file, info->curr_hash, |
537 | info->curr_minor_hash, | 508 | info->curr_minor_hash, |
538 | &info->next_hash); | 509 | &info->next_hash); |
539 | if (ret < 0) | 510 | if (ret < 0) |
540 | return ret; | 511 | return ret; |
541 | if (ret == 0) { | 512 | if (ret == 0) { |
542 | filp->f_pos = ext3_get_htree_eof(filp); | 513 | ctx->pos = ext3_get_htree_eof(file); |
543 | break; | 514 | break; |
544 | } | 515 | } |
545 | info->curr_node = rb_first(&info->root); | 516 | info->curr_node = rb_first(&info->root); |
@@ -548,7 +519,7 @@ static int ext3_dx_readdir(struct file * filp, | |||
548 | fname = rb_entry(info->curr_node, struct fname, rb_hash); | 519 | fname = rb_entry(info->curr_node, struct fname, rb_hash); |
549 | info->curr_hash = fname->hash; | 520 | info->curr_hash = fname->hash; |
550 | info->curr_minor_hash = fname->minor_hash; | 521 | info->curr_minor_hash = fname->minor_hash; |
551 | if (call_filldir(filp, dirent, filldir, fname)) | 522 | if (!call_filldir(file, ctx, fname)) |
552 | break; | 523 | break; |
553 | next_node: | 524 | next_node: |
554 | info->curr_node = rb_next(info->curr_node); | 525 | info->curr_node = rb_next(info->curr_node); |
@@ -559,7 +530,7 @@ static int ext3_dx_readdir(struct file * filp, | |||
559 | info->curr_minor_hash = fname->minor_hash; | 530 | info->curr_minor_hash = fname->minor_hash; |
560 | } else { | 531 | } else { |
561 | if (info->next_hash == ~0) { | 532 | if (info->next_hash == ~0) { |
562 | filp->f_pos = ext3_get_htree_eof(filp); | 533 | ctx->pos = ext3_get_htree_eof(file); |
563 | break; | 534 | break; |
564 | } | 535 | } |
565 | info->curr_hash = info->next_hash; | 536 | info->curr_hash = info->next_hash; |
@@ -567,7 +538,7 @@ static int ext3_dx_readdir(struct file * filp, | |||
567 | } | 538 | } |
568 | } | 539 | } |
569 | finished: | 540 | finished: |
570 | info->last_pos = filp->f_pos; | 541 | info->last_pos = ctx->pos; |
571 | return 0; | 542 | return 0; |
572 | } | 543 | } |
573 | 544 | ||
@@ -582,7 +553,7 @@ static int ext3_release_dir (struct inode * inode, struct file * filp) | |||
582 | const struct file_operations ext3_dir_operations = { | 553 | const struct file_operations ext3_dir_operations = { |
583 | .llseek = ext3_dir_llseek, | 554 | .llseek = ext3_dir_llseek, |
584 | .read = generic_read_dir, | 555 | .read = generic_read_dir, |
585 | .readdir = ext3_readdir, | 556 | .iterate = ext3_readdir, |
586 | .unlocked_ioctl = ext3_ioctl, | 557 | .unlocked_ioctl = ext3_ioctl, |
587 | #ifdef CONFIG_COMPAT | 558 | #ifdef CONFIG_COMPAT |
588 | .compat_ioctl = ext3_compat_ioctl, | 559 | .compat_ioctl = ext3_compat_ioctl, |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 40293a6ce804..aa9770c7e8df 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -2725,5 +2725,11 @@ static inline bool dir_emit_dots(struct file *file, struct dir_context *ctx) | |||
2725 | } | 2725 | } |
2726 | return true; | 2726 | return true; |
2727 | } | 2727 | } |
2728 | static inline bool dir_relax(struct inode *inode) | ||
2729 | { | ||
2730 | mutex_unlock(&inode->i_mutex); | ||
2731 | mutex_lock(&inode->i_mutex); | ||
2732 | return !IS_DEADDIR(inode); | ||
2733 | } | ||
2728 | 2734 | ||
2729 | #endif /* _LINUX_FS_H */ | 2735 | #endif /* _LINUX_FS_H */ |