1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * linux/fs/jfs/ioctl.c 4 * 5 * Copyright (C) 2006 Herbert Poetzl 6 * adapted from Remy Card's ext2/ioctl.c 7 */ 8 9 #include <linux/fs.h> 10 #include <linux/ctype.h> 11 #include <linux/capability.h> 12 #include <linux/mount.h> 13 #include <linux/time.h> 14 #include <linux/sched.h> 15 #include <linux/blkdev.h> 16 #include <asm/current.h> 17 #include <linux/uaccess.h> 18 19 #include "jfs_filsys.h" 20 #include "jfs_debug.h" 21 #include "jfs_incore.h" 22 #include "jfs_dinode.h" 23 #include "jfs_inode.h" 24 #include "jfs_dmap.h" 25 #include "jfs_discard.h" 26 27 static struct { 28 long jfs_flag; 29 long ext2_flag; 30 } jfs_map[] = { 31 {JFS_NOATIME_FL, FS_NOATIME_FL}, 32 {JFS_DIRSYNC_FL, FS_DIRSYNC_FL}, 33 {JFS_SYNC_FL, FS_SYNC_FL}, 34 {JFS_SECRM_FL, FS_SECRM_FL}, 35 {JFS_UNRM_FL, FS_UNRM_FL}, 36 {JFS_APPEND_FL, FS_APPEND_FL}, 37 {JFS_IMMUTABLE_FL, FS_IMMUTABLE_FL}, 38 {0, 0}, 39 }; 40 41 static long jfs_map_ext2(unsigned long flags, int from) 42 { 43 int index=0; 44 long mapped=0; 45 46 while (jfs_map[index].jfs_flag) { 47 if (from) { 48 if (jfs_map[index].ext2_flag & flags) 49 mapped |= jfs_map[index].jfs_flag; 50 } else { 51 if (jfs_map[index].jfs_flag & flags) 52 mapped |= jfs_map[index].ext2_flag; 53 } 54 index++; 55 } 56 return mapped; 57 } 58 59 60 long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 61 { 62 struct inode *inode = file_inode(filp); 63 struct jfs_inode_info *jfs_inode = JFS_IP(inode); 64 unsigned int flags; 65 66 switch (cmd) { 67 case JFS_IOC_GETFLAGS: 68 flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE; 69 flags = jfs_map_ext2(flags, 0); 70 return put_user(flags, (int __user *) arg); 71 case JFS_IOC_SETFLAGS: { 72 unsigned int oldflags; 73 int err; 74 75 err = mnt_want_write_file(filp); 76 if (err) 77 return err; 78 79 if (!inode_owner_or_capable(inode)) { 80 err = -EACCES; 81 goto setflags_out; 82 } 83 if (get_user(flags, (int __user *) arg)) { 84 err = -EFAULT; 85 goto setflags_out; 86 } 87 88 flags = jfs_map_ext2(flags, 1); 89 if (!S_ISDIR(inode->i_mode)) 90 flags &= ~JFS_DIRSYNC_FL; 91 92 /* Is it quota file? Do not allow user to mess with it */ 93 if (IS_NOQUOTA(inode)) { 94 err = -EPERM; 95 goto setflags_out; 96 } 97 98 /* Lock against other parallel changes of flags */ 99 inode_lock(inode); 100 101 oldflags = jfs_inode->mode2; 102 103 /* 104 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 105 * the relevant capability. 106 */ 107 if ((oldflags & JFS_IMMUTABLE_FL) || 108 ((flags ^ oldflags) & 109 (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) { 110 if (!capable(CAP_LINUX_IMMUTABLE)) { 111 inode_unlock(inode); 112 err = -EPERM; 113 goto setflags_out; 114 } 115 } 116 117 flags = flags & JFS_FL_USER_MODIFIABLE; 118 flags |= oldflags & ~JFS_FL_USER_MODIFIABLE; 119 jfs_inode->mode2 = flags; 120 121 jfs_set_inode_flags(inode); 122 inode_unlock(inode); 123 inode->i_ctime = current_time(inode); 124 mark_inode_dirty(inode); 125 setflags_out: 126 mnt_drop_write_file(filp); 127 return err; 128 } 129 130 case FITRIM: 131 { 132 struct super_block *sb = inode->i_sb; 133 struct request_queue *q = bdev_get_queue(sb->s_bdev); 134 struct fstrim_range range; 135 s64 ret = 0; 136 137 if (!capable(CAP_SYS_ADMIN)) 138 return -EPERM; 139 140 if (!blk_queue_discard(q)) { 141 jfs_warn("FITRIM not supported on device"); 142 return -EOPNOTSUPP; 143 } 144 145 if (copy_from_user(&range, (struct fstrim_range __user *)arg, 146 sizeof(range))) 147 return -EFAULT; 148 149 range.minlen = max_t(unsigned int, range.minlen, 150 q->limits.discard_granularity); 151 152 ret = jfs_ioc_trim(inode, &range); 153 if (ret < 0) 154 return ret; 155 156 if (copy_to_user((struct fstrim_range __user *)arg, &range, 157 sizeof(range))) 158 return -EFAULT; 159 160 return 0; 161 } 162 163 default: 164 return -ENOTTY; 165 } 166 } 167 168 #ifdef CONFIG_COMPAT 169 long jfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 170 { 171 /* While these ioctl numbers defined with 'long' and have different 172 * numbers than the 64bit ABI, 173 * the actual implementation only deals with ints and is compatible. 174 */ 175 switch (cmd) { 176 case JFS_IOC_GETFLAGS32: 177 cmd = JFS_IOC_GETFLAGS; 178 break; 179 case JFS_IOC_SETFLAGS32: 180 cmd = JFS_IOC_SETFLAGS; 181 break; 182 } 183 return jfs_ioctl(filp, cmd, arg); 184 } 185 #endif 186