• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * ntfsmove - Part of the Linux-NTFS project.
3  *
4  * Copyright (c) 2003 Richard Russon
5  * Copyright (c) 2003-2005 Anton Altaparmakov
6  *
7  * This utility will move files on an NTFS volume.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program (in the main directory of the Linux-NTFS
21  * distribution in the file COPYING); if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 
25 #include "config.h"
26 
27 #ifdef HAVE_STDIO_H
28 #include <stdio.h>
29 #endif
30 #ifdef HAVE_GETOPT_H
31 #include <getopt.h>
32 #endif
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39 #ifdef HAVE_LIMITS_H
40 #include <limits.h>
41 #endif
42 
43 #include "types.h"
44 #include "attrib.h"
45 #include "utils.h"
46 #include "volume.h"
47 #include "debug.h"
48 #include "dir.h"
49 #include "bitmap.h"
50 #include "ntfsmove.h"
51 /* #include "version.h" */
52 #include "logging.h"
53 
54 static const char *EXEC_NAME = "ntfsmove";
55 static struct options opts;
56 
57 /**
58  * version - Print version information about the program
59  *
60  * Print a copyright statement and a brief description of the program.
61  *
62  * Return:  none
63  */
version(void)64 static void version(void)
65 {
66 	ntfs_log_info("\n%s v%s (libntfs-3g) - Move files and directories on an "
67 			"NTFS volume.\n\n", EXEC_NAME, VERSION);
68 	ntfs_log_info("Copyright (c) 2003 Richard Russon\n");
69 	ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
70 }
71 
72 /**
73  * usage - Print a list of the parameters to the program
74  *
75  * Print a list of the parameters and options for the program.
76  *
77  * Return:  none
78  */
usage(void)79 static void usage(void)
80 {
81 	ntfs_log_info("\nUsage: %s [options] device file\n"
82 		"\n"
83 		"    -S      --start        Move to the start of the volume\n"
84 		"    -B      --best         Move to the best place on the volume\n"
85 		"    -E      --end          Move to the end of the volume\n"
86 		"    -C num  --cluster num  Move to this cluster offset\n"
87 		"\n"
88 		"    -D      --no-dirty     Do not mark volume dirty (require chkdsk)\n"
89 		"    -n      --no-action    Do not write to disk\n"
90 		"    -f      --force        Use less caution\n"
91 		"    -h      --help         Print this help\n"
92 		"    -q      --quiet        Less output\n"
93 		"    -V      --version      Version information\n"
94 		"    -v      --verbose      More output\n\n",
95 		EXEC_NAME);
96 	ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
97 }
98 
99 /**
100  * parse_options - Read and validate the programs command line
101  *
102  * Read the command line, verify the syntax and parse the options.
103  * This function is very long, but quite simple.
104  *
105  * Return:  1 Success
106  *	    0 Error, one or more problems
107  */
parse_options(int argc,char ** argv)108 static int parse_options(int argc, char **argv)
109 {
110 	static const char *sopt = "-BC:DEfh?nqSVv";
111 	static const struct option lopt[] = {
112 		{ "best",	no_argument,		NULL, 'B' },
113 		{ "cluster",	required_argument,	NULL, 'C' },
114 		{ "end",	no_argument,		NULL, 'E' },
115 		{ "force",	no_argument,		NULL, 'f' },
116 		{ "help",	no_argument,		NULL, 'h' },
117 		{ "no-action",	no_argument,		NULL, 'n' },
118 		{ "no-dirty",	no_argument,		NULL, 'D' },
119 		{ "quiet",	no_argument,		NULL, 'q' },
120 		{ "start",	no_argument,		NULL, 'S' },
121 		{ "verbose",	no_argument,		NULL, 'v' },
122 		{ "version",	no_argument,		NULL, 'V' },
123 		{ NULL,		0,			NULL, 0   }
124 	};
125 
126 	int c = -1;
127 	int err  = 0;
128 	int ver  = 0;
129 	int help = 0;
130 	int levels = 0;
131 	char *end = NULL;
132 
133 	opterr = 0; /* We'll handle the errors, thank you. */
134 
135 	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
136 		switch (c) {
137 		case 1:	/* A non-option argument */
138 			if (!opts.device) {
139 				opts.device = argv[optind-1];
140 			} else if (!opts.file) {
141 				opts.file = argv[optind-1];
142 			} else {
143 				opts.device = NULL;
144 				opts.file   = NULL;
145 				err++;
146 			}
147 			break;
148 		case 'B':
149 			if (opts.location == 0)
150 				opts.location = NTFS_MOVE_LOC_BEST;
151 			else
152 				opts.location = -1;
153 			break;
154 		case 'C':
155 			if (opts.location == 0) {
156 				opts.location = strtoll(optarg, &end, 0);
157 				if (end && *end)
158 					err++;
159 			} else {
160 				opts.location = -1;
161 			}
162 			break;
163 		case 'D':
164 			opts.nodirty++;
165 			break;
166 		case 'E':
167 			if (opts.location == 0)
168 				opts.location = NTFS_MOVE_LOC_END;
169 			else
170 				opts.location = -1;
171 			break;
172 		case 'f':
173 			opts.force++;
174 			break;
175 		case 'h':
176 		case '?':
177 			if (strncmp (argv[optind-1], "--log-", 6) == 0) {
178 				if (!ntfs_log_parse_option (argv[optind-1]))
179 					err++;
180 				break;
181 			}
182 			help++;
183 			break;
184 		case 'n':
185 			opts.noaction++;
186 			break;
187 		case 'q':
188 			opts.quiet++;
189 			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
190 			break;
191 		case 'S':
192 			if (opts.location == 0)
193 				opts.location = NTFS_MOVE_LOC_START;
194 			else
195 				opts.location = -1;
196 			break;
197 		case 'V':
198 			ver++;
199 			break;
200 		case 'v':
201 			opts.verbose++;
202 			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
203 			break;
204 		default:
205 			ntfs_log_error("Unknown option '%s'.\n", argv[optind-1]);
206 			err++;
207 			break;
208 		}
209 	}
210 
211 	/* Make sure we're in sync with the log levels */
212 	levels = ntfs_log_get_levels();
213 	if (levels & NTFS_LOG_LEVEL_VERBOSE)
214 		opts.verbose++;
215 	if (!(levels & NTFS_LOG_LEVEL_QUIET))
216 		opts.quiet++;
217 
218 	if (help || ver) {
219 		opts.quiet = 0;
220 	} else {
221 		if ((opts.device == NULL) ||
222 		    (opts.file   == NULL)) {
223 			if (argc > 1)
224 				ntfs_log_error("You must specify one device and one file.\n");
225 			err++;
226 		}
227 
228 		if (opts.quiet && opts.verbose) {
229 			ntfs_log_error("You may not use --quiet and --verbose at the "
230 					"same time.\n");
231 			err++;
232 		}
233 
234 		if (opts.location == -1) {
235 			ntfs_log_error("You may only specify one location option: "
236 				"--start, --best, --end or --cluster\n");
237 			err++;
238 		} else if (opts.location == 0) {
239 			opts.location = NTFS_MOVE_LOC_BEST;
240 		}
241 	}
242 
243 	if (ver)
244 		version();
245 	if (help || err)
246 		usage();
247 
248 	return (!err && !help && !ver);
249 }
250 
251 #if 0
252 
253 /**
254  * ntfs_debug_runlist_dump2 - Dump a runlist.
255  */
256 static int ntfs_debug_runlist_dump2(const runlist *rl, int abbr, char *prefix)
257 {
258 	//int abbr = 3;	/* abbreviate long lists */
259 	int len = 0;
260 	int i;
261 	int res = 0;
262 	u64 total = 0;
263 	const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "EINVAL", "XXXX" };
264 
265 	if (!rl) {
266 		ntfs_log_info("    Run list not present.\n");
267 		return 0;
268 	}
269 
270 	if (!prefix)
271 		prefix = "";
272 
273 	if (abbr)
274 		for (len = 0; rl[len].length; len++) ;
275 
276 	ntfs_log_info("%s     VCN      LCN      len\n", prefix);
277 	for (i = 0; rl->length; i++, rl++) {
278 		LCN lcn = rl->lcn;
279 
280 		total += rl->length;
281 		if (abbr)
282 			if (len > 20) {
283 				if ((i == abbr) && (len > (abbr*2)))
284 					ntfs_log_info("%s     ...      ...      ...\n", prefix);
285 				if ((i > (abbr-1)) && (i < (len - (abbr-1))))
286 					continue;
287 			}
288 
289 		if (rl->vcn < -1)
290 			res = -1;
291 
292 		if (lcn < (LCN)0) {
293 			int j = -lcn - 1;
294 
295 			if ((j < 0) || (j > 4)) {
296 				j = 4;
297 				res = -1;
298 			}
299 			ntfs_log_info("%s%8lld %8s %8lld\n", prefix,
300 				rl->vcn, lcn_str[j], rl->length);
301 		} else
302 			ntfs_log_info("%s%8lld %8lld %8lld\n", prefix,
303 				rl->vcn, rl->lcn, rl->length);
304 	}
305 	ntfs_log_info("%s                  --------\n", prefix);
306 	ntfs_log_info("%s                  %8lld\n", prefix, total);
307 	ntfs_log_info("\n");
308 	return res;
309 }
310 
311 #endif /* if 0 */
312 
313 /**
314  * resize_nonres_attr
315  */
resize_nonres_attr(MFT_RECORD * m,ATTR_RECORD * a,const u32 new_size)316 static int resize_nonres_attr(MFT_RECORD *m, ATTR_RECORD *a, const u32 new_size)
317 {
318 	int this_attr;
319 	int next_attr;
320 	int tail_size;
321 	int file_size;
322 	int old_size;
323 	u8 *ptr;
324 
325 	old_size  = le32_to_cpu(a->length);
326 	file_size = le32_to_cpu(m->bytes_in_use);
327 	this_attr = p2n(a)-p2n(m);
328 	next_attr = this_attr + le32_to_cpu(a->length);
329 	tail_size = file_size - next_attr;
330 	ptr = (u8*) m;
331 
332 	/*
333 	ntfs_log_info("old_size  = %d\n", old_size);
334 	ntfs_log_info("new_size  = %d\n", new_size);
335 	ntfs_log_info("file_size = %d\n", file_size);
336 	ntfs_log_info("this_attr = %d\n", this_attr);
337 	ntfs_log_info("next_attr = %d\n", next_attr);
338 	ntfs_log_info("tail_size = %d\n", tail_size);
339 	*/
340 
341 	memmove(ptr + this_attr + new_size, ptr + next_attr, tail_size);
342 
343 	a->length = cpu_to_le32(new_size);
344 	m->bytes_in_use = cpu_to_le32(le32_to_cpu(m->bytes_in_use) + (new_size - old_size));
345 
346 	return 0;
347 }
348 
349 /**
350  * calc_attr_length
351  */
calc_attr_length(ATTR_RECORD * rec,int runlength)352 static int calc_attr_length(ATTR_RECORD *rec, int runlength)
353 {
354 	int size;
355 
356 	if (!rec)
357 		return -1;
358 	if (!rec->non_resident)
359 		return -1;
360 
361 	size = le16_to_cpu(rec->mapping_pairs_offset) + runlength + 7;
362 	size &= 0xFFF8;
363 	return size;
364 }
365 
366 #if 0
367 
368 /**
369  * dump_runs
370  */
371 static void dump_runs(u8 *buffer, int len)
372 {
373 	int i;
374 	ntfs_log_info("RUN: \e[01;31m");
375 
376 	for (i = 0; i < len; i++) {
377 		ntfs_log_info(" %02x", buffer[i]);
378 	}
379 	ntfs_log_info("\e[0m\n");
380 }
381 
382 #endif /* if 0 */
383 
384 /**
385  * find_unused
386  */
find_unused(ntfs_volume * vol,s64 size,u64 loc,int flags)387 static runlist * find_unused(ntfs_volume *vol, s64 size, u64 loc
388 	__attribute__((unused)), int flags __attribute__((unused)))
389 {
390 	const int bufsize = 8192;
391 	u8 *buffer;
392 	int clus;
393 	int i;
394 	int curr = 0;
395 	int count = 0;
396 	s64 start = 0;
397 	int bit = 0;
398 	runlist *res = NULL;
399 
400 	//ntfs_log_info("find_unused\n");
401 	buffer = malloc(bufsize);
402 	if (!buffer) {
403 		ntfs_log_info("!buffer\n");
404 		return NULL;
405 	}
406 
407 	//ntfs_log_info("looking for space for %lld clusters\n", size);
408 
409 	clus = vol->lcnbmp_na->allocated_size / bufsize;
410 	//ntfs_log_info("clus = %d\n", clus);
411 
412 	for (i = 0; i < clus; i++) {
413 		int bytes_read, j;
414 
415 		bytes_read = ntfs_attr_pread(vol->lcnbmp_na, i*bufsize,
416 				bufsize, buffer);
417 		if (bytes_read != bufsize) {
418 			ntfs_log_info("!read\n");
419 			return NULL;
420 		}
421 		for (j = 0; j < bufsize*8; j++) {
422 			bit = !!test_bit(j & 7, buffer[j>>3]);
423 			if (curr == bit) {
424 				count++;
425 				if ((!bit) && (count >= size)) {
426 					//res = calloc(2, sizeof(*res));
427 					res = calloc(1, 4096);
428 					if (res) {
429 						res[0].vcn    = 0;
430 						res[0].lcn    = start;
431 						res[0].length = size;
432 						res[1].lcn    = LCN_ENOENT;
433 					}
434 					goto done;
435 				}
436 			} else {
437 				//ntfs_log_info("%d * %d\n", curr, count);
438 				curr = bit;
439 				count = 1;
440 				start = i*bufsize*8 + j;
441 			}
442 		}
443 	}
444 done:
445 	//ntfs_log_info("%d * %d\n", curr, count);
446 
447 	free(buffer);
448 
449 	if (res) {
450 		for (i = 0; i < size; i++) {
451 			if (utils_cluster_in_use(vol, res->lcn + i)) {
452 				ntfs_log_info("ERROR cluster %lld in use\n",
453 				(long long)res->lcn + i);
454 			}
455 		}
456 	} else {
457 		ntfs_log_info("failed\n");
458 	}
459 
460 	return res;
461 }
462 
463 /**
464  * dont_move
465  *
466  * Don't let the user move:
467  *   ANY metadata
468  *   Any fragmented MFT records
469  *   The boot file 'ntldr'
470  */
dont_move(ntfs_inode * ino)471 static int dont_move(ntfs_inode *ino)
472 {
473 	static const ntfschar ntldr[6] = {
474 		const_cpu_to_le16('n'), const_cpu_to_le16('t'), const_cpu_to_le16('l'),
475 		const_cpu_to_le16('d'), const_cpu_to_le16('r'), const_cpu_to_le16('\0')
476 	};
477 
478 	ATTR_RECORD *rec;
479 	FILE_NAME_ATTR *name;
480 
481 	if (utils_is_metadata(ino)) {
482 		ntfs_log_error("metadata\n");
483 		return 1;
484 	}
485 
486 	rec = find_first_attribute(AT_ATTRIBUTE_LIST, ino->mrec);
487 	if (rec) {
488 		ntfs_log_error("attribute list\n");
489 		return 1;
490 	}
491 
492 	rec = find_first_attribute(AT_FILE_NAME, ino->mrec);
493 	if (!rec) {
494 		ntfs_log_error("extend inode\n");
495 		return 1;
496 	}
497 
498 	name = (FILE_NAME_ATTR*) ((u8*)rec + le16_to_cpu(rec->value_offset));
499 	if (ntfs_names_are_equal(ntldr, 5, name->file_name, name->file_name_length,
500 		IGNORE_CASE, ino->vol->upcase, ino->vol->upcase_len)) {
501 		ntfs_log_error("ntldr\n");
502 		return 1;
503 	}
504 
505 	return 0;
506 }
507 
508 
509 /**
510  * bitmap_alloc
511  */
bitmap_alloc(ntfs_volume * vol,runlist_element * rl)512 static int bitmap_alloc(ntfs_volume *vol, runlist_element *rl)
513 {
514 	int res;
515 
516 	if (!rl)
517 		return -1;
518 
519 	res = ntfs_bitmap_set_run(vol->lcnbmp_na, rl->lcn, rl->length);
520 	if (res < 0) {
521 		ntfs_log_error("bitmap alloc returns %d\n", res);
522 	}
523 
524 	return res;
525 }
526 
527 /**
528  * bitmap_free
529  */
bitmap_free(ntfs_volume * vol,runlist_element * rl)530 static int bitmap_free(ntfs_volume *vol, runlist_element *rl)
531 {
532 	int res;
533 
534 	if (!rl)
535 		return -1;
536 
537 	res = ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn, rl->length);
538 	if (res < 0) {
539 		ntfs_log_error("bitmap free returns %d\n", res);
540 	}
541 
542 	return res;
543 }
544 
545 /**
546  * data_copy
547  */
data_copy(ntfs_volume * vol,runlist_element * from,runlist_element * to)548 static int data_copy(ntfs_volume *vol, runlist_element *from, runlist_element *to)
549 {
550 	int i;
551 	u8 *buffer;
552 	s64 res = 0;
553 
554 	if (!vol || !from || !to)
555 		return -1;
556 	if ((from->length != to->length) || (from->lcn < 0) || (to->lcn < 0))
557 		return -1;
558 
559 	//ntfs_log_info("data_copy: from 0x%llx to 0x%llx\n", from->lcn, to->lcn);
560 	buffer = malloc(vol->cluster_size);
561 	if (!buffer) {
562 		ntfs_log_info("!buffer\n");
563 		return -1;
564 	}
565 
566 	for (i = 0; i < from->length; i++) {
567 		//ntfs_log_info("read  cluster at %8lld\n", from->lcn+i);
568 		res = ntfs_pread(vol->dev, (from->lcn+i) * vol->cluster_size,
569 				vol->cluster_size, buffer);
570 		if (res != vol->cluster_size) {
571 			ntfs_log_error("!read\n");
572 			res = -1;
573 			break;
574 		}
575 
576 		//ntfs_log_info("write cluster to %8lld\n", to->lcn+i);
577 		res = ntfs_pwrite(vol->dev, (to->lcn+i) * vol->cluster_size,
578 				vol->cluster_size, buffer);
579 		if (res != vol->cluster_size) {
580 			ntfs_log_error("!write %lld\n", (long long)res);
581 			res = -1;
582 			break;
583 		}
584 	}
585 
586 	free(buffer);
587 	return res;
588 }
589 
590 /**
591  * move_runlist
592  *
593  * validate:
594  *   runlists are the same size
595  *   from in use
596  *   to not in use
597  * allocate new space
598  * copy data
599  * deallocate old space
600  */
move_runlist(ntfs_volume * vol,runlist_element * from,runlist_element * to)601 static s64 move_runlist(ntfs_volume *vol, runlist_element *from,
602 	runlist_element *to)
603 {
604 	int i;
605 
606 	if (!vol || !from || !to)
607 		return -1;
608 	if (from->length != to->length) {
609 		ntfs_log_error("diffsizes\n");
610 		return -1;
611 	}
612 
613 	if ((from->lcn < 0) || (to->lcn < 0)) {
614 		ntfs_log_error("invalid runs\n");
615 		return -1;
616 	}
617 
618 	for (i = 0; i < from->length; i++) {
619 		if (!utils_cluster_in_use(vol, from->lcn+i)) {
620 			ntfs_log_error("from not in use\n");
621 			return -1;
622 		}
623 	}
624 
625 	for (i = 0; i < to->length; i++) {
626 		if (utils_cluster_in_use(vol, to->lcn+i)) {
627 			ntfs_log_error("to is in use\n");
628 			return -1;
629 		}
630 	}
631 
632 	if (bitmap_alloc(vol, to) < 0) {
633 		ntfs_log_error("cannot bitmap_alloc\n");
634 		return -1;
635 	}
636 
637 	if (data_copy(vol, from, to) < 0) {
638 		ntfs_log_error("cannot data_copy\n");
639 		return -1;
640 	}
641 
642 	if (bitmap_free(vol, from) < 0) {
643 		ntfs_log_error("cannot bitmap_free\n");
644 		return -1;
645 	}
646 
647 	return 0;
648 }
649 
650 
651 /**original
652  * move_datarun
653  * > 0  Bytes moved / size to be moved
654  * = 0  Nothing to do
655  * < 0  Error
656  */
657 
658 // get size of runlist
659 // find somewhere to put data
660 // backup original runlist
661 // move the data
662 
663 // got to get the runlist out of this function
664 //      requires a mrec arg, not an ino (ino->mrec will do for now)
665 // check size of new runlist before allocating / moving
666 // replace one datarun with another (by hand)
move_datarun(ntfs_volume * vol,ntfs_inode * ino,ATTR_RECORD * rec,runlist_element * run,u64 loc,int flags)667 static s64 move_datarun(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec,
668 	runlist_element *run, u64 loc, int flags)
669 {
670 	runlist *from;
671 	runlist *to;
672 	int need_from;
673 	int need_to;
674 	int i;
675 	s64 res = -1;
676 
677 	// find empty space
678 	to = find_unused(vol, run->length, loc, flags);
679 	if (!to) {
680 		ntfs_log_error("!to\n");
681 		return -1;
682 	}
683 
684 	to->vcn = run->vcn;
685 
686 	// copy original runlist
687 	from = ntfs_mapping_pairs_decompress(vol, rec, NULL);
688 	if (!from) {
689 		ntfs_log_info("!from\n");
690 		return -1;
691 	}
692 
693 	ntfs_log_info("move %lld,%lld,%lld to %lld,%lld,%lld\n",
694 		(long long)run->vcn, (long long)run->lcn, (long long)run->length,
695 		(long long)to->vcn, (long long)to->lcn, (long long)to->length);
696 
697 	need_from = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX);
698 	ntfs_log_info("orig data run = %d bytes\n", need_from);
699 
700 	//ntfs_debug_runlist_dump2(from, 5, "\t");
701 
702 	for (i = 0; to[i].length > 0; i++) {
703 		if (from[i].vcn == run->vcn) {
704 			from[i].lcn = to->lcn;
705 			break;
706 		}
707 	}
708 
709 	//ntfs_debug_runlist_dump2(from, 5, "\t");
710 
711 	need_to = ntfs_get_size_for_mapping_pairs(vol, from, 0, INT_MAX);
712 	ntfs_log_info("new  data run = %d bytes\n", need_to);
713 
714 	need_from = calc_attr_length(rec, need_from);
715 	need_to   = calc_attr_length(rec, need_to);
716 
717 	ntfs_log_info("Before %d, after %d\n", need_from, need_to);
718 
719 	if (need_from != need_to) {
720 		if (resize_nonres_attr(ino->mrec, rec, need_to) < 0) {
721 			ntfs_log_info("!resize\n");
722 			return -1;
723 		}
724 	}
725 
726 	res = move_runlist(vol, run, to);
727 	if (res < 0) {
728 		ntfs_log_error("!move_runlist\n");
729 		return -1;
730 	}
731 
732 	// wipe orig runs
733 	memset(((u8*)rec) + le16_to_cpu(rec->mapping_pairs_offset), 0, need_to - le16_to_cpu(rec->mapping_pairs_offset));
734 
735 	// update data runs
736 	ntfs_mapping_pairs_build(vol, ((u8*)rec) + le16_to_cpu(rec->mapping_pairs_offset),
737 			need_to, from, 0, NULL);
738 
739 	// commit
740 	ntfs_inode_mark_dirty(ino);
741 
742 	if (ntfs_inode_sync(ino) < 0) {
743 		ntfs_log_info("!sync\n");
744 		return -1;
745 	}
746 
747 	free(from);
748 	free(to);
749 	return res;
750 }
751 
752 /**
753  * move_attribute -
754  *
755  * > 0  Bytes moved / size to be moved
756  * = 0  Nothing to do
757  * < 0  Error
758  */
move_attribute(ntfs_volume * vol,ntfs_inode * ino,ATTR_RECORD * rec,u64 loc,int flags)759 static s64 move_attribute(ntfs_volume *vol, ntfs_inode *ino, ATTR_RECORD *rec,
760 	u64 loc, int flags)
761 {
762 	int i;
763 	s64 res;
764 	s64 count = 0;
765 	runlist *runs;
766 
767 	// NTFS_MOVE_LOC_BEST : assess how much space this attribute will need,
768 	// find that space and pass the location to our children.
769 	// Anything else we pass directly to move_datarun.
770 
771 	runs = ntfs_mapping_pairs_decompress(vol, rec, NULL);
772 	if (!runs) {
773 		ntfs_log_error("!runs\n");
774 		return -1;
775 	}
776 
777 	//ntfs_debug_runlist_dump2(runs, 5, "\t");
778 
779 	//ntfs_log_info("             VCN     LCN     Length\n");
780 	for (i = 0; runs[i].length > 0; i++) {
781 		if (runs[i].lcn == LCN_RL_NOT_MAPPED) {
782 			continue;
783 		}
784 
785 		res = move_datarun(vol, ino, rec, runs+i, loc, flags);
786 		//ntfs_log_info("        %8lld %8lld %8lld\n", runs[i].vcn, runs[i].lcn, runs[i].length);
787 		if (res < 0) {
788 			ntfs_log_error("!move_datarun\n");
789 			count = res;
790 			break;
791 		}
792 		count += res;
793 	}
794 
795 	return count;
796 }
797 
798 /**
799  * move_file -
800  *
801  * > 0  Bytes moved / size to be moved
802  * = 0  Nothing to do
803  * < 0  Error
804  */
move_file(ntfs_volume * vol,ntfs_inode * ino,u64 loc,int flags)805 static s64 move_file(ntfs_volume *vol, ntfs_inode *ino, u64 loc, int flags)
806 {
807 	char *buffer;
808 	ntfs_attr_search_ctx *ctx;
809 	ATTR_RECORD *rec;
810 	s64 res;
811 	s64 count = 0;
812 
813 	buffer = malloc(MAX_PATH);
814 	if (!buffer) {
815 		ntfs_log_error("Out of memory\n");
816 		return -1;
817 	}
818 
819 	utils_inode_get_name(ino, buffer, MAX_PATH);
820 
821 	if (dont_move(ino)) {
822 		ntfs_log_error("can't move\n");
823 		return -1;
824 	}
825 
826 	ntfs_log_info("Moving %s\n", buffer);
827 
828 	// NTFS_MOVE_LOC_BEST : assess how much space all the attributes will need,
829 	// find that space and pass the location to our children.
830 	// Anything else we pass directly to move_attribute.
831 
832 	ctx = ntfs_attr_get_search_ctx(ino, NULL);
833 
834 	while ((rec = find_attribute(AT_UNUSED, ctx))) {
835 		utils_attr_get_name(vol, rec, buffer, MAX_PATH);
836 		ntfs_log_info("\tAttribute 0x%02x %s is ", le32_to_cpu(rec->type), buffer);
837 
838 		if (rec->non_resident) {
839 			ntfs_log_info("non-resident.   Moving it.\n");
840 
841 			res = move_attribute(vol, ino, rec, loc, flags);
842 			if (res < 0) {
843 				count = res;
844 				break;
845 			}
846 			count += res;
847 		} else {
848 			ntfs_log_info("resident.\n\t\tSkipping it.\n");
849 		}
850 	}
851 
852 	ntfs_attr_put_search_ctx(ctx);
853 	free(buffer);
854 	return count;
855 }
856 
857 
858 /**
859  * main - Begin here
860  *
861  * Start from here.
862  *
863  * Return:  0  Success, the program worked
864  *	    1  Error, something went wrong
865  */
main(int argc,char * argv[])866 int main(int argc, char *argv[])
867 {
868 	ntfs_volume *vol;
869 	ntfs_inode *inode;
870 	int flags = 0;
871 	int result = 1;
872 	s64 count;
873 
874 	ntfs_log_set_handler(ntfs_log_handler_outerr);
875 
876 	if (!parse_options(argc, argv))
877 		return 1;
878 
879 	utils_set_locale();
880 
881 	if (opts.noaction)
882 		flags |= NTFS_MNT_RDONLY;
883 	if (opts.force)
884 		flags |= NTFS_MNT_RECOVER;
885 
886 	vol = utils_mount_volume(opts.device, flags);
887 	if (!vol) {
888 		ntfs_log_info("!vol\n");
889 		return 1;
890 	}
891 
892 	inode = ntfs_pathname_to_inode(vol, NULL, opts.file);
893 	if (!inode) {
894 		ntfs_log_info("!inode\n");
895 		return 1;
896 	}
897 
898 	count = move_file(vol, inode, opts.location, 0);
899 	if ((count > 0) && (!opts.nodirty)) {
900 
901 		/* Porting note: libntfs-3g does not automatically set or clear
902 		 * dirty flags on mount/unmount. It always preserves them until
903 		 * they are explicitly changed with ntfs_volume_write_flags.
904 		 * This means that the dirty flag is possibly not set, but
905 		 * should be set. So we explicitly set it with a call to
906 		 * ntfs_volume_write_flags. */
907 		if(!(vol->flags & VOLUME_IS_DIRTY) && ntfs_volume_write_flags(
908 			vol, vol->flags | VOLUME_IS_DIRTY)) {
909 			ntfs_log_error("Error: Failed to set volume dirty "
910 				"flag (%d (%s))!\n", errno, strerror(errno));
911 		}
912 
913 		ntfs_log_info("Relocated %lld bytes\n", (long long)count);
914 	}
915 	if (count >= 0)
916 		result = 0;
917 
918 	if (result)
919 		ntfs_log_info("failed\n");
920 	else
921 		ntfs_log_info("success\n");
922 
923 	ntfs_inode_close(inode);
924 	ntfs_umount(vol, FALSE);
925 	return result;
926 }
927