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