|
@@ -1163,6 +1163,95 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * nilfs_ioctl_set_suinfo - set segment usage info
|
|
|
+ * @inode: inode object
|
|
|
+ * @filp: file object
|
|
|
+ * @cmd: ioctl's request code
|
|
|
+ * @argp: pointer on argument from userspace
|
|
|
+ *
|
|
|
+ * Description: Expects an array of nilfs_suinfo_update structures
|
|
|
+ * encapsulated in nilfs_argv and updates the segment usage info
|
|
|
+ * according to the flags in nilfs_suinfo_update.
|
|
|
+ *
|
|
|
+ * Return Value: On success, 0 is returned. On error, one of the
|
|
|
+ * following negative error codes is returned.
|
|
|
+ *
|
|
|
+ * %-EPERM - Not enough permissions
|
|
|
+ *
|
|
|
+ * %-EFAULT - Error copying input data
|
|
|
+ *
|
|
|
+ * %-EIO - I/O error.
|
|
|
+ *
|
|
|
+ * %-ENOMEM - Insufficient amount of memory available.
|
|
|
+ *
|
|
|
+ * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
|
|
|
+ */
|
|
|
+static int nilfs_ioctl_set_suinfo(struct inode *inode, struct file *filp,
|
|
|
+ unsigned int cmd, void __user *argp)
|
|
|
+{
|
|
|
+ struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
|
|
|
+ struct nilfs_transaction_info ti;
|
|
|
+ struct nilfs_argv argv;
|
|
|
+ size_t len;
|
|
|
+ void __user *base;
|
|
|
+ void *kbuf;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ ret = mnt_want_write_file(filp);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = -EFAULT;
|
|
|
+ if (copy_from_user(&argv, argp, sizeof(argv)))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = -EINVAL;
|
|
|
+ if (argv.v_size < sizeof(struct nilfs_suinfo_update))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (argv.v_nmembs > nilfs->ns_nsegments)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (argv.v_nmembs >= UINT_MAX / argv.v_size)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ len = argv.v_size * argv.v_nmembs;
|
|
|
+ if (!len) {
|
|
|
+ ret = 0;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ base = (void __user *)(unsigned long)argv.v_base;
|
|
|
+ kbuf = vmalloc(len);
|
|
|
+ if (!kbuf) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (copy_from_user(kbuf, base, len)) {
|
|
|
+ ret = -EFAULT;
|
|
|
+ goto out_free;
|
|
|
+ }
|
|
|
+
|
|
|
+ nilfs_transaction_begin(inode->i_sb, &ti, 0);
|
|
|
+ ret = nilfs_sufile_set_suinfo(nilfs->ns_sufile, kbuf, argv.v_size,
|
|
|
+ argv.v_nmembs);
|
|
|
+ if (unlikely(ret < 0))
|
|
|
+ nilfs_transaction_abort(inode->i_sb);
|
|
|
+ else
|
|
|
+ nilfs_transaction_commit(inode->i_sb); /* never fails */
|
|
|
+
|
|
|
+out_free:
|
|
|
+ vfree(kbuf);
|
|
|
+out:
|
|
|
+ mnt_drop_write_file(filp);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
{
|
|
|
struct inode *inode = file_inode(filp);
|
|
@@ -1189,6 +1278,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
return nilfs_ioctl_get_info(inode, filp, cmd, argp,
|
|
|
sizeof(struct nilfs_suinfo),
|
|
|
nilfs_ioctl_do_get_suinfo);
|
|
|
+ case NILFS_IOCTL_SET_SUINFO:
|
|
|
+ return nilfs_ioctl_set_suinfo(inode, filp, cmd, argp);
|
|
|
case NILFS_IOCTL_GET_SUSTAT:
|
|
|
return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
|
|
|
case NILFS_IOCTL_GET_VINFO:
|
|
@@ -1228,6 +1319,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
|
case NILFS_IOCTL_GET_CPINFO:
|
|
|
case NILFS_IOCTL_GET_CPSTAT:
|
|
|
case NILFS_IOCTL_GET_SUINFO:
|
|
|
+ case NILFS_IOCTL_SET_SUINFO:
|
|
|
case NILFS_IOCTL_GET_SUSTAT:
|
|
|
case NILFS_IOCTL_GET_VINFO:
|
|
|
case NILFS_IOCTL_GET_BDESCS:
|