diff options
Diffstat (limited to 'fs/btrfs/inode-item.c')
-rw-r--r-- | fs/btrfs/inode-item.c | 285 |
1 files changed, 274 insertions, 11 deletions
diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index a13cf1a96c73..48b8fda93132 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c | |||
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include "ctree.h" | 19 | #include "ctree.h" |
20 | #include "disk-io.h" | 20 | #include "disk-io.h" |
21 | #include "hash.h" | ||
21 | #include "transaction.h" | 22 | #include "transaction.h" |
22 | #include "print-tree.h" | 23 | #include "print-tree.h" |
23 | 24 | ||
@@ -50,18 +51,57 @@ static int find_name_in_backref(struct btrfs_path *path, const char *name, | |||
50 | return 0; | 51 | return 0; |
51 | } | 52 | } |
52 | 53 | ||
53 | struct btrfs_inode_ref * | 54 | int btrfs_find_name_in_ext_backref(struct btrfs_path *path, u64 ref_objectid, |
55 | const char *name, int name_len, | ||
56 | struct btrfs_inode_extref **extref_ret) | ||
57 | { | ||
58 | struct extent_buffer *leaf; | ||
59 | struct btrfs_inode_extref *extref; | ||
60 | unsigned long ptr; | ||
61 | unsigned long name_ptr; | ||
62 | u32 item_size; | ||
63 | u32 cur_offset = 0; | ||
64 | int ref_name_len; | ||
65 | |||
66 | leaf = path->nodes[0]; | ||
67 | item_size = btrfs_item_size_nr(leaf, path->slots[0]); | ||
68 | ptr = btrfs_item_ptr_offset(leaf, path->slots[0]); | ||
69 | |||
70 | /* | ||
71 | * Search all extended backrefs in this item. We're only | ||
72 | * looking through any collisions so most of the time this is | ||
73 | * just going to compare against one buffer. If all is well, | ||
74 | * we'll return success and the inode ref object. | ||
75 | */ | ||
76 | while (cur_offset < item_size) { | ||
77 | extref = (struct btrfs_inode_extref *) (ptr + cur_offset); | ||
78 | name_ptr = (unsigned long)(&extref->name); | ||
79 | ref_name_len = btrfs_inode_extref_name_len(leaf, extref); | ||
80 | |||
81 | if (ref_name_len == name_len && | ||
82 | btrfs_inode_extref_parent(leaf, extref) == ref_objectid && | ||
83 | (memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)) { | ||
84 | if (extref_ret) | ||
85 | *extref_ret = extref; | ||
86 | return 1; | ||
87 | } | ||
88 | |||
89 | cur_offset += ref_name_len + sizeof(*extref); | ||
90 | } | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static struct btrfs_inode_ref * | ||
54 | btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans, | 95 | btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans, |
55 | struct btrfs_root *root, | 96 | struct btrfs_root *root, |
56 | struct btrfs_path *path, | 97 | struct btrfs_path *path, |
57 | const char *name, int name_len, | 98 | const char *name, int name_len, |
58 | u64 inode_objectid, u64 ref_objectid, int mod) | 99 | u64 inode_objectid, u64 ref_objectid, int ins_len, |
100 | int cow) | ||
59 | { | 101 | { |
102 | int ret; | ||
60 | struct btrfs_key key; | 103 | struct btrfs_key key; |
61 | struct btrfs_inode_ref *ref; | 104 | struct btrfs_inode_ref *ref; |
62 | int ins_len = mod < 0 ? -1 : 0; | ||
63 | int cow = mod != 0; | ||
64 | int ret; | ||
65 | 105 | ||
66 | key.objectid = inode_objectid; | 106 | key.objectid = inode_objectid; |
67 | key.type = BTRFS_INODE_REF_KEY; | 107 | key.type = BTRFS_INODE_REF_KEY; |
@@ -77,13 +117,150 @@ btrfs_lookup_inode_ref(struct btrfs_trans_handle *trans, | |||
77 | return ref; | 117 | return ref; |
78 | } | 118 | } |
79 | 119 | ||
80 | int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, | 120 | /* Returns NULL if no extref found */ |
121 | struct btrfs_inode_extref * | ||
122 | btrfs_lookup_inode_extref(struct btrfs_trans_handle *trans, | ||
123 | struct btrfs_root *root, | ||
124 | struct btrfs_path *path, | ||
125 | const char *name, int name_len, | ||
126 | u64 inode_objectid, u64 ref_objectid, int ins_len, | ||
127 | int cow) | ||
128 | { | ||
129 | int ret; | ||
130 | struct btrfs_key key; | ||
131 | struct btrfs_inode_extref *extref; | ||
132 | |||
133 | key.objectid = inode_objectid; | ||
134 | key.type = BTRFS_INODE_EXTREF_KEY; | ||
135 | key.offset = btrfs_extref_hash(ref_objectid, name, name_len); | ||
136 | |||
137 | ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow); | ||
138 | if (ret < 0) | ||
139 | return ERR_PTR(ret); | ||
140 | if (ret > 0) | ||
141 | return NULL; | ||
142 | if (!btrfs_find_name_in_ext_backref(path, ref_objectid, name, name_len, &extref)) | ||
143 | return NULL; | ||
144 | return extref; | ||
145 | } | ||
146 | |||
147 | int btrfs_get_inode_ref_index(struct btrfs_trans_handle *trans, | ||
148 | struct btrfs_root *root, | ||
149 | struct btrfs_path *path, | ||
150 | const char *name, int name_len, | ||
151 | u64 inode_objectid, u64 ref_objectid, int mod, | ||
152 | u64 *ret_index) | ||
153 | { | ||
154 | struct btrfs_inode_ref *ref; | ||
155 | struct btrfs_inode_extref *extref; | ||
156 | int ins_len = mod < 0 ? -1 : 0; | ||
157 | int cow = mod != 0; | ||
158 | |||
159 | ref = btrfs_lookup_inode_ref(trans, root, path, name, name_len, | ||
160 | inode_objectid, ref_objectid, ins_len, | ||
161 | cow); | ||
162 | if (IS_ERR(ref)) | ||
163 | return PTR_ERR(ref); | ||
164 | |||
165 | if (ref != NULL) { | ||
166 | *ret_index = btrfs_inode_ref_index(path->nodes[0], ref); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | btrfs_release_path(path); | ||
171 | |||
172 | extref = btrfs_lookup_inode_extref(trans, root, path, name, | ||
173 | name_len, inode_objectid, | ||
174 | ref_objectid, ins_len, cow); | ||
175 | if (IS_ERR(extref)) | ||
176 | return PTR_ERR(extref); | ||
177 | |||
178 | if (extref) { | ||
179 | *ret_index = btrfs_inode_extref_index(path->nodes[0], extref); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | return -ENOENT; | ||
184 | } | ||
185 | |||
186 | int btrfs_del_inode_extref(struct btrfs_trans_handle *trans, | ||
81 | struct btrfs_root *root, | 187 | struct btrfs_root *root, |
82 | const char *name, int name_len, | 188 | const char *name, int name_len, |
83 | u64 inode_objectid, u64 ref_objectid, u64 *index) | 189 | u64 inode_objectid, u64 ref_objectid, u64 *index) |
84 | { | 190 | { |
85 | struct btrfs_path *path; | 191 | struct btrfs_path *path; |
86 | struct btrfs_key key; | 192 | struct btrfs_key key; |
193 | struct btrfs_inode_extref *extref; | ||
194 | struct extent_buffer *leaf; | ||
195 | int ret; | ||
196 | int del_len = name_len + sizeof(*extref); | ||
197 | unsigned long ptr; | ||
198 | unsigned long item_start; | ||
199 | u32 item_size; | ||
200 | |||
201 | key.objectid = inode_objectid; | ||
202 | btrfs_set_key_type(&key, BTRFS_INODE_EXTREF_KEY); | ||
203 | key.offset = btrfs_extref_hash(ref_objectid, name, name_len); | ||
204 | |||
205 | path = btrfs_alloc_path(); | ||
206 | if (!path) | ||
207 | return -ENOMEM; | ||
208 | |||
209 | path->leave_spinning = 1; | ||
210 | |||
211 | ret = btrfs_search_slot(trans, root, &key, path, -1, 1); | ||
212 | if (ret > 0) | ||
213 | ret = -ENOENT; | ||
214 | if (ret < 0) | ||
215 | goto out; | ||
216 | |||
217 | /* | ||
218 | * Sanity check - did we find the right item for this name? | ||
219 | * This should always succeed so error here will make the FS | ||
220 | * readonly. | ||
221 | */ | ||
222 | if (!btrfs_find_name_in_ext_backref(path, ref_objectid, | ||
223 | name, name_len, &extref)) { | ||
224 | btrfs_std_error(root->fs_info, -ENOENT); | ||
225 | ret = -EROFS; | ||
226 | goto out; | ||
227 | } | ||
228 | |||
229 | leaf = path->nodes[0]; | ||
230 | item_size = btrfs_item_size_nr(leaf, path->slots[0]); | ||
231 | if (index) | ||
232 | *index = btrfs_inode_extref_index(leaf, extref); | ||
233 | |||
234 | if (del_len == item_size) { | ||
235 | /* | ||
236 | * Common case only one ref in the item, remove the | ||
237 | * whole item. | ||
238 | */ | ||
239 | ret = btrfs_del_item(trans, root, path); | ||
240 | goto out; | ||
241 | } | ||
242 | |||
243 | ptr = (unsigned long)extref; | ||
244 | item_start = btrfs_item_ptr_offset(leaf, path->slots[0]); | ||
245 | |||
246 | memmove_extent_buffer(leaf, ptr, ptr + del_len, | ||
247 | item_size - (ptr + del_len - item_start)); | ||
248 | |||
249 | btrfs_truncate_item(trans, root, path, item_size - del_len, 1); | ||
250 | |||
251 | out: | ||
252 | btrfs_free_path(path); | ||
253 | |||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, | ||
258 | struct btrfs_root *root, | ||
259 | const char *name, int name_len, | ||
260 | u64 inode_objectid, u64 ref_objectid, u64 *index) | ||
261 | { | ||
262 | struct btrfs_path *path; | ||
263 | struct btrfs_key key; | ||
87 | struct btrfs_inode_ref *ref; | 264 | struct btrfs_inode_ref *ref; |
88 | struct extent_buffer *leaf; | 265 | struct extent_buffer *leaf; |
89 | unsigned long ptr; | 266 | unsigned long ptr; |
@@ -91,6 +268,7 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, | |||
91 | u32 item_size; | 268 | u32 item_size; |
92 | u32 sub_item_len; | 269 | u32 sub_item_len; |
93 | int ret; | 270 | int ret; |
271 | int search_ext_refs = 0; | ||
94 | int del_len = name_len + sizeof(*ref); | 272 | int del_len = name_len + sizeof(*ref); |
95 | 273 | ||
96 | key.objectid = inode_objectid; | 274 | key.objectid = inode_objectid; |
@@ -106,12 +284,14 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, | |||
106 | ret = btrfs_search_slot(trans, root, &key, path, -1, 1); | 284 | ret = btrfs_search_slot(trans, root, &key, path, -1, 1); |
107 | if (ret > 0) { | 285 | if (ret > 0) { |
108 | ret = -ENOENT; | 286 | ret = -ENOENT; |
287 | search_ext_refs = 1; | ||
109 | goto out; | 288 | goto out; |
110 | } else if (ret < 0) { | 289 | } else if (ret < 0) { |
111 | goto out; | 290 | goto out; |
112 | } | 291 | } |
113 | if (!find_name_in_backref(path, name, name_len, &ref)) { | 292 | if (!find_name_in_backref(path, name, name_len, &ref)) { |
114 | ret = -ENOENT; | 293 | ret = -ENOENT; |
294 | search_ext_refs = 1; | ||
115 | goto out; | 295 | goto out; |
116 | } | 296 | } |
117 | leaf = path->nodes[0]; | 297 | leaf = path->nodes[0]; |
@@ -129,8 +309,78 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, | |||
129 | item_start = btrfs_item_ptr_offset(leaf, path->slots[0]); | 309 | item_start = btrfs_item_ptr_offset(leaf, path->slots[0]); |
130 | memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, | 310 | memmove_extent_buffer(leaf, ptr, ptr + sub_item_len, |
131 | item_size - (ptr + sub_item_len - item_start)); | 311 | item_size - (ptr + sub_item_len - item_start)); |
132 | btrfs_truncate_item(trans, root, path, | 312 | btrfs_truncate_item(trans, root, path, item_size - sub_item_len, 1); |
133 | item_size - sub_item_len, 1); | 313 | out: |
314 | btrfs_free_path(path); | ||
315 | |||
316 | if (search_ext_refs) { | ||
317 | /* | ||
318 | * No refs were found, or we could not find the | ||
319 | * name in our ref array. Find and remove the extended | ||
320 | * inode ref then. | ||
321 | */ | ||
322 | return btrfs_del_inode_extref(trans, root, name, name_len, | ||
323 | inode_objectid, ref_objectid, index); | ||
324 | } | ||
325 | |||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | /* | ||
330 | * btrfs_insert_inode_extref() - Inserts an extended inode ref into a tree. | ||
331 | * | ||
332 | * The caller must have checked against BTRFS_LINK_MAX already. | ||
333 | */ | ||
334 | static int btrfs_insert_inode_extref(struct btrfs_trans_handle *trans, | ||
335 | struct btrfs_root *root, | ||
336 | const char *name, int name_len, | ||
337 | u64 inode_objectid, u64 ref_objectid, u64 index) | ||
338 | { | ||
339 | struct btrfs_inode_extref *extref; | ||
340 | int ret; | ||
341 | int ins_len = name_len + sizeof(*extref); | ||
342 | unsigned long ptr; | ||
343 | struct btrfs_path *path; | ||
344 | struct btrfs_key key; | ||
345 | struct extent_buffer *leaf; | ||
346 | struct btrfs_item *item; | ||
347 | |||
348 | key.objectid = inode_objectid; | ||
349 | key.type = BTRFS_INODE_EXTREF_KEY; | ||
350 | key.offset = btrfs_extref_hash(ref_objectid, name, name_len); | ||
351 | |||
352 | path = btrfs_alloc_path(); | ||
353 | if (!path) | ||
354 | return -ENOMEM; | ||
355 | |||
356 | path->leave_spinning = 1; | ||
357 | ret = btrfs_insert_empty_item(trans, root, path, &key, | ||
358 | ins_len); | ||
359 | if (ret == -EEXIST) { | ||
360 | if (btrfs_find_name_in_ext_backref(path, ref_objectid, | ||
361 | name, name_len, NULL)) | ||
362 | goto out; | ||
363 | |||
364 | btrfs_extend_item(trans, root, path, ins_len); | ||
365 | ret = 0; | ||
366 | } | ||
367 | if (ret < 0) | ||
368 | goto out; | ||
369 | |||
370 | leaf = path->nodes[0]; | ||
371 | item = btrfs_item_nr(leaf, path->slots[0]); | ||
372 | ptr = (unsigned long)btrfs_item_ptr(leaf, path->slots[0], char); | ||
373 | ptr += btrfs_item_size(leaf, item) - ins_len; | ||
374 | extref = (struct btrfs_inode_extref *)ptr; | ||
375 | |||
376 | btrfs_set_inode_extref_name_len(path->nodes[0], extref, name_len); | ||
377 | btrfs_set_inode_extref_index(path->nodes[0], extref, index); | ||
378 | btrfs_set_inode_extref_parent(path->nodes[0], extref, ref_objectid); | ||
379 | |||
380 | ptr = (unsigned long)&extref->name; | ||
381 | write_extent_buffer(path->nodes[0], name, ptr, name_len); | ||
382 | btrfs_mark_buffer_dirty(path->nodes[0]); | ||
383 | |||
134 | out: | 384 | out: |
135 | btrfs_free_path(path); | 385 | btrfs_free_path(path); |
136 | return ret; | 386 | return ret; |
@@ -191,6 +441,19 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, | |||
191 | 441 | ||
192 | out: | 442 | out: |
193 | btrfs_free_path(path); | 443 | btrfs_free_path(path); |
444 | |||
445 | if (ret == -EMLINK) { | ||
446 | struct btrfs_super_block *disk_super = root->fs_info->super_copy; | ||
447 | /* We ran out of space in the ref array. Need to | ||
448 | * add an extended ref. */ | ||
449 | if (btrfs_super_incompat_flags(disk_super) | ||
450 | & BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF) | ||
451 | ret = btrfs_insert_inode_extref(trans, root, name, | ||
452 | name_len, | ||
453 | inode_objectid, | ||
454 | ref_objectid, index); | ||
455 | } | ||
456 | |||
194 | return ret; | 457 | return ret; |
195 | } | 458 | } |
196 | 459 | ||