]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Tao Ma <tao.ma@oracle.com> |
2 | Subject: [PATCH 12/16] ocfs2: Add xattr lookup code xattr btrees | |
3 | Patch-mainline: 2.6.28? | |
4 | References: FATE302067 | |
5 | ||
6 | Add code to lookup a given extended attribute in the xattr btree. Lookup | |
7 | follows this general scheme: | |
8 | ||
9 | 1. Use ocfs2_xattr_get_rec to find the xattr extent record | |
10 | ||
11 | 2. Find the xattr bucket within the extent which may contain this xattr | |
12 | ||
13 | 3. Iterate the bucket to find the xattr. In ocfs2_xattr_block_get(), we need | |
14 | to recalcuate the block offset and name offset for the right position of | |
15 | name/value. | |
16 | ||
17 | Signed-off-by: Tao Ma <tao.ma@oracle.com> | |
18 | Signed-off-by: Mark Fasheh <mfasheh@suse.com> | |
19 | --- | |
20 | fs/ocfs2/xattr.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++---- | |
21 | 1 files changed, 328 insertions(+), 23 deletions(-) | |
22 | ||
23 | diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c | |
24 | index ed41c15..a5ca066 100644 | |
25 | --- a/fs/ocfs2/xattr.c | |
26 | +++ b/fs/ocfs2/xattr.c | |
27 | @@ -115,12 +115,25 @@ struct ocfs2_xattr_search { | |
28 | */ | |
29 | struct buffer_head *xattr_bh; | |
30 | struct ocfs2_xattr_header *header; | |
31 | + struct ocfs2_xattr_bucket bucket; | |
32 | void *base; | |
33 | void *end; | |
34 | struct ocfs2_xattr_entry *here; | |
35 | int not_found; | |
36 | }; | |
37 | ||
38 | +static int ocfs2_xattr_bucket_get_name_value(struct inode *inode, | |
39 | + struct ocfs2_xattr_header *xh, | |
40 | + int index, | |
41 | + int *block_off, | |
42 | + int *new_offset); | |
43 | + | |
44 | +static int ocfs2_xattr_index_block_find(struct inode *inode, | |
45 | + struct buffer_head *root_bh, | |
46 | + int name_index, | |
47 | + const char *name, | |
48 | + struct ocfs2_xattr_search *xs); | |
49 | + | |
50 | static int ocfs2_xattr_tree_list_index_block(struct inode *inode, | |
51 | struct ocfs2_xattr_tree_root *xt, | |
52 | char *buffer, | |
53 | @@ -620,7 +633,7 @@ static int ocfs2_xattr_find_entry(int name_index, | |
54 | } | |
55 | ||
56 | static int ocfs2_xattr_get_value_outside(struct inode *inode, | |
57 | - struct ocfs2_xattr_search *xs, | |
58 | + struct ocfs2_xattr_value_root *xv, | |
59 | void *buffer, | |
60 | size_t len) | |
61 | { | |
62 | @@ -629,12 +642,8 @@ static int ocfs2_xattr_get_value_outside(struct inode *inode, | |
63 | int i, ret = 0; | |
64 | size_t cplen, blocksize; | |
65 | struct buffer_head *bh = NULL; | |
66 | - struct ocfs2_xattr_value_root *xv; | |
67 | struct ocfs2_extent_list *el; | |
68 | ||
69 | - xv = (struct ocfs2_xattr_value_root *) | |
70 | - (xs->base + le16_to_cpu(xs->here->xe_name_offset) + | |
71 | - OCFS2_XATTR_SIZE(xs->here->xe_name_len)); | |
72 | el = &xv->xr_list; | |
73 | clusters = le32_to_cpu(xv->xr_clusters); | |
74 | bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); | |
75 | @@ -684,6 +693,7 @@ static int ocfs2_xattr_ibody_get(struct inode *inode, | |
76 | { | |
77 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | |
78 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | |
79 | + struct ocfs2_xattr_value_root *xv; | |
80 | size_t size; | |
81 | int ret = 0; | |
82 | ||
83 | @@ -708,7 +718,11 @@ static int ocfs2_xattr_ibody_get(struct inode *inode, | |
84 | le16_to_cpu(xs->here->xe_name_offset) + | |
85 | OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); | |
86 | } else { | |
87 | - ret = ocfs2_xattr_get_value_outside(inode, xs, | |
88 | + xv = (struct ocfs2_xattr_value_root *) | |
89 | + (xs->base + le16_to_cpu( | |
90 | + xs->here->xe_name_offset) + | |
91 | + OCFS2_XATTR_SIZE(xs->here->xe_name_len)); | |
92 | + ret = ocfs2_xattr_get_value_outside(inode, xv, | |
93 | buffer, size); | |
94 | if (ret < 0) { | |
95 | mlog_errno(ret); | |
96 | @@ -730,12 +744,15 @@ static int ocfs2_xattr_block_get(struct inode *inode, | |
97 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | |
98 | struct buffer_head *blk_bh = NULL; | |
99 | struct ocfs2_xattr_block *xb; | |
100 | + struct ocfs2_xattr_value_root *xv; | |
101 | size_t size; | |
102 | - int ret = -ENODATA; | |
103 | + int ret = -ENODATA, name_offset, name_len, block_off, i; | |
104 | ||
105 | if (!di->i_xattr_loc) | |
106 | return ret; | |
107 | ||
108 | + memset(&xs->bucket, 0, sizeof(xs->bucket)); | |
109 | + | |
110 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | |
111 | le64_to_cpu(di->i_xattr_loc), | |
112 | &blk_bh, OCFS2_BH_CACHED, inode); | |
113 | @@ -752,12 +769,19 @@ static int ocfs2_xattr_block_get(struct inode *inode, | |
114 | ||
115 | xs->xattr_bh = blk_bh; | |
116 | xb = (struct ocfs2_xattr_block *)blk_bh->b_data; | |
117 | - xs->header = &xb->xb_attrs.xb_header; | |
118 | - xs->base = (void *)xs->header; | |
119 | - xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; | |
120 | - xs->here = xs->header->xh_entries; | |
121 | ||
122 | - ret = ocfs2_xattr_find_entry(name_index, name, xs); | |
123 | + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { | |
124 | + xs->header = &xb->xb_attrs.xb_header; | |
125 | + xs->base = (void *)xs->header; | |
126 | + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; | |
127 | + xs->here = xs->header->xh_entries; | |
128 | + | |
129 | + ret = ocfs2_xattr_find_entry(name_index, name, xs); | |
130 | + } else | |
131 | + ret = ocfs2_xattr_index_block_find(inode, blk_bh, | |
132 | + name_index, | |
133 | + name, xs); | |
134 | + | |
135 | if (ret) | |
136 | goto cleanup; | |
137 | size = le64_to_cpu(xs->here->xe_value_size); | |
138 | @@ -765,12 +789,26 @@ static int ocfs2_xattr_block_get(struct inode *inode, | |
139 | ret = -ERANGE; | |
140 | if (size > buffer_size) | |
141 | goto cleanup; | |
142 | + | |
143 | + name_offset = le16_to_cpu(xs->here->xe_name_offset); | |
144 | + name_len = OCFS2_XATTR_SIZE(xs->here->xe_name_len); | |
145 | + i = xs->here - xs->header->xh_entries; | |
146 | + | |
147 | + if (le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED) { | |
148 | + ret = ocfs2_xattr_bucket_get_name_value(inode, | |
149 | + xs->bucket.xh, | |
150 | + i, | |
151 | + &block_off, | |
152 | + &name_offset); | |
153 | + xs->base = xs->bucket.bhs[block_off]->b_data; | |
154 | + } | |
155 | if (ocfs2_xattr_is_local(xs->here)) { | |
156 | memcpy(buffer, (void *)xs->base + | |
157 | - le16_to_cpu(xs->here->xe_name_offset) + | |
158 | - OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); | |
159 | + name_offset + name_len, size); | |
160 | } else { | |
161 | - ret = ocfs2_xattr_get_value_outside(inode, xs, | |
162 | + xv = (struct ocfs2_xattr_value_root *) | |
163 | + (xs->base + name_offset + name_len); | |
164 | + ret = ocfs2_xattr_get_value_outside(inode, xv, | |
165 | buffer, size); | |
166 | if (ret < 0) { | |
167 | mlog_errno(ret); | |
168 | @@ -780,8 +818,11 @@ static int ocfs2_xattr_block_get(struct inode *inode, | |
169 | } | |
170 | ret = size; | |
171 | cleanup: | |
172 | - brelse(blk_bh); | |
173 | + for (i = 0; i < OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET; i++) | |
174 | + brelse(xs->bucket.bhs[i]); | |
175 | + memset(&xs->bucket, 0, sizeof(xs->bucket)); | |
176 | ||
177 | + brelse(blk_bh); | |
178 | return ret; | |
179 | } | |
180 | ||
181 | @@ -1695,6 +1736,7 @@ static int ocfs2_xattr_block_find(struct inode *inode, | |
182 | { | |
183 | struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; | |
184 | struct buffer_head *blk_bh = NULL; | |
185 | + struct ocfs2_xattr_block *xb; | |
186 | int ret = 0; | |
187 | ||
188 | if (!di->i_xattr_loc) | |
189 | @@ -1715,20 +1757,26 @@ static int ocfs2_xattr_block_find(struct inode *inode, | |
190 | } | |
191 | ||
192 | xs->xattr_bh = blk_bh; | |
193 | - xs->header = &((struct ocfs2_xattr_block *)blk_bh->b_data)-> | |
194 | - xb_attrs.xb_header; | |
195 | - xs->base = (void *)xs->header; | |
196 | - xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; | |
197 | - xs->here = xs->header->xh_entries; | |
198 | + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; | |
199 | + | |
200 | + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { | |
201 | + xs->header = &xb->xb_attrs.xb_header; | |
202 | + xs->base = (void *)xs->header; | |
203 | + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; | |
204 | + xs->here = xs->header->xh_entries; | |
205 | + | |
206 | + ret = ocfs2_xattr_find_entry(name_index, name, xs); | |
207 | + } else | |
208 | + ret = ocfs2_xattr_index_block_find(inode, blk_bh, | |
209 | + name_index, | |
210 | + name, xs); | |
211 | ||
212 | - ret = ocfs2_xattr_find_entry(name_index, name, xs); | |
213 | if (ret && ret != -ENODATA) { | |
214 | xs->xattr_bh = NULL; | |
215 | goto cleanup; | |
216 | } | |
217 | xs->not_found = ret; | |
218 | return 0; | |
219 | - | |
220 | cleanup: | |
221 | brelse(blk_bh); | |
222 | ||
223 | @@ -1957,6 +2005,18 @@ cleanup: | |
224 | return ret; | |
225 | } | |
226 | ||
227 | +static inline u32 ocfs2_xattr_hash_by_name(struct inode *inode, | |
228 | + int name_index, | |
229 | + const char *suffix_name) | |
230 | +{ | |
231 | + struct xattr_handler *handler = ocfs2_xattr_handler(name_index); | |
232 | + char *prefix = handler->prefix; | |
233 | + int prefix_len = strlen(handler->prefix); | |
234 | + | |
235 | + return ocfs2_xattr_name_hash(inode, prefix, prefix_len, | |
236 | + (char *)suffix_name, strlen(suffix_name)); | |
237 | +} | |
238 | + | |
239 | /* | |
240 | * Find the xattr extent rec which may contains name_hash. | |
241 | * e_cpos will be the first name hash of the xattr rec. | |
242 | @@ -2026,6 +2086,251 @@ typedef int (xattr_bucket_func)(struct inode *inode, | |
243 | struct ocfs2_xattr_bucket *bucket, | |
244 | void *para); | |
245 | ||
246 | +static int ocfs2_find_xe_in_bucket(struct inode *inode, | |
247 | + struct buffer_head *header_bh, | |
248 | + int name_index, | |
249 | + const char *name, | |
250 | + u32 name_hash, | |
251 | + u16 *xe_index, | |
252 | + int *found) | |
253 | +{ | |
254 | + int i, ret = 0, cmp = 1, block_off, new_offset; | |
255 | + struct ocfs2_xattr_header *xh = | |
256 | + (struct ocfs2_xattr_header *)header_bh->b_data; | |
257 | + size_t name_len = strlen(name); | |
258 | + struct ocfs2_xattr_entry *xe = NULL; | |
259 | + struct buffer_head *name_bh = NULL; | |
260 | + char *xe_name; | |
261 | + | |
262 | + /* | |
263 | + * We don't use binary search in the bucket because there | |
264 | + * may be multiple entries with the same name hash. | |
265 | + */ | |
266 | + for (i = 0; i < le16_to_cpu(xh->xh_count); i++) { | |
267 | + xe = &xh->xh_entries[i]; | |
268 | + | |
269 | + if (name_hash > le32_to_cpu(xe->xe_name_hash)) | |
270 | + continue; | |
271 | + else if (name_hash < le32_to_cpu(xe->xe_name_hash)) | |
272 | + break; | |
273 | + | |
274 | + cmp = name_index - ocfs2_xattr_get_type(xe); | |
275 | + if (!cmp) | |
276 | + cmp = name_len - xe->xe_name_len; | |
277 | + if (cmp) | |
278 | + continue; | |
279 | + | |
280 | + ret = ocfs2_xattr_bucket_get_name_value(inode, | |
281 | + xh, | |
282 | + i, | |
283 | + &block_off, | |
284 | + &new_offset); | |
285 | + if (ret) { | |
286 | + mlog_errno(ret); | |
287 | + break; | |
288 | + } | |
289 | + | |
290 | + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | |
291 | + header_bh->b_blocknr + block_off, | |
292 | + &name_bh, OCFS2_BH_CACHED, inode); | |
293 | + if (ret) { | |
294 | + mlog_errno(ret); | |
295 | + break; | |
296 | + } | |
297 | + xe_name = name_bh->b_data + new_offset; | |
298 | + | |
299 | + cmp = memcmp(name, xe_name, name_len); | |
300 | + brelse(name_bh); | |
301 | + name_bh = NULL; | |
302 | + | |
303 | + if (cmp == 0) { | |
304 | + *xe_index = i; | |
305 | + *found = 1; | |
306 | + ret = 0; | |
307 | + break; | |
308 | + } | |
309 | + } | |
310 | + | |
311 | + return ret; | |
312 | +} | |
313 | + | |
314 | +/* | |
315 | + * Find the specified xattr entry in a series of buckets. | |
316 | + * This series start from p_blkno and last for num_clusters. | |
317 | + * The ocfs2_xattr_header.xh_num_buckets of the first bucket contains | |
318 | + * the num of the valid buckets. | |
319 | + * | |
320 | + * Return the buffer_head this xattr should reside in. And if the xattr's | |
321 | + * hash is in the gap of 2 buckets, return the lower bucket. | |
322 | + */ | |
323 | +static int ocfs2_xattr_bucket_find(struct inode *inode, | |
324 | + int name_index, | |
325 | + const char *name, | |
326 | + u32 name_hash, | |
327 | + u64 p_blkno, | |
328 | + u32 first_hash, | |
329 | + u32 num_clusters, | |
330 | + struct ocfs2_xattr_search *xs) | |
331 | +{ | |
332 | + int ret, found = 0; | |
333 | + struct buffer_head *bh = NULL; | |
334 | + struct buffer_head *lower_bh = NULL; | |
335 | + struct ocfs2_xattr_header *xh = NULL; | |
336 | + struct ocfs2_xattr_entry *xe = NULL; | |
337 | + u16 index = 0; | |
338 | + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); | |
339 | + int low_bucket = 0, bucket, high_bucket; | |
340 | + u32 last_hash; | |
341 | + u64 blkno; | |
342 | + | |
343 | + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), p_blkno, | |
344 | + &bh, OCFS2_BH_CACHED, inode); | |
345 | + if (ret) { | |
346 | + mlog_errno(ret); | |
347 | + goto out; | |
348 | + } | |
349 | + | |
350 | + xh = (struct ocfs2_xattr_header *)bh->b_data; | |
351 | + high_bucket = le16_to_cpu(xh->xh_num_buckets) - 1; | |
352 | + | |
353 | + while (low_bucket <= high_bucket) { | |
354 | + brelse(bh); | |
355 | + bh = NULL; | |
356 | + bucket = (low_bucket + high_bucket) / 2; | |
357 | + | |
358 | + blkno = p_blkno + bucket * blk_per_bucket; | |
359 | + | |
360 | + ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, | |
361 | + &bh, OCFS2_BH_CACHED, inode); | |
362 | + if (ret) { | |
363 | + mlog_errno(ret); | |
364 | + goto out; | |
365 | + } | |
366 | + | |
367 | + xh = (struct ocfs2_xattr_header *)bh->b_data; | |
368 | + xe = &xh->xh_entries[0]; | |
369 | + if (name_hash < le32_to_cpu(xe->xe_name_hash)) { | |
370 | + high_bucket = bucket - 1; | |
371 | + continue; | |
372 | + } | |
373 | + | |
374 | + /* | |
375 | + * Check whether the hash of the last entry in our | |
376 | + * bucket is larger than the search one. | |
377 | + */ | |
378 | + xe = &xh->xh_entries[le16_to_cpu(xh->xh_count) - 1]; | |
379 | + last_hash = le32_to_cpu(xe->xe_name_hash); | |
380 | + | |
381 | + /* record lower_bh which may be the insert place. */ | |
382 | + brelse(lower_bh); | |
383 | + lower_bh = bh; | |
384 | + bh = NULL; | |
385 | + | |
386 | + if (name_hash > le32_to_cpu(xe->xe_name_hash)) { | |
387 | + low_bucket = bucket + 1; | |
388 | + continue; | |
389 | + } | |
390 | + | |
391 | + /* the searched xattr should reside in this bucket if exists. */ | |
392 | + ret = ocfs2_find_xe_in_bucket(inode, lower_bh, | |
393 | + name_index, name, name_hash, | |
394 | + &index, &found); | |
395 | + if (ret) { | |
396 | + mlog_errno(ret); | |
397 | + goto out; | |
398 | + } | |
399 | + break; | |
400 | + } | |
401 | + | |
402 | + /* | |
403 | + * Record the bucket we have found. | |
404 | + * When the xattr's hash value is in the gap of 2 buckets, we will | |
405 | + * always set it to the previous bucket. | |
406 | + */ | |
407 | + if (!lower_bh) { | |
408 | + /* | |
409 | + * We can't find any bucket whose first name_hash is less | |
410 | + * than the find name_hash. | |
411 | + */ | |
412 | + BUG_ON(bh->b_blocknr != p_blkno); | |
413 | + lower_bh = bh; | |
414 | + bh = NULL; | |
415 | + } | |
416 | + xs->bucket.bhs[0] = lower_bh; | |
417 | + xs->bucket.xh = (struct ocfs2_xattr_header *) | |
418 | + xs->bucket.bhs[0]->b_data; | |
419 | + lower_bh = NULL; | |
420 | + | |
421 | + xs->header = xs->bucket.xh; | |
422 | + xs->base = xs->bucket.bhs[0]->b_data; | |
423 | + xs->end = xs->base + inode->i_sb->s_blocksize; | |
424 | + | |
425 | + if (found) { | |
426 | + /* | |
427 | + * If we have found the xattr enty, read all the blocks in | |
428 | + * this bucket. | |
429 | + */ | |
430 | + ret = ocfs2_read_blocks(OCFS2_SB(inode->i_sb), | |
431 | + xs->bucket.bhs[0]->b_blocknr + 1, | |
432 | + blk_per_bucket - 1, &xs->bucket.bhs[1], | |
433 | + OCFS2_BH_CACHED, inode); | |
434 | + if (ret) { | |
435 | + mlog_errno(ret); | |
436 | + goto out; | |
437 | + } | |
438 | + | |
439 | + xs->here = &xs->header->xh_entries[index]; | |
440 | + mlog(0, "find xattr %s in bucket %llu, entry = %u\n", name, | |
441 | + (unsigned long long)xs->bucket.bhs[0]->b_blocknr, index); | |
442 | + } else | |
443 | + ret = -ENODATA; | |
444 | + | |
445 | +out: | |
446 | + brelse(bh); | |
447 | + brelse(lower_bh); | |
448 | + return ret; | |
449 | +} | |
450 | + | |
451 | +static int ocfs2_xattr_index_block_find(struct inode *inode, | |
452 | + struct buffer_head *root_bh, | |
453 | + int name_index, | |
454 | + const char *name, | |
455 | + struct ocfs2_xattr_search *xs) | |
456 | +{ | |
457 | + int ret; | |
458 | + struct ocfs2_xattr_block *xb = | |
459 | + (struct ocfs2_xattr_block *)root_bh->b_data; | |
460 | + struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; | |
461 | + struct ocfs2_extent_list *el = &xb_root->xt_list; | |
462 | + u64 p_blkno = 0; | |
463 | + u32 first_hash, num_clusters = 0; | |
464 | + u32 name_hash = ocfs2_xattr_hash_by_name(inode, name_index, name); | |
465 | + | |
466 | + if (le16_to_cpu(el->l_next_free_rec) == 0) | |
467 | + return -ENODATA; | |
468 | + | |
469 | + mlog(0, "find xattr %s, hash = %u, index = %d in xattr tree\n", | |
470 | + name, name_hash, name_index); | |
471 | + | |
472 | + ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &first_hash, | |
473 | + &num_clusters, el); | |
474 | + if (ret) { | |
475 | + mlog_errno(ret); | |
476 | + goto out; | |
477 | + } | |
478 | + | |
479 | + BUG_ON(p_blkno == 0 || num_clusters == 0 || first_hash > name_hash); | |
480 | + | |
481 | + mlog(0, "find xattr extent rec %u clusters from %llu, the first hash " | |
482 | + "in the rec is %u\n", num_clusters, p_blkno, first_hash); | |
483 | + | |
484 | + ret = ocfs2_xattr_bucket_find(inode, name_index, name, name_hash, | |
485 | + p_blkno, first_hash, num_clusters, xs); | |
486 | + | |
487 | +out: | |
488 | + return ret; | |
489 | +} | |
490 | + | |
491 | static int ocfs2_iterate_xattr_buckets(struct inode *inode, | |
492 | u64 blkno, | |
493 | u32 clusters, | |
494 | -- | |
495 | 1.5.4.5 | |
496 |