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 #include <direct.h> /* For _mkdir() */ 29 # define mkdir(path,mode) _mkdir(path) 30 # define S_ISLNK(s) 0 31 # define lstat stat 32 # ifndef EACCESS /* seems to be missing from the Mingw headers */ 33 # define EACCESS 13 34 # endif 35 #endif 36 37 #ifndef O_BINARY 38 # define O_BINARY 0 39 #endif 40 41 /*#define DEBUG_MSGS*/ 42 #ifdef DEBUG_MSGS 43 # define DBUG(x) printf x 44 #else 45 # define DBUG(x) ((void)0) 46 #endif 47 48 #define FSSEP '/' /* filename separator char */ 49 50 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options); 51 52 /* 53 * Returns true if the source file is newer than the destination file. 54 * 55 * The check is based on the modification date, whole seconds only. This 56 * also returns true if the file sizes don't match. 57 */ 58 static bool isSourceNewer(const struct stat* pSrcStat, const struct stat* pDstStat) 59 { 60 return (pSrcStat->st_mtime > pDstStat->st_mtime) || 61 (pSrcStat->st_size != pDstStat->st_size); 62 } 63 64 /* 65 * Returns true if the source file has high resolution modification 66 * date. Cygwin/Mingw doesn't support st_mtim and always returns false. 67 */ 68 static bool isHiresMtime(const struct stat* pSrcStat) 69 { 70 #if defined(_WIN32) 71 return 0; 72 #elif defined(__APPLE__) 73 return pSrcStat->st_mtimespec.tv_nsec > 0; 74 #else 75 return pSrcStat->st_mtim.tv_nsec > 0; 76 #endif 77 } 78 79 /* 80 * Returns true if the source and destination files are actually the 81 * same thing. We detect this by checking the inode numbers, which seems 82 * to work on Cygwin. 83 */ 84 static bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat) 85 { 86 #if defined(_WIN32) 87 (void)pSrcStat; 88 (void)pDstStat; 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 98 static 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 104 static void printNotNewerMsg(const char* src, const char* dst, unsigned int options) 105 { 106 (void)src; 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 _WIN32 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 _WIN32 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 _WIN32 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 #if defined(__APPLE__) 299 // Copy Mac OS X resource forks too. 300 { 301 char* srcRsrcName = NULL; 302 char* dstRsrcName = NULL; 303 struct stat rsrcStat; 304 305 srcRsrcName = malloc(strlen(src) + 5 + 1); 306 strcpy(srcRsrcName, src); 307 strcat(srcRsrcName, "/rsrc"); 308 309 dstRsrcName = malloc(strlen(dst) + 5 + 1); 310 strcpy(dstRsrcName, dst); 311 strcat(dstRsrcName, "/rsrc"); 312 313 if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) { 314 DBUG(("--- RSRC: %s --> %s\n", srcRsrcName, dstRsrcName)); 315 316 srcFd = open(srcRsrcName, O_RDONLY); 317 dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0); 318 copyResult = -1; 319 if (srcFd >= 0 && dstFd >= 0) { 320 copyResult = copyFileContents(dstRsrcName, dstFd, 321 srcRsrcName, srcFd); 322 (void) close(srcFd); 323 (void) close(dstFd); 324 } 325 326 if (copyResult != 0) { 327 free(srcRsrcName); 328 free(dstRsrcName); 329 return -1; 330 } 331 } 332 333 free(srcRsrcName); 334 free(dstRsrcName); 335 } 336 #endif 337 338 setPermissions(dst, pSrcStat, options); 339 340 printCopyMsg(src, dst, options); 341 342 return 0; 343 } 344 345 346 /* 347 * Copy a symlink. This only happens if we're in "no derefence" mode, 348 * in which we copy the links rather than the files that are pointed at. 349 * 350 * We always discard the destination file. If it's a symlink already, 351 * we want to throw it out and replace it. If it's not a symlink, we 352 * need to trash it so we can create one. 353 */ 354 #if defined(_WIN32) 355 extern int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 356 #ifdef __clang__ 357 __attribute__((unavailable("no symlinks on Windows"))); 358 #else 359 __attribute__((error("no symlinks on Windows"))); 360 #endif 361 #else 362 static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 363 { 364 struct stat dstStat; 365 char linkBuf[PATH_MAX+1]; 366 int statResult, nameLen; 367 368 assert(options & COPY_NO_DEREFERENCE); 369 DBUG(("--- copying symlink '%s' to '%s'\n", src, dst)); 370 371 /* NOTE: we use lstat() here */ 372 statResult = lstat(dst, &dstStat); 373 if (statResult == 0 && !S_ISREG(dstStat.st_mode) 374 && !S_ISLNK(dstStat.st_mode) 375 ) 376 { 377 fprintf(stderr, 378 "acp: destination '%s' exists and is not regular or symlink\n", 379 dst); 380 return -1; 381 } 382 383 if (statResult == 0) { 384 if (isSameFile(pSrcStat, &dstStat)) { 385 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 386 src, dst); 387 return -1; 388 } 389 if (options & COPY_UPDATE_ONLY) { 390 if (!isSourceNewer(pSrcStat, &dstStat)) { 391 DBUG(("--- source is not newer: '%s'\n", src)); 392 printNotNewerMsg(src, dst, options); 393 return 0; 394 } 395 } 396 } 397 398 /* extract the symlink contents */ 399 nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1); 400 if (nameLen <= 0) { 401 fprintf(stderr, "acp: unable to read symlink '%s': %s\n", 402 src, strerror(errno)); 403 return -1; 404 } 405 linkBuf[nameLen] = '\0'; 406 DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf)); 407 408 if (statResult == 0) { 409 DBUG(("--- removing '%s'\n", dst)); 410 if (unlink(dst) != 0) { 411 fprintf(stderr, "acp: unable to remove '%s': %s\n", 412 dst, strerror(errno)); 413 return -1; 414 } 415 } 416 417 if (symlink(linkBuf, dst) != 0) { 418 fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n", 419 dst, linkBuf, strerror(errno)); 420 return -1; 421 } 422 423 /* 424 * There's no way to set the file date or access permissions, but 425 * it is possible to set the owner. 426 */ 427 if (options & COPY_PERMISSIONS) { 428 if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) 429 DBUG(("--- lchown failed: %s\n", strerror(errno))); 430 } 431 432 printCopyMsg(src, dst, options); 433 434 return 0; 435 } 436 #endif 437 438 /* 439 * Copy the contents of one directory to another. Both "src" and "dst" 440 * must be directories. We will create "dst" if it does not exist. 441 */ 442 int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 443 { 444 int retVal = 0; 445 struct stat dstStat; 446 DIR* dir; 447 int cc, statResult; 448 449 DBUG(("--- copy dir '%s' to '%s'\n", src, dst)); 450 451 statResult = stat(dst, &dstStat); 452 if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) { 453 fprintf(stderr, 454 "acp: destination '%s' exists and is not a directory\n", dst); 455 return -1; 456 } else if (statResult != 0 && errno != ENOENT) { 457 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 458 return -1; 459 } 460 461 if (statResult == 0) { 462 if (isSameFile(pSrcStat, &dstStat)) { 463 fprintf(stderr, 464 "acp: cannot copy directory into itself ('%s' and '%s')\n", 465 src, dst); 466 return -1; 467 } 468 } else { 469 DBUG(("--- creating dir '%s'\n", dst)); 470 cc = mkdir(dst, 0755); 471 if (cc != 0) { 472 fprintf(stderr, "acp: unable to create directory '%s': %s\n", 473 dst, strerror(errno)); 474 return -1; 475 } 476 477 /* only print on mkdir */ 478 printCopyMsg(src, dst, options); 479 } 480 481 /* 482 * Open the directory, and plow through its contents. 483 */ 484 dir = opendir(src); 485 if (dir == NULL) { 486 fprintf(stderr, "acp: unable to open directory '%s': %s\n", 487 src, strerror(errno)); 488 return -1; 489 } 490 491 while (1) { 492 struct dirent* ent; 493 char* srcFile; 494 char* dstFile; 495 int srcLen, dstLen, nameLen; 496 497 ent = readdir(dir); 498 if (ent == NULL) 499 break; 500 501 if (strcmp(ent->d_name, ".") == 0 || 502 strcmp(ent->d_name, "..") == 0) 503 { 504 continue; 505 } 506 507 nameLen = strlen(ent->d_name); 508 srcLen = strlen(src); 509 dstLen = strlen(dst); 510 511 srcFile = malloc(srcLen +1 + nameLen +1); 512 memcpy(srcFile, src, srcLen); 513 srcFile[srcLen] = FSSEP; 514 memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1); 515 516 dstFile = malloc(dstLen +1 + nameLen +1); 517 memcpy(dstFile, dst, dstLen); 518 dstFile[dstLen] = FSSEP; 519 memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1); 520 521 if (copyFileRecursive(srcFile, dstFile, false, options) != 0) 522 retVal = -1; /* note failure and keep going */ 523 524 free(srcFile); 525 free(dstFile); 526 } 527 closedir(dir); 528 529 setPermissions(dst, pSrcStat, options); 530 531 return retVal; 532 } 533 534 /* 535 * Do the actual copy. This is called recursively from copyDirectory(). 536 * 537 * "dst" should only be a directory if "src" is also a directory. 538 * 539 * Returns 0 on success. 540 */ 541 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options) 542 { 543 char* srcExe = NULL; 544 char* dstExe = NULL; 545 char* dstDir = NULL; 546 struct stat srcStat; 547 int retVal = 0; 548 int statResult, statErrno; 549 (void)isCmdLine; 550 551 /* 552 * Stat the source file. If it doesn't exist, fail. 553 */ 554 if (options & COPY_NO_DEREFERENCE) 555 statResult = lstat(src, &srcStat); 556 else 557 statResult = stat(src, &srcStat); 558 statErrno = errno; /* preserve across .exe attempt */ 559 560 if (statResult < 0) { 561 if (statErrno == ENOENT) 562 fprintf(stderr, "acp: file '%s' does not exist\n", src); 563 else 564 fprintf(stderr, "acp: unable to stat '%s': %s\n", 565 src, strerror(statErrno)); 566 retVal = -1; 567 goto bail; 568 } 569 570 /* 571 * If "src" is a directory, ignore it if "recursive" isn't set. 572 * 573 * We want to create "dst" as a directory (or verify that it already 574 * exists as a directory), and then copy its contents. 575 */ 576 if (S_ISDIR(srcStat.st_mode)) { 577 if (!(options & COPY_RECURSIVE)) { 578 fprintf(stderr, "acp: omitting directory '%s'\n", src); 579 } else { 580 retVal = copyDirectory(src, dst, &srcStat, options); 581 } 582 #if !defined(_WIN32) 583 } else if (S_ISLNK(srcStat.st_mode)) { 584 retVal = copySymlink(src, dst, &srcStat, options); 585 #endif 586 } else if (S_ISREG(srcStat.st_mode)) { 587 retVal = copyRegular(src, dst, &srcStat, options); 588 } else { 589 fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n", 590 src, srcStat.st_mode); 591 retVal = -1; 592 } 593 594 bail: 595 free(srcExe); 596 free(dstExe); 597 free(dstDir); 598 return retVal; 599 } 600 601 int copyFile(const char* src, const char* dst, unsigned int options) 602 { 603 return copyFileRecursive(src, dst, true, options); 604 } 605 606 607