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