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) __attribute__((error("no symlinks on Windows"))); 356 #else 357 static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 358 { 359 struct stat dstStat; 360 char linkBuf[PATH_MAX+1]; 361 int statResult, nameLen; 362 363 assert(options & COPY_NO_DEREFERENCE); 364 DBUG(("--- copying symlink '%s' to '%s'\n", src, dst)); 365 366 /* NOTE: we use lstat() here */ 367 statResult = lstat(dst, &dstStat); 368 if (statResult == 0 && !S_ISREG(dstStat.st_mode) 369 && !S_ISLNK(dstStat.st_mode) 370 ) 371 { 372 fprintf(stderr, 373 "acp: destination '%s' exists and is not regular or symlink\n", 374 dst); 375 return -1; 376 } 377 378 if (statResult == 0) { 379 if (isSameFile(pSrcStat, &dstStat)) { 380 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 381 src, dst); 382 return -1; 383 } 384 if (options & COPY_UPDATE_ONLY) { 385 if (!isSourceNewer(pSrcStat, &dstStat)) { 386 DBUG(("--- source is not newer: '%s'\n", src)); 387 printNotNewerMsg(src, dst, options); 388 return 0; 389 } 390 } 391 } 392 393 /* extract the symlink contents */ 394 nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1); 395 if (nameLen <= 0) { 396 fprintf(stderr, "acp: unable to read symlink '%s': %s\n", 397 src, strerror(errno)); 398 return -1; 399 } 400 linkBuf[nameLen] = '\0'; 401 DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf)); 402 403 if (statResult == 0) { 404 DBUG(("--- removing '%s'\n", dst)); 405 if (unlink(dst) != 0) { 406 fprintf(stderr, "acp: unable to remove '%s': %s\n", 407 dst, strerror(errno)); 408 return -1; 409 } 410 } 411 412 if (symlink(linkBuf, dst) != 0) { 413 fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n", 414 dst, linkBuf, strerror(errno)); 415 return -1; 416 } 417 418 /* 419 * There's no way to set the file date or access permissions, but 420 * it is possible to set the owner. 421 */ 422 if (options & COPY_PERMISSIONS) { 423 if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) 424 DBUG(("--- lchown failed: %s\n", strerror(errno))); 425 } 426 427 printCopyMsg(src, dst, options); 428 429 return 0; 430 } 431 #endif 432 433 /* 434 * Copy the contents of one directory to another. Both "src" and "dst" 435 * must be directories. We will create "dst" if it does not exist. 436 */ 437 int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 438 { 439 int retVal = 0; 440 struct stat dstStat; 441 DIR* dir; 442 int cc, statResult; 443 444 DBUG(("--- copy dir '%s' to '%s'\n", src, dst)); 445 446 statResult = stat(dst, &dstStat); 447 if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) { 448 fprintf(stderr, 449 "acp: destination '%s' exists and is not a directory\n", dst); 450 return -1; 451 } else if (statResult != 0 && errno != ENOENT) { 452 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 453 return -1; 454 } 455 456 if (statResult == 0) { 457 if (isSameFile(pSrcStat, &dstStat)) { 458 fprintf(stderr, 459 "acp: cannot copy directory into itself ('%s' and '%s')\n", 460 src, dst); 461 return -1; 462 } 463 } else { 464 DBUG(("--- creating dir '%s'\n", dst)); 465 cc = mkdir(dst, 0755); 466 if (cc != 0) { 467 fprintf(stderr, "acp: unable to create directory '%s': %s\n", 468 dst, strerror(errno)); 469 return -1; 470 } 471 472 /* only print on mkdir */ 473 printCopyMsg(src, dst, options); 474 } 475 476 /* 477 * Open the directory, and plow through its contents. 478 */ 479 dir = opendir(src); 480 if (dir == NULL) { 481 fprintf(stderr, "acp: unable to open directory '%s': %s\n", 482 src, strerror(errno)); 483 return -1; 484 } 485 486 while (1) { 487 struct dirent* ent; 488 char* srcFile; 489 char* dstFile; 490 int srcLen, dstLen, nameLen; 491 492 ent = readdir(dir); 493 if (ent == NULL) 494 break; 495 496 if (strcmp(ent->d_name, ".") == 0 || 497 strcmp(ent->d_name, "..") == 0) 498 { 499 continue; 500 } 501 502 nameLen = strlen(ent->d_name); 503 srcLen = strlen(src); 504 dstLen = strlen(dst); 505 506 srcFile = malloc(srcLen +1 + nameLen +1); 507 memcpy(srcFile, src, srcLen); 508 srcFile[srcLen] = FSSEP; 509 memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1); 510 511 dstFile = malloc(dstLen +1 + nameLen +1); 512 memcpy(dstFile, dst, dstLen); 513 dstFile[dstLen] = FSSEP; 514 memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1); 515 516 if (copyFileRecursive(srcFile, dstFile, false, options) != 0) 517 retVal = -1; /* note failure and keep going */ 518 519 free(srcFile); 520 free(dstFile); 521 } 522 closedir(dir); 523 524 setPermissions(dst, pSrcStat, options); 525 526 return retVal; 527 } 528 529 /* 530 * Do the actual copy. This is called recursively from copyDirectory(). 531 * 532 * "dst" should only be a directory if "src" is also a directory. 533 * 534 * Returns 0 on success. 535 */ 536 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options) 537 { 538 char* srcExe = NULL; 539 char* dstExe = NULL; 540 char* dstDir = NULL; 541 struct stat srcStat; 542 int retVal = 0; 543 int statResult, statErrno; 544 (void)isCmdLine; 545 546 /* 547 * Stat the source file. If it doesn't exist, fail. 548 */ 549 if (options & COPY_NO_DEREFERENCE) 550 statResult = lstat(src, &srcStat); 551 else 552 statResult = stat(src, &srcStat); 553 statErrno = errno; /* preserve across .exe attempt */ 554 555 if (statResult < 0) { 556 if (statErrno == ENOENT) 557 fprintf(stderr, "acp: file '%s' does not exist\n", src); 558 else 559 fprintf(stderr, "acp: unable to stat '%s': %s\n", 560 src, strerror(statErrno)); 561 retVal = -1; 562 goto bail; 563 } 564 565 /* 566 * If "src" is a directory, ignore it if "recursive" isn't set. 567 * 568 * We want to create "dst" as a directory (or verify that it already 569 * exists as a directory), and then copy its contents. 570 */ 571 if (S_ISDIR(srcStat.st_mode)) { 572 if (!(options & COPY_RECURSIVE)) { 573 fprintf(stderr, "acp: omitting directory '%s'\n", src); 574 } else { 575 retVal = copyDirectory(src, dst, &srcStat, options); 576 } 577 } else if (S_ISLNK(srcStat.st_mode)) { 578 retVal = copySymlink(src, dst, &srcStat, options); 579 } else if (S_ISREG(srcStat.st_mode)) { 580 retVal = copyRegular(src, dst, &srcStat, options); 581 } else { 582 fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n", 583 src, srcStat.st_mode); 584 retVal = -1; 585 } 586 587 bail: 588 free(srcExe); 589 free(dstExe); 590 free(dstDir); 591 return retVal; 592 } 593 594 int copyFile(const char* src, const char* dst, unsigned int options) 595 { 596 return copyFileRecursive(src, dst, true, options); 597 } 598 599 600