ntfscp.c revision 9663:ace9a2ac3683
1/**
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
7 *
8 * This utility will copy file to an NTFS volume.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program (in the main directory of the Linux-NTFS
22 * distribution in the file COPYING); if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25
26#include "config.h"
27
28#ifdef HAVE_STDIO_H
29#include <stdio.h>
30#endif
31#ifdef HAVE_GETOPT_H
32#include <getopt.h>
33#endif
34#ifdef HAVE_STDLIB_H
35#include <stdlib.h>
36#endif
37#ifdef HAVE_STRING_H
38#include <string.h>
39#endif
40#include <signal.h>
41#ifdef HAVE_SYS_STAT_H
42#include <sys/stat.h>
43#endif
44#ifdef HAVE_UNISTD_H
45#include <unistd.h>
46#endif
47#ifdef HAVE_LIBGEN_H
48#include <libgen.h>
49#endif
50
51#include "types.h"
52#include "attrib.h"
53#include "utils.h"
54#include "volume.h"
55#include "dir.h"
56#include "debug.h"
57#include "version.h"
58#include "logging.h"
59
60struct options {
61	char		*device;	/* Device/File to work with */
62	char		*src_file;	/* Source file */
63	char		*dest_file;	/* Destination file */
64	char		*attr_name;	/* Write to attribute with this name. */
65	int		 force;		/* Override common sense */
66	int		 quiet;		/* Less output */
67	int		 verbose;	/* Extra output */
68	int		 noaction;	/* Do not write to disk */
69	ATTR_TYPES	 attribute;	/* Write to this attribute. */
70	int		 inode;		/* Treat dest_file as inode number. */
71};
72
73static const char *EXEC_NAME = "ntfscp";
74static struct options opts;
75static volatile sig_atomic_t caught_terminate = 0;
76
77/**
78 * version - Print version information about the program
79 *
80 * Print a copyright statement and a brief description of the program.
81 *
82 * Return:  none
83 */
84static void version(void)
85{
86	ntfs_log_info("\n%s v%s (libntfs %s) - Copy file to an NTFS "
87		"volume.\n\n", EXEC_NAME, VERSION, ntfs_libntfs_version());
88	ntfs_log_info("Copyright (c) 2004-2007 Yura Pakhuchiy\n");
89	ntfs_log_info("Copyright (c) 2005 Anton Altaparmakov\n");
90	ntfs_log_info("Copyright (c) 2006 Hil Liao\n");
91	ntfs_log_info("\n%s\n%s%s\n", ntfs_gpl, ntfs_bugs, ntfs_home);
92}
93
94/**
95 * usage - Print a list of the parameters to the program
96 *
97 * Print a list of the parameters and options for the program.
98 *
99 * Return:  none
100 */
101static void usage(void)
102{
103	ntfs_log_info("\nUsage: %s [options] device src_file dest_file\n\n"
104		"    -a, --attribute NUM   Write to this attribute\n"
105		"    -i, --inode           Treat dest_file as inode number\n"
106		"    -f, --force           Use less caution\n"
107		"    -h, --help            Print this help\n"
108		"    -N, --attr-name NAME  Write to attribute with this name\n"
109		"    -n, --no-action       Do not write to disk\n"
110		"    -q, --quiet           Less output\n"
111		"    -V, --version         Version information\n"
112		"    -v, --verbose         More output\n\n",
113		EXEC_NAME);
114	ntfs_log_info("%s%s\n", ntfs_bugs, ntfs_home);
115}
116
117/**
118 * parse_options - Read and validate the programs command line
119 *
120 * Read the command line, verify the syntax and parse the options.
121 * This function is very long, but quite simple.
122 *
123 * Return:  1 Success
124 *	    0 Error, one or more problems
125 */
126static int parse_options(int argc, char **argv)
127{
128	static const char *sopt = "-a:ifh?N:nqVv";
129	static const struct option lopt[] = {
130		{ "attribute",	required_argument,	NULL, 'a' },
131		{ "inode",	no_argument,		NULL, 'i' },
132		{ "force",	no_argument,		NULL, 'f' },
133		{ "help",	no_argument,		NULL, 'h' },
134		{ "attr-name",	required_argument,	NULL, 'N' },
135		{ "no-action",	no_argument,		NULL, 'n' },
136		{ "quiet",	no_argument,		NULL, 'q' },
137		{ "version",	no_argument,		NULL, 'V' },
138		{ "verbose",	no_argument,		NULL, 'v' },
139		{ NULL,		0,			NULL, 0   }
140	};
141
142	char *s;
143	int c = -1;
144	int err  = 0;
145	int ver  = 0;
146	int help = 0;
147	int levels = 0;
148	s64 attr;
149
150	opts.device = NULL;
151	opts.src_file = NULL;
152	opts.dest_file = NULL;
153	opts.attr_name = NULL;
154	opts.inode = 0;
155	opts.attribute = AT_DATA;
156
157	opterr = 0; /* We'll handle the errors, thank you. */
158
159	while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
160		switch (c) {
161		case 1:	/* A non-option argument */
162			if (!opts.device) {
163				opts.device = argv[optind - 1];
164			} else if (!opts.src_file) {
165				opts.src_file = argv[optind - 1];
166			} else if (!opts.dest_file) {
167				opts.dest_file = argv[optind - 1];
168			} else {
169				ntfs_log_error("You must specify exactly two "
170						"files.\n");
171				err++;
172			}
173			break;
174		case 'a':
175			if (opts.attribute != AT_DATA) {
176				ntfs_log_error("You can specify only one "
177						"attribute.\n");
178				err++;
179				break;
180			}
181
182			attr = strtol(optarg, &s, 0);
183			if (*s) {
184				ntfs_log_error("Couldn't parse attribute.\n");
185				err++;
186			} else
187				opts.attribute = (ATTR_TYPES)cpu_to_le32(attr);
188			break;
189		case 'i':
190			opts.inode++;
191			break;
192		case 'f':
193			opts.force++;
194			break;
195		case 'h':
196		case '?':
197			if (strncmp(argv[optind - 1], "--log-", 6) == 0) {
198				if (!ntfs_log_parse_option(argv[optind - 1]))
199					err++;
200				break;
201			}
202			help++;
203			break;
204		case 'N':
205			if (opts.attr_name) {
206				ntfs_log_error("You can specify only one "
207						"attribute name.\n");
208				err++;
209			} else
210				opts.attr_name = argv[optind - 1];
211			break;
212		case 'n':
213			opts.noaction++;
214			break;
215		case 'q':
216			opts.quiet++;
217			ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
218			break;
219		case 'V':
220			ver++;
221			break;
222		case 'v':
223			opts.verbose++;
224			ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
225			break;
226		default:
227			ntfs_log_error("Unknown option '%s'.\n",
228					argv[optind - 1]);
229			err++;
230			break;
231		}
232	}
233
234	/* Make sure we're in sync with the log levels */
235	levels = ntfs_log_get_levels();
236	if (levels & NTFS_LOG_LEVEL_VERBOSE)
237		opts.verbose++;
238	if (!(levels & NTFS_LOG_LEVEL_QUIET))
239		opts.quiet++;
240
241	if (help || ver) {
242		opts.quiet = 0;
243	} else {
244		if (!opts.device) {
245			ntfs_log_error("You must specify a device.\n");
246			err++;
247		} else if (!opts.src_file) {
248			ntfs_log_error("You must specify a source file.\n");
249			err++;
250		} else if (!opts.dest_file) {
251			ntfs_log_error("You must specify a destination "
252					"file.\n");
253			err++;
254		}
255
256		if (opts.quiet && opts.verbose) {
257			ntfs_log_error("You may not use --quiet and --verbose "
258					"at the same time.\n");
259			err++;
260		}
261	}
262
263	if (ver)
264		version();
265	if (help || err)
266		usage();
267
268	return (!err && !help && !ver);
269}
270
271/**
272 * signal_handler - Handle SIGINT and SIGTERM: abort write, sync and exit.
273 */
274static void signal_handler(int arg __attribute__((unused)))
275{
276	caught_terminate++;
277}
278
279/**
280 * Create a regular file under the given directory inode
281 *
282 * It is a wrapper function to ntfs_create(...)
283 *
284 * Return:  the created file inode
285 */
286static ntfs_inode *ntfs_new_file(ntfs_inode *dir_ni,
287			  const char *filename)
288{
289	ntfschar *ufilename;
290	/* inode to the file that is being created */
291	ntfs_inode *ni;
292	int ufilename_len;
293
294	/* ntfs_mbstoucs(...) will allocate memory for ufilename if it's NULL */
295	ufilename = NULL;
296	ufilename_len = ntfs_mbstoucs(filename, &ufilename, 0);
297	if (ufilename_len == -1) {
298		ntfs_log_perror("ERROR: Failed to convert '%s' to unicode",
299					filename);
300		return NULL;
301	}
302	ni = ntfs_create(dir_ni, ufilename, ufilename_len, S_IFREG);
303	free(ufilename);
304	return ni;
305}
306
307/**
308 * main - Begin here
309 *
310 * Start from here.
311 *
312 * Return:  0  Success, the program worked
313 *	    1  Error, something went wrong
314 */
315int main(int argc, char *argv[])
316{
317	FILE *in;
318	ntfs_volume *vol;
319	ntfs_inode *out;
320	ntfs_attr *na;
321	int flags = 0;
322	int result = 1;
323	s64 new_size;
324	u64 offset;
325	char *buf;
326	s64 br, bw;
327	ntfschar *attr_name;
328	int attr_name_len = 0;
329
330	ntfs_log_set_handler(ntfs_log_handler_stderr);
331
332	if (!parse_options(argc, argv))
333		return 1;
334
335	utils_set_locale();
336
337	/* Set SIGINT handler. */
338	if (signal(SIGINT, signal_handler) == SIG_ERR) {
339		ntfs_log_perror("Failed to set SIGINT handler");
340		return 1;
341	}
342	/* Set SIGTERM handler. */
343	if (signal(SIGTERM, signal_handler) == SIG_ERR) {
344		ntfs_log_perror("Failed to set SIGTERM handler");
345		return 1;
346	}
347
348	if (opts.noaction)
349		flags = NTFS_MNT_RDONLY;
350	if (opts.force)
351		flags |= NTFS_MNT_FORCE;
352
353	vol = utils_mount_volume(opts.device, flags);
354	if (!vol) {
355		ntfs_log_perror("ERROR: couldn't mount volume");
356		return 1;
357	}
358
359	if (NVolWasDirty(vol) && !opts.force)
360		goto umount;
361
362	{
363		struct stat fst;
364		if (stat(opts.src_file, &fst) == -1) {
365			ntfs_log_perror("ERROR: Couldn't stat source file");
366			goto umount;
367		}
368		new_size = fst.st_size;
369	}
370	ntfs_log_verbose("New file size: %lld\n", new_size);
371
372	in = fopen(opts.src_file, "r");
373	if (!in) {
374		ntfs_log_perror("ERROR: Couldn't open source file");
375		goto umount;
376	}
377
378	if (opts.inode) {
379		s64 inode_num;
380		char *s;
381
382		inode_num = strtoll(opts.dest_file, &s, 0);
383		if (*s) {
384			ntfs_log_error("ERROR: Couldn't parse inode number.\n");
385			goto close_src;
386		}
387		out = ntfs_inode_open(vol, inode_num);
388	} else
389		out = ntfs_pathname_to_inode(vol, NULL, opts.dest_file);
390	if (!out) {
391		/* Copy the file if the dest_file's parent dir can be opened. */
392		char *parent_dirname;
393		char *filename;
394		ntfs_inode *dir_ni;
395		ntfs_inode *ni;
396		int dest_path_len;
397		char *dirname_last_whack;
398
399		filename = basename(opts.dest_file);
400		dest_path_len = strlen(opts.dest_file);
401		parent_dirname = strdup(opts.dest_file);
402		if (!parent_dirname) {
403			ntfs_log_perror("strdup() failed");
404			goto close_src;
405		}
406		dirname_last_whack = strrchr(parent_dirname, '/');
407		if (dirname_last_whack) {
408			dirname_last_whack[1] = 0;
409			dir_ni = ntfs_pathname_to_inode(vol, NULL,
410					parent_dirname);
411		} else {
412			ntfs_log_verbose("Target path does not contain '/'. "
413					"Using root directory as parent.\n");
414			dir_ni = ntfs_inode_open(vol, FILE_root);
415		}
416		if (dir_ni) {
417			if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
418				/* Remove the last '/' for estetic reasons. */
419				dirname_last_whack[0] = 0;
420				ntfs_log_error("The file '%s' already exists "
421						"and is not a directory. "
422						"Aborting.\n", parent_dirname);
423				free(parent_dirname);
424				ntfs_inode_close(dir_ni);
425				goto close_src;
426			}
427			ntfs_log_verbose("Creating a new file '%s' under '%s'"
428					 "\n", filename, parent_dirname);
429			ni = ntfs_new_file(dir_ni, filename);
430			ntfs_inode_close(dir_ni);
431			if (!ni) {
432				ntfs_log_perror("Failed to create '%s' under "
433						"'%s'", filename,
434						parent_dirname);
435				free(parent_dirname);
436				goto close_src;
437			}
438			out = ni;
439		} else {
440			ntfs_log_perror("ERROR: Couldn't open '%s'",
441					parent_dirname);
442			free(parent_dirname);
443			goto close_src;
444		}
445		free(parent_dirname);
446	}
447	/* The destination is a directory. */
448	if ((out->mrec->flags & MFT_RECORD_IS_DIRECTORY) && !opts.inode) {
449		char *filename;
450		char *overwrite_filename;
451		int overwrite_filename_len;
452		ntfs_inode *ni;
453		ntfs_inode *dir_ni;
454		int filename_len;
455		int dest_dirname_len;
456
457		filename = basename(opts.src_file);
458		dir_ni = out;
459		filename_len = strlen(filename);
460		dest_dirname_len = strlen(opts.dest_file);
461		overwrite_filename_len = filename_len+dest_dirname_len + 2;
462		overwrite_filename = malloc(overwrite_filename_len);
463		if (!overwrite_filename) {
464			ntfs_log_perror("ERROR: Failed to allocate %i bytes "
465					"memory for the overwrite filename",
466					overwrite_filename_len);
467			ntfs_inode_close(out);
468			goto close_src;
469		}
470		strcpy(overwrite_filename, opts.dest_file);
471		if (opts.dest_file[dest_dirname_len - 1] != '/') {
472			strcat(overwrite_filename, "/");
473		}
474		strcat(overwrite_filename, filename);
475		ni = ntfs_pathname_to_inode(vol, NULL, overwrite_filename);
476		/* Does a file with the same name exist in the dest dir? */
477		if (ni) {
478			ntfs_log_verbose("Destination path has a file with "
479					"the same name\nOverwriting the file "
480					"'%s'\n", overwrite_filename);
481			ntfs_inode_close(out);
482			out = ni;
483		} else {
484			ntfs_log_verbose("Creating a new file '%s' under "
485					"'%s'\n", filename, opts.dest_file);
486			ni = ntfs_new_file(dir_ni, filename);
487			ntfs_inode_close(dir_ni);
488			if (!ni) {
489				ntfs_log_perror("ERROR: Failed to create the "
490						"destination file under '%s'",
491						opts.dest_file);
492				free(overwrite_filename);
493				goto close_src;
494			}
495			out = ni;
496		}
497		free(overwrite_filename);
498	}
499
500	attr_name = ntfs_str2ucs(opts.attr_name, &attr_name_len);
501	if (!attr_name) {
502		ntfs_log_perror("ERROR: Failed to parse attribute name '%s'",
503				opts.attr_name);
504		goto close_dst;
505	}
506
507	na = ntfs_attr_open(out, opts.attribute, attr_name, attr_name_len);
508	if (!na) {
509		if (errno != ENOENT) {
510			ntfs_log_perror("ERROR: Couldn't open attribute");
511			goto close_dst;
512		}
513		/* Requested attribute isn't present, add it. */
514		if (ntfs_attr_add(out, opts.attribute, attr_name,
515				attr_name_len, NULL, 0)) {
516			ntfs_log_perror("ERROR: Couldn't add attribute");
517			goto close_dst;
518		}
519		na = ntfs_attr_open(out, opts.attribute, attr_name,
520				attr_name_len);
521		if (!na) {
522			ntfs_log_perror("ERROR: Couldn't open just added "
523					"attribute");
524			goto close_dst;
525		}
526	}
527	ntfs_ucsfree(attr_name);
528
529	ntfs_log_verbose("Old file size: %lld\n", na->data_size);
530	if (na->data_size != new_size) {
531		if (__ntfs_attr_truncate(na, new_size, FALSE)) {
532			ntfs_log_perror("ERROR: Couldn't resize attribute");
533			goto close_attr;
534		}
535	}
536
537	buf = malloc(NTFS_BUF_SIZE);
538	if (!buf) {
539		ntfs_log_perror("ERROR: malloc failed");
540		goto close_attr;
541	}
542
543	ntfs_log_verbose("Starting write.\n");
544	offset = 0;
545	while (!feof(in)) {
546		if (caught_terminate) {
547			ntfs_log_error("SIGTERM or SIGINT received.  "
548					"Aborting write.\n");
549			break;
550		}
551		br = fread(buf, 1, NTFS_BUF_SIZE, in);
552		if (!br) {
553			if (!feof(in)) ntfs_log_perror("ERROR: fread failed");
554			break;
555		}
556		bw = ntfs_attr_pwrite(na, offset, br, buf);
557		if (bw != br) {
558			ntfs_log_perror("ERROR: ntfs_attr_pwrite failed");
559			break;
560		}
561		offset += bw;
562	}
563	ntfs_log_verbose("Syncing.\n");
564	result = 0;
565	free(buf);
566close_attr:
567	ntfs_attr_close(na);
568close_dst:
569	while (ntfs_inode_close(out)) {
570		if (errno != EBUSY) {
571			ntfs_log_error("Sync failed. Run chkdsk.\n");
572			break;
573		}
574		ntfs_log_error("Device busy.  Will retry sync in 3 seconds.\n");
575		sleep(3);
576	}
577close_src:
578	fclose(in);
579umount:
580	ntfs_umount(vol, FALSE);
581	ntfs_log_verbose("Done.\n");
582	return result;
583}
584