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