nilfs2: implement FS_IOC_GETFLAGS/SETFLAGS/GETVERSION
authorRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Wed, 19 Jan 2011 17:09:53 +0000 (02:09 +0900)
committerRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Tue, 8 Mar 2011 05:58:30 +0000 (14:58 +0900)
Add support for the standard attributes set via chattr and read via
lsattr.  These attributes are already in the flags value in the nilfs2
inode, but currently we don't have any ioctl commands that expose them
to the userland.

Collaterally, this adds the FS_IOC_GETVERSION ioctl for getting
i_generation, which allows users to list the file's generation number
with "lsattr -v".

Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
fs/nilfs2/ioctl.c

index 4967389..3aad641 100644 (file)
@@ -97,6 +97,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 +730,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: