cp.c revision 1.3 1 /*
2 * Copyright (c) 1988 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * David Hitz of Auspex Systems Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #ifndef lint
38 char copyright[] =
39 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
40 All rights reserved.\n";
41 #endif /* not lint */
42
43 #ifndef lint
44 static char sccsid[] = "@(#)cp.c 5.24 (Berkeley) 5/6/91";
45 static char rcsid[] = "$Header: /tank/opengrok/rsync2/NetBSD/src/bin/cp/cp.c,v 1.3 1993/03/23 00:23:42 cgd Exp $";
46 #endif /* not lint */
47
48 /*
49 * cp copies source files to target files.
50 *
51 * The global PATH_T structures "to" and "from" always contain paths to the
52 * current source and target files, respectively. Since cp does not change
53 * directories, these paths can be either absolute or dot-realative.
54 *
55 * The basic algorithm is to initialize "to" and "from", and then call the
56 * recursive copy() function to do the actual work. If "from" is a file,
57 * copy copies the data. If "from" is a directory, copy creates the
58 * corresponding "to" directory, and calls itself recursively on all of
59 * the entries in the "from" directory.
60 */
61
62 #include <sys/param.h>
63 #include <sys/stat.h>
64 #include <sys/time.h>
65 #include <dirent.h>
66 #include <fcntl.h>
67 #include <errno.h>
68 #include <unistd.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include "cp.h"
73
74 PATH_T from = { from.p_path, "" };
75 PATH_T to = { to.p_path, "" };
76
77 uid_t myuid;
78 int exit_val, myumask;
79 int iflag, pflag, orflag, rflag;
80 int (*statfcn)();
81 char *buf, *progname;
82
83 main(argc, argv)
84 int argc;
85 char **argv;
86 {
87 extern int optind;
88 struct stat to_stat;
89 register int c, r;
90 int symfollow, lstat(), stat();
91 char *old_to, *p;
92
93 /*
94 * The utility cp(1) is used by mv(1) -- except for usage statements,
95 * print the "called as" program name.
96 */
97 progname = (p = rindex(*argv,'/')) ? ++p : *argv;
98
99 symfollow = 0;
100 while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
101 switch ((char)c) {
102 case 'f':
103 iflag = 0;
104 break;
105 case 'h':
106 symfollow = 1;
107 break;
108 case 'i':
109 iflag = isatty(fileno(stdin));
110 break;
111 case 'p':
112 pflag = 1;
113 break;
114 case 'R':
115 rflag = 1;
116 break;
117 case 'r':
118 orflag = 1;
119 break;
120 case '?':
121 default:
122 usage();
123 break;
124 }
125 }
126 argc -= optind;
127 argv += optind;
128
129 if (argc < 2)
130 usage();
131
132 if (rflag && orflag) {
133 (void)fprintf(stderr,
134 "cp: the -R and -r options are mutually exclusive.\n");
135 exit(1);
136 }
137
138 buf = (char *)malloc(MAXBSIZE);
139 if (!buf) {
140 (void)fprintf(stderr, "%s: out of space.\n", progname);
141 exit(1);
142 }
143
144 myuid = getuid();
145
146 /* copy the umask for explicit mode setting */
147 myumask = umask(0);
148 (void)umask(myumask);
149
150 /* consume last argument first. */
151 if (!path_set(&to, argv[--argc]))
152 exit(1);
153
154 statfcn = symfollow || !rflag ? stat : lstat;
155
156 /*
157 * Cp has two distinct cases:
158 *
159 * % cp [-rip] source target
160 * % cp [-rip] source1 ... directory
161 *
162 * In both cases, source can be either a file or a directory.
163 *
164 * In (1), the target becomes a copy of the source. That is, if the
165 * source is a file, the target will be a file, and likewise for
166 * directories.
167 *
168 * In (2), the real target is not directory, but "directory/source".
169 */
170
171 r = stat(to.p_path, &to_stat);
172 if (r == -1 && errno != ENOENT) {
173 error(to.p_path);
174 exit(1);
175 }
176 if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
177 /*
178 * Case (1). Target is not a directory.
179 */
180 if (argc > 1) {
181 usage();
182 exit(1);
183 }
184 if (!path_set(&from, *argv))
185 exit(1);
186 copy();
187 }
188 else {
189 /*
190 * Case (2). Target is a directory.
191 */
192 for (;; ++argv) {
193 if (!path_set(&from, *argv)) {
194 exit_val = 1;
195 continue;
196 }
197 old_to = path_append(&to, path_basename(&from), -1);
198 if (!old_to) {
199 exit_val = 1;
200 continue;
201 }
202 copy();
203 if (!--argc)
204 break;
205 path_restore(&to, old_to);
206 }
207 }
208 exit(exit_val);
209 }
210
211 /* copy file or directory at "from" to "to". */
212 copy()
213 {
214 struct stat from_stat, to_stat;
215 int dne, statval;
216
217 statval = statfcn(from.p_path, &from_stat);
218 if (statval == -1) {
219 error(from.p_path);
220 return;
221 }
222
223 /* not an error, but need to remember it happened */
224 if (stat(to.p_path, &to_stat) == -1)
225 dne = 1;
226 else {
227 if (to_stat.st_dev == from_stat.st_dev &&
228 to_stat.st_ino == from_stat.st_ino) {
229 (void)fprintf(stderr,
230 "%s: %s and %s are identical (not copied).\n",
231 progname, to.p_path, from.p_path);
232 exit_val = 1;
233 return;
234 }
235 dne = 0;
236 }
237
238 switch(from_stat.st_mode & S_IFMT) {
239 case S_IFLNK:
240 copy_link(!dne);
241 return;
242 case S_IFDIR:
243 if (!rflag && !orflag) {
244 (void)fprintf(stderr,
245 "%s: %s is a directory (not copied).\n",
246 progname, from.p_path);
247 exit_val = 1;
248 return;
249 }
250 if (dne) {
251 /*
252 * If the directory doesn't exist, create the new
253 * one with the from file mode plus owner RWX bits,
254 * modified by the umask. Trade-off between being
255 * able to write the directory (if from directory is
256 * 555) and not causing a permissions race. If the
257 * umask blocks owner writes cp fails.
258 */
259 if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
260 error(to.p_path);
261 return;
262 }
263 }
264 else if (!S_ISDIR(to_stat.st_mode) != S_IFDIR) {
265 (void)fprintf(stderr, "%s: %s: not a directory.\n",
266 progname, to.p_path);
267 return;
268 }
269 copy_dir();
270 /*
271 * If not -p and directory didn't exist, set it to be the
272 * same as the from directory, umodified by the umask;
273 * arguably wrong, but it's been that way forever.
274 */
275 if (pflag)
276 setfile(&from_stat, 0);
277 else if (dne)
278 (void)chmod(to.p_path, from_stat.st_mode);
279 return;
280 case S_IFCHR:
281 case S_IFBLK:
282 if (rflag) {
283 copy_special(&from_stat, !dne);
284 return;
285 }
286 break;
287 case S_IFIFO:
288 if (rflag) {
289 copy_fifo(&from_stat, !dne);
290 return;
291 }
292 break;
293 }
294 copy_file(&from_stat, dne);
295 }
296
297 copy_file(fs, dne)
298 struct stat *fs;
299 int dne;
300 {
301 register int from_fd, to_fd, rcount, wcount;
302 struct stat to_stat;
303
304 if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
305 error(from.p_path);
306 return;
307 }
308
309 /*
310 * If the file exists and we're interactive, verify with the user.
311 * If the file DNE, set the mode to be the from file, minus setuid
312 * bits, modified by the umask; arguably wrong, but it makes copying
313 * executables work right and it's been that way forever. (The
314 * other choice is 666 or'ed with the execute bits on the from file
315 * modified by the umask.)
316 */
317 if (!dne) {
318 if (iflag) {
319 int checkch, ch;
320
321 (void)fprintf(stderr, "overwrite %s? ", to.p_path);
322 checkch = ch = getchar();
323 while (ch != '\n' && ch != EOF)
324 ch = getchar();
325 if (checkch != 'y') {
326 (void)close(from_fd);
327 return;
328 }
329 }
330 to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
331 } else
332 to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
333 fs->st_mode & ~(S_ISUID|S_ISGID));
334
335 if (to_fd == -1) {
336 error(to.p_path);
337 (void)close(from_fd);
338 return;
339 }
340
341 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
342 wcount = write(to_fd, buf, rcount);
343 if (rcount != wcount || wcount == -1) {
344 error(to.p_path);
345 break;
346 }
347 }
348 if (rcount < 0)
349 error(from.p_path);
350 if (pflag)
351 setfile(fs, to_fd);
352 /*
353 * If the source was setuid or setgid, lose the bits unless the
354 * copy is owned by the same user and group.
355 */
356 else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
357 if (fstat(to_fd, &to_stat))
358 error(to.p_path);
359 #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
360 else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
361 fs->st_mode & RETAINBITS & ~myumask))
362 error(to.p_path);
363 (void)close(from_fd);
364 if (close(to_fd))
365 error(to.p_path);
366 }
367
368 copy_dir()
369 {
370 struct stat from_stat;
371 struct dirent *dp, **dir_list;
372 register int dir_cnt, i;
373 char *old_from, *old_to;
374
375 dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
376 if (dir_cnt == -1) {
377 (void)fprintf(stderr, "%s: can't read directory %s.\n",
378 progname, from.p_path);
379 exit_val = 1;
380 }
381
382 /*
383 * Instead of handling directory entries in the order they appear
384 * on disk, do non-directory files before directory files.
385 * There are two reasons to do directories last. The first is
386 * efficiency. Files tend to be in the same cylinder group as
387 * their parent, whereas directories tend not to be. Copying files
388 * all at once reduces seeking. Second, deeply nested tree's
389 * could use up all the file descriptors if we didn't close one
390 * directory before recursivly starting on the next.
391 */
392 /* copy files */
393 for (i = 0; i < dir_cnt; ++i) {
394 dp = dir_list[i];
395 if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
396 && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
397 goto done;
398 old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
399 if (!old_from) {
400 exit_val = 1;
401 goto done;
402 }
403
404 if (statfcn(from.p_path, &from_stat) < 0) {
405 error(dp->d_name);
406 path_restore(&from, old_from);
407 goto done;
408 }
409 if (S_ISDIR(from_stat.st_mode)) {
410 path_restore(&from, old_from);
411 continue;
412 }
413 old_to = path_append(&to, dp->d_name, (int)dp->d_namlen);
414 if (old_to) {
415 copy();
416 path_restore(&to, old_to);
417 } else
418 exit_val = 1;
419 path_restore(&from, old_from);
420 done: dir_list[i] = NULL;
421 (void)free((void *)dp);
422 }
423
424 /* copy directories */
425 for (i = 0; i < dir_cnt; ++i) {
426 dp = dir_list[i];
427 if (!dp)
428 continue;
429 old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
430 if (!old_from) {
431 exit_val = 1;
432 (void)free((void *)dp);
433 continue;
434 }
435 old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
436 if (!old_to) {
437 exit_val = 1;
438 (void)free((void *)dp);
439 path_restore(&from, old_from);
440 continue;
441 }
442 copy();
443 free((void *)dp);
444 path_restore(&from, old_from);
445 path_restore(&to, old_to);
446 }
447 free((void *)dir_list);
448 }
449
450 copy_link(exists)
451 int exists;
452 {
453 int len;
454 char link[MAXPATHLEN];
455
456 if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
457 error(from.p_path);
458 return;
459 }
460 link[len] = '\0';
461 if (exists && unlink(to.p_path)) {
462 error(to.p_path);
463 return;
464 }
465 if (symlink(link, to.p_path)) {
466 error(link);
467 return;
468 }
469 }
470
471 copy_fifo(from_stat, exists)
472 struct stat *from_stat;
473 int exists;
474 {
475 if (exists && unlink(to.p_path)) {
476 error(to.p_path);
477 return;
478 }
479 if (mkfifo(to.p_path, from_stat->st_mode)) {
480 error(to.p_path);
481 return;
482 }
483 if (pflag)
484 setfile(from_stat, 0);
485 }
486
487 copy_special(from_stat, exists)
488 struct stat *from_stat;
489 int exists;
490 {
491 if (exists && unlink(to.p_path)) {
492 error(to.p_path);
493 return;
494 }
495 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
496 error(to.p_path);
497 return;
498 }
499 if (pflag)
500 setfile(from_stat, 0);
501 }
502
503 setfile(fs, fd)
504 register struct stat *fs;
505 int fd;
506 {
507 static struct timeval tv[2];
508 char path[100];
509
510 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
511
512 tv[0].tv_sec = fs->st_atime;
513 tv[1].tv_sec = fs->st_mtime;
514 if (utimes(to.p_path, tv)) {
515 (void)snprintf(path, sizeof(path), "utimes: %s", to.p_path);
516 error(path);
517 }
518 /*
519 * Changing the ownership probably won't succeed, unless we're root
520 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
521 * the mode; current BSD behavior is to remove all setuid bits on
522 * chown. If chown fails, lose setuid/setgid bits.
523 */
524 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
525 chown(to.p_path, fs->st_uid, fs->st_gid)) {
526 if (errno != EPERM) {
527 (void)snprintf(path, sizeof(path),
528 "chown: %s", to.p_path);
529 error(path);
530 }
531 fs->st_mode &= ~(S_ISUID|S_ISGID);
532 }
533 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
534 (void)snprintf(path, sizeof(path), "chown: %s", to.p_path);
535 error(path);
536 }
537 }
538
539 error(s)
540 char *s;
541 {
542 exit_val = 1;
543 (void)fprintf(stderr, "%s: %s: %s\n", progname, s, strerror(errno));
544 }
545
546 usage()
547 {
548 (void)fprintf(stderr,
549 "usage: cp [-Rfhip] src target;\n or: cp [-Rfhip] src1 ... srcN directory\n");
550 exit(1);
551 }
552