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