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 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 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 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 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 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