153766Selan /*-
2*66606Sbostic * Copyright (c) 1991, 1993, 1994
360658Sbostic * The Regents of the University of California. All rights reserved.
453766Selan *
553766Selan * %sccs.include.redist.c%
653766Selan */
753766Selan
853766Selan #ifndef lint
9*66606Sbostic static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 04/01/94";
1053766Selan #endif /* not lint */
1153766Selan
1253766Selan #include <sys/param.h>
1353766Selan #include <sys/stat.h>
1453766Selan #include <sys/mman.h>
1553766Selan #include <sys/time.h>
1656975Sbostic
17*66606Sbostic #include <err.h>
1856975Sbostic #include <errno.h>
1953766Selan #include <fcntl.h>
2056975Sbostic #include <fts.h>
2153607Selan #include <stdio.h>
2253766Selan #include <stdlib.h>
2359456Sbostic #include <string.h>
2456975Sbostic #include <unistd.h>
2556975Sbostic
2653766Selan #include "extern.h"
2753607Selan
28*66606Sbostic int
copy_file(entp,dne)2953766Selan copy_file(entp, dne)
3053766Selan FTSENT *entp;
3153607Selan int dne;
3253607Selan {
3353607Selan static char buf[MAXBSIZE];
3453766Selan struct stat to_stat, *fs;
35*66606Sbostic int ch, checkch, from_fd, rcount, rval, to_fd, wcount;
36*66606Sbostic #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
3753607Selan char *p;
38*66606Sbostic #endif
3953766Selan
4053766Selan if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
41*66606Sbostic warn("%s", entp->fts_path);
42*66606Sbostic return (1);
4353607Selan }
4453607Selan
4553766Selan fs = entp->fts_statp;
4653766Selan
4753607Selan /*
4853607Selan * If the file exists and we're interactive, verify with the user.
4953607Selan * If the file DNE, set the mode to be the from file, minus setuid
5053607Selan * bits, modified by the umask; arguably wrong, but it makes copying
5153607Selan * executables work right and it's been that way forever. (The
5253607Selan * other choice is 666 or'ed with the execute bits on the from file
5353607Selan * modified by the umask.)
5453607Selan */
5553607Selan if (!dne) {
5653607Selan if (iflag) {
5753607Selan (void)fprintf(stderr, "overwrite %s? ", to.p_path);
5853607Selan checkch = ch = getchar();
5953607Selan while (ch != '\n' && ch != EOF)
6053607Selan ch = getchar();
6153607Selan if (checkch != 'y') {
6253607Selan (void)close(from_fd);
63*66606Sbostic return (0);
6453607Selan }
6553607Selan }
66*66606Sbostic to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
6753607Selan } else
68*66606Sbostic to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
69*66606Sbostic fs->st_mode & ~(S_ISUID | S_ISGID));
7053607Selan
7153607Selan if (to_fd == -1) {
72*66606Sbostic warn("%s", to.p_path);
7353607Selan (void)close(from_fd);
74*66606Sbostic return (1);;
7553607Selan }
7653607Selan
77*66606Sbostic rval = 0;
78*66606Sbostic
7953607Selan /*
8053607Selan * Mmap and write if less than 8M (the limit is so we don't totally
8153607Selan * trash memory on big files. This is really a minor hack, but it
8253607Selan * wins some CPU back.
8353607Selan */
84*66606Sbostic #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
8553607Selan if (fs->st_size <= 8 * 1048576) {
8654191Sbostic if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
87*66606Sbostic 0, from_fd, (off_t)0)) == (char *)-1) {
88*66606Sbostic warn("%s", entp->fts_path);
89*66606Sbostic rval = 1;
90*66606Sbostic } else {
91*66606Sbostic if (write(to_fd, p, fs->st_size) != fs->st_size) {
92*66606Sbostic warn("%s", to.p_path);
93*66606Sbostic rval = 1;
94*66606Sbostic }
95*66606Sbostic /* Some systems don't unmap on close(2). */
96*66606Sbostic if (munmap(p, fs->st_size) < 0) {
97*66606Sbostic warn("%s", entp->fts_path);
98*66606Sbostic rval = 1;
99*66606Sbostic }
100*66606Sbostic }
10155928Sbostic } else
10255928Sbostic #endif
10355928Sbostic {
10453607Selan while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
10553607Selan wcount = write(to_fd, buf, rcount);
10653607Selan if (rcount != wcount || wcount == -1) {
107*66606Sbostic warn("%s", to.p_path);
108*66606Sbostic rval = 1;
10953607Selan break;
11053607Selan }
11153607Selan }
112*66606Sbostic if (rcount < 0) {
113*66606Sbostic warn("%s", entp->fts_path);
114*66606Sbostic rval = 1;
115*66606Sbostic }
11653607Selan }
117*66606Sbostic
118*66606Sbostic /* If the copy went bad, lose the file. */
119*66606Sbostic if (rval == 1) {
120*66606Sbostic (void)unlink(to.p_path);
121*66606Sbostic (void)close(from_fd);
122*66606Sbostic (void)close(to_fd);
123*66606Sbostic return (1);
124*66606Sbostic }
125*66606Sbostic
126*66606Sbostic if (pflag && setfile(fs, to_fd))
127*66606Sbostic rval = 1;
12853607Selan /*
12953607Selan * If the source was setuid or setgid, lose the bits unless the
13053607Selan * copy is owned by the same user and group.
13153607Selan */
132*66606Sbostic #define RETAINBITS \
133*66606Sbostic (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
134*66606Sbostic else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid)
135*66606Sbostic if (fstat(to_fd, &to_stat)) {
136*66606Sbostic warn("%s", to.p_path);
137*66606Sbostic rval = 1;
138*66606Sbostic } else if (fs->st_gid == to_stat.st_gid &&
139*66606Sbostic fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
140*66606Sbostic warn("%s", to.p_path);
141*66606Sbostic rval = 1;
142*66606Sbostic }
14353607Selan (void)close(from_fd);
144*66606Sbostic if (close(to_fd)) {
145*66606Sbostic warn("%s", to.p_path);
146*66606Sbostic rval = 1;
147*66606Sbostic }
148*66606Sbostic return (rval);
14953607Selan }
15053607Selan
151*66606Sbostic int
copy_link(p,exists)15253766Selan copy_link(p, exists)
15353766Selan FTSENT *p;
15453607Selan int exists;
15553607Selan {
15653607Selan int len;
15753607Selan char link[MAXPATHLEN];
15853607Selan
15953766Selan if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
160*66606Sbostic warn("readlink: %s", p->fts_path);
161*66606Sbostic return (1);
16253607Selan }
16353607Selan link[len] = '\0';
16453607Selan if (exists && unlink(to.p_path)) {
165*66606Sbostic warn("unlink: %s", to.p_path);
166*66606Sbostic return (1);
16753607Selan }
16853607Selan if (symlink(link, to.p_path)) {
169*66606Sbostic warn("symlink: %s", link);
170*66606Sbostic return (1);
17153607Selan }
172*66606Sbostic return (0);
17353607Selan }
17453607Selan
175*66606Sbostic int
copy_fifo(from_stat,exists)17653607Selan copy_fifo(from_stat, exists)
17753607Selan struct stat *from_stat;
17853607Selan int exists;
17953607Selan {
18053607Selan if (exists && unlink(to.p_path)) {
181*66606Sbostic warn("unlink: %s", to.p_path);
182*66606Sbostic return (1);
18353607Selan }
18453607Selan if (mkfifo(to.p_path, from_stat->st_mode)) {
185*66606Sbostic warn("mkfifo: %s", to.p_path);
186*66606Sbostic return (1);
18753607Selan }
188*66606Sbostic return (pflag ? setfile(from_stat, 0) : 0);
18953607Selan }
19053607Selan
191*66606Sbostic int
copy_special(from_stat,exists)19253607Selan copy_special(from_stat, exists)
19353607Selan struct stat *from_stat;
19453607Selan int exists;
19553607Selan {
19653607Selan if (exists && unlink(to.p_path)) {
197*66606Sbostic warn("unlink: %s", to.p_path);
198*66606Sbostic return (1);
19953607Selan }
200*66606Sbostic if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
201*66606Sbostic warn("mknod: %s", to.p_path);
202*66606Sbostic return (1);
20353607Selan }
204*66606Sbostic return (pflag ? setfile(from_stat, 0) : 0);
20553607Selan }
20653607Selan
20753607Selan
208*66606Sbostic int
setfile(fs,fd)20953607Selan setfile(fs, fd)
21053607Selan register struct stat *fs;
21153607Selan int fd;
21253607Selan {
21353607Selan static struct timeval tv[2];
214*66606Sbostic int rval;
21553607Selan
216*66606Sbostic rval = 0;
217*66606Sbostic fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
21853607Selan
21956975Sbostic TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
22056975Sbostic TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
221*66606Sbostic if (utimes(to.p_path, tv)) {
222*66606Sbostic warn("utimes: %s", to.p_path);
223*66606Sbostic rval = 1;
224*66606Sbostic }
22553607Selan /*
22653607Selan * Changing the ownership probably won't succeed, unless we're root
22753607Selan * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
22853607Selan * the mode; current BSD behavior is to remove all setuid bits on
22953607Selan * chown. If chown fails, lose setuid/setgid bits.
23053607Selan */
23153607Selan if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
23253607Selan chown(to.p_path, fs->st_uid, fs->st_gid)) {
233*66606Sbostic if (errno != EPERM) {
234*66606Sbostic warn("chown: %s", to.p_path);
235*66606Sbostic rval = 1;
236*66606Sbostic }
237*66606Sbostic fs->st_mode &= ~(S_ISUID | S_ISGID);
23853607Selan }
239*66606Sbostic if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
240*66606Sbostic warn("chown: %s", to.p_path);
241*66606Sbostic rval = 1;
242*66606Sbostic }
24354540Sbostic
244*66606Sbostic if (fd ?
245*66606Sbostic fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
246*66606Sbostic warn("chflags: %s", to.p_path);
247*66606Sbostic rval = 1;
248*66606Sbostic }
249*66606Sbostic return (rval);
25053607Selan }
25153607Selan
25253607Selan void
usage()25353607Selan usage()
25453607Selan {
255*66606Sbostic (void)fprintf(stderr, "%s\n%s\n",
256*66606Sbostic "usage: cp [-R [-H | -L | -P] [-fip] src target",
257*66606Sbostic " cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory");
25853607Selan exit(1);
25953607Selan }
260