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 | |
51 | static const char *EXEC_NAME = "ntfsmove"; |
52 | static 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 | */ |
61 | static 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 | */ |
76 | static 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 | */ |
105 | static 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 | */ |
253 | static 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 | */ |
313 | static 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 | */ |
349 | static 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 | */ |
368 | static 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 | */ |
384 | static 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 | } |
441 | done: |
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 | */ |
468 | static 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 | */ |
509 | static 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 | */ |
527 | static 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 | */ |
545 | static 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 | */ |
598 | static 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) |
664 | static 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 | */ |
756 | static 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 | */ |
802 | static 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 | */ |
863 | int 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 |