1 /* 2 * linux/fs/ext2/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 "ext2.h" 11 #include <linux/capability.h> 12 #include <linux/time.h> 13 #include <linux/sched.h> 14 #include <linux/compat.h> 15 #include <linux/mount.h> 16 #include <linux/smp_lock.h> 17 #include <asm/current.h> 18 #include <asm/uaccess.h> 19 20 21 long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 22 { 23 struct inode *inode = filp->f_dentry->d_inode; 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 ext2_get_inode_flags(ei); 34 flags = ei->i_flags & EXT2_FL_USER_VISIBLE; 35 return put_user(flags, (int __user *) arg); 36 case EXT2_IOC_SETFLAGS: { 37 unsigned int oldflags; 38 39 ret = mnt_want_write(filp->f_path.mnt); 40 if (ret) 41 return ret; 42 43 if (!is_owner_or_cap(inode)) { 44 ret = -EACCES; 45 goto setflags_out; 46 } 47 48 if (get_user(flags, (int __user *) arg)) { 49 ret = -EFAULT; 50 goto setflags_out; 51 } 52 53 flags = ext2_mask_flags(inode->i_mode, flags); 54 55 mutex_lock(&inode->i_mutex); 56 /* Is it quota file? Do not allow user to mess with it */ 57 if (IS_NOQUOTA(inode)) { 58 mutex_unlock(&inode->i_mutex); 59 ret = -EPERM; 60 goto setflags_out; 61 } 62 oldflags = ei->i_flags; 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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) { 71 if (!capable(CAP_LINUX_IMMUTABLE)) { 72 mutex_unlock(&inode->i_mutex); 73 ret = -EPERM; 74 goto setflags_out; 75 } 76 } 77 78 flags = flags & EXT2_FL_USER_MODIFIABLE; 79 flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE; 80 ei->i_flags = flags; 81 mutex_unlock(&inode->i_mutex); 82 83 ext2_set_inode_flags(inode); 84 inode->i_ctime = CURRENT_TIME_SEC; 85 mark_inode_dirty(inode); 86 setflags_out: 87 mnt_drop_write(filp->f_path.mnt); 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 if (!is_owner_or_cap(inode)) 94 return -EPERM; 95 ret = mnt_want_write(filp->f_path.mnt); 96 if (ret) 97 return ret; 98 if (get_user(inode->i_generation, (int __user *) arg)) { 99 ret = -EFAULT; 100 } else { 101 inode->i_ctime = CURRENT_TIME_SEC; 102 mark_inode_dirty(inode); 103 } 104 mnt_drop_write(filp->f_path.mnt); 105 return ret; 106 case EXT2_IOC_GETRSVSZ: 107 if (test_opt(inode->i_sb, RESERVATION) 108 && S_ISREG(inode->i_mode) 109 && ei->i_block_alloc_info) { 110 rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; 111 return put_user(rsv_window_size, (int __user *)arg); 112 } 113 return -ENOTTY; 114 case EXT2_IOC_SETRSVSZ: { 115 116 if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) 117 return -ENOTTY; 118 119 if (!is_owner_or_cap(inode)) 120 return -EACCES; 121 122 if (get_user(rsv_window_size, (int __user *)arg)) 123 return -EFAULT; 124 125 ret = mnt_want_write(filp->f_path.mnt); 126 if (ret) 127 return ret; 128 129 if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS) 130 rsv_window_size = EXT2_MAX_RESERVE_BLOCKS; 131 132 /* 133 * need to allocate reservation structure for this inode 134 * before set the window size 135 */ 136 /* 137 * XXX What lock should protect the rsv_goal_size? 138 * Accessed in ext2_get_block only. ext3 uses i_truncate. 139 */ 140 mutex_lock(&ei->truncate_mutex); 141 if (!ei->i_block_alloc_info) 142 ext2_init_block_alloc_info(inode); 143 144 if (ei->i_block_alloc_info){ 145 struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; 146 rsv->rsv_goal_size = rsv_window_size; 147 } 148 mutex_unlock(&ei->truncate_mutex); 149 mnt_drop_write(filp->f_path.mnt); 150 return 0; 151 } 152 default: 153 return -ENOTTY; 154 } 155 } 156 157 #ifdef CONFIG_COMPAT 158 long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 159 { 160 /* These are just misnamed, they actually get/put from/to user an int */ 161 switch (cmd) { 162 case EXT2_IOC32_GETFLAGS: 163 cmd = EXT2_IOC_GETFLAGS; 164 break; 165 case EXT2_IOC32_SETFLAGS: 166 cmd = EXT2_IOC_SETFLAGS; 167 break; 168 case EXT2_IOC32_GETVERSION: 169 cmd = EXT2_IOC_GETVERSION; 170 break; 171 case EXT2_IOC32_SETVERSION: 172 cmd = EXT2_IOC_SETVERSION; 173 break; 174 default: 175 return -ENOIOCTLCMD; 176 } 177 return ext2_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); 178 } 179 #endif 180