1 /*  Copyright 1986-1992 Emmet P. Gray.
2  *  Copyright 1994,1996-2002,2007-2009 Alain Knaff.
3  *  This file is part of mtools.
4  *
5  *  Mtools is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  Mtools is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with Mtools.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * mcopy.c
19  * Copy an MSDOS files to and from Unix
20  *
21  */
22 
23 
24 #include "sysincludes.h"
25 #include "msdos.h"
26 #include "mtools.h"
27 #include "vfat.h"
28 #include "mainloop.h"
29 #include "plain_io.h"
30 #include "nameclash.h"
31 #include "file.h"
32 #include "fs.h"
33 
34 
35 /*
36  * Preserve the file modification times after the fclose()
37  */
38 
set_mtime(const char * target,time_t mtime)39 static void set_mtime(const char *target, time_t mtime)
40 {
41 	if (target && strcmp(target, "-") && mtime != 0L) {
42 #ifdef HAVE_UTIMES
43 		struct timeval tv[2];
44 		tv[0].tv_sec = mtime;
45 		tv[0].tv_usec = 0;
46 		tv[1].tv_sec = mtime;
47 		tv[1].tv_usec = 0;
48 		utimes((char *)target, tv);
49 #else
50 #ifdef HAVE_UTIME
51 		struct utimbuf utbuf;
52 
53 		utbuf.actime = mtime;
54 		utbuf.modtime = mtime;
55 		utime(target, &utbuf);
56 #endif
57 #endif
58 	}
59 	return;
60 }
61 
62 typedef struct Arg_t {
63 	int recursive;
64 	int preserveAttributes;
65 	int preserveTime;
66 	unsigned char attr;
67 	char *path;
68 	int textmode;
69 	int needfilter;
70 	int nowarn;
71 	int verbose;
72 	int type;
73 	int convertCharset;
74 	MainParam_t mp;
75 	ClashHandling_t ch;
76 	int noClobber;
77 } Arg_t;
78 
79 static int _unix_write(MainParam_t *mp, int needfilter, const char *unixFile);
80 
81 /* Write the Unix file */
unix_write(MainParam_t * mp,int needfilter)82 static int unix_write(MainParam_t *mp, int needfilter)
83 {
84 	Arg_t *arg=(Arg_t *) mp->arg;
85 
86 	if(arg->type)
87 		return _unix_write(mp, needfilter, "-");
88 	else {
89 		char *unixFile = mpBuildUnixFilename(mp);
90 		int ret;
91 		if(!unixFile) {
92 			printOom();
93 			return ERROR_ONE;
94 		}
95 		ret = _unix_write(mp, needfilter, unixFile);
96 		free(unixFile);
97 		return ret;
98 	}
99 }
100 
101 
102 /* Write the Unix file */
_unix_write(MainParam_t * mp,int needfilter,const char * unixFile)103 static int _unix_write(MainParam_t *mp, int needfilter, const char *unixFile)
104 {
105 	Arg_t *arg=(Arg_t *) mp->arg;
106 	time_t mtime;
107 	Stream_t *File=mp->File;
108 	Stream_t *Target, *Source;
109 	struct MT_STAT stbuf;
110 	int ret;
111 	char errmsg[80];
112 
113 	File->Class->get_data(File, &mtime, 0, 0, 0);
114 
115 	if (!arg->preserveTime)
116 		mtime = 0L;
117 
118 	/* if we are creating a file, check whether it already exists */
119 	if(!arg->type) {
120 		if (!arg->nowarn && !access(unixFile, 0)){
121 			if(arg->noClobber) {
122 				fprintf(stderr, "File \"%s\" exists. To overwrite, try again, and explicitly specify target directory\n",unixFile);
123 				return ERROR_ONE;
124 			}
125 
126 			/* sanity checking */
127 			if (!MT_STAT(unixFile, &stbuf)) {
128 				struct MT_STAT srcStbuf;
129 				int sFd; /* Source file descriptor */
130 				if(!S_ISREG(stbuf.st_mode)) {
131 					fprintf(stderr,"\"%s\" is not a regular file\n",
132 						unixFile);
133 
134 					return ERROR_ONE;
135 				}
136 				sFd = get_fd(File);
137 				if(sFd == -1) {
138 					fprintf(stderr, "Not ok Unix file ==> good\n");
139 				}
140 				if((!MT_FSTAT(sFd, &srcStbuf)) &&
141 				   stbuf.st_dev == srcStbuf.st_dev &&
142 				   stbuf.st_ino == srcStbuf.st_ino) {
143 					fprintf(stderr, "Attempt to copy file on itself\n");
144 					return ERROR_ONE;
145 				}
146 			}
147 
148 			if( ask_confirmation("File \"%s\" exists, overwrite (y/n) ? ",
149 					     unixFile)) {
150 				return ERROR_ONE;
151 			}
152 
153 		}
154 	}
155 
156 	if(!arg->type && arg->verbose) {
157 		fprintf(stderr,"Copying ");
158 		mpPrintFilename(stderr,mp);
159 		fprintf(stderr,"\n");
160 	}
161 
162 	if(got_signal) {
163 		return ERROR_ONE;
164 	}
165 
166 	if ((Target = SimpleFileOpen(0, 0, unixFile,
167 				     O_WRONLY | O_CREAT | O_TRUNC,
168 				     errmsg, 0, 0, 0))) {
169 		ret = 0;
170 		if(needfilter && arg->textmode){
171 			Source = open_filter(COPY(File),arg->convertCharset);
172 			if (!Source)
173 				ret = -1;
174 		} else
175 			Source = COPY(File);
176 
177 		if (ret == 0 )
178 			ret = copyfile(Source, Target);
179 		FREE(&Source);
180 		FREE(&Target);
181 		if(ret <= -1){
182 			if(!arg->type)
183 				unlink(unixFile);
184 			return ERROR_ONE;
185 		}
186 		if(!arg->type)
187 			set_mtime(unixFile, mtime);
188 		return GOT_ONE;
189 	} else {
190 		fprintf(stderr,"%s\n", errmsg);
191 		return ERROR_ONE;
192 	}
193 }
194 
makeUnixDir(char * filename)195 static int makeUnixDir(char *filename)
196 {
197 	if(!mkdir(filename
198 #ifndef OS_mingw32msvc
199 	          , 0777
200 #endif
201 	         ))
202 		return 0;
203 	if(errno == EEXIST) {
204 		struct MT_STAT buf;
205 		if(MT_STAT(filename, &buf) < 0)
206 			return -1;
207 		if(S_ISDIR(buf.st_mode))
208 			return 0;
209 		errno = ENOTDIR;
210 	}
211 	return -1;
212 }
213 
214 /* Copy a directory to Unix */
unix_copydir(direntry_t * entry,MainParam_t * mp)215 static int unix_copydir(direntry_t *entry, MainParam_t *mp)
216 {
217 	Arg_t *arg=(Arg_t *) mp->arg;
218 	time_t mtime;
219 	Stream_t *File=mp->File;
220 	int ret;
221 	char *unixFile;
222 
223 	if (!arg->recursive && mp->basenameHasWildcard)
224 		return 0;
225 
226 	File->Class->get_data(File, &mtime, 0, 0, 0);
227 	if (!arg->preserveTime)
228 		mtime = 0L;
229 	if(!arg->type && arg->verbose) {
230 		fprintf(stderr,"Copying ");
231 		fprintPwd(stderr, entry,0);
232 		fprintf(stderr, "\n");
233 	}
234 	if(got_signal)
235 		return ERROR_ONE;
236 	unixFile = mpBuildUnixFilename(mp);
237 	if(!unixFile) {
238 		printOom();
239 		return ERROR_ONE;
240 	}
241 	if(arg->type || !*mpPickTargetName(mp) || !makeUnixDir(unixFile)) {
242 		Arg_t newArg;
243 
244 		newArg = *arg;
245 		newArg.mp.arg = (void *) &newArg;
246 		newArg.mp.unixTarget = unixFile;
247 		newArg.mp.targetName = 0;
248 		newArg.mp.basenameHasWildcard = 1;
249 
250 		ret = mp->loop(File, &newArg.mp, "*");
251 		set_mtime(unixFile, mtime);
252 		free(unixFile);
253 		return ret | GOT_ONE;
254 	} else {
255 		perror("mkdir");
256 		fprintf(stderr,
257 			"Failure to make directory %s\n",
258 			unixFile);
259 		free(unixFile);
260 		return ERROR_ONE;
261 	}
262 }
263 
dos_to_unix(direntry_t * entry UNUSEDP,MainParam_t * mp)264 static  int dos_to_unix(direntry_t *entry UNUSEDP, MainParam_t *mp)
265 {
266 	return unix_write(mp, 1);
267 }
268 
269 
unix_to_unix(MainParam_t * mp)270 static  int unix_to_unix(MainParam_t *mp)
271 {
272 	return unix_write(mp, 0);
273 }
274 
275 
directory_dos_to_unix(direntry_t * entry,MainParam_t * mp)276 static int directory_dos_to_unix(direntry_t *entry, MainParam_t *mp)
277 {
278 	return unix_copydir(entry, mp);
279 }
280 
281 /*
282  * Open the named file for read, create the cluster chain, return the
283  * directory structure or NULL on error.
284  */
writeit(struct dos_name_t * dosname,char * longname,void * arg0,direntry_t * entry)285 static int writeit(struct dos_name_t *dosname,
286 		   char *longname,
287 		   void *arg0,
288 		   direntry_t *entry)
289 {
290 	Stream_t *Target;
291 	time_t now;
292 	int type, fat, ret;
293 	time_t date;
294 	mt_size_t filesize, newsize;
295 	Arg_t *arg = (Arg_t *) arg0;
296 
297 
298 
299 	if (arg->mp.File->Class->get_data(arg->mp.File,
300 									  & date, &filesize, &type, 0) < 0 ){
301 		fprintf(stderr, "Can't stat source file\n");
302 		return -1;
303 	}
304 
305 	if(fileTooBig(filesize)) {
306 		fprintf(stderr, "File \"%s\" too big\n", longname);
307 		return 1;
308 	}
309 
310 	if (type){
311 		if (arg->verbose)
312 			fprintf(stderr, "\"%s\" is a directory\n", longname);
313 		return -1;
314 	}
315 
316 	/*if (!arg->single || arg->recursive)*/
317 	if(arg->verbose)
318 		fprintf(stderr,"Copying %s\n", longname);
319 	if(got_signal)
320 		return -1;
321 
322 	/* will it fit? */
323 	if (!getfreeMinBytes(arg->mp.targetDir, filesize))
324 		return -1;
325 
326 	/* preserve mod time? */
327 	if (arg->preserveTime)
328 		now = date;
329 	else
330 		getTimeNow(&now);
331 
332 	mk_entry(dosname, arg->attr, 1, 0, now, &entry->dir);
333 
334 	Target = OpenFileByDirentry(entry);
335 	if(!Target){
336 		fprintf(stderr,"Could not open Target\n");
337 		exit(1);
338 	}
339 	if (arg->needfilter & arg->textmode)
340 		Target = open_filter(Target,arg->convertCharset);
341 
342 
343 
344 	ret = copyfile(arg->mp.File, Target);
345 	GET_DATA(Target, 0, &newsize, 0, &fat);
346 	FREE(&Target);
347 	if (arg->needfilter & arg->textmode)
348 	    newsize++; /* ugly hack: we gathered the size before the Ctrl-Z
349 			* was written.  Increment it manually */
350 	if(ret < 0 ){
351 		fat_free(arg->mp.targetDir, fat);
352 		return -1;
353 	} else {
354 		mk_entry(dosname, arg->attr, fat, truncBytes32(newsize),
355 				 now, &entry->dir);
356 		return 0;
357 	}
358 }
359 
360 
361 
dos_write(direntry_t * entry,MainParam_t * mp,int needfilter)362 static int dos_write(direntry_t *entry, MainParam_t *mp, int needfilter)
363 /* write a messy dos file to another messy dos file */
364 {
365 	int result;
366 	Arg_t * arg = (Arg_t *) (mp->arg);
367 	const char *targetName = mpPickTargetName(mp);
368 
369 	if(entry && arg->preserveAttributes)
370 		arg->attr = entry->dir.attr;
371 	else
372 		arg->attr = ATTR_ARCHIVE;
373 
374 	arg->needfilter = needfilter;
375 	if (entry && mp->targetDir == entry->Dir){
376 		arg->ch.ignore_entry = -1;
377 		arg->ch.source = entry->entry;
378 	} else {
379 		arg->ch.ignore_entry = -1;
380 		arg->ch.source = -2;
381 	}
382 	result = mwrite_one(mp->targetDir, targetName, 0,
383 			    writeit, (void *)arg, &arg->ch);
384 	if(result == 1)
385 		return GOT_ONE;
386 	else
387 		return ERROR_ONE;
388 }
389 
subDir(Stream_t * parent,const char * filename)390 static Stream_t *subDir(Stream_t *parent, const char *filename)
391 {
392 	direntry_t entry;
393 	initializeDirentry(&entry, parent);
394 
395 	switch(vfat_lookup(&entry, filename, -1, ACCEPT_DIR, 0, 0, 0, 0)) {
396 	    case 0:
397 		return OpenFileByDirentry(&entry);
398 	    case -1:
399 		return NULL;
400 	    default: /* IO Error */
401 		return NULL;
402 	}
403 }
404 
dos_copydir(direntry_t * entry,MainParam_t * mp)405 static int dos_copydir(direntry_t *entry, MainParam_t *mp)
406 /* copyes a directory to Dos */
407 {
408 	Arg_t * arg = (Arg_t *) (mp->arg);
409 	Arg_t newArg;
410 	time_t now;
411 	time_t date;
412 	int ret;
413 	const char *targetName = mpPickTargetName(mp);
414 
415 	if (!arg->recursive && mp->basenameHasWildcard)
416 		return 0;
417 
418 	if(entry && isSubdirOf(mp->targetDir, mp->File)) {
419 		fprintf(stderr, "Cannot recursively copy directory ");
420 		fprintPwd(stderr, entry,0);
421 		fprintf(stderr, " into one of its own subdirectories ");
422 		fprintPwd(stderr, getDirentry(mp->targetDir),0);
423 		fprintf(stderr, "\n");
424 		return ERROR_ONE;
425 	}
426 
427 	if (arg->mp.File->Class->get_data(arg->mp.File,
428 					  & date, 0, 0, 0) < 0 ){
429 		fprintf(stderr, "Can't stat source file\n");
430 		return ERROR_ONE;
431 	}
432 
433 	if(!arg->type && arg->verbose)
434 		fprintf(stderr,"Copying %s\n", mpGetBasename(mp));
435 
436 	if(entry && arg->preserveAttributes)
437 		arg->attr = entry->dir.attr;
438 	else
439 		arg->attr = 0;
440 
441 	if (entry && (mp->targetDir == entry->Dir)){
442 		arg->ch.ignore_entry = -1;
443 		arg->ch.source = entry->entry;
444 	} else {
445 		arg->ch.ignore_entry = -1;
446 		arg->ch.source = -2;
447 	}
448 
449 	/* preserve mod time? */
450 	if (arg->preserveTime)
451 		now = date;
452 	else
453 		getTimeNow(&now);
454 
455 	newArg = *arg;
456 	newArg.mp.arg = &newArg;
457 	newArg.mp.targetName = 0;
458 	newArg.mp.basenameHasWildcard = 1;
459 	if(*targetName) {
460 		/* maybe the directory already exist. Use it */
461 		newArg.mp.targetDir = subDir(mp->targetDir, targetName);
462 		if(!newArg.mp.targetDir)
463 			newArg.mp.targetDir = createDir(mp->targetDir,
464 							targetName,
465 							&arg->ch, arg->attr,
466 							now);
467 	} else
468 		newArg.mp.targetDir = mp->targetDir;
469 
470 	if(!newArg.mp.targetDir)
471 		return ERROR_ONE;
472 
473 	ret = mp->loop(mp->File, &newArg.mp, "*");
474 	if(*targetName)
475 		FREE(&newArg.mp.targetDir);
476 	return ret | GOT_ONE;
477 }
478 
479 
dos_to_dos(direntry_t * entry,MainParam_t * mp)480 static int dos_to_dos(direntry_t *entry, MainParam_t *mp)
481 {
482 	return dos_write(entry, mp, 0);
483 }
484 
unix_to_dos(MainParam_t * mp)485 static int unix_to_dos(MainParam_t *mp)
486 {
487 	return dos_write(0, mp, 1);
488 }
489 
490 static void usage(int ret) NORETURN;
usage(int ret)491 static void usage(int ret)
492 {
493 	fprintf(stderr,
494 		"Mtools version %s, dated %s\n", mversion, mdate);
495 	fprintf(stderr,
496 		"Usage: %s [-spatnmQVBT] [-D clash_option] sourcefile targetfile\n", progname);
497 	fprintf(stderr,
498 		"       %s [-spatnmQVBT] [-D clash_option] sourcefile [sourcefiles...] targetdirectory\n",
499 		progname);
500 	exit(ret);
501 }
502 
503 void mcopy(int argc, char **argv, int mtype) NORETURN;
mcopy(int argc,char ** argv,int mtype)504 void mcopy(int argc, char **argv, int mtype)
505 {
506 	Arg_t arg;
507 	int c, fastquit;
508 
509 
510 	/* get command line options */
511 
512 	init_clash_handling(& arg.ch);
513 
514 	/* get command line options */
515 	arg.recursive = 0;
516 	arg.preserveTime = 0;
517 	arg.preserveAttributes = 0;
518 	arg.nowarn = 0;
519 	arg.textmode = 0;
520 	arg.verbose = 0;
521 	arg.convertCharset = 0;
522 	arg.type = mtype;
523 	fastquit = 0;
524 	if(helpFlag(argc, argv))
525 		usage(0);
526 	while ((c = getopt(argc, argv, "i:abB/sptTnmvQD:oh")) != EOF) {
527 		switch (c) {
528 			case 'i':
529 				set_cmd_line_image(optarg);
530 				break;
531 			case 's':
532 			case '/':
533 				arg.recursive = 1;
534 				break;
535 			case 'p':
536 				arg.preserveAttributes = 1;
537 				break;
538 			case 'T':
539 				arg.convertCharset = 1;
540 			case 'a':
541 			case 't':
542 				arg.textmode = 1;
543 				break;
544 			case 'n':
545 				arg.nowarn = 1;
546 				break;
547 			case 'm':
548 				arg.preserveTime = 1;
549 				break;
550 			case 'v':
551 				arg.verbose = 1;
552 				break;
553 			case 'Q':
554 				fastquit = 1;
555 				break;
556 			case 'B':
557 			case 'b':
558 				batchmode = 1;
559 				break;
560 			case 'o':
561 				handle_clash_options(&arg.ch, c);
562 				break;
563 			case 'D':
564 				if(handle_clash_options(&arg.ch, *optarg))
565 					usage(1);
566 				break;
567 			case 'h':
568 				usage(0);
569 			case '?':
570 				usage(1);
571 			default:
572 				break;
573 		}
574 	}
575 
576 	if (argc - optind < 1)
577 		usage(1);
578 
579 	init_mp(&arg.mp);
580 	arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN | NO_DOTS;
581 	arg.mp.fast_quit = fastquit;
582 	arg.mp.arg = (void *) &arg;
583 	arg.mp.openflags = O_RDONLY;
584 	arg.noClobber = 0;
585 
586 	/* last parameter is "-", use mtype mode */
587 	if(!mtype && !strcmp(argv[argc-1], "-")) {
588 		arg.type = mtype = 1;
589 		argc--;
590 	}
591 
592 	if(mtype){
593 		/* Mtype = copying to stdout */
594 		arg.mp.targetName = strdup("-");
595 		arg.mp.unixTarget = strdup("");
596 		arg.mp.callback = dos_to_unix;
597 		arg.mp.dirCallback = unix_copydir;
598 		arg.mp.unixcallback = unix_to_unix;
599 	} else {
600 		const char *target;
601 		if (argc - optind == 1) {
602 			/* copying to the current directory */
603 			target = ".";
604 			arg.noClobber = 1;
605 		} else {
606 			/* target is the last item mentioned */
607 			argc--;
608 			target = argv[argc];
609 		}
610 
611 		target_lookup(&arg.mp, target);
612 		if(!arg.mp.targetDir && !arg.mp.unixTarget) {
613 			fprintf(stderr,"Bad target %s\n", target);
614 			exit(1);
615 		}
616 
617 		/* callback functions */
618 		if(arg.mp.unixTarget) {
619 			arg.mp.callback = dos_to_unix;
620 			arg.mp.dirCallback = directory_dos_to_unix;
621 			arg.mp.unixcallback = unix_to_unix;
622 		} else {
623 			arg.mp.dirCallback = dos_copydir;
624 			arg.mp.callback = dos_to_dos;
625 			arg.mp.unixcallback = unix_to_dos;
626 		}
627 	}
628 
629 	exit(main_loop(&arg.mp, argv + optind, argc - optind));
630 }
631