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