CopyFile.c revision 982e1f265073a7a226abe44ec476e960c0b0e43f
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 /* TODO: suspicious, we hit this case even when compiling for linux: b/26355387 */ 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 98static 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 104static 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 */ 116static 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 */ 160static 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 */ 209static 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#ifdef MACOSX_RSRC 299 { 300 char* srcRsrcName = NULL; 301 char* dstRsrcName = NULL; 302 struct stat rsrcStat; 303 304 srcRsrcName = malloc(strlen(src) + 5 + 1); 305 strcpy(srcRsrcName, src); 306 strcat(srcRsrcName, "/rsrc"); 307 308 dstRsrcName = malloc(strlen(dst) + 5 + 1); 309 strcpy(dstRsrcName, dst); 310 strcat(dstRsrcName, "/rsrc"); 311 312 if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) { 313 DBUG(("--- RSRC: %s --> %s\n", srcRsrcName, dstRsrcName)); 314 315 srcFd = open(srcRsrcName, O_RDONLY); 316 dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0); 317 copyResult = -1; 318 if (srcFd >= 0 && dstFd >= 0) { 319 copyResult = copyFileContents(dstRsrcName, dstFd, 320 srcRsrcName, srcFd); 321 (void) close(srcFd); 322 (void) close(dstFd); 323 } 324 325 if (copyResult != 0) 326 return -1; 327 } 328 329 free(srcRsrcName); 330 free(dstRsrcName); 331 } 332#endif 333 334 setPermissions(dst, pSrcStat, options); 335 336 printCopyMsg(src, dst, options); 337 338 return 0; 339} 340 341 342/* 343 * Copy a symlink. This only happens if we're in "no derefence" mode, 344 * in which we copy the links rather than the files that are pointed at. 345 * 346 * We always discard the destination file. If it's a symlink already, 347 * we want to throw it out and replace it. If it's not a symlink, we 348 * need to trash it so we can create one. 349 */ 350#if defined(_WIN32) 351extern int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) __attribute__((error("no symlinks on Windows"))); 352#else 353static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 354{ 355 struct stat dstStat; 356 char linkBuf[PATH_MAX+1]; 357 int statResult, nameLen; 358 359 assert(options & COPY_NO_DEREFERENCE); 360 DBUG(("--- copying symlink '%s' to '%s'\n", src, dst)); 361 362 /* NOTE: we use lstat() here */ 363 statResult = lstat(dst, &dstStat); 364 if (statResult == 0 && !S_ISREG(dstStat.st_mode) 365 && !S_ISLNK(dstStat.st_mode) 366 ) 367 { 368 fprintf(stderr, 369 "acp: destination '%s' exists and is not regular or symlink\n", 370 dst); 371 return -1; 372 } 373 374 if (statResult == 0) { 375 if (isSameFile(pSrcStat, &dstStat)) { 376 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 377 src, dst); 378 return -1; 379 } 380 if (options & COPY_UPDATE_ONLY) { 381 if (!isSourceNewer(pSrcStat, &dstStat)) { 382 DBUG(("--- source is not newer: '%s'\n", src)); 383 printNotNewerMsg(src, dst, options); 384 return 0; 385 } 386 } 387 } 388 389 /* extract the symlink contents */ 390 nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1); 391 if (nameLen <= 0) { 392 fprintf(stderr, "acp: unable to read symlink '%s': %s\n", 393 src, strerror(errno)); 394 return -1; 395 } 396 linkBuf[nameLen] = '\0'; 397 DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf)); 398 399 if (statResult == 0) { 400 DBUG(("--- removing '%s'\n", dst)); 401 if (unlink(dst) != 0) { 402 fprintf(stderr, "acp: unable to remove '%s': %s\n", 403 dst, strerror(errno)); 404 return -1; 405 } 406 } 407 408 if (symlink(linkBuf, dst) != 0) { 409 fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n", 410 dst, linkBuf, strerror(errno)); 411 return -1; 412 } 413 414 /* 415 * There's no way to set the file date or access permissions, but 416 * it is possible to set the owner. 417 */ 418 if (options & COPY_PERMISSIONS) { 419 if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) 420 DBUG(("--- lchown failed: %s\n", strerror(errno))); 421 } 422 423 printCopyMsg(src, dst, options); 424 425 return 0; 426} 427#endif 428 429/* 430 * Copy the contents of one directory to another. Both "src" and "dst" 431 * must be directories. We will create "dst" if it does not exist. 432 */ 433int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 434{ 435 int retVal = 0; 436 struct stat dstStat; 437 DIR* dir; 438 int cc, statResult; 439 440 DBUG(("--- copy dir '%s' to '%s'\n", src, dst)); 441 442 statResult = stat(dst, &dstStat); 443 if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) { 444 fprintf(stderr, 445 "acp: destination '%s' exists and is not a directory\n", dst); 446 return -1; 447 } else if (statResult != 0 && errno != ENOENT) { 448 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 449 return -1; 450 } 451 452 if (statResult == 0) { 453 if (isSameFile(pSrcStat, &dstStat)) { 454 fprintf(stderr, 455 "acp: cannot copy directory into itself ('%s' and '%s')\n", 456 src, dst); 457 return -1; 458 } 459 } else { 460 DBUG(("--- creating dir '%s'\n", dst)); 461 cc = mkdir(dst, 0755); 462 if (cc != 0) { 463 fprintf(stderr, "acp: unable to create directory '%s': %s\n", 464 dst, strerror(errno)); 465 return -1; 466 } 467 468 /* only print on mkdir */ 469 printCopyMsg(src, dst, options); 470 } 471 472 /* 473 * Open the directory, and plow through its contents. 474 */ 475 dir = opendir(src); 476 if (dir == NULL) { 477 fprintf(stderr, "acp: unable to open directory '%s': %s\n", 478 src, strerror(errno)); 479 return -1; 480 } 481 482 while (1) { 483 struct dirent* ent; 484 char* srcFile; 485 char* dstFile; 486 int srcLen, dstLen, nameLen; 487 488 ent = readdir(dir); 489 if (ent == NULL) 490 break; 491 492 if (strcmp(ent->d_name, ".") == 0 || 493 strcmp(ent->d_name, "..") == 0) 494 { 495 continue; 496 } 497 498 nameLen = strlen(ent->d_name); 499 srcLen = strlen(src); 500 dstLen = strlen(dst); 501 502 srcFile = malloc(srcLen +1 + nameLen +1); 503 memcpy(srcFile, src, srcLen); 504 srcFile[srcLen] = FSSEP; 505 memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1); 506 507 dstFile = malloc(dstLen +1 + nameLen +1); 508 memcpy(dstFile, dst, dstLen); 509 dstFile[dstLen] = FSSEP; 510 memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1); 511 512 if (copyFileRecursive(srcFile, dstFile, false, options) != 0) 513 retVal = -1; /* note failure and keep going */ 514 515 free(srcFile); 516 free(dstFile); 517 } 518 closedir(dir); 519 520 setPermissions(dst, pSrcStat, options); 521 522 return retVal; 523} 524 525/* 526 * Do the actual copy. This is called recursively from copyDirectory(). 527 * 528 * "dst" should only be a directory if "src" is also a directory. 529 * 530 * Returns 0 on success. 531 */ 532static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options) 533{ 534 char* srcExe = NULL; 535 char* dstExe = NULL; 536 char* dstDir = NULL; 537 struct stat srcStat; 538 int retVal = 0; 539 int statResult, statErrno; 540 (void)isCmdLine; 541 542 /* 543 * Stat the source file. If it doesn't exist, fail. 544 */ 545 if (options & COPY_NO_DEREFERENCE) 546 statResult = lstat(src, &srcStat); 547 else 548 statResult = stat(src, &srcStat); 549 statErrno = errno; /* preserve across .exe attempt */ 550 551 if (statResult < 0) { 552 if (statErrno == ENOENT) 553 fprintf(stderr, "acp: file '%s' does not exist\n", src); 554 else 555 fprintf(stderr, "acp: unable to stat '%s': %s\n", 556 src, strerror(statErrno)); 557 retVal = -1; 558 goto bail; 559 } 560 561 /* 562 * If "src" is a directory, ignore it if "recursive" isn't set. 563 * 564 * We want to create "dst" as a directory (or verify that it already 565 * exists as a directory), and then copy its contents. 566 */ 567 if (S_ISDIR(srcStat.st_mode)) { 568 if (!(options & COPY_RECURSIVE)) { 569 fprintf(stderr, "acp: omitting directory '%s'\n", src); 570 } else { 571 retVal = copyDirectory(src, dst, &srcStat, options); 572 } 573 } else if (S_ISLNK(srcStat.st_mode)) { 574 retVal = copySymlink(src, dst, &srcStat, options); 575 } else if (S_ISREG(srcStat.st_mode)) { 576 retVal = copyRegular(src, dst, &srcStat, options); 577 } else { 578 fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n", 579 src, srcStat.st_mode); 580 retVal = -1; 581 } 582 583bail: 584 free(srcExe); 585 free(dstExe); 586 free(dstDir); 587 return retVal; 588} 589 590int copyFile(const char* src, const char* dst, unsigned int options) 591{ 592 return copyFileRecursive(src, dst, true, options); 593} 594 595 596