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