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 <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <getopt.h>
18 #include <dirent.h>
19 #include <fcntl.h>
20 #include <utime.h>
21 #include <limits.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <host/CopyFile.h>
25
26 /*#define DEBUG_MSGS*/
27 #ifdef DEBUG_MSGS
28 # define DBUG(x) printf x
29 #else
30 # define DBUG(x) ((void)0)
31 #endif
32
33 #define FSSEP '/' /* filename separator char */
34
35
36 /*
37 * Process the command-line file arguments.
38 *
39 * Returns 0 on success.
40 */
process(int argc,char * const argv[],unsigned int options)41 int process(int argc, char* const argv[], unsigned int options)
42 {
43 int retVal = 0;
44 int i;
45 char* stripDest = NULL;
46 int stripDestLen;
47 bool destMustBeDir = false;
48 struct stat sb;
49
50 assert(argc >= 2);
51
52 /*
53 * Check for and trim a trailing slash on the last arg.
54 *
55 * It's useful to be able to say "cp foo bar/" when you want to copy
56 * a single file into a directory. If you say "cp foo bar", and "bar"
57 * does not exist, it will create "bar", when what you really wanted
58 * was for the cp command to fail with "directory does not exist".
59 */
60 stripDestLen = strlen(argv[argc-1]);
61 stripDest = malloc(stripDestLen+1);
62 memcpy(stripDest, argv[argc-1], stripDestLen+1);
63 if (stripDest[stripDestLen-1] == FSSEP) {
64 stripDest[--stripDestLen] = '\0';
65 destMustBeDir = true;
66 }
67
68 if (argc > 2)
69 destMustBeDir = true;
70
71 /*
72 * Start with a quick check to ensure that, if we're expecting to copy
73 * to a directory, the target already exists and is actually a directory.
74 * It's okay if it's a symlink to a directory.
75 *
76 * If it turns out to be a directory, go ahead and raise the
77 * destMustBeDir flag so we do some path concatenation below.
78 */
79 if (stat(stripDest, &sb) < 0) {
80 if (destMustBeDir) {
81 if (errno == ENOENT)
82 fprintf(stderr,
83 "acp: destination directory '%s' does not exist\n",
84 stripDest);
85 else
86 fprintf(stderr, "acp: unable to stat dest dir\n");
87 retVal = 1;
88 goto bail;
89 }
90 } else {
91 if (S_ISDIR(sb.st_mode)) {
92 DBUG(("--- dest exists and is a dir, setting flag\n"));
93 destMustBeDir = true;
94 } else if (destMustBeDir) {
95 fprintf(stderr,
96 "acp: destination '%s' is not a directory\n",
97 stripDest);
98 retVal = 1;
99 goto bail;
100 }
101 }
102
103 /*
104 * Copying files.
105 *
106 * Strip trailing slashes off. They shouldn't be there, but
107 * sometimes file completion will put them in for directories.
108 *
109 * The observed behavior of GNU and BSD cp is that they print warnings
110 * if something fails, but continue on. If any part fails, the command
111 * exits with an error status.
112 */
113 for (i = 0; i < argc-1; i++) {
114 const char* srcName;
115 char* src;
116 char* dst;
117 int copyResult;
118 int srcLen;
119
120 /* make a copy of the source name, and strip trailing '/' */
121 srcLen = strlen(argv[i]);
122 src = malloc(srcLen+1);
123 memcpy(src, argv[i], srcLen+1);
124
125 if (src[srcLen-1] == FSSEP)
126 src[--srcLen] = '\0';
127
128 /* find just the name part */
129 srcName = strrchr(src, FSSEP);
130 if (srcName == NULL) {
131 srcName = src;
132 } else {
133 srcName++;
134 assert(*srcName != '\0');
135 }
136
137 if (destMustBeDir) {
138 /* concatenate dest dir and src name */
139 int srcNameLen = strlen(srcName);
140
141 dst = malloc(stripDestLen +1 + srcNameLen +1);
142 memcpy(dst, stripDest, stripDestLen);
143 dst[stripDestLen] = FSSEP;
144 memcpy(dst + stripDestLen+1, srcName, srcNameLen+1);
145 } else {
146 /* simple */
147 dst = stripDest;
148 }
149
150 /*
151 * Copy the source to the destination.
152 */
153 copyResult = copyFile(src, dst, options);
154
155 if (copyResult != 0)
156 retVal = 1;
157
158 free(src);
159 if (dst != stripDest)
160 free(dst);
161 }
162
163 bail:
164 free(stripDest);
165 return retVal;
166 }
167
168 /*
169 * Set up the options.
170 */
main(int argc,char * const argv[])171 int main(int argc, char* const argv[])
172 {
173 bool wantUsage;
174 int ic, retVal;
175 int verboseLevel;
176 unsigned int options;
177
178 verboseLevel = 0;
179 options = 0;
180 wantUsage = false;
181
182 while (1) {
183 ic = getopt(argc, argv, "defprtuv");
184 if (ic < 0)
185 break;
186
187 switch (ic) {
188 case 'd':
189 options |= COPY_NO_DEREFERENCE;
190 break;
191 case 'e':
192 options |= COPY_TRY_EXE;
193 break;
194 case 'f':
195 options |= COPY_FORCE;
196 break;
197 case 'p':
198 options |= COPY_PERMISSIONS;
199 break;
200 case 't':
201 options |= COPY_TIMESTAMPS;
202 break;
203 case 'r':
204 options |= COPY_RECURSIVE;
205 break;
206 case 'u':
207 options |= COPY_UPDATE_ONLY;
208 break;
209 case 'v':
210 verboseLevel++;
211 break;
212 default:
213 fprintf(stderr, "Unexpected arg -%c\n", ic);
214 wantUsage = true;
215 break;
216 }
217
218 if (wantUsage)
219 break;
220 }
221
222 options |= verboseLevel & COPY_VERBOSE_MASK;
223
224 if (optind == argc-1) {
225 fprintf(stderr, "acp: missing destination file\n");
226 return 2;
227 } else if (optind+2 > argc)
228 wantUsage = true;
229
230 if (wantUsage) {
231 fprintf(stderr, "Usage: acp [OPTION]... SOURCE DEST\n");
232 fprintf(stderr, " or: acp [OPTION]... SOURCE... DIRECTORY\n");
233 fprintf(stderr, "\nOptions:\n");
234 fprintf(stderr, " -d never follow (dereference) symbolic links\n");
235 fprintf(stderr, " -e if source file doesn't exist, try adding "
236 "'.exe' [Win32 only]\n");
237 fprintf(stderr, " -f use force, removing existing file if it's "
238 "not writeable\n");
239 fprintf(stderr, " -p preserve mode, ownership\n");
240 fprintf(stderr, " -r recursive copy\n");
241 fprintf(stderr, " -t preserve timestamps\n");
242 fprintf(stderr, " -u update only: don't copy if dest is newer\n");
243 fprintf(stderr, " -v verbose output (-vv is more verbose)\n");
244 return 2;
245 }
246
247 retVal = process(argc-optind, argv+optind, options);
248 DBUG(("EXIT: %d\n", retVal));
249 return retVal;
250 }
251
252