diff options
Diffstat (limited to 'fs/ext4/ioctl.c')
| -rw-r--r-- | fs/ext4/ioctl.c | 96 |
1 files changed, 45 insertions, 51 deletions
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 7a6c2f1faba6..dc99b4776d58 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
| @@ -23,9 +23,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 23 | struct inode *inode = filp->f_dentry->d_inode; | 23 | struct inode *inode = filp->f_dentry->d_inode; |
| 24 | struct ext4_inode_info *ei = EXT4_I(inode); | 24 | struct ext4_inode_info *ei = EXT4_I(inode); |
| 25 | unsigned int flags; | 25 | unsigned int flags; |
| 26 | unsigned short rsv_window_size; | ||
| 27 | 26 | ||
| 28 | ext4_debug ("cmd = %u, arg = %lu\n", cmd, arg); | 27 | ext4_debug("cmd = %u, arg = %lu\n", cmd, arg); |
| 29 | 28 | ||
| 30 | switch (cmd) { | 29 | switch (cmd) { |
| 31 | case EXT4_IOC_GETFLAGS: | 30 | case EXT4_IOC_GETFLAGS: |
| @@ -34,7 +33,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 34 | return put_user(flags, (int __user *) arg); | 33 | return put_user(flags, (int __user *) arg); |
| 35 | case EXT4_IOC_SETFLAGS: { | 34 | case EXT4_IOC_SETFLAGS: { |
| 36 | handle_t *handle = NULL; | 35 | handle_t *handle = NULL; |
| 37 | int err; | 36 | int err, migrate = 0; |
| 38 | struct ext4_iloc iloc; | 37 | struct ext4_iloc iloc; |
| 39 | unsigned int oldflags; | 38 | unsigned int oldflags; |
| 40 | unsigned int jflag; | 39 | unsigned int jflag; |
| @@ -82,6 +81,17 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
| 82 | if (!capable(CAP_SYS_RESOURCE)) | 81 | if (!capable(CAP_SYS_RESOURCE)) |
| 83 | goto flags_out; | 82 | goto flags_out; |
| 84 | } | 83 | } |
| 84 | if (oldflags & EXT4_EXTENTS_FL) { | ||
| 85 | /* We don't support clearning extent flags */ | ||
| 86 | if (!(flags & EXT4_EXTENTS_FL)) { | ||
| 87 | err = -EOPNOTSUPP; | ||
| 88 | goto flags_out; | ||
| 89 | } | ||
| 90 | } else if (flags & EXT4_EXTENTS_FL) { | ||
| 91 | /* migrate the file */ | ||
| 92 | migrate = 1; | ||
| 93 | flags &= ~EXT4_EXTENTS_FL; | ||
| 94 | } | ||
| 85 | 95 | ||
| 86 | handle = ext4_journal_start(inode, 1); | 96 | handle = ext4_journal_start(inode, 1); |
| 87 | if (IS_ERR(handle)) { | 97 | if (IS_ERR(handle)) { |
| @@ -109,6 +119,10 @@ flags_err: | |||
| 109 | 119 | ||
| 110 | if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) | 120 | if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) |
| 111 | err = ext4_change_inode_journal_flag(inode, jflag); | 121 | err = ext4_change_inode_journal_flag(inode, jflag); |
| 122 | if (err) | ||
| 123 | goto flags_out; | ||
| 124 | if (migrate) | ||
| 125 | err = ext4_ext_migrate(inode); | ||
| 112 | flags_out: | 126 | flags_out: |
| 113 | mutex_unlock(&inode->i_mutex); | 127 | mutex_unlock(&inode->i_mutex); |
| 114 | mnt_drop_write(filp->f_path.mnt); | 128 | mnt_drop_write(filp->f_path.mnt); |
| @@ -175,53 +189,10 @@ setversion_out: | |||
| 175 | return ret; | 189 | return ret; |
| 176 | } | 190 | } |
| 177 | #endif | 191 | #endif |
| 178 | case EXT4_IOC_GETRSVSZ: | ||
| 179 | if (test_opt(inode->i_sb, RESERVATION) | ||
| 180 | && S_ISREG(inode->i_mode) | ||
| 181 | && ei->i_block_alloc_info) { | ||
| 182 | rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; | ||
| 183 | return put_user(rsv_window_size, (int __user *)arg); | ||
| 184 | } | ||
| 185 | return -ENOTTY; | ||
| 186 | case EXT4_IOC_SETRSVSZ: { | ||
| 187 | int err; | ||
| 188 | |||
| 189 | if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) | ||
| 190 | return -ENOTTY; | ||
| 191 | |||
| 192 | if (!is_owner_or_cap(inode)) | ||
| 193 | return -EACCES; | ||
| 194 | |||
| 195 | if (get_user(rsv_window_size, (int __user *)arg)) | ||
| 196 | return -EFAULT; | ||
| 197 | |||
| 198 | err = mnt_want_write(filp->f_path.mnt); | ||
| 199 | if (err) | ||
| 200 | return err; | ||
| 201 | |||
| 202 | if (rsv_window_size > EXT4_MAX_RESERVE_BLOCKS) | ||
| 203 | rsv_window_size = EXT4_MAX_RESERVE_BLOCKS; | ||
| 204 | |||
| 205 | /* | ||
| 206 | * need to allocate reservation structure for this inode | ||
| 207 | * before set the window size | ||
| 208 | */ | ||
| 209 | down_write(&ei->i_data_sem); | ||
| 210 | if (!ei->i_block_alloc_info) | ||
| 211 | ext4_init_block_alloc_info(inode); | ||
| 212 | |||
| 213 | if (ei->i_block_alloc_info){ | ||
| 214 | struct ext4_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; | ||
| 215 | rsv->rsv_goal_size = rsv_window_size; | ||
| 216 | } | ||
| 217 | up_write(&ei->i_data_sem); | ||
| 218 | mnt_drop_write(filp->f_path.mnt); | ||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | case EXT4_IOC_GROUP_EXTEND: { | 192 | case EXT4_IOC_GROUP_EXTEND: { |
| 222 | ext4_fsblk_t n_blocks_count; | 193 | ext4_fsblk_t n_blocks_count; |
| 223 | struct super_block *sb = inode->i_sb; | 194 | struct super_block *sb = inode->i_sb; |
| 224 | int err; | 195 | int err, err2; |
| 225 | 196 | ||
| 226 | if (!capable(CAP_SYS_RESOURCE)) | 197 | if (!capable(CAP_SYS_RESOURCE)) |
| 227 | return -EPERM; | 198 | return -EPERM; |
| @@ -235,8 +206,10 @@ setversion_out: | |||
| 235 | 206 | ||
| 236 | err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); | 207 | err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); |
| 237 | jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); | 208 | jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); |
| 238 | jbd2_journal_flush(EXT4_SB(sb)->s_journal); | 209 | err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); |
| 239 | jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); | 210 | jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); |
| 211 | if (err == 0) | ||
| 212 | err = err2; | ||
| 240 | mnt_drop_write(filp->f_path.mnt); | 213 | mnt_drop_write(filp->f_path.mnt); |
| 241 | 214 | ||
| 242 | return err; | 215 | return err; |
| @@ -244,7 +217,7 @@ setversion_out: | |||
| 244 | case EXT4_IOC_GROUP_ADD: { | 217 | case EXT4_IOC_GROUP_ADD: { |
| 245 | struct ext4_new_group_data input; | 218 | struct ext4_new_group_data input; |
| 246 | struct super_block *sb = inode->i_sb; | 219 | struct super_block *sb = inode->i_sb; |
| 247 | int err; | 220 | int err, err2; |
| 248 | 221 | ||
| 249 | if (!capable(CAP_SYS_RESOURCE)) | 222 | if (!capable(CAP_SYS_RESOURCE)) |
| 250 | return -EPERM; | 223 | return -EPERM; |
| @@ -259,15 +232,36 @@ setversion_out: | |||
| 259 | 232 | ||
| 260 | err = ext4_group_add(sb, &input); | 233 | err = ext4_group_add(sb, &input); |
| 261 | jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); | 234 | jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); |
| 262 | jbd2_journal_flush(EXT4_SB(sb)->s_journal); | 235 | err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); |
| 263 | jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); | 236 | jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); |
| 237 | if (err == 0) | ||
| 238 | err = err2; | ||
| 264 | mnt_drop_write(filp->f_path.mnt); | 239 | mnt_drop_write(filp->f_path.mnt); |
| 265 | 240 | ||
| 266 | return err; | 241 | return err; |
| 267 | } | 242 | } |
| 268 | 243 | ||
| 269 | case EXT4_IOC_MIGRATE: | 244 | case EXT4_IOC_MIGRATE: |
| 270 | return ext4_ext_migrate(inode, filp, cmd, arg); | 245 | { |
| 246 | int err; | ||
| 247 | if (!is_owner_or_cap(inode)) | ||
| 248 | return -EACCES; | ||
| 249 | |||
| 250 | err = mnt_want_write(filp->f_path.mnt); | ||
| 251 | if (err) | ||
| 252 | return err; | ||
| 253 | /* | ||
| 254 | * inode_mutex prevent write and truncate on the file. | ||
| 255 | * Read still goes through. We take i_data_sem in | ||
| 256 | * ext4_ext_swap_inode_data before we switch the | ||
| 257 | * inode format to prevent read. | ||
| 258 | */ | ||
| 259 | mutex_lock(&(inode->i_mutex)); | ||
| 260 | err = ext4_ext_migrate(inode); | ||
| 261 | mutex_unlock(&(inode->i_mutex)); | ||
| 262 | mnt_drop_write(filp->f_path.mnt); | ||
| 263 | return err; | ||
| 264 | } | ||
| 271 | 265 | ||
| 272 | default: | 266 | default: |
| 273 | return -ENOTTY; | 267 | return -ENOTTY; |
