aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/ioctl.c')
-rw-r--r--fs/ext4/ioctl.c101
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) {
239revert:
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
248err_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
252err_out:
253 up_write(&EXT4_I(inode)->i_mmap_sem);
207journal_err_out: 254journal_err_out:
208 unlock_two_nondirectories(inode, inode_bl); 255 unlock_two_nondirectories(inode, inode_bl);
209 iput(inode_bl); 256 iput(inode_bl);