Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-microblaze.git] / fs / ext4 / ioctl.c
index 5444d49..0b7f316 100644 (file)
@@ -745,6 +745,74 @@ static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
                fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
 }
 
+/* copied from fs/ioctl.c */
+static int fiemap_check_ranges(struct super_block *sb,
+                              u64 start, u64 len, u64 *new_len)
+{
+       u64 maxbytes = (u64) sb->s_maxbytes;
+
+       *new_len = len;
+
+       if (len == 0)
+               return -EINVAL;
+
+       if (start > maxbytes)
+               return -EFBIG;
+
+       /*
+        * Shrink request scope to what the fs can actually handle.
+        */
+       if (len > maxbytes || (maxbytes - len) < start)
+               *new_len = maxbytes - start;
+
+       return 0;
+}
+
+/* So that the fiemap access checks can't overflow on 32 bit machines. */
+#define FIEMAP_MAX_EXTENTS     (UINT_MAX / sizeof(struct fiemap_extent))
+
+static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
+{
+       struct fiemap fiemap;
+       struct fiemap __user *ufiemap = (struct fiemap __user *) arg;
+       struct fiemap_extent_info fieinfo = { 0, };
+       struct inode *inode = file_inode(filp);
+       struct super_block *sb = inode->i_sb;
+       u64 len;
+       int error;
+
+       if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
+               return -EFAULT;
+
+       if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
+               return -EINVAL;
+
+       error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
+                                   &len);
+       if (error)
+               return error;
+
+       fieinfo.fi_flags = fiemap.fm_flags;
+       fieinfo.fi_extents_max = fiemap.fm_extent_count;
+       fieinfo.fi_extents_start = ufiemap->fm_extents;
+
+       if (fiemap.fm_extent_count != 0 &&
+           !access_ok(fieinfo.fi_extents_start,
+                      fieinfo.fi_extents_max * sizeof(struct fiemap_extent)))
+               return -EFAULT;
+
+       if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
+               filemap_write_and_wait(inode->i_mapping);
+
+       error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start, len);
+       fiemap.fm_flags = fieinfo.fi_flags;
+       fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
+       if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
+               error = -EFAULT;
+
+       return error;
+}
+
 long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
@@ -1142,6 +1210,33 @@ resizefs_out:
                        return -EOPNOTSUPP;
                return fscrypt_ioctl_get_key_status(filp, (void __user *)arg);
 
+       case EXT4_IOC_CLEAR_ES_CACHE:
+       {
+               if (!inode_owner_or_capable(inode))
+                       return -EACCES;
+               ext4_clear_inode_es(inode);
+               return 0;
+       }
+
+       case EXT4_IOC_GETSTATE:
+       {
+               __u32   state = 0;
+
+               if (ext4_test_inode_state(inode, EXT4_STATE_EXT_PRECACHED))
+                       state |= EXT4_STATE_FLAG_EXT_PRECACHED;
+               if (ext4_test_inode_state(inode, EXT4_STATE_NEW))
+                       state |= EXT4_STATE_FLAG_NEW;
+               if (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY))
+                       state |= EXT4_STATE_FLAG_NEWENTRY;
+               if (ext4_test_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE))
+                       state |= EXT4_STATE_FLAG_DA_ALLOC_CLOSE;
+
+               return put_user(state, (__u32 __user *) arg);
+       }
+
+       case EXT4_IOC_GET_ES_CACHE:
+               return ext4_ioctl_get_es_cache(filp, arg);
+
        case EXT4_IOC_FSGETXATTR:
        {
                struct fsxattr fa;
@@ -1278,6 +1373,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case FS_IOC_GETFSMAP:
        case FS_IOC_ENABLE_VERITY:
        case FS_IOC_MEASURE_VERITY:
+       case EXT4_IOC_CLEAR_ES_CACHE:
+       case EXT4_IOC_GETSTATE:
+       case EXT4_IOC_GET_ES_CACHE:
                break;
        default:
                return -ENOIOCTLCMD;