diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-22 14:23:35 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-22 14:23:35 -0500 |
commit | 391f2a16b74b95da2f05a607f53213fc8ed24b8e (patch) | |
tree | 93bfd1906778d7ff24f6ce7c03f2e7c0f91caef5 /fs/ext4/ioctl.c | |
parent | d5ffdf8b4ac6e6db5702ba31870c476d5fa30660 (diff) | |
parent | 68ce7bfcd995a8a393b1b14fa67dbc16fa3dc784 (diff) |
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 updates from Ted Ts'o:
"Some locking and page fault bug fixes from Jan Kara, some ext4
encryption fixes from me, and Li Xi's Project Quota commits"
* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
fs: clean up the flags definition in uapi/linux/fs.h
ext4: add FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
ext4: add project quota support
ext4: adds project ID support
ext4 crypto: simplify interfaces to directory entry insert functions
ext4 crypto: add missing locking for keyring_key access
ext4: use pre-zeroed blocks for DAX page faults
ext4: implement allocation of pre-zeroed blocks
ext4: provide ext4_issue_zeroout()
ext4: get rid of EXT4_GET_BLOCKS_NO_LOCK flag
ext4: document lock ordering
ext4: fix races of writeback with punch hole and zero range
ext4: fix races between buffered IO and collapse / insert range
ext4: move unlocked dio protection from ext4_alloc_file_blocks()
ext4: fix races between page faults and hole punching
Diffstat (limited to 'fs/ext4/ioctl.c')
-rw-r--r-- | fs/ext4/ioctl.c | 376 |
1 files changed, 289 insertions, 87 deletions
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 5e872fd40e5e..2b0cb84255eb 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/mount.h> | 14 | #include <linux/mount.h> |
15 | #include <linux/file.h> | 15 | #include <linux/file.h> |
16 | #include <linux/random.h> | 16 | #include <linux/random.h> |
17 | #include <linux/quotaops.h> | ||
17 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
18 | #include "ext4_jbd2.h" | 19 | #include "ext4_jbd2.h" |
19 | #include "ext4.h" | 20 | #include "ext4.h" |
@@ -202,6 +203,238 @@ static int uuid_is_zero(__u8 u[16]) | |||
202 | return 1; | 203 | return 1; |
203 | } | 204 | } |
204 | 205 | ||
206 | static int ext4_ioctl_setflags(struct inode *inode, | ||
207 | unsigned int flags) | ||
208 | { | ||
209 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
210 | handle_t *handle = NULL; | ||
211 | int err = EPERM, migrate = 0; | ||
212 | struct ext4_iloc iloc; | ||
213 | unsigned int oldflags, mask, i; | ||
214 | unsigned int jflag; | ||
215 | |||
216 | /* Is it quota file? Do not allow user to mess with it */ | ||
217 | if (IS_NOQUOTA(inode)) | ||
218 | goto flags_out; | ||
219 | |||
220 | oldflags = ei->i_flags; | ||
221 | |||
222 | /* The JOURNAL_DATA flag is modifiable only by root */ | ||
223 | jflag = flags & EXT4_JOURNAL_DATA_FL; | ||
224 | |||
225 | /* | ||
226 | * The IMMUTABLE and APPEND_ONLY flags can only be changed by | ||
227 | * the relevant capability. | ||
228 | * | ||
229 | * This test looks nicer. Thanks to Pauline Middelink | ||
230 | */ | ||
231 | if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { | ||
232 | if (!capable(CAP_LINUX_IMMUTABLE)) | ||
233 | goto flags_out; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * The JOURNAL_DATA flag can only be changed by | ||
238 | * the relevant capability. | ||
239 | */ | ||
240 | if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { | ||
241 | if (!capable(CAP_SYS_RESOURCE)) | ||
242 | goto flags_out; | ||
243 | } | ||
244 | if ((flags ^ oldflags) & EXT4_EXTENTS_FL) | ||
245 | migrate = 1; | ||
246 | |||
247 | if (flags & EXT4_EOFBLOCKS_FL) { | ||
248 | /* we don't support adding EOFBLOCKS flag */ | ||
249 | if (!(oldflags & EXT4_EOFBLOCKS_FL)) { | ||
250 | err = -EOPNOTSUPP; | ||
251 | goto flags_out; | ||
252 | } | ||
253 | } else if (oldflags & EXT4_EOFBLOCKS_FL) | ||
254 | ext4_truncate(inode); | ||
255 | |||
256 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); | ||
257 | if (IS_ERR(handle)) { | ||
258 | err = PTR_ERR(handle); | ||
259 | goto flags_out; | ||
260 | } | ||
261 | if (IS_SYNC(inode)) | ||
262 | ext4_handle_sync(handle); | ||
263 | err = ext4_reserve_inode_write(handle, inode, &iloc); | ||
264 | if (err) | ||
265 | goto flags_err; | ||
266 | |||
267 | for (i = 0, mask = 1; i < 32; i++, mask <<= 1) { | ||
268 | if (!(mask & EXT4_FL_USER_MODIFIABLE)) | ||
269 | continue; | ||
270 | if (mask & flags) | ||
271 | ext4_set_inode_flag(inode, i); | ||
272 | else | ||
273 | ext4_clear_inode_flag(inode, i); | ||
274 | } | ||
275 | |||
276 | ext4_set_inode_flags(inode); | ||
277 | inode->i_ctime = ext4_current_time(inode); | ||
278 | |||
279 | err = ext4_mark_iloc_dirty(handle, inode, &iloc); | ||
280 | flags_err: | ||
281 | ext4_journal_stop(handle); | ||
282 | if (err) | ||
283 | goto flags_out; | ||
284 | |||
285 | if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) | ||
286 | err = ext4_change_inode_journal_flag(inode, jflag); | ||
287 | if (err) | ||
288 | goto flags_out; | ||
289 | if (migrate) { | ||
290 | if (flags & EXT4_EXTENTS_FL) | ||
291 | err = ext4_ext_migrate(inode); | ||
292 | else | ||
293 | err = ext4_ind_migrate(inode); | ||
294 | } | ||
295 | |||
296 | flags_out: | ||
297 | return err; | ||
298 | } | ||
299 | |||
300 | #ifdef CONFIG_QUOTA | ||
301 | static int ext4_ioctl_setproject(struct file *filp, __u32 projid) | ||
302 | { | ||
303 | struct inode *inode = file_inode(filp); | ||
304 | struct super_block *sb = inode->i_sb; | ||
305 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
306 | int err, rc; | ||
307 | handle_t *handle; | ||
308 | kprojid_t kprojid; | ||
309 | struct ext4_iloc iloc; | ||
310 | struct ext4_inode *raw_inode; | ||
311 | |||
312 | if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, | ||
313 | EXT4_FEATURE_RO_COMPAT_PROJECT)) { | ||
314 | if (projid != EXT4_DEF_PROJID) | ||
315 | return -EOPNOTSUPP; | ||
316 | else | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE) | ||
321 | return -EOPNOTSUPP; | ||
322 | |||
323 | kprojid = make_kprojid(&init_user_ns, (projid_t)projid); | ||
324 | |||
325 | if (projid_eq(kprojid, EXT4_I(inode)->i_projid)) | ||
326 | return 0; | ||
327 | |||
328 | err = mnt_want_write_file(filp); | ||
329 | if (err) | ||
330 | return err; | ||
331 | |||
332 | err = -EPERM; | ||
333 | mutex_lock(&inode->i_mutex); | ||
334 | /* Is it quota file? Do not allow user to mess with it */ | ||
335 | if (IS_NOQUOTA(inode)) | ||
336 | goto out_unlock; | ||
337 | |||
338 | err = ext4_get_inode_loc(inode, &iloc); | ||
339 | if (err) | ||
340 | goto out_unlock; | ||
341 | |||
342 | raw_inode = ext4_raw_inode(&iloc); | ||
343 | if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) { | ||
344 | err = -EOVERFLOW; | ||
345 | brelse(iloc.bh); | ||
346 | goto out_unlock; | ||
347 | } | ||
348 | brelse(iloc.bh); | ||
349 | |||
350 | dquot_initialize(inode); | ||
351 | |||
352 | handle = ext4_journal_start(inode, EXT4_HT_QUOTA, | ||
353 | EXT4_QUOTA_INIT_BLOCKS(sb) + | ||
354 | EXT4_QUOTA_DEL_BLOCKS(sb) + 3); | ||
355 | if (IS_ERR(handle)) { | ||
356 | err = PTR_ERR(handle); | ||
357 | goto out_unlock; | ||
358 | } | ||
359 | |||
360 | err = ext4_reserve_inode_write(handle, inode, &iloc); | ||
361 | if (err) | ||
362 | goto out_stop; | ||
363 | |||
364 | if (sb_has_quota_limits_enabled(sb, PRJQUOTA)) { | ||
365 | struct dquot *transfer_to[MAXQUOTAS] = { }; | ||
366 | |||
367 | transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid)); | ||
368 | if (transfer_to[PRJQUOTA]) { | ||
369 | err = __dquot_transfer(inode, transfer_to); | ||
370 | dqput(transfer_to[PRJQUOTA]); | ||
371 | if (err) | ||
372 | goto out_dirty; | ||
373 | } | ||
374 | } | ||
375 | EXT4_I(inode)->i_projid = kprojid; | ||
376 | inode->i_ctime = ext4_current_time(inode); | ||
377 | out_dirty: | ||
378 | rc = ext4_mark_iloc_dirty(handle, inode, &iloc); | ||
379 | if (!err) | ||
380 | err = rc; | ||
381 | out_stop: | ||
382 | ext4_journal_stop(handle); | ||
383 | out_unlock: | ||
384 | mutex_unlock(&inode->i_mutex); | ||
385 | mnt_drop_write_file(filp); | ||
386 | return err; | ||
387 | } | ||
388 | #else | ||
389 | static int ext4_ioctl_setproject(struct file *filp, __u32 projid) | ||
390 | { | ||
391 | if (projid != EXT4_DEF_PROJID) | ||
392 | return -EOPNOTSUPP; | ||
393 | return 0; | ||
394 | } | ||
395 | #endif | ||
396 | |||
397 | /* Transfer internal flags to xflags */ | ||
398 | static inline __u32 ext4_iflags_to_xflags(unsigned long iflags) | ||
399 | { | ||
400 | __u32 xflags = 0; | ||
401 | |||
402 | if (iflags & EXT4_SYNC_FL) | ||
403 | xflags |= FS_XFLAG_SYNC; | ||
404 | if (iflags & EXT4_IMMUTABLE_FL) | ||
405 | xflags |= FS_XFLAG_IMMUTABLE; | ||
406 | if (iflags & EXT4_APPEND_FL) | ||
407 | xflags |= FS_XFLAG_APPEND; | ||
408 | if (iflags & EXT4_NODUMP_FL) | ||
409 | xflags |= FS_XFLAG_NODUMP; | ||
410 | if (iflags & EXT4_NOATIME_FL) | ||
411 | xflags |= FS_XFLAG_NOATIME; | ||
412 | if (iflags & EXT4_PROJINHERIT_FL) | ||
413 | xflags |= FS_XFLAG_PROJINHERIT; | ||
414 | return xflags; | ||
415 | } | ||
416 | |||
417 | /* Transfer xflags flags to internal */ | ||
418 | static inline unsigned long ext4_xflags_to_iflags(__u32 xflags) | ||
419 | { | ||
420 | unsigned long iflags = 0; | ||
421 | |||
422 | if (xflags & FS_XFLAG_SYNC) | ||
423 | iflags |= EXT4_SYNC_FL; | ||
424 | if (xflags & FS_XFLAG_IMMUTABLE) | ||
425 | iflags |= EXT4_IMMUTABLE_FL; | ||
426 | if (xflags & FS_XFLAG_APPEND) | ||
427 | iflags |= EXT4_APPEND_FL; | ||
428 | if (xflags & FS_XFLAG_NODUMP) | ||
429 | iflags |= EXT4_NODUMP_FL; | ||
430 | if (xflags & FS_XFLAG_NOATIME) | ||
431 | iflags |= EXT4_NOATIME_FL; | ||
432 | if (xflags & FS_XFLAG_PROJINHERIT) | ||
433 | iflags |= EXT4_PROJINHERIT_FL; | ||
434 | |||
435 | return iflags; | ||
436 | } | ||
437 | |||
205 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 438 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
206 | { | 439 | { |
207 | struct inode *inode = file_inode(filp); | 440 | struct inode *inode = file_inode(filp); |
@@ -217,11 +450,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
217 | flags = ei->i_flags & EXT4_FL_USER_VISIBLE; | 450 | flags = ei->i_flags & EXT4_FL_USER_VISIBLE; |
218 | return put_user(flags, (int __user *) arg); | 451 | return put_user(flags, (int __user *) arg); |
219 | case EXT4_IOC_SETFLAGS: { | 452 | case EXT4_IOC_SETFLAGS: { |
220 | handle_t *handle = NULL; | 453 | int err; |
221 | int err, migrate = 0; | ||
222 | struct ext4_iloc iloc; | ||
223 | unsigned int oldflags, mask, i; | ||
224 | unsigned int jflag; | ||
225 | 454 | ||
226 | if (!inode_owner_or_capable(inode)) | 455 | if (!inode_owner_or_capable(inode)) |
227 | return -EACCES; | 456 | return -EACCES; |
@@ -235,89 +464,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
235 | 464 | ||
236 | flags = ext4_mask_flags(inode->i_mode, flags); | 465 | flags = ext4_mask_flags(inode->i_mode, flags); |
237 | 466 | ||
238 | err = -EPERM; | ||
239 | mutex_lock(&inode->i_mutex); | 467 | mutex_lock(&inode->i_mutex); |
240 | /* Is it quota file? Do not allow user to mess with it */ | 468 | err = ext4_ioctl_setflags(inode, flags); |
241 | if (IS_NOQUOTA(inode)) | ||
242 | goto flags_out; | ||
243 | |||
244 | oldflags = ei->i_flags; | ||
245 | |||
246 | /* The JOURNAL_DATA flag is modifiable only by root */ | ||
247 | jflag = flags & EXT4_JOURNAL_DATA_FL; | ||
248 | |||
249 | /* | ||
250 | * The IMMUTABLE and APPEND_ONLY flags can only be changed by | ||
251 | * the relevant capability. | ||
252 | * | ||
253 | * This test looks nicer. Thanks to Pauline Middelink | ||
254 | */ | ||
255 | if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { | ||
256 | if (!capable(CAP_LINUX_IMMUTABLE)) | ||
257 | goto flags_out; | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * The JOURNAL_DATA flag can only be changed by | ||
262 | * the relevant capability. | ||
263 | */ | ||
264 | if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { | ||
265 | if (!capable(CAP_SYS_RESOURCE)) | ||
266 | goto flags_out; | ||
267 | } | ||
268 | if ((flags ^ oldflags) & EXT4_EXTENTS_FL) | ||
269 | migrate = 1; | ||
270 | |||
271 | if (flags & EXT4_EOFBLOCKS_FL) { | ||
272 | /* we don't support adding EOFBLOCKS flag */ | ||
273 | if (!(oldflags & EXT4_EOFBLOCKS_FL)) { | ||
274 | err = -EOPNOTSUPP; | ||
275 | goto flags_out; | ||
276 | } | ||
277 | } else if (oldflags & EXT4_EOFBLOCKS_FL) | ||
278 | ext4_truncate(inode); | ||
279 | |||
280 | handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); | ||
281 | if (IS_ERR(handle)) { | ||
282 | err = PTR_ERR(handle); | ||
283 | goto flags_out; | ||
284 | } | ||
285 | if (IS_SYNC(inode)) | ||
286 | ext4_handle_sync(handle); | ||
287 | err = ext4_reserve_inode_write(handle, inode, &iloc); | ||
288 | if (err) | ||
289 | goto flags_err; | ||
290 | |||
291 | for (i = 0, mask = 1; i < 32; i++, mask <<= 1) { | ||
292 | if (!(mask & EXT4_FL_USER_MODIFIABLE)) | ||
293 | continue; | ||
294 | if (mask & flags) | ||
295 | ext4_set_inode_flag(inode, i); | ||
296 | else | ||
297 | ext4_clear_inode_flag(inode, i); | ||
298 | } | ||
299 | |||
300 | ext4_set_inode_flags(inode); | ||
301 | inode->i_ctime = ext4_current_time(inode); | ||
302 | |||
303 | err = ext4_mark_iloc_dirty(handle, inode, &iloc); | ||
304 | flags_err: | ||
305 | ext4_journal_stop(handle); | ||
306 | if (err) | ||
307 | goto flags_out; | ||
308 | |||
309 | if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) | ||
310 | err = ext4_change_inode_journal_flag(inode, jflag); | ||
311 | if (err) | ||
312 | goto flags_out; | ||
313 | if (migrate) { | ||
314 | if (flags & EXT4_EXTENTS_FL) | ||
315 | err = ext4_ext_migrate(inode); | ||
316 | else | ||
317 | err = ext4_ind_migrate(inode); | ||
318 | } | ||
319 | |||
320 | flags_out: | ||
321 | mutex_unlock(&inode->i_mutex); | 469 | mutex_unlock(&inode->i_mutex); |
322 | mnt_drop_write_file(filp); | 470 | mnt_drop_write_file(filp); |
323 | return err; | 471 | return err; |
@@ -689,6 +837,60 @@ encryption_policy_out: | |||
689 | return -EOPNOTSUPP; | 837 | return -EOPNOTSUPP; |
690 | #endif | 838 | #endif |
691 | } | 839 | } |
840 | case EXT4_IOC_FSGETXATTR: | ||
841 | { | ||
842 | struct fsxattr fa; | ||
843 | |||
844 | memset(&fa, 0, sizeof(struct fsxattr)); | ||
845 | ext4_get_inode_flags(ei); | ||
846 | fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE); | ||
847 | |||
848 | if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, | ||
849 | EXT4_FEATURE_RO_COMPAT_PROJECT)) { | ||
850 | fa.fsx_projid = (__u32)from_kprojid(&init_user_ns, | ||
851 | EXT4_I(inode)->i_projid); | ||
852 | } | ||
853 | |||
854 | if (copy_to_user((struct fsxattr __user *)arg, | ||
855 | &fa, sizeof(fa))) | ||
856 | return -EFAULT; | ||
857 | return 0; | ||
858 | } | ||
859 | case EXT4_IOC_FSSETXATTR: | ||
860 | { | ||
861 | struct fsxattr fa; | ||
862 | int err; | ||
863 | |||
864 | if (copy_from_user(&fa, (struct fsxattr __user *)arg, | ||
865 | sizeof(fa))) | ||
866 | return -EFAULT; | ||
867 | |||
868 | /* Make sure caller has proper permission */ | ||
869 | if (!inode_owner_or_capable(inode)) | ||
870 | return -EACCES; | ||
871 | |||
872 | err = mnt_want_write_file(filp); | ||
873 | if (err) | ||
874 | return err; | ||
875 | |||
876 | flags = ext4_xflags_to_iflags(fa.fsx_xflags); | ||
877 | flags = ext4_mask_flags(inode->i_mode, flags); | ||
878 | |||
879 | mutex_lock(&inode->i_mutex); | ||
880 | flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | | ||
881 | (flags & EXT4_FL_XFLAG_VISIBLE); | ||
882 | err = ext4_ioctl_setflags(inode, flags); | ||
883 | mutex_unlock(&inode->i_mutex); | ||
884 | mnt_drop_write_file(filp); | ||
885 | if (err) | ||
886 | return err; | ||
887 | |||
888 | err = ext4_ioctl_setproject(filp, fa.fsx_projid); | ||
889 | if (err) | ||
890 | return err; | ||
891 | |||
892 | return 0; | ||
893 | } | ||
692 | default: | 894 | default: |
693 | return -ENOTTY; | 895 | return -ENOTTY; |
694 | } | 896 | } |