diff options
Diffstat (limited to 'fs/ext4/ioctl.c')
| -rw-r--r-- | fs/ext4/ioctl.c | 101 |
1 files changed, 74 insertions, 27 deletions
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index d26bcac291bb..3c4f8bb59f8a 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
| @@ -63,18 +63,20 @@ static void swap_inode_data(struct inode *inode1, struct inode *inode2) | |||
| 63 | loff_t isize; | 63 | loff_t isize; |
| 64 | struct ext4_inode_info *ei1; | 64 | struct ext4_inode_info *ei1; |
| 65 | struct ext4_inode_info *ei2; | 65 | struct ext4_inode_info *ei2; |
| 66 | unsigned long tmp; | ||
| 66 | 67 | ||
| 67 | ei1 = EXT4_I(inode1); | 68 | ei1 = EXT4_I(inode1); |
| 68 | ei2 = EXT4_I(inode2); | 69 | ei2 = EXT4_I(inode2); |
| 69 | 70 | ||
| 70 | swap(inode1->i_version, inode2->i_version); | 71 | swap(inode1->i_version, inode2->i_version); |
| 71 | swap(inode1->i_blocks, inode2->i_blocks); | ||
| 72 | swap(inode1->i_bytes, inode2->i_bytes); | ||
| 73 | swap(inode1->i_atime, inode2->i_atime); | 72 | swap(inode1->i_atime, inode2->i_atime); |
| 74 | swap(inode1->i_mtime, inode2->i_mtime); | 73 | swap(inode1->i_mtime, inode2->i_mtime); |
| 75 | 74 | ||
| 76 | memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data)); | 75 | memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data)); |
| 77 | swap(ei1->i_flags, ei2->i_flags); | 76 | tmp = ei1->i_flags & EXT4_FL_SHOULD_SWAP; |
| 77 | ei1->i_flags = (ei2->i_flags & EXT4_FL_SHOULD_SWAP) | | ||
| 78 | (ei1->i_flags & ~EXT4_FL_SHOULD_SWAP); | ||
| 79 | ei2->i_flags = tmp | (ei2->i_flags & ~EXT4_FL_SHOULD_SWAP); | ||
| 78 | swap(ei1->i_disksize, ei2->i_disksize); | 80 | swap(ei1->i_disksize, ei2->i_disksize); |
| 79 | ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS); | 81 | ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS); |
| 80 | ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS); | 82 | ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS); |
| @@ -115,28 +117,42 @@ static long swap_inode_boot_loader(struct super_block *sb, | |||
| 115 | int err; | 117 | int err; |
| 116 | struct inode *inode_bl; | 118 | struct inode *inode_bl; |
| 117 | struct ext4_inode_info *ei_bl; | 119 | struct ext4_inode_info *ei_bl; |
| 118 | 120 | qsize_t size, size_bl, diff; | |
| 119 | if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) || | 121 | blkcnt_t blocks; |
| 120 | IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) || | 122 | unsigned short bytes; |
| 121 | ext4_has_inline_data(inode)) | ||
| 122 | return -EINVAL; | ||
| 123 | |||
| 124 | if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) || | ||
| 125 | !inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) | ||
| 126 | return -EPERM; | ||
| 127 | 123 | ||
| 128 | inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO, EXT4_IGET_SPECIAL); | 124 | inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO, EXT4_IGET_SPECIAL); |
| 129 | if (IS_ERR(inode_bl)) | 125 | if (IS_ERR(inode_bl)) |
| 130 | return PTR_ERR(inode_bl); | 126 | return PTR_ERR(inode_bl); |
| 131 | ei_bl = EXT4_I(inode_bl); | 127 | ei_bl = EXT4_I(inode_bl); |
| 132 | 128 | ||
| 133 | filemap_flush(inode->i_mapping); | ||
| 134 | filemap_flush(inode_bl->i_mapping); | ||
| 135 | |||
| 136 | /* Protect orig inodes against a truncate and make sure, | 129 | /* Protect orig inodes against a truncate and make sure, |
| 137 | * that only 1 swap_inode_boot_loader is running. */ | 130 | * that only 1 swap_inode_boot_loader is running. */ |
| 138 | lock_two_nondirectories(inode, inode_bl); | 131 | lock_two_nondirectories(inode, inode_bl); |
| 139 | 132 | ||
| 133 | if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode) || | ||
| 134 | IS_SWAPFILE(inode) || IS_ENCRYPTED(inode) || | ||
| 135 | (EXT4_I(inode)->i_flags & EXT4_JOURNAL_DATA_FL) || | ||
| 136 | ext4_has_inline_data(inode)) { | ||
| 137 | err = -EINVAL; | ||
| 138 | goto journal_err_out; | ||
| 139 | } | ||
| 140 | |||
| 141 | if (IS_RDONLY(inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) || | ||
| 142 | !inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) { | ||
| 143 | err = -EPERM; | ||
| 144 | goto journal_err_out; | ||
| 145 | } | ||
| 146 | |||
| 147 | down_write(&EXT4_I(inode)->i_mmap_sem); | ||
| 148 | err = filemap_write_and_wait(inode->i_mapping); | ||
| 149 | if (err) | ||
| 150 | goto err_out; | ||
| 151 | |||
| 152 | err = filemap_write_and_wait(inode_bl->i_mapping); | ||
| 153 | if (err) | ||
| 154 | goto err_out; | ||
| 155 | |||
| 140 | /* Wait for all existing dio workers */ | 156 | /* Wait for all existing dio workers */ |
| 141 | inode_dio_wait(inode); | 157 | inode_dio_wait(inode); |
| 142 | inode_dio_wait(inode_bl); | 158 | inode_dio_wait(inode_bl); |
| @@ -147,7 +163,7 @@ static long swap_inode_boot_loader(struct super_block *sb, | |||
| 147 | handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2); | 163 | handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2); |
| 148 | if (IS_ERR(handle)) { | 164 | if (IS_ERR(handle)) { |
| 149 | err = -EINVAL; | 165 | err = -EINVAL; |
| 150 | goto journal_err_out; | 166 | goto err_out; |
| 151 | } | 167 | } |
| 152 | 168 | ||
| 153 | /* Protect extent tree against block allocations via delalloc */ | 169 | /* Protect extent tree against block allocations via delalloc */ |
| @@ -170,6 +186,13 @@ static long swap_inode_boot_loader(struct super_block *sb, | |||
| 170 | memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data)); | 186 | memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data)); |
| 171 | } | 187 | } |
| 172 | 188 | ||
| 189 | err = dquot_initialize(inode); | ||
| 190 | if (err) | ||
| 191 | goto err_out1; | ||
| 192 | |||
| 193 | size = (qsize_t)(inode->i_blocks) * (1 << 9) + inode->i_bytes; | ||
| 194 | size_bl = (qsize_t)(inode_bl->i_blocks) * (1 << 9) + inode_bl->i_bytes; | ||
| 195 | diff = size - size_bl; | ||
| 173 | swap_inode_data(inode, inode_bl); | 196 | swap_inode_data(inode, inode_bl); |
| 174 | 197 | ||
| 175 | inode->i_ctime = inode_bl->i_ctime = current_time(inode); | 198 | inode->i_ctime = inode_bl->i_ctime = current_time(inode); |
| @@ -183,27 +206,51 @@ static long swap_inode_boot_loader(struct super_block *sb, | |||
| 183 | 206 | ||
| 184 | err = ext4_mark_inode_dirty(handle, inode); | 207 | err = ext4_mark_inode_dirty(handle, inode); |
| 185 | if (err < 0) { | 208 | if (err < 0) { |
| 209 | /* No need to update quota information. */ | ||
| 186 | ext4_warning(inode->i_sb, | 210 | ext4_warning(inode->i_sb, |
| 187 | "couldn't mark inode #%lu dirty (err %d)", | 211 | "couldn't mark inode #%lu dirty (err %d)", |
| 188 | inode->i_ino, err); | 212 | inode->i_ino, err); |
| 189 | /* Revert all changes: */ | 213 | /* Revert all changes: */ |
| 190 | swap_inode_data(inode, inode_bl); | 214 | swap_inode_data(inode, inode_bl); |
| 191 | ext4_mark_inode_dirty(handle, inode); | 215 | ext4_mark_inode_dirty(handle, inode); |
| 192 | } else { | 216 | goto err_out1; |
| 193 | err = ext4_mark_inode_dirty(handle, inode_bl); | 217 | } |
| 194 | if (err < 0) { | 218 | |
| 195 | ext4_warning(inode_bl->i_sb, | 219 | blocks = inode_bl->i_blocks; |
| 196 | "couldn't mark inode #%lu dirty (err %d)", | 220 | bytes = inode_bl->i_bytes; |
| 197 | inode_bl->i_ino, err); | 221 | inode_bl->i_blocks = inode->i_blocks; |
| 198 | /* Revert all changes: */ | 222 | inode_bl->i_bytes = inode->i_bytes; |
| 199 | swap_inode_data(inode, inode_bl); | 223 | err = ext4_mark_inode_dirty(handle, inode_bl); |
| 200 | ext4_mark_inode_dirty(handle, inode); | 224 | if (err < 0) { |
| 201 | ext4_mark_inode_dirty(handle, inode_bl); | 225 | /* No need to update quota information. */ |
| 202 | } | 226 | ext4_warning(inode_bl->i_sb, |
| 227 | "couldn't mark inode #%lu dirty (err %d)", | ||
| 228 | inode_bl->i_ino, err); | ||
| 229 | goto revert; | ||
| 230 | } | ||
| 231 | |||
| 232 | /* Bootloader inode should not be counted into quota information. */ | ||
| 233 | if (diff > 0) | ||
| 234 | dquot_free_space(inode, diff); | ||
| 235 | else | ||
| 236 | err = dquot_alloc_space(inode, -1 * diff); | ||
| 237 | |||
| 238 | if (err < 0) { | ||
| 239 | revert: | ||
| 240 | /* Revert all changes: */ | ||
| 241 | inode_bl->i_blocks = blocks; | ||
| 242 | inode_bl->i_bytes = bytes; | ||
| 243 | swap_inode_data(inode, inode_bl); | ||
| 244 | ext4_mark_inode_dirty(handle, inode); | ||
| 245 | ext4_mark_inode_dirty(handle, inode_bl); | ||
| 203 | } | 246 | } |
| 247 | |||
| 248 | err_out1: | ||
| 204 | ext4_journal_stop(handle); | 249 | ext4_journal_stop(handle); |
| 205 | ext4_double_up_write_data_sem(inode, inode_bl); | 250 | ext4_double_up_write_data_sem(inode, inode_bl); |
| 206 | 251 | ||
| 252 | err_out: | ||
| 253 | up_write(&EXT4_I(inode)->i_mmap_sem); | ||
| 207 | journal_err_out: | 254 | journal_err_out: |
| 208 | unlock_two_nondirectories(inode, inode_bl); | 255 | unlock_two_nondirectories(inode, inode_bl); |
| 209 | iput(inode_bl); | 256 | iput(inode_bl); |
