]>
Commit | Line | Data |
---|---|---|
bf69235a TT |
1 | /* |
2 | * online.c --- Do on-line resizing of the ext3 filesystem | |
3 | * | |
efc6f628 TT |
4 | * Copyright (C) 2006 by Theodore Ts'o |
5 | * | |
bf69235a TT |
6 | * %Begin-Header% |
7 | * This file may be redistributed under the terms of the GNU Public | |
8 | * License. | |
9 | * %End-Header% | |
10 | */ | |
11 | ||
d1154eb4 | 12 | #include "config.h" |
bf69235a TT |
13 | #include "resize2fs.h" |
14 | #ifdef HAVE_SYS_IOCTL_H | |
15 | #include <sys/ioctl.h> | |
16 | #endif | |
17 | #include <sys/stat.h> | |
18 | #include <fcntl.h> | |
19 | ||
20 | extern char *program_name; | |
21 | ||
20056960 TT |
22 | #define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1) |
23 | ||
5a760d49 TT |
24 | #ifdef __linux__ |
25 | static int parse_version_number(const char *s) | |
26 | { | |
27 | int major, minor, rev; | |
28 | char *endptr; | |
29 | const char *cp = s; | |
30 | ||
31 | if (!s) | |
32 | return 0; | |
33 | major = strtol(cp, &endptr, 10); | |
34 | if (cp == endptr || *endptr != '.') | |
35 | return 0; | |
36 | cp = endptr + 1; | |
37 | minor = strtol(cp, &endptr, 10); | |
38 | if (cp == endptr || *endptr != '.') | |
39 | return 0; | |
40 | cp = endptr + 1; | |
41 | rev = strtol(cp, &endptr, 10); | |
42 | if (cp == endptr) | |
43 | return 0; | |
44 | return ((((major * 256) + minor) * 256) + rev); | |
45 | } | |
46 | ||
47 | #define VERSION_CODE(a,b,c) (((a) << 16) + ((b) << 8) + (c)) | |
48 | ||
49 | #endif | |
50 | ||
efc6f628 | 51 | errcode_t online_resize_fs(ext2_filsys fs, const char *mtpt, |
8728d506 | 52 | blk64_t *new_size, int flags EXT2FS_ATTR((unused))) |
bf69235a | 53 | { |
3e41608a | 54 | #ifdef __linux__ |
bf69235a | 55 | struct ext2_new_group_input input; |
5c4f8d67 | 56 | struct ext4_new_group_input input64; |
bf69235a | 57 | struct ext2_super_block *sb = fs->super; |
de8f3a76 | 58 | unsigned long new_desc_blocks; |
bf69235a TT |
59 | ext2_filsys new_fs; |
60 | errcode_t retval; | |
9ff8ece5 | 61 | double percent; |
bf69235a | 62 | dgrp_t i; |
20056960 | 63 | blk_t size; |
9ff8ece5 | 64 | int fd, overhead; |
5c4f8d67 | 65 | int use_old_ioctl = 1; |
5a760d49 TT |
66 | int no_meta_bg_resize = 0; |
67 | int no_resize_ioctl = 0; | |
68 | ||
69 | if (getenv("RESIZE2FS_KERNEL_VERSION")) { | |
70 | char *version_to_emulate = getenv("RESIZE2FS_KERNEL_VERSION"); | |
71 | int kvers = parse_version_number(version_to_emulate); | |
72 | ||
73 | if (kvers < VERSION_CODE(3, 7, 0)) | |
74 | no_meta_bg_resize = 1; | |
75 | if (kvers < VERSION_CODE(3, 3, 0)) | |
76 | no_resize_ioctl = 1; | |
77 | } | |
bf69235a | 78 | |
21e37be6 | 79 | if (ext2fs_has_feature_sparse_super2(fs->super) && |
65c6c3e0 TT |
80 | (access("/sys/fs/ext4/features/sparse_super2", R_OK) != 0)) { |
81 | com_err(program_name, 0, _("kernel does not support online " | |
82 | "resize with sparse_super2")); | |
83 | exit(1); | |
84 | } | |
85 | ||
bf69235a TT |
86 | printf(_("Filesystem at %s is mounted on %s; " |
87 | "on-line resizing required\n"), fs->device_name, mtpt); | |
88 | ||
4efbac6f | 89 | if (*new_size < ext2fs_blocks_count(sb)) { |
1df56561 | 90 | com_err(program_name, 0, _("On-line shrinking not supported")); |
bf69235a TT |
91 | exit(1); |
92 | } | |
93 | ||
d255b22e | 94 | /* |
efc6f628 | 95 | * If the number of descriptor blocks is going to increase, |
d255b22e TT |
96 | * the on-line resizing inode must be present. |
97 | */ | |
98 | new_desc_blocks = ext2fs_div_ceil( | |
8728d506 VAH |
99 | ext2fs_div64_ceil(*new_size - |
100 | fs->super->s_first_data_block, | |
101 | EXT2_BLOCKS_PER_GROUP(fs->super)), | |
d255b22e | 102 | EXT2_DESC_PER_BLOCK(fs->super)); |
9f6ba888 | 103 | printf("old_desc_blocks = %lu, new_desc_blocks = %lu\n", |
de8f3a76 | 104 | fs->desc_blocks, new_desc_blocks); |
06ca8da7 TT |
105 | |
106 | /* | |
107 | * Do error checking to make sure the resize will be successful. | |
108 | */ | |
109 | if ((access("/sys/fs/ext4/features/meta_bg_resize", R_OK) != 0) || | |
5a760d49 | 110 | no_meta_bg_resize) { |
21e37be6 | 111 | if (!ext2fs_has_feature_resize_inode(fs->super) && |
06ca8da7 TT |
112 | (new_desc_blocks != fs->desc_blocks)) { |
113 | com_err(program_name, 0, | |
114 | _("Filesystem does not support online resizing")); | |
115 | exit(1); | |
116 | } | |
117 | ||
21e37be6 | 118 | if (ext2fs_has_feature_resize_inode(fs->super) && |
06ca8da7 TT |
119 | new_desc_blocks > (fs->desc_blocks + |
120 | fs->super->s_reserved_gdt_blocks)) { | |
121 | com_err(program_name, 0, | |
122 | _("Not enough reserved gdt blocks for resizing")); | |
123 | exit(1); | |
124 | } | |
125 | ||
126 | if ((ext2fs_blocks_count(sb) > MAX_32_NUM) || | |
127 | (*new_size > MAX_32_NUM)) { | |
128 | com_err(program_name, 0, | |
129 | _("Kernel does not support resizing a file system this large")); | |
130 | exit(1); | |
131 | } | |
d255b22e TT |
132 | } |
133 | ||
bf69235a TT |
134 | fd = open(mtpt, O_RDONLY); |
135 | if (fd < 0) { | |
efc6f628 | 136 | com_err(program_name, errno, |
bf69235a TT |
137 | _("while trying to open mountpoint %s"), mtpt); |
138 | exit(1); | |
139 | } | |
140 | ||
5a760d49 TT |
141 | if (no_resize_ioctl) { |
142 | printf(_("Old resize interface requested.\n")); | |
143 | } else if (ioctl(fd, EXT4_IOC_RESIZE_FS, new_size)) { | |
cacad7d6 LC |
144 | /* |
145 | * If kernel does not support EXT4_IOC_RESIZE_FS, use the | |
146 | * old online resize. Note that the old approach does not | |
147 | * handle >32 bit file systems | |
3f6fbf95 TT |
148 | * |
149 | * Sigh, if we are running a 32-bit binary on a 64-bit | |
150 | * kernel (which happens all the time on the MIPS | |
151 | * architecture in Debian, but can happen on other CPU | |
152 | * architectures as well) we will get EINVAL returned | |
153 | * when an ioctl doesn't exist, at least up to Linux | |
154 | * 3.1. See compat_sys_ioctl() in fs/compat_ioctl.c | |
155 | * in the kernel sources. This is probably a kernel | |
156 | * bug, but work around it here. | |
cacad7d6 | 157 | */ |
3f6fbf95 | 158 | if ((errno != ENOTTY) && (errno != EINVAL)) { |
9f6ba888 YY |
159 | if (errno == EPERM) |
160 | com_err(program_name, 0, | |
161 | _("Permission denied to resize filesystem")); | |
162 | else | |
163 | com_err(program_name, errno, | |
164 | _("While checking for on-line resizing " | |
165 | "support")); | |
166 | exit(1); | |
167 | } | |
168 | } else { | |
169 | close(fd); | |
170 | return 0; | |
171 | } | |
172 | ||
20056960 TT |
173 | size = ext2fs_blocks_count(sb); |
174 | ||
bf69235a TT |
175 | if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { |
176 | if (errno == EPERM) | |
efc6f628 | 177 | com_err(program_name, 0, |
bf69235a TT |
178 | _("Permission denied to resize filesystem")); |
179 | else if (errno == ENOTTY) | |
efc6f628 | 180 | com_err(program_name, 0, |
d255b22e | 181 | _("Kernel does not support online resizing")); |
efc6f628 TT |
182 | else |
183 | com_err(program_name, errno, | |
bf69235a TT |
184 | _("While checking for on-line resizing support")); |
185 | exit(1); | |
186 | } | |
187 | ||
4efbac6f VAH |
188 | percent = (ext2fs_r_blocks_count(sb) * 100.0) / |
189 | ext2fs_blocks_count(sb); | |
bf69235a TT |
190 | |
191 | retval = ext2fs_read_bitmaps(fs); | |
e39082e2 DW |
192 | if (retval) { |
193 | close(fd); | |
bf69235a | 194 | return retval; |
e39082e2 | 195 | } |
bf69235a TT |
196 | |
197 | retval = ext2fs_dup_handle(fs, &new_fs); | |
e39082e2 DW |
198 | if (retval) { |
199 | close(fd); | |
bf69235a | 200 | return retval; |
e39082e2 | 201 | } |
bf69235a | 202 | |
f844b32a TT |
203 | /* The current method of adding one block group at a time to a |
204 | * mounted filesystem means it is impossible to accomodate the | |
205 | * flex_bg allocation method of placing the metadata together | |
206 | * in a single block group. For now we "fix" this issue by | |
207 | * using the traditional layout for new block groups, where | |
208 | * each block group is self-contained and contains its own | |
209 | * bitmap blocks and inode tables. This means we don't get | |
210 | * the layout advantages of flex_bg in the new block groups, | |
211 | * but at least it allows on-line resizing to function. | |
212 | */ | |
21e37be6 | 213 | ext2fs_clear_feature_flex_bg(new_fs->super); |
c09043f1 | 214 | retval = adjust_fs_info(new_fs, fs, 0, *new_size); |
e39082e2 DW |
215 | if (retval) { |
216 | close(fd); | |
bf69235a | 217 | return retval; |
e39082e2 | 218 | } |
bf69235a | 219 | |
8728d506 | 220 | printf(_("Performing an on-line resize of %s to %llu (%dk) blocks.\n"), |
bf69235a TT |
221 | fs->device_name, *new_size, fs->blocksize / 1024); |
222 | ||
efc6f628 | 223 | size = fs->group_desc_count * sb->s_blocks_per_group + |
bf69235a TT |
224 | sb->s_first_data_block; |
225 | if (size > *new_size) | |
226 | size = *new_size; | |
227 | ||
228 | if (ioctl(fd, EXT2_IOC_GROUP_EXTEND, &size)) { | |
efc6f628 | 229 | com_err(program_name, errno, |
bf69235a TT |
230 | _("While trying to extend the last group")); |
231 | exit(1); | |
232 | } | |
233 | ||
234 | for (i = fs->group_desc_count; | |
235 | i < new_fs->group_desc_count; i++) { | |
236 | ||
237 | overhead = (int) (2 + new_fs->inode_blocks_per_group); | |
238 | ||
239 | if (ext2fs_bg_has_super(new_fs, new_fs->group_desc_count - 1)) | |
efc6f628 | 240 | overhead += 1 + new_fs->desc_blocks + |
bf69235a TT |
241 | new_fs->super->s_reserved_gdt_blocks; |
242 | ||
243 | input.group = i; | |
d7cca6b0 VAH |
244 | input.block_bitmap = ext2fs_block_bitmap_loc(new_fs, i); |
245 | input.inode_bitmap = ext2fs_inode_bitmap_loc(new_fs, i); | |
246 | input.inode_table = ext2fs_inode_table_loc(new_fs, i); | |
98f45471 | 247 | input.blocks_count = ext2fs_group_blocks_count(new_fs, i); |
9ff8ece5 TT |
248 | input.reserved_blocks = (blk_t) (percent * input.blocks_count |
249 | / 100.0); | |
bf69235a TT |
250 | |
251 | #if 0 | |
252 | printf("new block bitmap is at 0x%04x\n", input.block_bitmap); | |
253 | printf("new inode bitmap is at 0x%04x\n", input.inode_bitmap); | |
efc6f628 | 254 | printf("new inode table is at 0x%04x-0x%04x\n", |
bf69235a TT |
255 | input.inode_table, |
256 | input.inode_table + new_fs->inode_blocks_per_group-1); | |
d0ff90d5 | 257 | printf("new group has %u blocks\n", input.blocks_count); |
efc6f628 | 258 | printf("new group will reserve %d blocks\n", |
bf69235a | 259 | input.reserved_blocks); |
efc6f628 | 260 | printf("new group has %d free blocks\n", |
d7cca6b0 | 261 | ext2fs_bg_free_blocks_count(new_fs, i), |
bf69235a | 262 | printf("new group has %d free inodes (%d blocks)\n", |
d7cca6b0 | 263 | ext2fs_bg_free_inodes_count(new_fs, i), |
bf69235a TT |
264 | new_fs->inode_blocks_per_group); |
265 | printf("Adding group #%d\n", input.group); | |
266 | #endif | |
267 | ||
5c4f8d67 TT |
268 | if (use_old_ioctl && |
269 | ioctl(fd, EXT2_IOC_GROUP_ADD, &input) == 0) | |
270 | continue; | |
271 | else | |
f844b32a | 272 | use_old_ioctl = 0; |
5c4f8d67 TT |
273 | |
274 | input64.group = input.group; | |
275 | input64.block_bitmap = input.block_bitmap; | |
276 | input64.inode_bitmap = input.inode_bitmap; | |
277 | input64.inode_table = input.inode_table; | |
278 | input64.blocks_count = input.blocks_count; | |
279 | input64.reserved_blocks = input.reserved_blocks; | |
280 | input64.unused = input.unused; | |
281 | ||
282 | if (ioctl(fd, EXT4_IOC_GROUP_ADD, &input64) < 0) { | |
efc6f628 TT |
283 | com_err(program_name, errno, |
284 | _("While trying to add group #%d"), | |
bf69235a TT |
285 | input.group); |
286 | exit(1); | |
287 | } | |
288 | } | |
289 | ||
290 | ext2fs_free(new_fs); | |
291 | close(fd); | |
292 | ||
293 | return 0; | |
3e41608a | 294 | #else |
df9c01b1 | 295 | printf(_("Filesystem at %s is mounted on %s, and on-line resizing is " |
3e41608a ST |
296 | "not supported on this system.\n"), fs->device_name, mtpt); |
297 | exit(1); | |
298 | #endif | |
bf69235a | 299 | } |