ntfsresize.c revision 9663:ace9a2ac3683
1/** 2 * ntfsresize - Part of the Linux-NTFS project. 3 * 4 * Copyright (c) 2002-2006 Szabolcs Szakacsits 5 * Copyright (c) 2002-2005 Anton Altaparmakov 6 * Copyright (c) 2002-2003 Richard Russon 7 * Copyright (c) 2007 Yura Pakhuchiy 8 * 9 * This utility will resize an NTFS volume without data loss. 10 * 11 * WARNING FOR DEVELOPERS!!! Several external tools grep for text messages 12 * to control execution thus if you would like to change any message 13 * then PLEASE think twice before doing so then don't modify it. Thanks! 14 * 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program (in the main directory of the Linux-NTFS 27 * distribution in the file COPYING); if not, write to the Free Software 28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 29 */ 30 31#include "config.h" 32 33#ifdef HAVE_UNISTD_H 34#include <unistd.h> 35#endif 36#ifdef HAVE_STDLIB_H 37#include <stdlib.h> 38#endif 39#ifdef HAVE_STDIO_H 40#include <stdio.h> 41#endif 42#ifdef HAVE_STDARG_H 43#include <stdarg.h> 44#endif 45#ifdef HAVE_STRING_H 46#include <string.h> 47#endif 48#ifdef HAVE_ERRNO_H 49#include <errno.h> 50#endif 51#ifdef HAVE_GETOPT_H 52#include <getopt.h> 53#endif 54 55#include "debug.h" 56#include "types.h" 57#include "support.h" 58#include "endians.h" 59#include "bootsect.h" 60#include "device.h" 61#include "attrib.h" 62#include "volume.h" 63#include "mft.h" 64#include "bitmap.h" 65#include "inode.h" 66#include "runlist.h" 67#include "utils.h" 68#include "version.h" 69 70static const char *EXEC_NAME = "ntfsresize"; 71 72static const char *resize_warning_msg = 73"WARNING: Every sanity check passed and only the dangerous operations left.\n" 74"Make sure that important data has been backed up! Power outage or computer\n" 75"crash may result major data loss!\n"; 76 77static const char *resize_important_msg = 78#ifdef __sun 79"When booted, Windows will check the file system and may reboot.\n" 80"If you are running this from inside parted, STOP reading now.\n" 81"Otherwise, you can go on to shrink the device with fdisk or parted.\n" 82#else 83"You can go on to shrink the device for example with Linux fdisk.\n" 84#endif 85"IMPORTANT: When recreating the partition, make sure that you\n" 86" 1) create it at the same disk sector (use sector as the unit!)\n" 87" 2) create it with the same partition type (usually 7, HPFS/NTFS)\n" 88" 3) do not make it smaller than the new NTFS filesystem size\n" 89" 4) set the bootable flag for the partition if it existed before\n" 90"Otherwise you won't be able to access NTFS or can't boot from the disk!\n" 91"If you make a mistake and don't have a partition table backup then you\n" 92"can recover the partition table by TestDisk or Parted's rescue mode.\n"; 93 94static const char *invalid_ntfs_msg = 95"The device '%s' doesn't have a valid NTFS.\n" 96"Maybe you selected the wrong partition? Or the whole disk instead of a\n" 97"partition (e.g. /dev/hda, not /dev/hda1)? This error might also occur\n" 98"if the disk was incorrectly repartitioned (see the ntfsresize FAQ).\n"; 99 100static const char *corrupt_volume_msg = 101"NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" 102"The usage of the /f parameter is very IMPORTANT! No modification was\n" 103"and will be made to NTFS by this software until it gets repaired.\n"; 104 105static const char *hibernated_volume_msg = 106"The NTFS partition is hibernated. Windows must be resumed and turned off\n" 107"properly, so resizing could be done safely.\n"; 108 109static const char *unclean_journal_msg = 110"The NTFS journal file is unclean. Please shutdown Windows properly before\n" 111"using this software! Note, if you have run chkdsk previously then boot\n" 112"Windows again which will automatically initialize the journal correctly.\n"; 113 114static const char *opened_volume_msg = 115"This software has detected that the NTFS volume is already opened by another\n" 116"software thus it refuses to progress to preserve data consistency.\n"; 117 118static const char *bad_sectors_warning_msg = 119"****************************************************************************\n" 120"* WARNING: The disk has bad sector. This means physical damage on the disk *\n" 121"* surface caused by deterioration, manufacturing faults or other reason. *\n" 122"* The reliability of the disk may stay stable or degrade fast. We suggest *\n" 123"* making a full backup urgently by running 'ntfsclone --rescue ...' then *\n" 124"* run 'chkdsk /f /r' on Windows and rebooot it TWICE! Then you can resize *\n" 125"* NTFS safely by additionally using the --bad-sectors option of ntfsresize.*\n" 126"****************************************************************************\n"; 127 128static const char *many_bad_sectors_msg = 129"***************************************************************************\n" 130"* WARNING: The disk has many bad sectors. This means physical damage *\n" 131"* on the disk surface caused by deterioration, manufacturing faults or *\n" 132"* other reason. We suggest to get a replacement disk as soon as possible. *\n" 133"***************************************************************************\n"; 134 135static struct { 136 int verbose; 137 int debug; 138 int ro_flag; 139 int force; 140 int info; 141 int show_progress; 142 int badsectors; 143 s64 bytes; 144 char *volume; 145} opt; 146 147struct bitmap { 148 s64 size; 149 u8 *bm; 150}; 151 152#define NTFS_PROGBAR 0x0001 153#define NTFS_PROGBAR_SUPPRESS 0x0002 154 155struct progress_bar { 156 u64 start; 157 u64 stop; 158 int resolution; 159 int flags; 160 float unit; 161}; 162 163struct llcn_t { 164 s64 lcn; /* last used LCN for a "special" file/attr type */ 165 s64 inode; /* inode using it */ 166}; 167 168#define NTFSCK_PROGBAR 0x0001 169 170typedef struct { 171 ntfs_inode *ni; /* inode being processed */ 172 ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ 173 s64 inuse; /* num of clusters in use */ 174 int multi_ref; /* num of clusters referenced many times */ 175 int outsider; /* num of clusters outside the volume */ 176 int show_outsider; /* controls showing the above information */ 177 int flags; 178 struct bitmap lcn_bitmap; 179} ntfsck_t; 180 181typedef struct { 182 ntfs_volume *vol; 183 ntfs_inode *ni; /* inode being processed */ 184 s64 new_volume_size; /* in clusters; 0 = --info w/o --size */ 185 MFT_REF mref; /* mft reference */ 186 MFT_RECORD *mrec; /* mft record */ 187 ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ 188 u64 relocations; /* num of clusters to relocate */ 189 s64 inuse; /* num of clusters in use */ 190 runlist mftmir_rl; /* $MFTMirr AT_DATA's new position */ 191 s64 mftmir_old; /* $MFTMirr AT_DATA's old LCN */ 192 int dirty_inode; /* some inode data got relocated */ 193 int shrink; /* shrink = 1, enlarge = 0 */ 194 s64 badclusters; /* num of physically dead clusters */ 195 VCN mft_highest_vcn; /* used for relocating the $MFT */ 196 struct progress_bar progress; 197 struct bitmap lcn_bitmap; 198 /* Temporary statistics until all case is supported */ 199 struct llcn_t last_mft; 200 struct llcn_t last_mftmir; 201 struct llcn_t last_multi_mft; 202 struct llcn_t last_sparse; 203 struct llcn_t last_compressed; 204 struct llcn_t last_lcn; 205 s64 last_unsupp; /* last unsupported cluster */ 206} ntfs_resize_t; 207 208/* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster 209 allocation related structure, attached to ntfs_resize_t */ 210static s64 max_free_cluster_range = 0; 211 212#define NTFS_MBYTE (1000 * 1000) 213 214/* WARNING: don't modify the text, external tools grep for it */ 215#define ERR_PREFIX "ERROR" 216#define PERR_PREFIX ERR_PREFIX "(%d): " 217#define NERR_PREFIX ERR_PREFIX ": " 218 219#define DIRTY_NONE (0) 220#define DIRTY_INODE (1) 221#define DIRTY_ATTRIB (2) 222 223#define NTFS_MAX_CLUSTER_SIZE (65536) 224 225static s64 rounded_up_division(s64 numer, s64 denom) 226{ 227 return (numer + (denom - 1)) / denom; 228} 229 230/** 231 * perr_printf 232 * 233 * Print an error message. 234 */ 235__attribute__((format(printf, 1, 2))) 236static void perr_printf(const char *fmt, ...) 237{ 238 va_list ap; 239 int eo = errno; 240 241 fprintf(stdout, PERR_PREFIX, eo); 242 va_start(ap, fmt); 243 vfprintf(stdout, fmt, ap); 244 va_end(ap); 245 fprintf(stdout, ": %s\n", strerror(eo)); 246 fflush(stdout); 247 fflush(stderr); 248} 249 250__attribute__((format(printf, 1, 2))) 251static void err_printf(const char *fmt, ...) 252{ 253 va_list ap; 254 255 fprintf(stdout, NERR_PREFIX); 256 va_start(ap, fmt); 257 vfprintf(stdout, fmt, ap); 258 va_end(ap); 259 fflush(stdout); 260 fflush(stderr); 261} 262 263/** 264 * err_exit 265 * 266 * Print and error message and exit the program. 267 */ 268__attribute__((noreturn)) 269__attribute__((format(printf, 1, 2))) 270static int err_exit(const char *fmt, ...) 271{ 272 va_list ap; 273 274 fprintf(stdout, NERR_PREFIX); 275 va_start(ap, fmt); 276 vfprintf(stdout, fmt, ap); 277 va_end(ap); 278 fflush(stdout); 279 fflush(stderr); 280 exit(1); 281} 282 283/** 284 * perr_exit 285 * 286 * Print and error message and exit the program 287 */ 288__attribute__((noreturn)) 289__attribute__((format(printf, 1, 2))) 290static int perr_exit(const char *fmt, ...) 291{ 292 va_list ap; 293 int eo = errno; 294 295 fprintf(stdout, PERR_PREFIX, eo); 296 va_start(ap, fmt); 297 vfprintf(stdout, fmt, ap); 298 va_end(ap); 299 printf(": %s\n", strerror(eo)); 300 fflush(stdout); 301 fflush(stderr); 302 exit(1); 303} 304 305/** 306 * usage - Print a list of the parameters to the program 307 * 308 * Print a list of the parameters and options for the program. 309 * 310 * Return: none 311 */ 312__attribute__((noreturn)) 313static void usage(void) 314{ 315 316 printf("\nUsage: %s [OPTIONS] DEVICE\n" 317 " Resize an NTFS volume non-destructively, safely move any data if needed.\n" 318 "\n" 319 " -i, --info Estimate the smallest shrunken size possible\n" 320 " -s, --size SIZE Resize volume to SIZE[k|M|G] bytes\n" 321 "\n" 322 " -n, --no-action Do not write to disk\n" 323 " -b, --bad-sectors Support disks having bad sectors\n" 324 " -f, --force Force to progress\n" 325 " -P, --no-progress-bar Don't show progress bar\n" 326 " -v, --verbose More output\n" 327 " -V, --version Display version information\n" 328 " -h, --help Display this help\n" 329#ifdef DEBUG 330 " -d, --debug Show debug information\n" 331#endif 332 "\n" 333 " The options -i and -s are mutually exclusive. If both options are\n" 334 " omitted then the NTFS volume will be enlarged to the DEVICE size.\n" 335 "\n", EXEC_NAME); 336 printf("%s%s", ntfs_bugs, ntfs_home); 337 printf("Ntfsresize FAQ: http://linux-ntfs.sourceforge.net/info/ntfsresize.html\n"); 338 exit(1); 339} 340 341/** 342 * proceed_question 343 * 344 * Force the user to confirm an action before performing it. 345 * Copy-paste from e2fsprogs 346 */ 347static void proceed_question(void) 348{ 349 char buf[256]; 350 const char *short_yes = "yY"; 351 352 fflush(stdout); 353 fflush(stderr); 354 printf("Are you sure you want to proceed (y/[n])? "); 355 buf[0] = 0; 356 fgets(buf, sizeof(buf), stdin); 357 if (!strchr(short_yes, buf[0])) { 358 printf("OK quitting. NO CHANGES have been made to your " 359 "NTFS volume.\n"); 360 exit(1); 361 } 362} 363 364/** 365 * version - Print version information about the program 366 * 367 * Print a copyright statement and a brief description of the program. 368 * 369 * Return: none 370 */ 371static void version(void) 372{ 373 printf("\nResize an NTFS Volume, without data loss.\n\n"); 374 printf("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); 375 printf("Copyright (c) 2002-2005 Anton Altaparmakov\n"); 376 printf("Copyright (c) 2002-2003 Richard Russon\n"); 377 printf("Copyright (c) 2007 Yura Pakhuchiy\n"); 378 printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); 379} 380 381/** 382 * get_new_volume_size 383 * 384 * Convert a user-supplied string into a size. Without any suffix the number 385 * will be assumed to be in bytes. If the number has a suffix of k, M or G it 386 * will be scaled up by 1000, 1000000, or 1000000000. 387 */ 388static s64 get_new_volume_size(char *s) 389{ 390 s64 size; 391 char *suffix; 392 int prefix_kind = 1000; 393 394 size = strtoll(s, &suffix, 10); 395 if (size <= 0 || errno == ERANGE) 396 err_exit("Illegal new volume size\n"); 397 398 if (!*suffix) 399 return size; 400 401 if (strlen(suffix) == 2 && suffix[1] == 'i') 402 prefix_kind = 1024; 403 else if (strlen(suffix) > 1) 404 usage(); 405 406 /* We follow the SI prefixes: 407 http://physics.nist.gov/cuu/Units/prefixes.html 408 http://physics.nist.gov/cuu/Units/binary.html 409 Disk partitioning tools use prefixes as, 410 k M G 411 fdisk 2.11x- 2^10 2^20 10^3*2^20 412 fdisk 2.11y+ 10^3 10^6 10^9 413 cfdisk 10^3 10^6 10^9 414 sfdisk 2^10 2^20 415 parted 2^10 2^20 (may change) 416 fdisk (DOS) 2^10 2^20 417 */ 418 /* FIXME: check for overflow */ 419 switch (*suffix) { 420 case 'G': 421 size *= prefix_kind; 422 case 'M': 423 size *= prefix_kind; 424 case 'k': 425 size *= prefix_kind; 426 break; 427 default: 428 usage(); 429 } 430 431 return size; 432} 433 434/** 435 * parse_options - Read and validate the programs command line 436 * 437 * Read the command line, verify the syntax and parse the options. 438 * This function is very long, but quite simple. 439 * 440 * Return: 1 Success 441 * 0 Error, one or more problems 442 */ 443static int parse_options(int argc, char **argv) 444{ 445 static const char *sopt = "-bdfhinPs:vV"; 446 static const struct option lopt[] = { 447 { "bad-sectors",no_argument, NULL, 'b' }, 448#ifdef DEBUG 449 { "debug", no_argument, NULL, 'd' }, 450#endif 451 { "force", no_argument, NULL, 'f' }, 452 { "help", no_argument, NULL, 'h' }, 453 { "info", no_argument, NULL, 'i' }, 454 { "no-action", no_argument, NULL, 'n' }, 455 { "no-progress-bar", no_argument, NULL, 'P' }, 456 { "size", required_argument, NULL, 's' }, 457 { "verbose", no_argument, NULL, 'v' }, 458 { "version", no_argument, NULL, 'V' }, 459 { NULL, 0, NULL, 0 } 460 }; 461 462 int c; 463 int err = 0; 464 int ver = 0; 465 int help = 0; 466 467 memset(&opt, 0, sizeof(opt)); 468 opt.show_progress = 1; 469 470 while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { 471 switch (c) { 472 case 1: /* A non-option argument */ 473 if (!err && !opt.volume) 474 opt.volume = argv[optind-1]; 475 else 476 err++; 477 break; 478 case 'b': 479 opt.badsectors++; 480 break; 481 case 'd': 482 opt.debug++; 483 break; 484 case 'f': 485 opt.force++; 486 break; 487 case 'h': 488 case '?': 489 help++; 490 break; 491 case 'i': 492 opt.info++; 493 break; 494 case 'n': 495 opt.ro_flag = NTFS_MNT_RDONLY; 496 break; 497 case 'P': 498 opt.show_progress = 0; 499 break; 500 case 's': 501 if (!err && (opt.bytes == 0)) 502 opt.bytes = get_new_volume_size(optarg); 503 else 504 err++; 505 break; 506 case 'v': 507 opt.verbose++; 508 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); 509 break; 510 case 'V': 511 ver++; 512 break; 513 default: 514 if (optopt == 's') { 515 printf("Option '%s' requires an argument.\n", argv[optind-1]); 516 } else { 517 printf("Unknown option '%s'.\n", argv[optind-1]); 518 } 519 err++; 520 break; 521 } 522 } 523 524 if (!help && !ver) { 525 if (opt.volume == NULL) { 526 if (argc > 1) 527 printf("You must specify exactly one device.\n"); 528 err++; 529 } 530 if (opt.info) { 531 opt.ro_flag = NTFS_MNT_RDONLY; 532 if (opt.bytes) { 533 printf(NERR_PREFIX "Options --info and --size " 534 "can't be used together.\n"); 535 usage(); 536 } 537 } 538 } 539 540 /* Redirect stderr to stdout, note fflush()es are essential! */ 541 fflush(stdout); 542 fflush(stderr); 543 if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) 544 perr_exit("Failed to redirect stderr to stdout"); 545 fflush(stdout); 546 fflush(stderr); 547 548#ifdef DEBUG 549 if (!opt.debug) 550 if (!freopen("/dev/null", "w", stderr)) 551 perr_exit("Failed to redirect stderr to /dev/null"); 552#endif 553 554 if (ver) 555 version(); 556 if (help || err) 557 usage(); 558 559 return (!err && !help && !ver); 560} 561 562static void print_advise(ntfs_volume *vol, s64 supp_lcn) 563{ 564 s64 old_b, new_b, freed_b, old_mb, new_mb, freed_mb; 565 566 old_b = vol->nr_clusters * vol->cluster_size; 567 old_mb = rounded_up_division(old_b, NTFS_MBYTE); 568 569 /* Take the next supported cluster (free or relocatable) 570 plus reserve a cluster for the backup boot sector */ 571 supp_lcn += 2; 572 573 if (supp_lcn > vol->nr_clusters) { 574 err_printf("Very rare fragmentation type detected. " 575 "Sorry, it's not supported yet.\n" 576 "Try to defragment your NTFS, perhaps it helps.\n"); 577 exit(1); 578 } 579 580 new_b = supp_lcn * vol->cluster_size; 581 new_mb = rounded_up_division(new_b, NTFS_MBYTE); 582 freed_b = (vol->nr_clusters - supp_lcn + 1) * vol->cluster_size; 583 freed_mb = freed_b / NTFS_MBYTE; 584 585 /* WARNING: don't modify the text, external tools grep for it */ 586 printf("You might resize at %lld bytes ", (long long)new_b); 587 if ((new_mb * NTFS_MBYTE) < old_b) 588 printf("or %lld MB ", (long long)new_mb); 589 590 printf("(freeing "); 591 if (freed_mb && (old_mb - new_mb)) 592 printf("%lld MB", (long long)(old_mb - new_mb)); 593 else 594 printf("%lld bytes", (long long)freed_b); 595 printf(").\n"); 596 597 printf("Please make a test run using both the -n and -s options " 598 "before real resizing!\n"); 599} 600 601static void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len) 602{ 603 rl->vcn = vcn; 604 rl->lcn = lcn; 605 rl->length = len; 606} 607 608static int rl_items(runlist *rl) 609{ 610 int i = 0; 611 612 while (rl[i++].length) 613 ; 614 615 return i; 616} 617 618static void dump_run(runlist_element *r) 619{ 620 ntfs_log_verbose(" %8lld %8lld (0x%08llx) %lld\n", (long long)r->vcn, 621 (long long)r->lcn, (long long)r->lcn, 622 (long long)r->length); 623} 624 625static void dump_runlist(runlist *rl) 626{ 627 while (rl->length) 628 dump_run(rl++); 629} 630 631/** 632 * nr_clusters_to_bitmap_byte_size 633 * 634 * Take the number of clusters in the volume and calculate the size of $Bitmap. 635 * The size must be always a multiple of 8 bytes. 636 */ 637static s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) 638{ 639 s64 bm_bsize; 640 641 bm_bsize = rounded_up_division(nr_clusters, 8); 642 bm_bsize = (bm_bsize + 7) & ~7; 643 644 return bm_bsize; 645} 646 647static void collect_resize_constraints(ntfs_resize_t *resize, runlist *rl) 648{ 649 s64 inode, last_lcn; 650 ATTR_FLAGS flags; 651 ATTR_TYPES atype; 652 struct llcn_t *llcn = NULL; 653 int ret, supported = 0; 654 655 last_lcn = rl->lcn + (rl->length - 1); 656 657 inode = resize->ni->mft_no; 658 flags = resize->ctx->attr->flags; 659 atype = resize->ctx->attr->type; 660 661 if ((ret = ntfs_inode_badclus_bad(inode, resize->ctx->attr)) != 0) { 662 if (ret == -1) 663 perr_exit("Bad sector list check failed"); 664 return; 665 } 666 667 if (inode == FILE_Bitmap) { 668 llcn = &resize->last_lcn; 669 if (atype == AT_DATA && NInoAttrList(resize->ni)) 670 err_exit("Highly fragmented $Bitmap isn't supported yet."); 671 672 supported = 1; 673 674 } else if (inode == FILE_MFT) { 675 llcn = &resize->last_mft; 676 /* 677 * First run of $MFT AT_DATA isn't supported yet. 678 */ 679 if (atype != AT_DATA || rl->vcn) 680 supported = 1; 681 682 } else if (NInoAttrList(resize->ni)) { 683 llcn = &resize->last_multi_mft; 684 685 if (inode != FILE_MFTMirr) 686 supported = 1; 687 688 } else if (flags & ATTR_IS_SPARSE) { 689 llcn = &resize->last_sparse; 690 supported = 1; 691 692 } else if (flags & ATTR_IS_COMPRESSED) { 693 llcn = &resize->last_compressed; 694 supported = 1; 695 696 } else if (inode == FILE_MFTMirr) { 697 llcn = &resize->last_mftmir; 698 supported = 1; 699 700 /* Fragmented $MFTMirr DATA attribute isn't supported yet */ 701 if (atype == AT_DATA) 702 if (rl[1].length != 0 || rl->vcn) 703 supported = 0; 704 } else { 705 llcn = &resize->last_lcn; 706 supported = 1; 707 } 708 709 if (llcn->lcn < last_lcn) { 710 llcn->lcn = last_lcn; 711 llcn->inode = inode; 712 } 713 714 if (supported) 715 return; 716 717 if (resize->last_unsupp < last_lcn) 718 resize->last_unsupp = last_lcn; 719} 720 721 722static void collect_relocation_info(ntfs_resize_t *resize, runlist *rl) 723{ 724 s64 lcn, lcn_length, start, len, inode; 725 s64 new_vol_size; /* (last LCN on the volume) + 1 */ 726 727 lcn = rl->lcn; 728 lcn_length = rl->length; 729 inode = resize->ni->mft_no; 730 new_vol_size = resize->new_volume_size; 731 732 if (lcn + lcn_length <= new_vol_size) 733 return; 734 735 if (inode == FILE_Bitmap && resize->ctx->attr->type == AT_DATA) 736 return; 737 738 start = lcn; 739 len = lcn_length; 740 741 if (lcn < new_vol_size) { 742 start = new_vol_size; 743 len = lcn_length - (new_vol_size - lcn); 744 745 if (!opt.info && (inode == FILE_MFTMirr)) { 746 err_printf("$MFTMirr can't be split up yet. Please try " 747 "a different size.\n"); 748 print_advise(resize->vol, lcn + lcn_length - 1); 749 exit(1); 750 } 751 } 752 753 resize->relocations += len; 754 755 if (!opt.info || !resize->new_volume_size) 756 return; 757 758 printf("Relocation needed for inode %8lld attr 0x%x LCN 0x%08llx " 759 "length %6lld\n", (long long)inode, 760 (unsigned int)le32_to_cpu(resize->ctx->attr->type), 761 (unsigned long long)start, (long long)len); 762} 763 764/** 765 * build_lcn_usage_bitmap 766 * 767 * lcn_bitmap has one bit for each cluster on the disk. Initially, lcn_bitmap 768 * has no bits set. As each attribute record is read the bits in lcn_bitmap are 769 * checked to ensure that no other file already references that cluster. 770 * 771 * This serves as a rudimentary "chkdsk" operation. 772 */ 773static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck) 774{ 775 s64 inode; 776 ATTR_RECORD *a; 777 runlist *rl; 778 int i, j; 779 struct bitmap *lcn_bitmap = &fsck->lcn_bitmap; 780 781 a = fsck->ctx->attr; 782 inode = fsck->ni->mft_no; 783 784 if (!a->non_resident) 785 return; 786 787 if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) { 788 int err = errno; 789 perr_printf("ntfs_decompress_mapping_pairs"); 790 if (err == EIO) 791 printf("%s", corrupt_volume_msg); 792 exit(1); 793 } 794 795 796 for (i = 0; rl[i].length; i++) { 797 s64 lcn = rl[i].lcn; 798 s64 lcn_length = rl[i].length; 799 800 /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ 801 if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) 802 continue; 803 804 /* FIXME: ntfs_mapping_pairs_decompress should return error */ 805 if (lcn < 0 || lcn_length <= 0) 806 err_exit("Corrupt runlist in inode %lld attr %x LCN " 807 "%llx length %llx\n", inode, 808 (unsigned int)le32_to_cpu(a->type), lcn, 809 lcn_length); 810 811 for (j = 0; j < lcn_length; j++) { 812 u64 k = (u64)lcn + j; 813 814 if (k >= (u64)vol->nr_clusters) { 815 long long outsiders = lcn_length - j; 816 817 fsck->outsider += outsiders; 818 819 if (++fsck->show_outsider <= 10 || opt.verbose) 820 printf("Outside of the volume reference" 821 " for inode %lld at %lld:%lld\n", 822 inode, (long long)k, outsiders); 823 824 break; 825 } 826 827 if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) { 828 if (++fsck->multi_ref <= 10 || opt.verbose) 829 printf("Cluster %lld is referenced " 830 "multiple times!\n", 831 (long long)k); 832 continue; 833 } 834 } 835 fsck->inuse += lcn_length; 836 } 837 free(rl); 838} 839 840 841static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) 842{ 843 ntfs_attr_search_ctx *ret; 844 845 if ((ret = ntfs_attr_get_search_ctx(ni, mrec)) == NULL) 846 perr_printf("ntfs_attr_get_search_ctx"); 847 848 return ret; 849} 850 851/** 852 * walk_attributes 853 * 854 * For a given MFT Record, iterate through all its attributes. Any non-resident 855 * data runs will be marked in lcn_bitmap. 856 */ 857static int walk_attributes(ntfs_volume *vol, ntfsck_t *fsck) 858{ 859 if (!(fsck->ctx = attr_get_search_ctx(fsck->ni, NULL))) 860 return -1; 861 862 while (!ntfs_attrs_walk(fsck->ctx)) { 863 if (fsck->ctx->attr->type == AT_END) 864 break; 865 build_lcn_usage_bitmap(vol, fsck); 866 } 867 868 ntfs_attr_put_search_ctx(fsck->ctx); 869 return 0; 870} 871 872/** 873 * compare_bitmaps 874 * 875 * Compare two bitmaps. In this case, $Bitmap as read from the disk and 876 * lcn_bitmap which we built from the MFT Records. 877 */ 878static void compare_bitmaps(ntfs_volume *vol, struct bitmap *a) 879{ 880 s64 i, pos, count; 881 int mismatch = 0; 882 int backup_boot = 0; 883 u8 bm[NTFS_BUF_SIZE]; 884 885 printf("Accounting clusters ...\n"); 886 887 pos = 0; 888 while (1) { 889 count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); 890 if (count == -1) 891 perr_exit("Couldn't get $Bitmap $DATA"); 892 893 if (count == 0) { 894 if (a->size > pos) 895 err_exit("$Bitmap size is smaller than expected" 896 " (%lld != %lld)\n", a->size, pos); 897 break; 898 } 899 900 for (i = 0; i < count; i++, pos++) { 901 s64 cl; /* current cluster */ 902 903 if (a->size <= pos) 904 goto done; 905 906 if (a->bm[pos] == bm[i]) 907 continue; 908 909 for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { 910 char bit; 911 912 bit = ntfs_bit_get(a->bm, cl); 913 if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) 914 continue; 915 916 if (!mismatch && !bit && !backup_boot && 917 cl == vol->nr_clusters / 2) { 918 /* FIXME: call also boot sector check */ 919 backup_boot = 1; 920 printf("Found backup boot sector in " 921 "the middle of the volume.\n"); 922 continue; 923 } 924 925 if (++mismatch > 10 && !opt.verbose) 926 continue; 927 928 printf("Cluster accounting failed at %lld " 929 "(0x%llx): %s cluster in " 930 "$Bitmap\n", (long long)cl, 931 (unsigned long long)cl, 932 bit ? "missing" : "extra"); 933 } 934 } 935 } 936done: 937 if (mismatch) { 938 printf("Filesystem check failed! Totally %d cluster " 939 "accounting mismatches.\n", mismatch); 940 err_printf("%s", corrupt_volume_msg); 941 exit(1); 942 } 943} 944 945/** 946 * progress_init 947 * 948 * Create and scale our progress bar. 949 */ 950static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) 951{ 952 p->start = start; 953 p->stop = stop; 954 p->unit = 100.0 / (stop - start); 955 p->resolution = 100; 956 p->flags = flags; 957} 958 959/** 960 * progress_update 961 * 962 * Update the progress bar and tell the user. 963 */ 964static void progress_update(struct progress_bar *p, u64 current) 965{ 966 float percent; 967 968 if (!(p->flags & NTFS_PROGBAR)) 969 return; 970 if (p->flags & NTFS_PROGBAR_SUPPRESS) 971 return; 972 973 /* WARNING: don't modify the texts, external tools grep for them */ 974 percent = p->unit * current; 975 if (current != p->stop) { 976 if ((current - p->start) % p->resolution) 977 return; 978 printf("%6.2f percent completed\r", percent); 979 } else 980 printf("100.00 percent completed\n"); 981 fflush(stdout); 982} 983 984static int inode_close(ntfs_inode *ni) 985{ 986 if (ntfs_inode_close(ni)) { 987 perr_printf("ntfs_inode_close for inode %llu", 988 (unsigned long long)ni->mft_no); 989 return -1; 990 } 991 return 0; 992} 993 994/** 995 * walk_inodes 996 * 997 * Read each record in the MFT, skipping the unused ones, and build up a bitmap 998 * from all the non-resident attributes. 999 */ 1000static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck) 1001{ 1002 s64 nr_mft_records, inode = 0; 1003 ntfs_inode *ni; 1004 struct progress_bar progress; 1005 int pb_flags = 0; /* progress bar flags */ 1006 1007 /* WARNING: don't modify the text, external tools grep for it */ 1008 printf("Checking filesystem consistency ...\n"); 1009 1010 if (fsck->flags & NTFSCK_PROGBAR) 1011 pb_flags |= NTFS_PROGBAR; 1012 1013 nr_mft_records = vol->mft_na->initialized_size >> 1014 vol->mft_record_size_bits; 1015 1016 progress_init(&progress, inode, nr_mft_records - 1, pb_flags); 1017 1018 for (; inode < nr_mft_records; inode++) { 1019 progress_update(&progress, inode); 1020 1021 if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) { 1022 /* FIXME: continue only if it make sense, e.g. 1023 MFT record not in use based on $MFT bitmap */ 1024 if (errno == EIO || errno == ENOENT) 1025 continue; 1026 perr_printf("Reading inode %lld failed", inode); 1027 return -1; 1028 } 1029 1030 if (ni->mrec->base_mft_record) 1031 goto close_inode; 1032 1033 fsck->ni = ni; 1034 if (walk_attributes(vol, fsck) != 0) { 1035 inode_close(ni); 1036 return -1; 1037 } 1038close_inode: 1039 if (inode_close(ni) != 0) 1040 return -1; 1041 } 1042 return 0; 1043} 1044 1045static void build_resize_constraints(ntfs_resize_t *resize) 1046{ 1047 s64 i; 1048 runlist *rl; 1049 1050 if (!resize->ctx->attr->non_resident) 1051 return; 1052 1053 if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, 1054 resize->ctx->attr, NULL))) 1055 perr_exit("ntfs_decompress_mapping_pairs"); 1056 1057 for (i = 0; rl[i].length; i++) { 1058 /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ 1059 if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) 1060 continue; 1061 1062 collect_resize_constraints(resize, rl + i); 1063 if (resize->shrink) 1064 collect_relocation_info(resize, rl + i); 1065 } 1066 free(rl); 1067} 1068 1069static void resize_constraints_by_attributes(ntfs_resize_t *resize) 1070{ 1071 if (!(resize->ctx = attr_get_search_ctx(resize->ni, NULL))) 1072 exit(1); 1073 1074 while (!ntfs_attrs_walk(resize->ctx)) { 1075 if (resize->ctx->attr->type == AT_END) 1076 break; 1077 build_resize_constraints(resize); 1078 } 1079 1080 ntfs_attr_put_search_ctx(resize->ctx); 1081} 1082 1083static void set_resize_constraints(ntfs_resize_t *resize) 1084{ 1085 s64 nr_mft_records, inode; 1086 ntfs_inode *ni; 1087 1088 printf("Collecting resizing constraints ...\n"); 1089 1090 nr_mft_records = resize->vol->mft_na->initialized_size >> 1091 resize->vol->mft_record_size_bits; 1092 1093 for (inode = 0; inode < nr_mft_records; inode++) { 1094 1095 ni = ntfs_inode_open(resize->vol, (MFT_REF)inode); 1096 if (ni == NULL) { 1097 if (errno == EIO || errno == ENOENT) 1098 continue; 1099 perr_exit("Reading inode %lld failed", inode); 1100 } 1101 1102 if (ni->mrec->base_mft_record) 1103 goto close_inode; 1104 1105 resize->ni = ni; 1106 resize_constraints_by_attributes(resize); 1107close_inode: 1108 if (inode_close(ni) != 0) 1109 exit(1); 1110 } 1111} 1112 1113static void rl_fixup(runlist **rl) 1114{ 1115 runlist *tmp = *rl; 1116 1117 if (tmp->lcn == LCN_RL_NOT_MAPPED) { 1118 s64 unmapped_len = tmp->length; 1119 1120 ntfs_log_verbose("Skip unmapped run at the beginning ...\n"); 1121 1122 if (!tmp->length) 1123 err_exit("Empty unmapped runlist! Please report!\n"); 1124 (*rl)++; 1125 for (tmp = *rl; tmp->length; tmp++) 1126 tmp->vcn -= unmapped_len; 1127 } 1128 1129 for (tmp = *rl; tmp->length; tmp++) { 1130 if (tmp->lcn == LCN_RL_NOT_MAPPED) { 1131 ntfs_log_verbose("Skip unmapped run at the end ...\n"); 1132 1133 if (tmp[1].length) 1134 err_exit("Unmapped runlist in the middle! " 1135 "Please report!\n"); 1136 tmp->lcn = LCN_ENOENT; 1137 tmp->length = 0; 1138 } 1139 } 1140} 1141 1142static void replace_attribute_runlist(ntfs_volume *vol, 1143 ntfs_attr_search_ctx *ctx, 1144 runlist *rl) 1145{ 1146 int mp_size, l; 1147 void *mp; 1148 ATTR_RECORD *a = ctx->attr; 1149 1150 rl_fixup(&rl); 1151 1152 if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0)) == -1) 1153 perr_exit("ntfs_get_size_for_mapping_pairs"); 1154 1155 if (a->name_length) { 1156 u16 name_offs = le16_to_cpu(a->name_offset); 1157 u16 mp_offs = le16_to_cpu(a->u.nonres.mapping_pairs_offset); 1158 1159 if (name_offs >= mp_offs) 1160 err_exit("Attribute name is after mapping pairs! " 1161 "Please report!\n"); 1162 } 1163 1164 /* CHECKME: don't trust mapping_pairs is always the last item in the 1165 attribute, instead check for the real size/space */ 1166 l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->u.nonres.mapping_pairs_offset); 1167 if (mp_size > l) { 1168 s64 remains_size; 1169 char *next_attr; 1170 1171 ntfs_log_verbose("Enlarging attribute header ...\n"); 1172 1173 mp_size = (mp_size + 7) & ~7; 1174 1175 ntfs_log_verbose("Old mp size : %d\n", l); 1176 ntfs_log_verbose("New mp size : %d\n", mp_size); 1177 ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) 1178 le32_to_cpu(ctx->mrec->bytes_in_use)); 1179 1180 next_attr = (char *)a + le32_to_cpu(a->length); 1181 l = mp_size - l; 1182 1183 ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) 1184 le32_to_cpu(ctx->mrec->bytes_in_use)); 1185 ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int) 1186 le32_to_cpu(ctx->mrec->bytes_allocated)); 1187 1188 remains_size = le32_to_cpu(ctx->mrec->bytes_in_use); 1189 remains_size -= (next_attr - (char *)ctx->mrec); 1190 1191 ntfs_log_verbose("increase : %d\n", l); 1192 ntfs_log_verbose("shift : %lld\n", 1193 (long long)remains_size); 1194 1195 if (le32_to_cpu(ctx->mrec->bytes_in_use) + l > 1196 le32_to_cpu(ctx->mrec->bytes_allocated)) 1197 err_exit("Extended record needed (%u > %u), not yet " 1198 "supported!\nPlease try to free less space.\n", 1199 (unsigned int)le32_to_cpu(ctx->mrec-> 1200 bytes_in_use) + l, 1201 (unsigned int)le32_to_cpu(ctx->mrec-> 1202 bytes_allocated)); 1203 1204 memmove(next_attr + l, next_attr, remains_size); 1205 ctx->mrec->bytes_in_use = cpu_to_le32(l + 1206 le32_to_cpu(ctx->mrec->bytes_in_use)); 1207 a->length = cpu_to_le32(le32_to_cpu(a->length) + l); 1208 } 1209 1210 mp = ntfs_calloc(mp_size); 1211 if (!mp) 1212 perr_exit("ntfsc_calloc couldn't get memory"); 1213 1214 if (ntfs_mapping_pairs_build(vol, mp, mp_size, rl, 0, NULL)) 1215 perr_exit("ntfs_mapping_pairs_build"); 1216 1217 memmove((u8*)a + le16_to_cpu(a->u.nonres.mapping_pairs_offset), mp, mp_size); 1218 1219 free(mp); 1220} 1221 1222static void set_bitmap_range(struct bitmap *bm, s64 pos, s64 length, u8 bit) 1223{ 1224 while (length--) 1225 ntfs_bit_set(bm->bm, pos++, bit); 1226} 1227 1228static void set_bitmap_clusters(struct bitmap *bm, runlist *rl, u8 bit) 1229{ 1230 for (; rl->length; rl++) 1231 set_bitmap_range(bm, rl->lcn, rl->length, bit); 1232} 1233 1234static void release_bitmap_clusters(struct bitmap *bm, runlist *rl) 1235{ 1236 max_free_cluster_range = 0; 1237 set_bitmap_clusters(bm, rl, 0); 1238} 1239 1240static void set_max_free_zone(s64 length, s64 end, runlist_element *rle) 1241{ 1242 if (length > rle->length) { 1243 rle->lcn = end - length; 1244 rle->length = length; 1245 } 1246} 1247 1248static int find_free_cluster(struct bitmap *bm, 1249 runlist_element *rle, 1250 s64 nr_vol_clusters, 1251 int hint) 1252{ 1253 /* FIXME: get rid of this 'static' variable */ 1254 static s64 pos = 0; 1255 s64 i, items = rle->length; 1256 s64 free_zone = 0; 1257 1258 if (pos >= nr_vol_clusters) 1259 pos = 0; 1260 if (!max_free_cluster_range) 1261 max_free_cluster_range = nr_vol_clusters; 1262 rle->lcn = rle->length = 0; 1263 if (hint) 1264 pos = nr_vol_clusters / 2; 1265 i = pos; 1266 1267 do { 1268 if (!ntfs_bit_get(bm->bm, i)) { 1269 if (++free_zone == items) { 1270 set_max_free_zone(free_zone, i + 1, rle); 1271 break; 1272 } 1273 } else { 1274 set_max_free_zone(free_zone, i, rle); 1275 free_zone = 0; 1276 } 1277 if (++i == nr_vol_clusters) { 1278 set_max_free_zone(free_zone, i, rle); 1279 i = free_zone = 0; 1280 } 1281 if (rle->length == max_free_cluster_range) 1282 break; 1283 } while (i != pos); 1284 1285 if (i) 1286 set_max_free_zone(free_zone, i, rle); 1287 1288 if (!rle->lcn) { 1289 errno = ENOSPC; 1290 return -1; 1291 } 1292 if (rle->length < items && rle->length < max_free_cluster_range) { 1293 max_free_cluster_range = rle->length; 1294 ntfs_log_verbose("Max free range: %7lld \n", 1295 (long long)max_free_cluster_range); 1296 } 1297 pos = rle->lcn + items; 1298 if (pos == nr_vol_clusters) 1299 pos = 0; 1300 1301 set_bitmap_range(bm, rle->lcn, rle->length, 1); 1302 return 0; 1303} 1304 1305static runlist *alloc_cluster(struct bitmap *bm, 1306 s64 items, 1307 s64 nr_vol_clusters, 1308 int hint) 1309{ 1310 runlist_element rle; 1311 runlist *rl = NULL; 1312 int rl_size, runs = 0; 1313 s64 vcn = 0; 1314 1315 if (items <= 0) { 1316 errno = EINVAL; 1317 return NULL; 1318 } 1319 1320 while (items > 0) { 1321 1322 if (runs) 1323 hint = 0; 1324 rle.length = items; 1325 if (find_free_cluster(bm, &rle, nr_vol_clusters, hint) == -1) 1326 return NULL; 1327 1328 rl_size = (runs + 2) * sizeof(runlist_element); 1329 if (!(rl = (runlist *)realloc(rl, rl_size))) 1330 return NULL; 1331 1332 rl_set(rl + runs, vcn, rle.lcn, rle.length); 1333 1334 vcn += rle.length; 1335 items -= rle.length; 1336 runs++; 1337 } 1338 1339 rl_set(rl + runs, vcn, -1LL, 0LL); 1340 1341 if (runs > 1) { 1342 ntfs_log_verbose("Multi-run allocation: \n"); 1343 dump_runlist(rl); 1344 } 1345 return rl; 1346} 1347 1348static int read_all(struct ntfs_device *dev, void *buf, int count) 1349{ 1350 int i; 1351 1352 while (count > 0) { 1353 1354 i = count; 1355 if (!NDevReadOnly(dev)) 1356 i = dev->d_ops->read(dev, buf, count); 1357 1358 if (i < 0) { 1359 if (errno != EAGAIN && errno != EINTR) 1360 return -1; 1361 } else if (i > 0) { 1362 count -= i; 1363 buf = i + (char *)buf; 1364 } else 1365 err_exit("Unexpected end of file!\n"); 1366 } 1367 return 0; 1368} 1369 1370static int write_all(struct ntfs_device *dev, void *buf, int count) 1371{ 1372 int i; 1373 1374 while (count > 0) { 1375 1376 i = count; 1377 if (!NDevReadOnly(dev)) 1378 i = dev->d_ops->write(dev, buf, count); 1379 1380 if (i < 0) { 1381 if (errno != EAGAIN && errno != EINTR) 1382 return -1; 1383 } else { 1384 count -= i; 1385 buf = i + (char *)buf; 1386 } 1387 } 1388 return 0; 1389} 1390 1391/** 1392 * write_mft_record 1393 * 1394 * Write an MFT Record back to the disk. If the read-only command line option 1395 * was given, this function will do nothing. 1396 */ 1397static int write_mft_record(ntfs_volume *v, const MFT_REF mref, MFT_RECORD *buf) 1398{ 1399 if (ntfs_mft_record_write(v, mref, buf)) 1400 perr_exit("ntfs_mft_record_write"); 1401 1402// if (v->u.dev->d_ops->sync(v->u.dev) == -1) 1403// perr_exit("Failed to sync device"); 1404 1405 return 0; 1406} 1407 1408static void lseek_to_cluster(ntfs_volume *vol, s64 lcn) 1409{ 1410 off_t pos; 1411 pos = (off_t)(lcn * vol->cluster_size); 1412 if (vol->u.dev->d_ops->seek(vol->u.dev, pos, SEEK_SET) == (off_t)-1) 1413 perr_exit("seek failed to position %lld", lcn); 1414} 1415 1416static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len) 1417{ 1418 s64 i; 1419 char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ 1420 ntfs_volume *vol = resize->vol; 1421 1422 for (i = 0; i < len; i++) { 1423 1424 lseek_to_cluster(vol, src + i); 1425 1426 if (read_all(vol->u.dev, buff, vol->cluster_size) == -1) { 1427 perr_printf("Failed to read from the disk"); 1428 if (errno == EIO) 1429 printf("%s", bad_sectors_warning_msg); 1430 exit(1); 1431 } 1432 1433 lseek_to_cluster(vol, dest + i); 1434 1435 if (write_all(vol->u.dev, buff, vol->cluster_size) == -1) { 1436 perr_printf("Failed to write to the disk"); 1437 if (errno == EIO) 1438 printf("%s", bad_sectors_warning_msg); 1439 exit(1); 1440 } 1441 1442 resize->relocations++; 1443 progress_update(&resize->progress, resize->relocations); 1444 } 1445} 1446 1447static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn) 1448{ 1449 /* collect_shrink_constraints() ensured $MFTMir DATA is one run */ 1450 if (r->mref == FILE_MFTMirr && r->ctx->attr->type == AT_DATA) { 1451 if (!r->mftmir_old) { 1452 r->mftmir_rl.lcn = dest_rl->lcn; 1453 r->mftmir_rl.length = dest_rl->length; 1454 r->mftmir_old = src_lcn; 1455 } else 1456 err_exit("Multi-run $MFTMirr. Please report!\n"); 1457 } 1458 1459 for (; dest_rl->length; src_lcn += dest_rl->length, dest_rl++) 1460 copy_clusters(r, dest_rl->lcn, src_lcn, dest_rl->length); 1461} 1462 1463static void rl_split_run(runlist **rl, int run, s64 pos) 1464{ 1465 runlist *rl_new, *rle_new, *rle; 1466 int items, new_size, size_head, size_tail; 1467 s64 len_head, len_tail; 1468 1469 items = rl_items(*rl); 1470 new_size = (items + 1) * sizeof(runlist_element); 1471 size_head = run * sizeof(runlist_element); 1472 size_tail = (items - run - 1) * sizeof(runlist_element); 1473 1474 rl_new = ntfs_malloc(new_size); 1475 if (!rl_new) 1476 perr_exit("ntfs_malloc"); 1477 1478 rle_new = rl_new + run; 1479 rle = *rl + run; 1480 1481 memmove(rl_new, *rl, size_head); 1482 memmove(rle_new + 2, rle + 1, size_tail); 1483 1484 len_tail = rle->length - (pos - rle->lcn); 1485 len_head = rle->length - len_tail; 1486 1487 rl_set(rle_new, rle->vcn, rle->lcn, len_head); 1488 rl_set(rle_new + 1, rle->vcn + len_head, rle->lcn + len_head, len_tail); 1489 1490 ntfs_log_verbose("Splitting run at cluster %lld:\n", (long long)pos); 1491 dump_run(rle); dump_run(rle_new); dump_run(rle_new + 1); 1492 1493 free(*rl); 1494 *rl = rl_new; 1495} 1496 1497static void rl_insert_at_run(runlist **rl, int run, runlist *ins) 1498{ 1499 int items, ins_items; 1500 int new_size, size_tail; 1501 runlist *rle; 1502 s64 vcn; 1503 1504 items = rl_items(*rl); 1505 ins_items = rl_items(ins) - 1; 1506 new_size = ((items - 1) + ins_items) * sizeof(runlist_element); 1507 size_tail = (items - run - 1) * sizeof(runlist_element); 1508 1509 if (!(*rl = (runlist *)realloc(*rl, new_size))) 1510 perr_exit("realloc"); 1511 1512 rle = *rl + run; 1513 1514 memmove(rle + ins_items, rle + 1, size_tail); 1515 1516 for (vcn = rle->vcn; ins->length; rle++, vcn += ins->length, ins++) { 1517 rl_set(rle, vcn, ins->lcn, ins->length); 1518// dump_run(rle); 1519 } 1520 1521 return; 1522 1523 /* FIXME: fast path if ins_items = 1 */ 1524// (*rl + run)->lcn = ins->lcn; 1525} 1526 1527static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run) 1528{ 1529 s64 lcn, lcn_length; 1530 s64 new_vol_size; /* (last LCN on the volume) + 1 */ 1531 runlist *relocate_rl; /* relocate runlist to relocate_rl */ 1532 int hint; 1533 1534 lcn = (*rl + run)->lcn; 1535 lcn_length = (*rl + run)->length; 1536 new_vol_size = resize->new_volume_size; 1537 1538 if (lcn + lcn_length <= new_vol_size) 1539 return; 1540 1541 if (lcn < new_vol_size) { 1542 rl_split_run(rl, run, new_vol_size); 1543 return; 1544 } 1545 1546 hint = (resize->mref == FILE_MFTMirr) ? 1 : 0; 1547 if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap, lcn_length, 1548 new_vol_size, hint))) 1549 perr_exit("Cluster allocation failed for %llu:%lld", 1550 resize->mref, lcn_length); 1551 1552 /* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */ 1553 ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx " 1554 "--> 0x%08llx\n", (unsigned long long)resize->mref, 1555 (unsigned int)le32_to_cpu(resize->ctx->attr->type), 1556 (long long)lcn_length, 1557 (unsigned long long)(*rl + run)->vcn, 1558 (unsigned long long)lcn, 1559 (unsigned long long)relocate_rl->lcn); 1560 1561 relocate_clusters(resize, relocate_rl, lcn); 1562 rl_insert_at_run(rl, run, relocate_rl); 1563 1564 /* We don't release old clusters in the bitmap, that area isn't 1565 used by the allocator and will be truncated later on */ 1566 free(relocate_rl); 1567 1568 resize->dirty_inode = DIRTY_ATTRIB; 1569} 1570 1571static void relocate_attribute(ntfs_resize_t *resize) 1572{ 1573 ATTR_RECORD *a; 1574 runlist *rl; 1575 int i; 1576 1577 a = resize->ctx->attr; 1578 1579 if (!a->non_resident) 1580 return; 1581 1582 if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, a, NULL))) 1583 perr_exit("ntfs_decompress_mapping_pairs"); 1584 1585 for (i = 0; rl[i].length; i++) { 1586 s64 lcn = rl[i].lcn; 1587 s64 lcn_length = rl[i].length; 1588 1589 if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) 1590 continue; 1591 1592 /* FIXME: ntfs_mapping_pairs_decompress should return error */ 1593 if (lcn < 0 || lcn_length <= 0) 1594 err_exit("Corrupt runlist in MTF %llu attr %x LCN " 1595 "%llx length %llx\n", resize->mref, 1596 (unsigned int)le32_to_cpu(a->type), 1597 lcn, lcn_length); 1598 1599 relocate_run(resize, &rl, i); 1600 } 1601 1602 if (resize->dirty_inode == DIRTY_ATTRIB) { 1603 replace_attribute_runlist(resize->vol, resize->ctx, rl); 1604 resize->dirty_inode = DIRTY_INODE; 1605 } 1606 1607 free(rl); 1608} 1609 1610static int is_mftdata(ntfs_resize_t *resize) 1611{ 1612 if (resize->ctx->attr->type != AT_DATA) 1613 return 0; 1614 1615 if (resize->mref == 0) 1616 return 1; 1617 1618 if (MREF_LE(resize->mrec->base_mft_record) == 0 && 1619 MSEQNO_LE(resize->mrec->base_mft_record) != 0) 1620 return 1; 1621 1622 return 0; 1623} 1624 1625static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata) 1626{ 1627 ATTR_RECORD *attr = resize->ctx->attr; 1628 VCN highest_vcn, lowest_vcn; 1629 1630 if (do_mftdata) { 1631 1632 if (!is_mftdata(resize)) 1633 return 0; 1634 1635 highest_vcn = sle64_to_cpu(attr->u.nonres.highest_vcn); 1636 lowest_vcn = sle64_to_cpu(attr->u.nonres.lowest_vcn); 1637 1638 if (resize->mft_highest_vcn != highest_vcn) 1639 return 0; 1640 1641 if (lowest_vcn == 0) 1642 resize->mft_highest_vcn = lowest_vcn; 1643 else 1644 resize->mft_highest_vcn = lowest_vcn - 1; 1645 1646 } else if (is_mftdata(resize)) { 1647 1648 highest_vcn = sle64_to_cpu(attr->u.nonres.highest_vcn); 1649 1650 if (resize->mft_highest_vcn < highest_vcn) 1651 resize->mft_highest_vcn = highest_vcn; 1652 1653 return 0; 1654 } 1655 1656 return 1; 1657} 1658 1659static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata) 1660{ 1661 int ret; 1662 1663 if (!(resize->ctx = attr_get_search_ctx(NULL, resize->mrec))) 1664 exit(1); 1665 1666 while (!ntfs_attrs_walk(resize->ctx)) { 1667 if (resize->ctx->attr->type == AT_END) 1668 break; 1669 1670 if (handle_mftdata(resize, do_mftdata) == 0) 1671 continue; 1672 1673 ret = ntfs_inode_badclus_bad(resize->mref, resize->ctx->attr); 1674 if (ret == -1) 1675 perr_exit("Bad sector list check failed"); 1676 else if (ret == 1) 1677 continue; 1678 1679 if (resize->mref == FILE_Bitmap && 1680 resize->ctx->attr->type == AT_DATA) 1681 continue; 1682 1683 relocate_attribute(resize); 1684 } 1685 1686 ntfs_attr_put_search_ctx(resize->ctx); 1687} 1688 1689static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata) 1690{ 1691 if (ntfs_file_record_read(resize->vol, mref, &resize->mrec, NULL)) { 1692 /* FIXME: continue only if it make sense, e.g. 1693 MFT record not in use based on $MFT bitmap */ 1694 if (errno == EIO || errno == ENOENT) 1695 return; 1696 perr_exit("ntfs_file_record_record"); 1697 } 1698 1699 if (!(resize->mrec->flags & MFT_RECORD_IN_USE)) 1700 return; 1701 1702 resize->mref = mref; 1703 resize->dirty_inode = DIRTY_NONE; 1704 1705 relocate_attributes(resize, do_mftdata); 1706 1707 if (resize->dirty_inode == DIRTY_INODE) { 1708// if (vol->u.dev->d_ops->sync(vol->u.dev) == -1) 1709// perr_exit("Failed to sync device"); 1710 if (write_mft_record(resize->vol, mref, resize->mrec)) 1711 perr_exit("Couldn't update record %llu", mref); 1712 } 1713} 1714 1715static void relocate_inodes(ntfs_resize_t *resize) 1716{ 1717 s64 nr_mft_records; 1718 MFT_REF mref; 1719 VCN highest_vcn; 1720 1721 printf("Relocating needed data ...\n"); 1722 1723 progress_init(&resize->progress, 0, resize->relocations, resize->progress.flags); 1724 resize->relocations = 0; 1725 1726 resize->mrec = ntfs_malloc(resize->vol->mft_record_size); 1727 if (!resize->mrec) 1728 perr_exit("ntfs_malloc failed"); 1729 1730 nr_mft_records = resize->vol->mft_na->initialized_size >> 1731 resize->vol->mft_record_size_bits; 1732 1733 for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++) 1734 relocate_inode(resize, mref, 0); 1735 1736 while (1) { 1737 highest_vcn = resize->mft_highest_vcn; 1738 mref = nr_mft_records; 1739 do { 1740 relocate_inode(resize, --mref, 1); 1741 if (resize->mft_highest_vcn == 0) 1742 goto done; 1743 } while (mref); 1744 1745 if (highest_vcn == resize->mft_highest_vcn) 1746 err_exit("Sanity check failed! Highest_vcn = %lld. " 1747 "Please report!\n", highest_vcn); 1748 } 1749done: 1750 free(resize->mrec); 1751} 1752 1753static void print_hint(ntfs_volume *vol, const char *s, struct llcn_t llcn) 1754{ 1755 s64 runs_b, runs_mb; 1756 1757 if (llcn.lcn == 0) 1758 return; 1759 1760 runs_b = llcn.lcn * vol->cluster_size; 1761 runs_mb = rounded_up_division(runs_b, NTFS_MBYTE); 1762 printf("%-19s: %9lld MB %8lld\n", s, (long long)runs_mb, 1763 (long long)llcn.inode); 1764} 1765 1766/** 1767 * advise_on_resize 1768 * 1769 * The metadata file $Bitmap has one bit for each cluster on disk. This has 1770 * already been read into lcn_bitmap. By looking for the last used cluster on 1771 * the disk, we can work out by how much we can shrink the volume. 1772 */ 1773static void advise_on_resize(ntfs_resize_t *resize) 1774{ 1775 ntfs_volume *vol = resize->vol; 1776 1777 if (opt.verbose) { 1778 printf("Estimating smallest shrunken size supported ...\n"); 1779 printf("File feature Last used at By inode\n"); 1780 print_hint(vol, "$MFT", resize->last_mft); 1781 print_hint(vol, "Multi-Record", resize->last_multi_mft); 1782 print_hint(vol, "$MFTMirr", resize->last_mftmir); 1783 print_hint(vol, "Compressed", resize->last_compressed); 1784 print_hint(vol, "Sparse", resize->last_sparse); 1785 print_hint(vol, "Ordinary", resize->last_lcn); 1786 } 1787 1788 print_advise(vol, resize->last_unsupp); 1789} 1790 1791 1792static void rl_expand(runlist **rl, const VCN last_vcn) 1793{ 1794 int len; 1795 runlist *p = *rl; 1796 1797 len = rl_items(p) - 1; 1798 if (len <= 0) 1799 err_exit("rl_expand: bad runlist length: %d\n", len); 1800 1801 if (p[len].vcn > last_vcn) 1802 err_exit("rl_expand: length is already more than requested " 1803 "(%lld > %lld)\n", p[len].vcn, last_vcn); 1804 1805 if (p[len - 1].lcn == LCN_HOLE) { 1806 1807 p[len - 1].length += last_vcn - p[len].vcn; 1808 p[len].vcn = last_vcn; 1809 1810 } else if (p[len - 1].lcn >= 0) { 1811 1812 p = realloc(*rl, (++len + 1) * sizeof(runlist_element)); 1813 if (!p) 1814 perr_exit("rl_expand: realloc"); 1815 1816 p[len - 1].lcn = LCN_HOLE; 1817 p[len - 1].length = last_vcn - p[len - 1].vcn; 1818 rl_set(p + len, last_vcn, LCN_ENOENT, 0LL); 1819 *rl = p; 1820 1821 } else 1822 err_exit("rl_expand: bad LCN: %lld\n", p[len - 1].lcn); 1823} 1824 1825static void rl_truncate(runlist **rl, const VCN last_vcn) 1826{ 1827 int len; 1828 VCN vcn; 1829 1830 len = rl_items(*rl) - 1; 1831 if (len <= 0) 1832 err_exit("rl_truncate: bad runlist length: %d\n", len); 1833 1834 vcn = (*rl)[len].vcn; 1835 1836 if (vcn < last_vcn) 1837 rl_expand(rl, last_vcn); 1838 1839 else if (vcn > last_vcn) 1840 if (ntfs_rl_truncate(rl, last_vcn) == -1) 1841 perr_exit("ntfs_rl_truncate"); 1842} 1843 1844/** 1845 * bitmap_file_data_fixup 1846 * 1847 * $Bitmap can overlap the end of the volume. Any bits in this region 1848 * must be set. This region also encompasses the backup boot sector. 1849 */ 1850static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) 1851{ 1852 for (; cluster < bm->size << 3; cluster++) 1853 ntfs_bit_set(bm->bm, (u64)cluster, 1); 1854} 1855 1856/** 1857 * truncate_badclust_bad_attr 1858 * 1859 * The metadata file $BadClus needs to be shrunk. 1860 * 1861 * FIXME: this function should go away and instead using a generalized 1862 * "truncate_bitmap_data_attr()" 1863 */ 1864static void truncate_badclust_bad_attr(ntfs_resize_t *resize) 1865{ 1866 ATTR_RECORD *a; 1867 runlist *rl_bad; 1868 s64 nr_clusters = resize->new_volume_size; 1869 ntfs_volume *vol = resize->vol; 1870 1871 a = resize->ctx->attr; 1872 if (!a->non_resident) 1873 /* FIXME: handle resident attribute value */ 1874 err_exit("Resident attribute in $BadClust isn't supported!\n"); 1875 1876 if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, a, NULL))) 1877 perr_exit("ntfs_mapping_pairs_decompress"); 1878 1879 rl_truncate(&rl_bad, nr_clusters); 1880 1881 a->u.nonres.highest_vcn = cpu_to_sle64(nr_clusters - 1LL); 1882 a->u.nonres.allocated_size = cpu_to_sle64(nr_clusters * vol->cluster_size); 1883 a->u.nonres.data_size = cpu_to_sle64(nr_clusters * vol->cluster_size); 1884 1885 replace_attribute_runlist(vol, resize->ctx, rl_bad); 1886 1887 free(rl_bad); 1888} 1889 1890/** 1891 * realloc_bitmap_data_attr 1892 * 1893 * Reallocate the metadata file $Bitmap. It must be large enough for one bit 1894 * per cluster of the shrunken volume. Also it must be a of 8 bytes in size. 1895 */ 1896static void realloc_bitmap_data_attr(ntfs_resize_t *resize, 1897 runlist **rl, 1898 s64 nr_bm_clusters) 1899{ 1900 s64 i; 1901 ntfs_volume *vol = resize->vol; 1902 ATTR_RECORD *a = resize->ctx->attr; 1903 s64 new_size = resize->new_volume_size; 1904 struct bitmap *bm = &resize->lcn_bitmap; 1905 1906 if (!(*rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) 1907 perr_exit("ntfs_mapping_pairs_decompress"); 1908 1909 release_bitmap_clusters(bm, *rl); 1910 free(*rl); 1911 1912 for (i = vol->nr_clusters; i < new_size; i++) 1913 ntfs_bit_set(bm->bm, i, 0); 1914 1915 if (!(*rl = alloc_cluster(bm, nr_bm_clusters, new_size, 0))) 1916 perr_exit("Couldn't allocate $Bitmap clusters"); 1917} 1918 1919static void realloc_lcn_bitmap(ntfs_resize_t *resize, s64 bm_bsize) 1920{ 1921 u8 *tmp; 1922 1923 if (!(tmp = realloc(resize->lcn_bitmap.bm, bm_bsize))) 1924 perr_exit("realloc"); 1925 1926 resize->lcn_bitmap.bm = tmp; 1927 resize->lcn_bitmap.size = bm_bsize; 1928 bitmap_file_data_fixup(resize->new_volume_size, &resize->lcn_bitmap); 1929} 1930 1931/** 1932 * truncate_bitmap_data_attr 1933 */ 1934static void truncate_bitmap_data_attr(ntfs_resize_t *resize) 1935{ 1936 ATTR_RECORD *a; 1937 runlist *rl; 1938 s64 bm_bsize, size; 1939 s64 nr_bm_clusters; 1940 ntfs_volume *vol = resize->vol; 1941 1942 a = resize->ctx->attr; 1943 if (!a->non_resident) 1944 /* FIXME: handle resident attribute value */ 1945 err_exit("Resident attribute in $Bitmap isn't supported!\n"); 1946 1947 bm_bsize = nr_clusters_to_bitmap_byte_size(resize->new_volume_size); 1948 nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size); 1949 1950 if (resize->shrink) { 1951 realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); 1952 realloc_lcn_bitmap(resize, bm_bsize); 1953 } else { 1954 realloc_lcn_bitmap(resize, bm_bsize); 1955 realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); 1956 } 1957 1958 a->u.nonres.highest_vcn = cpu_to_sle64(nr_bm_clusters - 1LL); 1959 a->u.nonres.allocated_size = cpu_to_sle64(nr_bm_clusters * vol->cluster_size); 1960 a->u.nonres.data_size = cpu_to_sle64(bm_bsize); 1961 a->u.nonres.initialized_size = cpu_to_sle64(bm_bsize); 1962 1963 replace_attribute_runlist(vol, resize->ctx, rl); 1964 1965 /* 1966 * FIXME: update allocated/data sizes and timestamps in $FILE_NAME 1967 * attribute too, for now chkdsk will do this for us. 1968 */ 1969 1970 size = ntfs_rl_pwrite(vol, rl, 0, bm_bsize, resize->lcn_bitmap.bm); 1971 if (bm_bsize != size) { 1972 if (size == -1) 1973 perr_exit("Couldn't write $Bitmap"); 1974 err_exit("Couldn't write full $Bitmap file (%lld from %lld)\n", 1975 (long long)size, (long long)bm_bsize); 1976 } 1977 1978 free(rl); 1979} 1980 1981/** 1982 * lookup_data_attr 1983 * 1984 * Find the $DATA attribute (with or without a name) for the given MFT reference 1985 * (inode number). 1986 */ 1987static void lookup_data_attr(ntfs_volume *vol, 1988 MFT_REF mref, 1989 const char *aname, 1990 ntfs_attr_search_ctx **ctx) 1991{ 1992 ntfs_inode *ni; 1993 ntfschar *ustr; 1994 int len = 0; 1995 1996 if (!(ni = ntfs_inode_open(vol, mref))) 1997 perr_exit("ntfs_open_inode"); 1998 1999 if (!(*ctx = attr_get_search_ctx(ni, NULL))) 2000 exit(1); 2001 2002 if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) { 2003 perr_printf("Couldn't convert '%s' to Unicode", aname); 2004 exit(1); 2005 } 2006 2007 if (ntfs_attr_lookup(AT_DATA, ustr, len, 0, 0, NULL, 0, *ctx)) 2008 perr_exit("ntfs_lookup_attr"); 2009 2010 ntfs_ucsfree(ustr); 2011} 2012 2013static int check_bad_sectors(ntfs_volume *vol) 2014{ 2015 ntfs_attr_search_ctx *ctx; 2016 ntfs_inode *base_ni; 2017 runlist *rl; 2018 s64 i, badclusters = 0; 2019 2020 ntfs_log_verbose("Checking for bad sectors ...\n"); 2021 2022 lookup_data_attr(vol, FILE_BadClus, "$Bad", &ctx); 2023 2024 base_ni = ctx->base_ntfs_ino; 2025 if (!base_ni) 2026 base_ni = ctx->ntfs_ino; 2027 2028 if (NInoAttrList(base_ni)) { 2029 err_printf("Hopelessly many bad sectors has been detected!\n"); 2030 printf("%s", many_bad_sectors_msg); 2031 exit(1); 2032 } 2033 2034 if (!ctx->attr->non_resident) 2035 err_exit("Resident attribute in $BadClust! Please report to " 2036 "%s\n", NTFS_DEV_LIST); 2037 /* 2038 * FIXME: The below would be partial for non-base records in the 2039 * not yet supported multi-record case. Alternatively use audited 2040 * ntfs_attr_truncate after an umount & mount. 2041 */ 2042 if (!(rl = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL))) 2043 perr_exit("Decompressing $BadClust:$Bad mapping pairs failed"); 2044 2045 for (i = 0; rl[i].length; i++) { 2046 /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ 2047 if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) 2048 continue; 2049 2050 badclusters += rl[i].length; 2051 ntfs_log_verbose("Bad cluster: %#8llx - %#llx (%lld)\n", 2052 rl[i].lcn, rl[i].lcn + rl[i].length - 1, 2053 rl[i].length); 2054 } 2055 2056 if (badclusters) { 2057 printf("%sThis software has detected that the disk has at least" 2058 " %lld bad sector%s.\n", 2059 !opt.badsectors ? NERR_PREFIX : "WARNING: ", 2060 badclusters, badclusters - 1 ? "s" : ""); 2061 if (!opt.badsectors) { 2062 printf("%s", bad_sectors_warning_msg); 2063 exit(1); 2064 } else 2065 printf("WARNING: Bad sectors can cause reliability " 2066 "problems and massive data loss!!!\n"); 2067 } 2068 2069 free(rl); 2070 ntfs_attr_put_search_ctx(ctx); 2071 2072 return badclusters; 2073} 2074 2075/** 2076 * truncate_badclust_file 2077 * 2078 * Shrink the $BadClus file to match the new volume size. 2079 */ 2080static void truncate_badclust_file(ntfs_resize_t *resize) 2081{ 2082 printf("Updating $BadClust file ...\n"); 2083 2084 lookup_data_attr(resize->vol, FILE_BadClus, "$Bad", &resize->ctx); 2085 /* FIXME: sanity_check_attr(ctx->attr); */ 2086 truncate_badclust_bad_attr(resize); 2087 2088 if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no, 2089 resize->ctx->mrec)) 2090 perr_exit("Couldn't update $BadClust"); 2091 2092 ntfs_attr_put_search_ctx(resize->ctx); 2093} 2094 2095/** 2096 * truncate_bitmap_file 2097 * 2098 * Shrink the $Bitmap file to match the new volume size. 2099 */ 2100static void truncate_bitmap_file(ntfs_resize_t *resize) 2101{ 2102 printf("Updating $Bitmap file ...\n"); 2103 2104 lookup_data_attr(resize->vol, FILE_Bitmap, NULL, &resize->ctx); 2105 truncate_bitmap_data_attr(resize); 2106 2107 if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no, 2108 resize->ctx->mrec)) 2109 perr_exit("Couldn't update $Bitmap"); 2110 2111 ntfs_attr_put_search_ctx(resize->ctx); 2112} 2113 2114/** 2115 * setup_lcn_bitmap 2116 * 2117 * Allocate a block of memory with one bit for each cluster of the disk. 2118 * All the bits are set to 0, except those representing the region beyond the 2119 * end of the disk. 2120 */ 2121static int setup_lcn_bitmap(struct bitmap *bm, s64 nr_clusters) 2122{ 2123 /* Determine lcn bitmap byte size and allocate it. */ 2124 bm->size = rounded_up_division(nr_clusters, 8); 2125 2126 bm->bm = ntfs_calloc(bm->size); 2127 if (!bm->bm) 2128 return -1; 2129 2130 bitmap_file_data_fixup(nr_clusters, bm); 2131 return 0; 2132} 2133 2134/** 2135 * update_bootsector 2136 * 2137 * FIXME: should be done using ntfs_* functions 2138 */ 2139static void update_bootsector(ntfs_resize_t *r) 2140{ 2141 NTFS_BOOT_SECTOR bs; 2142 s64 bs_size = sizeof(NTFS_BOOT_SECTOR); 2143 ntfs_volume *vol = r->vol; 2144 2145 printf("Updating Boot record ...\n"); 2146 2147 if (vol->u.dev->d_ops->seek(vol->u.dev, 0, SEEK_SET) == (off_t)-1) 2148 perr_exit("lseek"); 2149 2150 if (vol->u.dev->d_ops->read(vol->u.dev, &bs, bs_size) == -1) 2151 perr_exit("read() error"); 2152 2153 bs.number_of_sectors = cpu_to_sle64(r->new_volume_size * 2154 bs.bpb.sectors_per_cluster); 2155 2156 if (r->mftmir_old) { 2157 r->progress.flags |= NTFS_PROGBAR_SUPPRESS; 2158 copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old, 2159 r->mftmir_rl.length); 2160 bs.mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn); 2161 r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS; 2162 } 2163 2164 if (vol->u.dev->d_ops->seek(vol->u.dev, 0, SEEK_SET) == (off_t)-1) 2165 perr_exit("lseek"); 2166 2167 if (!opt.ro_flag) 2168 if (vol->u.dev->d_ops->write(vol->u.dev, &bs, bs_size) == -1) 2169 perr_exit("write() error"); 2170} 2171 2172/** 2173 * vol_size 2174 */ 2175static s64 vol_size(ntfs_volume *v, s64 nr_clusters) 2176{ 2177 /* add one sector_size for the backup boot sector */ 2178 return nr_clusters * v->cluster_size + v->sector_size; 2179} 2180 2181/** 2182 * print_vol_size 2183 * 2184 * Print the volume size in bytes and decimal megabytes. 2185 */ 2186static void print_vol_size(const char *str, s64 bytes) 2187{ 2188 printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, 2189 (long long)rounded_up_division(bytes, NTFS_MBYTE)); 2190} 2191 2192/** 2193 * print_disk_usage 2194 * 2195 * Display the amount of disk space in use. 2196 */ 2197static void print_disk_usage(ntfs_volume *vol, s64 nr_used_clusters) 2198{ 2199 s64 total, used; 2200 2201 total = vol->nr_clusters * vol->cluster_size; 2202 used = nr_used_clusters * vol->cluster_size; 2203 2204 /* WARNING: don't modify the text, external tools grep for it */ 2205 printf("Space in use : %lld MB (%.1f%%)\n", 2206 (long long)rounded_up_division(used, NTFS_MBYTE), 2207 100.0 * ((float)used / total)); 2208} 2209 2210static void print_num_of_relocations(ntfs_resize_t *resize) 2211{ 2212 s64 relocations = resize->relocations * resize->vol->cluster_size; 2213 2214 printf("Needed relocations : %lld (%lld MB)\n", 2215 (long long)resize->relocations, (long long) 2216 rounded_up_division(relocations, NTFS_MBYTE)); 2217} 2218 2219/** 2220 * mount_volume 2221 * 2222 * First perform some checks to determine if the volume is already mounted, or 2223 * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount 2224 * the volume (load the metadata into memory). 2225 */ 2226static ntfs_volume *mount_volume(void) 2227{ 2228 unsigned long mntflag; 2229 ntfs_volume *vol = NULL; 2230 2231 if (ntfs_check_if_mounted(opt.volume, &mntflag)) { 2232 perr_printf("Failed to check '%s' mount state", opt.volume); 2233 printf("Probably /etc/mtab is missing. It's too risky to " 2234 "continue. You might try\nan another Linux distro.\n"); 2235 exit(1); 2236 } 2237 if (mntflag & NTFS_MF_MOUNTED) { 2238 if (!(mntflag & NTFS_MF_READONLY)) 2239 err_exit("Device '%s' is mounted read-write. " 2240 "You must 'umount' it first.\n", opt.volume); 2241 if (!opt.ro_flag) 2242 err_exit("Device '%s' is mounted. " 2243 "You must 'umount' it first.\n", opt.volume); 2244 } 2245 /* 2246 * Pass NTFS_MNT_FORENSIC so that the mount process does not modify the 2247 * volume at all. We will do the logfile emptying and dirty setting 2248 * later if needed. 2249 */ 2250 if (!(vol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_FORENSIC))) { 2251 int err = errno; 2252 2253 perr_printf("Opening '%s' as NTFS failed", opt.volume); 2254 if (err == EINVAL) 2255 printf(invalid_ntfs_msg, opt.volume); 2256 else if (err == EIO) 2257 printf("%s", corrupt_volume_msg); 2258 else if (err == EPERM) 2259 printf("%s", hibernated_volume_msg); 2260 else if (err == EOPNOTSUPP) 2261 printf("%s", unclean_journal_msg); 2262 else if (err == EBUSY) 2263 printf("%s", opened_volume_msg); 2264 exit(1); 2265 } 2266 2267 if (NVolWasDirty(vol)) 2268 if (opt.force-- <= 0) 2269 err_exit("Volume is scheduled for check.\nRun chkdsk /f" 2270 " and please try again, or see option -f.\n"); 2271 2272 if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) 2273 err_exit("Cluster size %u is too large!\n", 2274 (unsigned int)vol->cluster_size); 2275 2276 printf("Device name : %s\n", opt.volume); 2277 printf("NTFS volume version: %d.%d\n", vol->major_ver, vol->minor_ver); 2278 if (ntfs_version_is_supported(vol)) 2279 perr_exit("Unknown NTFS version"); 2280 2281 printf("Cluster size : %u bytes\n", 2282 (unsigned int)vol->cluster_size); 2283 print_vol_size("Current volume size", vol_size(vol, vol->nr_clusters)); 2284 2285 return vol; 2286} 2287 2288/** 2289 * prepare_volume_fixup 2290 * 2291 * Set the volume's dirty flag and wipe the filesystem journal. When Windows 2292 * boots it will automatically run chkdsk to check for any problems. If the 2293 * read-only command line option was given, this function will do nothing. 2294 */ 2295static void prepare_volume_fixup(ntfs_volume *vol) 2296{ 2297 printf("Schedule chkdsk for NTFS consistency check at Windows boot " 2298 "time ...\n"); 2299 vol->flags |= VOLUME_IS_DIRTY; 2300 if (ntfs_volume_write_flags(vol, vol->flags)) 2301 perr_exit("Failed to set the volume dirty"); 2302 NVolSetWasDirty(vol); 2303 if (vol->u.dev->d_ops->sync(vol->u.dev) == -1) 2304 perr_exit("Failed to sync device"); 2305 printf("Resetting $LogFile ... (this might take a while)\n"); 2306 if (ntfs_logfile_reset(vol)) 2307 perr_exit("Failed to reset $LogFile"); 2308 if (vol->u.dev->d_ops->sync(vol->u.dev) == -1) 2309 perr_exit("Failed to sync device"); 2310} 2311 2312static void set_disk_usage_constraint(ntfs_resize_t *resize) 2313{ 2314 /* last lcn for a filled up volume (no empty space) */ 2315 s64 last = resize->inuse - 1; 2316 2317 if (resize->last_unsupp < last) 2318 resize->last_unsupp = last; 2319} 2320 2321static void check_resize_constraints(ntfs_resize_t *resize) 2322{ 2323 s64 new_size = resize->new_volume_size; 2324 2325 /* FIXME: resize.shrink true also if only -i is used */ 2326 if (!resize->shrink) 2327 return; 2328 2329 if (resize->inuse == resize->vol->nr_clusters) 2330 err_exit("Volume is full. To shrink it, " 2331 "delete unused files.\n"); 2332 2333 if (opt.info) 2334 return; 2335 2336 /* FIXME: reserve some extra space so Windows can boot ... */ 2337 if (new_size < resize->inuse) 2338 err_exit("New size can't be less than the space already" 2339 " occupied by data.\nYou either need to delete unused" 2340 " files or see the -i option.\n"); 2341 2342 if (new_size <= resize->last_unsupp) 2343 err_exit("The fragmentation type, you have, isn't " 2344 "supported yet. Rerun ntfsresize\nwith " 2345 "the -i option to estimate the smallest " 2346 "shrunken volume size supported.\n"); 2347 2348 print_num_of_relocations(resize); 2349} 2350 2351static void check_cluster_allocation(ntfs_volume *vol, ntfsck_t *fsck) 2352{ 2353 memset(fsck, 0, sizeof(ntfsck_t)); 2354 2355 if (opt.show_progress) 2356 fsck->flags |= NTFSCK_PROGBAR; 2357 2358 if (setup_lcn_bitmap(&fsck->lcn_bitmap, vol->nr_clusters) != 0) 2359 perr_exit("Failed to setup allocation bitmap"); 2360 if (build_allocation_bitmap(vol, fsck) != 0) 2361 exit(1); 2362 if (fsck->outsider || fsck->multi_ref) { 2363 err_printf("Filesystem check failed!\n"); 2364 if (fsck->outsider) 2365 err_printf("%d clusters are referenced outside " 2366 "of the volume.\n", fsck->outsider); 2367 if (fsck->multi_ref) 2368 err_printf("%d clusters are referenced multiply" 2369 " times.\n", fsck->multi_ref); 2370 printf("%s", corrupt_volume_msg); 2371 exit(1); 2372 } 2373 2374 compare_bitmaps(vol, &fsck->lcn_bitmap); 2375} 2376 2377int main(int argc, char **argv) 2378{ 2379 ntfsck_t fsck; 2380 ntfs_resize_t resize; 2381 s64 new_size = 0; /* in clusters; 0 = --info w/o --size */ 2382 s64 device_size; /* in bytes */ 2383 ntfs_volume *vol; 2384 2385 ntfs_log_set_handler(ntfs_log_handler_outerr); 2386 2387 printf("%s v%s (libntfs %s)\n", EXEC_NAME, VERSION, 2388 ntfs_libntfs_version()); 2389 2390 if (!parse_options(argc, argv)) 2391 return 1; 2392 2393 utils_set_locale(); 2394 2395 if (!(vol = mount_volume())) 2396 err_exit("Couldn't open volume '%s'!\n", opt.volume); 2397 2398 device_size = ntfs_device_size_get(vol->u.dev, vol->sector_size); 2399 device_size *= vol->sector_size; 2400 if (device_size <= 0) 2401 err_exit("Couldn't get device size (%lld)!\n", device_size); 2402 2403 print_vol_size("Current device size", device_size); 2404 2405 if (device_size < vol->nr_clusters * vol->cluster_size) 2406 err_exit("Current NTFS volume size is bigger than the device " 2407 "size!\nCorrupt partition table or incorrect device " 2408 "partitioning?\n"); 2409 2410 if (!opt.bytes && !opt.info) 2411 opt.bytes = device_size; 2412 2413 /* Take the integer part: don't make the volume bigger than requested */ 2414 new_size = opt.bytes / vol->cluster_size; 2415 2416 /* Backup boot sector at the end of device isn't counted in NTFS 2417 volume size thus we have to reserve space for it. */ 2418 if (new_size) 2419 --new_size; 2420 2421 if (!opt.info) { 2422 print_vol_size("New volume size ", vol_size(vol, new_size)); 2423 if (device_size < opt.bytes) 2424 err_exit("New size can't be bigger than the device size" 2425 ".\nIf you want to enlarge NTFS then first " 2426 "enlarge the device size by e.g. fdisk.\n"); 2427 } 2428 2429 if (!opt.info && (new_size == vol->nr_clusters || 2430 (opt.bytes == device_size && 2431 new_size == vol->nr_clusters - 1))) { 2432 printf("Nothing to do: NTFS volume size is already OK.\n"); 2433 exit(0); 2434 } 2435 2436 memset(&resize, 0, sizeof(resize)); 2437 resize.vol = vol; 2438 resize.new_volume_size = new_size; 2439 /* This is also true if --info was used w/o --size (new_size = 0) */ 2440 if (new_size < vol->nr_clusters) 2441 resize.shrink = 1; 2442 if (opt.show_progress) 2443 resize.progress.flags |= NTFS_PROGBAR; 2444 /* 2445 * Checking and __reporting__ of bad sectors must be done before cluster 2446 * allocation check because chkdsk doesn't fix $Bitmap's w/ bad sectors 2447 * thus users would (were) quite confused why chkdsk doesn't work. 2448 */ 2449 resize.badclusters = check_bad_sectors(vol); 2450 2451 check_cluster_allocation(vol, &fsck); 2452 2453 print_disk_usage(vol, fsck.inuse); 2454 2455 resize.inuse = fsck.inuse; 2456 resize.lcn_bitmap = fsck.lcn_bitmap; 2457 2458 set_resize_constraints(&resize); 2459 set_disk_usage_constraint(&resize); 2460 check_resize_constraints(&resize); 2461 2462 if (opt.info) { 2463 advise_on_resize(&resize); 2464 exit(0); 2465 } 2466 2467 if (opt.force-- <= 0 && !opt.ro_flag) { 2468 printf("%s", resize_warning_msg); 2469 proceed_question(); 2470 } 2471 2472 /* FIXME: performance - relocate logfile here if it's needed */ 2473 prepare_volume_fixup(vol); 2474 2475 if (resize.relocations) 2476 relocate_inodes(&resize); 2477 2478 truncate_badclust_file(&resize); 2479 truncate_bitmap_file(&resize); 2480 update_bootsector(&resize); 2481 2482 /* We don't create backup boot sector because we don't know where the 2483 partition will be split. The scheduled chkdsk will fix it */ 2484 2485 if (opt.ro_flag) { 2486 printf("The read-only test run ended successfully.\n"); 2487 exit(0); 2488 } 2489 2490 /* WARNING: don't modify the texts, external tools grep for them */ 2491 printf("Syncing device ...\n"); 2492 if (vol->u.dev->d_ops->sync(vol->u.dev) == -1) 2493 perr_exit("fsync"); 2494 2495 printf("Successfully resized NTFS on device '%s'.\n", vol->u.dev->d_name); 2496 if (resize.shrink) 2497 printf("%s", resize_important_msg); 2498 2499 return 0; 2500} 2501