utils.c revision 9220
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
323044Sdg *
339220Sbde *	$Id: utils.c,v 1.3 1995/05/30 00:06:22 rgrimes Exp $
341556Srgrimes */
351556Srgrimes
361556Srgrimes#ifndef lint
371556Srgrimesstatic char sccsid[] = "@(#)utils.c	8.3 (Berkeley) 4/1/94";
381556Srgrimes#endif /* not lint */
391556Srgrimes
401556Srgrimes#include <sys/param.h>
411556Srgrimes#include <sys/stat.h>
421556Srgrimes#include <sys/mman.h>
431556Srgrimes#include <sys/time.h>
441556Srgrimes
451556Srgrimes#include <err.h>
461556Srgrimes#include <errno.h>
471556Srgrimes#include <fcntl.h>
481556Srgrimes#include <fts.h>
491556Srgrimes#include <stdio.h>
501556Srgrimes#include <stdlib.h>
511556Srgrimes#include <string.h>
521556Srgrimes#include <unistd.h>
531556Srgrimes
541556Srgrimes#include "extern.h"
551556Srgrimes
561556Srgrimesint
571556Srgrimescopy_file(entp, dne)
581556Srgrimes	FTSENT *entp;
591556Srgrimes	int dne;
601556Srgrimes{
611556Srgrimes	static char buf[MAXBSIZE];
621556Srgrimes	struct stat to_stat, *fs;
631556Srgrimes	int ch, checkch, from_fd, rcount, rval, to_fd, wcount;
641556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
651556Srgrimes	char *p;
661556Srgrimes#endif
678855Srgrimes
681556Srgrimes	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
691556Srgrimes		warn("%s", entp->fts_path);
701556Srgrimes		return (1);
711556Srgrimes	}
721556Srgrimes
731556Srgrimes	fs = entp->fts_statp;
741556Srgrimes
751556Srgrimes	/*
761556Srgrimes	 * If the file exists and we're interactive, verify with the user.
771556Srgrimes	 * If the file DNE, set the mode to be the from file, minus setuid
781556Srgrimes	 * bits, modified by the umask; arguably wrong, but it makes copying
791556Srgrimes	 * executables work right and it's been that way forever.  (The
801556Srgrimes	 * other choice is 666 or'ed with the execute bits on the from file
811556Srgrimes	 * modified by the umask.)
821556Srgrimes	 */
831556Srgrimes	if (!dne) {
841556Srgrimes		if (iflag) {
851556Srgrimes			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
861556Srgrimes			checkch = ch = getchar();
871556Srgrimes			while (ch != '\n' && ch != EOF)
881556Srgrimes				ch = getchar();
891556Srgrimes			if (checkch != 'y') {
901556Srgrimes				(void)close(from_fd);
911556Srgrimes				return (0);
921556Srgrimes			}
931556Srgrimes		}
941556Srgrimes		to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
951556Srgrimes	} else
961556Srgrimes		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
971556Srgrimes		    fs->st_mode & ~(S_ISUID | S_ISGID));
981556Srgrimes
991556Srgrimes	if (to_fd == -1) {
1001556Srgrimes		warn("%s", to.p_path);
1011556Srgrimes		(void)close(from_fd);
1021556Srgrimes		return (1);;
1031556Srgrimes	}
1041556Srgrimes
1051556Srgrimes	rval = 0;
1061556Srgrimes
1071556Srgrimes	/*
1081556Srgrimes	 * Mmap and write if less than 8M (the limit is so we don't totally
1091556Srgrimes	 * trash memory on big files.  This is really a minor hack, but it
1101556Srgrimes	 * wins some CPU back.
1111556Srgrimes	 */
1121556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
1131556Srgrimes	if (fs->st_size <= 8 * 1048576) {
1141556Srgrimes		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
1151556Srgrimes		    0, from_fd, (off_t)0)) == (char *)-1) {
1161556Srgrimes			warn("%s", entp->fts_path);
1171556Srgrimes			rval = 1;
1181556Srgrimes		} else {
1191556Srgrimes			if (write(to_fd, p, fs->st_size) != fs->st_size) {
1201556Srgrimes				warn("%s", to.p_path);
1211556Srgrimes				rval = 1;
1221556Srgrimes			}
1231556Srgrimes			/* Some systems don't unmap on close(2). */
1241556Srgrimes			if (munmap(p, fs->st_size) < 0) {
1251556Srgrimes				warn("%s", entp->fts_path);
1261556Srgrimes				rval = 1;
1271556Srgrimes			}
1281556Srgrimes		}
1291556Srgrimes	} else
1301556Srgrimes#endif
1311556Srgrimes	{
1321556Srgrimes		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
1331556Srgrimes			wcount = write(to_fd, buf, rcount);
1341556Srgrimes			if (rcount != wcount || wcount == -1) {
1351556Srgrimes				warn("%s", to.p_path);
1361556Srgrimes				rval = 1;
1371556Srgrimes				break;
1381556Srgrimes			}
1391556Srgrimes		}
1401556Srgrimes		if (rcount < 0) {
1411556Srgrimes			warn("%s", entp->fts_path);
1421556Srgrimes			rval = 1;
1431556Srgrimes		}
1441556Srgrimes	}
1451556Srgrimes
1469220Sbde	/*
1479220Sbde	 * Don't remove the target even after an error.  The target might
1489220Sbde	 * not be a regular file, or its attributes might be important,
1499220Sbde	 * or its contents might be irreplacable.  It would only be safe
1509220Sbde	 * to remove it if we created it and its length is 0.
1519220Sbde	 */
1521556Srgrimes
1531556Srgrimes	if (pflag && setfile(fs, to_fd))
1541556Srgrimes		rval = 1;
1551556Srgrimes	/*
1561556Srgrimes	 * If the source was setuid or setgid, lose the bits unless the
1571556Srgrimes	 * copy is owned by the same user and group.
1581556Srgrimes	 */
1591556Srgrimes#define	RETAINBITS \
1601556Srgrimes	(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
1611556Srgrimes	else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid)
1621556Srgrimes		if (fstat(to_fd, &to_stat)) {
1631556Srgrimes			warn("%s", to.p_path);
1641556Srgrimes			rval = 1;
1651556Srgrimes		} else if (fs->st_gid == to_stat.st_gid &&
1661556Srgrimes		    fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
1671556Srgrimes			warn("%s", to.p_path);
1681556Srgrimes			rval = 1;
1691556Srgrimes		}
1701556Srgrimes	(void)close(from_fd);
1711556Srgrimes	if (close(to_fd)) {
1721556Srgrimes		warn("%s", to.p_path);
1731556Srgrimes		rval = 1;
1741556Srgrimes	}
1751556Srgrimes	return (rval);
1761556Srgrimes}
1771556Srgrimes
1781556Srgrimesint
1791556Srgrimescopy_link(p, exists)
1801556Srgrimes	FTSENT *p;
1811556Srgrimes	int exists;
1821556Srgrimes{
1831556Srgrimes	int len;
1841556Srgrimes	char link[MAXPATHLEN];
1851556Srgrimes
1861556Srgrimes	if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
1871556Srgrimes		warn("readlink: %s", p->fts_path);
1881556Srgrimes		return (1);
1891556Srgrimes	}
1901556Srgrimes	link[len] = '\0';
1911556Srgrimes	if (exists && unlink(to.p_path)) {
1921556Srgrimes		warn("unlink: %s", to.p_path);
1931556Srgrimes		return (1);
1941556Srgrimes	}
1951556Srgrimes	if (symlink(link, to.p_path)) {
1961556Srgrimes		warn("symlink: %s", link);
1971556Srgrimes		return (1);
1981556Srgrimes	}
1991556Srgrimes	return (0);
2001556Srgrimes}
2011556Srgrimes
2021556Srgrimesint
2031556Srgrimescopy_fifo(from_stat, exists)
2041556Srgrimes	struct stat *from_stat;
2051556Srgrimes	int exists;
2061556Srgrimes{
2071556Srgrimes	if (exists && unlink(to.p_path)) {
2081556Srgrimes		warn("unlink: %s", to.p_path);
2091556Srgrimes		return (1);
2101556Srgrimes	}
2111556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
2121556Srgrimes		warn("mkfifo: %s", to.p_path);
2131556Srgrimes		return (1);
2141556Srgrimes	}
2151556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2161556Srgrimes}
2171556Srgrimes
2181556Srgrimesint
2191556Srgrimescopy_special(from_stat, exists)
2201556Srgrimes	struct stat *from_stat;
2211556Srgrimes	int exists;
2221556Srgrimes{
2231556Srgrimes	if (exists && unlink(to.p_path)) {
2241556Srgrimes		warn("unlink: %s", to.p_path);
2251556Srgrimes		return (1);
2261556Srgrimes	}
2271556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
2281556Srgrimes		warn("mknod: %s", to.p_path);
2291556Srgrimes		return (1);
2301556Srgrimes	}
2311556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2321556Srgrimes}
2331556Srgrimes
2341556Srgrimes
2351556Srgrimesint
2361556Srgrimessetfile(fs, fd)
2371556Srgrimes	register struct stat *fs;
2381556Srgrimes	int fd;
2391556Srgrimes{
2401556Srgrimes	static struct timeval tv[2];
2411556Srgrimes	int rval;
2421556Srgrimes
2431556Srgrimes	rval = 0;
2441556Srgrimes	fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
2451556Srgrimes
2461556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
2471556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
2481556Srgrimes	if (utimes(to.p_path, tv)) {
2491556Srgrimes		warn("utimes: %s", to.p_path);
2501556Srgrimes		rval = 1;
2511556Srgrimes	}
2521556Srgrimes	/*
2531556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
2541556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
2551556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
2561556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
2571556Srgrimes	 */
2581556Srgrimes	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
2591556Srgrimes	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
2601556Srgrimes		if (errno != EPERM) {
2611556Srgrimes			warn("chown: %s", to.p_path);
2621556Srgrimes			rval = 1;
2631556Srgrimes		}
2641556Srgrimes		fs->st_mode &= ~(S_ISUID | S_ISGID);
2651556Srgrimes	}
2661556Srgrimes	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
2671556Srgrimes		warn("chown: %s", to.p_path);
2681556Srgrimes		rval = 1;
2691556Srgrimes	}
2701556Srgrimes
2711556Srgrimes	if (fd ?
2721556Srgrimes	    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
2731556Srgrimes		warn("chflags: %s", to.p_path);
2741556Srgrimes		rval = 1;
2751556Srgrimes	}
2761556Srgrimes	return (rval);
2771556Srgrimes}
2781556Srgrimes
2791556Srgrimesvoid
2801556Srgrimesusage()
2811556Srgrimes{
2821556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
2831556Srgrimes"usage: cp [-R [-H | -L | -P] [-fip] src target",
2841556Srgrimes"       cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory");
2851556Srgrimes	exit(1);
2861556Srgrimes}
287