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