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 41static 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 64typedef 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 81static int _unix_write(direntry_t *entry, MainParam_t *mp, int needfilter, 82 const char *unixFile); 83 84/* Write the Unix file */ 85static 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 */ 106static 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 199static 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 */ 219static 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 268static int dos_to_unix(direntry_t *entry, MainParam_t *mp) 269{ 270 return unix_write(entry, mp, 1); 271} 272 273 274static int unix_to_unix(MainParam_t *mp) 275{ 276 return unix_write(0, mp, 0); 277} 278 279 280static 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 */ 289static 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 366static 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 394static 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 409static 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 484static int dos_to_dos(direntry_t *entry, MainParam_t *mp) 485{ 486 return dos_write(entry, mp, 0); 487} 488 489static int unix_to_dos(MainParam_t *mp) 490{ 491 return dos_write(0, mp, 1); 492} 493 494static void usage(int ret) NORETURN; 495static 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 507void 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} 636