CopyFile.c revision 676799f23d935b64873ccf5b1bf9b475cb392ea3
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(__CYGWIN__) || defined(__MINGW32__) 71 return 0; 72#elif defined(MACOSX_RSRC) 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#ifndef HAVE_VALID_STAT_ST_INO 87 /* TODO: suspicious, we hit this case even when compiling for linux: b/26355387 */ 88 (void)pSrcStat; 89 (void)pDstStat; 90 /* with MSVCRT.DLL, stat always sets st_ino to 0, and there is no simple way to */ 91 /* get the equivalent information with Win32 (Cygwin does some weird stuff in */ 92 /* its winsup/cygwin/fhandler_disk_file.cc to emulate this, too complex for us) */ 93 return 0; 94#else 95 return (pSrcStat->st_ino == pDstStat->st_ino); 96#endif 97} 98 99static void printCopyMsg(const char* src, const char* dst, unsigned int options) 100{ 101 if ((options & COPY_VERBOSE_MASK) > 0) 102 printf(" '%s' --> '%s'\n", src, dst); 103} 104 105static void printNotNewerMsg(const char* src, const char* dst, unsigned int options) 106{ 107 (void)src; 108 if ((options & COPY_VERBOSE_MASK) > 1) 109 printf(" '%s' is up-to-date\n", dst); 110} 111 112/* 113 * Copy the contents of one file to another. 114 * 115 * The files are assumed to be seeked to the start. 116 */ 117static int copyFileContents(const char* dst, int dstFd, const char* src, int srcFd) 118{ 119 unsigned char buf[8192]; 120 ssize_t readCount, writeCount; 121 122 /* 123 * Read a chunk, write it, and repeat. 124 */ 125 while (1) { 126 readCount = read(srcFd, buf, sizeof(buf)); 127 if (readCount < 0) { 128 fprintf(stderr, 129 "acp: failed reading '%s': %s\n", src, strerror(errno)); 130 return -1; 131 } 132 133 if (readCount > 0) { 134 writeCount = write(dstFd, buf, readCount); 135 if (writeCount < 0) { 136 fprintf(stderr, 137 "acp: failed writing '%s': %s\n", dst, strerror(errno)); 138 return -1; 139 } 140 if (writeCount != readCount) { 141 fprintf(stderr, "acp: partial write to '%s' (%zd of %zd)\n", 142 dst, writeCount, readCount); 143 return -1; 144 } 145 } 146 147 if (readCount < (ssize_t) sizeof(buf)) 148 break; 149 } 150 151 return 0; 152} 153 154/* 155 * Set the permissions, owner, and timestamps on the destination file 156 * equal to those of the source file. 157 * 158 * Failures here are "soft"; they don't produce warning messages and don't 159 * cause the cp command to report a failure. 160 */ 161static int setPermissions(const char* dst, const struct stat* pSrcStat, unsigned int options) 162{ 163 struct utimbuf ut; 164 165 if (options & COPY_TIMESTAMPS) { 166 /* 167 * Start with timestamps. The access and mod dates are not affected 168 * by the next operations. 169 */ 170 ut.actime = pSrcStat->st_atime; 171 ut.modtime = pSrcStat->st_mtime; 172 if (isHiresMtime(pSrcStat)) 173 ut.modtime += 1; 174 if (utime(dst, &ut) != 0) { 175 DBUG(("--- unable to set timestamps on '%s': %s\n", 176 dst, strerror(errno))); 177 } 178 } 179 180 if (options & COPY_PERMISSIONS) { 181 /* 182 * Set the permissions. 183 */ 184 if (chmod(dst, pSrcStat->st_mode & ~(S_IFMT)) != 0) { 185 DBUG(("--- unable to set perms on '%s' to 0%o: %s\n", 186 dst, pSrcStat->st_mode & ~(S_IFMT), strerror(errno))); 187 } 188#ifndef _WIN32 189 /* 190 * Set the owner. 191 */ 192 if (chown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) { 193 DBUG(("--- unable to set owner of '%s' to %d/%d: %s\n", 194 dst, pSrcStat->st_uid, pSrcStat->st_gid, strerror(errno))); 195 } 196#endif 197 } 198 199 return 0; 200} 201 202/* 203 * Copy a regular file. If the destination file exists and is not a 204 * regular file, we fail. However, we use stat() rather than lstat(), 205 * because it's okay to write through a symlink (the noDereference stuff 206 * only applies to the source file). 207 * 208 * If the file doesn't exist, create it. If it does exist, truncate it. 209 */ 210static int copyRegular(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options) 211{ 212 struct stat dstStat; 213 int srcFd, dstFd, statResult, copyResult; 214 215 DBUG(("--- copying regular '%s' to '%s'\n", src, dst)); 216 217 statResult = stat(dst, &dstStat); 218 if (statResult == 0 && !S_ISREG(dstStat.st_mode)) { 219 fprintf(stderr, 220 "acp: destination '%s' exists and is not regular file\n", 221 dst); 222 return -1; 223 } else if (statResult != 0 && errno != ENOENT) { 224 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst); 225 return -1; 226 } 227 228 if (statResult == 0) { 229 if (isSameFile(pSrcStat, &dstStat)) { 230 fprintf(stderr, "acp: '%s' and '%s' are the same file\n", 231 src, dst); 232 return -1; 233 } 234 if (options & COPY_UPDATE_ONLY) { 235 if (!isSourceNewer(pSrcStat, &dstStat)) { 236 DBUG(("--- source is not newer: '%s'\n", src)); 237 printNotNewerMsg(src, dst, options); 238 return 0; 239 } 240 } 241 } 242 243 /* open src */ 244 srcFd = open(src, O_RDONLY | O_BINARY, 0); 245 if (srcFd < 0) { 246 fprintf(stderr, "acp: unable to open '%s': %s\n", src, strerror(errno)); 247 return -1; 248 } 249 250 /* open dest with O_CREAT | O_TRUNC */ 251 DBUG(("--- opening '%s'\n", dst)); 252 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); 253 254 if (dstFd < 0) { 255 if (errno == ENOENT) { 256 /* this happens if the target directory doesn't exist */ 257 fprintf(stderr, 258 "acp: cannot create '%s': %s\n", dst, strerror(errno)); 259 (void) close(srcFd); 260 return -1; 261 } 262 263 /* if "force" is set, try removing the destination file and retry */ 264 if (options & COPY_FORCE) { 265 if (unlink(dst) != 0) { 266#ifdef _WIN32 267 /* MSVCRT.DLL unlink will fail with EACCESS if the file is set read-only */ 268 /* so try to change its mode, and unlink again */ 269 if (errno == EACCESS) { 270 if (chmod(dst, S_IWRITE|S_IREAD) == 0 && unlink(dst) == 0) 271 goto Open_File; 272 } 273#endif 274 fprintf(stderr, "acp: unable to remove '%s': %s\n", 275 dst, strerror(errno)); 276 (void) close(srcFd); 277 return -1; 278 } 279#ifdef _WIN32 280 Open_File: 281#endif 282 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); 283 } 284 } 285 if (dstFd < 0) { 286 fprintf(stderr, "acp: unable to open '%s': %s\n", 287 dst, strerror(errno)); 288 (void) close(srcFd); 289 return -1; 290 } 291 292 copyResult = copyFileContents(dst, dstFd, src, srcFd); 293 294 (void) close(srcFd); 295 (void) close(dstFd); 296 if (copyResult != 0) 297 return -1; 298 299#ifdef MACOSX_RSRC 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