aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/ioctl.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/ioctl.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/ioctl.c')
-rw-r--r--fs/ext4/ioctl.c197
1 files changed, 197 insertions, 0 deletions
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index a07b7bc0856a..cbc3acea6bcf 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -17,9 +17,201 @@
17#include <asm/uaccess.h> 17#include <asm/uaccess.h>
18#include "ext4_jbd2.h" 18#include "ext4_jbd2.h"
19#include "ext4.h" 19#include "ext4.h"
20#include "ext4_extents.h"
20 21
21#define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1) 22#define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1)
22 23
24/**
25 * Swap memory between @a and @b for @len bytes.
26 *
27 * @a: pointer to first memory area
28 * @b: pointer to second memory area
29 * @len: number of bytes to swap
30 *
31 */
32static void memswap(void *a, void *b, size_t len)
33{
34 unsigned char *ap, *bp;
35 unsigned char tmp;
36
37 ap = (unsigned char *)a;
38 bp = (unsigned char *)b;
39 while (len-- > 0) {
40 tmp = *ap;
41 *ap = *bp;
42 *bp = tmp;
43 ap++;
44 bp++;
45 }
46}
47
48/**
49 * Swap i_data and associated attributes between @inode1 and @inode2.
50 * This function is used for the primary swap between inode1 and inode2
51 * and also to revert this primary swap in case of errors.
52 *
53 * Therefore you have to make sure, that calling this method twice
54 * will revert all changes.
55 *
56 * @inode1: pointer to first inode
57 * @inode2: pointer to second inode
58 */
59static void swap_inode_data(struct inode *inode1, struct inode *inode2)
60{
61 loff_t isize;
62 struct ext4_inode_info *ei1;
63 struct ext4_inode_info *ei2;
64
65 ei1 = EXT4_I(inode1);
66 ei2 = EXT4_I(inode2);
67
68 memswap(&inode1->i_flags, &inode2->i_flags, sizeof(inode1->i_flags));
69 memswap(&inode1->i_version, &inode2->i_version,
70 sizeof(inode1->i_version));
71 memswap(&inode1->i_blocks, &inode2->i_blocks,
72 sizeof(inode1->i_blocks));
73 memswap(&inode1->i_bytes, &inode2->i_bytes, sizeof(inode1->i_bytes));
74 memswap(&inode1->i_atime, &inode2->i_atime, sizeof(inode1->i_atime));
75 memswap(&inode1->i_mtime, &inode2->i_mtime, sizeof(inode1->i_mtime));
76
77 memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data));
78 memswap(&ei1->i_flags, &ei2->i_flags, sizeof(ei1->i_flags));
79 memswap(&ei1->i_disksize, &ei2->i_disksize, sizeof(ei1->i_disksize));
80 memswap(&ei1->i_es_tree, &ei2->i_es_tree, sizeof(ei1->i_es_tree));
81 memswap(&ei1->i_es_lru_nr, &ei2->i_es_lru_nr, sizeof(ei1->i_es_lru_nr));
82
83 isize = i_size_read(inode1);
84 i_size_write(inode1, i_size_read(inode2));
85 i_size_write(inode2, isize);
86}
87
88/**
89 * Swap the information from the given @inode and the inode
90 * EXT4_BOOT_LOADER_INO. It will basically swap i_data and all other
91 * important fields of the inodes.
92 *
93 * @sb: the super block of the filesystem
94 * @inode: the inode to swap with EXT4_BOOT_LOADER_INO
95 *
96 */
97static long swap_inode_boot_loader(struct super_block *sb,
98 struct inode *inode)
99{
100 handle_t *handle;
101 int err;
102 struct inode *inode_bl;
103 struct ext4_inode_info *ei;
104 struct ext4_inode_info *ei_bl;
105 struct ext4_sb_info *sbi;
106
107 if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode)) {
108 err = -EINVAL;
109 goto swap_boot_out;
110 }
111
112 if (!inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) {
113 err = -EPERM;
114 goto swap_boot_out;
115 }
116
117 sbi = EXT4_SB(sb);
118 ei = EXT4_I(inode);
119
120 inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO);
121 if (IS_ERR(inode_bl)) {
122 err = PTR_ERR(inode_bl);
123 goto swap_boot_out;
124 }
125 ei_bl = EXT4_I(inode_bl);
126
127 filemap_flush(inode->i_mapping);
128 filemap_flush(inode_bl->i_mapping);
129
130 /* Protect orig inodes against a truncate and make sure,
131 * that only 1 swap_inode_boot_loader is running. */
132 ext4_inode_double_lock(inode, inode_bl);
133
134 truncate_inode_pages(&inode->i_data, 0);
135 truncate_inode_pages(&inode_bl->i_data, 0);
136
137 /* Wait for all existing dio workers */
138 ext4_inode_block_unlocked_dio(inode);
139 ext4_inode_block_unlocked_dio(inode_bl);
140 inode_dio_wait(inode);
141 inode_dio_wait(inode_bl);
142
143 handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2);
144 if (IS_ERR(handle)) {
145 err = -EINVAL;
146 goto swap_boot_out;
147 }
148
149 /* Protect extent tree against block allocations via delalloc */
150 ext4_double_down_write_data_sem(inode, inode_bl);
151
152 if (inode_bl->i_nlink == 0) {
153 /* this inode has never been used as a BOOT_LOADER */
154 set_nlink(inode_bl, 1);
155 i_uid_write(inode_bl, 0);
156 i_gid_write(inode_bl, 0);
157 inode_bl->i_flags = 0;
158 ei_bl->i_flags = 0;
159 inode_bl->i_version = 1;
160 i_size_write(inode_bl, 0);
161 inode_bl->i_mode = S_IFREG;
162 if (EXT4_HAS_INCOMPAT_FEATURE(sb,
163 EXT4_FEATURE_INCOMPAT_EXTENTS)) {
164 ext4_set_inode_flag(inode_bl, EXT4_INODE_EXTENTS);
165 ext4_ext_tree_init(handle, inode_bl);
166 } else
167 memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data));
168 }
169
170 swap_inode_data(inode, inode_bl);
171
172 inode->i_ctime = inode_bl->i_ctime = ext4_current_time(inode);
173
174 spin_lock(&sbi->s_next_gen_lock);
175 inode->i_generation = sbi->s_next_generation++;
176 inode_bl->i_generation = sbi->s_next_generation++;
177 spin_unlock(&sbi->s_next_gen_lock);
178
179 ext4_discard_preallocations(inode);
180
181 err = ext4_mark_inode_dirty(handle, inode);
182 if (err < 0) {
183 ext4_warning(inode->i_sb,
184 "couldn't mark inode #%lu dirty (err %d)",
185 inode->i_ino, err);
186 /* Revert all changes: */
187 swap_inode_data(inode, inode_bl);
188 } else {
189 err = ext4_mark_inode_dirty(handle, inode_bl);
190 if (err < 0) {
191 ext4_warning(inode_bl->i_sb,
192 "couldn't mark inode #%lu dirty (err %d)",
193 inode_bl->i_ino, err);
194 /* Revert all changes: */
195 swap_inode_data(inode, inode_bl);
196 ext4_mark_inode_dirty(handle, inode);
197 }
198 }
199
200 ext4_journal_stop(handle);
201
202 ext4_double_up_write_data_sem(inode, inode_bl);
203
204 ext4_inode_resume_unlocked_dio(inode);
205 ext4_inode_resume_unlocked_dio(inode_bl);
206
207 ext4_inode_double_unlock(inode, inode_bl);
208
209 iput(inode_bl);
210
211swap_boot_out:
212 return err;
213}
214
23long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 215long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
24{ 216{
25 struct inode *inode = file_inode(filp); 217 struct inode *inode = file_inode(filp);
@@ -353,6 +545,11 @@ group_add_out:
353 return err; 545 return err;
354 } 546 }
355 547
548 case EXT4_IOC_SWAP_BOOT:
549 if (!(filp->f_mode & FMODE_WRITE))
550 return -EBADF;
551 return swap_inode_boot_loader(sb, inode);
552
356 case EXT4_IOC_RESIZE_FS: { 553 case EXT4_IOC_RESIZE_FS: {
357 ext4_fsblk_t n_blocks_count; 554 ext4_fsblk_t n_blocks_count;
358 struct super_block *sb = inode->i_sb; 555 struct super_block *sb = inode->i_sb;