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