diff options
author | Mark Fasheh <mark.fasheh@oracle.com> | 2007-09-12 16:01:18 -0400 |
---|---|---|
committer | Mark Fasheh <mark.fasheh@oracle.com> | 2007-10-12 14:54:40 -0400 |
commit | 23193e513d1cd69411469f028d56fd175d4a6b07 (patch) | |
tree | 18c2c6019d4ba6253a7c2ce87b53a118801ce534 /fs/ocfs2 | |
parent | 1afc32b952335f665327a1a9001ba1b44bb76fd9 (diff) |
ocfs2: Read support for directories with inline data
This splits out extent based directory read support and implements
inline-data versions of those functions. All knowledge of inline-data versus
extent based directories is internalized. For lookups the code uses
ocfs2_find_entry_id(), full dir iterations make use of
ocfs2_dir_foreach_blk_id().
Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
Reviewed-by: Joel Becker <joel.becker@oracle.com>
Diffstat (limited to 'fs/ocfs2')
-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. |