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