aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ioctl.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2008-10-09 13:39:39 -0400
committerChris Mason <chris.mason@oracle.com>2008-10-09 13:39:39 -0400
commitcb8e70901d36f32017614f16d2cf7cc089544574 (patch)
treebc31116dcb5bcdd6f2677a0c83b824e77c1ef097 /fs/btrfs/ioctl.c
parent833023e46c2a0180ff07d90252c24cb3fdea811d (diff)
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 <hch@lst.de>
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r--fs/btrfs/ioctl.c102
1 files changed, 91 insertions, 11 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 50c8a066d1f0..3d85f18bbba6 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -21,6 +21,7 @@
21#include <linux/buffer_head.h> 21#include <linux/buffer_head.h>
22#include <linux/file.h> 22#include <linux/file.h>
23#include <linux/fs.h> 23#include <linux/fs.h>
24#include <linux/fsnotify.h>
24#include <linux/pagemap.h> 25#include <linux/pagemap.h>
25#include <linux/highmem.h> 26#include <linux/highmem.h>
26#include <linux/time.h> 27#include <linux/time.h>
@@ -28,12 +29,15 @@
28#include <linux/string.h> 29#include <linux/string.h>
29#include <linux/smp_lock.h> 30#include <linux/smp_lock.h>
30#include <linux/backing-dev.h> 31#include <linux/backing-dev.h>
32#include <linux/mount.h>
31#include <linux/mpage.h> 33#include <linux/mpage.h>
34#include <linux/namei.h>
32#include <linux/swap.h> 35#include <linux/swap.h>
33#include <linux/writeback.h> 36#include <linux/writeback.h>
34#include <linux/statfs.h> 37#include <linux/statfs.h>
35#include <linux/compat.h> 38#include <linux/compat.h>
36#include <linux/bit_spinlock.h> 39#include <linux/bit_spinlock.h>
40#include <linux/security.h>
37#include <linux/version.h> 41#include <linux/version.h>
38#include <linux/xattr.h> 42#include <linux/xattr.h>
39#include <linux/vmalloc.h> 43#include <linux/vmalloc.h>
@@ -48,8 +52,9 @@
48 52
49 53
50 54
51static noinline int create_subvol(struct btrfs_root *root, char *name, 55static noinline int create_subvol(struct btrfs_root *root,
52 int namelen) 56 struct dentry *dentry,
57 char *name, int namelen)
53{ 58{
54 struct btrfs_trans_handle *trans; 59 struct btrfs_trans_handle *trans;
55 struct btrfs_key key; 60 struct btrfs_key key;
@@ -151,14 +156,11 @@ static noinline int create_subvol(struct btrfs_root *root, char *name,
151 trans = btrfs_start_transaction(new_root, 1); 156 trans = btrfs_start_transaction(new_root, 1);
152 BUG_ON(!trans); 157 BUG_ON(!trans);
153 158
154 ret = btrfs_create_subvol_root(new_root, trans, new_dirid, 159 ret = btrfs_create_subvol_root(new_root, dentry, trans, new_dirid,
155 BTRFS_I(dir)->block_group); 160 BTRFS_I(dir)->block_group);
156 if (ret) 161 if (ret)
157 goto fail; 162 goto fail;
158 163
159 /* Invalidate existing dcache entry for new subvolume. */
160 btrfs_invalidate_dcache_root(root, name, namelen);
161
162fail: 164fail:
163 nr = trans->blocks_used; 165 nr = trans->blocks_used;
164 err = btrfs_commit_transaction(trans, new_root); 166 err = btrfs_commit_transaction(trans, new_root);
@@ -210,6 +212,79 @@ fail_unlock:
210 return ret; 212 return ret;
211} 213}
212 214
215/* copy of may_create in fs/namei.c() */
216static 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 */
230static 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);
278out_drop_write:
279 mnt_drop_write(parent->mnt);
280out_dput:
281 dput(dentry);
282out_unlock:
283 mutex_unlock(&parent->dentry->d_inode->i_mutex);
284 return error;
285}
286
287
213int btrfs_defrag_file(struct file *file) 288int btrfs_defrag_file(struct file *file)
214{ 289{
215 struct inode *inode = fdentry(file)->d_inode; 290 struct inode *inode = fdentry(file)->d_inode;
@@ -395,9 +470,10 @@ out:
395 return ret; 470 return ret;
396} 471}
397 472
398static noinline int btrfs_ioctl_snap_create(struct btrfs_root *root, 473static noinline int btrfs_ioctl_snap_create(struct file *file,
399 void __user *arg) 474 void __user *arg)
400{ 475{
476 struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
401 struct btrfs_ioctl_vol_args *vol_args; 477 struct btrfs_ioctl_vol_args *vol_args;
402 struct btrfs_dir_item *di; 478 struct btrfs_dir_item *di;
403 struct btrfs_path *path; 479 struct btrfs_path *path;
@@ -444,10 +520,14 @@ static noinline int btrfs_ioctl_snap_create(struct btrfs_root *root,
444 goto out; 520 goto out;
445 } 521 }
446 522
447 if (root == root->fs_info->tree_root) 523 if (root == root->fs_info->tree_root) {
448 ret = create_subvol(root, vol_args->name, namelen); 524 ret = btrfs_mksubvol(&file->f_path, vol_args->name,
449 else 525 file->f_path.dentry->d_inode->i_mode,
526 namelen);
527 } else {
450 ret = create_snapshot(root, vol_args->name, namelen); 528 ret = create_snapshot(root, vol_args->name, namelen);
529 }
530
451out: 531out:
452 kfree(vol_args); 532 kfree(vol_args);
453 return ret; 533 return ret;
@@ -761,7 +841,7 @@ long btrfs_ioctl(struct file *file, unsigned int
761 841
762 switch (cmd) { 842 switch (cmd) {
763 case BTRFS_IOC_SNAP_CREATE: 843 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);
765 case BTRFS_IOC_DEFRAG: 845 case BTRFS_IOC_DEFRAG:
766 return btrfs_ioctl_defrag(file); 846 return btrfs_ioctl_defrag(file);
767 case BTRFS_IOC_RESIZE: 847 case BTRFS_IOC_RESIZE: