utils.c revision 8855
1118611Snjl/*-
2118611Snjl * Copyright (c) 1991, 1993, 1994
3245582Sjkim *	The Regents of the University of California.  All rights reserved.
4118611Snjl *
5118611Snjl * Redistribution and use in source and binary forms, with or without
6118611Snjl * modification, are permitted provided that the following conditions
7217365Sjkim * are met:
8245582Sjkim * 1. Redistributions of source code must retain the above copyright
9118611Snjl *    notice, this list of conditions and the following disclaimer.
10118611Snjl * 2. Redistributions in binary form must reproduce the above copyright
11217365Sjkim *    notice, this list of conditions and the following disclaimer in the
12217365Sjkim *    documentation and/or other materials provided with the distribution.
13217365Sjkim * 3. All advertising materials mentioning features or use of this software
14217365Sjkim *    must display the following acknowledgement:
15217365Sjkim *	This product includes software developed by the University of
16217365Sjkim *	California, Berkeley and its contributors.
17217365Sjkim * 4. Neither the name of the University nor the names of its contributors
18217365Sjkim *    may be used to endorse or promote products derived from this software
19217365Sjkim *    without specific prior written permission.
20217365Sjkim *
21217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24217365Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31217365Sjkim * SUCH DAMAGE.
32217365Sjkim *
33217365Sjkim *	$Id: utils.c,v 1.2 1994/09/24 02:53:42 davidg Exp $
34217365Sjkim */
35217365Sjkim
36217365Sjkim#ifndef lint
37217365Sjkimstatic char sccsid[] = "@(#)utils.c	8.3 (Berkeley) 4/1/94";
38217365Sjkim#endif /* not lint */
39217365Sjkim
40217365Sjkim#include <sys/param.h>
41217365Sjkim#include <sys/stat.h>
42217365Sjkim#include <sys/mman.h>
43118611Snjl#include <sys/time.h>
44118611Snjl
45151937Sjkim#include <err.h>
46118611Snjl#include <errno.h>
47193529Sjkim#include <fcntl.h>
48193529Sjkim#include <fts.h>
49193529Sjkim#include <stdio.h>
50193529Sjkim#include <stdlib.h>
51118611Snjl#include <string.h>
52118611Snjl#include <unistd.h>
53118611Snjl
54118611Snjl#include "extern.h"
55118611Snjl
56151937Sjkimint
57118611Snjlcopy_file(entp, dne)
58151937Sjkim	FTSENT *entp;
59167802Sjkim	int dne;
60167802Sjkim{
61167802Sjkim	static char buf[MAXBSIZE];
62167802Sjkim	struct stat to_stat, *fs;
63167802Sjkim	int ch, checkch, from_fd, rcount, rval, to_fd, wcount;
64151937Sjkim#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
65212761Sjkim	char *p;
66193529Sjkim#endif
67193529Sjkim
68193529Sjkim	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
69193529Sjkim		warn("%s", entp->fts_path);
70118611Snjl		return (1);
71118611Snjl	}
72245582Sjkim
73118611Snjl	fs = entp->fts_statp;
74245582Sjkim
75118611Snjl	/*
76193529Sjkim	 * If the file exists and we're interactive, verify with the user.
77193529Sjkim	 * If the file DNE, set the mode to be the from file, minus setuid
78245582Sjkim	 * bits, modified by the umask; arguably wrong, but it makes copying
79245582Sjkim	 * executables work right and it's been that way forever.  (The
80245582Sjkim	 * other choice is 666 or'ed with the execute bits on the from file
81193529Sjkim	 * modified by the umask.)
82193529Sjkim	 */
83193529Sjkim	if (!dne) {
84167802Sjkim		if (iflag) {
85245582Sjkim			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
86118611Snjl			checkch = ch = getchar();
87118611Snjl			while (ch != '\n' && ch != EOF)
88118611Snjl				ch = getchar();
89245582Sjkim			if (checkch != 'y') {
90118611Snjl				(void)close(from_fd);
91245582Sjkim				return (0);
92245582Sjkim			}
93151937Sjkim		}
94118611Snjl		to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
95118611Snjl	} else
96118611Snjl		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
97118611Snjl		    fs->st_mode & ~(S_ISUID | S_ISGID));
98118611Snjl
99167802Sjkim	if (to_fd == -1) {
100167802Sjkim		warn("%s", to.p_path);
101167802Sjkim		(void)close(from_fd);
102167802Sjkim		return (1);;
103167802Sjkim	}
104167802Sjkim
105167802Sjkim	rval = 0;
106167802Sjkim
107167802Sjkim	/*
108167802Sjkim	 * Mmap and write if less than 8M (the limit is so we don't totally
109167802Sjkim	 * trash memory on big files.  This is really a minor hack, but it
110167802Sjkim	 * wins some CPU back.
111167802Sjkim	 */
112167802Sjkim#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
113167802Sjkim	if (fs->st_size <= 8 * 1048576) {
114167802Sjkim		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
115167802Sjkim		    0, from_fd, (off_t)0)) == (char *)-1) {
116167802Sjkim			warn("%s", entp->fts_path);
117167802Sjkim			rval = 1;
118167802Sjkim		} else {
119167802Sjkim			if (write(to_fd, p, fs->st_size) != fs->st_size) {
120167802Sjkim				warn("%s", to.p_path);
121167802Sjkim				rval = 1;
122167802Sjkim			}
123167802Sjkim			/* Some systems don't unmap on close(2). */
124167802Sjkim			if (munmap(p, fs->st_size) < 0) {
125167802Sjkim				warn("%s", entp->fts_path);
126167802Sjkim				rval = 1;
127167802Sjkim			}
128167802Sjkim		}
129167802Sjkim	} else
130167802Sjkim#endif
131167802Sjkim	{
132167802Sjkim		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
133167802Sjkim			wcount = write(to_fd, buf, rcount);
134167802Sjkim			if (rcount != wcount || wcount == -1) {
135167802Sjkim				warn("%s", to.p_path);
136167802Sjkim				rval = 1;
137167802Sjkim				break;
138167802Sjkim			}
139167802Sjkim		}
140167802Sjkim		if (rcount < 0) {
141167802Sjkim			warn("%s", entp->fts_path);
142167802Sjkim			rval = 1;
143167802Sjkim		}
144167802Sjkim	}
145167802Sjkim
146167802Sjkim	/* If the copy went bad, lose the file. */
147167802Sjkim	if (rval == 1) {
148167802Sjkim		(void)unlink(to.p_path);
149167802Sjkim		(void)close(from_fd);
150167802Sjkim		(void)close(to_fd);
151167802Sjkim		return (1);
152167802Sjkim	}
153167802Sjkim
154167802Sjkim	if (pflag && setfile(fs, to_fd))
155167802Sjkim		rval = 1;
156167802Sjkim	/*
157167802Sjkim	 * If the source was setuid or setgid, lose the bits unless the
158167802Sjkim	 * copy is owned by the same user and group.
159167802Sjkim	 */
160167802Sjkim#define	RETAINBITS \
161167802Sjkim	(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
162167802Sjkim	else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid)
163167802Sjkim		if (fstat(to_fd, &to_stat)) {
164167802Sjkim			warn("%s", to.p_path);
165245582Sjkim			rval = 1;
166167802Sjkim		} else if (fs->st_gid == to_stat.st_gid &&
167245582Sjkim		    fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
168167802Sjkim			warn("%s", to.p_path);
169245582Sjkim			rval = 1;
170167802Sjkim		}
171245582Sjkim	(void)close(from_fd);
172167802Sjkim	if (close(to_fd)) {
173167802Sjkim		warn("%s", to.p_path);
174167802Sjkim		rval = 1;
175245582Sjkim	}
176245582Sjkim	return (rval);
177245582Sjkim}
178167802Sjkim
179118611Snjlint
180245582Sjkimcopy_link(p, exists)
181118611Snjl	FTSENT *p;
182118611Snjl	int exists;
183118611Snjl{
184118611Snjl	int len;
185118611Snjl	char link[MAXPATHLEN];
186118611Snjl
187118611Snjl	if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
188118611Snjl		warn("readlink: %s", p->fts_path);
189118611Snjl		return (1);
190167802Sjkim	}
191118611Snjl	link[len] = '\0';
192245582Sjkim	if (exists && unlink(to.p_path)) {
193118611Snjl		warn("unlink: %s", to.p_path);
194118611Snjl		return (1);
195118611Snjl	}
196118611Snjl	if (symlink(link, to.p_path)) {
197118611Snjl		warn("symlink: %s", link);
198118611Snjl		return (1);
199245582Sjkim	}
200118611Snjl	return (0);
201118611Snjl}
202118611Snjl
203118611Snjlint
204118611Snjlcopy_fifo(from_stat, exists)
205118611Snjl	struct stat *from_stat;
206245582Sjkim	int exists;
207245582Sjkim{
208118611Snjl	if (exists && unlink(to.p_path)) {
209245582Sjkim		warn("unlink: %s", to.p_path);
210118611Snjl		return (1);
211118611Snjl	}
212118611Snjl	if (mkfifo(to.p_path, from_stat->st_mode)) {
213245582Sjkim		warn("mkfifo: %s", to.p_path);
214118611Snjl		return (1);
215	}
216	return (pflag ? setfile(from_stat, 0) : 0);
217}
218
219int
220copy_special(from_stat, exists)
221	struct stat *from_stat;
222	int exists;
223{
224	if (exists && unlink(to.p_path)) {
225		warn("unlink: %s", to.p_path);
226		return (1);
227	}
228	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
229		warn("mknod: %s", to.p_path);
230		return (1);
231	}
232	return (pflag ? setfile(from_stat, 0) : 0);
233}
234
235
236int
237setfile(fs, fd)
238	register struct stat *fs;
239	int fd;
240{
241	static struct timeval tv[2];
242	int rval;
243
244	rval = 0;
245	fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
246
247	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
248	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
249	if (utimes(to.p_path, tv)) {
250		warn("utimes: %s", to.p_path);
251		rval = 1;
252	}
253	/*
254	 * Changing the ownership probably won't succeed, unless we're root
255	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
256	 * the mode; current BSD behavior is to remove all setuid bits on
257	 * chown.  If chown fails, lose setuid/setgid bits.
258	 */
259	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
260	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
261		if (errno != EPERM) {
262			warn("chown: %s", to.p_path);
263			rval = 1;
264		}
265		fs->st_mode &= ~(S_ISUID | S_ISGID);
266	}
267	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
268		warn("chown: %s", to.p_path);
269		rval = 1;
270	}
271
272	if (fd ?
273	    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
274		warn("chflags: %s", to.p_path);
275		rval = 1;
276	}
277	return (rval);
278}
279
280void
281usage()
282{
283	(void)fprintf(stderr, "%s\n%s\n",
284"usage: cp [-R [-H | -L | -P] [-fip] src target",
285"       cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory");
286	exit(1);
287}
288