CopyFile.c revision 6c124d217fb18929842e75db608fcc813b19ba44
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#if defined(_WIN32) 28# define mkdir(path,mode) _mkdir(path) 29# define S_ISLNK(s) 0 30# define lstat stat 31# ifndef EACCESS /* seems to be missing from the Mingw headers */ 32# define EACCESS 13 33# endif 34#endif 35 36#ifndef O_BINARY 37# define O_BINARY 0 38#endif 39 40/*#define DEBUG_MSGS*/ 41#ifdef DEBUG_MSGS 42# define DBUG(x) printf x 43#else 44# define DBUG(x) ((void)0) 45#endif 46 47#define FSSEP '/' /* filename separator char */ 48 49static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options); 50 51/* 52 * Returns true if the source file is newer than the destination file. 53 * 54 * The check is based on the modification date, whole seconds only. This 55 * also returns true if the file sizes don't match. 56 */ 57static bool isSourceNewer(const struct stat* pSrcStat, const struct stat* pDstStat) 58{ 59 return (pSrcStat->st_mtime > pDstStat->st_mtime) || 60 (pSrcStat->st_size != pDstStat->st_size); 61} 62 63/* 64 * Returns true if the source file has high resolution modification 65 * date. Cygwin/Mingw doesn't support st_mtim and always returns false. 66 */ 67static bool isHiresMtime(const struct stat* pSrcStat) 68{ 69#if defined(__CYGWIN__) || defined(__MINGW32__) 70 return 0; 71#elif defined(MACOSX_RSRC) 72 return pSrcStat->st_mtimespec.tv_nsec > 0; 73#else 74 return pSrcStat->st_mtim.tv_nsec > 0; 75#endif 76} 77 78/* 79 * Returns true if the source and destination files are actually the 80 * same thing. We detect this by checking the inode numbers, which seems 81 * to work on Cygwin. 82 */ 83static bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat) 84{ 85#ifndef HAVE_VALID_STAT_ST_INO 86 /* with MSVCRT.DLL, stat always sets st_ino to 0, and there is no simple way to */ 87 /* get the equivalent information with Win32 (Cygwin does some weird stuff in */ 88 /* its winsup/cygwin/fhandler_disk_file.cc to emulate this, too complex for us) */ 89 return 0; 90#else 91 return (pSrcStat->st_ino == pDstStat->st_ino); 92#endif 93} 94 95static void printCopyMsg(const char* src, const char* dst, unsigned int options) 96{ 97 if ((options & COPY_VERBOSE_MASK) > 0) 98 printf(" '%s' --> '%s'\n", src, dst); 99} 100 101static void printNotNewerMsg(const char* src, const char* dst, unsigned int options) 102{ 103 if ((options & COPY_VERBOSE_MASK) > 1) 104 printf(" '%s' is up-to-date\n", dst); 105} 106 107/* 108 * Copy the contents of one file to another. 109 * 110 * The files are assumed to be seeked to the start. 111 */ 112static int copyFileContents(const char* dst, int dstFd, const char* src, int srcFd) 113{ 114 unsigned char buf[8192]; 115 ssize_t readCount, writeCount; 116 117 /* 118 * Read a chunk, write it, and repeat. 119 */ 120 while (1) { 121 readCount = read(srcFd, buf, sizeof(buf)); 122 if (readCount < 0) { 123 fprintf(stderr, 124 "acp: failed reading '%s': %s\n", src, strerror(errno)); 125 return -1; 126 } 127 128 if (readCount > 0) { 129 writeCount = write(dstFd, buf, readCount); 130 if (writeCount < 0) { 131 fprintf(stderr, 132 "acp: failed writing '%s': %s\n", dst, strerror(errno)); 133 return -1; 134 } 135 if (writeCount != readCount) { 136 fprintf(stderr, "acp: partial write to '%s' (%zd of %zd)\n", 137 dst, writeCount, readCount); 138 return -1; 139 } 140 } 141 142 if (readCount < (ssize_t) sizeof(buf)) 143 break; 144 } 145 146 return 0; 147} 148 149/* 150 * Set the permissions, owner, and timestamps on the destination file 151 * equal to those of the source file. 152 * 153 * Failures here are "soft"; they don't produce warning messages and don't 154 * cause the cp command to report a failure. 155 */ 156static int setPermissions(const char* dst, const struct stat* pSrcStat, unsigned int options) 157{ 158 struct utimbuf ut; 159 160 if (options & COPY_TIMESTAMPS) { 161 /* 162 * Start with timestamps. The access and mod dates are not affected 163 * by the next operations. 164 */ 165 ut.actime = pSrcStat->st_atime; 166 ut.modtime = pSrcStat->st_mtime; 167 if (isHiresMtime(pSrcStat)) 168 ut.modtime += 1; 169 if (utime(dst, &ut) != 0) { 170 DBUG(("--- unable to set timestamps on '%s': %s\n", 171 dst, strerror(errno))); 172 } 173 } 174 175 if (options & COPY_PERMISSIONS) { 176 /* 177 * Set the permissions. 178 */ 179 if (chmod(dst, pSrcStat->st_mode & ~(S_IFMT)) != 0) { 180 DBUG(("--- unable to set perms on '%s' to 0%o: %s\n", 181 dst, pSrcStat->st_mode & ~(S_IFMT), strerror(errno))); 182 } 183#ifndef _WIN32 184 /* 185 * Set the owner. 186 */ 187 if (chown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) { 188 DBUG(("--- unable to set owner of '%s' to %d/%d: %s\n", 189 dst, pSrcStat->st_uid, pSrcStat->st_gid, strerror(errno))); 190 } 191#endif 192 } 193 194 return 0; 195} 196 197/* 198 * Copy a regular file. If the destination file exists and is not a 199 * regular file, we fail. However, we use stat() rather than lstat(), 200 * because it's okay to write through a symlink (the noDereference stuff 201 * only applies to the source file). 202 * 203 * If the file doesn't exist, create it. If it does exist, truncate it. 204 */ 205static int copyRegular(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 206{ 207 struct stat dstStat; 208 int srcFd, dstFd, statResult, copyResult; 209 210 DBUG(("--- copying regular '%s' to '%s'\n", src, dst)); 211 212 statResult = stat(dst, &dstStat); 213 if (statResult == 0 && !S_ISREG(dstStat.st_mode)) { 214 fprintf(stderr, 215 "acp: destination '%s' exists and is not regular file\n", 216 dst); 217 return -1; 218 } else if (statResult != 0 && errno != ENOENT) { 219 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 220 return -1; 221 } 222 223 if (statResult == 0) { 224 if (isSameFile(pSrcStat, &dstStat)) { 225 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 226 src, dst); 227 return -1; 228 } 229 if (options & COPY_UPDATE_ONLY) { 230 if (!isSourceNewer(pSrcStat, &dstStat)) { 231 DBUG(("--- source is not newer: '%s'\n", src)); 232 printNotNewerMsg(src, dst, options); 233 return 0; 234 } 235 } 236 } 237 238 /* open src */ 239 srcFd = open(src, O_RDONLY | O_BINARY, 0); 240 if (srcFd < 0) { 241 fprintf(stderr, "acp: unable to open '%s': %s\n", src, strerror(errno)); 242 return -1; 243 } 244 245 /* open dest with O_CREAT | O_TRUNC */ 246 DBUG(("--- opening '%s'\n", dst)); 247 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); 248 249 if (dstFd < 0) { 250 if (errno == ENOENT) { 251 /* this happens if the target directory doesn't exist */ 252 fprintf(stderr, 253 "acp: cannot create '%s': %s\n", dst, strerror(errno)); 254 (void) close(srcFd); 255 return -1; 256 } 257 258 /* if "force" is set, try removing the destination file and retry */ 259 if (options & COPY_FORCE) { 260 if (unlink(dst) != 0) { 261#ifdef _WIN32 262 /* MSVCRT.DLL unlink will fail with EACCESS if the file is set read-only */ 263 /* so try to change its mode, and unlink again */ 264 if (errno == EACCESS) { 265 if (chmod(dst, S_IWRITE|S_IREAD) == 0 && unlink(dst) == 0) 266 goto Open_File; 267 } 268#endif 269 fprintf(stderr, "acp: unable to remove '%s': %s\n", 270 dst, strerror(errno)); 271 (void) close(srcFd); 272 return -1; 273 } 274#ifdef _WIN32 275 Open_File: 276#endif 277 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); 278 } 279 } 280 if (dstFd < 0) { 281 fprintf(stderr, "acp: unable to open '%s': %s\n", 282 dst, strerror(errno)); 283 (void) close(srcFd); 284 return -1; 285 } 286 287 copyResult = copyFileContents(dst, dstFd, src, srcFd); 288 289 (void) close(srcFd); 290 (void) close(dstFd); 291 if (copyResult != 0) 292 return -1; 293 294#ifdef MACOSX_RSRC 295 { 296 char* srcRsrcName = NULL; 297 char* dstRsrcName = NULL; 298 struct stat rsrcStat; 299 300 srcRsrcName = malloc(strlen(src) + 5 + 1); 301 strcpy(srcRsrcName, src); 302 strcat(srcRsrcName, "/rsrc"); 303 304 dstRsrcName = malloc(strlen(dst) + 5 + 1); 305 strcpy(dstRsrcName, dst); 306 strcat(dstRsrcName, "/rsrc"); 307 308 if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) { 309 DBUG(("--- RSRC: %s --> %s\n", srcRsrcName, dstRsrcName)); 310 311 srcFd = open(srcRsrcName, O_RDONLY); 312 dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0); 313 copyResult = -1; 314 if (srcFd >= 0 && dstFd >= 0) { 315 copyResult = copyFileContents(dstRsrcName, dstFd, 316 srcRsrcName, srcFd); 317 (void) close(srcFd); 318 (void) close(dstFd); 319 } 320 321 if (copyResult != 0) 322 return -1; 323 } 324 325 free(srcRsrcName); 326 free(dstRsrcName); 327 } 328#endif 329 330 setPermissions(dst, pSrcStat, options); 331 332 printCopyMsg(src, dst, options); 333 334 return 0; 335} 336 337 338/* 339 * Copy a symlink. This only happens if we're in "no derefence" mode, 340 * in which we copy the links rather than the files that are pointed at. 341 * 342 * We always discard the destination file. If it's a symlink already, 343 * we want to throw it out and replace it. If it's not a symlink, we 344 * need to trash it so we can create one. 345 */ 346#if defined(_WIN32) 347extern int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) __attribute__((error("no symlinks on Windows"))); 348#else 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 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 } else if (S_ISLNK(srcStat.st_mode)) { 620 retVal = copySymlink(src, dst, &srcStat, options); 621 } else if (S_ISREG(srcStat.st_mode)) { 622 retVal = copyRegular(src, dst, &srcStat, options); 623 } else { 624 fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n", 625 src, srcStat.st_mode); 626 retVal = -1; 627 } 628 629bail: 630 free(srcExe); 631 free(dstExe); 632 free(dstDir); 633 return retVal; 634} 635 636int copyFile(const char* src, const char* dst, unsigned int options) 637{ 638 return copyFileRecursive(src, dst, true, options); 639} 640 641 642