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 #ifndef _LARGEFILE_SOURCE 16 #define _LARGEFILE_SOURCE 17 #endif 18 #ifndef _LARGEFILE64_SOURCE 19 #define _LARGEFILE64_SOURCE 20 #endif 21 22 #include "config.h" 23 #ifdef HAVE_GETOPT_H 24 #include <getopt.h> 25 #else 26 extern char *optarg; 27 extern int optind; 28 #endif 29 #include <unistd.h> 30 #ifdef HAVE_STDLIB_H 31 #include <stdlib.h> 32 #endif 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <fcntl.h> 36 #include <libgen.h> 37 38 #include "e2p/e2p.h" 39 40 #include "resize2fs.h" 41 42 #include "../version.h" 43 44 char *program_name; 45 static char *device_name, *io_options; 46 47 static void usage (char *prog) 48 { 49 fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-P] " 50 "[-p] device [-b|-s|new_size] [-S RAID-stride] " 51 "[-z undo_file]\n\n"), 52 prog); 53 54 exit (1); 55 } 56 57 static errcode_t resize_progress_func(ext2_resize_t rfs, int pass, 58 unsigned long cur, unsigned long max) 59 { 60 ext2_sim_progmeter progress; 61 const char *label; 62 errcode_t retval; 63 64 progress = (ext2_sim_progmeter) rfs->prog_data; 65 if (max == 0) 66 return 0; 67 if (cur == 0) { 68 if (progress) 69 ext2fs_progress_close(progress); 70 progress = 0; 71 switch (pass) { 72 case E2_RSZ_EXTEND_ITABLE_PASS: 73 label = _("Extending the inode table"); 74 break; 75 case E2_RSZ_BLOCK_RELOC_PASS: 76 label = _("Relocating blocks"); 77 break; 78 case E2_RSZ_INODE_SCAN_PASS: 79 label = _("Scanning inode table"); 80 break; 81 case E2_RSZ_INODE_REF_UPD_PASS: 82 label = _("Updating inode references"); 83 break; 84 case E2_RSZ_MOVE_ITABLE_PASS: 85 label = _("Moving inode table"); 86 break; 87 default: 88 label = _("Unknown pass?!?"); 89 break; 90 } 91 printf(_("Begin pass %d (max = %lu)\n"), pass, max); 92 retval = ext2fs_progress_init(&progress, label, 30, 93 40, max, 0); 94 if (retval) 95 progress = 0; 96 rfs->prog_data = (void *) progress; 97 } 98 if (progress) 99 ext2fs_progress_update(progress, cur); 100 if (cur >= max) { 101 if (progress) 102 ext2fs_progress_close(progress); 103 progress = 0; 104 rfs->prog_data = 0; 105 } 106 return 0; 107 } 108 109 static void determine_fs_stride(ext2_filsys fs) 110 { 111 unsigned int group; 112 unsigned long long sum; 113 unsigned int has_sb, prev_has_sb = 0, num; 114 int i_stride, b_stride; 115 int flexbg_size = 1 << fs->super->s_log_groups_per_flex; 116 117 if (fs->stride) 118 return; 119 num = 0; sum = 0; 120 for (group = 0; group < fs->group_desc_count; group++) { 121 has_sb = ext2fs_bg_has_super(fs, group); 122 if (group == 0 || has_sb != prev_has_sb) 123 goto next; 124 b_stride = ext2fs_block_bitmap_loc(fs, group) - 125 ext2fs_block_bitmap_loc(fs, group - 1) - 126 fs->super->s_blocks_per_group; 127 i_stride = ext2fs_inode_bitmap_loc(fs, group) - 128 ext2fs_inode_bitmap_loc(fs, group - 1) - 129 fs->super->s_blocks_per_group; 130 if (b_stride != i_stride || 131 b_stride < 0 || 132 (flexbg_size > 1 && (group % flexbg_size == 0))) 133 goto next; 134 135 /* printf("group %d has stride %d\n", group, b_stride); */ 136 sum += b_stride; 137 num++; 138 139 next: 140 prev_has_sb = has_sb; 141 } 142 143 if (fs->group_desc_count > 12 && num < 3) 144 sum = 0; 145 146 if (num) 147 fs->stride = sum / num; 148 else 149 fs->stride = 0; 150 151 fs->super->s_raid_stride = fs->stride; 152 ext2fs_mark_super_dirty(fs); 153 154 #if 0 155 if (fs->stride) 156 printf("Using RAID stride of %d\n", fs->stride); 157 #endif 158 } 159 160 static void bigalloc_check(ext2_filsys fs, int force) 161 { 162 if (!force && ext2fs_has_feature_bigalloc(fs->super)) { 163 fprintf(stderr, "%s", _("\nResizing bigalloc file systems has " 164 "not been fully tested. Proceed at\n" 165 "your own risk! Use the force option " 166 "if you want to go ahead anyway.\n\n")); 167 exit(1); 168 } 169 } 170 171 static int resize2fs_setup_tdb(const char *device, char *undo_file, 172 io_manager *io_ptr) 173 { 174 errcode_t retval = ENOMEM; 175 const char *tdb_dir = NULL; 176 char *tdb_file = NULL; 177 char *dev_name, *tmp_name; 178 179 /* (re)open a specific undo file */ 180 if (undo_file && undo_file[0] != 0) { 181 retval = set_undo_io_backing_manager(*io_ptr); 182 if (retval) 183 goto err; 184 *io_ptr = undo_io_manager; 185 retval = set_undo_io_backup_file(undo_file); 186 if (retval) 187 goto err; 188 printf(_("Overwriting existing filesystem; this can be undone " 189 "using the command:\n" 190 " e2undo %s %s\n\n"), 191 undo_file, device); 192 return retval; 193 } 194 195 /* 196 * Configuration via a conf file would be 197 * nice 198 */ 199 tdb_dir = getenv("E2FSPROGS_UNDO_DIR"); 200 if (!tdb_dir) 201 tdb_dir = "/var/lib/e2fsprogs"; 202 203 if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) || 204 access(tdb_dir, W_OK)) 205 return 0; 206 207 tmp_name = strdup(device); 208 if (!tmp_name) 209 goto errout; 210 dev_name = basename(tmp_name); 211 tdb_file = malloc(strlen(tdb_dir) + 11 + strlen(dev_name) + 7 + 1); 212 if (!tdb_file) { 213 free(tmp_name); 214 goto errout; 215 } 216 sprintf(tdb_file, "%s/resize2fs-%s.e2undo", tdb_dir, dev_name); 217 free(tmp_name); 218 219 if ((unlink(tdb_file) < 0) && (errno != ENOENT)) { 220 retval = errno; 221 com_err(program_name, retval, 222 _("while trying to delete %s"), tdb_file); 223 goto errout; 224 } 225 226 retval = set_undo_io_backing_manager(*io_ptr); 227 if (retval) 228 goto errout; 229 *io_ptr = undo_io_manager; 230 retval = set_undo_io_backup_file(tdb_file); 231 if (retval) 232 goto errout; 233 printf(_("Overwriting existing filesystem; this can be undone " 234 "using the command:\n" 235 " e2undo %s %s\n\n"), tdb_file, device); 236 237 free(tdb_file); 238 return 0; 239 errout: 240 free(tdb_file); 241 err: 242 com_err(program_name, retval, "%s", 243 _("while trying to setup undo file\n")); 244 return retval; 245 } 246 247 int main (int argc, char ** argv) 248 { 249 errcode_t retval; 250 ext2_filsys fs; 251 int c; 252 int flags = 0; 253 int flush = 0; 254 int force = 0; 255 int io_flags = 0; 256 int force_min_size = 0; 257 int print_min_size = 0; 258 int fd, ret; 259 blk64_t new_size = 0; 260 blk64_t max_size = 0; 261 blk64_t min_size = 0; 262 io_manager io_ptr; 263 char *new_size_str = 0; 264 int use_stride = -1; 265 ext2fs_struct_stat st_buf; 266 __s64 new_file_size; 267 unsigned int sys_page_size = 4096; 268 unsigned int blocksize; 269 long sysval; 270 int len, mount_flags; 271 char *mtpt, *undo_file = NULL; 272 273 #ifdef ENABLE_NLS 274 setlocale(LC_MESSAGES, ""); 275 setlocale(LC_CTYPE, ""); 276 bindtextdomain(NLS_CAT_NAME, LOCALEDIR); 277 textdomain(NLS_CAT_NAME); 278 set_com_err_gettext(gettext); 279 #endif 280 281 add_error_table(&et_ext2_error_table); 282 283 fprintf (stderr, "resize2fs %s (%s)\n", 284 E2FSPROGS_VERSION, E2FSPROGS_DATE); 285 if (argc && *argv) 286 program_name = *argv; 287 288 while ((c = getopt(argc, argv, "d:fFhMPpS:bsz:")) != EOF) { 289 switch (c) { 290 case 'h': 291 usage(program_name); 292 break; 293 case 'f': 294 force = 1; 295 break; 296 case 'F': 297 flush = 1; 298 break; 299 case 'M': 300 force_min_size = 1; 301 break; 302 case 'P': 303 print_min_size = 1; 304 break; 305 case 'd': 306 flags |= atoi(optarg); 307 break; 308 case 'p': 309 flags |= RESIZE_PERCENT_COMPLETE; 310 break; 311 case 'S': 312 use_stride = atoi(optarg); 313 break; 314 case 'b': 315 flags |= RESIZE_ENABLE_64BIT; 316 break; 317 case 's': 318 flags |= RESIZE_DISABLE_64BIT; 319 break; 320 case 'z': 321 undo_file = optarg; 322 break; 323 default: 324 usage(program_name); 325 } 326 } 327 if (optind == argc) 328 usage(program_name); 329 330 device_name = argv[optind++]; 331 if (optind < argc) 332 new_size_str = argv[optind++]; 333 if (optind < argc) 334 usage(program_name); 335 336 io_options = strchr(device_name, '?'); 337 if (io_options) 338 *io_options++ = 0; 339 340 /* 341 * Figure out whether or not the device is mounted, and if it is 342 * where it is mounted. 343 */ 344 len=80; 345 while (1) { 346 mtpt = malloc(len); 347 if (!mtpt) 348 return ENOMEM; 349 mtpt[len-1] = 0; 350 retval = ext2fs_check_mount_point(device_name, &mount_flags, 351 mtpt, len); 352 if (retval) { 353 com_err("ext2fs_check_mount_point", retval, 354 _("while determining whether %s is mounted."), 355 device_name); 356 exit(1); 357 } 358 if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0)) 359 break; 360 free(mtpt); 361 len = 2 * len; 362 } 363 364 fd = ext2fs_open_file(device_name, O_RDWR, 0); 365 if (fd < 0) { 366 com_err("open", errno, _("while opening %s"), 367 device_name); 368 exit(1); 369 } 370 371 ret = ext2fs_fstat(fd, &st_buf); 372 if (ret < 0) { 373 com_err("open", errno, 374 _("while getting stat information for %s"), 375 device_name); 376 exit(1); 377 } 378 379 if (flush) { 380 retval = ext2fs_sync_device(fd, 1); 381 if (retval) { 382 com_err(argv[0], retval, 383 _("while trying to flush %s"), 384 device_name); 385 exit(1); 386 } 387 } 388 389 if (!S_ISREG(st_buf.st_mode )) { 390 close(fd); 391 fd = -1; 392 } 393 394 #ifdef CONFIG_TESTIO_DEBUG 395 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { 396 io_ptr = test_io_manager; 397 test_io_backing_manager = unix_io_manager; 398 } else 399 #endif 400 io_ptr = unix_io_manager; 401 402 if (!(mount_flags & EXT2_MF_MOUNTED)) 403 io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; 404 405 io_flags |= EXT2_FLAG_64BITS; 406 if (undo_file) { 407 retval = resize2fs_setup_tdb(device_name, undo_file, &io_ptr); 408 if (retval) 409 exit(1); 410 } 411 retval = ext2fs_open2(device_name, io_options, io_flags, 412 0, 0, io_ptr, &fs); 413 if (retval) { 414 com_err(program_name, retval, _("while trying to open %s"), 415 device_name); 416 printf("%s", _("Couldn't find valid filesystem superblock.\n")); 417 exit (1); 418 } 419 fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; 420 421 /* 422 * Before acting on an unmounted filesystem, make sure it's ok, 423 * unless the user is forcing it. 424 * 425 * We do ERROR and VALID checks even if we're only printing the 426 * minimum size, because traversal of a badly damaged filesystem 427 * can cause issues as well. We don't require it to be fscked after 428 * the last mount time in this case, though, as this is a bit less 429 * risky. 430 */ 431 if (!force && !(mount_flags & EXT2_MF_MOUNTED)) { 432 int checkit = 0; 433 434 if (fs->super->s_state & EXT2_ERROR_FS) 435 checkit = 1; 436 437 if ((fs->super->s_state & EXT2_VALID_FS) == 0) 438 checkit = 1; 439 440 if ((fs->super->s_lastcheck < fs->super->s_mtime) && 441 !print_min_size) 442 checkit = 1; 443 444 if ((ext2fs_free_blocks_count(fs->super) > 445 ext2fs_blocks_count(fs->super)) || 446 (fs->super->s_free_inodes_count > fs->super->s_inodes_count)) 447 checkit = 1; 448 449 if (checkit) { 450 fprintf(stderr, 451 _("Please run 'e2fsck -f %s' first.\n\n"), 452 device_name); 453 exit(1); 454 } 455 } 456 457 /* 458 * Check for compatibility with the feature sets. We need to 459 * be more stringent than ext2fs_open(). 460 */ 461 if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) { 462 com_err(program_name, EXT2_ET_UNSUPP_FEATURE, 463 "(%s)", device_name); 464 exit(1); 465 } 466 467 min_size = calculate_minimum_resize_size(fs, flags); 468 469 if (print_min_size) { 470 printf(_("Estimated minimum size of the filesystem: %llu\n"), 471 min_size); 472 exit(0); 473 } 474 475 /* Determine the system page size if possible */ 476 #ifdef HAVE_SYSCONF 477 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) 478 #define _SC_PAGESIZE _SC_PAGE_SIZE 479 #endif 480 #ifdef _SC_PAGESIZE 481 sysval = sysconf(_SC_PAGESIZE); 482 if (sysval > 0) 483 sys_page_size = sysval; 484 #endif /* _SC_PAGESIZE */ 485 #endif /* HAVE_SYSCONF */ 486 487 /* 488 * Get the size of the containing partition, and use this for 489 * defaults and for making sure the new filesystem doesn't 490 * exceed the partition size. 491 */ 492 blocksize = fs->blocksize; 493 retval = ext2fs_get_device_size2(device_name, blocksize, 494 &max_size); 495 if (retval) { 496 com_err(program_name, retval, "%s", 497 _("while trying to determine filesystem size")); 498 exit(1); 499 } 500 if (force_min_size) 501 new_size = min_size; 502 else if (new_size_str) { 503 new_size = parse_num_blocks2(new_size_str, 504 fs->super->s_log_block_size); 505 if (new_size == 0) { 506 com_err(program_name, 0, 507 _("Invalid new size: %s\n"), new_size_str); 508 exit(1); 509 } 510 } else { 511 new_size = max_size; 512 /* Round down to an even multiple of a pagesize */ 513 if (sys_page_size > blocksize) 514 new_size &= ~((blk64_t)((sys_page_size / blocksize)-1)); 515 } 516 /* If changing 64bit, don't change the filesystem size. */ 517 if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) { 518 new_size = ext2fs_blocks_count(fs->super); 519 } 520 if (!ext2fs_has_feature_64bit(fs->super)) { 521 /* Take 16T down to 2^32-1 blocks */ 522 if (new_size == (1ULL << 32)) 523 new_size--; 524 else if (new_size > (1ULL << 32)) { 525 com_err(program_name, 0, "%s", 526 _("New size too large to be " 527 "expressed in 32 bits\n")); 528 exit(1); 529 } 530 } 531 532 if (!force && new_size < min_size) { 533 com_err(program_name, 0, 534 _("New size smaller than minimum (%llu)\n"), min_size); 535 exit(1); 536 } 537 if (use_stride >= 0) { 538 if (use_stride >= (int) fs->super->s_blocks_per_group) { 539 com_err(program_name, 0, "%s", 540 _("Invalid stride length")); 541 exit(1); 542 } 543 fs->stride = fs->super->s_raid_stride = use_stride; 544 ext2fs_mark_super_dirty(fs); 545 } else 546 determine_fs_stride(fs); 547 548 /* 549 * If we are resizing a plain file, and it's not big enough, 550 * automatically extend it in a sparse fashion by writing the 551 * last requested block. 552 */ 553 new_file_size = ((__u64) new_size) * blocksize; 554 if ((__u64) new_file_size > 555 (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1) 556 fd = -1; 557 if ((new_file_size > st_buf.st_size) && 558 (fd > 0)) { 559 if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) && 560 (write(fd, "0", 1) == 1)) 561 max_size = new_size; 562 } 563 if (!force && (new_size > max_size)) { 564 fprintf(stderr, _("The containing partition (or device)" 565 " is only %llu (%dk) blocks.\nYou requested a new size" 566 " of %llu blocks.\n\n"), max_size, 567 blocksize / 1024, new_size); 568 exit(1); 569 } 570 if ((flags & RESIZE_DISABLE_64BIT) && (flags & RESIZE_ENABLE_64BIT)) { 571 fprintf(stderr, _("Cannot set and unset 64bit feature.\n")); 572 exit(1); 573 } else if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) { 574 if (new_size >= (1ULL << 32)) { 575 fprintf(stderr, _("Cannot change the 64bit feature " 576 "on a filesystem that is larger than " 577 "2^32 blocks.\n")); 578 exit(1); 579 } 580 if (mount_flags & EXT2_MF_MOUNTED) { 581 fprintf(stderr, _("Cannot change the 64bit feature " 582 "while the filesystem is mounted.\n")); 583 exit(1); 584 } 585 if (flags & RESIZE_ENABLE_64BIT && 586 !ext2fs_has_feature_extents(fs->super)) { 587 fprintf(stderr, _("Please enable the extents feature " 588 "with tune2fs before enabling the 64bit " 589 "feature.\n")); 590 exit(1); 591 } 592 } else if (new_size == ext2fs_blocks_count(fs->super)) { 593 fprintf(stderr, _("The filesystem is already %llu (%dk) " 594 "blocks long. Nothing to do!\n\n"), new_size, 595 blocksize / 1024); 596 exit(0); 597 } 598 if ((flags & RESIZE_ENABLE_64BIT) && 599 ext2fs_has_feature_64bit(fs->super)) { 600 fprintf(stderr, _("The filesystem is already 64-bit.\n")); 601 exit(0); 602 } 603 if ((flags & RESIZE_DISABLE_64BIT) && 604 !ext2fs_has_feature_64bit(fs->super)) { 605 fprintf(stderr, _("The filesystem is already 32-bit.\n")); 606 exit(0); 607 } 608 if (mount_flags & EXT2_MF_MOUNTED) { 609 retval = online_resize_fs(fs, mtpt, &new_size, flags); 610 } else { 611 bigalloc_check(fs, force); 612 if (flags & RESIZE_ENABLE_64BIT) 613 printf(_("Converting the filesystem to 64-bit.\n")); 614 else if (flags & RESIZE_DISABLE_64BIT) 615 printf(_("Converting the filesystem to 32-bit.\n")); 616 else 617 printf(_("Resizing the filesystem on " 618 "%s to %llu (%dk) blocks.\n"), 619 device_name, new_size, blocksize / 1024); 620 retval = resize_fs(fs, &new_size, flags, 621 ((flags & RESIZE_PERCENT_COMPLETE) ? 622 resize_progress_func : 0)); 623 } 624 free(mtpt); 625 if (retval) { 626 com_err(program_name, retval, _("while trying to resize %s"), 627 device_name); 628 fprintf(stderr, 629 _("Please run 'e2fsck -fy %s' to fix the filesystem\n" 630 "after the aborted resize operation.\n"), 631 device_name); 632 ext2fs_close_free(&fs); 633 exit(1); 634 } 635 printf(_("The filesystem on %s is now %llu (%dk) blocks long.\n\n"), 636 device_name, new_size, blocksize / 1024); 637 638 if ((st_buf.st_size > new_file_size) && 639 (fd > 0)) { 640 #ifdef HAVE_FTRUNCATE64 641 retval = ftruncate64(fd, new_file_size); 642 #else 643 retval = 0; 644 /* Only truncate if new_file_size doesn't overflow off_t */ 645 if (((off_t) new_file_size) == new_file_size) 646 retval = ftruncate(fd, (off_t) new_file_size); 647 #endif 648 if (retval) 649 com_err(program_name, retval, 650 _("while trying to truncate %s"), 651 device_name); 652 } 653 if (fd > 0) 654 close(fd); 655 remove_error_table(&et_ext2_error_table); 656 return (0); 657 } 658