aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/dir-item.c59
-rw-r--r--fs/btrfs/inode.c24
-rw-r--r--fs/btrfs/ioctl.c10
-rw-r--r--fs/btrfs/transaction.c2
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 */
3286int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
3287 const char *name, int name_len);
3286int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, 3288int 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
216int 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 }
270out:
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;