Skip to content

Commit cb8e709

Browse files
Christoph Hellwigchrismason-xx
authored andcommitted
Btrfs: Fix subvolume creation locking rules
Creating a subvolume is in many ways like a normal VFS ->mkdir, and we really need to play with the VFS topology locking rules. So instead of just creating the snapshot on disk and then later getting rid of confliting aliases do it correctly from the start. This will become especially important once we allow for subvolumes anywhere in the tree, and not just below a hidden root. Note that snapshots will need the same treatment, but do to the delay in creating them we can't do it currently. Chris promised to fix that issue, so I'll wait on that. Signed-off-by: Christoph Hellwig <[email protected]>
1 parent 833023e commit cb8e709

File tree

3 files changed

+100
-14
lines changed

3 files changed

+100
-14
lines changed

fs/btrfs/ctree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1791,7 +1791,7 @@ int btrfs_start_delalloc_inodes(struct btrfs_root *root);
17911791
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end);
17921792
int btrfs_writepages(struct address_space *mapping,
17931793
struct writeback_control *wbc);
1794-
int btrfs_create_subvol_root(struct btrfs_root *new_root,
1794+
int btrfs_create_subvol_root(struct btrfs_root *new_root, struct dentry *dentry,
17951795
struct btrfs_trans_handle *trans, u64 new_dirid,
17961796
struct btrfs_block_group_cache *block_group);
17971797

fs/btrfs/inode.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3420,11 +3420,12 @@ void btrfs_invalidate_dcache_root(struct btrfs_root *root, char *name,
34203420
/*
34213421
* create a new subvolume directory/inode (helper for the ioctl).
34223422
*/
3423-
int btrfs_create_subvol_root(struct btrfs_root *new_root,
3423+
int btrfs_create_subvol_root(struct btrfs_root *new_root, struct dentry *dentry,
34243424
struct btrfs_trans_handle *trans, u64 new_dirid,
34253425
struct btrfs_block_group_cache *block_group)
34263426
{
34273427
struct inode *inode;
3428+
int error;
34283429
u64 index = 0;
34293430

34303431
inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, new_dirid,
@@ -3438,7 +3439,12 @@ int btrfs_create_subvol_root(struct btrfs_root *new_root,
34383439
inode->i_nlink = 1;
34393440
btrfs_i_size_write(inode, 0);
34403441

3441-
return btrfs_update_inode(trans, new_root, inode);
3442+
error = btrfs_update_inode(trans, new_root, inode);
3443+
if (error)
3444+
return error;
3445+
3446+
d_instantiate(dentry, inode);
3447+
return 0;
34423448
}
34433449

34443450
/* helper function for file defrag and space balancing. This

fs/btrfs/ioctl.c

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,23 @@
2121
#include <linux/buffer_head.h>
2222
#include <linux/file.h>
2323
#include <linux/fs.h>
24+
#include <linux/fsnotify.h>
2425
#include <linux/pagemap.h>
2526
#include <linux/highmem.h>
2627
#include <linux/time.h>
2728
#include <linux/init.h>
2829
#include <linux/string.h>
2930
#include <linux/smp_lock.h>
3031
#include <linux/backing-dev.h>
32+
#include <linux/mount.h>
3133
#include <linux/mpage.h>
34+
#include <linux/namei.h>
3235
#include <linux/swap.h>
3336
#include <linux/writeback.h>
3437
#include <linux/statfs.h>
3538
#include <linux/compat.h>
3639
#include <linux/bit_spinlock.h>
40+
#include <linux/security.h>
3741
#include <linux/version.h>
3842
#include <linux/xattr.h>
3943
#include <linux/vmalloc.h>
@@ -48,8 +52,9 @@
4852

4953

5054

51-
static noinline int create_subvol(struct btrfs_root *root, char *name,
52-
int namelen)
55+
static noinline int create_subvol(struct btrfs_root *root,
56+
struct dentry *dentry,
57+
char *name, int namelen)
5358
{
5459
struct btrfs_trans_handle *trans;
5560
struct btrfs_key key;
@@ -151,14 +156,11 @@ static noinline int create_subvol(struct btrfs_root *root, char *name,
151156
trans = btrfs_start_transaction(new_root, 1);
152157
BUG_ON(!trans);
153158

154-
ret = btrfs_create_subvol_root(new_root, trans, new_dirid,
159+
ret = btrfs_create_subvol_root(new_root, dentry, trans, new_dirid,
155160
BTRFS_I(dir)->block_group);
156161
if (ret)
157162
goto fail;
158163

159-
/* Invalidate existing dcache entry for new subvolume. */
160-
btrfs_invalidate_dcache_root(root, name, namelen);
161-
162164
fail:
163165
nr = trans->blocks_used;
164166
err = btrfs_commit_transaction(trans, new_root);
@@ -210,6 +212,79 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
210212
return ret;
211213
}
212214

215+
/* copy of may_create in fs/namei.c() */
216+
static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
217+
{
218+
if (child->d_inode)
219+
return -EEXIST;
220+
if (IS_DEADDIR(dir))
221+
return -ENOENT;
222+
return inode_permission(dir, MAY_WRITE | MAY_EXEC);
223+
}
224+
225+
/*
226+
* Create a new subvolume below @parent. This is largely modeled after
227+
* sys_mkdirat and vfs_mkdir, but we only do a single component lookup
228+
* inside this filesystem so it's quite a bit simpler.
229+
*/
230+
static noinline int btrfs_mksubvol(struct path *parent, char *name,
231+
int mode, int namelen)
232+
{
233+
struct dentry *dentry;
234+
int error;
235+
236+
mutex_lock_nested(&parent->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
237+
238+
dentry = lookup_one_len(name, parent->dentry, namelen);
239+
error = PTR_ERR(dentry);
240+
if (IS_ERR(dentry))
241+
goto out_unlock;
242+
243+
error = -EEXIST;
244+
if (dentry->d_inode)
245+
goto out_dput;
246+
247+
if (!IS_POSIXACL(parent->dentry->d_inode))
248+
mode &= ~current->fs->umask;
249+
error = mnt_want_write(parent->mnt);
250+
if (error)
251+
goto out_dput;
252+
253+
error = btrfs_may_create(parent->dentry->d_inode, dentry);
254+
if (error)
255+
goto out_drop_write;
256+
257+
mode &= (S_IRWXUGO|S_ISVTX);
258+
error = security_inode_mkdir(parent->dentry->d_inode, dentry, mode);
259+
if (error)
260+
goto out_drop_write;
261+
262+
/*
263+
* Actually perform the low-level subvolume creation after all
264+
* this VFS fuzz.
265+
*
266+
* Eventually we want to pass in an inode under which we create this
267+
* subvolume, but for now all are under the filesystem root.
268+
*
269+
* Also we should pass on the mode eventually to allow creating new
270+
* subvolume with specific mode bits.
271+
*/
272+
error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root, dentry,
273+
name, namelen);
274+
if (error)
275+
goto out_drop_write;
276+
277+
fsnotify_mkdir(parent->dentry->d_inode, dentry);
278+
out_drop_write:
279+
mnt_drop_write(parent->mnt);
280+
out_dput:
281+
dput(dentry);
282+
out_unlock:
283+
mutex_unlock(&parent->dentry->d_inode->i_mutex);
284+
return error;
285+
}
286+
287+
213288
int btrfs_defrag_file(struct file *file)
214289
{
215290
struct inode *inode = fdentry(file)->d_inode;
@@ -395,9 +470,10 @@ static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg)
395470
return ret;
396471
}
397472

398-
static noinline int btrfs_ioctl_snap_create(struct btrfs_root *root,
473+
static noinline int btrfs_ioctl_snap_create(struct file *file,
399474
void __user *arg)
400475
{
476+
struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
401477
struct btrfs_ioctl_vol_args *vol_args;
402478
struct btrfs_dir_item *di;
403479
struct btrfs_path *path;
@@ -444,10 +520,14 @@ static noinline int btrfs_ioctl_snap_create(struct btrfs_root *root,
444520
goto out;
445521
}
446522

447-
if (root == root->fs_info->tree_root)
448-
ret = create_subvol(root, vol_args->name, namelen);
449-
else
523+
if (root == root->fs_info->tree_root) {
524+
ret = btrfs_mksubvol(&file->f_path, vol_args->name,
525+
file->f_path.dentry->d_inode->i_mode,
526+
namelen);
527+
} else {
450528
ret = create_snapshot(root, vol_args->name, namelen);
529+
}
530+
451531
out:
452532
kfree(vol_args);
453533
return ret;
@@ -761,7 +841,7 @@ long btrfs_ioctl(struct file *file, unsigned int
761841

762842
switch (cmd) {
763843
case BTRFS_IOC_SNAP_CREATE:
764-
return btrfs_ioctl_snap_create(root, (void __user *)arg);
844+
return btrfs_ioctl_snap_create(file, (void __user *)arg);
765845
case BTRFS_IOC_DEFRAG:
766846
return btrfs_ioctl_defrag(file);
767847
case BTRFS_IOC_RESIZE:

0 commit comments

Comments
 (0)