1 /* 2 * Copyright 2005 The Android Open Source Project 3 * 4 * Android "cp" replacement. 5 * 6 * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead 7 * of utime(), and getxattr()/setxattr() instead of chmod(). These are 8 * probably "better", but are non-portable, and not necessary for our 9 * purposes. 10 */ 11 #include <host/CopyFile.h> 12 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <unistd.h> 17 #include <sys/types.h> 18 #include <sys/stat.h> 19 #include <getopt.h> 20 #include <dirent.h> 21 #include <fcntl.h> 22 #include <utime.h> 23 #include <limits.h> 24 #include <errno.h> 25 #include <assert.h> 26 27 #ifdef HAVE_MS_C_RUNTIME 28 # define mkdir(path,mode) _mkdir(path) 29 #endif 30 31 #ifndef HAVE_SYMLINKS 32 # define lstat stat 33 # ifndef EACCESS /* seems to be missing from the Mingw headers */ 34 # define EACCESS 13 35 # endif 36 #endif 37 38 #ifndef O_BINARY 39 # define O_BINARY 0 40 #endif 41 42 /*#define DEBUG_MSGS*/ 43 #ifdef DEBUG_MSGS 44 # define DBUG(x) printf x 45 #else 46 # define DBUG(x) ((void)0) 47 #endif 48 49 #define FSSEP '/' /* filename separator char */ 50 51 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options); 52 53 /* 54 * Returns true if the source file is newer than the destination file. 55 * 56 * The check is based on the modification date, whole seconds only. This 57 * also returns true if the file sizes don't match. 58 */ 59 static bool isSourceNewer(const struct stat* pSrcStat, const struct stat* pDstStat) 60 { 61 return (pSrcStat->st_mtime > pDstStat->st_mtime) || 62 (pSrcStat->st_size != pDstStat->st_size); 63 } 64 65 /* 66 * Returns true if the source file has high resolution modification 67 * date. Cygwin/Mingw doesn't support st_mtim and always returns false. 68 */ 69 static bool isHiresMtime(const struct stat* pSrcStat) 70 { 71 #if HAVE_STAT_ST_MTIM 72 #if defined(MACOSX_RSRC) 73 return pSrcStat->st_mtimespec.tv_nsec > 0; 74 #else 75 return pSrcStat->st_mtim.tv_nsec > 0; 76 #endif 77 #else 78 return 0; 79 #endif 80 } 81 82 /* 83 * Returns true if the source and destination files are actually the 84 * same thing. We detect this by checking the inode numbers, which seems 85 * to work on Cygwin. 86 */ 87 static bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat) 88 { 89 #ifndef HAVE_VALID_STAT_ST_INO 90 /* with MSVCRT.DLL, stat always sets st_ino to 0, and there is no simple way to */ 91 /* get the equivalent information with Win32 (Cygwin does some weird stuff in */ 92 /* its winsup/cygwin/fhandler_disk_file.cc to emulate this, too complex for us) */ 93 return 0; 94 #else 95 return (pSrcStat->st_ino == pDstStat->st_ino); 96 #endif 97 } 98 99 static void printCopyMsg(const char* src, const char* dst, unsigned int options) 100 { 101 if ((options & COPY_VERBOSE_MASK) > 0) 102 printf(" '%s' --> '%s'\n", src, dst); 103 } 104 105 static void printNotNewerMsg(const char* src, const char* dst, unsigned int options) 106 { 107 if ((options & COPY_VERBOSE_MASK) > 1) 108 printf(" '%s' is up-to-date\n", dst); 109 } 110 111 /* 112 * Copy the contents of one file to another. 113 * 114 * The files are assumed to be seeked to the start. 115 */ 116 static int copyFileContents(const char* dst, int dstFd, const char* src, int srcFd) 117 { 118 unsigned char buf[8192]; 119 ssize_t readCount, writeCount; 120 121 /* 122 * Read a chunk, write it, and repeat. 123 */ 124 while (1) { 125 readCount = read(srcFd, buf, sizeof(buf)); 126 if (readCount < 0) { 127 fprintf(stderr, 128 "acp: failed reading '%s': %s\n", src, strerror(errno)); 129 return -1; 130 } 131 132 if (readCount > 0) { 133 writeCount = write(dstFd, buf, readCount); 134 if (writeCount < 0) { 135 fprintf(stderr, 136 "acp: failed writing '%s': %s\n", dst, strerror(errno)); 137 return -1; 138 } 139 if (writeCount != readCount) { 140 fprintf(stderr, "acp: partial write to '%s' (%zd of %zd)\n", 141 dst, writeCount, readCount); 142 return -1; 143 } 144 } 145 146 if (readCount < (ssize_t) sizeof(buf)) 147 break; 148 } 149 150 return 0; 151 } 152 153 /* 154 * Set the permissions, owner, and timestamps on the destination file 155 * equal to those of the source file. 156 * 157 * Failures here are "soft"; they don't produce warning messages and don't 158 * cause the cp command to report a failure. 159 */ 160 static int setPermissions(const char* dst, const struct stat* pSrcStat, unsigned int options) 161 { 162 struct utimbuf ut; 163 164 if (options & COPY_TIMESTAMPS) { 165 /* 166 * Start with timestamps. The access and mod dates are not affected 167 * by the next operations. 168 */ 169 ut.actime = pSrcStat->st_atime; 170 ut.modtime = pSrcStat->st_mtime; 171 if (isHiresMtime(pSrcStat)) 172 ut.modtime += 1; 173 if (utime(dst, &ut) != 0) { 174 DBUG(("--- unable to set timestamps on '%s': %s\n", 175 dst, strerror(errno))); 176 } 177 } 178 179 if (options & COPY_PERMISSIONS) { 180 /* 181 * Set the permissions. 182 */ 183 if (chmod(dst, pSrcStat->st_mode & ~(S_IFMT)) != 0) { 184 DBUG(("--- unable to set perms on '%s' to 0%o: %s\n", 185 dst, pSrcStat->st_mode & ~(S_IFMT), strerror(errno))); 186 } 187 #ifndef HAVE_MS_C_RUNTIME 188 /* 189 * Set the owner. 190 */ 191 if (chown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) { 192 DBUG(("--- unable to set owner of '%s' to %d/%d: %s\n", 193 dst, pSrcStat->st_uid, pSrcStat->st_gid, strerror(errno))); 194 } 195 #endif 196 } 197 198 return 0; 199 } 200 201 /* 202 * Copy a regular file. If the destination file exists and is not a 203 * regular file, we fail. However, we use stat() rather than lstat(), 204 * because it's okay to write through a symlink (the noDereference stuff 205 * only applies to the source file). 206 * 207 * If the file doesn't exist, create it. If it does exist, truncate it. 208 */ 209 static int copyRegular(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 210 { 211 struct stat dstStat; 212 int srcFd, dstFd, statResult, copyResult; 213 214 DBUG(("--- copying regular '%s' to '%s'\n", src, dst)); 215 216 statResult = stat(dst, &dstStat); 217 if (statResult == 0 && !S_ISREG(dstStat.st_mode)) { 218 fprintf(stderr, 219 "acp: destination '%s' exists and is not regular file\n", 220 dst); 221 return -1; 222 } else if (statResult != 0 && errno != ENOENT) { 223 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 224 return -1; 225 } 226 227 if (statResult == 0) { 228 if (isSameFile(pSrcStat, &dstStat)) { 229 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 230 src, dst); 231 return -1; 232 } 233 if (options & COPY_UPDATE_ONLY) { 234 if (!isSourceNewer(pSrcStat, &dstStat)) { 235 DBUG(("--- source is not newer: '%s'\n", src)); 236 printNotNewerMsg(src, dst, options); 237 return 0; 238 } 239 } 240 } 241 242 /* open src */ 243 srcFd = open(src, O_RDONLY | O_BINARY, 0); 244 if (srcFd < 0) { 245 fprintf(stderr, "acp: unable to open '%s': %s\n", src, strerror(errno)); 246 return -1; 247 } 248 249 /* open dest with O_CREAT | O_TRUNC */ 250 DBUG(("--- opening '%s'\n", dst)); 251 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); 252 253 if (dstFd < 0) { 254 if (errno == ENOENT) { 255 /* this happens if the target directory doesn't exist */ 256 fprintf(stderr, 257 "acp: cannot create '%s': %s\n", dst, strerror(errno)); 258 (void) close(srcFd); 259 return -1; 260 } 261 262 /* if "force" is set, try removing the destination file and retry */ 263 if (options & COPY_FORCE) { 264 if (unlink(dst) != 0) { 265 #ifdef HAVE_MS_C_RUNTIME 266 /* MSVCRT.DLL unlink will fail with EACCESS if the file is set read-only */ 267 /* so try to change its mode, and unlink again */ 268 if (errno == EACCESS) { 269 if (chmod(dst, S_IWRITE|S_IREAD) == 0 && unlink(dst) == 0) 270 goto Open_File; 271 } 272 #endif 273 fprintf(stderr, "acp: unable to remove '%s': %s\n", 274 dst, strerror(errno)); 275 (void) close(srcFd); 276 return -1; 277 } 278 #ifdef HAVE_MS_C_RUNTIME 279 Open_File: 280 #endif 281 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); 282 } 283 } 284 if (dstFd < 0) { 285 fprintf(stderr, "acp: unable to open '%s': %s\n", 286 dst, strerror(errno)); 287 (void) close(srcFd); 288 return -1; 289 } 290 291 copyResult = copyFileContents(dst, dstFd, src, srcFd); 292 293 (void) close(srcFd); 294 (void) close(dstFd); 295 if (copyResult != 0) 296 return -1; 297 298 #ifdef MACOSX_RSRC 299 { 300 char* srcRsrcName = NULL; 301 char* dstRsrcName = NULL; 302 struct stat rsrcStat; 303 304 srcRsrcName = malloc(strlen(src) + 5 + 1); 305 strcpy(srcRsrcName, src); 306 strcat(srcRsrcName, "/rsrc"); 307 308 dstRsrcName = malloc(strlen(dst) + 5 + 1); 309 strcpy(dstRsrcName, dst); 310 strcat(dstRsrcName, "/rsrc"); 311 312 if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) { 313 DBUG(("--- RSRC: %s --> %s\n", srcRsrcName, dstRsrcName)); 314 315 srcFd = open(srcRsrcName, O_RDONLY); 316 dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0); 317 copyResult = -1; 318 if (srcFd >= 0 && dstFd >= 0) { 319 copyResult = copyFileContents(dstRsrcName, dstFd, 320 srcRsrcName, srcFd); 321 (void) close(srcFd); 322 (void) close(dstFd); 323 } 324 325 if (copyResult != 0) 326 return -1; 327 } 328 329 free(srcRsrcName); 330 free(dstRsrcName); 331 } 332 #endif 333 334 setPermissions(dst, pSrcStat, options); 335 336 printCopyMsg(src, dst, options); 337 338 return 0; 339 } 340 341 342 #ifdef HAVE_SYMLINKS 343 /* 344 * Copy a symlink. This only happens if we're in "no derefence" mode, 345 * in which we copy the links rather than the files that are pointed at. 346 * 347 * We always discard the destination file. If it's a symlink already, 348 * we want to throw it out and replace it. If it's not a symlink, we 349 * need to trash it so we can create one. 350 */ 351 static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 352 { 353 struct stat dstStat; 354 char linkBuf[PATH_MAX+1]; 355 int statResult, nameLen; 356 357 assert(options & COPY_NO_DEREFERENCE); 358 DBUG(("--- copying symlink '%s' to '%s'\n", src, dst)); 359 360 /* NOTE: we use lstat() here */ 361 statResult = lstat(dst, &dstStat); 362 if (statResult == 0 && !S_ISREG(dstStat.st_mode) 363 && !S_ISLNK(dstStat.st_mode) 364 ) 365 { 366 fprintf(stderr, 367 "acp: destination '%s' exists and is not regular or symlink\n", 368 dst); 369 return -1; 370 } 371 372 if (statResult == 0) { 373 if (isSameFile(pSrcStat, &dstStat)) { 374 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 375 src, dst); 376 return -1; 377 } 378 if (options & COPY_UPDATE_ONLY) { 379 if (!isSourceNewer(pSrcStat, &dstStat)) { 380 DBUG(("--- source is not newer: '%s'\n", src)); 381 printNotNewerMsg(src, dst, options); 382 return 0; 383 } 384 } 385 } 386 387 /* extract the symlink contents */ 388 nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1); 389 if (nameLen <= 0) { 390 fprintf(stderr, "acp: unable to read symlink '%s': %s\n", 391 src, strerror(errno)); 392 return -1; 393 } 394 linkBuf[nameLen] = '\0'; 395 DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf)); 396 397 if (statResult == 0) { 398 DBUG(("--- removing '%s'\n", dst)); 399 if (unlink(dst) != 0) { 400 fprintf(stderr, "acp: unable to remove '%s': %s\n", 401 dst, strerror(errno)); 402 return -1; 403 } 404 } 405 406 if (symlink(linkBuf, dst) != 0) { 407 fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n", 408 dst, linkBuf, strerror(errno)); 409 return -1; 410 } 411 412 /* 413 * There's no way to set the file date or access permissions, but 414 * it is possible to set the owner. 415 */ 416 if (options & COPY_PERMISSIONS) { 417 if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) 418 DBUG(("--- lchown failed: %s\n", strerror(errno))); 419 } 420 421 printCopyMsg(src, dst, options); 422 423 return 0; 424 } 425 #endif /* HAVE_SYMLINKS */ 426 427 /* 428 * Copy the contents of one directory to another. Both "src" and "dst" 429 * must be directories. We will create "dst" if it does not exist. 430 */ 431 int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 432 { 433 int retVal = 0; 434 struct stat dstStat; 435 DIR* dir; 436 int cc, statResult; 437 438 DBUG(("--- copy dir '%s' to '%s'\n", src, dst)); 439 440 statResult = stat(dst, &dstStat); 441 if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) { 442 fprintf(stderr, 443 "acp: destination '%s' exists and is not a directory\n", dst); 444 return -1; 445 } else if (statResult != 0 && errno != ENOENT) { 446 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 447 return -1; 448 } 449 450 if (statResult == 0) { 451 if (isSameFile(pSrcStat, &dstStat)) { 452 fprintf(stderr, 453 "acp: cannot copy directory into itself ('%s' and '%s')\n", 454 src, dst); 455 return -1; 456 } 457 } else { 458 DBUG(("--- creating dir '%s'\n", dst)); 459 cc = mkdir(dst, 0755); 460 if (cc != 0) { 461 fprintf(stderr, "acp: unable to create directory '%s': %s\n", 462 dst, strerror(errno)); 463 return -1; 464 } 465 466 /* only print on mkdir */ 467 printCopyMsg(src, dst, options); 468 } 469 470 /* 471 * Open the directory, and plow through its contents. 472 */ 473 dir = opendir(src); 474 if (dir == NULL) { 475 fprintf(stderr, "acp: unable to open directory '%s': %s\n", 476 src, strerror(errno)); 477 return -1; 478 } 479 480 while (1) { 481 struct dirent* ent; 482 char* srcFile; 483 char* dstFile; 484 int srcLen, dstLen, nameLen; 485 486 ent = readdir(dir); 487 if (ent == NULL) 488 break; 489 490 if (strcmp(ent->d_name, ".") == 0 || 491 strcmp(ent->d_name, "..") == 0) 492 { 493 continue; 494 } 495 496 nameLen = strlen(ent->d_name); 497 srcLen = strlen(src); 498 dstLen = strlen(dst); 499 500 srcFile = malloc(srcLen +1 + nameLen +1); 501 memcpy(srcFile, src, srcLen); 502 srcFile[srcLen] = FSSEP; 503 memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1); 504 505 dstFile = malloc(dstLen +1 + nameLen +1); 506 memcpy(dstFile, dst, dstLen); 507 dstFile[dstLen] = FSSEP; 508 memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1); 509 510 if (copyFileRecursive(srcFile, dstFile, false, options) != 0) 511 retVal = -1; /* note failure and keep going */ 512 513 free(srcFile); 514 free(dstFile); 515 } 516 closedir(dir); 517 518 setPermissions(dst, pSrcStat, options); 519 520 return retVal; 521 } 522 523 /* 524 * Do the actual copy. This is called recursively from copyDirectory(). 525 * 526 * "dst" should only be a directory if "src" is also a directory. 527 * 528 * Returns 0 on success. 529 */ 530 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options) 531 { 532 char* srcExe = NULL; 533 char* dstExe = NULL; 534 char* dstDir = NULL; 535 struct stat srcStat; 536 int retVal = 0; 537 int statResult, statErrno; 538 539 /* 540 * Stat the source file. If it doesn't exist, fail. 541 */ 542 if (options & COPY_NO_DEREFERENCE) 543 statResult = lstat(src, &srcStat); 544 else 545 statResult = stat(src, &srcStat); 546 statErrno = errno; /* preserve across .exe attempt */ 547 548 #ifdef WIN32_EXE 549 /* 550 * Here's the interesting part. Under Cygwin, if you have a file 551 * called "foo.exe", stat("foo", ...) will succeed, but open("foo", ...) 552 * will fail. We need to figure out what its name is supposed to be 553 * so we can create the correct destination file. 554 * 555 * If we don't have the "-e" flag set, we want "acp foo bar" to fail, 556 * not automatically find "foo.exe". That way, if we really were 557 * trying to copy "foo", it doesn't grab something we don't want. 558 */ 559 if (isCmdLine && statResult == 0) { 560 int tmpFd; 561 tmpFd = open(src, O_RDONLY | O_BINARY, 0); 562 if (tmpFd < 0) { 563 statResult = -1; 564 statErrno = ENOENT; 565 } else { 566 (void) close(tmpFd); 567 } 568 } 569 570 /* 571 * If we didn't find the file, try it again with ".exe". 572 */ 573 if (isCmdLine && statResult < 0 && statErrno == ENOENT && (options & COPY_TRY_EXE)) { 574 srcExe = malloc(strlen(src) + 4 +1); 575 strcpy(srcExe, src); 576 strcat(srcExe, ".exe"); 577 578 if (options & COPY_NO_DEREFERENCE) 579 statResult = lstat(srcExe, &srcStat); 580 else 581 statResult = stat(srcExe, &srcStat); 582 583 if (statResult == 0 && !S_ISREG(srcStat.st_mode)) 584 statResult = -1; /* fail, use original statErrno below */ 585 586 if (statResult == 0) { 587 /* found a .exe, copy that instead */ 588 dstExe = malloc(strlen(dst) + 4 +1); 589 strcpy(dstExe, dst); 590 strcat(dstExe, ".exe"); 591 592 src = srcExe; 593 dst = dstExe; 594 } else { 595 DBUG(("--- couldn't find '%s' either\n", srcExe)); 596 } 597 } 598 #endif 599 if (statResult < 0) { 600 if (statErrno == ENOENT) 601 fprintf(stderr, "acp: file '%s' does not exist\n", src); 602 else 603 fprintf(stderr, "acp: unable to stat '%s': %s\n", 604 src, strerror(statErrno)); 605 retVal = -1; 606 goto bail; 607 } 608 609 /* 610 * If "src" is a directory, ignore it if "recursive" isn't set. 611 * 612 * We want to create "dst" as a directory (or verify that it already 613 * exists as a directory), and then copy its contents. 614 */ 615 if (S_ISDIR(srcStat.st_mode)) { 616 if (!(options & COPY_RECURSIVE)) { 617 fprintf(stderr, "acp: omitting directory '%s'\n", src); 618 } else { 619 retVal = copyDirectory(src, dst, &srcStat, options); 620 } 621 #ifdef HAVE_SYMLINKS 622 } else if (S_ISLNK(srcStat.st_mode)) { 623 retVal = copySymlink(src, dst, &srcStat, options); 624 #endif 625 } else if (S_ISREG(srcStat.st_mode)) { 626 retVal = copyRegular(src, dst, &srcStat, options); 627 } else { 628 fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n", 629 src, srcStat.st_mode); 630 retVal = -1; 631 } 632 633 bail: 634 free(srcExe); 635 free(dstExe); 636 free(dstDir); 637 return retVal; 638 } 639 640 int copyFile(const char* src, const char* dst, unsigned int options) 641 { 642 return copyFileRecursive(src, dst, true, options); 643 } 644 645 646