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