blob: 6f5418115aa996a4a136bb3d5e072288d2310102
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 | * Copyright (c) 2011-2013 Jean-Pierre Andre |
9 | * |
10 | * This utility will resize an NTFS volume without data loss. |
11 | * |
12 | * WARNING FOR DEVELOPERS!!! Several external tools grep for text messages |
13 | * to control execution thus if you would like to change any message |
14 | * then PLEASE think twice before doing so then don't modify it. Thanks! |
15 | * |
16 | * This program is free software; you can redistribute it and/or modify |
17 | * it under the terms of the GNU General Public License as published by |
18 | * the Free Software Foundation; either version 2 of the License, or |
19 | * (at your option) any later version. |
20 | * |
21 | * This program is distributed in the hope that it will be useful, |
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
24 | * GNU General Public License for more details. |
25 | * |
26 | * You should have received a copy of the GNU General Public License |
27 | * along with this program (in the main directory of the Linux-NTFS |
28 | * distribution in the file COPYING); if not, write to the Free Software |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
30 | */ |
31 | |
32 | #include "config.h" |
33 | |
34 | #ifdef HAVE_UNISTD_H |
35 | #include <unistd.h> |
36 | #endif |
37 | #ifdef HAVE_STDLIB_H |
38 | #include <stdlib.h> |
39 | #endif |
40 | #ifdef HAVE_STDIO_H |
41 | #include <stdio.h> |
42 | #endif |
43 | #ifdef HAVE_STDARG_H |
44 | #include <stdarg.h> |
45 | #endif |
46 | #ifdef HAVE_STRING_H |
47 | #include <string.h> |
48 | #endif |
49 | #ifdef HAVE_ERRNO_H |
50 | #include <errno.h> |
51 | #endif |
52 | #ifdef HAVE_LIMITS_H |
53 | #include <limits.h> |
54 | #endif |
55 | #ifdef HAVE_GETOPT_H |
56 | #include <getopt.h> |
57 | #endif |
58 | #ifdef HAVE_FCNTL_H |
59 | #include <fcntl.h> |
60 | #endif |
61 | |
62 | #include "debug.h" |
63 | #include "types.h" |
64 | #include "support.h" |
65 | #include "endians.h" |
66 | #include "bootsect.h" |
67 | #include "device.h" |
68 | #include "attrib.h" |
69 | #include "volume.h" |
70 | #include "mft.h" |
71 | #include "bitmap.h" |
72 | #include "inode.h" |
73 | #include "runlist.h" |
74 | #include "utils.h" |
75 | /* #include "version.h" */ |
76 | #include "misc.h" |
77 | |
78 | #define BAN_NEW_TEXT 1 /* Respect the ban on new messages */ |
79 | #define CLEAN_EXIT 0 /* traditionnally volume is not closed, there must be a reason */ |
80 | |
81 | static const char *EXEC_NAME = "ntfsresize"; |
82 | |
83 | static const char *resize_warning_msg = |
84 | "WARNING: Every sanity check passed and only the dangerous operations left.\n" |
85 | "Make sure that important data has been backed up! Power outage or computer\n" |
86 | "crash may result major data loss!\n"; |
87 | |
88 | static const char *resize_important_msg = |
89 | "You can go on to shrink the device for example with Linux fdisk.\n" |
90 | "IMPORTANT: When recreating the partition, make sure that you\n" |
91 | " 1) create it at the same disk sector (use sector as the unit!)\n" |
92 | " 2) create it with the same partition type (usually 7, HPFS/NTFS)\n" |
93 | " 3) do not make it smaller than the new NTFS filesystem size\n" |
94 | " 4) set the bootable flag for the partition if it existed before\n" |
95 | "Otherwise you won't be able to access NTFS or can't boot from the disk!\n" |
96 | "If you make a mistake and don't have a partition table backup then you\n" |
97 | "can recover the partition table by TestDisk or Parted's rescue mode.\n"; |
98 | |
99 | static const char *invalid_ntfs_msg = |
100 | "The device '%s' doesn't have a valid NTFS.\n" |
101 | "Maybe you selected the wrong partition? Or the whole disk instead of a\n" |
102 | "partition (e.g. /dev/hda, not /dev/hda1)? This error might also occur\n" |
103 | "if the disk was incorrectly repartitioned (see the ntfsresize FAQ).\n"; |
104 | |
105 | static const char *corrupt_volume_msg = |
106 | "NTFS is inconsistent. Run chkdsk /f on Windows then reboot it TWICE!\n" |
107 | "The usage of the /f parameter is very IMPORTANT! No modification was\n" |
108 | "and will be made to NTFS by this software until it gets repaired.\n"; |
109 | |
110 | static const char *hibernated_volume_msg = |
111 | "The NTFS partition is hibernated. Windows must be resumed and turned off\n" |
112 | "properly, so resizing could be done safely.\n"; |
113 | |
114 | static const char *unclean_journal_msg = |
115 | "The NTFS journal file is unclean. Please shutdown Windows properly before\n" |
116 | "using this software! Note, if you have run chkdsk previously then boot\n" |
117 | "Windows again which will automatically initialize the journal correctly.\n"; |
118 | |
119 | static const char *opened_volume_msg = |
120 | "This software has detected that the NTFS volume is already opened by another\n" |
121 | "software thus it refuses to progress to preserve data consistency.\n"; |
122 | |
123 | static const char *bad_sectors_warning_msg = |
124 | "****************************************************************************\n" |
125 | "* WARNING: The disk has bad sector. This means physical damage on the disk *\n" |
126 | "* surface caused by deterioration, manufacturing faults or other reason. *\n" |
127 | "* The reliability of the disk may stay stable or degrade fast. We suggest *\n" |
128 | "* making a full backup urgently by running 'ntfsclone --rescue ...' then *\n" |
129 | "* run 'chkdsk /f /r' on Windows and rebooot it TWICE! Then you can resize *\n" |
130 | "* NTFS safely by additionally using the --bad-sectors option of ntfsresize.*\n" |
131 | "****************************************************************************\n"; |
132 | |
133 | static const char *many_bad_sectors_msg = |
134 | "***************************************************************************\n" |
135 | "* WARNING: The disk has many bad sectors. This means physical damage *\n" |
136 | "* on the disk surface caused by deterioration, manufacturing faults or *\n" |
137 | "* other reason. We suggest to get a replacement disk as soon as possible. *\n" |
138 | "***************************************************************************\n"; |
139 | |
140 | static struct { |
141 | int verbose; |
142 | int debug; |
143 | int ro_flag; |
144 | int force; |
145 | int info; |
146 | int infombonly; |
147 | int expand; |
148 | int reliable_size; |
149 | int show_progress; |
150 | int badsectors; |
151 | int check; |
152 | s64 bytes; |
153 | char *volume; |
154 | } opt; |
155 | |
156 | struct bitmap { |
157 | s64 size; |
158 | u8 *bm; |
159 | }; |
160 | |
161 | #define NTFS_PROGBAR 0x0001 |
162 | #define NTFS_PROGBAR_SUPPRESS 0x0002 |
163 | |
164 | struct progress_bar { |
165 | u64 start; |
166 | u64 stop; |
167 | int resolution; |
168 | int flags; |
169 | float unit; |
170 | }; |
171 | |
172 | struct llcn_t { |
173 | s64 lcn; /* last used LCN for a "special" file/attr type */ |
174 | s64 inode; /* inode using it */ |
175 | }; |
176 | |
177 | #define NTFSCK_PROGBAR 0x0001 |
178 | |
179 | /* runlists which have to be processed later */ |
180 | struct DELAYED { |
181 | struct DELAYED *next; |
182 | ATTR_TYPES type; |
183 | MFT_REF mref; |
184 | VCN lowest_vcn; |
185 | int name_len; |
186 | ntfschar *attr_name; |
187 | runlist_element *rl; |
188 | runlist *head_rl; |
189 | } ; |
190 | |
191 | typedef struct { |
192 | ntfs_inode *ni; /* inode being processed */ |
193 | ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ |
194 | s64 inuse; /* num of clusters in use */ |
195 | int multi_ref; /* num of clusters referenced many times */ |
196 | int outsider; /* num of clusters outside the volume */ |
197 | int show_outsider; /* controls showing the above information */ |
198 | int flags; |
199 | struct bitmap lcn_bitmap; |
200 | } ntfsck_t; |
201 | |
202 | typedef struct { |
203 | ntfs_volume *vol; |
204 | ntfs_inode *ni; /* inode being processed */ |
205 | s64 new_volume_size; /* in clusters; 0 = --info w/o --size */ |
206 | MFT_REF mref; /* mft reference */ |
207 | MFT_RECORD *mrec; /* mft record */ |
208 | ntfs_attr_search_ctx *ctx; /* inode attribute being processed */ |
209 | u64 relocations; /* num of clusters to relocate */ |
210 | s64 inuse; /* num of clusters in use */ |
211 | runlist mftmir_rl; /* $MFTMirr AT_DATA's new position */ |
212 | s64 mftmir_old; /* $MFTMirr AT_DATA's old LCN */ |
213 | int dirty_inode; /* some inode data got relocated */ |
214 | int shrink; /* shrink = 1, enlarge = 0 */ |
215 | s64 badclusters; /* num of physically dead clusters */ |
216 | VCN mft_highest_vcn; /* used for relocating the $MFT */ |
217 | runlist_element *new_mft_start; /* new first run for $MFT:$DATA */ |
218 | struct DELAYED *delayed_runlists; /* runlists to process later */ |
219 | struct progress_bar progress; |
220 | struct bitmap lcn_bitmap; |
221 | /* Temporary statistics until all case is supported */ |
222 | struct llcn_t last_mft; |
223 | struct llcn_t last_mftmir; |
224 | struct llcn_t last_multi_mft; |
225 | struct llcn_t last_sparse; |
226 | struct llcn_t last_compressed; |
227 | struct llcn_t last_lcn; |
228 | s64 last_unsupp; /* last unsupported cluster */ |
229 | } ntfs_resize_t; |
230 | |
231 | /* FIXME: This, lcn_bitmap and pos from find_free_cluster() will make a cluster |
232 | allocation related structure, attached to ntfs_resize_t */ |
233 | static s64 max_free_cluster_range = 0; |
234 | |
235 | #define NTFS_MBYTE (1000 * 1000) |
236 | |
237 | /* WARNING: don't modify the text, external tools grep for it */ |
238 | #define ERR_PREFIX "ERROR" |
239 | #define PERR_PREFIX ERR_PREFIX "(%d): " |
240 | #define NERR_PREFIX ERR_PREFIX ": " |
241 | |
242 | #define DIRTY_NONE (0) |
243 | #define DIRTY_INODE (1) |
244 | #define DIRTY_ATTRIB (2) |
245 | |
246 | #define NTFS_MAX_CLUSTER_SIZE (65536) |
247 | |
248 | static s64 rounded_up_division(s64 numer, s64 denom) |
249 | { |
250 | return (numer + (denom - 1)) / denom; |
251 | } |
252 | |
253 | /** |
254 | * perr_printf |
255 | * |
256 | * Print an error message. |
257 | */ |
258 | __attribute__((format(printf, 1, 2))) |
259 | static void perr_printf(const char *fmt, ...) |
260 | { |
261 | va_list ap; |
262 | int eo = errno; |
263 | |
264 | fprintf(stdout, PERR_PREFIX, eo); |
265 | va_start(ap, fmt); |
266 | vfprintf(stdout, fmt, ap); |
267 | va_end(ap); |
268 | fprintf(stdout, ": %s\n", strerror(eo)); |
269 | fflush(stdout); |
270 | fflush(stderr); |
271 | } |
272 | |
273 | __attribute__((format(printf, 1, 2))) |
274 | static void err_printf(const char *fmt, ...) |
275 | { |
276 | va_list ap; |
277 | |
278 | fprintf(stdout, NERR_PREFIX); |
279 | va_start(ap, fmt); |
280 | vfprintf(stdout, fmt, ap); |
281 | va_end(ap); |
282 | fflush(stdout); |
283 | fflush(stderr); |
284 | } |
285 | |
286 | /** |
287 | * err_exit |
288 | * |
289 | * Print and error message and exit the program. |
290 | */ |
291 | __attribute__((noreturn)) |
292 | __attribute__((format(printf, 1, 2))) |
293 | static void err_exit(const char *fmt, ...) |
294 | { |
295 | va_list ap; |
296 | |
297 | fprintf(stdout, NERR_PREFIX); |
298 | va_start(ap, fmt); |
299 | vfprintf(stdout, fmt, ap); |
300 | va_end(ap); |
301 | fflush(stdout); |
302 | fflush(stderr); |
303 | exit(1); |
304 | } |
305 | |
306 | /** |
307 | * perr_exit |
308 | * |
309 | * Print and error message and exit the program |
310 | */ |
311 | __attribute__((noreturn)) |
312 | __attribute__((format(printf, 1, 2))) |
313 | static void perr_exit(const char *fmt, ...) |
314 | { |
315 | va_list ap; |
316 | int eo = errno; |
317 | |
318 | fprintf(stdout, PERR_PREFIX, eo); |
319 | va_start(ap, fmt); |
320 | vfprintf(stdout, fmt, ap); |
321 | va_end(ap); |
322 | printf(": %s\n", strerror(eo)); |
323 | fflush(stdout); |
324 | fflush(stderr); |
325 | exit(1); |
326 | } |
327 | |
328 | /** |
329 | * usage - Print a list of the parameters to the program |
330 | * |
331 | * Print a list of the parameters and options for the program. |
332 | * |
333 | * Return: none |
334 | */ |
335 | __attribute__((noreturn)) |
336 | static void usage(void) |
337 | { |
338 | |
339 | printf("\nUsage: %s [OPTIONS] DEVICE\n" |
340 | " Resize an NTFS volume non-destructively, safely move any data if needed.\n" |
341 | "\n" |
342 | " -c, --check Check to ensure that the device is ready for resize\n" |
343 | " -i, --info Estimate the smallest shrunken size or the smallest\n" |
344 | " expansion size\n" |
345 | " -m, --info-mb-only Estimate the smallest shrunken size possible,\n" |
346 | " output size in MB only\n" |
347 | " -s, --size SIZE Resize volume to SIZE[k|M|G] bytes\n" |
348 | " -x, --expand Expand to full partition\n" |
349 | "\n" |
350 | " -n, --no-action Do not write to disk\n" |
351 | " -b, --bad-sectors Support disks having bad sectors\n" |
352 | " -f, --force Force to progress\n" |
353 | " -P, --no-progress-bar Don't show progress bar\n" |
354 | " -v, --verbose More output\n" |
355 | " -V, --version Display version information\n" |
356 | " -h, --help Display this help\n" |
357 | #ifdef DEBUG |
358 | " -d, --debug Show debug information\n" |
359 | #endif |
360 | "\n" |
361 | " The options -i and -x are exclusive of option -s, and -m is exclusive\n" |
362 | " of option -x. If options -i, -m, -s and -x are are all omitted\n" |
363 | " then the NTFS volume will be enlarged to the DEVICE size.\n" |
364 | "\n", EXEC_NAME); |
365 | printf("%s%s", ntfs_bugs, ntfs_home); |
366 | printf("Ntfsresize FAQ: http://linux-ntfs.sourceforge.net/info/ntfsresize.html\n"); |
367 | exit(1); |
368 | } |
369 | |
370 | /** |
371 | * proceed_question |
372 | * |
373 | * Force the user to confirm an action before performing it. |
374 | * Copy-paste from e2fsprogs |
375 | */ |
376 | static void proceed_question(void) |
377 | { |
378 | char buf[256]; |
379 | const char *short_yes = "yY"; |
380 | |
381 | fflush(stdout); |
382 | fflush(stderr); |
383 | printf("Are you sure you want to proceed (y/[n])? "); |
384 | buf[0] = 0; |
385 | if (fgets(buf, sizeof(buf), stdin) |
386 | && !strchr(short_yes, buf[0])) { |
387 | printf("OK quitting. NO CHANGES have been made to your " |
388 | "NTFS volume.\n"); |
389 | exit(1); |
390 | } |
391 | } |
392 | |
393 | /** |
394 | * version - Print version information about the program |
395 | * |
396 | * Print a copyright statement and a brief description of the program. |
397 | * |
398 | * Return: none |
399 | */ |
400 | static void version(void) |
401 | { |
402 | printf("\nResize an NTFS Volume, without data loss.\n\n"); |
403 | printf("Copyright (c) 2002-2006 Szabolcs Szakacsits\n"); |
404 | printf("Copyright (c) 2002-2005 Anton Altaparmakov\n"); |
405 | printf("Copyright (c) 2002-2003 Richard Russon\n"); |
406 | printf("Copyright (c) 2007 Yura Pakhuchiy\n"); |
407 | printf("Copyright (c) 2011-2012 Jean-Pierre Andre\n"); |
408 | printf("\n%s\n%s%s", ntfs_gpl, ntfs_bugs, ntfs_home); |
409 | } |
410 | |
411 | /** |
412 | * get_new_volume_size |
413 | * |
414 | * Convert a user-supplied string into a size. Without any suffix the number |
415 | * will be assumed to be in bytes. If the number has a suffix of k, M or G it |
416 | * will be scaled up by 1000, 1000000, or 1000000000. |
417 | */ |
418 | static s64 get_new_volume_size(char *s) |
419 | { |
420 | s64 size; |
421 | char *suffix; |
422 | int prefix_kind = 1000; |
423 | |
424 | size = strtoll(s, &suffix, 10); |
425 | if (size <= 0 || errno == ERANGE) |
426 | err_exit("Illegal new volume size\n"); |
427 | |
428 | if (!*suffix) { |
429 | opt.reliable_size = 1; |
430 | return size; |
431 | } |
432 | |
433 | if (strlen(suffix) == 2 && suffix[1] == 'i') |
434 | prefix_kind = 1024; |
435 | else if (strlen(suffix) > 1) |
436 | usage(); |
437 | |
438 | /* We follow the SI prefixes: |
439 | http://physics.nist.gov/cuu/Units/prefixes.html |
440 | http://physics.nist.gov/cuu/Units/binary.html |
441 | Disk partitioning tools use prefixes as, |
442 | k M G |
443 | fdisk 2.11x- 2^10 2^20 10^3*2^20 |
444 | fdisk 2.11y+ 10^3 10^6 10^9 |
445 | cfdisk 10^3 10^6 10^9 |
446 | sfdisk 2^10 2^20 |
447 | parted 2^10 2^20 (may change) |
448 | fdisk (DOS) 2^10 2^20 |
449 | */ |
450 | /* FIXME: check for overflow */ |
451 | switch (*suffix) { |
452 | case 'G': |
453 | size *= prefix_kind; |
454 | case 'M': |
455 | size *= prefix_kind; |
456 | case 'k': |
457 | size *= prefix_kind; |
458 | break; |
459 | default: |
460 | usage(); |
461 | } |
462 | |
463 | return size; |
464 | } |
465 | |
466 | /** |
467 | * parse_options - Read and validate the programs command line |
468 | * |
469 | * Read the command line, verify the syntax and parse the options. |
470 | * This function is very long, but quite simple. |
471 | * |
472 | * Return: 1 Success |
473 | * 0 Error, one or more problems |
474 | */ |
475 | static int parse_options(int argc, char **argv) |
476 | { |
477 | static const char *sopt = "-bcdfhimnPs:vVx"; |
478 | static const struct option lopt[] = { |
479 | { "bad-sectors",no_argument, NULL, 'b' }, |
480 | { "check", no_argument, NULL, 'c' }, |
481 | #ifdef DEBUG |
482 | { "debug", no_argument, NULL, 'd' }, |
483 | #endif |
484 | { "force", no_argument, NULL, 'f' }, |
485 | { "help", no_argument, NULL, 'h' }, |
486 | { "info", no_argument, NULL, 'i' }, |
487 | { "info-mb-only", no_argument, NULL, 'm' }, |
488 | { "no-action", no_argument, NULL, 'n' }, |
489 | { "no-progress-bar", no_argument, NULL, 'P' }, |
490 | { "size", required_argument, NULL, 's' }, |
491 | { "expand", no_argument, NULL, 'x' }, |
492 | { "verbose", no_argument, NULL, 'v' }, |
493 | { "version", no_argument, NULL, 'V' }, |
494 | { NULL, 0, NULL, 0 } |
495 | }; |
496 | |
497 | int c; |
498 | int err = 0; |
499 | int ver = 0; |
500 | int help = 0; |
501 | |
502 | memset(&opt, 0, sizeof(opt)); |
503 | opt.show_progress = 1; |
504 | |
505 | while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { |
506 | switch (c) { |
507 | case 1: /* A non-option argument */ |
508 | if (!err && !opt.volume) |
509 | opt.volume = argv[optind-1]; |
510 | else |
511 | err++; |
512 | break; |
513 | case 'b': |
514 | opt.badsectors++; |
515 | break; |
516 | case 'c': |
517 | opt.check++; |
518 | break; |
519 | case 'd': |
520 | opt.debug++; |
521 | break; |
522 | case 'f': |
523 | opt.force++; |
524 | break; |
525 | case 'h': |
526 | case '?': |
527 | help++; |
528 | break; |
529 | case 'i': |
530 | opt.info++; |
531 | break; |
532 | case 'm': |
533 | opt.infombonly++; |
534 | break; |
535 | case 'n': |
536 | opt.ro_flag = NTFS_MNT_RDONLY; |
537 | break; |
538 | case 'P': |
539 | opt.show_progress = 0; |
540 | break; |
541 | case 's': |
542 | if (!err && (opt.bytes == 0)) |
543 | opt.bytes = get_new_volume_size(optarg); |
544 | else |
545 | err++; |
546 | break; |
547 | case 'v': |
548 | opt.verbose++; |
549 | ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); |
550 | break; |
551 | case 'V': |
552 | ver++; |
553 | break; |
554 | case 'x': |
555 | opt.expand++; |
556 | break; |
557 | default: |
558 | if (optopt == 's') { |
559 | printf("Option '%s' requires an argument.\n", argv[optind-1]); |
560 | } else { |
561 | printf("Unknown option '%s'.\n", argv[optind-1]); |
562 | } |
563 | err++; |
564 | break; |
565 | } |
566 | } |
567 | |
568 | if (!help && !ver) { |
569 | if (opt.volume == NULL) { |
570 | if (argc > 1) |
571 | printf("You must specify exactly one device.\n"); |
572 | err++; |
573 | } |
574 | if (opt.info || opt.infombonly) { |
575 | opt.ro_flag = NTFS_MNT_RDONLY; |
576 | } |
577 | if (opt.bytes |
578 | && (opt.expand || opt.info || opt.infombonly)) { |
579 | printf(NERR_PREFIX "Options --info(-mb-only) and --expand " |
580 | "cannot be used with --size.\n"); |
581 | usage(); |
582 | } |
583 | if (opt.expand && opt.infombonly) { |
584 | printf(NERR_PREFIX "Options --info-mb-only " |
585 | "cannot be used with --expand.\n"); |
586 | usage(); |
587 | } |
588 | } |
589 | |
590 | /* Redirect stderr to stdout, note fflush()es are essential! */ |
591 | fflush(stdout); |
592 | fflush(stderr); |
593 | if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) |
594 | perr_exit("Failed to redirect stderr to stdout"); |
595 | fflush(stdout); |
596 | fflush(stderr); |
597 | |
598 | #ifdef DEBUG |
599 | if (!opt.debug) |
600 | if (!freopen("/dev/null", "w", stderr)) |
601 | perr_exit("Failed to redirect stderr to /dev/null"); |
602 | #endif |
603 | |
604 | if (ver) |
605 | version(); |
606 | if (help || err) |
607 | usage(); |
608 | |
609 | return (!err && !help && !ver); |
610 | } |
611 | |
612 | static void print_advise(ntfs_volume *vol, s64 supp_lcn) |
613 | { |
614 | s64 old_b, new_b, freed_b, old_mb, new_mb, freed_mb; |
615 | |
616 | old_b = vol->nr_clusters * vol->cluster_size; |
617 | old_mb = rounded_up_division(old_b, NTFS_MBYTE); |
618 | |
619 | /* Take the next supported cluster (free or relocatable) |
620 | plus reserve a cluster for the backup boot sector */ |
621 | supp_lcn += 2; |
622 | |
623 | if (supp_lcn > vol->nr_clusters) { |
624 | err_printf("Very rare fragmentation type detected. " |
625 | "Sorry, it's not supported yet.\n" |
626 | "Try to defragment your NTFS, perhaps it helps.\n"); |
627 | exit(1); |
628 | } |
629 | |
630 | new_b = supp_lcn * vol->cluster_size; |
631 | new_mb = rounded_up_division(new_b, NTFS_MBYTE); |
632 | freed_b = (vol->nr_clusters - supp_lcn + 1) * vol->cluster_size; |
633 | freed_mb = freed_b / NTFS_MBYTE; |
634 | |
635 | /* WARNING: don't modify the text, external tools grep for it */ |
636 | if (!opt.infombonly) |
637 | printf("You might resize at %lld bytes ", (long long)new_b); |
638 | if ((new_mb * NTFS_MBYTE) < old_b) { |
639 | if (!opt.infombonly) |
640 | printf("or %lld MB ", (long long)new_mb); |
641 | else |
642 | printf("Minsize (in MB): %lld\n", (long long)new_mb); |
643 | } |
644 | |
645 | if (!opt.infombonly) { |
646 | printf("(freeing "); |
647 | if (freed_mb && (old_mb - new_mb)) |
648 | printf("%lld MB", (long long)(old_mb - new_mb)); |
649 | else |
650 | printf("%lld bytes", (long long)freed_b); |
651 | printf(").\n"); |
652 | |
653 | printf("Please make a test run using both the -n and -s " |
654 | "options before real resizing!\n"); |
655 | } |
656 | } |
657 | |
658 | static void rl_set(runlist *rl, VCN vcn, LCN lcn, s64 len) |
659 | { |
660 | rl->vcn = vcn; |
661 | rl->lcn = lcn; |
662 | rl->length = len; |
663 | } |
664 | |
665 | static int rl_items(runlist *rl) |
666 | { |
667 | int i = 0; |
668 | |
669 | while (rl[i++].length) |
670 | ; |
671 | |
672 | return i; |
673 | } |
674 | |
675 | static void dump_run(runlist_element *r) |
676 | { |
677 | ntfs_log_verbose(" %8lld %8lld (0x%08llx) %lld\n", (long long)r->vcn, |
678 | (long long)r->lcn, (long long)r->lcn, |
679 | (long long)r->length); |
680 | } |
681 | |
682 | static void dump_runlist(runlist *rl) |
683 | { |
684 | while (rl->length) |
685 | dump_run(rl++); |
686 | } |
687 | |
688 | /** |
689 | * nr_clusters_to_bitmap_byte_size |
690 | * |
691 | * Take the number of clusters in the volume and calculate the size of $Bitmap. |
692 | * The size must be always a multiple of 8 bytes. |
693 | */ |
694 | static s64 nr_clusters_to_bitmap_byte_size(s64 nr_clusters) |
695 | { |
696 | s64 bm_bsize; |
697 | |
698 | bm_bsize = rounded_up_division(nr_clusters, 8); |
699 | bm_bsize = (bm_bsize + 7) & ~7; |
700 | |
701 | return bm_bsize; |
702 | } |
703 | |
704 | static void collect_resize_constraints(ntfs_resize_t *resize, runlist *rl) |
705 | { |
706 | s64 inode, last_lcn; |
707 | ATTR_FLAGS flags; |
708 | ATTR_TYPES atype; |
709 | struct llcn_t *llcn = NULL; |
710 | int ret, supported = 0; |
711 | |
712 | last_lcn = rl->lcn + (rl->length - 1); |
713 | |
714 | inode = resize->ni->mft_no; |
715 | flags = resize->ctx->attr->flags; |
716 | atype = resize->ctx->attr->type; |
717 | |
718 | if ((ret = ntfs_inode_badclus_bad(inode, resize->ctx->attr)) != 0) { |
719 | if (ret == -1) |
720 | perr_exit("Bad sector list check failed"); |
721 | return; |
722 | } |
723 | |
724 | if (inode == FILE_Bitmap) { |
725 | llcn = &resize->last_lcn; |
726 | if (atype == AT_DATA && NInoAttrList(resize->ni)) |
727 | err_exit("Highly fragmented $Bitmap isn't supported yet."); |
728 | |
729 | supported = 1; |
730 | |
731 | } else if (NInoAttrList(resize->ni)) { |
732 | llcn = &resize->last_multi_mft; |
733 | |
734 | if (inode != FILE_MFTMirr) |
735 | supported = 1; |
736 | |
737 | } else if (flags & ATTR_IS_SPARSE) { |
738 | llcn = &resize->last_sparse; |
739 | supported = 1; |
740 | |
741 | } else if (flags & ATTR_IS_COMPRESSED) { |
742 | llcn = &resize->last_compressed; |
743 | supported = 1; |
744 | |
745 | } else if (inode == FILE_MFTMirr) { |
746 | llcn = &resize->last_mftmir; |
747 | supported = 1; |
748 | |
749 | /* Fragmented $MFTMirr DATA attribute isn't supported yet */ |
750 | if (atype == AT_DATA) |
751 | if (rl[1].length != 0 || rl->vcn) |
752 | supported = 0; |
753 | } else { |
754 | llcn = &resize->last_lcn; |
755 | supported = 1; |
756 | } |
757 | |
758 | if (llcn->lcn < last_lcn) { |
759 | llcn->lcn = last_lcn; |
760 | llcn->inode = inode; |
761 | } |
762 | |
763 | if (supported) |
764 | return; |
765 | |
766 | if (resize->last_unsupp < last_lcn) |
767 | resize->last_unsupp = last_lcn; |
768 | } |
769 | |
770 | |
771 | static void collect_relocation_info(ntfs_resize_t *resize, runlist *rl) |
772 | { |
773 | s64 lcn, lcn_length, start, len, inode; |
774 | s64 new_vol_size; /* (last LCN on the volume) + 1 */ |
775 | |
776 | lcn = rl->lcn; |
777 | lcn_length = rl->length; |
778 | inode = resize->ni->mft_no; |
779 | new_vol_size = resize->new_volume_size; |
780 | |
781 | if (lcn + lcn_length <= new_vol_size) |
782 | return; |
783 | |
784 | if (inode == FILE_Bitmap && resize->ctx->attr->type == AT_DATA) |
785 | return; |
786 | |
787 | start = lcn; |
788 | len = lcn_length; |
789 | |
790 | if (lcn < new_vol_size) { |
791 | start = new_vol_size; |
792 | len = lcn_length - (new_vol_size - lcn); |
793 | |
794 | if ((!opt.info && !opt.infombonly) && (inode == FILE_MFTMirr)) { |
795 | err_printf("$MFTMirr can't be split up yet. Please try " |
796 | "a different size.\n"); |
797 | print_advise(resize->vol, lcn + lcn_length - 1); |
798 | exit(1); |
799 | } |
800 | } |
801 | |
802 | resize->relocations += len; |
803 | |
804 | if ((!opt.info && !opt.infombonly) || !resize->new_volume_size) |
805 | return; |
806 | |
807 | printf("Relocation needed for inode %8lld attr 0x%x LCN 0x%08llx " |
808 | "length %6lld\n", (long long)inode, |
809 | (unsigned int)le32_to_cpu(resize->ctx->attr->type), |
810 | (unsigned long long)start, (long long)len); |
811 | } |
812 | |
813 | /** |
814 | * build_lcn_usage_bitmap |
815 | * |
816 | * lcn_bitmap has one bit for each cluster on the disk. Initially, lcn_bitmap |
817 | * has no bits set. As each attribute record is read the bits in lcn_bitmap are |
818 | * checked to ensure that no other file already references that cluster. |
819 | * |
820 | * This serves as a rudimentary "chkdsk" operation. |
821 | */ |
822 | static void build_lcn_usage_bitmap(ntfs_volume *vol, ntfsck_t *fsck) |
823 | { |
824 | s64 inode; |
825 | ATTR_RECORD *a; |
826 | runlist *rl; |
827 | int i, j; |
828 | struct bitmap *lcn_bitmap = &fsck->lcn_bitmap; |
829 | |
830 | a = fsck->ctx->attr; |
831 | inode = fsck->ni->mft_no; |
832 | |
833 | if (!a->non_resident) |
834 | return; |
835 | |
836 | if (!(rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) { |
837 | int err = errno; |
838 | perr_printf("ntfs_decompress_mapping_pairs"); |
839 | if (err == EIO) |
840 | printf("%s", corrupt_volume_msg); |
841 | exit(1); |
842 | } |
843 | |
844 | |
845 | for (i = 0; rl[i].length; i++) { |
846 | s64 lcn = rl[i].lcn; |
847 | s64 lcn_length = rl[i].length; |
848 | |
849 | /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ |
850 | if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) |
851 | continue; |
852 | |
853 | /* FIXME: ntfs_mapping_pairs_decompress should return error */ |
854 | if (lcn < 0 || lcn_length <= 0) |
855 | err_exit("Corrupt runlist in inode %lld attr %x LCN " |
856 | "%llx length %llx\n", (long long)inode, |
857 | (unsigned int)le32_to_cpu(a->type), |
858 | (long long)lcn, (long long)lcn_length); |
859 | |
860 | for (j = 0; j < lcn_length; j++) { |
861 | u64 k = (u64)lcn + j; |
862 | |
863 | if (k >= (u64)vol->nr_clusters) { |
864 | long long outsiders = lcn_length - j; |
865 | |
866 | fsck->outsider += outsiders; |
867 | |
868 | if (++fsck->show_outsider <= 10 || opt.verbose) |
869 | printf("Outside of the volume reference" |
870 | " for inode %lld at %lld:%lld\n", |
871 | (long long)inode, (long long)k, |
872 | (long long)outsiders); |
873 | |
874 | break; |
875 | } |
876 | |
877 | if (ntfs_bit_get_and_set(lcn_bitmap->bm, k, 1)) { |
878 | if (++fsck->multi_ref <= 10 || opt.verbose) |
879 | printf("Cluster %lld is referenced " |
880 | "multiple times!\n", |
881 | (long long)k); |
882 | continue; |
883 | } |
884 | } |
885 | fsck->inuse += lcn_length; |
886 | } |
887 | free(rl); |
888 | } |
889 | |
890 | |
891 | static ntfs_attr_search_ctx *attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) |
892 | { |
893 | ntfs_attr_search_ctx *ret; |
894 | |
895 | if ((ret = ntfs_attr_get_search_ctx(ni, mrec)) == NULL) |
896 | perr_printf("ntfs_attr_get_search_ctx"); |
897 | |
898 | return ret; |
899 | } |
900 | |
901 | /** |
902 | * walk_attributes |
903 | * |
904 | * For a given MFT Record, iterate through all its attributes. Any non-resident |
905 | * data runs will be marked in lcn_bitmap. |
906 | */ |
907 | static int walk_attributes(ntfs_volume *vol, ntfsck_t *fsck) |
908 | { |
909 | if (!(fsck->ctx = attr_get_search_ctx(fsck->ni, NULL))) |
910 | return -1; |
911 | |
912 | while (!ntfs_attrs_walk(fsck->ctx)) { |
913 | if (fsck->ctx->attr->type == AT_END) |
914 | break; |
915 | build_lcn_usage_bitmap(vol, fsck); |
916 | } |
917 | |
918 | ntfs_attr_put_search_ctx(fsck->ctx); |
919 | return 0; |
920 | } |
921 | |
922 | /** |
923 | * compare_bitmaps |
924 | * |
925 | * Compare two bitmaps. In this case, $Bitmap as read from the disk and |
926 | * lcn_bitmap which we built from the MFT Records. |
927 | */ |
928 | static void compare_bitmaps(ntfs_volume *vol, struct bitmap *a) |
929 | { |
930 | s64 i, pos, count; |
931 | int mismatch = 0; |
932 | int backup_boot = 0; |
933 | u8 bm[NTFS_BUF_SIZE]; |
934 | |
935 | if (!opt.infombonly) |
936 | printf("Accounting clusters ...\n"); |
937 | |
938 | pos = 0; |
939 | while (1) { |
940 | count = ntfs_attr_pread(vol->lcnbmp_na, pos, NTFS_BUF_SIZE, bm); |
941 | if (count == -1) |
942 | perr_exit("Couldn't get $Bitmap $DATA"); |
943 | |
944 | if (count == 0) { |
945 | if (a->size > pos) |
946 | err_exit("$Bitmap size is smaller than expected" |
947 | " (%lld != %lld)\n", |
948 | (long long)a->size, (long long)pos); |
949 | break; |
950 | } |
951 | |
952 | for (i = 0; i < count; i++, pos++) { |
953 | s64 cl; /* current cluster */ |
954 | |
955 | if (a->size <= pos) |
956 | goto done; |
957 | |
958 | if (a->bm[pos] == bm[i]) |
959 | continue; |
960 | |
961 | for (cl = pos * 8; cl < (pos + 1) * 8; cl++) { |
962 | char bit; |
963 | |
964 | bit = ntfs_bit_get(a->bm, cl); |
965 | if (bit == ntfs_bit_get(bm, i * 8 + cl % 8)) |
966 | continue; |
967 | |
968 | if (!mismatch && !bit && !backup_boot && |
969 | cl == vol->nr_clusters / 2) { |
970 | /* FIXME: call also boot sector check */ |
971 | backup_boot = 1; |
972 | printf("Found backup boot sector in " |
973 | "the middle of the volume.\n"); |
974 | continue; |
975 | } |
976 | |
977 | if (++mismatch > 10 && !opt.verbose) |
978 | continue; |
979 | |
980 | printf("Cluster accounting failed at %lld " |
981 | "(0x%llx): %s cluster in " |
982 | "$Bitmap\n", (long long)cl, |
983 | (unsigned long long)cl, |
984 | bit ? "missing" : "extra"); |
985 | } |
986 | } |
987 | } |
988 | done: |
989 | if (mismatch) { |
990 | printf("Filesystem check failed! Totally %d cluster " |
991 | "accounting mismatches.\n", mismatch); |
992 | err_printf("%s", corrupt_volume_msg); |
993 | exit(1); |
994 | } |
995 | } |
996 | |
997 | /** |
998 | * progress_init |
999 | * |
1000 | * Create and scale our progress bar. |
1001 | */ |
1002 | static void progress_init(struct progress_bar *p, u64 start, u64 stop, int flags) |
1003 | { |
1004 | p->start = start; |
1005 | p->stop = stop; |
1006 | p->unit = 100.0 / (stop - start); |
1007 | p->resolution = 100; |
1008 | p->flags = flags; |
1009 | } |
1010 | |
1011 | /** |
1012 | * progress_update |
1013 | * |
1014 | * Update the progress bar and tell the user. |
1015 | */ |
1016 | static void progress_update(struct progress_bar *p, u64 current) |
1017 | { |
1018 | float percent; |
1019 | |
1020 | if (!(p->flags & NTFS_PROGBAR)) |
1021 | return; |
1022 | if (p->flags & NTFS_PROGBAR_SUPPRESS) |
1023 | return; |
1024 | |
1025 | /* WARNING: don't modify the texts, external tools grep for them */ |
1026 | percent = p->unit * current; |
1027 | if (current != p->stop) { |
1028 | if ((current - p->start) % p->resolution) |
1029 | return; |
1030 | printf("%6.2f percent completed\r", percent); |
1031 | } else |
1032 | printf("100.00 percent completed\n"); |
1033 | fflush(stdout); |
1034 | } |
1035 | |
1036 | static int inode_close(ntfs_inode *ni) |
1037 | { |
1038 | if (ntfs_inode_close(ni)) { |
1039 | perr_printf("ntfs_inode_close for inode %llu", |
1040 | (unsigned long long)ni->mft_no); |
1041 | return -1; |
1042 | } |
1043 | return 0; |
1044 | } |
1045 | |
1046 | /** |
1047 | * walk_inodes |
1048 | * |
1049 | * Read each record in the MFT, skipping the unused ones, and build up a bitmap |
1050 | * from all the non-resident attributes. |
1051 | */ |
1052 | static int build_allocation_bitmap(ntfs_volume *vol, ntfsck_t *fsck) |
1053 | { |
1054 | s64 nr_mft_records, inode = 0; |
1055 | ntfs_inode *ni; |
1056 | struct progress_bar progress; |
1057 | int pb_flags = 0; /* progress bar flags */ |
1058 | |
1059 | /* WARNING: don't modify the text, external tools grep for it */ |
1060 | if (!opt.infombonly) |
1061 | printf("Checking filesystem consistency ...\n"); |
1062 | |
1063 | if (fsck->flags & NTFSCK_PROGBAR) |
1064 | pb_flags |= NTFS_PROGBAR; |
1065 | |
1066 | nr_mft_records = vol->mft_na->initialized_size >> |
1067 | vol->mft_record_size_bits; |
1068 | |
1069 | progress_init(&progress, inode, nr_mft_records - 1, pb_flags); |
1070 | |
1071 | for (; inode < nr_mft_records; inode++) { |
1072 | if (!opt.infombonly) |
1073 | progress_update(&progress, inode); |
1074 | |
1075 | if ((ni = ntfs_inode_open(vol, (MFT_REF)inode)) == NULL) { |
1076 | /* FIXME: continue only if it make sense, e.g. |
1077 | MFT record not in use based on $MFT bitmap */ |
1078 | if (errno == EIO || errno == ENOENT) |
1079 | continue; |
1080 | perr_printf("Reading inode %lld failed", |
1081 | (long long)inode); |
1082 | return -1; |
1083 | } |
1084 | |
1085 | if (ni->mrec->base_mft_record) |
1086 | goto close_inode; |
1087 | |
1088 | fsck->ni = ni; |
1089 | if (walk_attributes(vol, fsck) != 0) { |
1090 | inode_close(ni); |
1091 | return -1; |
1092 | } |
1093 | close_inode: |
1094 | if (inode_close(ni) != 0) |
1095 | return -1; |
1096 | } |
1097 | return 0; |
1098 | } |
1099 | |
1100 | static void build_resize_constraints(ntfs_resize_t *resize) |
1101 | { |
1102 | s64 i; |
1103 | runlist *rl; |
1104 | |
1105 | if (!resize->ctx->attr->non_resident) |
1106 | return; |
1107 | |
1108 | if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, |
1109 | resize->ctx->attr, NULL))) |
1110 | perr_exit("ntfs_decompress_mapping_pairs"); |
1111 | |
1112 | for (i = 0; rl[i].length; i++) { |
1113 | /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ |
1114 | if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) |
1115 | continue; |
1116 | |
1117 | collect_resize_constraints(resize, rl + i); |
1118 | if (resize->shrink) |
1119 | collect_relocation_info(resize, rl + i); |
1120 | } |
1121 | free(rl); |
1122 | } |
1123 | |
1124 | static void resize_constraints_by_attributes(ntfs_resize_t *resize) |
1125 | { |
1126 | if (!(resize->ctx = attr_get_search_ctx(resize->ni, NULL))) |
1127 | exit(1); |
1128 | |
1129 | while (!ntfs_attrs_walk(resize->ctx)) { |
1130 | if (resize->ctx->attr->type == AT_END) |
1131 | break; |
1132 | build_resize_constraints(resize); |
1133 | } |
1134 | |
1135 | ntfs_attr_put_search_ctx(resize->ctx); |
1136 | } |
1137 | |
1138 | static void set_resize_constraints(ntfs_resize_t *resize) |
1139 | { |
1140 | s64 nr_mft_records, inode; |
1141 | ntfs_inode *ni; |
1142 | |
1143 | if (!opt.infombonly) |
1144 | printf("Collecting resizing constraints ...\n"); |
1145 | |
1146 | nr_mft_records = resize->vol->mft_na->initialized_size >> |
1147 | resize->vol->mft_record_size_bits; |
1148 | |
1149 | for (inode = 0; inode < nr_mft_records; inode++) { |
1150 | |
1151 | ni = ntfs_inode_open(resize->vol, (MFT_REF)inode); |
1152 | if (ni == NULL) { |
1153 | if (errno == EIO || errno == ENOENT) |
1154 | continue; |
1155 | perr_exit("Reading inode %lld failed", |
1156 | (long long)inode); |
1157 | } |
1158 | |
1159 | if (ni->mrec->base_mft_record) |
1160 | goto close_inode; |
1161 | |
1162 | resize->ni = ni; |
1163 | resize_constraints_by_attributes(resize); |
1164 | close_inode: |
1165 | if (inode_close(ni) != 0) |
1166 | exit(1); |
1167 | } |
1168 | } |
1169 | |
1170 | static void rl_fixup(runlist **rl) |
1171 | { |
1172 | runlist *tmp = *rl; |
1173 | |
1174 | if (tmp->lcn == LCN_RL_NOT_MAPPED) { |
1175 | s64 unmapped_len = tmp->length; |
1176 | |
1177 | ntfs_log_verbose("Skip unmapped run at the beginning ...\n"); |
1178 | |
1179 | if (!tmp->length) |
1180 | err_exit("Empty unmapped runlist! Please report!\n"); |
1181 | (*rl)++; |
1182 | for (tmp = *rl; tmp->length; tmp++) |
1183 | tmp->vcn -= unmapped_len; |
1184 | } |
1185 | |
1186 | for (tmp = *rl; tmp->length; tmp++) { |
1187 | if (tmp->lcn == LCN_RL_NOT_MAPPED) { |
1188 | ntfs_log_verbose("Skip unmapped run at the end ...\n"); |
1189 | |
1190 | if (tmp[1].length) |
1191 | err_exit("Unmapped runlist in the middle! " |
1192 | "Please report!\n"); |
1193 | tmp->lcn = LCN_ENOENT; |
1194 | tmp->length = 0; |
1195 | } |
1196 | } |
1197 | } |
1198 | |
1199 | /* |
1200 | * Plug a replacement (partial) runlist into full runlist |
1201 | * |
1202 | * Returns 0 if successful |
1203 | * -1 if failed |
1204 | */ |
1205 | |
1206 | static int replace_runlist(ntfs_attr *na, const runlist_element *reprl, |
1207 | VCN lowest_vcn) |
1208 | { |
1209 | const runlist_element *prep; |
1210 | const runlist_element *pold; |
1211 | runlist_element *pnew; |
1212 | runlist_element *newrl; |
1213 | VCN nextvcn; |
1214 | s32 oldcnt, newcnt; |
1215 | s32 newsize; |
1216 | int r; |
1217 | |
1218 | r = -1; /* default return */ |
1219 | /* allocate a new runlist able to hold both */ |
1220 | oldcnt = 0; |
1221 | while (na->rl[oldcnt].length) |
1222 | oldcnt++; |
1223 | newcnt = 0; |
1224 | while (reprl[newcnt].length) |
1225 | newcnt++; |
1226 | newsize = ((oldcnt + newcnt)*sizeof(runlist_element) + 4095) & -4096; |
1227 | newrl = (runlist_element*)malloc(newsize); |
1228 | if (newrl) { |
1229 | /* copy old runs until reaching replaced ones */ |
1230 | pnew = newrl; |
1231 | pold = na->rl; |
1232 | while (pold->length |
1233 | && ((pold->vcn + pold->length) |
1234 | <= (reprl[0].vcn + lowest_vcn))) { |
1235 | *pnew = *pold; |
1236 | pnew++; |
1237 | pold++; |
1238 | } |
1239 | /* split a possible old run partially overlapped */ |
1240 | if (pold->length |
1241 | && (pold->vcn < (reprl[0].vcn + lowest_vcn))) { |
1242 | pnew->vcn = pold->vcn; |
1243 | pnew->lcn = pold->lcn; |
1244 | pnew->length = reprl[0].vcn + lowest_vcn - pold->vcn; |
1245 | pnew++; |
1246 | } |
1247 | /* copy new runs */ |
1248 | prep = reprl; |
1249 | nextvcn = prep->vcn + lowest_vcn; |
1250 | while (prep->length) { |
1251 | pnew->vcn = prep->vcn + lowest_vcn; |
1252 | pnew->lcn = prep->lcn; |
1253 | pnew->length = prep->length; |
1254 | nextvcn = pnew->vcn + pnew->length; |
1255 | pnew++; |
1256 | prep++; |
1257 | } |
1258 | /* locate the first fully replaced old run */ |
1259 | while (pold->length |
1260 | && ((pold->vcn + pold->length) <= nextvcn)) { |
1261 | pold++; |
1262 | } |
1263 | /* split a possible old run partially overlapped */ |
1264 | if (pold->length |
1265 | && (pold->vcn < nextvcn)) { |
1266 | pnew->vcn = nextvcn; |
1267 | pnew->lcn = pold->lcn + nextvcn - pold->vcn; |
1268 | pnew->length = pold->length - nextvcn + pold->vcn; |
1269 | pnew++; |
1270 | } |
1271 | /* copy old runs beyond replaced ones */ |
1272 | while (pold->length) { |
1273 | *pnew = *pold; |
1274 | pnew++; |
1275 | pold++; |
1276 | } |
1277 | /* the terminator is same as the old one */ |
1278 | *pnew = *pold; |
1279 | /* deallocate the old runlist and replace */ |
1280 | free(na->rl); |
1281 | na->rl = newrl; |
1282 | r = 0; |
1283 | } |
1284 | return (r); |
1285 | } |
1286 | |
1287 | /* |
1288 | * Expand the new runlist in new extent(s) |
1289 | * |
1290 | * This implies allocating inode extents and, generally, creating |
1291 | * an attribute list and allocating clusters for the list, and |
1292 | * shuffle the existing attributes accordingly. |
1293 | * |
1294 | * Sometimes the runlist being reallocated is within an extent, |
1295 | * so we have a partial runlist to plug into an existing one |
1296 | * whose other parts have already been processed or will have |
1297 | * to be processed later, and we must not interfere with the |
1298 | * processing of these parts. |
1299 | * |
1300 | * This cannot be done on the runlist part stored in a single |
1301 | * extent, it has to be done globally for the file. |
1302 | * |
1303 | * We use the standard library functions, so we must wait until |
1304 | * the new global bitmap and the new MFT bitmap are saved to |
1305 | * disk and usable for the allocation of a new extent and creation |
1306 | * of an attribute list. |
1307 | * |
1308 | * Aborts if something goes wrong. There should be no data damage, |
1309 | * because the old runlist is still in use and the bootsector has |
1310 | * not been updated yet, so the initial clusters can be accessed. |
1311 | */ |
1312 | |
1313 | static void expand_attribute_runlist(ntfs_volume *vol, struct DELAYED *delayed) |
1314 | { |
1315 | ntfs_inode *ni; |
1316 | ntfs_attr *na; |
1317 | ATTR_TYPES type; |
1318 | MFT_REF mref; |
1319 | runlist_element *rl; |
1320 | |
1321 | /* open the inode */ |
1322 | mref = delayed->mref; |
1323 | #ifndef BAN_NEW_TEXT |
1324 | ntfs_log_verbose("Processing a delayed update for inode %lld\n", |
1325 | (long long)mref); |
1326 | #endif |
1327 | type = delayed->type; |
1328 | rl = delayed->rl; |
1329 | ni = ntfs_inode_open(vol,mref); |
1330 | if (ni) { |
1331 | na = ntfs_attr_open(ni, type, |
1332 | delayed->attr_name, delayed->name_len); |
1333 | if (na) { |
1334 | if (!ntfs_attr_map_whole_runlist(na)) { |
1335 | if (replace_runlist(na,rl,delayed->lowest_vcn) |
1336 | || ntfs_attr_update_mapping_pairs(na,0)) |
1337 | perr_exit("Could not update runlist " |
1338 | "for attribute 0x%lx in inode %lld", |
1339 | (long)le32_to_cpu(type),(long long)mref); |
1340 | } else |
1341 | perr_exit("Could not map attribute 0x%lx in inode %lld", |
1342 | (long)le32_to_cpu(type),(long long)mref); |
1343 | ntfs_attr_close(na); |
1344 | } else |
1345 | perr_exit("Could not open attribute 0x%lx in inode %lld", |
1346 | (long)le32_to_cpu(type),(long long)mref); |
1347 | ntfs_inode_mark_dirty(ni); |
1348 | if (ntfs_inode_close(ni)) |
1349 | perr_exit("Failed to close inode %lld through the library", |
1350 | (long long)mref); |
1351 | } else |
1352 | perr_exit("Could not open inode %lld through the library", |
1353 | (long long)mref); |
1354 | } |
1355 | |
1356 | /* |
1357 | * Process delayed runlist updates |
1358 | */ |
1359 | |
1360 | static void delayed_updates(ntfs_resize_t *resize) |
1361 | { |
1362 | struct DELAYED *delayed; |
1363 | |
1364 | while (resize->delayed_runlists) { |
1365 | delayed = resize->delayed_runlists; |
1366 | expand_attribute_runlist(resize->vol, delayed); |
1367 | resize->delayed_runlists = resize->delayed_runlists->next; |
1368 | if (delayed->attr_name) |
1369 | free(delayed->attr_name); |
1370 | free(delayed->head_rl); |
1371 | free(delayed); |
1372 | } |
1373 | } |
1374 | |
1375 | /* |
1376 | * Queue a runlist replacement for later update |
1377 | * |
1378 | * Store the attribute identification relative to base inode |
1379 | */ |
1380 | |
1381 | static void replace_later(ntfs_resize_t *resize, runlist *rl, runlist *head_rl) |
1382 | { |
1383 | struct DELAYED *delayed; |
1384 | ATTR_RECORD *a; |
1385 | MFT_REF mref; |
1386 | leMFT_REF lemref; |
1387 | int name_len; |
1388 | ntfschar *attr_name; |
1389 | |
1390 | /* save the attribute parameters, to be able to find it later */ |
1391 | a = resize->ctx->attr; |
1392 | name_len = a->name_length; |
1393 | attr_name = (ntfschar*)NULL; |
1394 | if (name_len) { |
1395 | attr_name = (ntfschar*)ntfs_malloc(name_len*sizeof(ntfschar)); |
1396 | if (attr_name) |
1397 | memcpy(attr_name,(u8*)a + le16_to_cpu(a->name_offset), |
1398 | name_len*sizeof(ntfschar)); |
1399 | } |
1400 | delayed = (struct DELAYED*)ntfs_malloc(sizeof(struct DELAYED)); |
1401 | if (delayed && (attr_name || !name_len)) { |
1402 | lemref = resize->ctx->mrec->base_mft_record; |
1403 | if (lemref) |
1404 | mref = le64_to_cpu(lemref); |
1405 | else |
1406 | mref = resize->mref; |
1407 | delayed->mref = MREF(mref); |
1408 | delayed->type = a->type; |
1409 | delayed->attr_name = attr_name; |
1410 | delayed->name_len = name_len; |
1411 | delayed->lowest_vcn = le64_to_cpu(a->lowest_vcn); |
1412 | delayed->rl = rl; |
1413 | delayed->head_rl = head_rl; |
1414 | delayed->next = resize->delayed_runlists; |
1415 | resize->delayed_runlists = delayed; |
1416 | } else |
1417 | perr_exit("Could not store delayed update data"); |
1418 | } |
1419 | |
1420 | /* |
1421 | * Replace the runlist in an attribute |
1422 | * |
1423 | * This sometimes requires expanding the runlist into another extent, |
1424 | * which has to be done globally on the attribute. Is so, the action |
1425 | * is put in a delay queue, and the caller must not free the runlist. |
1426 | * |
1427 | * Returns 0 if the replacement could be done |
1428 | * 1 when it has been put in the delay queue. |
1429 | */ |
1430 | |
1431 | static int replace_attribute_runlist(ntfs_resize_t *resize, runlist *rl) |
1432 | { |
1433 | int mp_size, l; |
1434 | int must_delay; |
1435 | void *mp; |
1436 | runlist *head_rl; |
1437 | ntfs_volume *vol; |
1438 | ntfs_attr_search_ctx *ctx; |
1439 | ATTR_RECORD *a; |
1440 | |
1441 | vol = resize->vol; |
1442 | ctx = resize->ctx; |
1443 | a = ctx->attr; |
1444 | head_rl = rl; |
1445 | rl_fixup(&rl); |
1446 | |
1447 | if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX)) == -1) |
1448 | perr_exit("ntfs_get_size_for_mapping_pairs"); |
1449 | |
1450 | if (a->name_length) { |
1451 | u16 name_offs = le16_to_cpu(a->name_offset); |
1452 | u16 mp_offs = le16_to_cpu(a->mapping_pairs_offset); |
1453 | |
1454 | if (name_offs >= mp_offs) |
1455 | err_exit("Attribute name is after mapping pairs! " |
1456 | "Please report!\n"); |
1457 | } |
1458 | |
1459 | /* CHECKME: don't trust mapping_pairs is always the last item in the |
1460 | attribute, instead check for the real size/space */ |
1461 | l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->mapping_pairs_offset); |
1462 | must_delay = 0; |
1463 | if (mp_size > l) { |
1464 | s32 remains_size; |
1465 | char *next_attr; |
1466 | |
1467 | ntfs_log_verbose("Enlarging attribute header ...\n"); |
1468 | |
1469 | mp_size = (mp_size + 7) & ~7; |
1470 | |
1471 | ntfs_log_verbose("Old mp size : %d\n", l); |
1472 | ntfs_log_verbose("New mp size : %d\n", mp_size); |
1473 | ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) |
1474 | le32_to_cpu(ctx->mrec->bytes_in_use)); |
1475 | |
1476 | next_attr = (char *)a + le32_to_cpu(a->length); |
1477 | l = mp_size - l; |
1478 | |
1479 | ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) |
1480 | le32_to_cpu(ctx->mrec->bytes_in_use)); |
1481 | ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int) |
1482 | le32_to_cpu(ctx->mrec->bytes_allocated)); |
1483 | |
1484 | remains_size = le32_to_cpu(ctx->mrec->bytes_in_use); |
1485 | remains_size -= (next_attr - (char *)ctx->mrec); |
1486 | |
1487 | ntfs_log_verbose("increase : %d\n", l); |
1488 | ntfs_log_verbose("shift : %lld\n", |
1489 | (long long)remains_size); |
1490 | if (le32_to_cpu(ctx->mrec->bytes_in_use) + l > |
1491 | le32_to_cpu(ctx->mrec->bytes_allocated)) { |
1492 | #ifndef BAN_NEW_TEXT |
1493 | ntfs_log_verbose("Queuing expansion for later processing\n"); |
1494 | #endif |
1495 | must_delay = 1; |
1496 | replace_later(resize,rl,head_rl); |
1497 | } else { |
1498 | memmove(next_attr + l, next_attr, remains_size); |
1499 | ctx->mrec->bytes_in_use = cpu_to_le32(l + |
1500 | le32_to_cpu(ctx->mrec->bytes_in_use)); |
1501 | a->length = cpu_to_le32(le32_to_cpu(a->length) + l); |
1502 | } |
1503 | } |
1504 | |
1505 | if (!must_delay) { |
1506 | mp = ntfs_calloc(mp_size); |
1507 | if (!mp) |
1508 | perr_exit("ntfsc_calloc couldn't get memory"); |
1509 | |
1510 | if (ntfs_mapping_pairs_build(vol, (u8*)mp, mp_size, rl, 0, NULL)) |
1511 | perr_exit("ntfs_mapping_pairs_build"); |
1512 | |
1513 | memmove((u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp, mp_size); |
1514 | |
1515 | free(mp); |
1516 | } |
1517 | return (must_delay); |
1518 | } |
1519 | |
1520 | static void set_bitmap_range(struct bitmap *bm, s64 pos, s64 length, u8 bit) |
1521 | { |
1522 | while (length--) |
1523 | ntfs_bit_set(bm->bm, pos++, bit); |
1524 | } |
1525 | |
1526 | static void set_bitmap_clusters(struct bitmap *bm, runlist *rl, u8 bit) |
1527 | { |
1528 | for (; rl->length; rl++) |
1529 | set_bitmap_range(bm, rl->lcn, rl->length, bit); |
1530 | } |
1531 | |
1532 | static void release_bitmap_clusters(struct bitmap *bm, runlist *rl) |
1533 | { |
1534 | max_free_cluster_range = 0; |
1535 | set_bitmap_clusters(bm, rl, 0); |
1536 | } |
1537 | |
1538 | static void set_max_free_zone(s64 length, s64 end, runlist_element *rle) |
1539 | { |
1540 | if (length > rle->length) { |
1541 | rle->lcn = end - length; |
1542 | rle->length = length; |
1543 | } |
1544 | } |
1545 | |
1546 | static int find_free_cluster(struct bitmap *bm, |
1547 | runlist_element *rle, |
1548 | s64 nr_vol_clusters, |
1549 | int hint) |
1550 | { |
1551 | /* FIXME: get rid of this 'static' variable */ |
1552 | static s64 pos = 0; |
1553 | s64 i, items = rle->length; |
1554 | s64 free_zone = 0; |
1555 | |
1556 | if (pos >= nr_vol_clusters) |
1557 | pos = 0; |
1558 | if (!max_free_cluster_range) |
1559 | max_free_cluster_range = nr_vol_clusters; |
1560 | rle->lcn = rle->length = 0; |
1561 | if (hint) |
1562 | pos = nr_vol_clusters / 2; |
1563 | i = pos; |
1564 | |
1565 | do { |
1566 | if (!ntfs_bit_get(bm->bm, i)) { |
1567 | if (++free_zone == items) { |
1568 | set_max_free_zone(free_zone, i + 1, rle); |
1569 | break; |
1570 | } |
1571 | } else { |
1572 | set_max_free_zone(free_zone, i, rle); |
1573 | free_zone = 0; |
1574 | } |
1575 | if (++i == nr_vol_clusters) { |
1576 | set_max_free_zone(free_zone, i, rle); |
1577 | i = free_zone = 0; |
1578 | } |
1579 | if (rle->length == max_free_cluster_range) |
1580 | break; |
1581 | } while (i != pos); |
1582 | |
1583 | if (i) |
1584 | set_max_free_zone(free_zone, i, rle); |
1585 | |
1586 | if (!rle->lcn) { |
1587 | errno = ENOSPC; |
1588 | return -1; |
1589 | } |
1590 | if (rle->length < items && rle->length < max_free_cluster_range) { |
1591 | max_free_cluster_range = rle->length; |
1592 | ntfs_log_verbose("Max free range: %7lld \n", |
1593 | (long long)max_free_cluster_range); |
1594 | } |
1595 | pos = rle->lcn + items; |
1596 | if (pos == nr_vol_clusters) |
1597 | pos = 0; |
1598 | |
1599 | set_bitmap_range(bm, rle->lcn, rle->length, 1); |
1600 | return 0; |
1601 | } |
1602 | |
1603 | static runlist *alloc_cluster(struct bitmap *bm, |
1604 | s64 items, |
1605 | s64 nr_vol_clusters, |
1606 | int hint) |
1607 | { |
1608 | runlist_element rle; |
1609 | runlist *rl = NULL; |
1610 | int rl_size, runs = 0; |
1611 | s64 vcn = 0; |
1612 | |
1613 | if (items <= 0) { |
1614 | errno = EINVAL; |
1615 | return NULL; |
1616 | } |
1617 | |
1618 | while (items > 0) { |
1619 | |
1620 | if (runs) |
1621 | hint = 0; |
1622 | rle.length = items; |
1623 | if (find_free_cluster(bm, &rle, nr_vol_clusters, hint) == -1) |
1624 | return NULL; |
1625 | |
1626 | rl_size = (runs + 2) * sizeof(runlist_element); |
1627 | if (!(rl = (runlist *)realloc(rl, rl_size))) |
1628 | return NULL; |
1629 | |
1630 | rl_set(rl + runs, vcn, rle.lcn, rle.length); |
1631 | |
1632 | vcn += rle.length; |
1633 | items -= rle.length; |
1634 | runs++; |
1635 | } |
1636 | |
1637 | rl_set(rl + runs, vcn, -1LL, 0LL); |
1638 | |
1639 | if (runs > 1) { |
1640 | ntfs_log_verbose("Multi-run allocation: \n"); |
1641 | dump_runlist(rl); |
1642 | } |
1643 | return rl; |
1644 | } |
1645 | |
1646 | static int read_all(struct ntfs_device *dev, void *buf, int count) |
1647 | { |
1648 | int i; |
1649 | |
1650 | while (count > 0) { |
1651 | |
1652 | i = count; |
1653 | if (!NDevReadOnly(dev)) |
1654 | i = dev->d_ops->read(dev, buf, count); |
1655 | |
1656 | if (i < 0) { |
1657 | if (errno != EAGAIN && errno != EINTR) |
1658 | return -1; |
1659 | } else if (i > 0) { |
1660 | count -= i; |
1661 | buf = i + (char *)buf; |
1662 | } else |
1663 | err_exit("Unexpected end of file!\n"); |
1664 | } |
1665 | return 0; |
1666 | } |
1667 | |
1668 | static int write_all(struct ntfs_device *dev, void *buf, int count) |
1669 | { |
1670 | int i; |
1671 | |
1672 | while (count > 0) { |
1673 | |
1674 | i = count; |
1675 | if (!NDevReadOnly(dev)) |
1676 | i = dev->d_ops->write(dev, buf, count); |
1677 | |
1678 | if (i < 0) { |
1679 | if (errno != EAGAIN && errno != EINTR) |
1680 | return -1; |
1681 | } else { |
1682 | count -= i; |
1683 | buf = i + (char *)buf; |
1684 | } |
1685 | } |
1686 | return 0; |
1687 | } |
1688 | |
1689 | /** |
1690 | * write_mft_record |
1691 | * |
1692 | * Write an MFT Record back to the disk. If the read-only command line option |
1693 | * was given, this function will do nothing. |
1694 | */ |
1695 | static int write_mft_record(ntfs_volume *v, const MFT_REF mref, MFT_RECORD *buf) |
1696 | { |
1697 | if (ntfs_mft_record_write(v, mref, buf)) |
1698 | perr_exit("ntfs_mft_record_write"); |
1699 | |
1700 | // if (v->dev->d_ops->sync(v->dev) == -1) |
1701 | // perr_exit("Failed to sync device"); |
1702 | |
1703 | return 0; |
1704 | } |
1705 | |
1706 | static void lseek_to_cluster(ntfs_volume *vol, s64 lcn) |
1707 | { |
1708 | off_t pos; |
1709 | |
1710 | pos = (off_t)(lcn * vol->cluster_size); |
1711 | |
1712 | if (vol->dev->d_ops->seek(vol->dev, pos, SEEK_SET) == (off_t)-1) |
1713 | perr_exit("seek failed to position %lld", (long long)lcn); |
1714 | } |
1715 | |
1716 | static void copy_clusters(ntfs_resize_t *resize, s64 dest, s64 src, s64 len) |
1717 | { |
1718 | s64 i; |
1719 | char buff[NTFS_MAX_CLUSTER_SIZE]; /* overflow checked at mount time */ |
1720 | ntfs_volume *vol = resize->vol; |
1721 | |
1722 | for (i = 0; i < len; i++) { |
1723 | |
1724 | lseek_to_cluster(vol, src + i); |
1725 | |
1726 | if (read_all(vol->dev, buff, vol->cluster_size) == -1) { |
1727 | perr_printf("Failed to read from the disk"); |
1728 | if (errno == EIO) |
1729 | printf("%s", bad_sectors_warning_msg); |
1730 | exit(1); |
1731 | } |
1732 | |
1733 | lseek_to_cluster(vol, dest + i); |
1734 | |
1735 | if (write_all(vol->dev, buff, vol->cluster_size) == -1) { |
1736 | perr_printf("Failed to write to the disk"); |
1737 | if (errno == EIO) |
1738 | printf("%s", bad_sectors_warning_msg); |
1739 | exit(1); |
1740 | } |
1741 | |
1742 | resize->relocations++; |
1743 | progress_update(&resize->progress, resize->relocations); |
1744 | } |
1745 | } |
1746 | |
1747 | static void relocate_clusters(ntfs_resize_t *r, runlist *dest_rl, s64 src_lcn) |
1748 | { |
1749 | /* collect_shrink_constraints() ensured $MFTMir DATA is one run */ |
1750 | if (r->mref == FILE_MFTMirr && r->ctx->attr->type == AT_DATA) { |
1751 | if (!r->mftmir_old) { |
1752 | r->mftmir_rl.lcn = dest_rl->lcn; |
1753 | r->mftmir_rl.length = dest_rl->length; |
1754 | r->mftmir_old = src_lcn; |
1755 | } else |
1756 | err_exit("Multi-run $MFTMirr. Please report!\n"); |
1757 | } |
1758 | |
1759 | for (; dest_rl->length; src_lcn += dest_rl->length, dest_rl++) |
1760 | copy_clusters(r, dest_rl->lcn, src_lcn, dest_rl->length); |
1761 | } |
1762 | |
1763 | static void rl_split_run(runlist **rl, int run, s64 pos) |
1764 | { |
1765 | runlist *rl_new, *rle_new, *rle; |
1766 | int items, new_size, size_head, size_tail; |
1767 | s64 len_head, len_tail; |
1768 | |
1769 | items = rl_items(*rl); |
1770 | new_size = (items + 1) * sizeof(runlist_element); |
1771 | size_head = run * sizeof(runlist_element); |
1772 | size_tail = (items - run - 1) * sizeof(runlist_element); |
1773 | |
1774 | rl_new = ntfs_malloc(new_size); |
1775 | if (!rl_new) |
1776 | perr_exit("ntfs_malloc"); |
1777 | |
1778 | rle_new = rl_new + run; |
1779 | rle = *rl + run; |
1780 | |
1781 | memmove(rl_new, *rl, size_head); |
1782 | memmove(rle_new + 2, rle + 1, size_tail); |
1783 | |
1784 | len_tail = rle->length - (pos - rle->lcn); |
1785 | len_head = rle->length - len_tail; |
1786 | |
1787 | rl_set(rle_new, rle->vcn, rle->lcn, len_head); |
1788 | rl_set(rle_new + 1, rle->vcn + len_head, rle->lcn + len_head, len_tail); |
1789 | |
1790 | ntfs_log_verbose("Splitting run at cluster %lld:\n", (long long)pos); |
1791 | dump_run(rle); dump_run(rle_new); dump_run(rle_new + 1); |
1792 | |
1793 | free(*rl); |
1794 | *rl = rl_new; |
1795 | } |
1796 | |
1797 | static void rl_insert_at_run(runlist **rl, int run, runlist *ins) |
1798 | { |
1799 | int items, ins_items; |
1800 | int new_size, size_tail; |
1801 | runlist *rle; |
1802 | s64 vcn; |
1803 | |
1804 | items = rl_items(*rl); |
1805 | ins_items = rl_items(ins) - 1; |
1806 | new_size = ((items - 1) + ins_items) * sizeof(runlist_element); |
1807 | size_tail = (items - run - 1) * sizeof(runlist_element); |
1808 | |
1809 | if (!(*rl = (runlist *)realloc(*rl, new_size))) |
1810 | perr_exit("realloc"); |
1811 | |
1812 | rle = *rl + run; |
1813 | |
1814 | memmove(rle + ins_items, rle + 1, size_tail); |
1815 | |
1816 | for (vcn = rle->vcn; ins->length; rle++, vcn += ins->length, ins++) { |
1817 | rl_set(rle, vcn, ins->lcn, ins->length); |
1818 | // dump_run(rle); |
1819 | } |
1820 | |
1821 | return; |
1822 | |
1823 | /* FIXME: fast path if ins_items = 1 */ |
1824 | // (*rl + run)->lcn = ins->lcn; |
1825 | } |
1826 | |
1827 | static void relocate_run(ntfs_resize_t *resize, runlist **rl, int run) |
1828 | { |
1829 | s64 lcn, lcn_length; |
1830 | s64 new_vol_size; /* (last LCN on the volume) + 1 */ |
1831 | runlist *relocate_rl; /* relocate runlist to relocate_rl */ |
1832 | int hint; |
1833 | |
1834 | lcn = (*rl + run)->lcn; |
1835 | lcn_length = (*rl + run)->length; |
1836 | new_vol_size = resize->new_volume_size; |
1837 | |
1838 | if (lcn + lcn_length <= new_vol_size) |
1839 | return; |
1840 | |
1841 | if (lcn < new_vol_size) { |
1842 | rl_split_run(rl, run, new_vol_size); |
1843 | return; |
1844 | } |
1845 | |
1846 | hint = (resize->mref == FILE_MFTMirr) ? 1 : 0; |
1847 | if ((resize->mref == FILE_MFT) |
1848 | && (resize->ctx->attr->type == AT_DATA) |
1849 | && !run |
1850 | && resize->new_mft_start) { |
1851 | relocate_rl = resize->new_mft_start; |
1852 | } else |
1853 | if (!(relocate_rl = alloc_cluster(&resize->lcn_bitmap, |
1854 | lcn_length, new_vol_size, hint))) |
1855 | perr_exit("Cluster allocation failed for %llu:%lld", |
1856 | (unsigned long long)resize->mref, |
1857 | (long long)lcn_length); |
1858 | |
1859 | /* FIXME: check $MFTMirr DATA isn't multi-run (or support it) */ |
1860 | ntfs_log_verbose("Relocate record %7llu:0x%x:%08lld:0x%08llx:0x%08llx " |
1861 | "--> 0x%08llx\n", (unsigned long long)resize->mref, |
1862 | (unsigned int)le32_to_cpu(resize->ctx->attr->type), |
1863 | (long long)lcn_length, |
1864 | (unsigned long long)(*rl + run)->vcn, |
1865 | (unsigned long long)lcn, |
1866 | (unsigned long long)relocate_rl->lcn); |
1867 | |
1868 | relocate_clusters(resize, relocate_rl, lcn); |
1869 | rl_insert_at_run(rl, run, relocate_rl); |
1870 | |
1871 | /* We don't release old clusters in the bitmap, that area isn't |
1872 | used by the allocator and will be truncated later on */ |
1873 | |
1874 | /* Do not free the relocated MFT start */ |
1875 | if ((resize->mref != FILE_MFT) |
1876 | || (resize->ctx->attr->type != AT_DATA) |
1877 | || run |
1878 | || !resize->new_mft_start) |
1879 | free(relocate_rl); |
1880 | |
1881 | resize->dirty_inode = DIRTY_ATTRIB; |
1882 | } |
1883 | |
1884 | static void relocate_attribute(ntfs_resize_t *resize) |
1885 | { |
1886 | ATTR_RECORD *a; |
1887 | runlist *rl; |
1888 | int i; |
1889 | |
1890 | a = resize->ctx->attr; |
1891 | |
1892 | if (!a->non_resident) |
1893 | return; |
1894 | |
1895 | if (!(rl = ntfs_mapping_pairs_decompress(resize->vol, a, NULL))) |
1896 | perr_exit("ntfs_decompress_mapping_pairs"); |
1897 | |
1898 | for (i = 0; rl[i].length; i++) { |
1899 | s64 lcn = rl[i].lcn; |
1900 | s64 lcn_length = rl[i].length; |
1901 | |
1902 | if (lcn == LCN_HOLE || lcn == LCN_RL_NOT_MAPPED) |
1903 | continue; |
1904 | |
1905 | /* FIXME: ntfs_mapping_pairs_decompress should return error */ |
1906 | if (lcn < 0 || lcn_length <= 0) |
1907 | err_exit("Corrupt runlist in MTF %llu attr %x LCN " |
1908 | "%llx length %llx\n", |
1909 | (unsigned long long)resize->mref, |
1910 | (unsigned int)le32_to_cpu(a->type), |
1911 | (long long)lcn, (long long)lcn_length); |
1912 | |
1913 | relocate_run(resize, &rl, i); |
1914 | } |
1915 | |
1916 | if (resize->dirty_inode == DIRTY_ATTRIB) { |
1917 | if (!replace_attribute_runlist(resize, rl)) |
1918 | free(rl); |
1919 | resize->dirty_inode = DIRTY_INODE; |
1920 | } else |
1921 | free(rl); |
1922 | } |
1923 | |
1924 | static int is_mftdata(ntfs_resize_t *resize) |
1925 | { |
1926 | /* |
1927 | * We must update the MFT own DATA record at the end of the second |
1928 | * step, because the old MFT must be kept available for processing |
1929 | * the other files. |
1930 | */ |
1931 | |
1932 | if (resize->ctx->attr->type != AT_DATA) |
1933 | return 0; |
1934 | |
1935 | if (resize->mref == 0) |
1936 | return 1; |
1937 | |
1938 | if (MREF_LE(resize->mrec->base_mft_record) == 0 && |
1939 | MSEQNO_LE(resize->mrec->base_mft_record) != 0) |
1940 | return 1; |
1941 | |
1942 | return 0; |
1943 | } |
1944 | |
1945 | static int handle_mftdata(ntfs_resize_t *resize, int do_mftdata) |
1946 | { |
1947 | ATTR_RECORD *attr = resize->ctx->attr; |
1948 | VCN highest_vcn, lowest_vcn; |
1949 | |
1950 | if (do_mftdata) { |
1951 | |
1952 | if (!is_mftdata(resize)) |
1953 | return 0; |
1954 | |
1955 | highest_vcn = sle64_to_cpu(attr->highest_vcn); |
1956 | lowest_vcn = sle64_to_cpu(attr->lowest_vcn); |
1957 | |
1958 | if (resize->mft_highest_vcn != highest_vcn) |
1959 | return 0; |
1960 | |
1961 | if (lowest_vcn == 0) |
1962 | resize->mft_highest_vcn = lowest_vcn; |
1963 | else |
1964 | resize->mft_highest_vcn = lowest_vcn - 1; |
1965 | |
1966 | } else if (is_mftdata(resize)) { |
1967 | |
1968 | highest_vcn = sle64_to_cpu(attr->highest_vcn); |
1969 | |
1970 | if (resize->mft_highest_vcn < highest_vcn) |
1971 | resize->mft_highest_vcn = highest_vcn; |
1972 | |
1973 | return 0; |
1974 | } |
1975 | |
1976 | return 1; |
1977 | } |
1978 | |
1979 | static void relocate_attributes(ntfs_resize_t *resize, int do_mftdata) |
1980 | { |
1981 | int ret; |
1982 | |
1983 | if (!(resize->ctx = attr_get_search_ctx(NULL, resize->mrec))) |
1984 | exit(1); |
1985 | |
1986 | while (!ntfs_attrs_walk(resize->ctx)) { |
1987 | if (resize->ctx->attr->type == AT_END) |
1988 | break; |
1989 | |
1990 | if (handle_mftdata(resize, do_mftdata) == 0) |
1991 | continue; |
1992 | |
1993 | ret = ntfs_inode_badclus_bad(resize->mref, resize->ctx->attr); |
1994 | if (ret == -1) |
1995 | perr_exit("Bad sector list check failed"); |
1996 | else if (ret == 1) |
1997 | continue; |
1998 | |
1999 | if (resize->mref == FILE_Bitmap && |
2000 | resize->ctx->attr->type == AT_DATA) |
2001 | continue; |
2002 | |
2003 | relocate_attribute(resize); |
2004 | } |
2005 | |
2006 | ntfs_attr_put_search_ctx(resize->ctx); |
2007 | } |
2008 | |
2009 | static void relocate_inode(ntfs_resize_t *resize, MFT_REF mref, int do_mftdata) |
2010 | { |
2011 | ntfs_volume *vol = resize->vol; |
2012 | |
2013 | if (ntfs_file_record_read(vol, mref, &resize->mrec, NULL)) { |
2014 | /* FIXME: continue only if it make sense, e.g. |
2015 | MFT record not in use based on $MFT bitmap */ |
2016 | if (errno == EIO || errno == ENOENT) |
2017 | return; |
2018 | perr_exit("ntfs_file_record_record"); |
2019 | } |
2020 | |
2021 | if (!(resize->mrec->flags & MFT_RECORD_IN_USE)) |
2022 | return; |
2023 | |
2024 | resize->mref = mref; |
2025 | resize->dirty_inode = DIRTY_NONE; |
2026 | |
2027 | relocate_attributes(resize, do_mftdata); |
2028 | |
2029 | // if (vol->dev->d_ops->sync(vol->dev) == -1) |
2030 | // perr_exit("Failed to sync device"); |
2031 | /* relocate MFT during second step, even if not dirty */ |
2032 | if ((mref == FILE_MFT) && do_mftdata && resize->new_mft_start) { |
2033 | s64 pos; |
2034 | |
2035 | /* write the MFT own record at its new location */ |
2036 | pos = (resize->new_mft_start->lcn |
2037 | << vol->cluster_size_bits) |
2038 | + (FILE_MFT << vol->mft_record_size_bits); |
2039 | if (!opt.ro_flag |
2040 | && (ntfs_mst_pwrite(vol->dev, pos, 1, |
2041 | vol->mft_record_size, resize->mrec) != 1)) |
2042 | perr_exit("Couldn't update MFT own record"); |
2043 | } else { |
2044 | if ((resize->dirty_inode == DIRTY_INODE) |
2045 | && write_mft_record(vol, mref, resize->mrec)) { |
2046 | perr_exit("Couldn't update record %llu", |
2047 | (unsigned long long)mref); |
2048 | } |
2049 | } |
2050 | } |
2051 | |
2052 | static void relocate_inodes(ntfs_resize_t *resize) |
2053 | { |
2054 | s64 nr_mft_records; |
2055 | MFT_REF mref; |
2056 | VCN highest_vcn; |
2057 | u64 length; |
2058 | |
2059 | printf("Relocating needed data ...\n"); |
2060 | |
2061 | progress_init(&resize->progress, 0, resize->relocations, resize->progress.flags); |
2062 | resize->relocations = 0; |
2063 | |
2064 | resize->mrec = ntfs_malloc(resize->vol->mft_record_size); |
2065 | if (!resize->mrec) |
2066 | perr_exit("ntfs_malloc failed"); |
2067 | |
2068 | nr_mft_records = resize->vol->mft_na->initialized_size >> |
2069 | resize->vol->mft_record_size_bits; |
2070 | |
2071 | /* |
2072 | * If we need to relocate the first run of the MFT DATA, |
2073 | * do it now, to have a better chance of getting at least |
2074 | * 16 records in the first chunk. This is mandatory to be |
2075 | * later able to read an MFT extent in record 15. |
2076 | * Should this fail, we can stop with no damage, the volume |
2077 | * is still in its initial state. |
2078 | */ |
2079 | if (!resize->vol->mft_na->rl) |
2080 | err_exit("Internal error : no runlist for $MFT\n"); |
2081 | |
2082 | if ((resize->vol->mft_na->rl->lcn + resize->vol->mft_na->rl->length) |
2083 | >= resize->new_volume_size) { |
2084 | /* |
2085 | * The length of the first run is normally found in |
2086 | * mft_na. However in some rare circumstance, this is |
2087 | * merged with the first run of an extent of MFT, |
2088 | * which implies there is a single run in the base record. |
2089 | * So we have to make sure not to overflow from the |
2090 | * runs present in the base extent. |
2091 | */ |
2092 | length = resize->vol->mft_na->rl->length; |
2093 | if (ntfs_file_record_read(resize->vol, FILE_MFT, |
2094 | &resize->mrec, NULL) |
2095 | || !(resize->ctx = attr_get_search_ctx(NULL, |
2096 | resize->mrec))) { |
2097 | err_exit("Could not read the base record of MFT\n"); |
2098 | } |
2099 | while (!ntfs_attrs_walk(resize->ctx) |
2100 | && (resize->ctx->attr->type != AT_DATA)) { } |
2101 | if (resize->ctx->attr->type == AT_DATA) { |
2102 | le64 high_le; |
2103 | |
2104 | high_le = resize->ctx->attr->highest_vcn; |
2105 | if (le64_to_cpu(high_le) < length) |
2106 | length = le64_to_cpu(high_le) + 1; |
2107 | } else { |
2108 | err_exit("Could not find the DATA of MFT\n"); |
2109 | } |
2110 | ntfs_attr_put_search_ctx(resize->ctx); |
2111 | resize->new_mft_start = alloc_cluster(&resize->lcn_bitmap, |
2112 | length, resize->new_volume_size, 0); |
2113 | if (!resize->new_mft_start |
2114 | || (((resize->new_mft_start->length |
2115 | << resize->vol->cluster_size_bits) |
2116 | >> resize->vol->mft_record_size_bits) < 16)) { |
2117 | err_exit("Could not allocate 16 records in" |
2118 | " the first MFT chunk\n"); |
2119 | } |
2120 | } |
2121 | |
2122 | for (mref = 0; mref < (MFT_REF)nr_mft_records; mref++) |
2123 | relocate_inode(resize, mref, 0); |
2124 | |
2125 | while (1) { |
2126 | highest_vcn = resize->mft_highest_vcn; |
2127 | mref = nr_mft_records; |
2128 | do { |
2129 | relocate_inode(resize, --mref, 1); |
2130 | if (resize->mft_highest_vcn == 0) |
2131 | goto done; |
2132 | } while (mref); |
2133 | |
2134 | if (highest_vcn == resize->mft_highest_vcn) |
2135 | err_exit("Sanity check failed! Highest_vcn = %lld. " |
2136 | "Please report!\n", (long long)highest_vcn); |
2137 | } |
2138 | done: |
2139 | free(resize->mrec); |
2140 | } |
2141 | |
2142 | static void print_hint(ntfs_volume *vol, const char *s, struct llcn_t llcn) |
2143 | { |
2144 | s64 runs_b, runs_mb; |
2145 | |
2146 | if (llcn.lcn == 0) |
2147 | return; |
2148 | |
2149 | runs_b = llcn.lcn * vol->cluster_size; |
2150 | runs_mb = rounded_up_division(runs_b, NTFS_MBYTE); |
2151 | printf("%-19s: %9lld MB %8lld\n", s, (long long)runs_mb, |
2152 | (long long)llcn.inode); |
2153 | } |
2154 | |
2155 | /** |
2156 | * advise_on_resize |
2157 | * |
2158 | * The metadata file $Bitmap has one bit for each cluster on disk. This has |
2159 | * already been read into lcn_bitmap. By looking for the last used cluster on |
2160 | * the disk, we can work out by how much we can shrink the volume. |
2161 | */ |
2162 | static void advise_on_resize(ntfs_resize_t *resize) |
2163 | { |
2164 | ntfs_volume *vol = resize->vol; |
2165 | |
2166 | if (opt.verbose) { |
2167 | printf("Estimating smallest shrunken size supported ...\n"); |
2168 | printf("File feature Last used at By inode\n"); |
2169 | print_hint(vol, "$MFT", resize->last_mft); |
2170 | print_hint(vol, "Multi-Record", resize->last_multi_mft); |
2171 | print_hint(vol, "$MFTMirr", resize->last_mftmir); |
2172 | print_hint(vol, "Compressed", resize->last_compressed); |
2173 | print_hint(vol, "Sparse", resize->last_sparse); |
2174 | print_hint(vol, "Ordinary", resize->last_lcn); |
2175 | } |
2176 | |
2177 | print_advise(vol, resize->last_unsupp); |
2178 | } |
2179 | |
2180 | static void rl_expand(runlist **rl, const VCN last_vcn) |
2181 | { |
2182 | int len; |
2183 | runlist *p = *rl; |
2184 | |
2185 | len = rl_items(p) - 1; |
2186 | if (len <= 0) |
2187 | err_exit("rl_expand: bad runlist length: %d\n", len); |
2188 | |
2189 | if (p[len].vcn > last_vcn) |
2190 | err_exit("rl_expand: length is already more than requested " |
2191 | "(%lld > %lld)\n", |
2192 | (long long)p[len].vcn, (long long)last_vcn); |
2193 | |
2194 | if (p[len - 1].lcn == LCN_HOLE) { |
2195 | |
2196 | p[len - 1].length += last_vcn - p[len].vcn; |
2197 | p[len].vcn = last_vcn; |
2198 | |
2199 | } else if (p[len - 1].lcn >= 0) { |
2200 | |
2201 | p = realloc(*rl, (++len + 1) * sizeof(runlist_element)); |
2202 | if (!p) |
2203 | perr_exit("rl_expand: realloc"); |
2204 | |
2205 | p[len - 1].lcn = LCN_HOLE; |
2206 | p[len - 1].length = last_vcn - p[len - 1].vcn; |
2207 | rl_set(p + len, last_vcn, LCN_ENOENT, 0LL); |
2208 | *rl = p; |
2209 | |
2210 | } else |
2211 | err_exit("rl_expand: bad LCN: %lld\n", |
2212 | (long long)p[len - 1].lcn); |
2213 | } |
2214 | |
2215 | static void rl_truncate(runlist **rl, const VCN last_vcn) |
2216 | { |
2217 | int len; |
2218 | VCN vcn; |
2219 | |
2220 | len = rl_items(*rl) - 1; |
2221 | if (len <= 0) |
2222 | err_exit("rl_truncate: bad runlist length: %d\n", len); |
2223 | |
2224 | vcn = (*rl)[len].vcn; |
2225 | |
2226 | if (vcn < last_vcn) |
2227 | rl_expand(rl, last_vcn); |
2228 | |
2229 | else if (vcn > last_vcn) |
2230 | if (ntfs_rl_truncate(rl, last_vcn) == -1) |
2231 | perr_exit("ntfs_rl_truncate"); |
2232 | } |
2233 | |
2234 | /** |
2235 | * bitmap_file_data_fixup |
2236 | * |
2237 | * $Bitmap can overlap the end of the volume. Any bits in this region |
2238 | * must be set. This region also encompasses the backup boot sector. |
2239 | */ |
2240 | static void bitmap_file_data_fixup(s64 cluster, struct bitmap *bm) |
2241 | { |
2242 | for (; cluster < bm->size << 3; cluster++) |
2243 | ntfs_bit_set(bm->bm, (u64)cluster, 1); |
2244 | } |
2245 | |
2246 | /** |
2247 | * truncate_badclust_bad_attr |
2248 | * |
2249 | * The metadata file $BadClus needs to be shrunk. |
2250 | * |
2251 | * FIXME: this function should go away and instead using a generalized |
2252 | * "truncate_bitmap_data_attr()" |
2253 | */ |
2254 | static void truncate_badclust_bad_attr(ntfs_resize_t *resize) |
2255 | { |
2256 | ATTR_RECORD *a; |
2257 | runlist *rl_bad; |
2258 | s64 nr_clusters = resize->new_volume_size; |
2259 | ntfs_volume *vol = resize->vol; |
2260 | |
2261 | a = resize->ctx->attr; |
2262 | if (!a->non_resident) |
2263 | /* FIXME: handle resident attribute value */ |
2264 | err_exit("Resident attribute in $BadClust isn't supported!\n"); |
2265 | |
2266 | if (!(rl_bad = ntfs_mapping_pairs_decompress(vol, a, NULL))) |
2267 | perr_exit("ntfs_mapping_pairs_decompress"); |
2268 | |
2269 | rl_truncate(&rl_bad, nr_clusters); |
2270 | |
2271 | a->highest_vcn = cpu_to_sle64(nr_clusters - 1LL); |
2272 | a->allocated_size = cpu_to_sle64(nr_clusters * vol->cluster_size); |
2273 | a->data_size = cpu_to_sle64(nr_clusters * vol->cluster_size); |
2274 | |
2275 | if (!replace_attribute_runlist(resize, rl_bad)) |
2276 | free(rl_bad); |
2277 | } |
2278 | |
2279 | /** |
2280 | * realloc_bitmap_data_attr |
2281 | * |
2282 | * Reallocate the metadata file $Bitmap. It must be large enough for one bit |
2283 | * per cluster of the shrunken volume. Also it must be a of 8 bytes in size. |
2284 | */ |
2285 | static void realloc_bitmap_data_attr(ntfs_resize_t *resize, |
2286 | runlist **rl, |
2287 | s64 nr_bm_clusters) |
2288 | { |
2289 | s64 i; |
2290 | ntfs_volume *vol = resize->vol; |
2291 | ATTR_RECORD *a = resize->ctx->attr; |
2292 | s64 new_size = resize->new_volume_size; |
2293 | struct bitmap *bm = &resize->lcn_bitmap; |
2294 | |
2295 | if (!(*rl = ntfs_mapping_pairs_decompress(vol, a, NULL))) |
2296 | perr_exit("ntfs_mapping_pairs_decompress"); |
2297 | |
2298 | release_bitmap_clusters(bm, *rl); |
2299 | free(*rl); |
2300 | |
2301 | for (i = vol->nr_clusters; i < new_size; i++) |
2302 | ntfs_bit_set(bm->bm, i, 0); |
2303 | |
2304 | if (!(*rl = alloc_cluster(bm, nr_bm_clusters, new_size, 0))) |
2305 | perr_exit("Couldn't allocate $Bitmap clusters"); |
2306 | } |
2307 | |
2308 | static void realloc_lcn_bitmap(ntfs_resize_t *resize, s64 bm_bsize) |
2309 | { |
2310 | u8 *tmp; |
2311 | |
2312 | if (!(tmp = realloc(resize->lcn_bitmap.bm, bm_bsize))) |
2313 | perr_exit("realloc"); |
2314 | |
2315 | resize->lcn_bitmap.bm = tmp; |
2316 | resize->lcn_bitmap.size = bm_bsize; |
2317 | bitmap_file_data_fixup(resize->new_volume_size, &resize->lcn_bitmap); |
2318 | } |
2319 | |
2320 | /** |
2321 | * truncate_bitmap_data_attr |
2322 | */ |
2323 | static void truncate_bitmap_data_attr(ntfs_resize_t *resize) |
2324 | { |
2325 | ATTR_RECORD *a; |
2326 | runlist *rl; |
2327 | s64 bm_bsize, size; |
2328 | s64 nr_bm_clusters; |
2329 | int truncated; |
2330 | ntfs_volume *vol = resize->vol; |
2331 | |
2332 | a = resize->ctx->attr; |
2333 | if (!a->non_resident) |
2334 | /* FIXME: handle resident attribute value */ |
2335 | err_exit("Resident attribute in $Bitmap isn't supported!\n"); |
2336 | |
2337 | bm_bsize = nr_clusters_to_bitmap_byte_size(resize->new_volume_size); |
2338 | nr_bm_clusters = rounded_up_division(bm_bsize, vol->cluster_size); |
2339 | |
2340 | if (resize->shrink) { |
2341 | realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); |
2342 | realloc_lcn_bitmap(resize, bm_bsize); |
2343 | } else { |
2344 | realloc_lcn_bitmap(resize, bm_bsize); |
2345 | realloc_bitmap_data_attr(resize, &rl, nr_bm_clusters); |
2346 | } |
2347 | |
2348 | a->highest_vcn = cpu_to_sle64(nr_bm_clusters - 1LL); |
2349 | a->allocated_size = cpu_to_sle64(nr_bm_clusters * vol->cluster_size); |
2350 | a->data_size = cpu_to_sle64(bm_bsize); |
2351 | a->initialized_size = cpu_to_sle64(bm_bsize); |
2352 | |
2353 | truncated = !replace_attribute_runlist(resize, rl); |
2354 | |
2355 | /* |
2356 | * FIXME: update allocated/data sizes and timestamps in $FILE_NAME |
2357 | * attribute too, for now chkdsk will do this for us. |
2358 | */ |
2359 | |
2360 | size = ntfs_rl_pwrite(vol, rl, 0, 0, bm_bsize, resize->lcn_bitmap.bm); |
2361 | if (bm_bsize != size) { |
2362 | if (size == -1) |
2363 | perr_exit("Couldn't write $Bitmap"); |
2364 | err_exit("Couldn't write full $Bitmap file (%lld from %lld)\n", |
2365 | (long long)size, (long long)bm_bsize); |
2366 | } |
2367 | |
2368 | if (truncated) |
2369 | free(rl); |
2370 | } |
2371 | |
2372 | /** |
2373 | * lookup_data_attr |
2374 | * |
2375 | * Find the $DATA attribute (with or without a name) for the given MFT reference |
2376 | * (inode number). |
2377 | */ |
2378 | static void lookup_data_attr(ntfs_volume *vol, |
2379 | MFT_REF mref, |
2380 | const char *aname, |
2381 | ntfs_attr_search_ctx **ctx) |
2382 | { |
2383 | ntfs_inode *ni; |
2384 | ntfschar *ustr; |
2385 | int len = 0; |
2386 | |
2387 | if (!(ni = ntfs_inode_open(vol, mref))) |
2388 | perr_exit("ntfs_open_inode"); |
2389 | |
2390 | if (!(*ctx = attr_get_search_ctx(ni, NULL))) |
2391 | exit(1); |
2392 | |
2393 | if ((ustr = ntfs_str2ucs(aname, &len)) == NULL) { |
2394 | perr_printf("Couldn't convert '%s' to Unicode", aname); |
2395 | exit(1); |
2396 | } |
2397 | |
2398 | if (ntfs_attr_lookup(AT_DATA, ustr, len, CASE_SENSITIVE, |
2399 | 0, NULL, 0, *ctx)) |
2400 | perr_exit("ntfs_lookup_attr"); |
2401 | |
2402 | ntfs_ucsfree(ustr); |
2403 | } |
2404 | |
2405 | #if CLEAN_EXIT |
2406 | |
2407 | static void close_inode_and_context(ntfs_attr_search_ctx *ctx) |
2408 | { |
2409 | ntfs_inode *ni; |
2410 | |
2411 | ni = ctx->base_ntfs_ino; |
2412 | if (!ni) |
2413 | ni = ctx->ntfs_ino; |
2414 | ntfs_attr_put_search_ctx(ctx); |
2415 | if (ni) |
2416 | ntfs_inode_close(ni); |
2417 | } |
2418 | |
2419 | #endif /* CLEAN_EXIT */ |
2420 | |
2421 | static int check_bad_sectors(ntfs_volume *vol) |
2422 | { |
2423 | ntfs_attr_search_ctx *ctx; |
2424 | ntfs_inode *base_ni; |
2425 | runlist *rl; |
2426 | s64 i, badclusters = 0; |
2427 | |
2428 | ntfs_log_verbose("Checking for bad sectors ...\n"); |
2429 | |
2430 | lookup_data_attr(vol, FILE_BadClus, "$Bad", &ctx); |
2431 | |
2432 | base_ni = ctx->base_ntfs_ino; |
2433 | if (!base_ni) |
2434 | base_ni = ctx->ntfs_ino; |
2435 | |
2436 | if (NInoAttrList(base_ni)) { |
2437 | err_printf("Too many bad sectors have been detected!\n"); |
2438 | printf("%s", many_bad_sectors_msg); |
2439 | exit(1); |
2440 | } |
2441 | |
2442 | if (!ctx->attr->non_resident) |
2443 | err_exit("Resident attribute in $BadClust! Please report to " |
2444 | "%s\n", NTFS_DEV_LIST); |
2445 | /* |
2446 | * FIXME: The below would be partial for non-base records in the |
2447 | * not yet supported multi-record case. Alternatively use audited |
2448 | * ntfs_attr_truncate after an umount & mount. |
2449 | */ |
2450 | if (!(rl = ntfs_mapping_pairs_decompress(vol, ctx->attr, NULL))) |
2451 | perr_exit("Decompressing $BadClust:$Bad mapping pairs failed"); |
2452 | |
2453 | for (i = 0; rl[i].length; i++) { |
2454 | /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ |
2455 | if (rl[i].lcn == LCN_HOLE || rl[i].lcn == LCN_RL_NOT_MAPPED) |
2456 | continue; |
2457 | |
2458 | badclusters += rl[i].length; |
2459 | ntfs_log_verbose("Bad cluster: %#8llx - %#llx (%lld)\n", |
2460 | (long long)rl[i].lcn, |
2461 | (long long)rl[i].lcn + rl[i].length - 1, |
2462 | (long long)rl[i].length); |
2463 | } |
2464 | |
2465 | if (badclusters) { |
2466 | printf("%sThis software has detected that the disk has at least" |
2467 | " %lld bad sector%s.\n", |
2468 | !opt.badsectors ? NERR_PREFIX : "WARNING: ", |
2469 | (long long)badclusters, badclusters - 1 ? "s" : ""); |
2470 | if (!opt.badsectors) { |
2471 | printf("%s", bad_sectors_warning_msg); |
2472 | exit(1); |
2473 | } else |
2474 | printf("WARNING: Bad sectors can cause reliability " |
2475 | "problems and massive data loss!!!\n"); |
2476 | } |
2477 | |
2478 | free(rl); |
2479 | #if CLEAN_EXIT |
2480 | close_inode_and_context(ctx); |
2481 | #else |
2482 | ntfs_attr_put_search_ctx(ctx); |
2483 | #endif |
2484 | |
2485 | return badclusters; |
2486 | } |
2487 | |
2488 | /** |
2489 | * truncate_badclust_file |
2490 | * |
2491 | * Shrink the $BadClus file to match the new volume size. |
2492 | */ |
2493 | static void truncate_badclust_file(ntfs_resize_t *resize) |
2494 | { |
2495 | printf("Updating $BadClust file ...\n"); |
2496 | |
2497 | lookup_data_attr(resize->vol, FILE_BadClus, "$Bad", &resize->ctx); |
2498 | /* FIXME: sanity_check_attr(ctx->attr); */ |
2499 | truncate_badclust_bad_attr(resize); |
2500 | |
2501 | if (write_mft_record(resize->vol, resize->ctx->ntfs_ino->mft_no, |
2502 | resize->ctx->mrec)) |
2503 | perr_exit("Couldn't update $BadClust"); |
2504 | |
2505 | #if CLEAN_EXIT |
2506 | close_inode_and_context(resize->ctx); |
2507 | #else |
2508 | ntfs_attr_put_search_ctx(resize->ctx); |
2509 | #endif |
2510 | } |
2511 | |
2512 | /** |
2513 | * truncate_bitmap_file |
2514 | * |
2515 | * Shrink the $Bitmap file to match the new volume size. |
2516 | */ |
2517 | static void truncate_bitmap_file(ntfs_resize_t *resize) |
2518 | { |
2519 | ntfs_volume *vol = resize->vol; |
2520 | |
2521 | printf("Updating $Bitmap file ...\n"); |
2522 | |
2523 | lookup_data_attr(resize->vol, FILE_Bitmap, NULL, &resize->ctx); |
2524 | truncate_bitmap_data_attr(resize); |
2525 | |
2526 | if (resize->new_mft_start) { |
2527 | s64 pos; |
2528 | |
2529 | /* write the MFT record at its new location */ |
2530 | pos = (resize->new_mft_start->lcn << vol->cluster_size_bits) |
2531 | + (FILE_Bitmap << vol->mft_record_size_bits); |
2532 | if (!opt.ro_flag |
2533 | && (ntfs_mst_pwrite(vol->dev, pos, 1, |
2534 | vol->mft_record_size, resize->ctx->mrec) != 1)) |
2535 | perr_exit("Couldn't update $Bitmap at new location"); |
2536 | } else { |
2537 | if (write_mft_record(vol, resize->ctx->ntfs_ino->mft_no, |
2538 | resize->ctx->mrec)) |
2539 | perr_exit("Couldn't update $Bitmap"); |
2540 | } |
2541 | |
2542 | #if CLEAN_EXIT |
2543 | close_inode_and_context(resize->ctx); |
2544 | #else |
2545 | ntfs_attr_put_search_ctx(resize->ctx); |
2546 | #endif |
2547 | } |
2548 | |
2549 | /** |
2550 | * setup_lcn_bitmap |
2551 | * |
2552 | * Allocate a block of memory with one bit for each cluster of the disk. |
2553 | * All the bits are set to 0, except those representing the region beyond the |
2554 | * end of the disk. |
2555 | */ |
2556 | static int setup_lcn_bitmap(struct bitmap *bm, s64 nr_clusters) |
2557 | { |
2558 | /* Determine lcn bitmap byte size and allocate it. */ |
2559 | bm->size = rounded_up_division(nr_clusters, 8); |
2560 | |
2561 | bm->bm = ntfs_calloc(bm->size); |
2562 | if (!bm->bm) |
2563 | return -1; |
2564 | |
2565 | bitmap_file_data_fixup(nr_clusters, bm); |
2566 | return 0; |
2567 | } |
2568 | |
2569 | /** |
2570 | * update_bootsector |
2571 | * |
2572 | * FIXME: should be done using ntfs_* functions |
2573 | */ |
2574 | static void update_bootsector(ntfs_resize_t *r) |
2575 | { |
2576 | NTFS_BOOT_SECTOR *bs; |
2577 | ntfs_volume *vol = r->vol; |
2578 | s64 bs_size = vol->sector_size; |
2579 | |
2580 | printf("Updating Boot record ...\n"); |
2581 | |
2582 | bs = (NTFS_BOOT_SECTOR*)ntfs_malloc(vol->sector_size); |
2583 | if (!bs) |
2584 | perr_exit("ntfs_malloc"); |
2585 | |
2586 | if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1) |
2587 | perr_exit("lseek"); |
2588 | |
2589 | if (vol->dev->d_ops->read(vol->dev, bs, bs_size) == -1) |
2590 | perr_exit("read() error"); |
2591 | |
2592 | bs->number_of_sectors = cpu_to_sle64(r->new_volume_size * |
2593 | bs->bpb.sectors_per_cluster); |
2594 | |
2595 | if (r->mftmir_old) { |
2596 | r->progress.flags |= NTFS_PROGBAR_SUPPRESS; |
2597 | /* Be sure the MFTMirr holds the updated MFT runlist */ |
2598 | if (r->new_mft_start) |
2599 | copy_clusters(r, r->mftmir_rl.lcn, |
2600 | r->new_mft_start->lcn, r->mftmir_rl.length); |
2601 | else |
2602 | copy_clusters(r, r->mftmir_rl.lcn, r->mftmir_old, |
2603 | r->mftmir_rl.length); |
2604 | bs->mftmirr_lcn = cpu_to_sle64(r->mftmir_rl.lcn); |
2605 | r->progress.flags &= ~NTFS_PROGBAR_SUPPRESS; |
2606 | } |
2607 | /* Set the start of the relocated MFT */ |
2608 | if (r->new_mft_start) { |
2609 | bs->mft_lcn = cpu_to_sle64(r->new_mft_start->lcn); |
2610 | /* no more need for the new MFT start */ |
2611 | free(r->new_mft_start); |
2612 | r->new_mft_start = (runlist_element*)NULL; |
2613 | } |
2614 | |
2615 | if (vol->dev->d_ops->seek(vol->dev, 0, SEEK_SET) == (off_t)-1) |
2616 | perr_exit("lseek"); |
2617 | |
2618 | if (!opt.ro_flag) |
2619 | if (vol->dev->d_ops->write(vol->dev, bs, bs_size) == -1) |
2620 | perr_exit("write() error"); |
2621 | /* |
2622 | * Set the backup boot sector, if the target size is |
2623 | * either not defined or is defined with no multiplier |
2624 | * suffix and is a multiple of the sector size. |
2625 | * With these conditions we can be confident enough that |
2626 | * the partition size is already defined or it will be |
2627 | * later defined with the same exact value. |
2628 | */ |
2629 | if (!opt.ro_flag && opt.reliable_size |
2630 | && !(opt.bytes % vol->sector_size)) { |
2631 | if (vol->dev->d_ops->seek(vol->dev, opt.bytes |
2632 | - vol->sector_size, SEEK_SET) == (off_t)-1) |
2633 | perr_exit("lseek"); |
2634 | if (vol->dev->d_ops->write(vol->dev, bs, bs_size) == -1) |
2635 | perr_exit("write() error"); |
2636 | } |
2637 | free(bs); |
2638 | } |
2639 | |
2640 | /** |
2641 | * vol_size |
2642 | */ |
2643 | static s64 vol_size(ntfs_volume *v, s64 nr_clusters) |
2644 | { |
2645 | /* add one sector_size for the backup boot sector */ |
2646 | return nr_clusters * v->cluster_size + v->sector_size; |
2647 | } |
2648 | |
2649 | /** |
2650 | * print_vol_size |
2651 | * |
2652 | * Print the volume size in bytes and decimal megabytes. |
2653 | */ |
2654 | static void print_vol_size(const char *str, s64 bytes) |
2655 | { |
2656 | printf("%s: %lld bytes (%lld MB)\n", str, (long long)bytes, |
2657 | (long long)rounded_up_division(bytes, NTFS_MBYTE)); |
2658 | } |
2659 | |
2660 | /** |
2661 | * print_disk_usage |
2662 | * |
2663 | * Display the amount of disk space in use. |
2664 | */ |
2665 | static void print_disk_usage(ntfs_volume *vol, s64 nr_used_clusters) |
2666 | { |
2667 | s64 total, used; |
2668 | |
2669 | total = vol->nr_clusters * vol->cluster_size; |
2670 | used = nr_used_clusters * vol->cluster_size; |
2671 | |
2672 | /* WARNING: don't modify the text, external tools grep for it */ |
2673 | if (!opt.infombonly) { |
2674 | printf("Space in use : %lld MB (%.1f%%)\n", |
2675 | (long long)rounded_up_division(used, NTFS_MBYTE), |
2676 | 100.0 * ((float)used / total)); |
2677 | } |
2678 | } |
2679 | |
2680 | static void print_num_of_relocations(ntfs_resize_t *resize) |
2681 | { |
2682 | s64 relocations = resize->relocations * resize->vol->cluster_size; |
2683 | |
2684 | printf("Needed relocations : %lld (%lld MB)\n", |
2685 | (long long)resize->relocations, (long long) |
2686 | rounded_up_division(relocations, NTFS_MBYTE)); |
2687 | } |
2688 | |
2689 | static ntfs_volume *check_volume(void) |
2690 | { |
2691 | ntfs_volume *myvol = NULL; |
2692 | |
2693 | /* |
2694 | * Pass NTFS_MNT_FORENSIC so that the mount process does not modify the |
2695 | * volume at all. We will do the logfile emptying and dirty setting |
2696 | * later if needed. |
2697 | */ |
2698 | if (!(myvol = ntfs_mount(opt.volume, opt.ro_flag | NTFS_MNT_FORENSIC))) |
2699 | { |
2700 | int err = errno; |
2701 | |
2702 | perr_printf("Opening '%s' as NTFS failed", opt.volume); |
2703 | switch (err) { |
2704 | case EINVAL : |
2705 | printf(invalid_ntfs_msg, opt.volume); |
2706 | break; |
2707 | case EIO : |
2708 | printf("%s", corrupt_volume_msg); |
2709 | break; |
2710 | case EPERM : |
2711 | printf("%s", hibernated_volume_msg); |
2712 | break; |
2713 | case EOPNOTSUPP : |
2714 | printf("%s", unclean_journal_msg); |
2715 | break; |
2716 | case EBUSY : |
2717 | printf("%s", opened_volume_msg); |
2718 | break; |
2719 | default : |
2720 | break; |
2721 | } |
2722 | exit(1); |
2723 | } |
2724 | return myvol; |
2725 | } |
2726 | |
2727 | |
2728 | /** |
2729 | * mount_volume |
2730 | * |
2731 | * First perform some checks to determine if the volume is already mounted, or |
2732 | * is dirty (Windows wasn't shutdown properly). If everything is OK, then mount |
2733 | * the volume (load the metadata into memory). |
2734 | */ |
2735 | static ntfs_volume *mount_volume(void) |
2736 | { |
2737 | unsigned long mntflag; |
2738 | ntfs_volume *vol = NULL; |
2739 | |
2740 | if (ntfs_check_if_mounted(opt.volume, &mntflag)) { |
2741 | perr_printf("Failed to check '%s' mount state", opt.volume); |
2742 | printf("Probably /etc/mtab is missing. It's too risky to " |
2743 | "continue. You might try\nan another Linux distro.\n"); |
2744 | exit(1); |
2745 | } |
2746 | if (mntflag & NTFS_MF_MOUNTED) { |
2747 | if (!(mntflag & NTFS_MF_READONLY)) |
2748 | err_exit("Device '%s' is mounted read-write. " |
2749 | "You must 'umount' it first.\n", opt.volume); |
2750 | if (!opt.ro_flag) |
2751 | err_exit("Device '%s' is mounted. " |
2752 | "You must 'umount' it first.\n", opt.volume); |
2753 | } |
2754 | vol = check_volume(); |
2755 | |
2756 | if (vol->flags & VOLUME_IS_DIRTY) |
2757 | if (opt.force-- <= 0) |
2758 | err_exit("Volume is scheduled for check.\nRun chkdsk /f" |
2759 | " and please try again, or see option -f.\n"); |
2760 | |
2761 | if (NTFS_MAX_CLUSTER_SIZE < vol->cluster_size) |
2762 | err_exit("Cluster size %u is too large!\n", |
2763 | (unsigned int)vol->cluster_size); |
2764 | |
2765 | if (!opt.infombonly) { |
2766 | printf("Device name : %s\n", opt.volume); |
2767 | printf("NTFS volume version: %d.%d\n", |
2768 | vol->major_ver, vol->minor_ver); |
2769 | } |
2770 | if (ntfs_version_is_supported(vol)) |
2771 | perr_exit("Unknown NTFS version"); |
2772 | |
2773 | if (!opt.infombonly) { |
2774 | printf("Cluster size : %u bytes\n", |
2775 | (unsigned int)vol->cluster_size); |
2776 | print_vol_size("Current volume size", |
2777 | vol_size(vol, vol->nr_clusters)); |
2778 | } |
2779 | |
2780 | return vol; |
2781 | } |
2782 | |
2783 | /** |
2784 | * prepare_volume_fixup |
2785 | * |
2786 | * Set the volume's dirty flag and wipe the filesystem journal. When Windows |
2787 | * boots it will automatically run chkdsk to check for any problems. If the |
2788 | * read-only command line option was given, this function will do nothing. |
2789 | */ |
2790 | static void prepare_volume_fixup(ntfs_volume *vol) |
2791 | { |
2792 | printf("Schedule chkdsk for NTFS consistency check at Windows boot " |
2793 | "time ...\n"); |
2794 | vol->flags |= VOLUME_IS_DIRTY; |
2795 | if (ntfs_volume_write_flags(vol, vol->flags)) |
2796 | perr_exit("Failed to set the volume dirty"); |
2797 | |
2798 | /* Porting note: This flag does not exist in libntfs-3g. The dirty flag |
2799 | * is never modified by libntfs-3g on unmount and we set it above. We |
2800 | * can safely comment out this statement. */ |
2801 | /* NVolSetWasDirty(vol); */ |
2802 | |
2803 | if (vol->dev->d_ops->sync(vol->dev) == -1) |
2804 | perr_exit("Failed to sync device"); |
2805 | printf("Resetting $LogFile ... (this might take a while)\n"); |
2806 | if (ntfs_logfile_reset(vol)) |
2807 | perr_exit("Failed to reset $LogFile"); |
2808 | if (vol->dev->d_ops->sync(vol->dev) == -1) |
2809 | perr_exit("Failed to sync device"); |
2810 | } |
2811 | |
2812 | static void set_disk_usage_constraint(ntfs_resize_t *resize) |
2813 | { |
2814 | /* last lcn for a filled up volume (no empty space) */ |
2815 | s64 last = resize->inuse - 1; |
2816 | |
2817 | if (resize->last_unsupp < last) |
2818 | resize->last_unsupp = last; |
2819 | } |
2820 | |
2821 | static void check_resize_constraints(ntfs_resize_t *resize) |
2822 | { |
2823 | s64 new_size = resize->new_volume_size; |
2824 | |
2825 | /* FIXME: resize.shrink true also if only -i is used */ |
2826 | if (!resize->shrink) |
2827 | return; |
2828 | |
2829 | if (resize->inuse == resize->vol->nr_clusters) |
2830 | err_exit("Volume is full. To shrink it, " |
2831 | "delete unused files.\n"); |
2832 | |
2833 | if (opt.info || opt.infombonly) |
2834 | return; |
2835 | |
2836 | /* FIXME: reserve some extra space so Windows can boot ... */ |
2837 | if (new_size < resize->inuse) |
2838 | err_exit("New size can't be less than the space already" |
2839 | " occupied by data.\nYou either need to delete unused" |
2840 | " files or see the -i option.\n"); |
2841 | |
2842 | if (new_size <= resize->last_unsupp) |
2843 | err_exit("The fragmentation type, you have, isn't " |
2844 | "supported yet. Rerun ntfsresize\nwith " |
2845 | "the -i option to estimate the smallest " |
2846 | "shrunken volume size supported.\n"); |
2847 | |
2848 | print_num_of_relocations(resize); |
2849 | } |
2850 | |
2851 | static void check_cluster_allocation(ntfs_volume *vol, ntfsck_t *fsck) |
2852 | { |
2853 | memset(fsck, 0, sizeof(ntfsck_t)); |
2854 | |
2855 | if (opt.show_progress) |
2856 | fsck->flags |= NTFSCK_PROGBAR; |
2857 | |
2858 | if (setup_lcn_bitmap(&fsck->lcn_bitmap, vol->nr_clusters) != 0) |
2859 | perr_exit("Failed to setup allocation bitmap"); |
2860 | if (build_allocation_bitmap(vol, fsck) != 0) |
2861 | exit(1); |
2862 | if (fsck->outsider || fsck->multi_ref) { |
2863 | err_printf("Filesystem check failed!\n"); |
2864 | if (fsck->outsider) |
2865 | err_printf("%d clusters are referenced outside " |
2866 | "of the volume.\n", fsck->outsider); |
2867 | if (fsck->multi_ref) |
2868 | err_printf("%d clusters are referenced multiple" |
2869 | " times.\n", fsck->multi_ref); |
2870 | printf("%s", corrupt_volume_msg); |
2871 | exit(1); |
2872 | } |
2873 | |
2874 | compare_bitmaps(vol, &fsck->lcn_bitmap); |
2875 | } |
2876 | |
2877 | /* |
2878 | * Following are functions to expand an NTFS file system |
2879 | * to the beginning of a partition. The old metadata can be |
2880 | * located according to the backup bootsector, provided it can |
2881 | * still be found at the end of the partition. |
2882 | * |
2883 | * The data itself is kept in place, and this is only possible |
2884 | * if the expanded size is a multiple of cluster size, and big |
2885 | * enough to hold the new $Boot, $Bitmap and $MFT |
2886 | * |
2887 | * The volume cannot be mounted because the layout of data does |
2888 | * not match the volume parameters. The alignments of MFT entries |
2889 | * and index blocks may be different in the new volume and the old |
2890 | * one. The "ntfs_volume" structure is only partially usable, |
2891 | * "ntfs_inode" and "search_context" cannot be used until the |
2892 | * metadata has been moved and the volume is opened. |
2893 | * |
2894 | * Currently, no part of this new code is called from old code, |
2895 | * and the only change in old code is the processing of options. |
2896 | * Deduplication of code should be done later when the code is |
2897 | * proved safe. |
2898 | * |
2899 | */ |
2900 | |
2901 | typedef struct EXPAND { |
2902 | ntfs_volume *vol; |
2903 | u64 original_sectors; |
2904 | u64 new_sectors; |
2905 | u64 bitmap_allocated; |
2906 | u64 bitmap_size; |
2907 | u64 boot_size; |
2908 | u64 mft_size; |
2909 | LCN mft_lcn; |
2910 | s64 byte_increment; |
2911 | s64 sector_increment; |
2912 | s64 cluster_increment; |
2913 | u8 *bitmap; |
2914 | u8 *mft_bitmap; |
2915 | char *bootsector; |
2916 | MFT_RECORD *mrec; |
2917 | struct progress_bar *progress; |
2918 | struct DELAYED *delayed_runlists; /* runlists to process later */ |
2919 | } expand_t; |
2920 | |
2921 | /* |
2922 | * Locate an attribute in an MFT record |
2923 | * |
2924 | * Returns NULL if not found (with no error message) |
2925 | */ |
2926 | |
2927 | static ATTR_RECORD *find_attr(MFT_RECORD *mrec, ATTR_TYPES type, |
2928 | ntfschar *name, int namelen) |
2929 | { |
2930 | ATTR_RECORD *a; |
2931 | u32 offset; |
2932 | ntfschar *attrname; |
2933 | |
2934 | /* fetch the requested attribute */ |
2935 | offset = le16_to_cpu(mrec->attrs_offset); |
2936 | a = (ATTR_RECORD*)((char*)mrec + offset); |
2937 | attrname = (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)); |
2938 | while ((a->type != AT_END) |
2939 | && ((a->type != type) |
2940 | || (a->name_length != namelen) |
2941 | || (namelen && memcmp(attrname,name,2*namelen))) |
2942 | && (offset < le32_to_cpu(mrec->bytes_in_use))) { |
2943 | offset += le32_to_cpu(a->length); |
2944 | a = (ATTR_RECORD*)((char*)mrec + offset); |
2945 | if (namelen) |
2946 | attrname = (ntfschar*)((char*)a |
2947 | + le16_to_cpu(a->name_offset)); |
2948 | } |
2949 | if ((a->type != type) |
2950 | || (a->name_length != namelen) |
2951 | || (namelen && memcmp(attrname,name,2*namelen))) |
2952 | a = (ATTR_RECORD*)NULL; |
2953 | return (a); |
2954 | } |
2955 | |
2956 | /* |
2957 | * Read an MFT record and find an unnamed attribute |
2958 | * |
2959 | * Returns NULL if fails to read or attribute is not found |
2960 | */ |
2961 | |
2962 | static ATTR_RECORD *get_unnamed_attr(expand_t *expand, ATTR_TYPES type, |
2963 | s64 inum) |
2964 | { |
2965 | ntfs_volume *vol; |
2966 | ATTR_RECORD *a; |
2967 | MFT_RECORD *mrec; |
2968 | s64 pos; |
2969 | BOOL found; |
2970 | int got; |
2971 | |
2972 | found = FALSE; |
2973 | a = (ATTR_RECORD*)NULL; |
2974 | mrec = expand->mrec; |
2975 | vol = expand->vol; |
2976 | pos = (vol->mft_lcn << vol->cluster_size_bits) |
2977 | + (inum << vol->mft_record_size_bits) |
2978 | + expand->byte_increment; |
2979 | got = ntfs_mst_pread(vol->dev, pos, 1, vol->mft_record_size, mrec); |
2980 | if ((got == 1) && (mrec->flags & MFT_RECORD_IN_USE)) { |
2981 | a = find_attr(expand->mrec, type, NULL, 0); |
2982 | found = a && (a->type == type) && !a->name_length; |
2983 | } |
2984 | /* not finding the attribute list is not an error */ |
2985 | if (!found && (type != AT_ATTRIBUTE_LIST)) { |
2986 | err_printf("Could not find attribute 0x%lx in inode %lld\n", |
2987 | (long)le32_to_cpu(type), (long long)inum); |
2988 | a = (ATTR_RECORD*)NULL; |
2989 | } |
2990 | return (a); |
2991 | } |
2992 | |
2993 | /* |
2994 | * Read an MFT record and find an unnamed attribute |
2995 | * |
2996 | * Returns NULL if fails |
2997 | */ |
2998 | |
2999 | static ATTR_RECORD *read_and_get_attr(expand_t *expand, ATTR_TYPES type, |
3000 | s64 inum, ntfschar *name, int namelen) |
3001 | { |
3002 | ntfs_volume *vol; |
3003 | ATTR_RECORD *a; |
3004 | MFT_RECORD *mrec; |
3005 | s64 pos; |
3006 | int got; |
3007 | |
3008 | a = (ATTR_RECORD*)NULL; |
3009 | mrec = expand->mrec; |
3010 | vol = expand->vol; |
3011 | pos = (vol->mft_lcn << vol->cluster_size_bits) |
3012 | + (inum << vol->mft_record_size_bits) |
3013 | + expand->byte_increment; |
3014 | got = ntfs_mst_pread(vol->dev, pos, 1, vol->mft_record_size, mrec); |
3015 | if ((got == 1) && (mrec->flags & MFT_RECORD_IN_USE)) { |
3016 | a = find_attr(expand->mrec, type, name, namelen); |
3017 | } |
3018 | if (!a) { |
3019 | err_printf("Could not find attribute 0x%lx in inode %lld\n", |
3020 | (long)le32_to_cpu(type), (long long)inum); |
3021 | } |
3022 | return (a); |
3023 | } |
3024 | |
3025 | /* |
3026 | * Get the size allocated to the unnamed data of some inode |
3027 | * |
3028 | * Returns zero if fails. |
3029 | */ |
3030 | |
3031 | static s64 get_data_size(expand_t *expand, s64 inum) |
3032 | { |
3033 | ATTR_RECORD *a; |
3034 | s64 size; |
3035 | |
3036 | size = 0; |
3037 | /* get the size of unnamed $DATA */ |
3038 | a = get_unnamed_attr(expand, AT_DATA, inum); |
3039 | if (a && a->non_resident) |
3040 | size = le64_to_cpu(a->allocated_size); |
3041 | if (!size) { |
3042 | err_printf("Bad record %lld, could not get its size\n", |
3043 | (long long)inum); |
3044 | } |
3045 | return (size); |
3046 | } |
3047 | |
3048 | /* |
3049 | * Get the MFT bitmap |
3050 | * |
3051 | * Returns NULL if fails. |
3052 | */ |
3053 | |
3054 | static u8 *get_mft_bitmap(expand_t *expand) |
3055 | { |
3056 | ATTR_RECORD *a; |
3057 | ntfs_volume *vol; |
3058 | runlist_element *rl; |
3059 | runlist_element *prl; |
3060 | u32 bitmap_size; |
3061 | BOOL ok; |
3062 | |
3063 | expand->mft_bitmap = (u8*)NULL; |
3064 | vol = expand->vol; |
3065 | /* get the runlist of unnamed bitmap */ |
3066 | a = get_unnamed_attr(expand, AT_BITMAP, FILE_MFT); |
3067 | ok = TRUE; |
3068 | bitmap_size = le64_to_cpu(a->allocated_size); |
3069 | if (a |
3070 | && a->non_resident |
3071 | && ((bitmap_size << (vol->mft_record_size_bits + 3)) |
3072 | >= expand->mft_size)) { |
3073 | // rl in extent not implemented |
3074 | rl = ntfs_mapping_pairs_decompress(expand->vol, a, |
3075 | (runlist_element*)NULL); |
3076 | expand->mft_bitmap = (u8*)ntfs_calloc(bitmap_size); |
3077 | if (rl && expand->mft_bitmap) { |
3078 | for (prl=rl; prl->length && ok; prl++) { |
3079 | lseek_to_cluster(vol, |
3080 | prl->lcn + expand->cluster_increment); |
3081 | ok = !read_all(vol->dev, expand->mft_bitmap, |
3082 | prl->length << vol->cluster_size_bits); |
3083 | } |
3084 | if (!ok) { |
3085 | err_printf("Could not read the MFT bitmap\n"); |
3086 | free(expand->mft_bitmap); |
3087 | expand->mft_bitmap = (u8*)NULL; |
3088 | } |
3089 | free(rl); |
3090 | } else { |
3091 | err_printf("Could not get the MFT bitmap\n"); |
3092 | } |
3093 | } else |
3094 | err_printf("Invalid MFT bitmap\n"); |
3095 | return (expand->mft_bitmap); |
3096 | } |
3097 | |
3098 | /* |
3099 | * Check for bad sectors |
3100 | * |
3101 | * Deduplication to be done when proved safe |
3102 | */ |
3103 | |
3104 | static int check_expand_bad_sectors(expand_t *expand, ATTR_RECORD *a) |
3105 | { |
3106 | runlist *rl; |
3107 | int res; |
3108 | s64 i, badclusters = 0; |
3109 | |
3110 | res = 0; |
3111 | ntfs_log_verbose("Checking for bad sectors ...\n"); |
3112 | |
3113 | if (find_attr(expand->mrec, AT_ATTRIBUTE_LIST, NULL, 0)) { |
3114 | err_printf("Hopelessly many bad sectors have been detected!\n"); |
3115 | err_printf("%s", many_bad_sectors_msg); |
3116 | res = -1; |
3117 | } else { |
3118 | |
3119 | /* |
3120 | * FIXME: The below would be partial for non-base records in the |
3121 | * not yet supported multi-record case. Alternatively use audited |
3122 | * ntfs_attr_truncate after an umount & mount. |
3123 | */ |
3124 | rl = ntfs_mapping_pairs_decompress(expand->vol, a, NULL); |
3125 | if (!rl) { |
3126 | perr_printf("Decompressing $BadClust:" |
3127 | "$Bad mapping pairs failed"); |
3128 | res = -1; |
3129 | } else { |
3130 | for (i = 0; rl[i].length; i++) { |
3131 | /* CHECKME: LCN_RL_NOT_MAPPED check isn't needed */ |
3132 | if (rl[i].lcn == LCN_HOLE |
3133 | || rl[i].lcn == LCN_RL_NOT_MAPPED) |
3134 | continue; |
3135 | |
3136 | badclusters += rl[i].length; |
3137 | ntfs_log_verbose("Bad cluster: %#8llx - %#llx" |
3138 | " (%lld)\n", |
3139 | (long long)rl[i].lcn, |
3140 | (long long)rl[i].lcn |
3141 | + rl[i].length - 1, |
3142 | (long long)rl[i].length); |
3143 | } |
3144 | |
3145 | if (badclusters) { |
3146 | err_printf("%sThis software has detected that" |
3147 | " the disk has at least" |
3148 | " %lld bad sector%s.\n", |
3149 | !opt.badsectors ? NERR_PREFIX |
3150 | : "WARNING: ", |
3151 | (long long)badclusters, |
3152 | badclusters - 1 ? "s" : ""); |
3153 | if (!opt.badsectors) { |
3154 | err_printf("%s", bad_sectors_warning_msg); |
3155 | res = -1; |
3156 | } else |
3157 | err_printf("WARNING: Bad sectors can cause" |
3158 | " reliability problems" |
3159 | " and massive data loss!!!\n"); |
3160 | } |
3161 | free(rl); |
3162 | } |
3163 | } |
3164 | return (res); |
3165 | } |
3166 | |
3167 | /* |
3168 | * Check miscellaneous expansion constraints |
3169 | */ |
3170 | |
3171 | static int check_expand_constraints(expand_t *expand) |
3172 | { |
3173 | static ntfschar bad[] = { |
3174 | const_cpu_to_le16('$'), const_cpu_to_le16('B'), |
3175 | const_cpu_to_le16('a'), const_cpu_to_le16('d') |
3176 | } ; |
3177 | ATTR_RECORD *a; |
3178 | runlist_element *rl; |
3179 | VOLUME_INFORMATION *volinfo; |
3180 | VOLUME_FLAGS flags; |
3181 | int res; |
3182 | |
3183 | if (opt.verbose) |
3184 | ntfs_log_verbose("Checking for expansion constraints...\n"); |
3185 | res = 0; |
3186 | /* extents for $MFT are not supported */ |
3187 | if (get_unnamed_attr(expand, AT_ATTRIBUTE_LIST, FILE_MFT)) { |
3188 | err_printf("The $MFT is too much fragmented\n"); |
3189 | res = -1; |
3190 | } |
3191 | /* fragmented $MFTMirr is not supported */ |
3192 | a = get_unnamed_attr(expand, AT_DATA, FILE_MFTMirr); |
3193 | if (a) { |
3194 | rl = ntfs_mapping_pairs_decompress(expand->vol, a, NULL); |
3195 | if (!rl || !rl[0].length || rl[1].length) { |
3196 | err_printf("$MFTMirr is bad or fragmented\n"); |
3197 | res = -1; |
3198 | } |
3199 | free(rl); |
3200 | } |
3201 | /* fragmented $Boot is not supported */ |
3202 | a = get_unnamed_attr(expand, AT_DATA, FILE_Boot); |
3203 | if (a) { |
3204 | rl = ntfs_mapping_pairs_decompress(expand->vol, a, NULL); |
3205 | if (!rl || !rl[0].length || rl[1].length) { |
3206 | err_printf("$Boot is bad or fragmented\n"); |
3207 | res = -1; |
3208 | } |
3209 | free(rl); |
3210 | } |
3211 | /* Volume should not be marked dirty */ |
3212 | a = get_unnamed_attr(expand, AT_VOLUME_INFORMATION, FILE_Volume); |
3213 | if (a) { |
3214 | volinfo = (VOLUME_INFORMATION*) |
3215 | (le16_to_cpu(a->value_offset) + (char*)a); |
3216 | flags = volinfo->flags; |
3217 | if ((flags & VOLUME_IS_DIRTY) && (opt.force-- <= 0)) { |
3218 | err_printf("Volume is scheduled for check.\nRun chkdsk /f" |
3219 | " and please try again, or see option -f.\n"); |
3220 | res = -1; |
3221 | } |
3222 | } else { |
3223 | err_printf("Could not get Volume flags\n"); |
3224 | res = -1; |
3225 | } |
3226 | |
3227 | /* There should not be too many bad clusters */ |
3228 | a = read_and_get_attr(expand, AT_DATA, FILE_BadClus, bad, 4); |
3229 | if (!a || !a->non_resident) { |
3230 | err_printf("Resident attribute in $BadClust! Please report to " |
3231 | "%s\n", NTFS_DEV_LIST); |
3232 | res = -1; |
3233 | } else |
3234 | if (check_expand_bad_sectors(expand,a)) |
3235 | res = -1; |
3236 | return (res); |
3237 | } |
3238 | |
3239 | /* |
3240 | * Compute the new sizes and check whether the NTFS file |
3241 | * system can be expanded |
3242 | * |
3243 | * The partition has to have been expanded, |
3244 | * the extra space must be able to hold the $MFT, $Boot, and $Bitmap |
3245 | * the extra space must be a multiple of cluster size |
3246 | * |
3247 | * Returns TRUE if the partition can be expanded, |
3248 | * FALSE if it canno be expanded or option --info was set |
3249 | */ |
3250 | |
3251 | static BOOL can_expand(expand_t *expand, ntfs_volume *vol) |
3252 | { |
3253 | s64 old_sector_count; |
3254 | s64 sectors_needed; |
3255 | s64 clusters; |
3256 | s64 minimum_size; |
3257 | s64 got; |
3258 | s64 advice; |
3259 | s64 bitmap_bits; |
3260 | BOOL ok; |
3261 | |
3262 | ok = TRUE; |
3263 | old_sector_count = vol->nr_clusters |
3264 | << (vol->cluster_size_bits - vol->sector_size_bits); |
3265 | /* do not include the space lost near the end */ |
3266 | expand->cluster_increment = (expand->new_sectors |
3267 | >> (vol->cluster_size_bits - vol->sector_size_bits)) |
3268 | - vol->nr_clusters; |
3269 | expand->byte_increment = expand->cluster_increment |
3270 | << vol->cluster_size_bits; |
3271 | expand->sector_increment = expand->byte_increment |
3272 | >> vol->sector_size_bits; |
3273 | printf("Sectors allocated to volume : old %lld current %lld difference %lld\n", |
3274 | (long long)old_sector_count, |
3275 | (long long)(old_sector_count + expand->sector_increment), |
3276 | (long long)expand->sector_increment); |
3277 | printf("Clusters allocated to volume : old %lld current %lld difference %lld\n", |
3278 | (long long)vol->nr_clusters, |
3279 | (long long)(vol->nr_clusters |
3280 | + expand->cluster_increment), |
3281 | (long long)expand->cluster_increment); |
3282 | /* the new size must be bigger */ |
3283 | if ((expand->sector_increment < 0) |
3284 | || (!expand->sector_increment && !opt.info)) { |
3285 | err_printf("Cannot expand volume : the partition has not been expanded\n"); |
3286 | ok = FALSE; |
3287 | } |
3288 | /* the old bootsector must match the backup */ |
3289 | got = ntfs_pread(expand->vol->dev, expand->byte_increment, |
3290 | vol->sector_size, expand->mrec); |
3291 | if ((got != vol->sector_size) |
3292 | || memcmp(expand->bootsector,expand->mrec,vol->sector_size)) { |
3293 | err_printf("The backup bootsector does not match the old bootsector\n"); |
3294 | ok = FALSE; |
3295 | } |
3296 | if (ok) { |
3297 | /* read the first MFT record, to get the MFT size */ |
3298 | expand->mft_size = get_data_size(expand, FILE_MFT); |
3299 | /* read the 6th MFT record, to get the $Boot size */ |
3300 | expand->boot_size = get_data_size(expand, FILE_Boot); |
3301 | if (!expand->mft_size || !expand->boot_size) { |
3302 | ok = FALSE; |
3303 | } else { |
3304 | /* |
3305 | * The bitmap is one bit per full cluster, |
3306 | * accounting for the backup bootsector. |
3307 | * When evaluating the minimal size, the bitmap |
3308 | * size must be adapted to the minimal size : |
3309 | * bits = clusters + ceil(clusters/clustersize) |
3310 | */ |
3311 | if (opt.info) { |
3312 | clusters = (((expand->original_sectors + 1) |
3313 | << vol->sector_size_bits) |
3314 | + expand->mft_size |
3315 | + expand->boot_size) |
3316 | >> vol->cluster_size_bits; |
3317 | bitmap_bits = ((clusters + 1) |
3318 | << vol->cluster_size_bits) |
3319 | / (vol->cluster_size + 1); |
3320 | } else { |
3321 | bitmap_bits = (expand->new_sectors + 1) |
3322 | >> (vol->cluster_size_bits |
3323 | - vol->sector_size_bits); |
3324 | } |
3325 | /* byte size must be a multiple of 8 */ |
3326 | expand->bitmap_size = ((bitmap_bits + 63) >> 3) & -8; |
3327 | expand->bitmap_allocated = ((expand->bitmap_size - 1) |
3328 | | (vol->cluster_size - 1)) + 1; |
3329 | expand->mft_lcn = (expand->boot_size |
3330 | + expand->bitmap_allocated) |
3331 | >> vol->cluster_size_bits; |
3332 | /* |
3333 | * Check whether $Boot, $Bitmap and $MFT can fit |
3334 | * into the expanded space. |
3335 | */ |
3336 | sectors_needed = (expand->boot_size + expand->mft_size |
3337 | + expand->bitmap_allocated) |
3338 | >> vol->sector_size_bits; |
3339 | if (!opt.info |
3340 | && (sectors_needed >= expand->sector_increment)) { |
3341 | err_printf("The expanded space cannot hold the new metadata\n"); |
3342 | err_printf(" expanded space %lld sectors\n", |
3343 | (long long)expand->sector_increment); |
3344 | err_printf(" needed space %lld sectors\n", |
3345 | (long long)sectors_needed); |
3346 | ok = FALSE; |
3347 | } |
3348 | } |
3349 | } |
3350 | if (ok) { |
3351 | advice = expand->byte_increment; |
3352 | /* the increment must be an integral number of clusters */ |
3353 | if (expand->byte_increment & (vol->cluster_size - 1)) { |
3354 | err_printf("Cannot expand volume without copying the data :\n"); |
3355 | err_printf("There are %d sectors in a cluster,\n", |
3356 | (int)(vol->cluster_size/vol->sector_size)); |
3357 | err_printf(" and the sector difference is not a multiple of %d\n", |
3358 | (int)(vol->cluster_size/vol->sector_size)); |
3359 | advice = expand->byte_increment & ~vol->cluster_size; |
3360 | ok = FALSE; |
3361 | } |
3362 | if (!ok) |
3363 | err_printf("You should increase the beginning of partition by %d sectors\n", |
3364 | (int)((expand->byte_increment - advice) |
3365 | >> vol->sector_size_bits)); |
3366 | } |
3367 | if (ok) |
3368 | ok = !check_expand_constraints(expand); |
3369 | if (ok && opt.info) { |
3370 | minimum_size = (expand->original_sectors |
3371 | << vol->sector_size_bits) |
3372 | + expand->boot_size |
3373 | + expand->mft_size |
3374 | + expand->bitmap_allocated; |
3375 | |
3376 | printf("You must expand the partition to at least %lld bytes,\n", |
3377 | (long long)(minimum_size + vol->sector_size)); |
3378 | printf("and you may add a multiple of %ld bytes to this size.\n", |
3379 | (long)vol->cluster_size); |
3380 | printf("The minimum NTFS volume size is %lld bytes\n", |
3381 | (long long)minimum_size); |
3382 | ok = FALSE; |
3383 | } |
3384 | return (ok); |
3385 | } |
3386 | |
3387 | static int set_bitmap(expand_t *expand, runlist_element *rl) |
3388 | { |
3389 | int res; |
3390 | s64 lcn; |
3391 | s64 lcn_end; |
3392 | BOOL reallocated; |
3393 | |
3394 | res = -1; |
3395 | reallocated = FALSE; |
3396 | if ((rl->lcn >= 0) |
3397 | && (rl->length > 0) |
3398 | && ((rl->lcn + rl->length) |
3399 | <= (expand->vol->nr_clusters + expand->cluster_increment))) { |
3400 | lcn = rl->lcn; |
3401 | lcn_end = lcn + rl->length; |
3402 | while ((lcn & 7) && (lcn < lcn_end)) { |
3403 | if (expand->bitmap[lcn >> 3] & 1 << (lcn & 7)) |
3404 | reallocated = TRUE; |
3405 | expand->bitmap[lcn >> 3] |= 1 << (lcn & 7); |
3406 | lcn++; |
3407 | } |
3408 | while ((lcn_end - lcn) >= 8) { |
3409 | if (expand->bitmap[lcn >> 3]) |
3410 | reallocated = TRUE; |
3411 | expand->bitmap[lcn >> 3] = 255; |
3412 | lcn += 8; |
3413 | } |
3414 | while (lcn < lcn_end) { |
3415 | if (expand->bitmap[lcn >> 3] & 1 << (lcn & 7)) |
3416 | reallocated = TRUE; |
3417 | expand->bitmap[lcn >> 3] |= 1 << (lcn & 7); |
3418 | lcn++; |
3419 | } |
3420 | if (reallocated) |
3421 | err_printf("Reallocated cluster found in run" |
3422 | " lcn 0x%llx length %lld\n", |
3423 | (long long)rl->lcn,(long long)rl->length); |
3424 | else |
3425 | res = 0; |
3426 | } else { |
3427 | err_printf("Bad run : lcn 0x%llx length %lld\n", |
3428 | (long long)rl->lcn,(long long)rl->length); |
3429 | } |
3430 | return (res); |
3431 | } |
3432 | |
3433 | /* |
3434 | * Write the backup bootsector |
3435 | * |
3436 | * When this has been done, the resizing cannot be done again |
3437 | */ |
3438 | |
3439 | static int write_bootsector(expand_t *expand) |
3440 | { |
3441 | ntfs_volume *vol; |
3442 | s64 bw; |
3443 | int res; |
3444 | |
3445 | res = -1; |
3446 | vol = expand->vol; |
3447 | if (opt.verbose) |
3448 | ntfs_log_verbose("Rewriting the backup bootsector\n"); |
3449 | if (opt.ro_flag) |
3450 | bw = vol->sector_size; |
3451 | else |
3452 | bw = ntfs_pwrite(vol->dev, |
3453 | expand->new_sectors*vol->sector_size, |
3454 | vol->sector_size, expand->bootsector); |
3455 | if (bw == vol->sector_size) |
3456 | res = 0; |
3457 | else { |
3458 | if (bw != -1) |
3459 | errno = EINVAL; |
3460 | if (!bw) |
3461 | err_printf("Failed to rewrite the bootsector (size=0)\n"); |
3462 | else |
3463 | err_printf("Error rewriting the bootsector"); |
3464 | } |
3465 | return (res); |
3466 | } |
3467 | |
3468 | /* |
3469 | * Write the new main bitmap |
3470 | */ |
3471 | |
3472 | static int write_bitmap(expand_t *expand) |
3473 | { |
3474 | ntfs_volume *vol; |
3475 | s64 bw; |
3476 | u64 cluster; |
3477 | int res; |
3478 | |
3479 | res = -1; |
3480 | vol = expand->vol; |
3481 | cluster = vol->nr_clusters + expand->cluster_increment; |
3482 | while (cluster < (expand->bitmap_size << 3)) { |
3483 | expand->bitmap[cluster >> 3] |= 1 << (cluster & 7); |
3484 | cluster++; |
3485 | } |
3486 | if (opt.verbose) |
3487 | ntfs_log_verbose("Writing the new bitmap...\n"); |
3488 | /* write the full allocation (to avoid having to read) */ |
3489 | if (opt.ro_flag) |
3490 | bw = expand->bitmap_allocated; |
3491 | else |
3492 | bw = ntfs_pwrite(vol->dev, expand->boot_size, |
3493 | expand->bitmap_allocated, expand->bitmap); |
3494 | if (bw == (s64)expand->bitmap_allocated) |
3495 | res = 0; |
3496 | else { |
3497 | if (bw != -1) |
3498 | errno = EINVAL; |
3499 | if (!bw) |
3500 | err_printf("Failed to write the bitmap (size=0)\n"); |
3501 | else |
3502 | err_printf("Error rewriting the bitmap"); |
3503 | } |
3504 | return (res); |
3505 | } |
3506 | |
3507 | /* |
3508 | * Copy the $MFT to $MFTMirr |
3509 | * |
3510 | * The $MFTMirr is not relocated as it should be kept away from $MFT. |
3511 | * Apart from the backup bootsector, this is the only part which is |
3512 | * overwritten. This has no effect on being able to redo the resizing |
3513 | * if something goes wrong, as the $MFTMirr is never read. However |
3514 | * this is done near the end of the resizing. |
3515 | */ |
3516 | |
3517 | static int copy_mftmirr(expand_t *expand) |
3518 | { |
3519 | ntfs_volume *vol; |
3520 | s64 pos; |
3521 | s64 inum; |
3522 | int res; |
3523 | u16 usa_ofs; |
3524 | le16 *pusn; |
3525 | u16 usn; |
3526 | |
3527 | if (opt.verbose) |
3528 | ntfs_log_verbose("Copying $MFT to $MFTMirr...\n"); |
3529 | vol = expand->vol; |
3530 | res = 0; |
3531 | for (inum=FILE_MFT; !res && (inum<=FILE_Volume); inum++) { |
3532 | /* read the new $MFT */ |
3533 | pos = (expand->mft_lcn << vol->cluster_size_bits) |
3534 | + (inum << vol->mft_record_size_bits); |
3535 | if (ntfs_mst_pread(vol->dev, pos, 1, vol->mft_record_size, |
3536 | expand->mrec) == 1) { |
3537 | /* overwrite the old $MFTMirr */ |
3538 | pos = (vol->mftmirr_lcn << vol->cluster_size_bits) |
3539 | + (inum << vol->mft_record_size_bits) |
3540 | + expand->byte_increment; |
3541 | usa_ofs = le16_to_cpu(expand->mrec->usa_ofs); |
3542 | pusn = (le16*)((u8*)expand->mrec + usa_ofs); |
3543 | usn = le16_to_cpu(*pusn) - 1; |
3544 | if (!usn || (usn == 0xffff)) |
3545 | usn = -2; |
3546 | *pusn = cpu_to_le16(usn); |
3547 | if (!opt.ro_flag |
3548 | && (ntfs_mst_pwrite(vol->dev, pos, 1, |
3549 | vol->mft_record_size, expand->mrec) != 1)) { |
3550 | err_printf("Failed to overwrite the old $MFTMirr\n"); |
3551 | res = -1; |
3552 | } |
3553 | } else { |
3554 | err_printf("Failed to write the new $MFT\n"); |
3555 | res = -1; |
3556 | } |
3557 | } |
3558 | return (res); |
3559 | } |
3560 | |
3561 | /* |
3562 | * Copy the $Boot, including the bootsector |
3563 | * |
3564 | * When the bootsector has been copied, repair tools are able to |
3565 | * fix things, but this is dangerous if the other metadata do |
3566 | * not point to actual user data. So this must be done near the end |
3567 | * of resizing. |
3568 | */ |
3569 | |
3570 | static int copy_boot(expand_t *expand) |
3571 | { |
3572 | NTFS_BOOT_SECTOR *bs; |
3573 | char *buf; |
3574 | ntfs_volume *vol; |
3575 | s64 mftmirr_lcn; |
3576 | s64 written; |
3577 | u32 boot_cnt; |
3578 | u32 hidden_sectors; |
3579 | le32 hidden_sectors_le; |
3580 | int res; |
3581 | |
3582 | if (opt.verbose) |
3583 | ntfs_log_verbose("Copying $Boot...\n"); |
3584 | vol = expand->vol; |
3585 | res = 0; |
3586 | buf = (char*)ntfs_malloc(vol->cluster_size); |
3587 | if (buf) { |
3588 | /* set the new volume parameters in the bootsector */ |
3589 | bs = (NTFS_BOOT_SECTOR*)expand->bootsector; |
3590 | bs->number_of_sectors = cpu_to_le64(expand->new_sectors); |
3591 | bs->mft_lcn = cpu_to_le64(expand->mft_lcn); |
3592 | mftmirr_lcn = vol->mftmirr_lcn + expand->cluster_increment; |
3593 | bs->mftmirr_lcn = cpu_to_le64(mftmirr_lcn); |
3594 | /* the hidden sectors are needed to boot into windows */ |
3595 | memcpy(&hidden_sectors_le,&bs->bpb.hidden_sectors,4); |
3596 | /* alignment messed up on the Sparc */ |
3597 | if (hidden_sectors_le) { |
3598 | hidden_sectors = le32_to_cpu(hidden_sectors_le); |
3599 | if (hidden_sectors >= expand->sector_increment) |
3600 | hidden_sectors -= expand->sector_increment; |
3601 | else |
3602 | hidden_sectors = 0; |
3603 | hidden_sectors_le = cpu_to_le32(hidden_sectors); |
3604 | memcpy(&bs->bpb.hidden_sectors,&hidden_sectors_le,4); |
3605 | } |
3606 | written = 0; |
3607 | boot_cnt = expand->boot_size >> vol->cluster_size_bits; |
3608 | while (!res && (written < boot_cnt)) { |
3609 | lseek_to_cluster(vol, expand->cluster_increment + written); |
3610 | if (!read_all(vol->dev, buf, vol->cluster_size)) { |
3611 | if (!written) |
3612 | memcpy(buf, expand->bootsector, vol->sector_size); |
3613 | lseek_to_cluster(vol, written); |
3614 | if (!opt.ro_flag |
3615 | && write_all(vol->dev, buf, vol->cluster_size)) { |
3616 | err_printf("Failed to write the new $Boot\n"); |
3617 | res = -1; |
3618 | } else |
3619 | written++; |
3620 | } else { |
3621 | err_printf("Failed to read the old $Boot\n"); |
3622 | res = -1; |
3623 | } |
3624 | } |
3625 | free(buf); |
3626 | } else { |
3627 | err_printf("Failed to allocate buffer\n"); |
3628 | res = -1; |
3629 | } |
3630 | return (res); |
3631 | } |
3632 | |
3633 | /* |
3634 | * Process delayed runlist updates |
3635 | * |
3636 | * This is derived from delayed_updates() and they should |
3637 | * both be merged when the new code is considered safe. |
3638 | */ |
3639 | |
3640 | static void delayed_expand(ntfs_volume *vol, struct DELAYED *delayed, |
3641 | struct progress_bar *progress) |
3642 | { |
3643 | unsigned long count; |
3644 | struct DELAYED *current; |
3645 | int step = 100; |
3646 | |
3647 | if (delayed) { |
3648 | if (opt.verbose) |
3649 | ntfs_log_verbose("Delayed updating of overflowing runlists...\n"); |
3650 | count = 0; |
3651 | /* count by steps because of inappropriate resolution */ |
3652 | for (current=delayed; current; current=current->next) |
3653 | count += step; |
3654 | progress_init(progress, 0, count, |
3655 | (opt.show_progress ? NTFS_PROGBAR : 0)); |
3656 | current = delayed; |
3657 | count = 0; |
3658 | while (current) { |
3659 | delayed = current; |
3660 | if (!opt.ro_flag) |
3661 | expand_attribute_runlist(vol, delayed); |
3662 | count += step; |
3663 | progress_update(progress, count); |
3664 | current = current->next; |
3665 | if (delayed->attr_name) |
3666 | free(delayed->attr_name); |
3667 | free(delayed->head_rl); |
3668 | free(delayed); |
3669 | } |
3670 | } |
3671 | } |
3672 | |
3673 | /* |
3674 | * Expand the sizes in indexes for inodes which were expanded |
3675 | * |
3676 | * Only the new $Bitmap sizes are identified as needed to be |
3677 | * adjusted in index. The $BadClus is only expanded in an |
3678 | * alternate data stream, whose sizes are not present in the index. |
3679 | * |
3680 | * This is modifying the initial data, and can only be done when |
3681 | * the volume has been reopened after expanding. |
3682 | */ |
3683 | |
3684 | static int expand_index_sizes(expand_t *expand) |
3685 | { |
3686 | ntfs_inode *ni; |
3687 | int res; |
3688 | |
3689 | res = -1; |
3690 | ni = ntfs_inode_open(expand->vol, FILE_Bitmap); |
3691 | if (ni) { |
3692 | NInoSetDirty(ni); |
3693 | NInoFileNameSetDirty(ni); |
3694 | ntfs_inode_close(ni); |
3695 | res = 0; |
3696 | } |
3697 | return (res); |
3698 | } |
3699 | |
3700 | /* |
3701 | * Update a runlist into an attribute |
3702 | * |
3703 | * This is derived from replace_attribute_runlist() and they should |
3704 | * both be merged when the new code is considered safe. |
3705 | */ |
3706 | |
3707 | static int update_runlist(expand_t *expand, s64 inum, |
3708 | ATTR_RECORD *a, runlist_element *rl) |
3709 | { |
3710 | ntfs_resize_t resize; |
3711 | ntfs_attr_search_ctx ctx; |
3712 | ntfs_volume *vol; |
3713 | MFT_RECORD *mrec; |
3714 | runlist *head_rl; |
3715 | int mp_size; |
3716 | int l; |
3717 | int must_delay; |
3718 | void *mp; |
3719 | |
3720 | vol = expand->vol; |
3721 | mrec = expand->mrec; |
3722 | head_rl = rl; |
3723 | rl_fixup(&rl); |
3724 | if ((mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, |
3725 | 0, INT_MAX)) == -1) |
3726 | perr_exit("ntfs_get_size_for_mapping_pairs"); |
3727 | |
3728 | if (a->name_length) { |
3729 | u16 name_offs = le16_to_cpu(a->name_offset); |
3730 | u16 mp_offs = le16_to_cpu(a->mapping_pairs_offset); |
3731 | |
3732 | if (name_offs >= mp_offs) |
3733 | err_exit("Attribute name is after mapping pairs! " |
3734 | "Please report!\n"); |
3735 | } |
3736 | |
3737 | /* CHECKME: don't trust mapping_pairs is always the last item in the |
3738 | attribute, instead check for the real size/space */ |
3739 | l = (int)le32_to_cpu(a->length) - le16_to_cpu(a->mapping_pairs_offset); |
3740 | must_delay = 0; |
3741 | if (mp_size > l) { |
3742 | s32 remains_size; |
3743 | char *next_attr; |
3744 | |
3745 | ntfs_log_verbose("Enlarging attribute header ...\n"); |
3746 | |
3747 | mp_size = (mp_size + 7) & ~7; |
3748 | |
3749 | ntfs_log_verbose("Old mp size : %d\n", l); |
3750 | ntfs_log_verbose("New mp size : %d\n", mp_size); |
3751 | ntfs_log_verbose("Bytes in use : %u\n", (unsigned int) |
3752 | le32_to_cpu(mrec->bytes_in_use)); |
3753 | |
3754 | next_attr = (char *)a + le32_to_cpu(a->length); |
3755 | l = mp_size - l; |
3756 | |
3757 | ntfs_log_verbose("Bytes in use new : %u\n", l + (unsigned int) |
3758 | le32_to_cpu(mrec->bytes_in_use)); |
3759 | ntfs_log_verbose("Bytes allocated : %u\n", (unsigned int) |
3760 | le32_to_cpu(mrec->bytes_allocated)); |
3761 | |
3762 | remains_size = le32_to_cpu(mrec->bytes_in_use); |
3763 | remains_size -= (next_attr - (char *)mrec); |
3764 | |
3765 | ntfs_log_verbose("increase : %d\n", l); |
3766 | ntfs_log_verbose("shift : %lld\n", |
3767 | (long long)remains_size); |
3768 | if (le32_to_cpu(mrec->bytes_in_use) + l > |
3769 | le32_to_cpu(mrec->bytes_allocated)) { |
3770 | ntfs_log_verbose("Queuing expansion for later processing\n"); |
3771 | /* hack for reusing unmodified old code ! */ |
3772 | resize.ctx = &ctx; |
3773 | ctx.attr = a; |
3774 | ctx.mrec = mrec; |
3775 | resize.mref = inum; |
3776 | resize.delayed_runlists = expand->delayed_runlists; |
3777 | must_delay = 1; |
3778 | replace_later(&resize,rl,head_rl); |
3779 | expand->delayed_runlists = resize.delayed_runlists; |
3780 | } else { |
3781 | memmove(next_attr + l, next_attr, remains_size); |
3782 | mrec->bytes_in_use = cpu_to_le32(l + |
3783 | le32_to_cpu(mrec->bytes_in_use)); |
3784 | a->length = cpu_to_le32(le32_to_cpu(a->length) + l); |
3785 | } |
3786 | } |
3787 | |
3788 | if (!must_delay) { |
3789 | mp = ntfs_calloc(mp_size); |
3790 | if (!mp) |
3791 | perr_exit("ntfsc_calloc couldn't get memory"); |
3792 | |
3793 | if (ntfs_mapping_pairs_build(vol, (u8*)mp, mp_size, rl, 0, NULL)) |
3794 | perr_exit("ntfs_mapping_pairs_build"); |
3795 | |
3796 | memmove((u8*)a + le16_to_cpu(a->mapping_pairs_offset), mp, mp_size); |
3797 | |
3798 | free(mp); |
3799 | } |
3800 | return (must_delay); |
3801 | } |
3802 | |
3803 | /* |
3804 | * Create a minimal valid MFT record |
3805 | */ |
3806 | |
3807 | static int minimal_record(expand_t *expand, MFT_RECORD *mrec) |
3808 | { |
3809 | int usa_count; |
3810 | u32 bytes_in_use; |
3811 | |
3812 | memset(mrec,0,expand->vol->mft_record_size); |
3813 | mrec->magic = magic_FILE; |
3814 | mrec->usa_ofs = const_cpu_to_le16(sizeof(MFT_RECORD)); |
3815 | usa_count = expand->vol->mft_record_size / NTFS_BLOCK_SIZE + 1; |
3816 | mrec->usa_count = cpu_to_le16(usa_count); |
3817 | bytes_in_use = (sizeof(MFT_RECORD) + 2*usa_count + 7) & -8; |
3818 | memset(((char*)mrec) + bytes_in_use, 255, 4); /* AT_END */ |
3819 | bytes_in_use += 8; |
3820 | mrec->bytes_in_use = cpu_to_le32(bytes_in_use); |
3821 | mrec->bytes_allocated = cpu_to_le32(expand->vol->mft_record_size); |
3822 | return (0); |
3823 | } |
3824 | |
3825 | /* |
3826 | * Rebase all runlists of an MFT record |
3827 | * |
3828 | * Iterate through all its attributes and offset the non resident ones |
3829 | */ |
3830 | |
3831 | static int rebase_runlists(expand_t *expand, s64 inum) |
3832 | { |
3833 | MFT_RECORD *mrec; |
3834 | ATTR_RECORD *a; |
3835 | runlist_element *rl; |
3836 | runlist_element *prl; |
3837 | u32 offset; |
3838 | int res; |
3839 | |
3840 | res = 0; |
3841 | mrec = expand->mrec; |
3842 | offset = le16_to_cpu(mrec->attrs_offset); |
3843 | a = (ATTR_RECORD*)((char*)mrec + offset); |
3844 | while (!res && (a->type != AT_END) |
3845 | && (offset < le32_to_cpu(mrec->bytes_in_use))) { |
3846 | if (a->non_resident) { |
3847 | rl = ntfs_mapping_pairs_decompress(expand->vol, a, |
3848 | (runlist_element*)NULL); |
3849 | if (rl) { |
3850 | for (prl=rl; prl->length; prl++) |
3851 | if (prl->lcn >= 0) { |
3852 | prl->lcn += expand->cluster_increment; |
3853 | if (set_bitmap(expand,prl)) |
3854 | res = -1; |
3855 | } |
3856 | if (update_runlist(expand,inum,a,rl)) { |
3857 | ntfs_log_verbose("Runlist updating has to be delayed\n"); |
3858 | } else |
3859 | free(rl); |
3860 | } else { |
3861 | err_printf("Could not get a runlist of inode %lld\n", |
3862 | (long long)inum); |
3863 | res = -1; |
3864 | } |
3865 | } |
3866 | offset += le32_to_cpu(a->length); |
3867 | a = (ATTR_RECORD*)((char*)mrec + offset); |
3868 | } |
3869 | return (res); |
3870 | } |
3871 | |
3872 | /* |
3873 | * Rebase the runlists present in records with relocated $DATA |
3874 | * |
3875 | * The returned runlist is the old rebased runlist for $DATA, |
3876 | * which is generally different from the new computed runlist. |
3877 | */ |
3878 | |
3879 | static runlist_element *rebase_runlists_meta(expand_t *expand, s64 inum) |
3880 | { |
3881 | MFT_RECORD *mrec; |
3882 | ATTR_RECORD *a; |
3883 | ntfs_volume *vol; |
3884 | runlist_element *rl; |
3885 | runlist_element *old_rl; |
3886 | runlist_element *prl; |
3887 | runlist_element new_rl[2]; |
3888 | s64 data_size; |
3889 | s64 allocated_size; |
3890 | s64 lcn; |
3891 | u64 lth; |
3892 | u32 offset; |
3893 | BOOL keeprl; |
3894 | int res; |
3895 | |
3896 | res = 0; |
3897 | old_rl = (runlist_element*)NULL; |
3898 | vol = expand->vol; |
3899 | mrec = expand->mrec; |
3900 | switch (inum) { |
3901 | case FILE_Boot : |
3902 | lcn = 0; |
3903 | lth = expand->boot_size >> vol->cluster_size_bits; |
3904 | data_size = expand->boot_size; |
3905 | break; |
3906 | case FILE_Bitmap : |
3907 | lcn = expand->boot_size >> vol->cluster_size_bits; |
3908 | lth = expand->bitmap_allocated >> vol->cluster_size_bits; |
3909 | data_size = expand->bitmap_size; |
3910 | break; |
3911 | case FILE_MFT : |
3912 | lcn = (expand->boot_size + expand->bitmap_allocated) |
3913 | >> vol->cluster_size_bits; |
3914 | lth = expand->mft_size >> vol->cluster_size_bits; |
3915 | data_size = expand->mft_size; |
3916 | break; |
3917 | case FILE_BadClus : |
3918 | lcn = 0; /* not used */ |
3919 | lth = vol->nr_clusters + expand->cluster_increment; |
3920 | data_size = lth << vol->cluster_size_bits; |
3921 | break; |
3922 | default : |
3923 | lcn = lth = data_size = 0; |
3924 | res = -1; |
3925 | } |
3926 | allocated_size = lth << vol->cluster_size_bits; |
3927 | offset = le16_to_cpu(mrec->attrs_offset); |
3928 | a = (ATTR_RECORD*)((char*)mrec + offset); |
3929 | while (!res && (a->type != AT_END) |
3930 | && (offset < le32_to_cpu(mrec->bytes_in_use))) { |
3931 | if (a->non_resident) { |
3932 | keeprl = FALSE; |
3933 | rl = ntfs_mapping_pairs_decompress(vol, a, |
3934 | (runlist_element*)NULL); |
3935 | if (rl) { |
3936 | /* rebase the old runlist */ |
3937 | for (prl=rl; prl->length; prl++) |
3938 | if (prl->lcn >= 0) { |
3939 | prl->lcn += expand->cluster_increment; |
3940 | if ((a->type != AT_DATA) |
3941 | && set_bitmap(expand,prl)) |
3942 | res = -1; |
3943 | } |
3944 | /* relocated unnamed data (not $BadClus) */ |
3945 | if ((a->type == AT_DATA) |
3946 | && !a->name_length |
3947 | && (inum != FILE_BadClus)) { |
3948 | old_rl = rl; |
3949 | rl = new_rl; |
3950 | keeprl = TRUE; |
3951 | rl[0].vcn = 0; |
3952 | rl[0].lcn = lcn; |
3953 | rl[0].length = lth; |
3954 | rl[1].vcn = lth; |
3955 | rl[1].lcn = LCN_ENOENT; |
3956 | rl[1].length = 0; |
3957 | if (set_bitmap(expand,rl)) |
3958 | res = -1; |
3959 | a->data_size = cpu_to_le64(data_size); |
3960 | a->initialized_size = a->data_size; |
3961 | a->allocated_size |
3962 | = cpu_to_le64(allocated_size); |
3963 | a->highest_vcn = cpu_to_le64(lth - 1); |
3964 | } |
3965 | /* expand the named data for $BadClus */ |
3966 | if ((a->type == AT_DATA) |
3967 | && a->name_length |
3968 | && (inum == FILE_BadClus)) { |
3969 | old_rl = rl; |
3970 | keeprl = TRUE; |
3971 | prl = rl; |
3972 | if (prl->length) { |
3973 | while (prl[1].length) |
3974 | prl++; |
3975 | prl->length = lth - prl->vcn; |
3976 | prl[1].vcn = lth; |
3977 | } else |
3978 | prl->vcn = lth; |
3979 | a->data_size = cpu_to_le64(data_size); |
3980 | /* do not change the initialized size */ |
3981 | a->allocated_size |
3982 | = cpu_to_le64(allocated_size); |
3983 | a->highest_vcn = cpu_to_le64(lth - 1); |
3984 | } |
3985 | if (!res && update_runlist(expand,inum,a,rl)) |
3986 | res = -1; |
3987 | if (!keeprl) |
3988 | free(rl); |
3989 | } else { |
3990 | err_printf("Could not get the data runlist of inode %lld\n", |
3991 | (long long)inum); |
3992 | res = -1; |
3993 | } |
3994 | } |
3995 | offset += le32_to_cpu(a->length); |
3996 | a = (ATTR_RECORD*)((char*)mrec + offset); |
3997 | } |
3998 | if (res && old_rl) { |
3999 | free(old_rl); |
4000 | old_rl = (runlist_element*)NULL; |
4001 | } |
4002 | return (old_rl); |
4003 | } |
4004 | |
4005 | /* |
4006 | * Rebase all runlists in an MFT record |
4007 | * |
4008 | * Read from the old $MFT, rebase the runlists, |
4009 | * and write to the new $MFT |
4010 | */ |
4011 | |
4012 | static int rebase_inode(expand_t *expand, const runlist_element *prl, |
4013 | s64 inum, s64 jnum) |
4014 | { |
4015 | MFT_RECORD *mrec; |
4016 | runlist_element *rl; |
4017 | ntfs_volume *vol; |
4018 | s64 pos; |
4019 | int res; |
4020 | |
4021 | res = 0; |
4022 | vol = expand->vol; |
4023 | mrec = expand->mrec; |
4024 | if (expand->mft_bitmap[inum >> 3] & (1 << (inum & 7))) { |
4025 | pos = (prl->lcn << vol->cluster_size_bits) |
4026 | + ((inum - jnum) << vol->mft_record_size_bits); |
4027 | if ((ntfs_mst_pread(vol->dev, pos, 1, |
4028 | vol->mft_record_size, mrec) == 1) |
4029 | && (mrec->flags & MFT_RECORD_IN_USE)) { |
4030 | switch (inum) { |
4031 | case FILE_Bitmap : |
4032 | case FILE_Boot : |
4033 | case FILE_BadClus : |
4034 | rl = rebase_runlists_meta(expand, inum); |
4035 | if (rl) |
4036 | free(rl); |
4037 | else |
4038 | res = -1; |
4039 | break; |
4040 | default : |
4041 | res = rebase_runlists(expand, inum); |
4042 | break; |
4043 | } |
4044 | } else { |
4045 | err_printf("Could not read the $MFT entry %lld\n", |
4046 | (long long)inum); |
4047 | res = -1; |
4048 | } |
4049 | } else { |
4050 | /* |
4051 | * Replace unused records (possibly uninitialized) |
4052 | * by minimal valid records, not marked in use |
4053 | */ |
4054 | res = minimal_record(expand,mrec); |
4055 | } |
4056 | if (!res) { |
4057 | pos = (expand->mft_lcn << vol->cluster_size_bits) |
4058 | + (inum << vol->mft_record_size_bits); |
4059 | if (opt.verbose) |
4060 | ntfs_log_verbose("Rebasing inode %lld cluster 0x%llx\n", |
4061 | (long long)inum, |
4062 | (long long)(pos >> vol->cluster_size_bits)); |
4063 | if (!opt.ro_flag |
4064 | && (ntfs_mst_pwrite(vol->dev, pos, 1, |
4065 | vol->mft_record_size, mrec) != 1)) { |
4066 | err_printf("Could not write the $MFT entry %lld\n", |
4067 | (long long)inum); |
4068 | res = -1; |
4069 | } |
4070 | } |
4071 | return (res); |
4072 | } |
4073 | |
4074 | /* |
4075 | * Rebase all runlists |
4076 | * |
4077 | * First get the $MFT and define its location in the expanded space, |
4078 | * then rebase the other inodes and write them to the new $MFT |
4079 | */ |
4080 | |
4081 | static int rebase_all_inodes(expand_t *expand) |
4082 | { |
4083 | ntfs_volume *vol; |
4084 | MFT_RECORD *mrec; |
4085 | s64 inum; |
4086 | s64 jnum; |
4087 | s64 inodecnt; |
4088 | s64 pos; |
4089 | s64 got; |
4090 | int res; |
4091 | runlist_element *mft_rl; |
4092 | runlist_element *prl; |
4093 | |
4094 | res = 0; |
4095 | mft_rl = (runlist_element*)NULL; |
4096 | vol = expand->vol; |
4097 | mrec = expand->mrec; |
4098 | inum = 0; |
4099 | pos = (vol->mft_lcn + expand->cluster_increment) |
4100 | << vol->cluster_size_bits; |
4101 | got = ntfs_mst_pread(vol->dev, pos, 1, |
4102 | vol->mft_record_size, mrec); |
4103 | if ((got == 1) && (mrec->flags & MFT_RECORD_IN_USE)) { |
4104 | pos = expand->mft_lcn << vol->cluster_size_bits; |
4105 | if (opt.verbose) |
4106 | ntfs_log_verbose("Rebasing inode %lld cluster 0x%llx\n", |
4107 | (long long)inum, |
4108 | (long long)(pos >> vol->cluster_size_bits)); |
4109 | mft_rl = rebase_runlists_meta(expand, FILE_MFT); |
4110 | if (!mft_rl |
4111 | || (!opt.ro_flag |
4112 | && (ntfs_mst_pwrite(vol->dev, pos, 1, |
4113 | vol->mft_record_size, mrec) != 1))) |
4114 | res = -1; |
4115 | else { |
4116 | for (prl=mft_rl; prl->length; prl++) { } |
4117 | inodecnt = (prl->vcn << vol->cluster_size_bits) |
4118 | >> vol->mft_record_size_bits; |
4119 | progress_init(expand->progress, 0, inodecnt, |
4120 | (opt.show_progress ? NTFS_PROGBAR : 0)); |
4121 | prl = mft_rl; |
4122 | jnum = 0; |
4123 | do { |
4124 | inum++; |
4125 | while (prl->length |
4126 | && ((inum << vol->mft_record_size_bits) |
4127 | >= ((prl->vcn + prl->length) |
4128 | << vol->cluster_size_bits))) { |
4129 | prl++; |
4130 | jnum = inum; |
4131 | } |
4132 | progress_update(expand->progress, inum); |
4133 | if (prl->length) { |
4134 | res = rebase_inode(expand, |
4135 | prl,inum,jnum); |
4136 | } |
4137 | } while (!res && prl->length); |
4138 | free(mft_rl); |
4139 | } |
4140 | } else { |
4141 | err_printf("Could not read the old $MFT\n"); |
4142 | res = -1; |
4143 | } |
4144 | return (res); |
4145 | } |
4146 | |
4147 | |
4148 | |
4149 | /* |
4150 | * Get the old volume parameters from the backup bootsector |
4151 | * |
4152 | */ |
4153 | |
4154 | static ntfs_volume *get_volume_data(expand_t *expand, struct ntfs_device *dev, |
4155 | s32 sector_size) |
4156 | { |
4157 | s64 br; |
4158 | ntfs_volume *vol; |
4159 | le16 sector_size_le; |
4160 | NTFS_BOOT_SECTOR *bs; |
4161 | BOOL ok; |
4162 | |
4163 | ok = FALSE; |
4164 | vol = (ntfs_volume*)ntfs_malloc(sizeof(ntfs_volume)); |
4165 | expand->bootsector = (char*)ntfs_malloc(sector_size); |
4166 | if (vol && expand->bootsector) { |
4167 | expand->vol = vol; |
4168 | vol->dev = dev; |
4169 | br = ntfs_pread(dev, expand->new_sectors*sector_size, |
4170 | sector_size, expand->bootsector); |
4171 | if (br != sector_size) { |
4172 | if (br != -1) |
4173 | errno = EINVAL; |
4174 | if (!br) |
4175 | err_printf("Failed to read the backup bootsector (size=0)\n"); |
4176 | else |
4177 | err_printf("Error reading the backup bootsector"); |
4178 | } else { |
4179 | bs = (NTFS_BOOT_SECTOR*)expand->bootsector; |
4180 | /* alignment problem on Sparc, even doing memcpy() */ |
4181 | sector_size_le = cpu_to_le16(sector_size); |
4182 | if (!memcmp(§or_size_le, |
4183 | &bs->bpb.bytes_per_sector,2) |
4184 | && ntfs_boot_sector_is_ntfs(bs) |
4185 | && !ntfs_boot_sector_parse(vol, bs)) { |
4186 | expand->original_sectors |
4187 | = le64_to_cpu(bs->number_of_sectors); |
4188 | expand->mrec = (MFT_RECORD*) |
4189 | ntfs_malloc(vol->mft_record_size); |
4190 | if (expand->mrec |
4191 | && can_expand(expand,vol)) { |
4192 | ntfs_log_verbose("Resizing is possible\n"); |
4193 | ok = TRUE; |
4194 | } |
4195 | } else |
4196 | err_printf("Could not get the old volume parameters " |
4197 | "from the backup bootsector\n"); |
4198 | } |
4199 | if (!ok) { |
4200 | free(vol); |
4201 | free(expand->bootsector); |
4202 | } |
4203 | } |
4204 | return (ok ? vol : (ntfs_volume*)NULL); |
4205 | } |
4206 | |
4207 | static int really_expand(expand_t *expand) |
4208 | { |
4209 | ntfs_volume *vol; |
4210 | struct ntfs_device *dev; |
4211 | int res; |
4212 | |
4213 | res = -1; |
4214 | |
4215 | expand->bitmap = (u8*)ntfs_calloc(expand->bitmap_allocated); |
4216 | if (expand->bitmap |
4217 | && get_mft_bitmap(expand)) { |
4218 | printf("\n*** WARNING ***\n\n"); |
4219 | printf("Expanding a volume is an experimental new feature\n"); |
4220 | if (!opt.ro_flag) |
4221 | printf("A first check with option -n is recommended\n"); |
4222 | printf("\nShould something go wrong during the actual" |
4223 | " resizing (power outage, etc.),\n"); |
4224 | printf("just restart the procedure, but DO NOT TRY to repair" |
4225 | " with chkdsk or similar,\n"); |
4226 | printf("until the resizing is over," |
4227 | " you would LOSE YOUR DATA !\n"); |
4228 | printf("\nYou have been warned !\n\n"); |
4229 | if (!opt.ro_flag && (opt.force-- <= 0)) |
4230 | proceed_question(); |
4231 | if (!rebase_all_inodes(expand) |
4232 | && !write_bitmap(expand) |
4233 | && !copy_mftmirr(expand) |
4234 | && !copy_boot(expand)) { |
4235 | free(expand->vol); |
4236 | expand->vol = (ntfs_volume*)NULL; |
4237 | free(expand->mft_bitmap); |
4238 | expand->mft_bitmap = (u8*)NULL; |
4239 | if (!opt.ro_flag) { |
4240 | /* the volume must be dirty, do not check */ |
4241 | opt.force++; |
4242 | vol = mount_volume(); |
4243 | if (vol) { |
4244 | dev = vol->dev; |
4245 | ntfs_log_verbose("Remounting the updated volume\n"); |
4246 | expand->vol = vol; |
4247 | ntfs_log_verbose("Delayed runlist updatings\n"); |
4248 | delayed_expand(vol, expand->delayed_runlists, |
4249 | expand->progress); |
4250 | expand->delayed_runlists |
4251 | = (struct DELAYED*)NULL; |
4252 | expand_index_sizes(expand); |
4253 | /* rewriting the backup bootsector, no return ticket now ! */ |
4254 | res = write_bootsector(expand); |
4255 | if (dev->d_ops->sync(dev) == -1) { |
4256 | printf("Could not sync\n"); |
4257 | res = -1; |
4258 | } |
4259 | ntfs_umount(vol,0); |
4260 | if (!res) |
4261 | printf("\nResizing completed successfully\n"); |
4262 | } |
4263 | } else { |
4264 | ntfs_log_verbose("Delayed runlist updatings\n"); |
4265 | delayed_expand(expand->vol, |
4266 | expand->delayed_runlists, |
4267 | expand->progress); |
4268 | expand->delayed_runlists |
4269 | = (struct DELAYED*)NULL; |
4270 | printf("\nAll checks have been completed successfully\n"); |
4271 | printf("Cannot check further in no-action mode\n"); |
4272 | } |
4273 | free(expand->bootsector); |
4274 | free(expand->mrec); |
4275 | } |
4276 | free(expand->bitmap); |
4277 | } else { |
4278 | err_printf("Failed to allocate memory\n"); |
4279 | } |
4280 | return (res); |
4281 | } |
4282 | |
4283 | /* |
4284 | * Expand a volume to beginning of partition |
4285 | * |
4286 | * We rely on the backup bootsector to determine the original |
4287 | * volume size and metadata. |
4288 | */ |
4289 | |
4290 | static int expand_to_beginning(void) |
4291 | { |
4292 | expand_t expand; |
4293 | struct progress_bar progress; |
4294 | int ret; |
4295 | ntfs_volume *vol; |
4296 | struct ntfs_device *dev; |
4297 | int sector_size; |
4298 | s64 new_sectors; |
4299 | |
4300 | ret = -1; |
4301 | dev = ntfs_device_alloc(opt.volume, 0, &ntfs_device_default_io_ops, |
4302 | NULL); |
4303 | if (dev) { |
4304 | if (!(*dev->d_ops->open)(dev, |
4305 | (opt.ro_flag ? O_RDONLY : O_RDWR))) { |
4306 | sector_size = ntfs_device_sector_size_get(dev); |
4307 | if (sector_size <= 0) { |
4308 | sector_size = 512; |
4309 | new_sectors = ntfs_device_size_get(dev, |
4310 | sector_size); |
4311 | if (!new_sectors) { |
4312 | sector_size = 4096; |
4313 | new_sectors = ntfs_device_size_get(dev, |
4314 | sector_size); |
4315 | } |
4316 | } else |
4317 | new_sectors = ntfs_device_size_get(dev, |
4318 | sector_size); |
4319 | if (new_sectors) { |
4320 | new_sectors--; /* last sector not counted */ |
4321 | expand.new_sectors = new_sectors; |
4322 | expand.progress = &progress; |
4323 | expand.delayed_runlists = (struct DELAYED*)NULL; |
4324 | vol = get_volume_data(&expand,dev,sector_size); |
4325 | if (vol) { |
4326 | expand.vol = vol; |
4327 | ret = really_expand(&expand); |
4328 | } |
4329 | } |
4330 | (*dev->d_ops->close)(dev); |
4331 | } else { |
4332 | err_exit("Couldn't open volume '%s'!\n", opt.volume); |
4333 | } |
4334 | ntfs_device_free(dev); |
4335 | } |
4336 | return (ret); |
4337 | } |
4338 | |
4339 | |
4340 | int main(int argc, char **argv) |
4341 | { |
4342 | ntfsck_t fsck; |
4343 | ntfs_resize_t resize; |
4344 | s64 new_size = 0; /* in clusters; 0 = --info w/o --size */ |
4345 | s64 device_size; /* in bytes */ |
4346 | ntfs_volume *vol = NULL; |
4347 | |
4348 | ntfs_log_set_handler(ntfs_log_handler_outerr); |
4349 | |
4350 | printf("%s v%s (libntfs-3g)\n", EXEC_NAME, VERSION); |
4351 | |
4352 | if (!parse_options(argc, argv)) |
4353 | return 1; |
4354 | |
4355 | utils_set_locale(); |
4356 | |
4357 | /* |
4358 | * If we're just checking the device, we'll do it first, |
4359 | * and exit out, no matter what we find. |
4360 | */ |
4361 | if (opt.check) { |
4362 | vol = check_volume(); |
4363 | #if CLEAN_EXIT |
4364 | if (vol) |
4365 | ntfs_umount(vol,0); |
4366 | #endif |
4367 | exit(0); |
4368 | } else { |
4369 | if (opt.expand) { |
4370 | /* |
4371 | * If we are to expand to beginning of partition, do |
4372 | * not try to mount : when merging two partitions, |
4373 | * the beginning of the partition would contain an |
4374 | * old filesystem which is not the one to expand. |
4375 | */ |
4376 | if (expand_to_beginning() && !opt.info) |
4377 | exit(1); |
4378 | return (0); |
4379 | } |
4380 | } |
4381 | |
4382 | if (!(vol = mount_volume())) |
4383 | err_exit("Couldn't open volume '%s'!\n", opt.volume); |
4384 | |
4385 | device_size = ntfs_device_size_get(vol->dev, vol->sector_size); |
4386 | device_size *= vol->sector_size; |
4387 | if (device_size <= 0) |
4388 | err_exit("Couldn't get device size (%lld)!\n", |
4389 | (long long)device_size); |
4390 | |
4391 | if (!opt.infombonly) |
4392 | print_vol_size("Current device size", device_size); |
4393 | |
4394 | if (device_size < vol->nr_clusters * vol->cluster_size) |
4395 | err_exit("Current NTFS volume size is bigger than the device " |
4396 | "size!\nCorrupt partition table or incorrect device " |
4397 | "partitioning?\n"); |
4398 | |
4399 | if (!opt.bytes && !opt.info && !opt.infombonly) { |
4400 | opt.bytes = device_size; |
4401 | opt.reliable_size = 1; |
4402 | } |
4403 | |
4404 | /* Backup boot sector at the end of device isn't counted in NTFS |
4405 | volume size thus we have to reserve space for it. */ |
4406 | if (opt.bytes > vol->sector_size) |
4407 | new_size = (opt.bytes - vol->sector_size) / vol->cluster_size; |
4408 | else |
4409 | new_size = 0; |
4410 | |
4411 | if (!opt.info && !opt.infombonly) { |
4412 | print_vol_size("New volume size ", vol_size(vol, new_size)); |
4413 | if (device_size < opt.bytes) |
4414 | err_exit("New size can't be bigger than the device size" |
4415 | ".\nIf you want to enlarge NTFS then first " |
4416 | "enlarge the device size by e.g. fdisk.\n"); |
4417 | } |
4418 | |
4419 | if (!opt.info && !opt.infombonly && (new_size == vol->nr_clusters || |
4420 | (opt.bytes == device_size && |
4421 | new_size == vol->nr_clusters - 1))) { |
4422 | printf("Nothing to do: NTFS volume size is already OK.\n"); |
4423 | exit(0); |
4424 | } |
4425 | |
4426 | memset(&resize, 0, sizeof(resize)); |
4427 | resize.vol = vol; |
4428 | resize.new_volume_size = new_size; |
4429 | /* This is also true if --info was used w/o --size (new_size = 0) */ |
4430 | if (new_size < vol->nr_clusters) |
4431 | resize.shrink = 1; |
4432 | if (opt.show_progress) |
4433 | resize.progress.flags |= NTFS_PROGBAR; |
4434 | /* |
4435 | * Checking and __reporting__ of bad sectors must be done before cluster |
4436 | * allocation check because chkdsk doesn't fix $Bitmap's w/ bad sectors |
4437 | * thus users would (were) quite confused why chkdsk doesn't work. |
4438 | */ |
4439 | resize.badclusters = check_bad_sectors(vol); |
4440 | |
4441 | NVolSetNoFixupWarn(vol); |
4442 | check_cluster_allocation(vol, &fsck); |
4443 | |
4444 | print_disk_usage(vol, fsck.inuse); |
4445 | |
4446 | resize.inuse = fsck.inuse; |
4447 | resize.lcn_bitmap = fsck.lcn_bitmap; |
4448 | |
4449 | set_resize_constraints(&resize); |
4450 | set_disk_usage_constraint(&resize); |
4451 | check_resize_constraints(&resize); |
4452 | |
4453 | if (opt.info || opt.infombonly) { |
4454 | advise_on_resize(&resize); |
4455 | exit(0); |
4456 | } |
4457 | |
4458 | if (opt.force-- <= 0 && !opt.ro_flag) { |
4459 | printf("%s", resize_warning_msg); |
4460 | proceed_question(); |
4461 | } |
4462 | |
4463 | /* FIXME: performance - relocate logfile here if it's needed */ |
4464 | prepare_volume_fixup(vol); |
4465 | |
4466 | if (resize.relocations) |
4467 | relocate_inodes(&resize); |
4468 | |
4469 | truncate_badclust_file(&resize); |
4470 | truncate_bitmap_file(&resize); |
4471 | delayed_updates(&resize); |
4472 | update_bootsector(&resize); |
4473 | |
4474 | /* We don't create backup boot sector because we don't know where the |
4475 | partition will be split. The scheduled chkdsk will fix it */ |
4476 | |
4477 | if (opt.ro_flag) { |
4478 | printf("The read-only test run ended successfully.\n"); |
4479 | exit(0); |
4480 | } |
4481 | |
4482 | /* WARNING: don't modify the texts, external tools grep for them */ |
4483 | printf("Syncing device ...\n"); |
4484 | if (vol->dev->d_ops->sync(vol->dev) == -1) |
4485 | perr_exit("fsync"); |
4486 | |
4487 | printf("Successfully resized NTFS on device '%s'.\n", vol->dev->d_name); |
4488 | if (resize.shrink) |
4489 | printf("%s", resize_important_msg); |
4490 | #if CLEAN_EXIT |
4491 | if (resize.lcn_bitmap.bm) |
4492 | free(resize.lcn_bitmap.bm); |
4493 | if (vol) |
4494 | ntfs_umount(vol,0); |
4495 | #endif |
4496 | return 0; |
4497 | } |
4498 |