aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/move_extent.c
diff options
context:
space:
mode:
authorDr. Tilmann Bubeck <t.bubeck@reinform.de>2013-04-08 12:54:05 -0400
committerTheodore Ts'o <tytso@mit.edu>2013-04-08 12:54:05 -0400
commit393d1d1d76933886d5e1ce603214c9987589c6d5 (patch)
tree2f2368a9d787ccb8e69f61a3e5023ef9c4abfd8b /fs/ext4/move_extent.c
parentf78ee70db40040e6f38a5134527d4760254d6683 (diff)
ext4: implementation of a new ioctl called EXT4_IOC_SWAP_BOOT
Add a new ioctl, EXT4_IOC_SWAP_BOOT which swaps i_blocks and associated attributes (like i_blocks, i_size, i_flags, ...) from the specified inode with inode EXT4_BOOT_LOADER_INO (#5). This is typically used to store a boot loader in a secure part of the filesystem, where it can't be changed by a normal user by accident. The data blocks of the previous boot loader will be associated with the given inode. This usercode program is a simple example of the usage: int main(int argc, char *argv[]) { int fd; int err; if ( argc != 2 ) { printf("usage: ext4-swap-boot-inode FILE-TO-SWAP\n"); exit(1); } fd = open(argv[1], O_WRONLY); if ( fd < 0 ) { perror("open"); exit(1); } err = ioctl(fd, EXT4_IOC_SWAP_BOOT); if ( err < 0 ) { perror("ioctl"); exit(1); } close(fd); exit(0); } [ Modified by Theodore Ts'o to fix a number of bugs in the original code.] Signed-off-by: Dr. Tilmann Bubeck <t.bubeck@reinform.de> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/move_extent.c')
-rw-r--r--fs/ext4/move_extent.c48
1 files changed, 25 insertions, 23 deletions
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 33e1c086858b..a2e696e16331 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -144,12 +144,13 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
144} 144}
145 145
146/** 146/**
147 * double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem 147 * ext4_double_down_write_data_sem - Acquire two inodes' write lock
148 * of i_data_sem
148 * 149 *
149 * Acquire write lock of i_data_sem of the two inodes 150 * Acquire write lock of i_data_sem of the two inodes
150 */ 151 */
151static void 152void
152double_down_write_data_sem(struct inode *first, struct inode *second) 153ext4_double_down_write_data_sem(struct inode *first, struct inode *second)
153{ 154{
154 if (first < second) { 155 if (first < second) {
155 down_write(&EXT4_I(first)->i_data_sem); 156 down_write(&EXT4_I(first)->i_data_sem);
@@ -162,14 +163,15 @@ double_down_write_data_sem(struct inode *first, struct inode *second)
162} 163}
163 164
164/** 165/**
165 * double_up_write_data_sem - Release two inodes' write lock of i_data_sem 166 * ext4_double_up_write_data_sem - Release two inodes' write lock of i_data_sem
166 * 167 *
167 * @orig_inode: original inode structure to be released its lock first 168 * @orig_inode: original inode structure to be released its lock first
168 * @donor_inode: donor inode structure to be released its lock second 169 * @donor_inode: donor inode structure to be released its lock second
169 * Release write lock of i_data_sem of two inodes (orig and donor). 170 * Release write lock of i_data_sem of two inodes (orig and donor).
170 */ 171 */
171static void 172void
172double_up_write_data_sem(struct inode *orig_inode, struct inode *donor_inode) 173ext4_double_up_write_data_sem(struct inode *orig_inode,
174 struct inode *donor_inode)
173{ 175{
174 up_write(&EXT4_I(orig_inode)->i_data_sem); 176 up_write(&EXT4_I(orig_inode)->i_data_sem);
175 up_write(&EXT4_I(donor_inode)->i_data_sem); 177 up_write(&EXT4_I(donor_inode)->i_data_sem);
@@ -976,7 +978,7 @@ again:
976 * necessary, just swap data blocks between orig and donor. 978 * necessary, just swap data blocks between orig and donor.
977 */ 979 */
978 if (uninit) { 980 if (uninit) {
979 double_down_write_data_sem(orig_inode, donor_inode); 981 ext4_double_down_write_data_sem(orig_inode, donor_inode);
980 /* If any of extents in range became initialized we have to 982 /* If any of extents in range became initialized we have to
981 * fallback to data copying */ 983 * fallback to data copying */
982 uninit = mext_check_coverage(orig_inode, orig_blk_offset, 984 uninit = mext_check_coverage(orig_inode, orig_blk_offset,
@@ -990,7 +992,7 @@ again:
990 goto drop_data_sem; 992 goto drop_data_sem;
991 993
992 if (!uninit) { 994 if (!uninit) {
993 double_up_write_data_sem(orig_inode, donor_inode); 995 ext4_double_up_write_data_sem(orig_inode, donor_inode);
994 goto data_copy; 996 goto data_copy;
995 } 997 }
996 if ((page_has_private(pagep[0]) && 998 if ((page_has_private(pagep[0]) &&
@@ -1004,7 +1006,7 @@ again:
1004 donor_inode, orig_blk_offset, 1006 donor_inode, orig_blk_offset,
1005 block_len_in_page, err); 1007 block_len_in_page, err);
1006 drop_data_sem: 1008 drop_data_sem:
1007 double_up_write_data_sem(orig_inode, donor_inode); 1009 ext4_double_up_write_data_sem(orig_inode, donor_inode);
1008 goto unlock_pages; 1010 goto unlock_pages;
1009 } 1011 }
1010data_copy: 1012data_copy:
@@ -1065,11 +1067,11 @@ repair_branches:
1065 * Extents are swapped already, but we are not able to copy data. 1067 * Extents are swapped already, but we are not able to copy data.
1066 * Try to swap extents to it's original places 1068 * Try to swap extents to it's original places
1067 */ 1069 */
1068 double_down_write_data_sem(orig_inode, donor_inode); 1070 ext4_double_down_write_data_sem(orig_inode, donor_inode);
1069 replaced_count = mext_replace_branches(handle, donor_inode, orig_inode, 1071 replaced_count = mext_replace_branches(handle, donor_inode, orig_inode,
1070 orig_blk_offset, 1072 orig_blk_offset,
1071 block_len_in_page, &err2); 1073 block_len_in_page, &err2);
1072 double_up_write_data_sem(orig_inode, donor_inode); 1074 ext4_double_up_write_data_sem(orig_inode, donor_inode);
1073 if (replaced_count != block_len_in_page) { 1075 if (replaced_count != block_len_in_page) {
1074 EXT4_ERROR_INODE_BLOCK(orig_inode, (sector_t)(orig_blk_offset), 1076 EXT4_ERROR_INODE_BLOCK(orig_inode, (sector_t)(orig_blk_offset),
1075 "Unable to copy data block," 1077 "Unable to copy data block,"
@@ -1209,15 +1211,15 @@ mext_check_arguments(struct inode *orig_inode,
1209} 1211}
1210 1212
1211/** 1213/**
1212 * mext_inode_double_lock - Lock i_mutex on both @inode1 and @inode2 1214 * ext4_inode_double_lock - Lock i_mutex on both @inode1 and @inode2
1213 * 1215 *
1214 * @inode1: the inode structure 1216 * @inode1: the inode structure
1215 * @inode2: the inode structure 1217 * @inode2: the inode structure
1216 * 1218 *
1217 * Lock two inodes' i_mutex 1219 * Lock two inodes' i_mutex
1218 */ 1220 */
1219static void 1221void
1220mext_inode_double_lock(struct inode *inode1, struct inode *inode2) 1222ext4_inode_double_lock(struct inode *inode1, struct inode *inode2)
1221{ 1223{
1222 BUG_ON(inode1 == inode2); 1224 BUG_ON(inode1 == inode2);
1223 if (inode1 < inode2) { 1225 if (inode1 < inode2) {
@@ -1230,15 +1232,15 @@ mext_inode_double_lock(struct inode *inode1, struct inode *inode2)
1230} 1232}
1231 1233
1232/** 1234/**
1233 * mext_inode_double_unlock - Release i_mutex on both @inode1 and @inode2 1235 * ext4_inode_double_unlock - Release i_mutex on both @inode1 and @inode2
1234 * 1236 *
1235 * @inode1: the inode that is released first 1237 * @inode1: the inode that is released first
1236 * @inode2: the inode that is released second 1238 * @inode2: the inode that is released second
1237 * 1239 *
1238 */ 1240 */
1239 1241
1240static void 1242void
1241mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) 1243ext4_inode_double_unlock(struct inode *inode1, struct inode *inode2)
1242{ 1244{
1243 mutex_unlock(&inode1->i_mutex); 1245 mutex_unlock(&inode1->i_mutex);
1244 mutex_unlock(&inode2->i_mutex); 1246 mutex_unlock(&inode2->i_mutex);
@@ -1333,7 +1335,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1333 return -EINVAL; 1335 return -EINVAL;
1334 } 1336 }
1335 /* Protect orig and donor inodes against a truncate */ 1337 /* Protect orig and donor inodes against a truncate */
1336 mext_inode_double_lock(orig_inode, donor_inode); 1338 ext4_inode_double_lock(orig_inode, donor_inode);
1337 1339
1338 /* Wait for all existing dio workers */ 1340 /* Wait for all existing dio workers */
1339 ext4_inode_block_unlocked_dio(orig_inode); 1341 ext4_inode_block_unlocked_dio(orig_inode);
@@ -1342,7 +1344,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1342 inode_dio_wait(donor_inode); 1344 inode_dio_wait(donor_inode);
1343 1345
1344 /* Protect extent tree against block allocations via delalloc */ 1346 /* Protect extent tree against block allocations via delalloc */
1345 double_down_write_data_sem(orig_inode, donor_inode); 1347 ext4_double_down_write_data_sem(orig_inode, donor_inode);
1346 /* Check the filesystem environment whether move_extent can be done */ 1348 /* Check the filesystem environment whether move_extent can be done */
1347 ret = mext_check_arguments(orig_inode, donor_inode, orig_start, 1349 ret = mext_check_arguments(orig_inode, donor_inode, orig_start,
1348 donor_start, &len); 1350 donor_start, &len);
@@ -1466,7 +1468,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1466 * b. racing with ->readpage, ->write_begin, and ext4_get_block 1468 * b. racing with ->readpage, ->write_begin, and ext4_get_block
1467 * in move_extent_per_page 1469 * in move_extent_per_page
1468 */ 1470 */
1469 double_up_write_data_sem(orig_inode, donor_inode); 1471 ext4_double_up_write_data_sem(orig_inode, donor_inode);
1470 1472
1471 while (orig_page_offset <= seq_end_page) { 1473 while (orig_page_offset <= seq_end_page) {
1472 1474
@@ -1500,7 +1502,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1500 block_len_in_page = rest_blocks; 1502 block_len_in_page = rest_blocks;
1501 } 1503 }
1502 1504
1503 double_down_write_data_sem(orig_inode, donor_inode); 1505 ext4_double_down_write_data_sem(orig_inode, donor_inode);
1504 if (ret < 0) 1506 if (ret < 0)
1505 break; 1507 break;
1506 1508
@@ -1538,10 +1540,10 @@ out:
1538 ext4_ext_drop_refs(holecheck_path); 1540 ext4_ext_drop_refs(holecheck_path);
1539 kfree(holecheck_path); 1541 kfree(holecheck_path);
1540 } 1542 }
1541 double_up_write_data_sem(orig_inode, donor_inode); 1543 ext4_double_up_write_data_sem(orig_inode, donor_inode);
1542 ext4_inode_resume_unlocked_dio(orig_inode); 1544 ext4_inode_resume_unlocked_dio(orig_inode);
1543 ext4_inode_resume_unlocked_dio(donor_inode); 1545 ext4_inode_resume_unlocked_dio(donor_inode);
1544 mext_inode_double_unlock(orig_inode, donor_inode); 1546 ext4_inode_double_unlock(orig_inode, donor_inode);
1545 1547
1546 return ret; 1548 return ret;
1547} 1549}