diff options
Diffstat (limited to 'fs/ext4/ioctl.c')
-rw-r--r-- | fs/ext4/ioctl.c | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index a4273ddb9922..dfcb815275f1 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -19,6 +19,9 @@ | |||
19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
20 | #include "ext4_jbd2.h" | 20 | #include "ext4_jbd2.h" |
21 | #include "ext4.h" | 21 | #include "ext4.h" |
22 | #include <linux/fsmap.h> | ||
23 | #include "fsmap.h" | ||
24 | #include <trace/events/ext4.h> | ||
22 | 25 | ||
23 | /** | 26 | /** |
24 | * Swap memory between @a and @b for @len bytes. | 27 | * Swap memory between @a and @b for @len bytes. |
@@ -489,6 +492,90 @@ int ext4_shutdown(struct super_block *sb, unsigned long arg) | |||
489 | return 0; | 492 | return 0; |
490 | } | 493 | } |
491 | 494 | ||
495 | struct getfsmap_info { | ||
496 | struct super_block *gi_sb; | ||
497 | struct fsmap_head __user *gi_data; | ||
498 | unsigned int gi_idx; | ||
499 | __u32 gi_last_flags; | ||
500 | }; | ||
501 | |||
502 | static int ext4_getfsmap_format(struct ext4_fsmap *xfm, void *priv) | ||
503 | { | ||
504 | struct getfsmap_info *info = priv; | ||
505 | struct fsmap fm; | ||
506 | |||
507 | trace_ext4_getfsmap_mapping(info->gi_sb, xfm); | ||
508 | |||
509 | info->gi_last_flags = xfm->fmr_flags; | ||
510 | ext4_fsmap_from_internal(info->gi_sb, &fm, xfm); | ||
511 | if (copy_to_user(&info->gi_data->fmh_recs[info->gi_idx++], &fm, | ||
512 | sizeof(struct fsmap))) | ||
513 | return -EFAULT; | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | static int ext4_ioc_getfsmap(struct super_block *sb, | ||
519 | struct fsmap_head __user *arg) | ||
520 | { | ||
521 | struct getfsmap_info info = {0}; | ||
522 | struct ext4_fsmap_head xhead = {0}; | ||
523 | struct fsmap_head head; | ||
524 | bool aborted = false; | ||
525 | int error; | ||
526 | |||
527 | if (copy_from_user(&head, arg, sizeof(struct fsmap_head))) | ||
528 | return -EFAULT; | ||
529 | if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) || | ||
530 | memchr_inv(head.fmh_keys[0].fmr_reserved, 0, | ||
531 | sizeof(head.fmh_keys[0].fmr_reserved)) || | ||
532 | memchr_inv(head.fmh_keys[1].fmr_reserved, 0, | ||
533 | sizeof(head.fmh_keys[1].fmr_reserved))) | ||
534 | return -EINVAL; | ||
535 | /* | ||
536 | * ext4 doesn't report file extents at all, so the only valid | ||
537 | * file offsets are the magic ones (all zeroes or all ones). | ||
538 | */ | ||
539 | if (head.fmh_keys[0].fmr_offset || | ||
540 | (head.fmh_keys[1].fmr_offset != 0 && | ||
541 | head.fmh_keys[1].fmr_offset != -1ULL)) | ||
542 | return -EINVAL; | ||
543 | |||
544 | xhead.fmh_iflags = head.fmh_iflags; | ||
545 | xhead.fmh_count = head.fmh_count; | ||
546 | ext4_fsmap_to_internal(sb, &xhead.fmh_keys[0], &head.fmh_keys[0]); | ||
547 | ext4_fsmap_to_internal(sb, &xhead.fmh_keys[1], &head.fmh_keys[1]); | ||
548 | |||
549 | trace_ext4_getfsmap_low_key(sb, &xhead.fmh_keys[0]); | ||
550 | trace_ext4_getfsmap_high_key(sb, &xhead.fmh_keys[1]); | ||
551 | |||
552 | info.gi_sb = sb; | ||
553 | info.gi_data = arg; | ||
554 | error = ext4_getfsmap(sb, &xhead, ext4_getfsmap_format, &info); | ||
555 | if (error == EXT4_QUERY_RANGE_ABORT) { | ||
556 | error = 0; | ||
557 | aborted = true; | ||
558 | } else if (error) | ||
559 | return error; | ||
560 | |||
561 | /* If we didn't abort, set the "last" flag in the last fmx */ | ||
562 | if (!aborted && info.gi_idx) { | ||
563 | info.gi_last_flags |= FMR_OF_LAST; | ||
564 | if (copy_to_user(&info.gi_data->fmh_recs[info.gi_idx - 1].fmr_flags, | ||
565 | &info.gi_last_flags, | ||
566 | sizeof(info.gi_last_flags))) | ||
567 | return -EFAULT; | ||
568 | } | ||
569 | |||
570 | /* copy back header */ | ||
571 | head.fmh_entries = xhead.fmh_entries; | ||
572 | head.fmh_oflags = xhead.fmh_oflags; | ||
573 | if (copy_to_user(arg, &head, sizeof(struct fsmap_head))) | ||
574 | return -EFAULT; | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
492 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 579 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
493 | { | 580 | { |
494 | struct inode *inode = file_inode(filp); | 581 | struct inode *inode = file_inode(filp); |
@@ -499,6 +586,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
499 | ext4_debug("cmd = %u, arg = %lu\n", cmd, arg); | 586 | ext4_debug("cmd = %u, arg = %lu\n", cmd, arg); |
500 | 587 | ||
501 | switch (cmd) { | 588 | switch (cmd) { |
589 | case FS_IOC_GETFSMAP: | ||
590 | return ext4_ioc_getfsmap(sb, (void __user *)arg); | ||
502 | case EXT4_IOC_GETFLAGS: | 591 | case EXT4_IOC_GETFLAGS: |
503 | ext4_get_inode_flags(ei); | 592 | ext4_get_inode_flags(ei); |
504 | flags = ei->i_flags & EXT4_FL_USER_VISIBLE; | 593 | flags = ei->i_flags & EXT4_FL_USER_VISIBLE; |
@@ -1009,6 +1098,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
1009 | case EXT4_IOC_GET_ENCRYPTION_PWSALT: | 1098 | case EXT4_IOC_GET_ENCRYPTION_PWSALT: |
1010 | case EXT4_IOC_GET_ENCRYPTION_POLICY: | 1099 | case EXT4_IOC_GET_ENCRYPTION_POLICY: |
1011 | case EXT4_IOC_SHUTDOWN: | 1100 | case EXT4_IOC_SHUTDOWN: |
1101 | case FS_IOC_GETFSMAP: | ||
1012 | break; | 1102 | break; |
1013 | default: | 1103 | default: |
1014 | return -ENOIOCTLCMD; | 1104 | return -ENOIOCTLCMD; |