blob: 24381b46575c7dc5cafa65bc837343956c80fe27 [file] [log] [blame]
Steve Kondik2111ad72013-07-07 12:07:44 -07001/**
2 * ntfscp - Part of the Linux-NTFS project.
3 *
4 * Copyright (c) 2004-2007 Yura Pakhuchiy
5 * Copyright (c) 2005 Anton Altaparmakov
6 * Copyright (c) 2006 Hil Liao
Steve Kondik79165c32015-11-09 19:43:00 -08007 * Copyright (c) 2014 Jean-Pierre Andre
Steve Kondik2111ad72013-07-07 12:07:44 -07008 *
9 * This utility will copy file to an NTFS volume.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program (in the main directory of the Linux-NTFS
23 * distribution in the file COPYING); if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "config.h"
28
29#ifdef HAVE_STDIO_H
30#include <stdio.h>
31#endif
32#ifdef HAVE_GETOPT_H
33#include <getopt.h>
34#endif
35#ifdef HAVE_STDLIB_H
36#include <stdlib.h>
37#endif
38#ifdef HAVE_STRING_H
39#include <string.h>
40#endif
41#include <signal.h>
42#ifdef HAVE_SYS_STAT_H
43#include <sys/stat.h>
44#endif
45#ifdef HAVE_UNISTD_H
46#include <unistd.h>
47#endif
48#ifdef HAVE_LIBGEN_H
49#include <libgen.h>
50#endif
51
52#include "types.h"
53#include "attrib.h"
54#include "utils.h"
55#include "volume.h"
56#include "dir.h"
Steve Kondik79165c32015-11-09 19:43:00 -080057#include "bitmap.h"
Steve Kondik2111ad72013-07-07 12:07:44 -070058#include "debug.h"
59/* #include "version.h" */
60#include "logging.h"
Steve Kondik79165c32015-11-09 19:43:00 -080061#include "misc.h"
Steve Kondik2111ad72013-07-07 12:07:44 -070062
63struct options {
64 char *device; /* Device/File to work with */
65 char *src_file; /* Source file */
66 char *dest_file; /* Destination file */
67 char *attr_name; /* Write to attribute with this name. */
68 int force; /* Override common sense */
69 int quiet; /* Less output */
70 int verbose; /* Extra output */
Steve Kondik79165c32015-11-09 19:43:00 -080071 int minfragments; /* Do minimal fragmentation */
Steve Kondik2111ad72013-07-07 12:07:44 -070072 int noaction; /* Do not write to disk */
73 ATTR_TYPES attribute; /* Write to this attribute. */
74 int inode; /* Treat dest_file as inode number. */
75};
76
Steve Kondik79165c32015-11-09 19:43:00 -080077struct ALLOC_CONTEXT {
78 ntfs_volume *vol;
79 ntfs_attr *na;
80 runlist_element *rl;
81 unsigned char *buf;
82 s64 gathered_clusters;
83 s64 wanted_clusters;
84 s64 new_size;
85 s64 lcn;
86 int rl_allocated;
87 int rl_count;
88} ;
89
90enum STEP { STEP_ERR, STEP_ZERO, STEP_ONE } ;
91
Steve Kondik2111ad72013-07-07 12:07:44 -070092static const char *EXEC_NAME = "ntfscp";
93static struct options opts;
94static volatile sig_atomic_t caught_terminate = 0;
95
96/**
97 * version - Print version information about the program
98 *
99 * Print a copyright statement and a brief description of the program.
100 *
101 * Return: none
102 */
103static void version(void)
104{
105 ntfs_log_info("\n%s v%s (libntfs-3g) - Copy file to an NTFS "
106 "volume.\n\n", EXEC_NAME, VERSION);
107 ntfs_log_info("Copyright (c) 2004-2007 Yura Pakhuchiy\n");
108 ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n");
109 ntfs_log_info("Copyright (c) 2006 Hil Liao\n");
Steve Kondik79165c32015-11-09 19:43:00 -0800110 ntfs_log_info("Copyright (c) 2014 Jean-Pierre Andre\n");
Steve Kondik2111ad72013-07-07 12:07:44 -0700111 ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
112}
113
114/**
115 * usage - Print a list of the parameters to the program
116 *
117 * Print a list of the parameters and options for the program.
118 *
119 * Return: none
120 */
121static void usage(void)
122{
123 ntfs_log_info("\nUsage: %s [options] device src_file dest_file\n\n"
124 " -a, --attribute NUM Write to this attribute\n"
125 " -i, --inode Treat dest_file as inode number\n"
126 " -f, --force Use less caution\n"
127 " -h, --help Print this help\n"
Steve Kondik79165c32015-11-09 19:43:00 -0800128 " -m, --min_fragments Do minimal fragmentation\n"
Steve Kondik2111ad72013-07-07 12:07:44 -0700129 " -N, --attr-name NAME Write to attribute with this name\n"
130 " -n, --no-action Do not write to disk\n"
131 " -q, --quiet Less output\n"
132 " -V, --version Version information\n"
133 " -v, --verbose More output\n\n",
134 EXEC_NAME);
135 ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
136}
137
138/**
139 * parse_options - Read and validate the programs command line
140 *
141 * Read the command line, verify the syntax and parse the options.
142 * This function is very long, but quite simple.
143 *
144 * Return: 1 Success
145 * 0 Error, one or more problems
146 */
147static int parse_options(int argc, char **argv)
148{
Steve Kondik79165c32015-11-09 19:43:00 -0800149 static const char *sopt = "-a:ifh?mN:no:qVv";
Steve Kondik2111ad72013-07-07 12:07:44 -0700150 static const struct option lopt[] = {
151 { "attribute", required_argument, NULL, 'a' },
152 { "inode", no_argument, NULL, 'i' },
153 { "force", no_argument, NULL, 'f' },
154 { "help", no_argument, NULL, 'h' },
Steve Kondik79165c32015-11-09 19:43:00 -0800155 { "min-fragments", no_argument, NULL, 'm' },
Steve Kondik2111ad72013-07-07 12:07:44 -0700156 { "attr-name", required_argument, NULL, 'N' },
157 { "no-action", no_argument, NULL, 'n' },
158 { "quiet", no_argument, NULL, 'q' },
159 { "version", no_argument, NULL, 'V' },
160 { "verbose", no_argument, NULL, 'v' },
161 { NULL, 0, NULL, 0 }
162 };
163
164 char *s;
165 int c = -1;
166 int err = 0;
167 int ver = 0;
168 int help = 0;
169 int levels = 0;
170 s64 attr;
171
172 opts.device = NULL;
173 opts.src_file = NULL;
174 opts.dest_file = NULL;
175 opts.attr_name = NULL;
176 opts.inode = 0;
177 opts.attribute = AT_DATA;
178
179 opterr = 0; /* We'll handle the errors, thank you. */
180
181 while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
182 switch (c) {
183 case 1: /* A non-option argument */
184 if (!opts.device) {
185 opts.device = argv[optind - 1];
186 } else if (!opts.src_file) {
187 opts.src_file = argv[optind - 1];
188 } else if (!opts.dest_file) {
189 opts.dest_file = argv[optind - 1];
190 } else {
191 ntfs_log_error("You must specify exactly two "
192 "files.\n");
193 err++;
194 }
195 break;
196 case 'a':
197 if (opts.attribute != AT_DATA) {
198 ntfs_log_error("You can specify only one "
199 "attribute.\n");
200 err++;
201 break;
202 }
203
204 attr = strtol(optarg, &s, 0);
205 if (*s) {
206 ntfs_log_error("Couldn't parse attribute.\n");
207 err++;
208 } else
209 opts.attribute = (ATTR_TYPES)cpu_to_le32(attr);
210 break;
211 case 'i':
212 opts.inode++;
213 break;
214 case 'f':
215 opts.force++;
216 break;
217 case 'h':
Steve Kondik2111ad72013-07-07 12:07:44 -0700218 help++;
219 break;
Steve Kondik79165c32015-11-09 19:43:00 -0800220 case 'm':
221 opts.minfragments++;
222 break;
Steve Kondik2111ad72013-07-07 12:07:44 -0700223 case 'N':
224 if (opts.attr_name) {
225 ntfs_log_error("You can specify only one "
226 "attribute name.\n");
227 err++;
228 } else
229 opts.attr_name = argv[optind - 1];
230 break;
231 case 'n':
232 opts.noaction++;
233 break;
234 case 'q':
235 opts.quiet++;
236 ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
237 break;
238 case 'V':
239 ver++;
240 break;
241 case 'v':
242 opts.verbose++;
243 ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
244 break;
Steve Kondik79165c32015-11-09 19:43:00 -0800245 case '?':
246 if (strncmp(argv[optind - 1], "--log-", 6) == 0) {
247 if (!ntfs_log_parse_option(argv[optind - 1]))
248 err++;
249 break;
250 }
251 /* fall through */
Steve Kondik2111ad72013-07-07 12:07:44 -0700252 default:
253 ntfs_log_error("Unknown option '%s'.\n",
254 argv[optind - 1]);
255 err++;
256 break;
257 }
258 }
259
260 /* Make sure we're in sync with the log levels */
261 levels = ntfs_log_get_levels();
262 if (levels & NTFS_LOG_LEVEL_VERBOSE)
263 opts.verbose++;
264 if (!(levels & NTFS_LOG_LEVEL_QUIET))
265 opts.quiet++;
266
267 if (help || ver) {
268 opts.quiet = 0;
269 } else {
270 if (!opts.device) {
271 ntfs_log_error("You must specify a device.\n");
272 err++;
273 } else if (!opts.src_file) {
274 ntfs_log_error("You must specify a source file.\n");
275 err++;
276 } else if (!opts.dest_file) {
277 ntfs_log_error("You must specify a destination "
278 "file.\n");
279 err++;
280 }
281
282 if (opts.quiet && opts.verbose) {
283 ntfs_log_error("You may not use --quiet and --verbose "
284 "at the same time.\n");
285 err++;
286 }
287 }
288
289 if (ver)
290 version();
291 if (help || err)
292 usage();
293
Steve Kondik79165c32015-11-09 19:43:00 -0800294 /* tri-state 0 : done, 1 : error, -1 : proceed */
295 return (err ? 1 : (help || ver ? 0 : -1));
Steve Kondik2111ad72013-07-07 12:07:44 -0700296}
297
298/**
299 * signal_handler - Handle SIGINT and SIGTERM: abort write, sync and exit.
300 */
301static void signal_handler(int arg __attribute__((unused)))
302{
303 caught_terminate++;
304}
305
Steve Kondik79165c32015-11-09 19:43:00 -0800306/*
307 * Search for the next '0' in a bitmap chunk
308 *
309 * Returns the position of next '0'
310 * or -1 if there are no more '0's
311 */
312
313static int next_zero(struct ALLOC_CONTEXT *alctx, s32 bufpos, s32 count)
314{
315 s32 index;
316 unsigned int q,b;
317
318 index = -1;
319 while ((index < 0) && (bufpos < count)) {
320 q = alctx->buf[bufpos >> 3];
321 if (q == 255)
322 bufpos = (bufpos | 7) + 1;
323 else {
324 b = bufpos & 7;
325 while ((b < 8)
326 && ((1 << b) & q))
327 b++;
328 if (b < 8) {
329 index = (bufpos & -8) | b;
330 } else {
331 bufpos = (bufpos | 7) + 1;
332 }
333 }
334 }
335 return (index);
336}
337
338/*
339 * Search for the next '1' in a bitmap chunk
340 *
341 * Returns the position of next '1'
342 * or -1 if there are no more '1's
343 */
344
345static int next_one(struct ALLOC_CONTEXT *alctx, s32 bufpos, s32 count)
346{
347 s32 index;
348 unsigned int q,b;
349
350 index = -1;
351 while ((index < 0) && (bufpos < count)) {
352 q = alctx->buf[bufpos >> 3];
353 if (q == 0)
354 bufpos = (bufpos | 7) + 1;
355 else {
356 b = bufpos & 7;
357 while ((b < 8)
358 && !((1 << b) & q))
359 b++;
360 if (b < 8) {
361 index = (bufpos & -8) | b;
362 } else {
363 bufpos = (bufpos | 7) + 1;
364 }
365 }
366 }
367 return (index);
368}
369
370/*
371 * Allocate a bigger runlist when needed
372 *
373 * The allocation is done by multiple of 4096 entries to avoid
374 * frequent reallocations.
375 *
376 * Returns 0 if successful
377 * -1 otherwise, with errno set accordingly
378 */
379
380static int run_alloc(struct ALLOC_CONTEXT *alctx, s32 count)
381{
382 runlist_element *prl;
383 int err;
384
385 err = 0;
386 if (count > alctx->rl_allocated) {
387 prl = (runlist_element*)ntfs_malloc(
388 (alctx->rl_allocated + 4096)*sizeof(runlist_element));
389 if (prl) {
390 if (alctx->rl) {
391 memcpy(prl, alctx->rl, alctx->rl_allocated
392 *sizeof(runlist_element));
393 free(alctx->rl);
394 }
395 alctx->rl = prl;
396 alctx->rl_allocated += 4096;
397 } else
398 err = -1;
399 }
400 return (err);
401}
402
403/*
404 * Merge a new run into the current optimal runlist
405 *
406 * The new run is inserted only if it leads to improving the runlist.
407 * Runs in the current list are dropped when inserting the new one
408 * make them unneeded.
409 * The current runlist is sorted by run sizes, and there is no
410 * terminator.
411 *
412 * Returns 0 if successful
413 * -1 otherwise, with errno set accordingly
414 */
415
416static int merge_run(struct ALLOC_CONTEXT *alctx, s64 lcn, s32 count)
417{
418 s64 excess;
419 BOOL replace;
420 int k;
421 int drop;
422 int err;
423
424 err = 0;
425 if (alctx->rl_count) {
426 excess = alctx->gathered_clusters + count
427 - alctx->wanted_clusters;
428 if (alctx->rl_count > 1)
429 /* replace if we can reduce the number of runs */
430 replace = excess > (alctx->rl[0].length
431 + alctx->rl[1].length);
432 else
433 /* replace if we can shorten a single run */
434 replace = (excess > alctx->rl[0].length)
435 && (count < alctx->rl[0].length);
436 } else
437 replace = FALSE;
438 if (replace) {
439 /* Using this run, we can now drop smaller runs */
440 drop = 0;
441 excess = alctx->gathered_clusters + count
442 - alctx->wanted_clusters;
443 /* Compute how many clusters we can drop */
444 while ((drop < alctx->rl_count)
445 && (alctx->rl[drop].length <= excess)) {
446 excess -= alctx->rl[drop].length;
447 drop++;
448 }
449 k = 0;
450 while (((k + drop) < alctx->rl_count)
451 && (alctx->rl[k + drop].length < count)) {
452 alctx->rl[k] = alctx->rl[k + drop];
453 k++;
454 }
455 alctx->rl[k].length = count;
456 alctx->rl[k].lcn = lcn;
457 if (drop > 1) {
458 while ((k + drop) < alctx->rl_count) {
459 alctx->rl[k + 1] = alctx->rl[k + drop];
460 k++;
461 }
462 }
463 alctx->rl_count -= (drop - 1);
464 alctx->gathered_clusters = alctx->wanted_clusters + excess;
465 } else {
466 if (alctx->gathered_clusters < alctx->wanted_clusters) {
467 /* We had not gathered enough clusters */
468 if (!run_alloc(alctx, alctx->rl_count + 1)) {
469 k = alctx->rl_count - 1;
470 while ((k >= 0)
471 && (alctx->rl[k].length > count)) {
472 alctx->rl[k+1] = alctx->rl[k];
473 k--;
474 }
475 alctx->rl[k+1].length = count;
476 alctx->rl[k+1].lcn = lcn;
477 alctx->rl_count++;
478 alctx->gathered_clusters += count;
479 }
480 }
481 }
482 return (err);
483}
484
485/*
486 * Examine a buffer from the global bitmap
487 * in order to locate free runs of clusters
488 *
489 * Returns STEP_ZERO or STEP_ONE depending on whether the last
490 * bit examined was in a search for '0' or '1'. This must be
491 * put as argument to next examination.
492 * Returns STEP_ERR if there was an error.
493 */
494
495static enum STEP examine_buf(struct ALLOC_CONTEXT *alctx, s64 pos, s64 br,
496 enum STEP step)
497{
498 s32 count;
499 s64 offbuf; /* first bit available in buf */
500 s32 bufpos; /* bit index in buf */
501 s32 index;
502
503 bufpos = pos & ((alctx->vol->cluster_size << 3) - 1);
504 offbuf = pos - bufpos;
505 while (bufpos < (br << 3)) {
506 if (step == STEP_ZERO) {
507 /* find first zero */
508 index = next_zero(alctx, bufpos, br << 3);
509 if (index >= 0) {
510 alctx->lcn = offbuf + index;
511 step = STEP_ONE;
512 bufpos = index;
513 } else {
514 bufpos = br << 3;
515 }
516 } else {
517 /* find first one */
518 index = next_one(alctx, bufpos, br << 3);
519 if (index >= 0) {
520 count = offbuf + index - alctx->lcn;
521 step = STEP_ZERO;
522 bufpos = index;
523 if (merge_run(alctx, alctx->lcn, count)) {
524 step = STEP_ERR;
525 bufpos = br << 3;
526 }
527 } else {
528 bufpos = br << 3;
529 }
530 }
531 }
532 return (step);
533}
534
535/*
536 * Sort the final runlist by lcn's and insert a terminator
537 *
538 * Returns 0 if successful
539 * -1 otherwise, with errno set accordingly
540 */
541
542static int sort_runlist(struct ALLOC_CONTEXT *alctx)
543{
544 LCN lcn;
545 VCN vcn;
546 s64 length;
547 BOOL sorted;
548 int err;
549 int k;
550
551 err = 0;
552 /* This sorting can be much improved... */
553 do {
554 sorted = TRUE;
555 for (k=0; (k+1)<alctx->rl_count; k++) {
556 if (alctx->rl[k+1].lcn < alctx->rl[k].lcn) {
557 length = alctx->rl[k].length;
558 lcn = alctx->rl[k].lcn;
559 alctx->rl[k] = alctx->rl[k+1];
560 alctx->rl[k+1].length = length;
561 alctx->rl[k+1].lcn = lcn;
562 sorted = FALSE;
563 }
564 }
565 } while (!sorted);
566 /* compute the vcns */
567 vcn = 0;
568 for (k=0; k<alctx->rl_count; k++) {
569 alctx->rl[k].vcn = vcn;
570 vcn += alctx->rl[k].length;
571 }
572 /* Shorten the last run if we got too much */
573 if (vcn > alctx->wanted_clusters) {
574 k = alctx->rl_count - 1;
575 alctx->rl[k].length -= vcn - alctx->wanted_clusters;
576 vcn = alctx->wanted_clusters;
577 }
578 /* Append terminator */
579 if (run_alloc(alctx, alctx->rl_count + 1))
580 err = -1;
581 else {
582 k = alctx->rl_count++;
583 alctx->rl[k].vcn = vcn;
584 alctx->rl[k].length = 0;
585 alctx->rl[k].lcn = LCN_ENOENT;
586 }
587 return (err);
588}
589
590/*
591 * Update the sizes of an attribute
592 *
593 * Returns 0 if successful
594 * -1 otherwise, with errno set accordingly
595 */
596
597static int set_sizes(struct ALLOC_CONTEXT *alctx, ntfs_attr_search_ctx *ctx)
598{
599 ntfs_attr *na;
600 ntfs_inode *ni;
601 ATTR_RECORD *attr;
602
603 na = alctx->na;
604 /* Compute the sizes */
605 na->data_size = alctx->new_size;
606 na->initialized_size = 0;
607 na->allocated_size = alctx->wanted_clusters
608 << alctx->vol->cluster_size_bits;
609 /* Feed the sizes into the attribute */
610 attr = ctx->attr;
611 attr->non_resident = 1;
Steve Kondike68cb602016-08-28 00:45:36 -0700612 attr->data_size = cpu_to_sle64(na->data_size);
613 attr->initialized_size = cpu_to_sle64(na->initialized_size);
614 attr->allocated_size = cpu_to_sle64(na->allocated_size);
Steve Kondik79165c32015-11-09 19:43:00 -0800615 if (na->data_flags & ATTR_IS_SPARSE)
Steve Kondike68cb602016-08-28 00:45:36 -0700616 attr->compressed_size = cpu_to_sle64(na->compressed_size);
Steve Kondik79165c32015-11-09 19:43:00 -0800617 /* Copy the unnamed data attribute sizes to inode */
618 if ((opts.attribute == AT_DATA) && !na->name_len) {
619 ni = na->ni;
620 ni->data_size = na->data_size;
621 if (na->data_flags & ATTR_IS_SPARSE) {
622 ni->allocated_size = na->compressed_size;
623 ni->flags |= FILE_ATTR_SPARSE_FILE;
624 } else
625 ni->allocated_size = na->allocated_size;
626 }
627 return (0);
628}
629
630/*
631 * Assign a runlist to an attribute and store
632 *
633 * Returns 0 if successful
634 * -1 otherwise, with errno set accordingly
635 */
636
637static int assign_runlist(struct ALLOC_CONTEXT *alctx)
638{
639 ntfs_attr *na;
640 ntfs_attr_search_ctx *ctx;
641 int k;
642 int err;
643
644 err = 0;
645 na = alctx->na;
646 if (na->rl)
647 free(na->rl);
648 na->rl = alctx->rl;
649 /* Allocate the clusters */
650 for (k=0; ((k + 1) < alctx->rl_count) && !err; k++) {
651 if (ntfs_bitmap_set_run(alctx->vol->lcnbmp_na,
652 alctx->rl[k].lcn, alctx->rl[k].length)) {
653 err = -1;
654 }
655 }
656 na->allocated_size = alctx->wanted_clusters
657 << alctx->vol->cluster_size_bits;
658 NAttrSetNonResident(na);
659 NAttrSetFullyMapped(na);
660 if (err || ntfs_attr_update_mapping_pairs(na, 0)) {
661 err = -1;
662 } else {
663 ctx = ntfs_attr_get_search_ctx(alctx->na->ni, NULL);
664 if (ctx) {
665 if (ntfs_attr_lookup(opts.attribute, na->name,
666 na->name_len,
667 CASE_SENSITIVE, 0, NULL, 0, ctx)) {
668 err = -1;
669 } else {
670 if (set_sizes(alctx, ctx))
671 err = -1;
672 }
673 } else
674 err = -1;
675 ntfs_attr_put_search_ctx(ctx);
676 }
677 return (err);
678}
679
680/*
681 * Find the runs which minimize fragmentation
682 *
683 * Only the first and second data zones are examined, the MFT zone
684 * is preserved.
685 *
686 * Returns 0 if successful
687 * -1 otherwise, with errno set accordingly
688 */
689
690static int find_best_runs(struct ALLOC_CONTEXT *alctx)
691{
692 ntfs_volume *vol;
693 s64 pos; /* bit index in bitmap */
694 s64 br; /* byte count in buf */
695 int err;
696 enum STEP step;
697
698 err = 0;
699 vol = alctx->vol;
700 /* examine the first data zone */
701 pos = vol->mft_zone_end;
702 br = vol->cluster_size;
703 step = STEP_ZERO;
704 while ((step != STEP_ERR)
705 && (br == vol->cluster_size)
706 && (pos < vol->nr_clusters)) {
707 br = ntfs_attr_pread(vol->lcnbmp_na,
708 (pos >> 3) & -vol->cluster_size,
709 vol->cluster_size, alctx->buf);
710 if (br > 0) {
711 step = examine_buf(alctx, pos, br, step);
712 pos = (pos | ((vol->cluster_size << 3) - 1)) + 1;
713 }
714 }
715 /* examine the second data zone */
716 pos = 0;
717 br = vol->cluster_size;
718 step = STEP_ZERO;
719 while ((step != STEP_ERR)
720 && (br == vol->cluster_size)
721 && (pos < vol->mft_zone_start)) {
722 br = ntfs_attr_pread(vol->lcnbmp_na,
723 (pos >> 3) & -vol->cluster_size,
724 vol->cluster_size, alctx->buf);
725 if (br > 0) {
726 step = examine_buf(alctx, pos, br, step);
727 pos = (pos | ((vol->cluster_size << 3) - 1)) + 1;
728 }
729 }
730 if (alctx->gathered_clusters < alctx->wanted_clusters) {
731 errno = ENOSPC;
732 ntfs_log_error("Error : not enough space on device\n");
733 err = -1;
734 } else {
735 if ((step == STEP_ERR) || sort_runlist(alctx))
736 err = -1;
737 }
738 return (err);
739}
740
741/*
742 * Preallocate clusters with minimal fragmentation
743 *
744 * Returns 0 if successful
745 * -1 otherwise, with errno set accordingly
746 */
747
748static int preallocate(ntfs_attr *na, s64 new_size)
749{
750 struct ALLOC_CONTEXT *alctx;
751 ntfs_volume *vol;
752 int err;
753
754 err = 0;
755 vol = na->ni->vol;
756 alctx = (struct ALLOC_CONTEXT*)ntfs_malloc(sizeof(struct ALLOC_CONTEXT));
757 if (alctx) {
758 alctx->buf = (unsigned char*)ntfs_malloc(vol->cluster_size);
759 if (alctx->buf) {
760 alctx->na = na;
761 alctx->vol = vol;
762 alctx->rl_count = 0;
763 alctx->rl_allocated = 0;
764 alctx->rl = (runlist_element*)NULL;
765 alctx->new_size = new_size;
766 alctx->wanted_clusters = (new_size
767 + vol->cluster_size - 1)
768 >> vol->cluster_size_bits;
769 alctx->gathered_clusters = 0;
770 if (find_best_runs(alctx))
771 err = -1;
772 if (!err && !opts.noaction) {
773 if (assign_runlist(alctx))
774 err = -1;
775 } else
776 free(alctx->rl);
777 free(alctx->buf);
778 } else
779 err = -1;
780 free(alctx);
781 } else
782 err = -1;
783 return (err);
784}
785
Steve Kondik2111ad72013-07-07 12:07:44 -0700786/**
787 * Create a regular file under the given directory inode
788 *
789 * It is a wrapper function to ntfs_create(...)
790 *
791 * Return: the created file inode
792 */
793static ntfs_inode *ntfs_new_file(ntfs_inode *dir_ni,
794 const char *filename)
795{
796 ntfschar *ufilename;
797 /* inode to the file that is being created */
798 ntfs_inode *ni;
799 int ufilename_len;
800
801 /* ntfs_mbstoucs(...) will allocate memory for ufilename if it's NULL */
802 ufilename = NULL;
803 ufilename_len = ntfs_mbstoucs(filename, &ufilename);
804 if (ufilename_len == -1) {
805 ntfs_log_perror("ERROR: Failed to convert '%s' to unicode",
806 filename);
807 return NULL;
808 }
Steve Kondike68cb602016-08-28 00:45:36 -0700809 ni = ntfs_create(dir_ni, const_cpu_to_le32(0), ufilename, ufilename_len, S_IFREG);
Steve Kondik2111ad72013-07-07 12:07:44 -0700810 free(ufilename);
811 return ni;
812}
813
814/**
815 * main - Begin here
816 *
817 * Start from here.
818 *
819 * Return: 0 Success, the program worked
820 * 1 Error, something went wrong
821 */
822int main(int argc, char *argv[])
823{
824 FILE *in;
825 ntfs_volume *vol;
826 ntfs_inode *out;
827 ntfs_attr *na;
828 int flags = 0;
Steve Kondik79165c32015-11-09 19:43:00 -0800829 int res;
Steve Kondik2111ad72013-07-07 12:07:44 -0700830 int result = 1;
831 s64 new_size;
832 u64 offset;
833 char *buf;
834 s64 br, bw;
835 ntfschar *attr_name;
836 int attr_name_len = 0;
Steve Kondike68cb602016-08-28 00:45:36 -0700837#ifdef HAVE_WINDOWS_H
838 char *unix_name;
839#endif
Steve Kondik2111ad72013-07-07 12:07:44 -0700840
841 ntfs_log_set_handler(ntfs_log_handler_stderr);
842
Steve Kondik79165c32015-11-09 19:43:00 -0800843 res = parse_options(argc, argv);
844 if (res >= 0)
845 return (res);
Steve Kondik2111ad72013-07-07 12:07:44 -0700846
847 utils_set_locale();
848
849 /* Set SIGINT handler. */
850 if (signal(SIGINT, signal_handler) == SIG_ERR) {
851 ntfs_log_perror("Failed to set SIGINT handler");
852 return 1;
853 }
854 /* Set SIGTERM handler. */
855 if (signal(SIGTERM, signal_handler) == SIG_ERR) {
856 ntfs_log_perror("Failed to set SIGTERM handler");
857 return 1;
858 }
859
860 if (opts.noaction)
861 flags = NTFS_MNT_RDONLY;
862 if (opts.force)
863 flags |= NTFS_MNT_RECOVER;
864
865 vol = utils_mount_volume(opts.device, flags);
866 if (!vol) {
867 ntfs_log_perror("ERROR: couldn't mount volume");
868 return 1;
869 }
870
871 if ((vol->flags & VOLUME_IS_DIRTY) && !opts.force)
872 goto umount;
873
874 NVolSetCompression(vol); /* allow compression */
875 if (ntfs_volume_get_free_space(vol)) {
876 ntfs_log_perror("ERROR: couldn't get free space");
877 goto umount;
878 }
879
880 {
881 struct stat fst;
882 if (stat(opts.src_file, &fst) == -1) {
883 ntfs_log_perror("ERROR: Couldn't stat source file");
884 goto umount;
885 }
886 new_size = fst.st_size;
887 }
888 ntfs_log_verbose("New file size: %lld\n", (long long)new_size);
889
890 in = fopen(opts.src_file, "r");
891 if (!in) {
892 ntfs_log_perror("ERROR: Couldn't open source file");
893 goto umount;
894 }
895
896 if (opts.inode) {
897 s64 inode_num;
898 char *s;
899
900 inode_num = strtoll(opts.dest_file, &s, 0);
901 if (*s) {
902 ntfs_log_error("ERROR: Couldn't parse inode number.\n");
903 goto close_src;
904 }
905 out = ntfs_inode_open(vol, inode_num);
Steve Kondike68cb602016-08-28 00:45:36 -0700906 } else {
907#ifdef HAVE_WINDOWS_H
908 unix_name = ntfs_utils_unix_path(opts.dest_file);
909 if (unix_name) {
910 out = ntfs_pathname_to_inode(vol, NULL, unix_name);
911 } else
912 out = (ntfs_inode*)NULL;
913#else
Steve Kondik2111ad72013-07-07 12:07:44 -0700914 out = ntfs_pathname_to_inode(vol, NULL, opts.dest_file);
Steve Kondike68cb602016-08-28 00:45:36 -0700915#endif
916 }
Steve Kondik2111ad72013-07-07 12:07:44 -0700917 if (!out) {
918 /* Copy the file if the dest_file's parent dir can be opened. */
919 char *parent_dirname;
920 char *filename;
921 ntfs_inode *dir_ni;
922 ntfs_inode *ni;
923 char *dirname_last_whack;
924
Steve Kondike68cb602016-08-28 00:45:36 -0700925#ifdef HAVE_WINDOWS_H
926 filename = basename(unix_name);
927 parent_dirname = strdup(unix_name);
928#else
Steve Kondik2111ad72013-07-07 12:07:44 -0700929 filename = basename(opts.dest_file);
930 parent_dirname = strdup(opts.dest_file);
Steve Kondike68cb602016-08-28 00:45:36 -0700931#endif
Steve Kondik2111ad72013-07-07 12:07:44 -0700932 if (!parent_dirname) {
933 ntfs_log_perror("strdup() failed");
934 goto close_src;
935 }
936 dirname_last_whack = strrchr(parent_dirname, '/');
937 if (dirname_last_whack) {
Steve Kondik79165c32015-11-09 19:43:00 -0800938 if (dirname_last_whack == parent_dirname)
939 dirname_last_whack[1] = 0;
940 else
941 *dirname_last_whack = 0;
Steve Kondik2111ad72013-07-07 12:07:44 -0700942 dir_ni = ntfs_pathname_to_inode(vol, NULL,
943 parent_dirname);
944 } else {
945 ntfs_log_verbose("Target path does not contain '/'. "
946 "Using root directory as parent.\n");
947 dir_ni = ntfs_inode_open(vol, FILE_root);
948 }
949 if (dir_ni) {
950 if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
951 /* Remove the last '/' for estetic reasons. */
952 dirname_last_whack[0] = 0;
953 ntfs_log_error("The file '%s' already exists "
954 "and is not a directory. "
955 "Aborting.\n", parent_dirname);
956 free(parent_dirname);
957 ntfs_inode_close(dir_ni);
958 goto close_src;
959 }
960 ntfs_log_verbose("Creating a new file '%s' under '%s'"
961 "\n", filename, parent_dirname);
962 ni = ntfs_new_file(dir_ni, filename);
963 ntfs_inode_close(dir_ni);
964 if (!ni) {
965 ntfs_log_perror("Failed to create '%s' under "
966 "'%s'", filename,
967 parent_dirname);
968 free(parent_dirname);
969 goto close_src;
970 }
971 out = ni;
972 } else {
973 ntfs_log_perror("ERROR: Couldn't open '%s'",
974 parent_dirname);
975 free(parent_dirname);
976 goto close_src;
977 }
978 free(parent_dirname);
979 }
980 /* The destination is a directory. */
981 if ((out->mrec->flags & MFT_RECORD_IS_DIRECTORY) && !opts.inode) {
982 char *filename;
983 char *overwrite_filename;
984 int overwrite_filename_len;
985 ntfs_inode *ni;
986 ntfs_inode *dir_ni;
987 int filename_len;
988 int dest_dirname_len;
989
990 filename = basename(opts.src_file);
991 dir_ni = out;
992 filename_len = strlen(filename);
993 dest_dirname_len = strlen(opts.dest_file);
994 overwrite_filename_len = filename_len+dest_dirname_len + 2;
995 overwrite_filename = malloc(overwrite_filename_len);
996 if (!overwrite_filename) {
997 ntfs_log_perror("ERROR: Failed to allocate %i bytes "
998 "memory for the overwrite filename",
999 overwrite_filename_len);
1000 ntfs_inode_close(out);
1001 goto close_src;
1002 }
Steve Kondike68cb602016-08-28 00:45:36 -07001003#ifdef HAVE_WINDOWS_H
1004 strcpy(overwrite_filename, unix_name);
1005#else
Steve Kondik2111ad72013-07-07 12:07:44 -07001006 strcpy(overwrite_filename, opts.dest_file);
Steve Kondike68cb602016-08-28 00:45:36 -07001007#endif
1008 if (overwrite_filename[dest_dirname_len - 1] != '/') {
Steve Kondik2111ad72013-07-07 12:07:44 -07001009 strcat(overwrite_filename, "/");
1010 }
1011 strcat(overwrite_filename, filename);
Steve Kondik79165c32015-11-09 19:43:00 -08001012 ni = ntfs_pathname_to_inode(vol, dir_ni, overwrite_filename);
Steve Kondik2111ad72013-07-07 12:07:44 -07001013 /* Does a file with the same name exist in the dest dir? */
1014 if (ni) {
1015 ntfs_log_verbose("Destination path has a file with "
1016 "the same name\nOverwriting the file "
1017 "'%s'\n", overwrite_filename);
1018 ntfs_inode_close(out);
1019 out = ni;
1020 } else {
1021 ntfs_log_verbose("Creating a new file '%s' under "
1022 "'%s'\n", filename, opts.dest_file);
1023 ni = ntfs_new_file(dir_ni, filename);
1024 ntfs_inode_close(dir_ni);
1025 if (!ni) {
1026 ntfs_log_perror("ERROR: Failed to create the "
1027 "destination file under '%s'",
1028 opts.dest_file);
1029 free(overwrite_filename);
1030 goto close_src;
1031 }
1032 out = ni;
1033 }
1034 free(overwrite_filename);
1035 }
1036
1037 attr_name = ntfs_str2ucs(opts.attr_name, &attr_name_len);
1038 if (!attr_name) {
1039 ntfs_log_perror("ERROR: Failed to parse attribute name '%s'",
1040 opts.attr_name);
1041 goto close_dst;
1042 }
1043
1044 na = ntfs_attr_open(out, opts.attribute, attr_name, attr_name_len);
1045 if (!na) {
1046 if (errno != ENOENT) {
1047 ntfs_log_perror("ERROR: Couldn't open attribute");
1048 goto close_dst;
1049 }
1050 /* Requested attribute isn't present, add it. */
1051 if (ntfs_attr_add(out, opts.attribute, attr_name,
1052 attr_name_len, NULL, 0)) {
1053 ntfs_log_perror("ERROR: Couldn't add attribute");
1054 goto close_dst;
1055 }
1056 na = ntfs_attr_open(out, opts.attribute, attr_name,
1057 attr_name_len);
1058 if (!na) {
1059 ntfs_log_perror("ERROR: Couldn't open just added "
1060 "attribute");
1061 goto close_dst;
1062 }
1063 }
Steve Kondik2111ad72013-07-07 12:07:44 -07001064
1065 ntfs_log_verbose("Old file size: %lld\n", (long long)na->data_size);
Steve Kondik79165c32015-11-09 19:43:00 -08001066 if (opts.minfragments && NAttrCompressed(na)) {
1067 ntfs_log_info("Warning : Cannot avoid fragmentation"
1068 " of a compressed attribute\n");
1069 opts.minfragments = 0;
1070 }
1071 if (na->data_size && opts.minfragments) {
1072 if (ntfs_attr_truncate(na, 0)) {
1073 ntfs_log_perror(
1074 "ERROR: Couldn't truncate existing attribute");
Steve Kondik2111ad72013-07-07 12:07:44 -07001075 goto close_attr;
1076 }
1077 }
Steve Kondik79165c32015-11-09 19:43:00 -08001078 if (na->data_size != new_size) {
1079 if (opts.minfragments) {
1080 /*
1081 * Do a standard truncate() to check whether the
1082 * attribute has to be made non-resident.
1083 * If still resident, preallocation is not needed.
1084 */
1085 if (ntfs_attr_truncate(na, new_size)) {
1086 ntfs_log_perror(
1087 "ERROR: Couldn't resize attribute");
1088 goto close_attr;
1089 }
1090 if (NAttrNonResident(na)
1091 && preallocate(na, new_size)) {
1092 ntfs_log_perror(
1093 "ERROR: Couldn't preallocate attribute");
1094 goto close_attr;
1095 }
1096 } else {
1097 if (ntfs_attr_truncate_solid(na, new_size)) {
1098 ntfs_log_perror(
1099 "ERROR: Couldn't resize attribute");
1100 goto close_attr;
1101 }
1102 }
1103 }
Steve Kondik2111ad72013-07-07 12:07:44 -07001104
1105 buf = malloc(NTFS_BUF_SIZE);
1106 if (!buf) {
1107 ntfs_log_perror("ERROR: malloc failed");
1108 goto close_attr;
1109 }
1110
1111 ntfs_log_verbose("Starting write.\n");
1112 offset = 0;
1113 while (!feof(in)) {
1114 if (caught_terminate) {
1115 ntfs_log_error("SIGTERM or SIGINT received. "
1116 "Aborting write.\n");
1117 break;
1118 }
1119 br = fread(buf, 1, NTFS_BUF_SIZE, in);
1120 if (!br) {
1121 if (!feof(in)) ntfs_log_perror("ERROR: fread failed");
1122 break;
1123 }
1124 bw = ntfs_attr_pwrite(na, offset, br, buf);
1125 if (bw != br) {
1126 ntfs_log_perror("ERROR: ntfs_attr_pwrite failed");
1127 break;
1128 }
1129 offset += bw;
1130 }
1131 if ((na->data_flags & ATTR_COMPRESSION_MASK)
1132 && ntfs_attr_pclose(na))
1133 ntfs_log_perror("ERROR: ntfs_attr_pclose failed");
1134 ntfs_log_verbose("Syncing.\n");
1135 result = 0;
1136 free(buf);
1137close_attr:
1138 ntfs_attr_close(na);
1139close_dst:
Steve Kondik79165c32015-11-09 19:43:00 -08001140 while (ntfs_inode_close(out) && !opts.noaction) {
Steve Kondik2111ad72013-07-07 12:07:44 -07001141 if (errno != EBUSY) {
1142 ntfs_log_error("Sync failed. Run chkdsk.\n");
1143 break;
1144 }
1145 ntfs_log_error("Device busy. Will retry sync in 3 seconds.\n");
1146 sleep(3);
1147 }
1148close_src:
1149 fclose(in);
1150umount:
1151 ntfs_umount(vol, FALSE);
1152 ntfs_log_verbose("Done.\n");
1153 return result;
1154}