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