diff options
author | Dr. Tilmann Bubeck <t.bubeck@reinform.de> | 2013-04-08 12:54:05 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2013-04-08 12:54:05 -0400 |
commit | 393d1d1d76933886d5e1ce603214c9987589c6d5 (patch) | |
tree | 2f2368a9d787ccb8e69f61a3e5023ef9c4abfd8b /fs/ext4/move_extent.c | |
parent | f78ee70db40040e6f38a5134527d4760254d6683 (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.c | 48 |
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 | */ |
151 | static void | 152 | void |
152 | double_down_write_data_sem(struct inode *first, struct inode *second) | 153 | ext4_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 | */ |
171 | static void | 172 | void |
172 | double_up_write_data_sem(struct inode *orig_inode, struct inode *donor_inode) | 173 | ext4_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 | } |
1010 | data_copy: | 1012 | data_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 | */ |
1219 | static void | 1221 | void |
1220 | mext_inode_double_lock(struct inode *inode1, struct inode *inode2) | 1222 | ext4_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 | ||
1240 | static void | 1242 | void |
1241 | mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) | 1243 | ext4_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 | } |