ioctl.c 4.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * linux/fs/jfs/ioctl.c
 *
 * Copyright (C) 2006 Herbert Poetzl
 * adapted from Remy Card's ext2/ioctl.c
 */

#include <linux/fs.h>
#include <linux/ctype.h>
#include <linux/capability.h>
11
#include <linux/mount.h>
12
#include <linux/time.h>
13
#include <linux/sched.h>
14
#include <linux/blkdev.h>
15 16 17
#include <asm/current.h>
#include <asm/uaccess.h>

18 19
#include "jfs_filsys.h"
#include "jfs_debug.h"
20 21 22
#include "jfs_incore.h"
#include "jfs_dinode.h"
#include "jfs_inode.h"
23 24
#include "jfs_dmap.h"
#include "jfs_discard.h"
25 26 27 28 29

static struct {
	long jfs_flag;
	long ext2_flag;
} jfs_map[] = {
30 31 32 33 34 35 36
	{JFS_NOATIME_FL,	FS_NOATIME_FL},
	{JFS_DIRSYNC_FL,	FS_DIRSYNC_FL},
	{JFS_SYNC_FL,		FS_SYNC_FL},
	{JFS_SECRM_FL,		FS_SECRM_FL},
	{JFS_UNRM_FL,		FS_UNRM_FL},
	{JFS_APPEND_FL,		FS_APPEND_FL},
	{JFS_IMMUTABLE_FL,	FS_IMMUTABLE_FL},
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
	{0, 0},
};

static long jfs_map_ext2(unsigned long flags, int from)
{
	int index=0;
	long mapped=0;

	while (jfs_map[index].jfs_flag) {
		if (from) {
			if (jfs_map[index].ext2_flag & flags)
				mapped |= jfs_map[index].jfs_flag;
		} else {
			if (jfs_map[index].jfs_flag & flags)
				mapped |= jfs_map[index].ext2_flag;
		}
		index++;
	}
	return mapped;
}


59
long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
60
{
61
	struct inode *inode = filp->f_dentry->d_inode;
62 63 64 65 66
	struct jfs_inode_info *jfs_inode = JFS_IP(inode);
	unsigned int flags;

	switch (cmd) {
	case JFS_IOC_GETFLAGS:
67
		jfs_get_inode_flags(jfs_inode);
68 69 70 71 72
		flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE;
		flags = jfs_map_ext2(flags, 0);
		return put_user(flags, (int __user *) arg);
	case JFS_IOC_SETFLAGS: {
		unsigned int oldflags;
73
		int err;
74

75
		err = mnt_want_write_file(filp);
76 77
		if (err)
			return err;
78

79
		if (!inode_owner_or_capable(inode)) {
80 81 82 83 84 85 86
			err = -EACCES;
			goto setflags_out;
		}
		if (get_user(flags, (int __user *) arg)) {
			err = -EFAULT;
			goto setflags_out;
		}
87 88 89 90 91

		flags = jfs_map_ext2(flags, 1);
		if (!S_ISDIR(inode->i_mode))
			flags &= ~JFS_DIRSYNC_FL;

92
		/* Is it quota file? Do not allow user to mess with it */
93 94 95 96
		if (IS_NOQUOTA(inode)) {
			err = -EPERM;
			goto setflags_out;
		}
97 98 99 100

		/* Lock against other parallel changes of flags */
		mutex_lock(&inode->i_mutex);

101
		jfs_get_inode_flags(jfs_inode);
102 103 104 105 106 107 108 109 110
		oldflags = jfs_inode->mode2;

		/*
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
		 * the relevant capability.
		 */
		if ((oldflags & JFS_IMMUTABLE_FL) ||
			((flags ^ oldflags) &
			(JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
111 112
			if (!capable(CAP_LINUX_IMMUTABLE)) {
				mutex_unlock(&inode->i_mutex);
113 114
				err = -EPERM;
				goto setflags_out;
115
			}
116 117 118 119 120 121 122
		}

		flags = flags & JFS_FL_USER_MODIFIABLE;
		flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
		jfs_inode->mode2 = flags;

		jfs_set_inode_flags(inode);
123
		mutex_unlock(&inode->i_mutex);
124 125
		inode->i_ctime = CURRENT_TIME_SEC;
		mark_inode_dirty(inode);
126
setflags_out:
A
Al Viro 已提交
127
		mnt_drop_write_file(filp);
128
		return err;
129
	}
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

	case FITRIM:
	{
		struct super_block *sb = inode->i_sb;
		struct request_queue *q = bdev_get_queue(sb->s_bdev);
		struct fstrim_range range;
		s64 ret = 0;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

		if (!blk_queue_discard(q)) {
			jfs_warn("FITRIM not supported on device");
			return -EOPNOTSUPP;
		}

		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
		    sizeof(range)))
			return -EFAULT;

		range.minlen = max_t(unsigned int, range.minlen,
			q->limits.discard_granularity);

		ret = jfs_ioc_trim(inode, &range);
		if (ret < 0)
			return ret;

		if (copy_to_user((struct fstrim_range __user *)arg, &range,
		    sizeof(range)))
			return -EFAULT;

		return 0;
	}

164 165 166 167 168
	default:
		return -ENOTTY;
	}
}

169 170 171 172 173 174 175 176 177 178 179 180 181 182
#ifdef CONFIG_COMPAT
long jfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	/* While these ioctl numbers defined with 'long' and have different
	 * numbers than the 64bit ABI,
	 * the actual implementation only deals with ints and is compatible.
	 */
	switch (cmd) {
	case JFS_IOC_GETFLAGS32:
		cmd = JFS_IOC_GETFLAGS;
		break;
	case JFS_IOC_SETFLAGS32:
		cmd = JFS_IOC_SETFLAGS;
		break;
183 184 185
	case FITRIM:
		cmd = FITRIM;
		break;
186 187 188 189
	}
	return jfs_ioctl(filp, cmd, arg);
}
#endif