diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
-rw-r--r-- | fs/btrfs/ioctl.c | 118 |
1 files changed, 108 insertions, 10 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5fdb2abc4fa7..2616f7ed4799 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/xattr.h> | 40 | #include <linux/xattr.h> |
41 | #include <linux/vmalloc.h> | 41 | #include <linux/vmalloc.h> |
42 | #include <linux/slab.h> | 42 | #include <linux/slab.h> |
43 | #include <linux/blkdev.h> | ||
43 | #include "compat.h" | 44 | #include "compat.h" |
44 | #include "ctree.h" | 45 | #include "ctree.h" |
45 | #include "disk-io.h" | 46 | #include "disk-io.h" |
@@ -80,6 +81,13 @@ static unsigned int btrfs_flags_to_ioctl(unsigned int flags) | |||
80 | iflags |= FS_NOATIME_FL; | 81 | iflags |= FS_NOATIME_FL; |
81 | if (flags & BTRFS_INODE_DIRSYNC) | 82 | if (flags & BTRFS_INODE_DIRSYNC) |
82 | iflags |= FS_DIRSYNC_FL; | 83 | iflags |= FS_DIRSYNC_FL; |
84 | if (flags & BTRFS_INODE_NODATACOW) | ||
85 | iflags |= FS_NOCOW_FL; | ||
86 | |||
87 | if ((flags & BTRFS_INODE_COMPRESS) && !(flags & BTRFS_INODE_NOCOMPRESS)) | ||
88 | iflags |= FS_COMPR_FL; | ||
89 | else if (flags & BTRFS_INODE_NOCOMPRESS) | ||
90 | iflags |= FS_NOCOMP_FL; | ||
83 | 91 | ||
84 | return iflags; | 92 | return iflags; |
85 | } | 93 | } |
@@ -138,6 +146,21 @@ static int btrfs_ioctl_getflags(struct file *file, void __user *arg) | |||
138 | return 0; | 146 | return 0; |
139 | } | 147 | } |
140 | 148 | ||
149 | static int check_flags(unsigned int flags) | ||
150 | { | ||
151 | if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ | ||
152 | FS_NOATIME_FL | FS_NODUMP_FL | \ | ||
153 | FS_SYNC_FL | FS_DIRSYNC_FL | \ | ||
154 | FS_NOCOMP_FL | FS_COMPR_FL | | ||
155 | FS_NOCOW_FL)) | ||
156 | return -EOPNOTSUPP; | ||
157 | |||
158 | if ((flags & FS_NOCOMP_FL) && (flags & FS_COMPR_FL)) | ||
159 | return -EINVAL; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
141 | static int btrfs_ioctl_setflags(struct file *file, void __user *arg) | 164 | static int btrfs_ioctl_setflags(struct file *file, void __user *arg) |
142 | { | 165 | { |
143 | struct inode *inode = file->f_path.dentry->d_inode; | 166 | struct inode *inode = file->f_path.dentry->d_inode; |
@@ -153,12 +176,11 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) | |||
153 | if (copy_from_user(&flags, arg, sizeof(flags))) | 176 | if (copy_from_user(&flags, arg, sizeof(flags))) |
154 | return -EFAULT; | 177 | return -EFAULT; |
155 | 178 | ||
156 | if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ | 179 | ret = check_flags(flags); |
157 | FS_NOATIME_FL | FS_NODUMP_FL | \ | 180 | if (ret) |
158 | FS_SYNC_FL | FS_DIRSYNC_FL)) | 181 | return ret; |
159 | return -EOPNOTSUPP; | ||
160 | 182 | ||
161 | if (!is_owner_or_cap(inode)) | 183 | if (!inode_owner_or_capable(inode)) |
162 | return -EACCES; | 184 | return -EACCES; |
163 | 185 | ||
164 | mutex_lock(&inode->i_mutex); | 186 | mutex_lock(&inode->i_mutex); |
@@ -200,7 +222,25 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) | |||
200 | ip->flags |= BTRFS_INODE_DIRSYNC; | 222 | ip->flags |= BTRFS_INODE_DIRSYNC; |
201 | else | 223 | else |
202 | ip->flags &= ~BTRFS_INODE_DIRSYNC; | 224 | ip->flags &= ~BTRFS_INODE_DIRSYNC; |
225 | if (flags & FS_NOCOW_FL) | ||
226 | ip->flags |= BTRFS_INODE_NODATACOW; | ||
227 | else | ||
228 | ip->flags &= ~BTRFS_INODE_NODATACOW; | ||
203 | 229 | ||
230 | /* | ||
231 | * The COMPRESS flag can only be changed by users, while the NOCOMPRESS | ||
232 | * flag may be changed automatically if compression code won't make | ||
233 | * things smaller. | ||
234 | */ | ||
235 | if (flags & FS_NOCOMP_FL) { | ||
236 | ip->flags &= ~BTRFS_INODE_COMPRESS; | ||
237 | ip->flags |= BTRFS_INODE_NOCOMPRESS; | ||
238 | } else if (flags & FS_COMPR_FL) { | ||
239 | ip->flags |= BTRFS_INODE_COMPRESS; | ||
240 | ip->flags &= ~BTRFS_INODE_NOCOMPRESS; | ||
241 | } else { | ||
242 | ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS); | ||
243 | } | ||
204 | 244 | ||
205 | trans = btrfs_join_transaction(root, 1); | 245 | trans = btrfs_join_transaction(root, 1); |
206 | BUG_ON(IS_ERR(trans)); | 246 | BUG_ON(IS_ERR(trans)); |
@@ -213,9 +253,11 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg) | |||
213 | btrfs_end_transaction(trans, root); | 253 | btrfs_end_transaction(trans, root); |
214 | 254 | ||
215 | mnt_drop_write(file->f_path.mnt); | 255 | mnt_drop_write(file->f_path.mnt); |
256 | |||
257 | ret = 0; | ||
216 | out_unlock: | 258 | out_unlock: |
217 | mutex_unlock(&inode->i_mutex); | 259 | mutex_unlock(&inode->i_mutex); |
218 | return 0; | 260 | return ret; |
219 | } | 261 | } |
220 | 262 | ||
221 | static int btrfs_ioctl_getversion(struct file *file, int __user *arg) | 263 | static int btrfs_ioctl_getversion(struct file *file, int __user *arg) |
@@ -225,6 +267,49 @@ static int btrfs_ioctl_getversion(struct file *file, int __user *arg) | |||
225 | return put_user(inode->i_generation, arg); | 267 | return put_user(inode->i_generation, arg); |
226 | } | 268 | } |
227 | 269 | ||
270 | static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg) | ||
271 | { | ||
272 | struct btrfs_root *root = fdentry(file)->d_sb->s_fs_info; | ||
273 | struct btrfs_fs_info *fs_info = root->fs_info; | ||
274 | struct btrfs_device *device; | ||
275 | struct request_queue *q; | ||
276 | struct fstrim_range range; | ||
277 | u64 minlen = ULLONG_MAX; | ||
278 | u64 num_devices = 0; | ||
279 | int ret; | ||
280 | |||
281 | if (!capable(CAP_SYS_ADMIN)) | ||
282 | return -EPERM; | ||
283 | |||
284 | mutex_lock(&fs_info->fs_devices->device_list_mutex); | ||
285 | list_for_each_entry(device, &fs_info->fs_devices->devices, dev_list) { | ||
286 | if (!device->bdev) | ||
287 | continue; | ||
288 | q = bdev_get_queue(device->bdev); | ||
289 | if (blk_queue_discard(q)) { | ||
290 | num_devices++; | ||
291 | minlen = min((u64)q->limits.discard_granularity, | ||
292 | minlen); | ||
293 | } | ||
294 | } | ||
295 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | ||
296 | if (!num_devices) | ||
297 | return -EOPNOTSUPP; | ||
298 | |||
299 | if (copy_from_user(&range, arg, sizeof(range))) | ||
300 | return -EFAULT; | ||
301 | |||
302 | range.minlen = max(range.minlen, minlen); | ||
303 | ret = btrfs_trim_fs(root, &range); | ||
304 | if (ret < 0) | ||
305 | return ret; | ||
306 | |||
307 | if (copy_to_user(arg, &range, sizeof(range))) | ||
308 | return -EFAULT; | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
228 | static noinline int create_subvol(struct btrfs_root *root, | 313 | static noinline int create_subvol(struct btrfs_root *root, |
229 | struct dentry *dentry, | 314 | struct dentry *dentry, |
230 | char *name, int namelen, | 315 | char *name, int namelen, |
@@ -294,6 +379,10 @@ static noinline int create_subvol(struct btrfs_root *root, | |||
294 | inode_item->nbytes = cpu_to_le64(root->leafsize); | 379 | inode_item->nbytes = cpu_to_le64(root->leafsize); |
295 | inode_item->mode = cpu_to_le32(S_IFDIR | 0755); | 380 | inode_item->mode = cpu_to_le32(S_IFDIR | 0755); |
296 | 381 | ||
382 | root_item.flags = 0; | ||
383 | root_item.byte_limit = 0; | ||
384 | inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); | ||
385 | |||
297 | btrfs_set_root_bytenr(&root_item, leaf->start); | 386 | btrfs_set_root_bytenr(&root_item, leaf->start); |
298 | btrfs_set_root_generation(&root_item, trans->transid); | 387 | btrfs_set_root_generation(&root_item, trans->transid); |
299 | btrfs_set_root_level(&root_item, 0); | 388 | btrfs_set_root_level(&root_item, 0); |
@@ -409,7 +498,9 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry, | |||
409 | if (ret) | 498 | if (ret) |
410 | goto fail; | 499 | goto fail; |
411 | 500 | ||
412 | btrfs_orphan_cleanup(pending_snapshot->snap); | 501 | ret = btrfs_orphan_cleanup(pending_snapshot->snap); |
502 | if (ret) | ||
503 | goto fail; | ||
413 | 504 | ||
414 | parent = dget_parent(dentry); | 505 | parent = dget_parent(dentry); |
415 | inode = btrfs_lookup_dentry(parent->d_inode, dentry); | 506 | inode = btrfs_lookup_dentry(parent->d_inode, dentry); |
@@ -1077,7 +1168,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, | |||
1077 | if (flags & ~BTRFS_SUBVOL_RDONLY) | 1168 | if (flags & ~BTRFS_SUBVOL_RDONLY) |
1078 | return -EOPNOTSUPP; | 1169 | return -EOPNOTSUPP; |
1079 | 1170 | ||
1080 | if (!is_owner_or_cap(inode)) | 1171 | if (!inode_owner_or_capable(inode)) |
1081 | return -EACCES; | 1172 | return -EACCES; |
1082 | 1173 | ||
1083 | down_write(&root->fs_info->subvol_sem); | 1174 | down_write(&root->fs_info->subvol_sem); |
@@ -2202,7 +2293,7 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) | |||
2202 | struct btrfs_ioctl_space_info space; | 2293 | struct btrfs_ioctl_space_info space; |
2203 | struct btrfs_ioctl_space_info *dest; | 2294 | struct btrfs_ioctl_space_info *dest; |
2204 | struct btrfs_ioctl_space_info *dest_orig; | 2295 | struct btrfs_ioctl_space_info *dest_orig; |
2205 | struct btrfs_ioctl_space_info *user_dest; | 2296 | struct btrfs_ioctl_space_info __user *user_dest; |
2206 | struct btrfs_space_info *info; | 2297 | struct btrfs_space_info *info; |
2207 | u64 types[] = {BTRFS_BLOCK_GROUP_DATA, | 2298 | u64 types[] = {BTRFS_BLOCK_GROUP_DATA, |
2208 | BTRFS_BLOCK_GROUP_SYSTEM, | 2299 | BTRFS_BLOCK_GROUP_SYSTEM, |
@@ -2348,12 +2439,17 @@ static noinline long btrfs_ioctl_start_sync(struct file *file, void __user *argp | |||
2348 | struct btrfs_root *root = BTRFS_I(file->f_dentry->d_inode)->root; | 2439 | struct btrfs_root *root = BTRFS_I(file->f_dentry->d_inode)->root; |
2349 | struct btrfs_trans_handle *trans; | 2440 | struct btrfs_trans_handle *trans; |
2350 | u64 transid; | 2441 | u64 transid; |
2442 | int ret; | ||
2351 | 2443 | ||
2352 | trans = btrfs_start_transaction(root, 0); | 2444 | trans = btrfs_start_transaction(root, 0); |
2353 | if (IS_ERR(trans)) | 2445 | if (IS_ERR(trans)) |
2354 | return PTR_ERR(trans); | 2446 | return PTR_ERR(trans); |
2355 | transid = trans->transid; | 2447 | transid = trans->transid; |
2356 | btrfs_commit_transaction_async(trans, root, 0); | 2448 | ret = btrfs_commit_transaction_async(trans, root, 0); |
2449 | if (ret) { | ||
2450 | btrfs_end_transaction(trans, root); | ||
2451 | return ret; | ||
2452 | } | ||
2357 | 2453 | ||
2358 | if (argp) | 2454 | if (argp) |
2359 | if (copy_to_user(argp, &transid, sizeof(transid))) | 2455 | if (copy_to_user(argp, &transid, sizeof(transid))) |
@@ -2388,6 +2484,8 @@ long btrfs_ioctl(struct file *file, unsigned int | |||
2388 | return btrfs_ioctl_setflags(file, argp); | 2484 | return btrfs_ioctl_setflags(file, argp); |
2389 | case FS_IOC_GETVERSION: | 2485 | case FS_IOC_GETVERSION: |
2390 | return btrfs_ioctl_getversion(file, argp); | 2486 | return btrfs_ioctl_getversion(file, argp); |
2487 | case FITRIM: | ||
2488 | return btrfs_ioctl_fitrim(file, argp); | ||
2391 | case BTRFS_IOC_SNAP_CREATE: | 2489 | case BTRFS_IOC_SNAP_CREATE: |
2392 | return btrfs_ioctl_snap_create(file, argp, 0); | 2490 | return btrfs_ioctl_snap_create(file, argp, 0); |
2393 | case BTRFS_IOC_SNAP_CREATE_V2: | 2491 | case BTRFS_IOC_SNAP_CREATE_V2: |