]>
Commit | Line | Data |
---|---|---|
24b2c7a7 TT |
1 | /* |
2 | * main.c --- ext2 resizer main program | |
3 | * | |
0cee8a5c TT |
4 | * Copyright (C) 1997, 1998 by Theodore Ts'o and |
5 | * PowerQuest, Inc. | |
6 | * | |
55f4cbd9 | 7 | * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 by Theodore Ts'o |
efc6f628 | 8 | * |
24b2c7a7 | 9 | * %Begin-Header% |
0cee8a5c TT |
10 | * This file may be redistributed under the terms of the GNU Public |
11 | * License. | |
24b2c7a7 TT |
12 | * %End-Header% |
13 | */ | |
14 | ||
fe07357f TT |
15 | #define _LARGEFILE_SOURCE |
16 | #define _LARGEFILE64_SOURCE | |
17 | ||
d1154eb4 | 18 | #include "config.h" |
c762c8e6 TT |
19 | #ifdef HAVE_GETOPT_H |
20 | #include <getopt.h> | |
373b8337 TT |
21 | #else |
22 | extern char *optarg; | |
23 | extern int optind; | |
c762c8e6 | 24 | #endif |
fe07357f | 25 | #include <unistd.h> |
f38cf3cb TT |
26 | #ifdef HAVE_STDLIB_H |
27 | #include <stdlib.h> | |
28 | #endif | |
fe07357f | 29 | #include <sys/types.h> |
116db1b5 | 30 | #include <sys/stat.h> |
fe07357f | 31 | #include <fcntl.h> |
c762c8e6 | 32 | |
55f4cbd9 TT |
33 | #include "e2p/e2p.h" |
34 | ||
24b2c7a7 TT |
35 | #include "resize2fs.h" |
36 | ||
0cee8a5c | 37 | #include "../version.h" |
2e561e02 | 38 | |
2e8ca9a2 | 39 | char *program_name, *device_name, *io_options; |
24b2c7a7 | 40 | |
dfcdc32f | 41 | static void usage (char *prog) |
24b2c7a7 | 42 | { |
199ddaaa JB |
43 | fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-P] " |
44 | "[-p] device [new_size]\n\n"), prog); | |
2e561e02 | 45 | |
24b2c7a7 TT |
46 | exit (1); |
47 | } | |
48 | ||
3b627e8d TT |
49 | static errcode_t resize_progress_func(ext2_resize_t rfs, int pass, |
50 | unsigned long cur, unsigned long max) | |
63b44fbe TT |
51 | { |
52 | ext2_sim_progmeter progress; | |
53 | const char *label; | |
54 | errcode_t retval; | |
55 | ||
56 | progress = (ext2_sim_progmeter) rfs->prog_data; | |
f4b2a6db | 57 | if (max == 0) |
3b627e8d | 58 | return 0; |
63b44fbe TT |
59 | if (cur == 0) { |
60 | if (progress) | |
61 | ext2fs_progress_close(progress); | |
62 | progress = 0; | |
63 | switch (pass) { | |
a8519a2d | 64 | case E2_RSZ_EXTEND_ITABLE_PASS: |
a13575f4 | 65 | label = _("Extending the inode table"); |
63b44fbe TT |
66 | break; |
67 | case E2_RSZ_BLOCK_RELOC_PASS: | |
a13575f4 | 68 | label = _("Relocating blocks"); |
63b44fbe | 69 | break; |
a8519a2d | 70 | case E2_RSZ_INODE_SCAN_PASS: |
a13575f4 | 71 | label = _("Scanning inode table"); |
63b44fbe TT |
72 | break; |
73 | case E2_RSZ_INODE_REF_UPD_PASS: | |
a13575f4 | 74 | label = _("Updating inode references"); |
63b44fbe TT |
75 | break; |
76 | case E2_RSZ_MOVE_ITABLE_PASS: | |
a13575f4 | 77 | label = _("Moving inode table"); |
63b44fbe | 78 | break; |
a8519a2d | 79 | default: |
a13575f4 | 80 | label = _("Unknown pass?!?"); |
a8519a2d | 81 | break; |
63b44fbe | 82 | } |
a13575f4 | 83 | printf(_("Begin pass %d (max = %lu)\n"), pass, max); |
63b44fbe TT |
84 | retval = ext2fs_progress_init(&progress, label, 30, |
85 | 40, max, 0); | |
86 | if (retval) | |
87 | progress = 0; | |
88 | rfs->prog_data = (void *) progress; | |
89 | } | |
90 | if (progress) | |
91 | ext2fs_progress_update(progress, cur); | |
92 | if (cur >= max) { | |
93 | if (progress) | |
94 | ext2fs_progress_close(progress); | |
95 | progress = 0; | |
96 | rfs->prog_data = 0; | |
97 | } | |
3b627e8d | 98 | return 0; |
63b44fbe TT |
99 | } |
100 | ||
46c5490d TT |
101 | static void determine_fs_stride(ext2_filsys fs) |
102 | { | |
103 | unsigned int group; | |
104 | unsigned long long sum; | |
105 | unsigned int has_sb, prev_has_sb, num; | |
106 | int i_stride, b_stride; | |
107 | ||
96c6a3ac TT |
108 | if (fs->stride) |
109 | return; | |
46c5490d TT |
110 | num = 0; sum = 0; |
111 | for (group = 0; group < fs->group_desc_count; group++) { | |
112 | has_sb = ext2fs_bg_has_super(fs, group); | |
113 | if (group == 0 || has_sb != prev_has_sb) | |
114 | goto next; | |
d7cca6b0 VAH |
115 | b_stride = ext2fs_block_bitmap_loc(fs, group) - |
116 | ext2fs_block_bitmap_loc(fs, group - 1) - | |
46c5490d | 117 | fs->super->s_blocks_per_group; |
d7cca6b0 VAH |
118 | i_stride = ext2fs_inode_bitmap_loc(fs, group) - |
119 | ext2fs_inode_bitmap_loc(fs, group - 1) - | |
46c5490d TT |
120 | fs->super->s_blocks_per_group; |
121 | if (b_stride != i_stride || | |
122 | b_stride < 0) | |
123 | goto next; | |
124 | ||
125 | /* printf("group %d has stride %d\n", group, b_stride); */ | |
126 | sum += b_stride; | |
127 | num++; | |
efc6f628 | 128 | |
46c5490d TT |
129 | next: |
130 | prev_has_sb = has_sb; | |
131 | } | |
132 | ||
133 | if (fs->group_desc_count > 12 && num < 3) | |
134 | sum = 0; | |
135 | ||
136 | if (num) | |
137 | fs->stride = sum / num; | |
138 | else | |
139 | fs->stride = 0; | |
140 | ||
96c6a3ac TT |
141 | fs->super->s_raid_stride = fs->stride; |
142 | ext2fs_mark_super_dirty(fs); | |
143 | ||
46c5490d TT |
144 | #if 0 |
145 | if (fs->stride) | |
146 | printf("Using RAID stride of %d\n", fs->stride); | |
147 | #endif | |
148 | } | |
149 | ||
0a617317 | 150 | int main (int argc, char ** argv) |
24b2c7a7 TT |
151 | { |
152 | errcode_t retval; | |
153 | ext2_filsys fs; | |
154 | int c; | |
05e112a1 | 155 | int flags = 0; |
c762c8e6 | 156 | int flush = 0; |
f4b2a6db | 157 | int force = 0; |
5d2ef12f | 158 | int io_flags = 0; |
199ddaaa JB |
159 | int force_min_size = 0; |
160 | int print_min_size = 0; | |
fe07357f | 161 | int fd, ret; |
8728d506 VAH |
162 | blk64_t new_size = 0; |
163 | blk64_t max_size = 0; | |
164 | blk64_t min_size = 0; | |
24b2c7a7 | 165 | io_manager io_ptr; |
55f4cbd9 | 166 | char *new_size_str = 0; |
46c5490d | 167 | int use_stride = -1; |
fe07357f TT |
168 | #ifdef HAVE_FSTAT64 |
169 | struct stat64 st_buf; | |
170 | #else | |
116db1b5 | 171 | struct stat st_buf; |
fe07357f TT |
172 | #endif |
173 | __s64 new_file_size; | |
54434927 | 174 | unsigned int sys_page_size = 4096; |
a7ccdff8 | 175 | long sysval; |
bf69235a TT |
176 | int len, mount_flags; |
177 | char *mtpt; | |
ddc32a04 TT |
178 | |
179 | #ifdef ENABLE_NLS | |
180 | setlocale(LC_MESSAGES, ""); | |
181 | setlocale(LC_CTYPE, ""); | |
182 | bindtextdomain(NLS_CAT_NAME, LOCALEDIR); | |
183 | textdomain(NLS_CAT_NAME); | |
184 | #endif | |
185 | ||
a6d8302b | 186 | add_error_table(&et_ext2_error_table); |
24b2c7a7 | 187 | |
f35fd3d5 | 188 | fprintf (stderr, "resize2fs %s (%s)\n", |
ba0af756 | 189 | E2FSPROGS_VERSION, E2FSPROGS_DATE); |
24b2c7a7 TT |
190 | if (argc && *argv) |
191 | program_name = *argv; | |
2e561e02 | 192 | |
199ddaaa | 193 | while ((c = getopt (argc, argv, "d:fFhMPpS:")) != EOF) { |
24b2c7a7 TT |
194 | switch (c) { |
195 | case 'h': | |
196 | usage(program_name); | |
197 | break; | |
f4b2a6db TT |
198 | case 'f': |
199 | force = 1; | |
200 | break; | |
c762c8e6 TT |
201 | case 'F': |
202 | flush = 1; | |
203 | break; | |
199ddaaa JB |
204 | case 'M': |
205 | force_min_size = 1; | |
206 | break; | |
207 | case 'P': | |
208 | print_min_size = 1; | |
209 | break; | |
05e112a1 TT |
210 | case 'd': |
211 | flags |= atoi(optarg); | |
212 | break; | |
213 | case 'p': | |
214 | flags |= RESIZE_PERCENT_COMPLETE; | |
215 | break; | |
46c5490d TT |
216 | case 'S': |
217 | use_stride = atoi(optarg); | |
218 | break; | |
24b2c7a7 | 219 | default: |
f4b2a6db | 220 | usage(program_name); |
24b2c7a7 TT |
221 | } |
222 | } | |
f4b2a6db TT |
223 | if (optind == argc) |
224 | usage(program_name); | |
2e561e02 | 225 | |
24b2c7a7 | 226 | device_name = argv[optind++]; |
55f4cbd9 TT |
227 | if (optind < argc) |
228 | new_size_str = argv[optind++]; | |
f4b2a6db TT |
229 | if (optind < argc) |
230 | usage(program_name); | |
efc6f628 | 231 | |
2e8ca9a2 TT |
232 | io_options = strchr(device_name, '?'); |
233 | if (io_options) | |
234 | *io_options++ = 0; | |
235 | ||
bf69235a TT |
236 | /* |
237 | * Figure out whether or not the device is mounted, and if it is | |
238 | * where it is mounted. | |
239 | */ | |
240 | len=80; | |
241 | while (1) { | |
242 | mtpt = malloc(len); | |
243 | if (!mtpt) | |
244 | return ENOMEM; | |
245 | mtpt[len-1] = 0; | |
efc6f628 | 246 | retval = ext2fs_check_mount_point(device_name, &mount_flags, |
bf69235a TT |
247 | mtpt, len); |
248 | if (retval) { | |
249 | com_err("ext2fs_check_mount_point", retval, | |
250 | _("while determining whether %s is mounted."), | |
251 | device_name); | |
252 | exit(1); | |
253 | } | |
3a4d9869 | 254 | if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0)) |
bf69235a TT |
255 | break; |
256 | free(mtpt); | |
257 | len = 2 * len; | |
258 | } | |
fe07357f | 259 | |
c859cb1d | 260 | fd = ext2fs_open_file(device_name, O_RDWR); |
fe07357f TT |
261 | if (fd < 0) { |
262 | com_err("open", errno, _("while opening %s"), | |
263 | device_name); | |
264 | exit(1); | |
265 | } | |
266 | ||
267 | #ifdef HAVE_FSTAT64 | |
268 | ret = fstat64(fd, &st_buf); | |
269 | #else | |
270 | ret = fstat(fd, &st_buf); | |
271 | #endif | |
272 | if (ret < 0) { | |
efc6f628 | 273 | com_err("open", errno, |
fe07357f TT |
274 | _("while getting stat information for %s"), |
275 | device_name); | |
276 | exit(1); | |
277 | } | |
efc6f628 | 278 | |
c762c8e6 | 279 | if (flush) { |
48e08e03 TT |
280 | retval = ext2fs_sync_device(fd, 1); |
281 | if (retval) { | |
efc6f628 | 282 | com_err(argv[0], retval, |
a13575f4 | 283 | _("while trying to flush %s"), |
c762c8e6 TT |
284 | device_name); |
285 | exit(1); | |
286 | } | |
fe07357f TT |
287 | } |
288 | ||
289 | if (!S_ISREG(st_buf.st_mode )) { | |
c762c8e6 | 290 | close(fd); |
fe07357f | 291 | fd = -1; |
c762c8e6 TT |
292 | } |
293 | ||
f38cf3cb TT |
294 | #ifdef CONFIG_TESTIO_DEBUG |
295 | if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { | |
05e112a1 TT |
296 | io_ptr = test_io_manager; |
297 | test_io_backing_manager = unix_io_manager; | |
efc6f628 | 298 | } else |
f38cf3cb | 299 | #endif |
05e112a1 TT |
300 | io_ptr = unix_io_manager; |
301 | ||
5d2ef12f TT |
302 | if (!(mount_flags & EXT2_MF_MOUNTED)) |
303 | io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; | |
efc6f628 | 304 | retval = ext2fs_open2(device_name, io_options, io_flags, |
2e8ca9a2 | 305 | 0, 0, io_ptr, &fs); |
24b2c7a7 | 306 | if (retval) { |
a13575f4 | 307 | com_err (program_name, retval, _("while trying to open %s"), |
24b2c7a7 | 308 | device_name); |
a13575f4 | 309 | printf (_("Couldn't find valid filesystem superblock.\n")); |
24b2c7a7 TT |
310 | exit (1); |
311 | } | |
236efede | 312 | |
101c84f2 TT |
313 | /* |
314 | * Check for compatibility with the feature sets. We need to | |
315 | * be more stringent than ext2fs_open(). | |
316 | */ | |
76dd5e5c | 317 | if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) { |
101c84f2 TT |
318 | com_err(program_name, EXT2_ET_UNSUPP_FEATURE, |
319 | "(%s)", device_name); | |
320 | exit(1); | |
321 | } | |
a8d632a4 TT |
322 | |
323 | /* | |
324 | * XXXX The combination of flex_bg and !resize_inode causes | |
325 | * major problems for resize2fs, since when the group descriptors | |
326 | * grow in size this can potentially require multiple inode | |
327 | * tables to be moved aside to make room, and resize2fs chokes | |
328 | * rather badly in this scenario. It's a rare combination, | |
329 | * except when a filesystem is expanded more than a certain | |
330 | * size, so for now, we'll just prohibit that combination. | |
331 | * This is something we should fix eventually, though. | |
332 | */ | |
333 | if ((fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) && | |
334 | !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)) { | |
335 | com_err(program_name, 0, _("%s: The combination of flex_bg " | |
336 | "and\n\t!resize_inode features " | |
337 | "is not supported by resize2fs.\n"), | |
338 | device_name); | |
339 | exit(1); | |
340 | } | |
efc6f628 | 341 | |
0d04d88a ES |
342 | min_size = calculate_minimum_resize_size(fs); |
343 | ||
199ddaaa | 344 | if (print_min_size) { |
8587d393 | 345 | if (!force && ((fs->super->s_state & EXT2_ERROR_FS) || |
5fa92bc7 ES |
346 | ((fs->super->s_state & EXT2_VALID_FS) == 0))) { |
347 | fprintf(stderr, | |
348 | _("Please run 'e2fsck -f %s' first.\n\n"), | |
349 | device_name); | |
350 | exit(1); | |
351 | } | |
8728d506 | 352 | printf(_("Estimated minimum size of the filesystem: %llu\n"), |
0d04d88a | 353 | min_size); |
199ddaaa JB |
354 | exit(0); |
355 | } | |
356 | ||
a7ccdff8 TT |
357 | /* Determine the system page size if possible */ |
358 | #ifdef HAVE_SYSCONF | |
359 | #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) | |
360 | #define _SC_PAGESIZE _SC_PAGE_SIZE | |
361 | #endif | |
362 | #ifdef _SC_PAGESIZE | |
363 | sysval = sysconf(_SC_PAGESIZE); | |
364 | if (sysval > 0) | |
365 | sys_page_size = sysval; | |
366 | #endif /* _SC_PAGESIZE */ | |
367 | #endif /* HAVE_SYSCONF */ | |
368 | ||
f4b2a6db TT |
369 | /* |
370 | * Get the size of the containing partition, and use this for | |
fe07357f | 371 | * defaults and for making sure the new filesystem doesn't |
f4b2a6db TT |
372 | * exceed the partition size. |
373 | */ | |
8728d506 VAH |
374 | retval = ext2fs_get_device_size2(device_name, fs->blocksize, |
375 | &max_size); | |
f4b2a6db TT |
376 | if (retval) { |
377 | com_err(program_name, retval, | |
a13575f4 | 378 | _("while trying to determine filesystem size")); |
f4b2a6db TT |
379 | exit(1); |
380 | } | |
199ddaaa | 381 | if (force_min_size) |
1d8af189 | 382 | new_size = min_size; |
199ddaaa | 383 | else if (new_size_str) { |
8728d506 VAH |
384 | new_size = parse_num_blocks2(new_size_str, |
385 | fs->super->s_log_block_size); | |
279a000f ES |
386 | if (new_size == 0) { |
387 | com_err(program_name, 0, | |
388 | _("Invalid new size: %s\n"), new_size_str); | |
389 | exit(1); | |
390 | } | |
55f4cbd9 | 391 | } else { |
248eb837 ES |
392 | /* Take down devices exactly 16T to 2^32-1 blocks */ |
393 | if (max_size == (1ULL << 32)) | |
394 | max_size--; | |
395 | else if (max_size > (1ULL << 32)) { | |
396 | com_err(program_name, 0, _("New size too large to be " | |
397 | "expressed in 32 bits\n")); | |
398 | exit(1); | |
399 | } | |
f4b2a6db | 400 | new_size = max_size; |
a7ccdff8 TT |
401 | /* Round down to an even multiple of a pagesize */ |
402 | if (sys_page_size > fs->blocksize) | |
403 | new_size &= ~((sys_page_size / fs->blocksize)-1); | |
404 | } | |
46c5490d | 405 | |
0d04d88a ES |
406 | if (!force && new_size < min_size) { |
407 | com_err(program_name, 0, | |
8728d506 | 408 | _("New size smaller than minimum (%llu)\n"), min_size); |
0d04d88a ES |
409 | exit(1); |
410 | } | |
46c5490d | 411 | if (use_stride >= 0) { |
de8f3a76 | 412 | if (use_stride >= (int) fs->super->s_blocks_per_group) { |
efc6f628 | 413 | com_err(program_name, 0, |
46c5490d TT |
414 | _("Invalid stride length")); |
415 | exit(1); | |
416 | } | |
96c6a3ac TT |
417 | fs->stride = fs->super->s_raid_stride = use_stride; |
418 | ext2fs_mark_super_dirty(fs); | |
46c5490d TT |
419 | } else |
420 | determine_fs_stride(fs); | |
efc6f628 | 421 | |
116db1b5 TT |
422 | /* |
423 | * If we are resizing a plain file, and it's not big enough, | |
424 | * automatically extend it in a sparse fashion by writing the | |
425 | * last requested block. | |
426 | */ | |
fe07357f | 427 | new_file_size = ((__u64) new_size) * fs->blocksize; |
efc6f628 | 428 | if ((__u64) new_file_size > |
fe07357f TT |
429 | (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1) |
430 | fd = -1; | |
431 | if ((new_file_size > st_buf.st_size) && | |
432 | (fd > 0)) { | |
433 | if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) && | |
434 | (write(fd, "0", 1) == 1)) | |
116db1b5 | 435 | max_size = new_size; |
116db1b5 | 436 | } |
f4b2a6db | 437 | if (!force && (new_size > max_size)) { |
a13575f4 | 438 | fprintf(stderr, _("The containing partition (or device)" |
8728d506 VAH |
439 | " is only %llu (%dk) blocks.\nYou requested a new size" |
440 | " of %llu blocks.\n\n"), max_size, | |
792a0881 | 441 | fs->blocksize / 1024, new_size); |
f4b2a6db TT |
442 | exit(1); |
443 | } | |
4efbac6f | 444 | if (new_size == ext2fs_blocks_count(fs->super)) { |
8728d506 | 445 | fprintf(stderr, _("The filesystem is already %llu blocks " |
a13575f4 | 446 | "long. Nothing to do!\n\n"), new_size); |
f4b2a6db TT |
447 | exit(0); |
448 | } | |
bf69235a TT |
449 | if (mount_flags & EXT2_MF_MOUNTED) { |
450 | retval = online_resize_fs(fs, mtpt, &new_size, flags); | |
451 | } else { | |
452 | if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) || | |
453 | (fs->super->s_state & EXT2_ERROR_FS) || | |
454 | ((fs->super->s_state & EXT2_VALID_FS) == 0))) { | |
efc6f628 | 455 | fprintf(stderr, |
bf69235a TT |
456 | _("Please run 'e2fsck -f %s' first.\n\n"), |
457 | device_name); | |
458 | exit(1); | |
459 | } | |
96cdb37e | 460 | printf(_("Resizing the filesystem on " |
8728d506 | 461 | "%s to %llu (%dk) blocks.\n"), |
bf69235a TT |
462 | device_name, new_size, fs->blocksize / 1024); |
463 | retval = resize_fs(fs, &new_size, flags, | |
464 | ((flags & RESIZE_PERCENT_COMPLETE) ? | |
465 | resize_progress_func : 0)); | |
f4b2a6db | 466 | } |
3a4d9869 | 467 | free(mtpt); |
1e1da29f | 468 | if (retval) { |
a13575f4 | 469 | com_err(program_name, retval, _("while trying to resize %s"), |
1e1da29f | 470 | device_name); |
96cdb37e TT |
471 | fprintf(stderr, |
472 | _("Please run 'e2fsck -fy %s' to fix the filesystem\n" | |
473 | "after the aborted resize operation.\n"), | |
474 | device_name); | |
475 | ext2fs_close(fs); | |
16082112 | 476 | exit(1); |
1e1da29f | 477 | } |
8728d506 | 478 | printf(_("The filesystem on %s is now %llu blocks long.\n\n"), |
7e71e4c5 | 479 | device_name, new_size); |
fe07357f TT |
480 | |
481 | if ((st_buf.st_size > new_file_size) && | |
482 | (fd > 0)) { | |
261cd396 | 483 | #ifdef HAVE_FTRUNCATE64 |
fb72556a | 484 | retval = ftruncate64(fd, new_file_size); |
fe07357f | 485 | #else |
fb72556a | 486 | retval = 0; |
98dca6c5 TT |
487 | /* Only truncate if new_file_size doesn't overflow off_t */ |
488 | if (((off_t) new_file_size) == new_file_size) | |
fb72556a | 489 | retval = ftruncate(fd, (off_t) new_file_size); |
fe07357f | 490 | #endif |
fb72556a TT |
491 | if (retval) |
492 | com_err(program_name, retval, | |
493 | _("while trying to truncate %s"), | |
494 | device_name); | |
fe07357f TT |
495 | } |
496 | if (fd > 0) | |
497 | close(fd); | |
a6d8302b | 498 | remove_error_table(&et_ext2_error_table); |
0a617317 | 499 | return (0); |
24b2c7a7 | 500 | } |