Home | History | Annotate | Download | only in libhost
      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