Home | History | Annotate | Download | only in host
      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 #ifdef HAVE_MS_C_RUNTIME
     28 #  define  mkdir(path,mode)   _mkdir(path)
     29 #endif
     30 
     31 #ifndef HAVE_SYMLINKS
     32 #  define  lstat              stat
     33 #  ifndef EACCESS   /* seems to be missing from the Mingw headers */
     34 #    define  EACCESS            13
     35 #  endif
     36 #endif
     37 
     38 #ifndef O_BINARY
     39 #  define  O_BINARY  0
     40 #endif
     41 
     42 /*#define DEBUG_MSGS*/
     43 #ifdef DEBUG_MSGS
     44 # define DBUG(x) printf x
     45 #else
     46 # define DBUG(x) ((void)0)
     47 #endif
     48 
     49 #define FSSEP '/'       /* filename separator char */
     50 
     51 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options);
     52 
     53 /*
     54  * Returns true if the source file is newer than the destination file.
     55  *
     56  * The check is based on the modification date, whole seconds only.  This
     57  * also returns true if the file sizes don't match.
     58  */
     59 static bool isSourceNewer(const struct stat* pSrcStat, const struct stat* pDstStat)
     60 {
     61     return (pSrcStat->st_mtime > pDstStat->st_mtime) ||
     62            (pSrcStat->st_size != pDstStat->st_size);
     63 }
     64 
     65 /*
     66  * Returns true if the source file has high resolution modification
     67  * date. Cygwin/Mingw doesn't support st_mtim and always returns false.
     68  */
     69 static bool isHiresMtime(const struct stat* pSrcStat)
     70 {
     71 #if HAVE_STAT_ST_MTIM
     72 #if defined(MACOSX_RSRC)
     73     return pSrcStat->st_mtimespec.tv_nsec > 0;
     74 #else
     75     return pSrcStat->st_mtim.tv_nsec > 0;
     76 #endif
     77 #else
     78     return 0;
     79 #endif
     80 }
     81 
     82 /*
     83  * Returns true if the source and destination files are actually the
     84  * same thing.  We detect this by checking the inode numbers, which seems
     85  * to work on Cygwin.
     86  */
     87 static bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat)
     88 {
     89 #ifndef HAVE_VALID_STAT_ST_INO
     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 
     99 static 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 
    105 static void printNotNewerMsg(const char* src, const char* dst, unsigned int options)
    106 {
    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 HAVE_MS_C_RUNTIME
    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 HAVE_MS_C_RUNTIME
    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 HAVE_MS_C_RUNTIME
    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 #ifdef HAVE_SYMLINKS
    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 static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
    352 {
    353     struct stat dstStat;
    354     char linkBuf[PATH_MAX+1];
    355     int statResult, nameLen;
    356 
    357     assert(options & COPY_NO_DEREFERENCE);
    358     DBUG(("--- copying symlink '%s' to '%s'\n", src, dst));
    359 
    360     /* NOTE: we use lstat() here */
    361     statResult = lstat(dst, &dstStat);
    362     if (statResult == 0 && !S_ISREG(dstStat.st_mode)
    363                          && !S_ISLNK(dstStat.st_mode)
    364 						 )
    365     {
    366         fprintf(stderr,
    367             "acp: destination '%s' exists and is not regular or symlink\n",
    368             dst);
    369         return -1;
    370     }
    371 
    372     if (statResult == 0) {
    373         if (isSameFile(pSrcStat, &dstStat)) {
    374             fprintf(stderr, "acp: '%s' and '%s' are the same file\n",
    375                 src, dst);
    376             return -1;
    377         }
    378         if (options & COPY_UPDATE_ONLY) {
    379             if (!isSourceNewer(pSrcStat, &dstStat)) {
    380                 DBUG(("---  source is not newer: '%s'\n", src));
    381                 printNotNewerMsg(src, dst, options);
    382                 return 0;
    383             }
    384         }
    385     }
    386 
    387     /* extract the symlink contents */
    388     nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1);
    389     if (nameLen <= 0) {
    390         fprintf(stderr, "acp: unable to read symlink '%s': %s\n",
    391             src, strerror(errno));
    392         return -1;
    393     }
    394     linkBuf[nameLen] = '\0';
    395     DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf));
    396 
    397     if (statResult == 0) {
    398         DBUG(("---  removing '%s'\n", dst));
    399         if (unlink(dst) != 0) {
    400             fprintf(stderr, "acp: unable to remove '%s': %s\n",
    401                 dst, strerror(errno));
    402             return -1;
    403         }
    404     }
    405 
    406     if (symlink(linkBuf, dst) != 0) {
    407         fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n",
    408             dst, linkBuf, strerror(errno));
    409         return -1;
    410     }
    411 
    412     /*
    413      * There's no way to set the file date or access permissions, but
    414      * it is possible to set the owner.
    415      */
    416     if (options & COPY_PERMISSIONS) {
    417         if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0)
    418             DBUG(("---  lchown failed: %s\n", strerror(errno)));
    419     }
    420 
    421     printCopyMsg(src, dst, options);
    422 
    423     return 0;
    424 }
    425 #endif /* HAVE_SYMLINKS */
    426 
    427 /*
    428  * Copy the contents of one directory to another.  Both "src" and "dst"
    429  * must be directories.  We will create "dst" if it does not exist.
    430  */
    431 int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
    432 {
    433     int retVal = 0;
    434     struct stat dstStat;
    435     DIR* dir;
    436     int cc, statResult;
    437 
    438     DBUG(("--- copy dir '%s' to '%s'\n", src, dst));
    439 
    440     statResult = stat(dst, &dstStat);
    441     if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) {
    442         fprintf(stderr,
    443             "acp: destination '%s' exists and is not a directory\n", dst);
    444         return -1;
    445     } else if (statResult != 0 && errno != ENOENT) {
    446         fprintf(stderr, "acp: unable to stat destination '%s'\n", dst);
    447         return -1;
    448     }
    449 
    450     if (statResult == 0) {
    451         if (isSameFile(pSrcStat, &dstStat)) {
    452             fprintf(stderr,
    453                 "acp: cannot copy directory into itself ('%s' and '%s')\n",
    454                 src, dst);
    455             return -1;
    456         }
    457     } else {
    458         DBUG(("---  creating dir '%s'\n", dst));
    459         cc = mkdir(dst, 0755);
    460         if (cc != 0) {
    461             fprintf(stderr, "acp: unable to create directory '%s': %s\n",
    462                 dst, strerror(errno));
    463             return -1;
    464         }
    465 
    466         /* only print on mkdir */
    467         printCopyMsg(src, dst, options);
    468     }
    469 
    470     /*
    471      * Open the directory, and plow through its contents.
    472      */
    473     dir = opendir(src);
    474     if (dir == NULL) {
    475         fprintf(stderr, "acp: unable to open directory '%s': %s\n",
    476             src, strerror(errno));
    477         return -1;
    478     }
    479 
    480     while (1) {
    481         struct dirent* ent;
    482         char* srcFile;
    483         char* dstFile;
    484         int srcLen, dstLen, nameLen;
    485 
    486         ent = readdir(dir);
    487         if (ent == NULL)
    488             break;
    489 
    490         if (strcmp(ent->d_name, ".") == 0 ||
    491             strcmp(ent->d_name, "..") == 0)
    492         {
    493             continue;
    494         }
    495 
    496         nameLen = strlen(ent->d_name);
    497         srcLen = strlen(src);
    498         dstLen = strlen(dst);
    499 
    500         srcFile = malloc(srcLen +1 + nameLen +1);
    501         memcpy(srcFile, src, srcLen);
    502         srcFile[srcLen] = FSSEP;
    503         memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1);
    504 
    505         dstFile = malloc(dstLen +1 + nameLen +1);
    506         memcpy(dstFile, dst, dstLen);
    507         dstFile[dstLen] = FSSEP;
    508         memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1);
    509 
    510         if (copyFileRecursive(srcFile, dstFile, false, options) != 0)
    511             retVal = -1;        /* note failure and keep going */
    512 
    513         free(srcFile);
    514         free(dstFile);
    515     }
    516     closedir(dir);
    517 
    518     setPermissions(dst, pSrcStat, options);
    519 
    520     return retVal;
    521 }
    522 
    523 /*
    524  * Do the actual copy.  This is called recursively from copyDirectory().
    525  *
    526  * "dst" should only be a directory if "src" is also a directory.
    527  *
    528  * Returns 0 on success.
    529  */
    530 static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options)
    531 {
    532     char* srcExe = NULL;
    533     char* dstExe = NULL;
    534     char* dstDir = NULL;
    535     struct stat srcStat;
    536     int retVal = 0;
    537     int statResult, statErrno;
    538 
    539     /*
    540      * Stat the source file.  If it doesn't exist, fail.
    541      */
    542     if (options & COPY_NO_DEREFERENCE)
    543         statResult = lstat(src, &srcStat);
    544     else
    545         statResult = stat(src, &srcStat);
    546     statErrno = errno;        /* preserve across .exe attempt */
    547 
    548 #ifdef WIN32_EXE
    549     /*
    550      * Here's the interesting part.  Under Cygwin, if you have a file
    551      * called "foo.exe", stat("foo", ...) will succeed, but open("foo", ...)
    552      * will fail.  We need to figure out what its name is supposed to be
    553      * so we can create the correct destination file.
    554      *
    555      * If we don't have the "-e" flag set, we want "acp foo bar" to fail,
    556      * not automatically find "foo.exe".  That way, if we really were
    557      * trying to copy "foo", it doesn't grab something we don't want.
    558      */
    559     if (isCmdLine && statResult == 0) {
    560         int tmpFd;
    561         tmpFd = open(src, O_RDONLY | O_BINARY, 0);
    562         if (tmpFd < 0) {
    563             statResult = -1;
    564             statErrno = ENOENT;
    565         } else {
    566             (void) close(tmpFd);
    567         }
    568     }
    569 
    570     /*
    571      * If we didn't find the file, try it again with ".exe".
    572      */
    573     if (isCmdLine && statResult < 0 && statErrno == ENOENT && (options & COPY_TRY_EXE)) {
    574         srcExe = malloc(strlen(src) + 4 +1);
    575         strcpy(srcExe, src);
    576         strcat(srcExe, ".exe");
    577 
    578         if (options & COPY_NO_DEREFERENCE)
    579             statResult = lstat(srcExe, &srcStat);
    580         else
    581             statResult = stat(srcExe, &srcStat);
    582 
    583         if (statResult == 0 && !S_ISREG(srcStat.st_mode))
    584             statResult = -1;        /* fail, use original statErrno below */
    585 
    586         if (statResult == 0) {
    587             /* found a .exe, copy that instead */
    588             dstExe = malloc(strlen(dst) + 4 +1);
    589             strcpy(dstExe, dst);
    590             strcat(dstExe, ".exe");
    591 
    592             src = srcExe;
    593             dst = dstExe;
    594         } else {
    595             DBUG(("---  couldn't find '%s' either\n", srcExe));
    596         }
    597     }
    598 #endif
    599     if (statResult < 0) {
    600         if (statErrno == ENOENT)
    601             fprintf(stderr, "acp: file '%s' does not exist\n", src);
    602         else
    603             fprintf(stderr, "acp: unable to stat '%s': %s\n",
    604                 src, strerror(statErrno));
    605         retVal = -1;
    606         goto bail;
    607     }
    608 
    609     /*
    610      * If "src" is a directory, ignore it if "recursive" isn't set.
    611      *
    612      * We want to create "dst" as a directory (or verify that it already
    613      * exists as a directory), and then copy its contents.
    614      */
    615     if (S_ISDIR(srcStat.st_mode)) {
    616         if (!(options & COPY_RECURSIVE)) {
    617             fprintf(stderr, "acp: omitting directory '%s'\n", src);
    618         } else {
    619             retVal = copyDirectory(src, dst, &srcStat, options);
    620         }
    621 #ifdef HAVE_SYMLINKS
    622     } else if (S_ISLNK(srcStat.st_mode)) {
    623         retVal = copySymlink(src, dst, &srcStat, options);
    624 #endif
    625     } else if (S_ISREG(srcStat.st_mode)) {
    626         retVal = copyRegular(src, dst, &srcStat, options);
    627     } else {
    628         fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n",
    629             src, srcStat.st_mode);
    630         retVal = -1;
    631     }
    632 
    633 bail:
    634     free(srcExe);
    635     free(dstExe);
    636     free(dstDir);
    637     return retVal;
    638 }
    639 
    640 int copyFile(const char* src, const char* dst, unsigned int options)
    641 {
    642     return copyFileRecursive(src, dst, true, options);
    643 }
    644 
    645 
    646