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