1 /* 2 * linux/fs/ext4/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/fs.h> 11 #include <linux/jbd2.h> 12 #include <linux/capability.h> 13 #include <linux/ext4_fs.h> 14 #include <linux/ext4_jbd2.h> 15 #include <linux/time.h> 16 #include <linux/compat.h> 17 #include <linux/smp_lock.h> 18 #include <asm/uaccess.h> 19 20 int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, 21 unsigned long arg) 22 { 23 struct ext4_inode_info *ei = EXT4_I(inode); 24 unsigned int flags; 25 unsigned short rsv_window_size; 26 27 ext4_debug ("cmd = %u, arg = %lu\n", cmd, arg); 28 29 switch (cmd) { 30 case EXT4_IOC_GETFLAGS: 31 ext4_get_inode_flags(ei); 32 flags = ei->i_flags & EXT4_FL_USER_VISIBLE; 33 return put_user(flags, (int __user *) arg); 34 case EXT4_IOC_SETFLAGS: { 35 handle_t *handle = NULL; 36 int err; 37 struct ext4_iloc iloc; 38 unsigned int oldflags; 39 unsigned int jflag; 40 41 if (IS_RDONLY(inode)) 42 return -EROFS; 43 44 if (!is_owner_or_cap(inode)) 45 return -EACCES; 46 47 if (get_user(flags, (int __user *) arg)) 48 return -EFAULT; 49 50 if (!S_ISDIR(inode->i_mode)) 51 flags &= ~EXT4_DIRSYNC_FL; 52 53 mutex_lock(&inode->i_mutex); 54 /* Is it quota file? Do not allow user to mess with it */ 55 if (IS_NOQUOTA(inode)) { 56 mutex_unlock(&inode->i_mutex); 57 return -EPERM; 58 } 59 oldflags = ei->i_flags; 60 61 /* The JOURNAL_DATA flag is modifiable only by root */ 62 jflag = flags & EXT4_JOURNAL_DATA_FL; 63 64 /* 65 * The IMMUTABLE and APPEND_ONLY flags can only be changed by 66 * the relevant capability. 67 * 68 * This test looks nicer. Thanks to Pauline Middelink 69 */ 70 if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { 71 if (!capable(CAP_LINUX_IMMUTABLE)) { 72 mutex_unlock(&inode->i_mutex); 73 return -EPERM; 74 } 75 } 76 77 /* 78 * The JOURNAL_DATA flag can only be changed by 79 * the relevant capability. 80 */ 81 if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { 82 if (!capable(CAP_SYS_RESOURCE)) { 83 mutex_unlock(&inode->i_mutex); 84 return -EPERM; 85 } 86 } 87 88 89 handle = ext4_journal_start(inode, 1); 90 if (IS_ERR(handle)) { 91 mutex_unlock(&inode->i_mutex); 92 return PTR_ERR(handle); 93 } 94 if (IS_SYNC(inode)) 95 handle->h_sync = 1; 96 err = ext4_reserve_inode_write(handle, inode, &iloc); 97 if (err) 98 goto flags_err; 99 100 flags = flags & EXT4_FL_USER_MODIFIABLE; 101 flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE; 102 ei->i_flags = flags; 103 104 ext4_set_inode_flags(inode); 105 inode->i_ctime = ext4_current_time(inode); 106 107 err = ext4_mark_iloc_dirty(handle, inode, &iloc); 108 flags_err: 109 ext4_journal_stop(handle); 110 if (err) { 111 mutex_unlock(&inode->i_mutex); 112 return err; 113 } 114 115 if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) 116 err = ext4_change_inode_journal_flag(inode, jflag); 117 mutex_unlock(&inode->i_mutex); 118 return err; 119 } 120 case EXT4_IOC_GETVERSION: 121 case EXT4_IOC_GETVERSION_OLD: 122 return put_user(inode->i_generation, (int __user *) arg); 123 case EXT4_IOC_SETVERSION: 124 case EXT4_IOC_SETVERSION_OLD: { 125 handle_t *handle; 126 struct ext4_iloc iloc; 127 __u32 generation; 128 int err; 129 130 if (!is_owner_or_cap(inode)) 131 return -EPERM; 132 if (IS_RDONLY(inode)) 133 return -EROFS; 134 if (get_user(generation, (int __user *) arg)) 135 return -EFAULT; 136 137 handle = ext4_journal_start(inode, 1); 138 if (IS_ERR(handle)) 139 return PTR_ERR(handle); 140 err = ext4_reserve_inode_write(handle, inode, &iloc); 141 if (err == 0) { 142 inode->i_ctime = ext4_current_time(inode); 143 inode->i_generation = generation; 144 err = ext4_mark_iloc_dirty(handle, inode, &iloc); 145 } 146 ext4_journal_stop(handle); 147 return err; 148 } 149 #ifdef CONFIG_JBD2_DEBUG 150 case EXT4_IOC_WAIT_FOR_READONLY: 151 /* 152 * This is racy - by the time we're woken up and running, 153 * the superblock could be released. And the module could 154 * have been unloaded. So sue me. 155 * 156 * Returns 1 if it slept, else zero. 157 */ 158 { 159 struct super_block *sb = inode->i_sb; 160 DECLARE_WAITQUEUE(wait, current); 161 int ret = 0; 162 163 set_current_state(TASK_INTERRUPTIBLE); 164 add_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait); 165 if (timer_pending(&EXT4_SB(sb)->turn_ro_timer)) { 166 schedule(); 167 ret = 1; 168 } 169 remove_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait); 170 return ret; 171 } 172 #endif 173 case EXT4_IOC_GETRSVSZ: 174 if (test_opt(inode->i_sb, RESERVATION) 175 && S_ISREG(inode->i_mode) 176 && ei->i_block_alloc_info) { 177 rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; 178 return put_user(rsv_window_size, (int __user *)arg); 179 } 180 return -ENOTTY; 181 case EXT4_IOC_SETRSVSZ: { 182 183 if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) 184 return -ENOTTY; 185 186 if (IS_RDONLY(inode)) 187 return -EROFS; 188 189 if (!is_owner_or_cap(inode)) 190 return -EACCES; 191 192 if (get_user(rsv_window_size, (int __user *)arg)) 193 return -EFAULT; 194 195 if (rsv_window_size > EXT4_MAX_RESERVE_BLOCKS) 196 rsv_window_size = EXT4_MAX_RESERVE_BLOCKS; 197 198 /* 199 * need to allocate reservation structure for this inode 200 * before set the window size 201 */ 202 down_write(&ei->i_data_sem); 203 if (!ei->i_block_alloc_info) 204 ext4_init_block_alloc_info(inode); 205 206 if (ei->i_block_alloc_info){ 207 struct ext4_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; 208 rsv->rsv_goal_size = rsv_window_size; 209 } 210 up_write(&ei->i_data_sem); 211 return 0; 212 } 213 case EXT4_IOC_GROUP_EXTEND: { 214 ext4_fsblk_t n_blocks_count; 215 struct super_block *sb = inode->i_sb; 216 int err; 217 218 if (!capable(CAP_SYS_RESOURCE)) 219 return -EPERM; 220 221 if (IS_RDONLY(inode)) 222 return -EROFS; 223 224 if (get_user(n_blocks_count, (__u32 __user *)arg)) 225 return -EFAULT; 226 227 err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); 228 jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); 229 jbd2_journal_flush(EXT4_SB(sb)->s_journal); 230 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 231 232 return err; 233 } 234 case EXT4_IOC_GROUP_ADD: { 235 struct ext4_new_group_data input; 236 struct super_block *sb = inode->i_sb; 237 int err; 238 239 if (!capable(CAP_SYS_RESOURCE)) 240 return -EPERM; 241 242 if (IS_RDONLY(inode)) 243 return -EROFS; 244 245 if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, 246 sizeof(input))) 247 return -EFAULT; 248 249 err = ext4_group_add(sb, &input); 250 jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); 251 jbd2_journal_flush(EXT4_SB(sb)->s_journal); 252 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); 253 254 return err; 255 } 256 257 case EXT4_IOC_MIGRATE: 258 return ext4_ext_migrate(inode, filp, cmd, arg); 259 260 default: 261 return -ENOTTY; 262 } 263 } 264 265 #ifdef CONFIG_COMPAT 266 long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 267 { 268 struct inode *inode = file->f_path.dentry->d_inode; 269 int ret; 270 271 /* These are just misnamed, they actually get/put from/to user an int */ 272 switch (cmd) { 273 case EXT4_IOC32_GETFLAGS: 274 cmd = EXT4_IOC_GETFLAGS; 275 break; 276 case EXT4_IOC32_SETFLAGS: 277 cmd = EXT4_IOC_SETFLAGS; 278 break; 279 case EXT4_IOC32_GETVERSION: 280 cmd = EXT4_IOC_GETVERSION; 281 break; 282 case EXT4_IOC32_SETVERSION: 283 cmd = EXT4_IOC_SETVERSION; 284 break; 285 case EXT4_IOC32_GROUP_EXTEND: 286 cmd = EXT4_IOC_GROUP_EXTEND; 287 break; 288 case EXT4_IOC32_GETVERSION_OLD: 289 cmd = EXT4_IOC_GETVERSION_OLD; 290 break; 291 case EXT4_IOC32_SETVERSION_OLD: 292 cmd = EXT4_IOC_SETVERSION_OLD; 293 break; 294 #ifdef CONFIG_JBD2_DEBUG 295 case EXT4_IOC32_WAIT_FOR_READONLY: 296 cmd = EXT4_IOC_WAIT_FOR_READONLY; 297 break; 298 #endif 299 case EXT4_IOC32_GETRSVSZ: 300 cmd = EXT4_IOC_GETRSVSZ; 301 break; 302 case EXT4_IOC32_SETRSVSZ: 303 cmd = EXT4_IOC_SETRSVSZ; 304 break; 305 case EXT4_IOC_GROUP_ADD: 306 break; 307 default: 308 return -ENOIOCTLCMD; 309 } 310 lock_kernel(); 311 ret = ext4_ioctl(inode, file, cmd, (unsigned long) compat_ptr(arg)); 312 unlock_kernel(); 313 return ret; 314 } 315 #endif 316