blob: f0c8fe5b50a66fb61db63dfdb82841194bd4ce73 [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001/*
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
51static 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 */
59static 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/*
Kan-Ru Chen24d22be2010-07-05 17:04:37 +080066 * Returns true if the source file has high resolution modification
Raphael Molla63e3cc2010-10-13 16:50:05 -070067 * date. Cygwin/Mingw doesn't support st_mtim and always returns false.
Kan-Ru Chen24d22be2010-07-05 17:04:37 +080068 */
69static bool isHiresMtime(const struct stat* pSrcStat)
70{
Yabin Cui4120e832014-11-10 10:37:14 -080071#if defined(__CYGWIN__) || defined(__MINGW32__)
72 return 0;
73#elif defined(MACOSX_RSRC)
Kan-Ru Chen24d22be2010-07-05 17:04:37 +080074 return pSrcStat->st_mtimespec.tv_nsec > 0;
75#else
76 return pSrcStat->st_mtim.tv_nsec > 0;
77#endif
78}
79
80/*
The Android Open Source Project88b60792009-03-03 19:28:42 -080081 * Returns true if the source and destination files are actually the
82 * same thing. We detect this by checking the inode numbers, which seems
83 * to work on Cygwin.
84 */
85static bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat)
86{
87#ifndef HAVE_VALID_STAT_ST_INO
88 /* with MSVCRT.DLL, stat always sets st_ino to 0, and there is no simple way to */
89 /* get the equivalent information with Win32 (Cygwin does some weird stuff in */
90 /* its winsup/cygwin/fhandler_disk_file.cc to emulate this, too complex for us) */
91 return 0;
92#else
93 return (pSrcStat->st_ino == pDstStat->st_ino);
94#endif
95}
96
97static void printCopyMsg(const char* src, const char* dst, unsigned int options)
98{
99 if ((options & COPY_VERBOSE_MASK) > 0)
100 printf(" '%s' --> '%s'\n", src, dst);
101}
102
103static void printNotNewerMsg(const char* src, const char* dst, unsigned int options)
104{
105 if ((options & COPY_VERBOSE_MASK) > 1)
106 printf(" '%s' is up-to-date\n", dst);
107}
108
109/*
110 * Copy the contents of one file to another.
111 *
112 * The files are assumed to be seeked to the start.
113 */
114static int copyFileContents(const char* dst, int dstFd, const char* src, int srcFd)
115{
116 unsigned char buf[8192];
117 ssize_t readCount, writeCount;
118
119 /*
120 * Read a chunk, write it, and repeat.
121 */
122 while (1) {
123 readCount = read(srcFd, buf, sizeof(buf));
124 if (readCount < 0) {
125 fprintf(stderr,
126 "acp: failed reading '%s': %s\n", src, strerror(errno));
127 return -1;
128 }
129
130 if (readCount > 0) {
131 writeCount = write(dstFd, buf, readCount);
132 if (writeCount < 0) {
133 fprintf(stderr,
134 "acp: failed writing '%s': %s\n", dst, strerror(errno));
135 return -1;
136 }
137 if (writeCount != readCount) {
Andrew Hsieh276053b2012-02-27 16:06:00 -0800138 fprintf(stderr, "acp: partial write to '%s' (%zd of %zd)\n",
The Android Open Source Project88b60792009-03-03 19:28:42 -0800139 dst, writeCount, readCount);
140 return -1;
141 }
142 }
143
144 if (readCount < (ssize_t) sizeof(buf))
145 break;
146 }
147
148 return 0;
149}
150
151/*
152 * Set the permissions, owner, and timestamps on the destination file
153 * equal to those of the source file.
154 *
155 * Failures here are "soft"; they don't produce warning messages and don't
156 * cause the cp command to report a failure.
157 */
158static int setPermissions(const char* dst, const struct stat* pSrcStat, unsigned int options)
159{
160 struct utimbuf ut;
161
162 if (options & COPY_TIMESTAMPS) {
163 /*
164 * Start with timestamps. The access and mod dates are not affected
165 * by the next operations.
166 */
167 ut.actime = pSrcStat->st_atime;
168 ut.modtime = pSrcStat->st_mtime;
Kan-Ru Chen24d22be2010-07-05 17:04:37 +0800169 if (isHiresMtime(pSrcStat))
170 ut.modtime += 1;
The Android Open Source Project88b60792009-03-03 19:28:42 -0800171 if (utime(dst, &ut) != 0) {
172 DBUG(("--- unable to set timestamps on '%s': %s\n",
173 dst, strerror(errno)));
174 }
175 }
176
177 if (options & COPY_PERMISSIONS) {
178 /*
179 * Set the permissions.
180 */
181 if (chmod(dst, pSrcStat->st_mode & ~(S_IFMT)) != 0) {
182 DBUG(("--- unable to set perms on '%s' to 0%o: %s\n",
183 dst, pSrcStat->st_mode & ~(S_IFMT), strerror(errno)));
184 }
185#ifndef HAVE_MS_C_RUNTIME
186 /*
187 * Set the owner.
188 */
189 if (chown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) {
190 DBUG(("--- unable to set owner of '%s' to %d/%d: %s\n",
191 dst, pSrcStat->st_uid, pSrcStat->st_gid, strerror(errno)));
192 }
193#endif
194 }
195
196 return 0;
197}
198
199/*
200 * Copy a regular file. If the destination file exists and is not a
201 * regular file, we fail. However, we use stat() rather than lstat(),
202 * because it's okay to write through a symlink (the noDereference stuff
203 * only applies to the source file).
204 *
205 * If the file doesn't exist, create it. If it does exist, truncate it.
206 */
207static int copyRegular(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
208{
209 struct stat dstStat;
210 int srcFd, dstFd, statResult, copyResult;
211
212 DBUG(("--- copying regular '%s' to '%s'\n", src, dst));
213
214 statResult = stat(dst, &dstStat);
215 if (statResult == 0 && !S_ISREG(dstStat.st_mode)) {
216 fprintf(stderr,
217 "acp: destination '%s' exists and is not regular file\n",
218 dst);
219 return -1;
220 } else if (statResult != 0 && errno != ENOENT) {
221 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst);
222 return -1;
223 }
224
225 if (statResult == 0) {
226 if (isSameFile(pSrcStat, &dstStat)) {
227 fprintf(stderr, "acp: '%s' and '%s' are the same file\n",
228 src, dst);
229 return -1;
230 }
231 if (options & COPY_UPDATE_ONLY) {
232 if (!isSourceNewer(pSrcStat, &dstStat)) {
233 DBUG(("--- source is not newer: '%s'\n", src));
234 printNotNewerMsg(src, dst, options);
235 return 0;
236 }
237 }
238 }
239
240 /* open src */
241 srcFd = open(src, O_RDONLY | O_BINARY, 0);
242 if (srcFd < 0) {
243 fprintf(stderr, "acp: unable to open '%s': %s\n", src, strerror(errno));
244 return -1;
245 }
246
247 /* open dest with O_CREAT | O_TRUNC */
248 DBUG(("--- opening '%s'\n", dst));
249 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
250
251 if (dstFd < 0) {
252 if (errno == ENOENT) {
253 /* this happens if the target directory doesn't exist */
254 fprintf(stderr,
255 "acp: cannot create '%s': %s\n", dst, strerror(errno));
256 (void) close(srcFd);
257 return -1;
258 }
259
260 /* if "force" is set, try removing the destination file and retry */
261 if (options & COPY_FORCE) {
262 if (unlink(dst) != 0) {
263#ifdef HAVE_MS_C_RUNTIME
264 /* MSVCRT.DLL unlink will fail with EACCESS if the file is set read-only */
265 /* so try to change its mode, and unlink again */
266 if (errno == EACCESS) {
267 if (chmod(dst, S_IWRITE|S_IREAD) == 0 && unlink(dst) == 0)
268 goto Open_File;
269 }
270#endif
271 fprintf(stderr, "acp: unable to remove '%s': %s\n",
272 dst, strerror(errno));
273 (void) close(srcFd);
274 return -1;
275 }
276#ifdef HAVE_MS_C_RUNTIME
277 Open_File:
278#endif
279 dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
280 }
281 }
282 if (dstFd < 0) {
283 fprintf(stderr, "acp: unable to open '%s': %s\n",
284 dst, strerror(errno));
285 (void) close(srcFd);
286 return -1;
287 }
288
289 copyResult = copyFileContents(dst, dstFd, src, srcFd);
290
291 (void) close(srcFd);
292 (void) close(dstFd);
293 if (copyResult != 0)
294 return -1;
295
296#ifdef MACOSX_RSRC
297 {
298 char* srcRsrcName = NULL;
299 char* dstRsrcName = NULL;
300 struct stat rsrcStat;
301
302 srcRsrcName = malloc(strlen(src) + 5 + 1);
303 strcpy(srcRsrcName, src);
304 strcat(srcRsrcName, "/rsrc");
305
306 dstRsrcName = malloc(strlen(dst) + 5 + 1);
307 strcpy(dstRsrcName, dst);
308 strcat(dstRsrcName, "/rsrc");
309
310 if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) {
311 DBUG(("--- RSRC: %s --> %s\n", srcRsrcName, dstRsrcName));
312
313 srcFd = open(srcRsrcName, O_RDONLY);
314 dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0);
315 copyResult = -1;
316 if (srcFd >= 0 && dstFd >= 0) {
317 copyResult = copyFileContents(dstRsrcName, dstFd,
318 srcRsrcName, srcFd);
319 (void) close(srcFd);
320 (void) close(dstFd);
321 }
322
323 if (copyResult != 0)
324 return -1;
325 }
326
327 free(srcRsrcName);
328 free(dstRsrcName);
329 }
330#endif
331
332 setPermissions(dst, pSrcStat, options);
333
334 printCopyMsg(src, dst, options);
335
336 return 0;
337}
338
339
340#ifdef HAVE_SYMLINKS
341/*
342 * Copy a symlink. This only happens if we're in "no derefence" mode,
343 * in which we copy the links rather than the files that are pointed at.
344 *
345 * We always discard the destination file. If it's a symlink already,
346 * we want to throw it out and replace it. If it's not a symlink, we
347 * need to trash it so we can create one.
348 */
349static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
350{
351 struct stat dstStat;
352 char linkBuf[PATH_MAX+1];
353 int statResult, nameLen;
354
355 assert(options & COPY_NO_DEREFERENCE);
356 DBUG(("--- copying symlink '%s' to '%s'\n", src, dst));
357
358 /* NOTE: we use lstat() here */
359 statResult = lstat(dst, &dstStat);
360 if (statResult == 0 && !S_ISREG(dstStat.st_mode)
361 && !S_ISLNK(dstStat.st_mode)
362 )
363 {
364 fprintf(stderr,
365 "acp: destination '%s' exists and is not regular or symlink\n",
366 dst);
367 return -1;
368 }
369
370 if (statResult == 0) {
371 if (isSameFile(pSrcStat, &dstStat)) {
372 fprintf(stderr, "acp: '%s' and '%s' are the same file\n",
373 src, dst);
374 return -1;
375 }
376 if (options & COPY_UPDATE_ONLY) {
377 if (!isSourceNewer(pSrcStat, &dstStat)) {
378 DBUG(("--- source is not newer: '%s'\n", src));
379 printNotNewerMsg(src, dst, options);
380 return 0;
381 }
382 }
383 }
384
385 /* extract the symlink contents */
386 nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1);
387 if (nameLen <= 0) {
388 fprintf(stderr, "acp: unable to read symlink '%s': %s\n",
389 src, strerror(errno));
390 return -1;
391 }
392 linkBuf[nameLen] = '\0';
393 DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf));
394
395 if (statResult == 0) {
396 DBUG(("--- removing '%s'\n", dst));
397 if (unlink(dst) != 0) {
398 fprintf(stderr, "acp: unable to remove '%s': %s\n",
399 dst, strerror(errno));
400 return -1;
401 }
402 }
403
404 if (symlink(linkBuf, dst) != 0) {
405 fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n",
406 dst, linkBuf, strerror(errno));
407 return -1;
408 }
409
410 /*
411 * There's no way to set the file date or access permissions, but
412 * it is possible to set the owner.
413 */
414 if (options & COPY_PERMISSIONS) {
415 if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0)
416 DBUG(("--- lchown failed: %s\n", strerror(errno)));
417 }
418
419 printCopyMsg(src, dst, options);
420
421 return 0;
422}
423#endif /* HAVE_SYMLINKS */
424
425/*
426 * Copy the contents of one directory to another. Both "src" and "dst"
427 * must be directories. We will create "dst" if it does not exist.
428 */
429int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
430{
431 int retVal = 0;
432 struct stat dstStat;
433 DIR* dir;
434 int cc, statResult;
435
436 DBUG(("--- copy dir '%s' to '%s'\n", src, dst));
437
438 statResult = stat(dst, &dstStat);
439 if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) {
440 fprintf(stderr,
441 "acp: destination '%s' exists and is not a directory\n", dst);
442 return -1;
443 } else if (statResult != 0 && errno != ENOENT) {
444 fprintf(stderr, "acp: unable to stat destination '%s'\n", dst);
445 return -1;
446 }
447
448 if (statResult == 0) {
449 if (isSameFile(pSrcStat, &dstStat)) {
450 fprintf(stderr,
451 "acp: cannot copy directory into itself ('%s' and '%s')\n",
452 src, dst);
453 return -1;
454 }
455 } else {
456 DBUG(("--- creating dir '%s'\n", dst));
457 cc = mkdir(dst, 0755);
458 if (cc != 0) {
459 fprintf(stderr, "acp: unable to create directory '%s': %s\n",
460 dst, strerror(errno));
461 return -1;
462 }
463
464 /* only print on mkdir */
465 printCopyMsg(src, dst, options);
466 }
467
468 /*
469 * Open the directory, and plow through its contents.
470 */
471 dir = opendir(src);
472 if (dir == NULL) {
473 fprintf(stderr, "acp: unable to open directory '%s': %s\n",
474 src, strerror(errno));
475 return -1;
476 }
477
478 while (1) {
479 struct dirent* ent;
480 char* srcFile;
481 char* dstFile;
482 int srcLen, dstLen, nameLen;
483
484 ent = readdir(dir);
485 if (ent == NULL)
486 break;
487
488 if (strcmp(ent->d_name, ".") == 0 ||
489 strcmp(ent->d_name, "..") == 0)
490 {
491 continue;
492 }
493
494 nameLen = strlen(ent->d_name);
495 srcLen = strlen(src);
496 dstLen = strlen(dst);
497
498 srcFile = malloc(srcLen +1 + nameLen +1);
499 memcpy(srcFile, src, srcLen);
500 srcFile[srcLen] = FSSEP;
501 memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1);
502
503 dstFile = malloc(dstLen +1 + nameLen +1);
504 memcpy(dstFile, dst, dstLen);
505 dstFile[dstLen] = FSSEP;
506 memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1);
507
508 if (copyFileRecursive(srcFile, dstFile, false, options) != 0)
509 retVal = -1; /* note failure and keep going */
510
511 free(srcFile);
512 free(dstFile);
513 }
514 closedir(dir);
515
516 setPermissions(dst, pSrcStat, options);
517
518 return retVal;
519}
520
521/*
522 * Do the actual copy. This is called recursively from copyDirectory().
523 *
524 * "dst" should only be a directory if "src" is also a directory.
525 *
526 * Returns 0 on success.
527 */
528static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options)
529{
530 char* srcExe = NULL;
531 char* dstExe = NULL;
532 char* dstDir = NULL;
533 struct stat srcStat;
534 int retVal = 0;
535 int statResult, statErrno;
536
537 /*
538 * Stat the source file. If it doesn't exist, fail.
539 */
540 if (options & COPY_NO_DEREFERENCE)
541 statResult = lstat(src, &srcStat);
542 else
543 statResult = stat(src, &srcStat);
544 statErrno = errno; /* preserve across .exe attempt */
545
546#ifdef WIN32_EXE
547 /*
548 * Here's the interesting part. Under Cygwin, if you have a file
549 * called "foo.exe", stat("foo", ...) will succeed, but open("foo", ...)
550 * will fail. We need to figure out what its name is supposed to be
551 * so we can create the correct destination file.
552 *
553 * If we don't have the "-e" flag set, we want "acp foo bar" to fail,
554 * not automatically find "foo.exe". That way, if we really were
555 * trying to copy "foo", it doesn't grab something we don't want.
556 */
557 if (isCmdLine && statResult == 0) {
558 int tmpFd;
559 tmpFd = open(src, O_RDONLY | O_BINARY, 0);
560 if (tmpFd < 0) {
561 statResult = -1;
562 statErrno = ENOENT;
563 } else {
564 (void) close(tmpFd);
565 }
566 }
567
568 /*
569 * If we didn't find the file, try it again with ".exe".
570 */
571 if (isCmdLine && statResult < 0 && statErrno == ENOENT && (options & COPY_TRY_EXE)) {
572 srcExe = malloc(strlen(src) + 4 +1);
573 strcpy(srcExe, src);
574 strcat(srcExe, ".exe");
575
576 if (options & COPY_NO_DEREFERENCE)
577 statResult = lstat(srcExe, &srcStat);
578 else
579 statResult = stat(srcExe, &srcStat);
580
581 if (statResult == 0 && !S_ISREG(srcStat.st_mode))
582 statResult = -1; /* fail, use original statErrno below */
583
584 if (statResult == 0) {
585 /* found a .exe, copy that instead */
586 dstExe = malloc(strlen(dst) + 4 +1);
587 strcpy(dstExe, dst);
588 strcat(dstExe, ".exe");
589
590 src = srcExe;
591 dst = dstExe;
592 } else {
593 DBUG(("--- couldn't find '%s' either\n", srcExe));
594 }
595 }
596#endif
597 if (statResult < 0) {
598 if (statErrno == ENOENT)
599 fprintf(stderr, "acp: file '%s' does not exist\n", src);
600 else
601 fprintf(stderr, "acp: unable to stat '%s': %s\n",
602 src, strerror(statErrno));
603 retVal = -1;
604 goto bail;
605 }
606
607 /*
608 * If "src" is a directory, ignore it if "recursive" isn't set.
609 *
610 * We want to create "dst" as a directory (or verify that it already
611 * exists as a directory), and then copy its contents.
612 */
613 if (S_ISDIR(srcStat.st_mode)) {
614 if (!(options & COPY_RECURSIVE)) {
615 fprintf(stderr, "acp: omitting directory '%s'\n", src);
616 } else {
617 retVal = copyDirectory(src, dst, &srcStat, options);
618 }
619#ifdef HAVE_SYMLINKS
620 } else if (S_ISLNK(srcStat.st_mode)) {
621 retVal = copySymlink(src, dst, &srcStat, options);
622#endif
623 } else if (S_ISREG(srcStat.st_mode)) {
624 retVal = copyRegular(src, dst, &srcStat, options);
625 } else {
626 fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n",
627 src, srcStat.st_mode);
628 retVal = -1;
629 }
630
631bail:
632 free(srcExe);
633 free(dstExe);
634 free(dstDir);
635 return retVal;
636}
637
638int copyFile(const char* src, const char* dst, unsigned int options)
639{
640 return copyFileRecursive(src, dst, true, options);
641}
642
643