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