diff options
| -rw-r--r-- | fs/ocfs2/dir.c | 176 |
1 files changed, 168 insertions, 8 deletions
diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index f2e2ffbf6c95..8deed89330c7 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c | |||
| @@ -81,6 +81,10 @@ static int ocfs2_do_extend_dir(struct super_block *sb, | |||
| 81 | struct ocfs2_alloc_context *meta_ac, | 81 | struct ocfs2_alloc_context *meta_ac, |
| 82 | struct buffer_head **new_bh); | 82 | struct buffer_head **new_bh); |
| 83 | 83 | ||
| 84 | /* | ||
| 85 | * bh passed here can be an inode block or a dir data block, depending | ||
| 86 | * on the inode inline data flag. | ||
| 87 | */ | ||
| 84 | static int ocfs2_check_dir_entry(struct inode * dir, | 88 | static int ocfs2_check_dir_entry(struct inode * dir, |
| 85 | struct ocfs2_dir_entry * de, | 89 | struct ocfs2_dir_entry * de, |
| 86 | struct buffer_head * bh, | 90 | struct buffer_head * bh, |
| @@ -125,6 +129,8 @@ static int inline ocfs2_search_dirblock(struct buffer_head *bh, | |||
| 125 | struct inode *dir, | 129 | struct inode *dir, |
| 126 | const char *name, int namelen, | 130 | const char *name, int namelen, |
| 127 | unsigned long offset, | 131 | unsigned long offset, |
| 132 | char *first_de, | ||
| 133 | unsigned int bytes, | ||
| 128 | struct ocfs2_dir_entry **res_dir) | 134 | struct ocfs2_dir_entry **res_dir) |
| 129 | { | 135 | { |
| 130 | struct ocfs2_dir_entry *de; | 136 | struct ocfs2_dir_entry *de; |
| @@ -134,8 +140,8 @@ static int inline ocfs2_search_dirblock(struct buffer_head *bh, | |||
| 134 | 140 | ||
| 135 | mlog_entry_void(); | 141 | mlog_entry_void(); |
| 136 | 142 | ||
| 137 | de_buf = bh->b_data; | 143 | de_buf = first_de; |
| 138 | dlimit = de_buf + dir->i_sb->s_blocksize; | 144 | dlimit = de_buf + bytes; |
| 139 | 145 | ||
| 140 | while (de_buf < dlimit) { | 146 | while (de_buf < dlimit) { |
| 141 | /* this code is executed quadratically often */ | 147 | /* this code is executed quadratically often */ |
| @@ -171,9 +177,39 @@ bail: | |||
| 171 | return ret; | 177 | return ret; |
| 172 | } | 178 | } |
| 173 | 179 | ||
| 174 | struct buffer_head *ocfs2_find_entry(const char *name, int namelen, | 180 | static struct buffer_head *ocfs2_find_entry_id(const char *name, |
| 175 | struct inode *dir, | 181 | int namelen, |
| 176 | struct ocfs2_dir_entry **res_dir) | 182 | struct inode *dir, |
| 183 | struct ocfs2_dir_entry **res_dir) | ||
| 184 | { | ||
| 185 | int ret, found; | ||
| 186 | struct buffer_head *di_bh = NULL; | ||
| 187 | struct ocfs2_dinode *di; | ||
| 188 | struct ocfs2_inline_data *data; | ||
| 189 | |||
| 190 | ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno, | ||
| 191 | &di_bh, OCFS2_BH_CACHED, dir); | ||
| 192 | if (ret) { | ||
| 193 | mlog_errno(ret); | ||
| 194 | goto out; | ||
| 195 | } | ||
| 196 | |||
| 197 | di = (struct ocfs2_dinode *)di_bh->b_data; | ||
| 198 | data = &di->id2.i_data; | ||
| 199 | |||
| 200 | found = ocfs2_search_dirblock(di_bh, dir, name, namelen, 0, | ||
| 201 | data->id_data, i_size_read(dir), res_dir); | ||
| 202 | if (found == 1) | ||
| 203 | return di_bh; | ||
| 204 | |||
| 205 | brelse(di_bh); | ||
| 206 | out: | ||
| 207 | return NULL; | ||
| 208 | } | ||
| 209 | |||
| 210 | struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen, | ||
| 211 | struct inode *dir, | ||
| 212 | struct ocfs2_dir_entry **res_dir) | ||
| 177 | { | 213 | { |
| 178 | struct super_block *sb; | 214 | struct super_block *sb; |
| 179 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; | 215 | struct buffer_head *bh_use[NAMEI_RA_SIZE]; |
| @@ -188,7 +224,6 @@ struct buffer_head *ocfs2_find_entry(const char *name, int namelen, | |||
| 188 | 224 | ||
| 189 | mlog_entry_void(); | 225 | mlog_entry_void(); |
| 190 | 226 | ||
| 191 | *res_dir = NULL; | ||
| 192 | sb = dir->i_sb; | 227 | sb = dir->i_sb; |
| 193 | 228 | ||
| 194 | nblocks = i_size_read(dir) >> sb->s_blocksize_bits; | 229 | nblocks = i_size_read(dir) >> sb->s_blocksize_bits; |
| @@ -236,6 +271,7 @@ restart: | |||
| 236 | } | 271 | } |
| 237 | i = ocfs2_search_dirblock(bh, dir, name, namelen, | 272 | i = ocfs2_search_dirblock(bh, dir, name, namelen, |
| 238 | block << sb->s_blocksize_bits, | 273 | block << sb->s_blocksize_bits, |
| 274 | bh->b_data, sb->s_blocksize, | ||
| 239 | res_dir); | 275 | res_dir); |
| 240 | if (i == 1) { | 276 | if (i == 1) { |
| 241 | OCFS2_I(dir)->ip_dir_start_lookup = block; | 277 | OCFS2_I(dir)->ip_dir_start_lookup = block; |
| @@ -271,6 +307,30 @@ cleanup_and_exit: | |||
| 271 | return ret; | 307 | return ret; |
| 272 | } | 308 | } |
| 273 | 309 | ||
| 310 | /* | ||
| 311 | * Try to find an entry of the provided name within 'dir'. | ||
| 312 | * | ||
| 313 | * If nothing was found, NULL is returned. Otherwise, a buffer_head | ||
| 314 | * and pointer to the dir entry are passed back. | ||
| 315 | * | ||
| 316 | * Caller can NOT assume anything about the contents of the | ||
| 317 | * buffer_head - it is passed back only so that it can be passed into | ||
| 318 | * any one of the manipulation functions (add entry, delete entry, | ||
| 319 | * etc). As an example, bh in the extent directory case is a data | ||
| 320 | * block, in the inline-data case it actually points to an inode. | ||
| 321 | */ | ||
| 322 | struct buffer_head *ocfs2_find_entry(const char *name, int namelen, | ||
| 323 | struct inode *dir, | ||
| 324 | struct ocfs2_dir_entry **res_dir) | ||
| 325 | { | ||
| 326 | *res_dir = NULL; | ||
| 327 | |||
| 328 | if (OCFS2_I(dir)->ip_dyn_features & OCFS2_INLINE_DATA_FL) | ||
| 329 | return ocfs2_find_entry_id(name, namelen, dir, res_dir); | ||
| 330 | |||
| 331 | return ocfs2_find_entry_el(name, namelen, dir, res_dir); | ||
| 332 | } | ||
| 333 | |||
| 274 | int ocfs2_update_entry(struct inode *dir, handle_t *handle, | 334 | int ocfs2_update_entry(struct inode *dir, handle_t *handle, |
| 275 | struct buffer_head *de_bh, struct ocfs2_dir_entry *de, | 335 | struct buffer_head *de_bh, struct ocfs2_dir_entry *de, |
| 276 | struct inode *new_entry_inode) | 336 | struct inode *new_entry_inode) |
| @@ -459,8 +519,98 @@ bail: | |||
| 459 | return retval; | 519 | return retval; |
| 460 | } | 520 | } |
| 461 | 521 | ||
| 462 | static int ocfs2_dir_foreach_blk(struct inode *inode, unsigned long *f_version, | 522 | static int ocfs2_dir_foreach_blk_id(struct inode *inode, |
| 463 | loff_t *f_pos, void *priv, filldir_t filldir) | 523 | unsigned long *f_version, |
| 524 | loff_t *f_pos, void *priv, | ||
| 525 | filldir_t filldir) | ||
| 526 | { | ||
| 527 | int ret, i, filldir_ret; | ||
| 528 | unsigned long offset = *f_pos; | ||
| 529 | struct buffer_head *di_bh = NULL; | ||
| 530 | struct ocfs2_dinode *di; | ||
| 531 | struct ocfs2_inline_data *data; | ||
| 532 | struct ocfs2_dir_entry *de; | ||
| 533 | |||
| 534 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno, | ||
| 535 | &di_bh, OCFS2_BH_CACHED, inode); | ||
| 536 | if (ret) { | ||
| 537 | mlog(ML_ERROR, "Unable to read inode block for dir %llu\n", | ||
| 538 | (unsigned long long)OCFS2_I(inode)->ip_blkno); | ||
| 539 | goto out; | ||
| 540 | } | ||
| 541 | |||
| 542 | di = (struct ocfs2_dinode *)di_bh->b_data; | ||
| 543 | data = &di->id2.i_data; | ||
| 544 | |||
| 545 | while (*f_pos < i_size_read(inode)) { | ||
| 546 | revalidate: | ||
| 547 | /* If the dir block has changed since the last call to | ||
| 548 | * readdir(2), then we might be pointing to an invalid | ||
| 549 | * dirent right now. Scan from the start of the block | ||
| 550 | * to make sure. */ | ||
| 551 | if (*f_version != inode->i_version) { | ||
| 552 | for (i = 0; i < i_size_read(inode) && i < offset; ) { | ||
| 553 | de = (struct ocfs2_dir_entry *) | ||
| 554 | (data->id_data + i); | ||
| 555 | /* It's too expensive to do a full | ||
| 556 | * dirent test each time round this | ||
| 557 | * loop, but we do have to test at | ||
| 558 | * least that it is non-zero. A | ||
| 559 | * failure will be detected in the | ||
| 560 | * dirent test below. */ | ||
| 561 | if (le16_to_cpu(de->rec_len) < | ||
| 562 | OCFS2_DIR_REC_LEN(1)) | ||
| 563 | break; | ||
| 564 | i += le16_to_cpu(de->rec_len); | ||
| 565 | } | ||
| 566 | *f_pos = offset = i; | ||
| 567 | *f_version = inode->i_version; | ||
| 568 | } | ||
| 569 | |||
| 570 | de = (struct ocfs2_dir_entry *) (data->id_data + *f_pos); | ||
| 571 | if (!ocfs2_check_dir_entry(inode, de, di_bh, *f_pos)) { | ||
| 572 | /* On error, skip the f_pos to the end. */ | ||
| 573 | *f_pos = i_size_read(inode); | ||
| 574 | goto out; | ||
| 575 | } | ||
| 576 | offset += le16_to_cpu(de->rec_len); | ||
| 577 | if (le64_to_cpu(de->inode)) { | ||
| 578 | /* We might block in the next section | ||
| 579 | * if the data destination is | ||
| 580 | * currently swapped out. So, use a | ||
| 581 | * version stamp to detect whether or | ||
| 582 | * not the directory has been modified | ||
| 583 | * during the copy operation. | ||
| 584 | */ | ||
| 585 | unsigned long version = *f_version; | ||
| 586 | unsigned char d_type = DT_UNKNOWN; | ||
| 587 | |||
| 588 | if (de->file_type < OCFS2_FT_MAX) | ||
| 589 | d_type = ocfs2_filetype_table[de->file_type]; | ||
| 590 | |||
| 591 | filldir_ret = filldir(priv, de->name, | ||
| 592 | de->name_len, | ||
| 593 | *f_pos, | ||
| 594 | le64_to_cpu(de->inode), | ||
| 595 | d_type); | ||
| 596 | if (filldir_ret) | ||
| 597 | break; | ||
| 598 | if (version != *f_version) | ||
| 599 | goto revalidate; | ||
| 600 | } | ||
| 601 | *f_pos += le16_to_cpu(de->rec_len); | ||
| 602 | } | ||
| 603 | |||
| 604 | out: | ||
| 605 | brelse(di_bh); | ||
| 606 | |||
| 607 | return 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | static int ocfs2_dir_foreach_blk_el(struct inode *inode, | ||
| 611 | unsigned long *f_version, | ||
| 612 | loff_t *f_pos, void *priv, | ||
| 613 | filldir_t filldir) | ||
| 464 | { | 614 | { |
| 465 | int error = 0; | 615 | int error = 0; |
| 466 | unsigned long offset, blk, last_ra_blk = 0; | 616 | unsigned long offset, blk, last_ra_blk = 0; |
| @@ -576,6 +726,16 @@ out: | |||
| 576 | return stored; | 726 | return stored; |
| 577 | } | 727 | } |
| 578 | 728 | ||
| 729 | static int ocfs2_dir_foreach_blk(struct inode *inode, unsigned long *f_version, | ||
| 730 | loff_t *f_pos, void *priv, filldir_t filldir) | ||
| 731 | { | ||
| 732 | if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) | ||
| 733 | return ocfs2_dir_foreach_blk_id(inode, f_version, f_pos, priv, | ||
| 734 | filldir); | ||
| 735 | |||
| 736 | return ocfs2_dir_foreach_blk_el(inode, f_version, f_pos, priv, filldir); | ||
| 737 | } | ||
| 738 | |||
| 579 | /* | 739 | /* |
| 580 | * This is intended to be called from inside other kernel functions, | 740 | * This is intended to be called from inside other kernel functions, |
| 581 | * so we fake some arguments. | 741 | * so we fake some arguments. |
