|
21 | 21 | #include <linux/buffer_head.h>
|
22 | 22 | #include <linux/file.h>
|
23 | 23 | #include <linux/fs.h>
|
| 24 | +#include <linux/fsnotify.h> |
24 | 25 | #include <linux/pagemap.h>
|
25 | 26 | #include <linux/highmem.h>
|
26 | 27 | #include <linux/time.h>
|
27 | 28 | #include <linux/init.h>
|
28 | 29 | #include <linux/string.h>
|
29 | 30 | #include <linux/smp_lock.h>
|
30 | 31 | #include <linux/backing-dev.h>
|
| 32 | +#include <linux/mount.h> |
31 | 33 | #include <linux/mpage.h>
|
| 34 | +#include <linux/namei.h> |
32 | 35 | #include <linux/swap.h>
|
33 | 36 | #include <linux/writeback.h>
|
34 | 37 | #include <linux/statfs.h>
|
35 | 38 | #include <linux/compat.h>
|
36 | 39 | #include <linux/bit_spinlock.h>
|
| 40 | +#include <linux/security.h> |
37 | 41 | #include <linux/version.h>
|
38 | 42 | #include <linux/xattr.h>
|
39 | 43 | #include <linux/vmalloc.h>
|
|
48 | 52 |
|
49 | 53 |
|
50 | 54 |
|
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) |
53 | 58 | {
|
54 | 59 | struct btrfs_trans_handle *trans;
|
55 | 60 | struct btrfs_key key;
|
@@ -151,14 +156,11 @@ static noinline int create_subvol(struct btrfs_root *root, char *name,
|
151 | 156 | trans = btrfs_start_transaction(new_root, 1);
|
152 | 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 | 160 | BTRFS_I(dir)->block_group);
|
156 | 161 | if (ret)
|
157 | 162 | goto fail;
|
158 | 163 |
|
159 |
| - /* Invalidate existing dcache entry for new subvolume. */ |
160 |
| - btrfs_invalidate_dcache_root(root, name, namelen); |
161 |
| - |
162 | 164 | fail:
|
163 | 165 | nr = trans->blocks_used;
|
164 | 166 | err = btrfs_commit_transaction(trans, new_root);
|
@@ -210,6 +212,79 @@ static int create_snapshot(struct btrfs_root *root, char *name, int namelen)
|
210 | 212 | return ret;
|
211 | 213 | }
|
212 | 214 |
|
| 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 | + |
213 | 288 | int btrfs_defrag_file(struct file *file)
|
214 | 289 | {
|
215 | 290 | struct inode *inode = fdentry(file)->d_inode;
|
@@ -395,9 +470,10 @@ static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg)
|
395 | 470 | return ret;
|
396 | 471 | }
|
397 | 472 |
|
398 |
| -static noinline int btrfs_ioctl_snap_create(struct btrfs_root *root, |
| 473 | +static noinline int btrfs_ioctl_snap_create(struct file *file, |
399 | 474 | void __user *arg)
|
400 | 475 | {
|
| 476 | + struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; |
401 | 477 | struct btrfs_ioctl_vol_args *vol_args;
|
402 | 478 | struct btrfs_dir_item *di;
|
403 | 479 | struct btrfs_path *path;
|
@@ -444,10 +520,14 @@ static noinline int btrfs_ioctl_snap_create(struct btrfs_root *root,
|
444 | 520 | goto out;
|
445 | 521 | }
|
446 | 522 |
|
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 { |
450 | 528 | ret = create_snapshot(root, vol_args->name, namelen);
|
| 529 | + } |
| 530 | + |
451 | 531 | out:
|
452 | 532 | kfree(vol_args);
|
453 | 533 | return ret;
|
@@ -761,7 +841,7 @@ long btrfs_ioctl(struct file *file, unsigned int
|
761 | 841 |
|
762 | 842 | switch (cmd) {
|
763 | 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 | 845 | case BTRFS_IOC_DEFRAG:
|
766 | 846 | return btrfs_ioctl_defrag(file);
|
767 | 847 | case BTRFS_IOC_RESIZE:
|
|
0 commit comments