1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * linux/fs/ext2/ioctl.c 4 * 5 * Copyright (C) 1993, 1994, 1995 6 * Remy Card (card@masi.ibp.fr) 7 * Laboratoire MASI - Institut Blaise Pascal 8 * Universite Pierre et Marie Curie (Paris VI) 9 */ 10 11 #include "ext2.h" 12 #include <linux/capability.h> 13 #include <linux/time.h> 14 #include <linux/sched.h> 15 #include <linux/compat.h> 16 #include <linux/mount.h> 17 #include <asm/current.h> 18 #include <linux/uaccess.h> 19 20 21 long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 22 { 23 struct inode *inode = file_inode(filp); 24 struct ext2_inode_info *ei = EXT2_I(inode); 25 unsigned int flags; 26 unsigned short rsv_window_size; 27 int ret; 28 29 ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); 30 31 switch (cmd) { 32 case EXT2_IOC_GETFLAGS: 33 flags = ei->i_flags & EXT2_FL_USER_VISIBLE; 34 return put_user(flags, (int __user *) arg); 35 case EXT2_IOC_SETFLAGS: { 36 unsigned int oldflags; 37 38 ret = mnt_want_write_file(filp); 39 if (ret) 40 return ret; 41 42 if (!inode_owner_or_capable(inode)) { 43 ret = -EACCES; 44 goto setflags_out; 45 } 46 47 if (get_user(flags, (int __user *) arg)) { 48 ret = -EFAULT; 49 goto setflags_out; 50 } 51 52 flags = ext2_mask_flags(inode->i_mode, flags); 53 54 inode_lock(inode); 55 /* Is it quota file? Do not allow user to mess with it */ 56 if (IS_NOQUOTA(inode)) { 57 inode_unlock(inode); 58 ret = -EPERM; 59 goto setflags_out; 60 } 61 oldflags = ei->i_flags; 62 63 /* 64 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 65 * the relevant capability. 66 * 67 * This test looks nicer. Thanks to Pauline Middelink 68 */ 69 if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) { 70 if (!capable(CAP_LINUX_IMMUTABLE)) { 71 inode_unlock(inode); 72 ret = -EPERM; 73 goto setflags_out; 74 } 75 } 76 77 flags = flags & EXT2_FL_USER_MODIFIABLE; 78 flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; 79 ei->i_flags = flags; 80 81 ext2_set_inode_flags(inode); 82 inode->i_ctime = current_time(inode); 83 inode_unlock(inode); 84 85 mark_inode_dirty(inode); 86 setflags_out: 87 mnt_drop_write_file(filp); 88 return ret; 89 } 90 case EXT2_IOC_GETVERSION: 91 return put_user(inode->i_generation, (int __user *) arg); 92 case EXT2_IOC_SETVERSION: { 93 __u32 generation; 94 95 if (!inode_owner_or_capable(inode)) 96 return -EPERM; 97 ret = mnt_want_write_file(filp); 98 if (ret) 99 return ret; 100 if (get_user(generation, (int __user *) arg)) { 101 ret = -EFAULT; 102 goto setversion_out; 103 } 104 105 inode_lock(inode); 106 inode->i_ctime = current_time(inode); 107 inode->i_generation = generation; 108 inode_unlock(inode); 109 110 mark_inode_dirty(inode); 111 setversion_out: 112 mnt_drop_write_file(filp); 113 return ret; 114 } 115 case EXT2_IOC_GETRSVSZ: 116 if (test_opt(inode->i_sb, RESERVATION) 117 && S_ISREG(inode->i_mode) 118 && ei->i_block_alloc_info) { 119 rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; 120 return put_user(rsv_window_size, (int __user *)arg); 121 } 122 return -ENOTTY; 123 case EXT2_IOC_SETRSVSZ: { 124 125 if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) 126 return -ENOTTY; 127 128 if (!inode_owner_or_capable(inode)) 129 return -EACCES; 130 131 if (get_user(rsv_window_size, (int __user *)arg)) 132 return -EFAULT; 133 134 ret = mnt_want_write_file(filp); 135 if (ret) 136 return ret; 137 138 if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS) 139 rsv_window_size = EXT2_MAX_RESERVE_BLOCKS; 140 141 /* 142 * need to allocate reservation structure for this inode 143 * before set the window size 144 */ 145 /* 146 * XXX What lock should protect the rsv_goal_size? 147 * Accessed in ext2_get_block only. ext3 uses i_truncate. 148 */ 149 mutex_lock(&ei->truncate_mutex); 150 if (!ei->i_block_alloc_info) 151 ext2_init_block_alloc_info(inode); 152 153 if (ei->i_block_alloc_info){ 154 struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; 155 rsv->rsv_goal_size = rsv_window_size; 156 } 157 mutex_unlock(&ei->truncate_mutex); 158 mnt_drop_write_file(filp); 159 return 0; 160 } 161 default: 162 return -ENOTTY; 163 } 164 } 165 166 #ifdef CONFIG_COMPAT 167 long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 168 { 169 /* These are just misnamed, they actually get/put from/to user an int */ 170 switch (cmd) { 171 case EXT2_IOC32_GETFLAGS: 172 cmd = EXT2_IOC_GETFLAGS; 173 break; 174 case EXT2_IOC32_SETFLAGS: 175 cmd = EXT2_IOC_SETFLAGS; 176 break; 177 case EXT2_IOC32_GETVERSION: 178 cmd = EXT2_IOC_GETVERSION; 179 break; 180 case EXT2_IOC32_SETVERSION: 181 cmd = EXT2_IOC_SETVERSION; 182 break; 183 default: 184 return -ENOIOCTLCMD; 185 } 186 return ext2_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); 187 } 188 #endif 189