btrfs: add FS_IOC_FSGETXATTR ioctl
[sfrench/cifs-2.6.git] / fs / btrfs / ioctl.c
index 48e2ddff32bd012d031a1636108f945936e32f06..c3100f3988e2258f4d4c52dc923cfaae629731ab 100644 (file)
@@ -93,20 +93,22 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
                       int no_time_update);
 
 /* Mask out flags that are inappropriate for the given type of inode. */
-static unsigned int btrfs_mask_flags(umode_t mode, unsigned int flags)
+static unsigned int btrfs_mask_fsflags_for_type(struct inode *inode,
+               unsigned int flags)
 {
-       if (S_ISDIR(mode))
+       if (S_ISDIR(inode->i_mode))
                return flags;
-       else if (S_ISREG(mode))
+       else if (S_ISREG(inode->i_mode))
                return flags & ~FS_DIRSYNC_FL;
        else
                return flags & (FS_NODUMP_FL | FS_NOATIME_FL);
 }
 
 /*
- * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl.
+ * Export internal inode flags to the format expected by the FS_IOC_GETFLAGS
+ * ioctl.
  */
-static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
+static unsigned int btrfs_inode_flags_to_fsflags(unsigned int flags)
 {
        unsigned int iflags = 0;
 
@@ -136,7 +138,7 @@ static unsigned int btrfs_flags_to_ioctl(unsigned int flags)
 /*
  * Update inode->i_flags based on the btrfs internal flags.
  */
-void btrfs_update_iflags(struct inode *inode)
+void btrfs_sync_inode_flags_to_i_flags(struct inode *inode)
 {
        struct btrfs_inode *ip = BTRFS_I(inode);
        unsigned int new_fl = 0;
@@ -160,14 +162,15 @@ void btrfs_update_iflags(struct inode *inode)
 static int btrfs_ioctl_getflags(struct file *file, void __user *arg)
 {
        struct btrfs_inode *ip = BTRFS_I(file_inode(file));
-       unsigned int flags = btrfs_flags_to_ioctl(ip->flags);
+       unsigned int flags = btrfs_inode_flags_to_fsflags(ip->flags);
 
        if (copy_to_user(arg, &flags, sizeof(flags)))
                return -EFAULT;
        return 0;
 }
 
-static int check_flags(unsigned int flags)
+/* Check if @flags are a supported and valid set of FS_*_FL flags */
+static int check_fsflags(unsigned int flags)
 {
        if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
                      FS_NOATIME_FL | FS_NODUMP_FL | \
@@ -204,7 +207,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        if (copy_from_user(&flags, arg, sizeof(flags)))
                return -EFAULT;
 
-       ret = check_flags(flags);
+       ret = check_fsflags(flags);
        if (ret)
                return ret;
 
@@ -218,8 +221,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        i_oldflags = inode->i_flags;
        mode = inode->i_mode;
 
-       flags = btrfs_mask_flags(inode->i_mode, flags);
-       oldflags = btrfs_flags_to_ioctl(ip->flags);
+       flags = btrfs_mask_fsflags_for_type(inode, flags);
+       oldflags = btrfs_inode_flags_to_fsflags(ip->flags);
        if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
                if (!capable(CAP_LINUX_IMMUTABLE)) {
                        ret = -EPERM;
@@ -317,7 +320,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
                goto out_drop;
        }
 
-       btrfs_update_iflags(inode);
+       btrfs_sync_inode_flags_to_i_flags(inode);
        inode_inc_iversion(inode);
        inode->i_ctime = current_time(inode);
        ret = btrfs_update_inode(trans, root, inode);
@@ -335,6 +338,56 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
        return ret;
 }
 
+/*
+ * Translate btrfs internal inode flags to xflags as expected by the
+ * FS_IOC_FSGETXATT ioctl. Filter only the supported ones, unknown flags are
+ * silently dropped.
+ */
+static unsigned int btrfs_inode_flags_to_xflags(unsigned int flags)
+{
+       unsigned int xflags = 0;
+
+       if (flags & BTRFS_INODE_APPEND)
+               xflags |= FS_XFLAG_APPEND;
+       if (flags & BTRFS_INODE_IMMUTABLE)
+               xflags |= FS_XFLAG_IMMUTABLE;
+       if (flags & BTRFS_INODE_NOATIME)
+               xflags |= FS_XFLAG_NOATIME;
+       if (flags & BTRFS_INODE_NODUMP)
+               xflags |= FS_XFLAG_NODUMP;
+       if (flags & BTRFS_INODE_SYNC)
+               xflags |= FS_XFLAG_SYNC;
+
+       return xflags;
+}
+
+/* Check if @flags are a supported and valid set of FS_XFLAGS_* flags */
+static int check_xflags(unsigned int flags)
+{
+       if (flags & ~(FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE | FS_XFLAG_NOATIME |
+                     FS_XFLAG_NODUMP | FS_XFLAG_SYNC))
+               return -EOPNOTSUPP;
+       return 0;
+}
+
+/*
+ * Set the xflags from the internal inode flags. The remaining items of fsxattr
+ * are zeroed.
+ */
+static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg)
+{
+       struct btrfs_inode *binode = BTRFS_I(file_inode(file));
+       struct fsxattr fa;
+
+       memset(&fa, 0, sizeof(fa));
+       fa.fsx_xflags = btrfs_inode_flags_to_xflags(binode->flags);
+
+       if (copy_to_user(arg, &fa, sizeof(fa)))
+               return -EFAULT;
+
+       return 0;
+}
+
 static int btrfs_ioctl_getversion(struct file *file, int __user *arg)
 {
        struct inode *inode = file_inode(file);
@@ -5374,6 +5427,8 @@ long btrfs_ioctl(struct file *file, unsigned int
                return btrfs_ioctl_get_features(file, argp);
        case BTRFS_IOC_SET_FEATURES:
                return btrfs_ioctl_set_features(file, argp);
+       case FS_IOC_FSGETXATTR:
+               return btrfs_ioctl_fsgetxattr(file, argp);
        }
 
        return -ENOTTY;