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