1/* 2 * linux/fs/ext3/ioctl.c 3 * 4 * Copyright (C) 1993, 1994, 1995 5 * Remy Card (card@masi.ibp.fr) 6 * Laboratoire MASI - Institut Blaise Pascal 7 * Universite Pierre et Marie Curie (Paris VI) 8 */ 9 10#include <linux/mount.h> 11#include <linux/compat.h> 12#include <asm/uaccess.h> 13#include "ext3.h" 14 15long ext3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 16{ 17 struct inode *inode = file_inode(filp); 18 struct ext3_inode_info *ei = EXT3_I(inode); 19 unsigned int flags; 20 unsigned short rsv_window_size; 21 22 ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg); 23 24 switch (cmd) { 25 case EXT3_IOC_GETFLAGS: 26 ext3_get_inode_flags(ei); 27 flags = ei->i_flags & EXT3_FL_USER_VISIBLE; 28 return put_user(flags, (int __user *) arg); 29 case EXT3_IOC_SETFLAGS: { 30 handle_t *handle = NULL; 31 int err; 32 struct ext3_iloc iloc; 33 unsigned int oldflags; 34 unsigned int jflag; 35 36 if (!inode_owner_or_capable(inode)) 37 return -EACCES; 38 39 if (get_user(flags, (int __user *) arg)) 40 return -EFAULT; 41 42 err = mnt_want_write_file(filp); 43 if (err) 44 return err; 45 46 flags = ext3_mask_flags(inode->i_mode, flags); 47 48 mutex_lock(&inode->i_mutex); 49 50 /* Is it quota file? Do not allow user to mess with it */ 51 err = -EPERM; 52 if (IS_NOQUOTA(inode)) 53 goto flags_out; 54 55 oldflags = ei->i_flags; 56 57 /* The JOURNAL_DATA flag is modifiable only by root */ 58 jflag = flags & EXT3_JOURNAL_DATA_FL; 59 60 /* 61 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 62 * the relevant capability. 63 * 64 * This test looks nicer. Thanks to Pauline Middelink 65 */ 66 if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) { 67 if (!capable(CAP_LINUX_IMMUTABLE)) 68 goto flags_out; 69 } 70 71 /* 72 * The JOURNAL_DATA flag can only be changed by 73 * the relevant capability. 74 */ 75 if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) { 76 if (!capable(CAP_SYS_RESOURCE)) 77 goto flags_out; 78 } 79 80 handle = ext3_journal_start(inode, 1); 81 if (IS_ERR(handle)) { 82 err = PTR_ERR(handle); 83 goto flags_out; 84 } 85 if (IS_SYNC(inode)) 86 handle->h_sync = 1; 87 err = ext3_reserve_inode_write(handle, inode, &iloc); 88 if (err) 89 goto flags_err; 90 91 flags = flags & EXT3_FL_USER_MODIFIABLE; 92 flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE; 93 ei->i_flags = flags; 94 95 ext3_set_inode_flags(inode); 96 inode->i_ctime = CURRENT_TIME_SEC; 97 98 err = ext3_mark_iloc_dirty(handle, inode, &iloc); 99flags_err: 100 ext3_journal_stop(handle); 101 if (err) 102 goto flags_out; 103 104 if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) 105 err = ext3_change_inode_journal_flag(inode, jflag); 106flags_out: 107 mutex_unlock(&inode->i_mutex); 108 mnt_drop_write_file(filp); 109 return err; 110 } 111 case EXT3_IOC_GETVERSION: 112 case EXT3_IOC_GETVERSION_OLD: 113 return put_user(inode->i_generation, (int __user *) arg); 114 case EXT3_IOC_SETVERSION: 115 case EXT3_IOC_SETVERSION_OLD: { 116 handle_t *handle; 117 struct ext3_iloc iloc; 118 __u32 generation; 119 int err; 120 121 if (!inode_owner_or_capable(inode)) 122 return -EPERM; 123 124 err = mnt_want_write_file(filp); 125 if (err) 126 return err; 127 if (get_user(generation, (int __user *) arg)) { 128 err = -EFAULT; 129 goto setversion_out; 130 } 131 132 mutex_lock(&inode->i_mutex); 133 handle = ext3_journal_start(inode, 1); 134 if (IS_ERR(handle)) { 135 err = PTR_ERR(handle); 136 goto unlock_out; 137 } 138 err = ext3_reserve_inode_write(handle, inode, &iloc); 139 if (err == 0) { 140 inode->i_ctime = CURRENT_TIME_SEC; 141 inode->i_generation = generation; 142 err = ext3_mark_iloc_dirty(handle, inode, &iloc); 143 } 144 ext3_journal_stop(handle); 145 146unlock_out: 147 mutex_unlock(&inode->i_mutex); 148setversion_out: 149 mnt_drop_write_file(filp); 150 return err; 151 } 152 case EXT3_IOC_GETRSVSZ: 153 if (test_opt(inode->i_sb, RESERVATION) 154 && S_ISREG(inode->i_mode) 155 && ei->i_block_alloc_info) { 156 rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; 157 return put_user(rsv_window_size, (int __user *)arg); 158 } 159 return -ENOTTY; 160 case EXT3_IOC_SETRSVSZ: { 161 int err; 162 163 if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) 164 return -ENOTTY; 165 166 err = mnt_want_write_file(filp); 167 if (err) 168 return err; 169 170 if (!inode_owner_or_capable(inode)) { 171 err = -EACCES; 172 goto setrsvsz_out; 173 } 174 175 if (get_user(rsv_window_size, (int __user *)arg)) { 176 err = -EFAULT; 177 goto setrsvsz_out; 178 } 179 180 if (rsv_window_size > EXT3_MAX_RESERVE_BLOCKS) 181 rsv_window_size = EXT3_MAX_RESERVE_BLOCKS; 182 183 /* 184 * need to allocate reservation structure for this inode 185 * before set the window size 186 */ 187 mutex_lock(&ei->truncate_mutex); 188 if (!ei->i_block_alloc_info) 189 ext3_init_block_alloc_info(inode); 190 191 if (ei->i_block_alloc_info){ 192 struct ext3_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; 193 rsv->rsv_goal_size = rsv_window_size; 194 } 195 mutex_unlock(&ei->truncate_mutex); 196setrsvsz_out: 197 mnt_drop_write_file(filp); 198 return err; 199 } 200 case EXT3_IOC_GROUP_EXTEND: { 201 ext3_fsblk_t n_blocks_count; 202 struct super_block *sb = inode->i_sb; 203 int err, err2; 204 205 if (!capable(CAP_SYS_RESOURCE)) 206 return -EPERM; 207 208 err = mnt_want_write_file(filp); 209 if (err) 210 return err; 211 212 if (get_user(n_blocks_count, (__u32 __user *)arg)) { 213 err = -EFAULT; 214 goto group_extend_out; 215 } 216 err = ext3_group_extend(sb, EXT3_SB(sb)->s_es, n_blocks_count); 217 journal_lock_updates(EXT3_SB(sb)->s_journal); 218 err2 = journal_flush(EXT3_SB(sb)->s_journal); 219 journal_unlock_updates(EXT3_SB(sb)->s_journal); 220 if (err == 0) 221 err = err2; 222group_extend_out: 223 mnt_drop_write_file(filp); 224 return err; 225 } 226 case EXT3_IOC_GROUP_ADD: { 227 struct ext3_new_group_data input; 228 struct super_block *sb = inode->i_sb; 229 int err, err2; 230 231 if (!capable(CAP_SYS_RESOURCE)) 232 return -EPERM; 233 234 err = mnt_want_write_file(filp); 235 if (err) 236 return err; 237 238 if (copy_from_user(&input, (struct ext3_new_group_input __user *)arg, 239 sizeof(input))) { 240 err = -EFAULT; 241 goto group_add_out; 242 } 243 244 err = ext3_group_add(sb, &input); 245 journal_lock_updates(EXT3_SB(sb)->s_journal); 246 err2 = journal_flush(EXT3_SB(sb)->s_journal); 247 journal_unlock_updates(EXT3_SB(sb)->s_journal); 248 if (err == 0) 249 err = err2; 250group_add_out: 251 mnt_drop_write_file(filp); 252 return err; 253 } 254 case FITRIM: { 255 256 struct super_block *sb = inode->i_sb; 257 struct fstrim_range range; 258 int ret = 0; 259 260 if (!capable(CAP_SYS_ADMIN)) 261 return -EPERM; 262 263 if (copy_from_user(&range, (struct fstrim_range __user *)arg, 264 sizeof(range))) 265 return -EFAULT; 266 267 ret = ext3_trim_fs(sb, &range); 268 if (ret < 0) 269 return ret; 270 271 if (copy_to_user((struct fstrim_range __user *)arg, &range, 272 sizeof(range))) 273 return -EFAULT; 274 275 return 0; 276 } 277 278 default: 279 return -ENOTTY; 280 } 281} 282 283#ifdef CONFIG_COMPAT 284long ext3_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 285{ 286 /* These are just misnamed, they actually get/put from/to user an int */ 287 switch (cmd) { 288 case EXT3_IOC32_GETFLAGS: 289 cmd = EXT3_IOC_GETFLAGS; 290 break; 291 case EXT3_IOC32_SETFLAGS: 292 cmd = EXT3_IOC_SETFLAGS; 293 break; 294 case EXT3_IOC32_GETVERSION: 295 cmd = EXT3_IOC_GETVERSION; 296 break; 297 case EXT3_IOC32_SETVERSION: 298 cmd = EXT3_IOC_SETVERSION; 299 break; 300 case EXT3_IOC32_GROUP_EXTEND: 301 cmd = EXT3_IOC_GROUP_EXTEND; 302 break; 303 case EXT3_IOC32_GETVERSION_OLD: 304 cmd = EXT3_IOC_GETVERSION_OLD; 305 break; 306 case EXT3_IOC32_SETVERSION_OLD: 307 cmd = EXT3_IOC_SETVERSION_OLD; 308 break; 309#ifdef CONFIG_JBD_DEBUG 310 case EXT3_IOC32_WAIT_FOR_READONLY: 311 cmd = EXT3_IOC_WAIT_FOR_READONLY; 312 break; 313#endif 314 case EXT3_IOC32_GETRSVSZ: 315 cmd = EXT3_IOC_GETRSVSZ; 316 break; 317 case EXT3_IOC32_SETRSVSZ: 318 cmd = EXT3_IOC_SETRSVSZ; 319 break; 320 case EXT3_IOC_GROUP_ADD: 321 break; 322 default: 323 return -ENOIOCTLCMD; 324 } 325 return ext3_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); 326} 327#endif 328