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 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <fcntl.h> 28 29 #include "e2p/e2p.h" 30 31 #include "resize2fs.h" 32 33 #include "../version.h" 34 35 char *program_name, *device_name, *io_options; 36 37 static void usage (char *prog) 38 { 39 fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-p] " 40 "device [new_size]\n\n"), prog); 41 42 exit (1); 43 } 44 45 static errcode_t resize_progress_func(ext2_resize_t rfs, int pass, 46 unsigned long cur, unsigned long max) 47 { 48 ext2_sim_progmeter progress; 49 const char *label; 50 errcode_t retval; 51 52 progress = (ext2_sim_progmeter) rfs->prog_data; 53 if (max == 0) 54 return 0; 55 if (cur == 0) { 56 if (progress) 57 ext2fs_progress_close(progress); 58 progress = 0; 59 switch (pass) { 60 case E2_RSZ_EXTEND_ITABLE_PASS: 61 label = _("Extending the inode table"); 62 break; 63 case E2_RSZ_BLOCK_RELOC_PASS: 64 label = _("Relocating blocks"); 65 break; 66 case E2_RSZ_INODE_SCAN_PASS: 67 label = _("Scanning inode table"); 68 break; 69 case E2_RSZ_INODE_REF_UPD_PASS: 70 label = _("Updating inode references"); 71 break; 72 case E2_RSZ_MOVE_ITABLE_PASS: 73 label = _("Moving inode table"); 74 break; 75 default: 76 label = _("Unknown pass?!?"); 77 break; 78 } 79 printf(_("Begin pass %d (max = %lu)\n"), pass, max); 80 retval = ext2fs_progress_init(&progress, label, 30, 81 40, max, 0); 82 if (retval) 83 progress = 0; 84 rfs->prog_data = (void *) progress; 85 } 86 if (progress) 87 ext2fs_progress_update(progress, cur); 88 if (cur >= max) { 89 if (progress) 90 ext2fs_progress_close(progress); 91 progress = 0; 92 rfs->prog_data = 0; 93 } 94 return 0; 95 } 96 97 static void determine_fs_stride(ext2_filsys fs) 98 { 99 unsigned int group; 100 unsigned long long sum; 101 unsigned int has_sb, prev_has_sb, num; 102 int i_stride, b_stride; 103 104 if (fs->stride) 105 return; 106 num = 0; sum = 0; 107 for (group = 0; group < fs->group_desc_count; group++) { 108 has_sb = ext2fs_bg_has_super(fs, group); 109 if (group == 0 || has_sb != prev_has_sb) 110 goto next; 111 b_stride = fs->group_desc[group].bg_block_bitmap - 112 fs->group_desc[group-1].bg_block_bitmap - 113 fs->super->s_blocks_per_group; 114 i_stride = fs->group_desc[group].bg_inode_bitmap - 115 fs->group_desc[group-1].bg_inode_bitmap - 116 fs->super->s_blocks_per_group; 117 if (b_stride != i_stride || 118 b_stride < 0) 119 goto next; 120 121 /* printf("group %d has stride %d\n", group, b_stride); */ 122 sum += b_stride; 123 num++; 124 125 next: 126 prev_has_sb = has_sb; 127 } 128 129 if (fs->group_desc_count > 12 && num < 3) 130 sum = 0; 131 132 if (num) 133 fs->stride = sum / num; 134 else 135 fs->stride = 0; 136 137 fs->super->s_raid_stride = fs->stride; 138 ext2fs_mark_super_dirty(fs); 139 140 #if 0 141 if (fs->stride) 142 printf("Using RAID stride of %d\n", fs->stride); 143 #endif 144 } 145 146 int main (int argc, char ** argv) 147 { 148 errcode_t retval; 149 ext2_filsys fs; 150 int c; 151 int flags = 0; 152 int flush = 0; 153 int force = 0; 154 int io_flags = 0; 155 int fd, ret; 156 blk_t new_size = 0; 157 blk_t max_size = 0; 158 io_manager io_ptr; 159 char *new_size_str = 0; 160 int use_stride = -1; 161 #ifdef HAVE_FSTAT64 162 struct stat64 st_buf; 163 #else 164 struct stat st_buf; 165 #endif 166 __s64 new_file_size; 167 unsigned int sys_page_size = 4096; 168 long sysval; 169 int len, mount_flags; 170 char *mtpt; 171 172 #ifdef ENABLE_NLS 173 setlocale(LC_MESSAGES, ""); 174 setlocale(LC_CTYPE, ""); 175 bindtextdomain(NLS_CAT_NAME, LOCALEDIR); 176 textdomain(NLS_CAT_NAME); 177 #endif 178 179 add_error_table(&et_ext2_error_table); 180 181 fprintf (stderr, "resize2fs %s (%s)\n", 182 E2FSPROGS_VERSION, E2FSPROGS_DATE); 183 if (argc && *argv) 184 program_name = *argv; 185 186 while ((c = getopt (argc, argv, "d:fFhpS:")) != EOF) { 187 switch (c) { 188 case 'h': 189 usage(program_name); 190 break; 191 case 'f': 192 force = 1; 193 break; 194 case 'F': 195 flush = 1; 196 break; 197 case 'd': 198 flags |= atoi(optarg); 199 break; 200 case 'p': 201 flags |= RESIZE_PERCENT_COMPLETE; 202 break; 203 case 'S': 204 use_stride = atoi(optarg); 205 break; 206 default: 207 usage(program_name); 208 } 209 } 210 if (optind == argc) 211 usage(program_name); 212 213 device_name = argv[optind++]; 214 if (optind < argc) 215 new_size_str = argv[optind++]; 216 if (optind < argc) 217 usage(program_name); 218 219 io_options = strchr(device_name, '?'); 220 if (io_options) 221 *io_options++ = 0; 222 223 /* 224 * Figure out whether or not the device is mounted, and if it is 225 * where it is mounted. 226 */ 227 len=80; 228 while (1) { 229 mtpt = malloc(len); 230 if (!mtpt) 231 return ENOMEM; 232 mtpt[len-1] = 0; 233 retval = ext2fs_check_mount_point(device_name, &mount_flags, 234 mtpt, len); 235 if (retval) { 236 com_err("ext2fs_check_mount_point", retval, 237 _("while determining whether %s is mounted."), 238 device_name); 239 exit(1); 240 } 241 if (!(mount_flags & EXT2_MF_MOUNTED) || (mtpt[len-1] == 0)) 242 break; 243 free(mtpt); 244 len = 2 * len; 245 } 246 247 #ifdef HAVE_OPEN64 248 fd = open64(device_name, O_RDWR); 249 #else 250 fd = open(device_name, O_RDWR); 251 #endif 252 if (fd < 0) { 253 com_err("open", errno, _("while opening %s"), 254 device_name); 255 exit(1); 256 } 257 258 #ifdef HAVE_FSTAT64 259 ret = fstat64(fd, &st_buf); 260 #else 261 ret = fstat(fd, &st_buf); 262 #endif 263 if (ret < 0) { 264 com_err("open", errno, 265 _("while getting stat information for %s"), 266 device_name); 267 exit(1); 268 } 269 270 if (flush) { 271 retval = ext2fs_sync_device(fd, 1); 272 if (retval) { 273 com_err(argv[0], retval, 274 _("while trying to flush %s"), 275 device_name); 276 exit(1); 277 } 278 } 279 280 if (!S_ISREG(st_buf.st_mode )) { 281 close(fd); 282 fd = -1; 283 } 284 285 if (flags & RESIZE_DEBUG_IO) { 286 io_ptr = test_io_manager; 287 test_io_backing_manager = unix_io_manager; 288 } else 289 io_ptr = unix_io_manager; 290 291 if (!(mount_flags & EXT2_MF_MOUNTED)) 292 io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE; 293 retval = ext2fs_open2(device_name, io_options, io_flags, 294 0, 0, io_ptr, &fs); 295 if (retval) { 296 com_err (program_name, retval, _("while trying to open %s"), 297 device_name); 298 printf (_("Couldn't find valid filesystem superblock.\n")); 299 exit (1); 300 } 301 /* 302 * Check for compatibility with the feature sets. We need to 303 * be more stringent than ext2fs_open(). 304 */ 305 if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) { 306 com_err(program_name, EXT2_ET_UNSUPP_FEATURE, 307 "(%s)", device_name); 308 exit(1); 309 } 310 311 /* Determine the system page size if possible */ 312 #ifdef HAVE_SYSCONF 313 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) 314 #define _SC_PAGESIZE _SC_PAGE_SIZE 315 #endif 316 #ifdef _SC_PAGESIZE 317 sysval = sysconf(_SC_PAGESIZE); 318 if (sysval > 0) 319 sys_page_size = sysval; 320 #endif /* _SC_PAGESIZE */ 321 #endif /* HAVE_SYSCONF */ 322 323 /* 324 * Get the size of the containing partition, and use this for 325 * defaults and for making sure the new filesystem doesn't 326 * exceed the partition size. 327 */ 328 retval = ext2fs_get_device_size(device_name, fs->blocksize, 329 &max_size); 330 if (retval) { 331 com_err(program_name, retval, 332 _("while trying to determine filesystem size")); 333 exit(1); 334 } 335 if (new_size_str) { 336 new_size = parse_num_blocks(new_size_str, 337 fs->super->s_log_block_size); 338 if (!new_size) { 339 com_err(program_name, 0, _("bad filesystem size - %s"), 340 new_size_str); 341 exit(1); 342 } 343 } else { 344 new_size = max_size; 345 /* Round down to an even multiple of a pagesize */ 346 if (sys_page_size > fs->blocksize) 347 new_size &= ~((sys_page_size / fs->blocksize)-1); 348 } 349 350 if (use_stride >= 0) { 351 if (use_stride >= (int) fs->super->s_blocks_per_group) { 352 com_err(program_name, 0, 353 _("Invalid stride length")); 354 exit(1); 355 } 356 fs->stride = fs->super->s_raid_stride = use_stride; 357 ext2fs_mark_super_dirty(fs); 358 } else 359 determine_fs_stride(fs); 360 361 /* 362 * If we are resizing a plain file, and it's not big enough, 363 * automatically extend it in a sparse fashion by writing the 364 * last requested block. 365 */ 366 new_file_size = ((__u64) new_size) * fs->blocksize; 367 if ((__u64) new_file_size > 368 (((__u64) 1) << (sizeof(st_buf.st_size)*8 - 1)) - 1) 369 fd = -1; 370 if ((new_file_size > st_buf.st_size) && 371 (fd > 0)) { 372 if ((ext2fs_llseek(fd, new_file_size-1, SEEK_SET) >= 0) && 373 (write(fd, "0", 1) == 1)) 374 max_size = new_size; 375 } 376 if (!force && (new_size > max_size)) { 377 fprintf(stderr, _("The containing partition (or device)" 378 " is only %u (%dk) blocks.\nYou requested a new size" 379 " of %u blocks.\n\n"), max_size, 380 fs->blocksize / 1024, new_size); 381 exit(1); 382 } 383 if (new_size == fs->super->s_blocks_count) { 384 fprintf(stderr, _("The filesystem is already %u blocks " 385 "long. Nothing to do!\n\n"), new_size); 386 exit(0); 387 } 388 if (mount_flags & EXT2_MF_MOUNTED) { 389 retval = online_resize_fs(fs, mtpt, &new_size, flags); 390 } else { 391 if (!force && ((fs->super->s_lastcheck < fs->super->s_mtime) || 392 (fs->super->s_state & EXT2_ERROR_FS) || 393 ((fs->super->s_state & EXT2_VALID_FS) == 0))) { 394 fprintf(stderr, 395 _("Please run 'e2fsck -f %s' first.\n\n"), 396 device_name); 397 exit(1); 398 } 399 printf("Resizing the filesystem on %s to %u (%dk) blocks.\n", 400 device_name, new_size, fs->blocksize / 1024); 401 retval = resize_fs(fs, &new_size, flags, 402 ((flags & RESIZE_PERCENT_COMPLETE) ? 403 resize_progress_func : 0)); 404 } 405 if (retval) { 406 com_err(program_name, retval, _("while trying to resize %s"), 407 device_name); 408 ext2fs_close (fs); 409 exit(1); 410 } 411 printf(_("The filesystem on %s is now %u blocks long.\n\n"), 412 device_name, new_size); 413 414 if ((st_buf.st_size > new_file_size) && 415 (fd > 0)) { 416 #ifdef HAVE_FTRUNCATE64 417 ftruncate64(fd, new_file_size); 418 #else 419 /* Only truncate if new_file_size doesn't overflow off_t */ 420 if (((off_t) new_file_size) == new_file_size) 421 ftruncate(fd, (off_t) new_file_size); 422 #endif 423 } 424 if (fd > 0) 425 close(fd); 426 remove_error_table(&et_ext2_error_table); 427 return (0); 428 } 429