diff options
-rw-r--r-- | fs/btrfs/ctree.h | 2 | ||||
-rw-r--r-- | fs/btrfs/dir-item.c | 59 | ||||
-rw-r--r-- | fs/btrfs/inode.c | 24 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 10 | ||||
-rw-r--r-- | fs/btrfs/transaction.c | 2 |
5 files changed, 95 insertions, 2 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 44d9bc87e863..547b7b05727f 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -3283,6 +3283,8 @@ void btrfs_update_root_times(struct btrfs_trans_handle *trans, | |||
3283 | struct btrfs_root *root); | 3283 | struct btrfs_root *root); |
3284 | 3284 | ||
3285 | /* dir-item.c */ | 3285 | /* dir-item.c */ |
3286 | int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, | ||
3287 | const char *name, int name_len); | ||
3286 | int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, | 3288 | int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, |
3287 | struct btrfs_root *root, const char *name, | 3289 | struct btrfs_root *root, const char *name, |
3288 | int name_len, struct inode *dir, | 3290 | int name_len, struct inode *dir, |
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index c1a074d0696f..502c2158167c 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c | |||
@@ -213,6 +213,65 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans, | |||
213 | return btrfs_match_dir_item_name(root, path, name, name_len); | 213 | return btrfs_match_dir_item_name(root, path, name, name_len); |
214 | } | 214 | } |
215 | 215 | ||
216 | int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, | ||
217 | const char *name, int name_len) | ||
218 | { | ||
219 | int ret; | ||
220 | struct btrfs_key key; | ||
221 | struct btrfs_dir_item *di; | ||
222 | int data_size; | ||
223 | struct extent_buffer *leaf; | ||
224 | int slot; | ||
225 | struct btrfs_path *path; | ||
226 | |||
227 | |||
228 | path = btrfs_alloc_path(); | ||
229 | if (!path) | ||
230 | return -ENOMEM; | ||
231 | |||
232 | key.objectid = dir; | ||
233 | btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); | ||
234 | key.offset = btrfs_name_hash(name, name_len); | ||
235 | |||
236 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | ||
237 | |||
238 | /* return back any errors */ | ||
239 | if (ret < 0) | ||
240 | goto out; | ||
241 | |||
242 | /* nothing found, we're safe */ | ||
243 | if (ret > 0) { | ||
244 | ret = 0; | ||
245 | goto out; | ||
246 | } | ||
247 | |||
248 | /* we found an item, look for our name in the item */ | ||
249 | di = btrfs_match_dir_item_name(root, path, name, name_len); | ||
250 | if (di) { | ||
251 | /* our exact name was found */ | ||
252 | ret = -EEXIST; | ||
253 | goto out; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * see if there is room in the item to insert this | ||
258 | * name | ||
259 | */ | ||
260 | data_size = sizeof(*di) + name_len + sizeof(struct btrfs_item); | ||
261 | leaf = path->nodes[0]; | ||
262 | slot = path->slots[0]; | ||
263 | if (data_size + btrfs_item_size_nr(leaf, slot) + | ||
264 | sizeof(struct btrfs_item) > BTRFS_LEAF_DATA_SIZE(root)) { | ||
265 | ret = -EOVERFLOW; | ||
266 | } else { | ||
267 | /* plenty of insertion room */ | ||
268 | ret = 0; | ||
269 | } | ||
270 | out: | ||
271 | btrfs_free_path(path); | ||
272 | return ret; | ||
273 | } | ||
274 | |||
216 | /* | 275 | /* |
217 | * lookup a directory item based on index. 'dir' is the objectid | 276 | * lookup a directory item based on index. 'dir' is the objectid |
218 | * we're searching in, and 'mod' tells us if you plan on deleting the | 277 | * we're searching in, and 'mod' tells us if you plan on deleting the |
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2e6918c85b72..e95b1f90a1f6 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c | |||
@@ -4885,7 +4885,7 @@ int btrfs_add_link(struct btrfs_trans_handle *trans, | |||
4885 | ret = btrfs_insert_dir_item(trans, root, name, name_len, | 4885 | ret = btrfs_insert_dir_item(trans, root, name, name_len, |
4886 | parent_inode, &key, | 4886 | parent_inode, &key, |
4887 | btrfs_inode_type(inode), index); | 4887 | btrfs_inode_type(inode), index); |
4888 | if (ret == -EEXIST) | 4888 | if (ret == -EEXIST || ret == -EOVERFLOW) |
4889 | goto fail_dir_item; | 4889 | goto fail_dir_item; |
4890 | else if (ret) { | 4890 | else if (ret) { |
4891 | btrfs_abort_transaction(trans, root, ret); | 4891 | btrfs_abort_transaction(trans, root, ret); |
@@ -7336,6 +7336,28 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
7336 | if (S_ISDIR(old_inode->i_mode) && new_inode && | 7336 | if (S_ISDIR(old_inode->i_mode) && new_inode && |
7337 | new_inode->i_size > BTRFS_EMPTY_DIR_SIZE) | 7337 | new_inode->i_size > BTRFS_EMPTY_DIR_SIZE) |
7338 | return -ENOTEMPTY; | 7338 | return -ENOTEMPTY; |
7339 | |||
7340 | |||
7341 | /* check for collisions, even if the name isn't there */ | ||
7342 | ret = btrfs_check_dir_item_collision(root, new_dir->i_ino, | ||
7343 | new_dentry->d_name.name, | ||
7344 | new_dentry->d_name.len); | ||
7345 | |||
7346 | if (ret) { | ||
7347 | if (ret == -EEXIST) { | ||
7348 | /* we shouldn't get | ||
7349 | * eexist without a new_inode */ | ||
7350 | if (!new_inode) { | ||
7351 | WARN_ON(1); | ||
7352 | return ret; | ||
7353 | } | ||
7354 | } else { | ||
7355 | /* maybe -EOVERFLOW */ | ||
7356 | return ret; | ||
7357 | } | ||
7358 | } | ||
7359 | ret = 0; | ||
7360 | |||
7339 | /* | 7361 | /* |
7340 | * we're using rename to replace one file with another. | 7362 | * we're using rename to replace one file with another. |
7341 | * and the replacement file is large. Start IO on it now so | 7363 | * and the replacement file is large. Start IO on it now so |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 657d83ca9dea..d4608ab72b79 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -710,6 +710,16 @@ static noinline int btrfs_mksubvol(struct path *parent, | |||
710 | if (error) | 710 | if (error) |
711 | goto out_dput; | 711 | goto out_dput; |
712 | 712 | ||
713 | /* | ||
714 | * even if this name doesn't exist, we may get hash collisions. | ||
715 | * check for them now when we can safely fail | ||
716 | */ | ||
717 | error = btrfs_check_dir_item_collision(BTRFS_I(dir)->root, | ||
718 | dir->i_ino, name, | ||
719 | namelen); | ||
720 | if (error) | ||
721 | goto out_dput; | ||
722 | |||
713 | down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem); | 723 | down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem); |
714 | 724 | ||
715 | if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0) | 725 | if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0) |
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index e6509b92433b..87fac9a21ea5 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -1190,7 +1190,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, | |||
1190 | parent_inode, &key, | 1190 | parent_inode, &key, |
1191 | BTRFS_FT_DIR, index); | 1191 | BTRFS_FT_DIR, index); |
1192 | /* We have check then name at the beginning, so it is impossible. */ | 1192 | /* We have check then name at the beginning, so it is impossible. */ |
1193 | BUG_ON(ret == -EEXIST); | 1193 | BUG_ON(ret == -EEXIST || ret == -EOVERFLOW); |
1194 | if (ret) { | 1194 | if (ret) { |
1195 | btrfs_abort_transaction(trans, root, ret); | 1195 | btrfs_abort_transaction(trans, root, ret); |
1196 | goto fail; | 1196 | goto fail; |