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