nilfs2: add compat ioctl
[linux.git] / fs / nilfs2 / ioctl.c
index 496738963fdbd029aa2a39723442020c5c4d9e93..d89173edd7fed24f1e37ddac61cba678705b25b9 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/capability.h>  /* capable() */
 #include <linux/uaccess.h>     /* copy_from_user(), copy_to_user() */
 #include <linux/vmalloc.h>
+#include <linux/compat.h>      /* compat_ptr() */
 #include <linux/mount.h>       /* mnt_want_write(), mnt_drop_write() */
 #include <linux/nilfs2_fs.h>
 #include "nilfs.h"
@@ -97,6 +98,70 @@ static int nilfs_ioctl_wrap_copy(struct the_nilfs *nilfs,
        return ret;
 }
 
+static int nilfs_ioctl_getflags(struct inode *inode, void __user *argp)
+{
+       unsigned int flags = NILFS_I(inode)->i_flags & FS_FL_USER_VISIBLE;
+
+       return put_user(flags, (int __user *)argp);
+}
+
+static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
+                               void __user *argp)
+{
+       struct nilfs_transaction_info ti;
+       unsigned int flags, oldflags;
+       int ret;
+
+       if (!is_owner_or_cap(inode))
+               return -EACCES;
+
+       if (get_user(flags, (int __user *)argp))
+               return -EFAULT;
+
+       ret = mnt_want_write(filp->f_path.mnt);
+       if (ret)
+               return ret;
+
+       flags = nilfs_mask_flags(inode->i_mode, flags);
+
+       mutex_lock(&inode->i_mutex);
+
+       oldflags = NILFS_I(inode)->i_flags;
+
+       /*
+        * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
+        * relevant capability.
+        */
+       ret = -EPERM;
+       if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
+           !capable(CAP_LINUX_IMMUTABLE))
+               goto out;
+
+       ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
+       if (ret)
+               goto out;
+
+       NILFS_I(inode)->i_flags = (oldflags & ~FS_FL_USER_MODIFIABLE) |
+               (flags & FS_FL_USER_MODIFIABLE);
+
+       nilfs_set_inode_flags(inode);
+       inode->i_ctime = CURRENT_TIME;
+       if (IS_SYNC(inode))
+               nilfs_set_transaction_flag(NILFS_TI_SYNC);
+
+       nilfs_mark_inode_dirty(inode);
+       ret = nilfs_transaction_commit(inode->i_sb);
+out:
+       mutex_unlock(&inode->i_mutex);
+       mnt_drop_write(filp->f_path.mnt);
+       return ret;
+}
+
+static int nilfs_ioctl_getversion(struct inode *inode, void __user *argp)
+{
+       return put_user(inode->i_generation, (int __user *)argp);
+}
+
 static int nilfs_ioctl_change_cpmode(struct inode *inode, struct file *filp,
                                     unsigned int cmd, void __user *argp)
 {
@@ -666,6 +731,12 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        void __user *argp = (void __user *)arg;
 
        switch (cmd) {
+       case FS_IOC_GETFLAGS:
+               return nilfs_ioctl_getflags(inode, argp);
+       case FS_IOC_SETFLAGS:
+               return nilfs_ioctl_setflags(inode, filp, argp);
+       case FS_IOC_GETVERSION:
+               return nilfs_ioctl_getversion(inode, argp);
        case NILFS_IOCTL_CHANGE_CPMODE:
                return nilfs_ioctl_change_cpmode(inode, filp, cmd, argp);
        case NILFS_IOCTL_DELETE_CHECKPOINT:
@@ -696,3 +767,23 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                return -ENOTTY;
        }
 }
+
+#ifdef CONFIG_COMPAT
+long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case FS_IOC32_GETFLAGS:
+               cmd = FS_IOC_GETFLAGS;
+               break;
+       case FS_IOC32_SETFLAGS:
+               cmd = FS_IOC_SETFLAGS;
+               break;
+       case FS_IOC32_GETVERSION:
+               cmd = FS_IOC_GETVERSION;
+               break;
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return nilfs_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif