• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*  Copyright 1986-1992 Emmet P. Gray.
2  *  Copyright 1994,1996-2002,2007-2009,2021 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(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 	char errmsg[80];
111 
112 	File->Class->get_data(File, &mtime, 0, 0, 0);
113 
114 	if (!arg->preserveTime)
115 		mtime = 0L;
116 
117 	/* if we are creating a file, check whether it already exists */
118 	if(!arg->type) {
119 		if (!arg->nowarn && !access(unixFile, 0)){
120 			if(arg->noClobber) {
121 				fprintf(stderr, "File \"%s\" exists. To overwrite, try again, and explicitly specify target directory\n",unixFile);
122 				return ERROR_ONE;
123 			}
124 
125 			/* sanity checking */
126 			if (!MT_STAT(unixFile, &stbuf)) {
127 				struct MT_STAT srcStbuf;
128 				int sFd; /* Source file descriptor */
129 				if(!S_ISREG(stbuf.st_mode)) {
130 					fprintf(stderr,"\"%s\" is not a regular file\n",
131 						unixFile);
132 
133 					return ERROR_ONE;
134 				}
135 				sFd = get_fd(File);
136 				if(sFd == -1) {
137 				} else if((!MT_FSTAT(sFd, &srcStbuf)) &&
138 					   stbuf.st_dev == srcStbuf.st_dev &&
139 					   stbuf.st_ino == srcStbuf.st_ino) {
140 					fprintf(stderr, "Attempt to copy file on itself\n");
141 					return ERROR_ONE;
142 				}
143 			}
144 
145 			if( ask_confirmation("File \"%s\" exists, overwrite (y/n) ? ",
146 					     unixFile)) {
147 				return ERROR_ONE;
148 			}
149 
150 		}
151 	}
152 
153 	if(!arg->type && arg->verbose) {
154 		fprintf(stderr,"Copying ");
155 		mpPrintFilename(stderr,mp);
156 		fprintf(stderr,"\n");
157 	}
158 
159 	if(got_signal) {
160 		return ERROR_ONE;
161 	}
162 
163 	if ((Target = SimpleFileOpen(0, 0, unixFile,
164 				     O_WRONLY | O_CREAT | O_TRUNC,
165 				     errmsg, 0, 0, 0))) {
166 		mt_off_t ret;
167 		Source = COPY(File);
168 		if(needfilter && arg->textmode)
169 			Source = open_dos2unix(Source,arg->convertCharset);
170 
171 		if (Source)
172 			ret = copyfile(Source, Target);
173 		else
174 			ret = -1;
175 		FREE(&Source);
176 		FREE(&Target);
177 		if(ret < 0){
178 			if(!arg->type)
179 				unlink(unixFile);
180 			return ERROR_ONE;
181 		}
182 		if(!arg->type)
183 			set_mtime(unixFile, mtime);
184 		return GOT_ONE;
185 	} else {
186 		fprintf(stderr,"%s\n", errmsg);
187 		return ERROR_ONE;
188 	}
189 }
190 
makeUnixDir(char * filename)191 static int makeUnixDir(char *filename)
192 {
193 	if(!mkdir(filename
194 #ifndef OS_mingw32msvc
195 	          , 0777
196 #endif
197 	         ))
198 		return 0;
199 	if(errno == EEXIST) {
200 		struct MT_STAT buf;
201 		if(MT_STAT(filename, &buf) < 0)
202 			return -1;
203 		if(S_ISDIR(buf.st_mode))
204 			return 0;
205 		errno = ENOTDIR;
206 	}
207 	return -1;
208 }
209 
210 /* Copy a directory to Unix */
unix_copydir(direntry_t * entry,MainParam_t * mp)211 static int unix_copydir(direntry_t *entry, MainParam_t *mp)
212 {
213 	Arg_t *arg=(Arg_t *) mp->arg;
214 	time_t mtime;
215 	Stream_t *File=mp->File;
216 	int ret;
217 	char *unixFile;
218 
219 	if (!arg->recursive && mp->basenameHasWildcard)
220 		return 0;
221 
222 	File->Class->get_data(File, &mtime, 0, 0, 0);
223 	if (!arg->preserveTime)
224 		mtime = 0L;
225 	if(!arg->type && arg->verbose) {
226 		fprintf(stderr,"Copying ");
227 		fprintPwd(stderr, entry,0);
228 		fprintf(stderr, "\n");
229 	}
230 	if(got_signal)
231 		return ERROR_ONE;
232 	unixFile = mpBuildUnixFilename(mp);
233 	if(!unixFile) {
234 		printOom();
235 		return ERROR_ONE;
236 	}
237 	if(arg->type || !*mpPickTargetName(mp) || !makeUnixDir(unixFile)) {
238 		Arg_t newArg;
239 
240 		newArg = *arg;
241 		newArg.mp.arg = (void *) &newArg;
242 		newArg.mp.unixTarget = unixFile;
243 		newArg.mp.targetName = 0;
244 		newArg.mp.basenameHasWildcard = 1;
245 
246 		ret = mp->loop(File, &newArg.mp, "*");
247 		set_mtime(unixFile, mtime);
248 		free(unixFile);
249 		return ret | GOT_ONE;
250 	} else {
251 		perror("mkdir");
252 		fprintf(stderr,
253 			"Failure to make directory %s\n",
254 			unixFile);
255 		free(unixFile);
256 		return ERROR_ONE;
257 	}
258 }
259 
dos_to_unix(direntry_t * entry UNUSEDP,MainParam_t * mp)260 static  int dos_to_unix(direntry_t *entry UNUSEDP, MainParam_t *mp)
261 {
262 	return unix_write(mp, 1);
263 }
264 
265 
unix_to_unix(MainParam_t * mp)266 static  int unix_to_unix(MainParam_t *mp)
267 {
268 	return unix_write(mp, 0);
269 }
270 
271 
directory_dos_to_unix(direntry_t * entry,MainParam_t * mp)272 static int directory_dos_to_unix(direntry_t *entry, MainParam_t *mp)
273 {
274 	return unix_copydir(entry, mp);
275 }
276 
277 /*
278  * Open the named file for read, create the cluster chain, return the
279  * directory structure or NULL on error.
280  */
writeit(struct dos_name_t * dosname,char * longname,void * arg0,direntry_t * entry)281 static int writeit(struct dos_name_t *dosname,
282 		   char *longname,
283 		   void *arg0,
284 		   direntry_t *entry)
285 {
286 	Stream_t *Target;
287 	time_t now;
288 	int type;
289 	mt_off_t ret;
290 	uint32_t fat;
291 	time_t date;
292 	mt_off_t filesize;
293 	Arg_t *arg = (Arg_t *) arg0;
294 	Stream_t *Source = COPY(arg->mp.File);
295 
296 	if (Source->Class->get_data(Source, &date, &filesize,
297 				    &type, 0) < 0 ){
298 		fprintf(stderr, "Can't stat source file\n");
299 		return -1;
300 	}
301 
302 	if(fileTooBig(filesize)) {
303 		fprintf(stderr, "File \"%s\" too big\n", longname);
304 		return 1;
305 	}
306 
307 	if (type){
308 		if (arg->verbose)
309 			fprintf(stderr, "\"%s\" is a directory\n", longname);
310 		return -1;
311 	}
312 
313 	/*if (!arg->single || arg->recursive)*/
314 	if(arg->verbose)
315 		fprintf(stderr,"Copying %s\n", longname);
316 	if(got_signal)
317 		return -1;
318 
319 	/* will it fit? */
320 	if (!getfreeMinBytes(arg->mp.targetDir, filesize))
321 		return -1;
322 
323 	/* preserve mod time? */
324 	if (arg->preserveTime)
325 		now = date;
326 	else
327 		getTimeNow(&now);
328 
329 	mk_entry(dosname, arg->attr, 1, 0, now, &entry->dir);
330 
331 	Target = OpenFileByDirentry(entry);
332 	if(!Target){
333 		fprintf(stderr,"Could not open Target\n");
334 		exit(1);
335 	}
336 	if (arg->needfilter & arg->textmode) {
337 		Source = open_unix2dos(Source,arg->convertCharset);
338 	}
339 
340 	ret = copyfile(Source, Target);
341 	GET_DATA(Target, 0, 0, 0, &fat);
342 	FREE(&Source);
343 	FREE(&Target);
344 	if(ret < 0 ){
345 		fat_free(arg->mp.targetDir, fat);
346 		return -1;
347 	} else {
348 		mk_entry(dosname, arg->attr, fat, (uint32_t)ret,
349 				 now, &entry->dir);
350 		return 0;
351 	}
352 }
353 
354 
355 
dos_write(direntry_t * entry,MainParam_t * mp,int needfilter)356 static int dos_write(direntry_t *entry, MainParam_t *mp, int needfilter)
357 /* write a messy dos file to another messy dos file */
358 {
359 	int result;
360 	Arg_t * arg = (Arg_t *) (mp->arg);
361 	const char *targetName = mpPickTargetName(mp);
362 
363 	if(entry && arg->preserveAttributes)
364 		arg->attr = entry->dir.attr;
365 	else
366 		arg->attr = ATTR_ARCHIVE;
367 
368 	arg->needfilter = needfilter;
369 	if (entry && mp->targetDir == entry->Dir){
370 		arg->ch.ignore_entry = -1;
371 		arg->ch.source = entry->entry;
372 	} else {
373 		arg->ch.ignore_entry = -1;
374 		arg->ch.source = -2;
375 	}
376 	result = mwrite_one(mp->targetDir, targetName, 0,
377 			    writeit, (void *)arg, &arg->ch);
378 	if(result == 1)
379 		return GOT_ONE;
380 	else
381 		return ERROR_ONE;
382 }
383 
subDir(Stream_t * parent,const char * filename)384 static Stream_t *subDir(Stream_t *parent, const char *filename)
385 {
386 	direntry_t entry;
387 	initializeDirentry(&entry, parent);
388 
389 	switch(vfat_lookup_zt(&entry, filename, ACCEPT_DIR, 0, 0, 0, 0)) {
390 	    case 0:
391 		return OpenFileByDirentry(&entry);
392 	    case -1:
393 		return NULL;
394 	    default: /* IO Error */
395 		return NULL;
396 	}
397 }
398 
dos_copydir(direntry_t * entry,MainParam_t * mp)399 static int dos_copydir(direntry_t *entry, MainParam_t *mp)
400 /* copyes a directory to Dos */
401 {
402 	Arg_t * arg = (Arg_t *) (mp->arg);
403 	Arg_t newArg;
404 	time_t now;
405 	time_t date;
406 	int ret;
407 	const char *targetName = mpPickTargetName(mp);
408 
409 	if (!arg->recursive && mp->basenameHasWildcard)
410 		return 0;
411 
412 	if(entry && isSubdirOf(mp->targetDir, mp->File)) {
413 		fprintf(stderr, "Cannot recursively copy directory ");
414 		fprintPwd(stderr, entry,0);
415 		fprintf(stderr, " into one of its own subdirectories ");
416 		fprintPwd(stderr, getDirentry(mp->targetDir),0);
417 		fprintf(stderr, "\n");
418 		return ERROR_ONE;
419 	}
420 
421 	if (arg->mp.File->Class->get_data(arg->mp.File,
422 					  & date, 0, 0, 0) < 0 ){
423 		fprintf(stderr, "Can't stat source file\n");
424 		return ERROR_ONE;
425 	}
426 
427 	if(!arg->type && arg->verbose)
428 		fprintf(stderr,"Copying %s\n", mpGetBasename(mp));
429 
430 	if(entry && arg->preserveAttributes)
431 		arg->attr = entry->dir.attr;
432 	else
433 		arg->attr = 0;
434 
435 	if (entry && (mp->targetDir == entry->Dir)){
436 		arg->ch.ignore_entry = -1;
437 		arg->ch.source = entry->entry;
438 	} else {
439 		arg->ch.ignore_entry = -1;
440 		arg->ch.source = -2;
441 	}
442 
443 	/* preserve mod time? */
444 	if (arg->preserveTime)
445 		now = date;
446 	else
447 		getTimeNow(&now);
448 
449 	newArg = *arg;
450 	newArg.mp.arg = &newArg;
451 	newArg.mp.targetName = 0;
452 	newArg.mp.basenameHasWildcard = 1;
453 	if(*targetName) {
454 		/* maybe the directory already exist. Use it */
455 		newArg.mp.targetDir = subDir(mp->targetDir, targetName);
456 		if(!newArg.mp.targetDir)
457 			newArg.mp.targetDir = createDir(mp->targetDir,
458 							targetName,
459 							&arg->ch, arg->attr,
460 							now);
461 	} else
462 		newArg.mp.targetDir = mp->targetDir;
463 
464 	if(!newArg.mp.targetDir)
465 		return ERROR_ONE;
466 
467 	ret = mp->loop(mp->File, &newArg.mp, "*");
468 	if(*targetName)
469 		FREE(&newArg.mp.targetDir);
470 	return ret | GOT_ONE;
471 }
472 
473 
dos_to_dos(direntry_t * entry,MainParam_t * mp)474 static int dos_to_dos(direntry_t *entry, MainParam_t *mp)
475 {
476 	return dos_write(entry, mp, 0);
477 }
478 
unix_to_dos(MainParam_t * mp)479 static int unix_to_dos(MainParam_t *mp)
480 {
481 	return dos_write(0, mp, 1);
482 }
483 
484 static void usage(int ret) NORETURN;
usage(int ret)485 static void usage(int ret)
486 {
487 	fprintf(stderr,
488 		"Mtools version %s, dated %s\n", mversion, mdate);
489 	fprintf(stderr,
490 		"Usage: %s [-spatnmQVBT] [-D clash_option] sourcefile targetfile\n", progname);
491 	fprintf(stderr,
492 		"       %s [-spatnmQVBT] [-D clash_option] sourcefile [sourcefiles...] targetdirectory\n",
493 		progname);
494 	exit(ret);
495 }
496 
497 void mcopy(int argc, char **argv, int mtype) NORETURN;
mcopy(int argc,char ** argv,int mtype)498 void mcopy(int argc, char **argv, int mtype)
499 {
500 	Arg_t arg;
501 	int c, fastquit;
502 
503 
504 	/* get command line options */
505 
506 	init_clash_handling(& arg.ch);
507 
508 	/* get command line options */
509 	arg.recursive = 0;
510 	arg.preserveTime = 0;
511 	arg.preserveAttributes = 0;
512 	arg.nowarn = 0;
513 	arg.textmode = 0;
514 	arg.verbose = 0;
515 	arg.convertCharset = 0;
516 	arg.type = mtype;
517 	fastquit = 0;
518 	if(helpFlag(argc, argv))
519 		usage(0);
520 	while ((c = getopt(argc, argv, "i:abB/sptTnmvQD:oh")) != EOF) {
521 		switch (c) {
522 			case 'i':
523 				set_cmd_line_image(optarg);
524 				break;
525 			case 's':
526 			case '/':
527 				arg.recursive = 1;
528 				break;
529 			case 'p':
530 				arg.preserveAttributes = 1;
531 				break;
532 			case 'T':
533 				arg.convertCharset = 1;
534 				 /*-fallthrough*/
535 			case 'a':
536 			case 't':
537 				arg.textmode = 1;
538 				break;
539 			case 'n':
540 				arg.nowarn = 1;
541 				break;
542 			case 'm':
543 				arg.preserveTime = 1;
544 				break;
545 			case 'v':
546 				arg.verbose = 1;
547 				break;
548 			case 'Q':
549 				fastquit = 1;
550 				break;
551 			case 'B':
552 			case 'b':
553 				batchmode = 1;
554 				break;
555 			case 'o':
556 				handle_clash_options(&arg.ch, (char) c);
557 				break;
558 			case 'D':
559 				if(handle_clash_options(&arg.ch, *optarg))
560 					usage(1);
561 				break;
562 			case 'h':
563 				usage(0);
564 			case '?':
565 				usage(1);
566 			default:
567 				break;
568 
569 		}
570 	}
571 
572 	if (argc - optind < 1)
573 		usage(1);
574 
575 	init_mp(&arg.mp);
576 	arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN | NO_DOTS;
577 	arg.mp.fast_quit = fastquit;
578 	arg.mp.arg = (void *) &arg;
579 	arg.mp.openflags = O_RDONLY;
580 	arg.noClobber = 0;
581 
582 	/* last parameter is "-", use mtype mode */
583 	if(!mtype && !strcmp(argv[argc-1], "-")) {
584 		arg.type = mtype = 1;
585 		argc--;
586 	}
587 
588 	if(mtype){
589 		/* Mtype = copying to stdout */
590 		arg.mp.targetName = strdup("-");
591 		arg.mp.unixTarget = strdup("");
592 		arg.mp.callback = dos_to_unix;
593 		arg.mp.dirCallback = unix_copydir;
594 		arg.mp.unixcallback = unix_to_unix;
595 	} else {
596 		const char *target;
597 		if (argc - optind == 1) {
598 			/* copying to the current directory */
599 			target = ".";
600 			arg.noClobber = 1;
601 		} else {
602 			/* target is the last item mentioned */
603 			argc--;
604 			target = argv[argc];
605 		}
606 
607 		target_lookup(&arg.mp, target);
608 		if(!arg.mp.targetDir && !arg.mp.unixTarget) {
609 			fprintf(stderr,"Bad target %s\n", target);
610 			exit(1);
611 		}
612 
613 		/* callback functions */
614 		if(arg.mp.unixTarget) {
615 			arg.mp.callback = dos_to_unix;
616 			arg.mp.dirCallback = directory_dos_to_unix;
617 			arg.mp.unixcallback = unix_to_unix;
618 		} else {
619 			arg.mp.dirCallback = dos_copydir;
620 			arg.mp.callback = dos_to_dos;
621 			arg.mp.unixcallback = unix_to_dos;
622 		}
623 	}
624 
625 	exit(main_loop(&arg.mp, argv + optind, argc - optind));
626 }
627