utils.c revision 35773
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.
321556Srgrimes */
331556Srgrimes
341556Srgrimes#ifndef lint
3535773Scharnier#if 0
3620412Sstevestatic char const sccsid[] = "@(#)utils.c	8.3 (Berkeley) 4/1/94";
3735773Scharnier#endif
3835773Scharnierstatic const char rcsid[] =
3935773Scharnier	"$Id$";
401556Srgrimes#endif /* not lint */
411556Srgrimes
421556Srgrimes#include <sys/param.h>
431556Srgrimes#include <sys/stat.h>
4435773Scharnier#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
451556Srgrimes#include <sys/mman.h>
4635773Scharnier#endif
471556Srgrimes
481556Srgrimes#include <err.h>
491556Srgrimes#include <errno.h>
501556Srgrimes#include <fcntl.h>
511556Srgrimes#include <fts.h>
521556Srgrimes#include <stdio.h>
531556Srgrimes#include <unistd.h>
541556Srgrimes
551556Srgrimes#include "extern.h"
561556Srgrimes
571556Srgrimesint
581556Srgrimescopy_file(entp, dne)
591556Srgrimes	FTSENT *entp;
601556Srgrimes	int dne;
611556Srgrimes{
621556Srgrimes	static char buf[MAXBSIZE];
631556Srgrimes	struct stat to_stat, *fs;
6432540Sbde	int ch, checkch, from_fd, rcount, rval, to_fd, wcount, wresid;
6532540Sbde	char *bufp;
661556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
671556Srgrimes	char *p;
681556Srgrimes#endif
698855Srgrimes
701556Srgrimes	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
711556Srgrimes		warn("%s", entp->fts_path);
721556Srgrimes		return (1);
731556Srgrimes	}
741556Srgrimes
751556Srgrimes	fs = entp->fts_statp;
761556Srgrimes
771556Srgrimes	/*
781556Srgrimes	 * If the file exists and we're interactive, verify with the user.
791556Srgrimes	 * If the file DNE, set the mode to be the from file, minus setuid
801556Srgrimes	 * bits, modified by the umask; arguably wrong, but it makes copying
811556Srgrimes	 * executables work right and it's been that way forever.  (The
821556Srgrimes	 * other choice is 666 or'ed with the execute bits on the from file
831556Srgrimes	 * modified by the umask.)
841556Srgrimes	 */
851556Srgrimes	if (!dne) {
8630088Swosch#define YESNO "(y/n [n]) "
871556Srgrimes		if (iflag) {
8830088Swosch			(void)fprintf(stderr, "overwrite %s? %s",
8930088Swosch					to.p_path, YESNO);
901556Srgrimes			checkch = ch = getchar();
911556Srgrimes			while (ch != '\n' && ch != EOF)
921556Srgrimes				ch = getchar();
9314416Swosch			if (checkch != 'y' && checkch != 'Y') {
941556Srgrimes				(void)close(from_fd);
9530088Swosch				(void)fprintf(stderr, "not overwritten\n");
961556Srgrimes				return (0);
971556Srgrimes			}
981556Srgrimes		}
9914416Swosch
10014416Swosch		if (fflag) {
10114416Swosch		    /* remove existing destination file name,
10214416Swosch		     * create a new file  */
10314416Swosch		    (void)unlink(to.p_path);
10414416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
10514416Swosch				 fs->st_mode & ~(S_ISUID | S_ISGID));
10614416Swosch		} else
10714416Swosch		    /* overwrite existing destination file name */
10814416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
1091556Srgrimes	} else
1101556Srgrimes		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
1111556Srgrimes		    fs->st_mode & ~(S_ISUID | S_ISGID));
1121556Srgrimes
1131556Srgrimes	if (to_fd == -1) {
1141556Srgrimes		warn("%s", to.p_path);
1151556Srgrimes		(void)close(from_fd);
1161556Srgrimes		return (1);;
1171556Srgrimes	}
1181556Srgrimes
1191556Srgrimes	rval = 0;
1201556Srgrimes
1211556Srgrimes	/*
1221556Srgrimes	 * Mmap and write if less than 8M (the limit is so we don't totally
1231556Srgrimes	 * trash memory on big files.  This is really a minor hack, but it
1241556Srgrimes	 * wins some CPU back.
1251556Srgrimes	 */
1261556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
1271556Srgrimes	if (fs->st_size <= 8 * 1048576) {
1281556Srgrimes		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
12921786Salex		    MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
1301556Srgrimes			warn("%s", entp->fts_path);
1311556Srgrimes			rval = 1;
1321556Srgrimes		} else {
13332540Sbde			for (bufp = p, wresid = fs->st_size; ;
13432588Sbde			    bufp += wcount, wresid -= wcount) {
13532540Sbde				wcount = write(to_fd, bufp, wresid);
13632540Sbde				if (wcount >= wresid || wcount <= 0)
13732540Sbde					break;
13832540Sbde			}
13932540Sbde			if (wcount != wresid) {
1401556Srgrimes				warn("%s", to.p_path);
1411556Srgrimes				rval = 1;
1421556Srgrimes			}
1431556Srgrimes			/* Some systems don't unmap on close(2). */
1441556Srgrimes			if (munmap(p, fs->st_size) < 0) {
1451556Srgrimes				warn("%s", entp->fts_path);
1461556Srgrimes				rval = 1;
1471556Srgrimes			}
1481556Srgrimes		}
1491556Srgrimes	} else
1501556Srgrimes#endif
1511556Srgrimes	{
1521556Srgrimes		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
15332540Sbde			for (bufp = buf, wresid = rcount; ;
15432540Sbde			    bufp += wcount, wresid -= wcount) {
15532540Sbde				wcount = write(to_fd, bufp, wresid);
15632540Sbde				if (wcount >= wresid || wcount <= 0)
15732540Sbde					break;
15832540Sbde			}
15932540Sbde			if (wcount != wresid) {
1601556Srgrimes				warn("%s", to.p_path);
1611556Srgrimes				rval = 1;
1621556Srgrimes				break;
1631556Srgrimes			}
1641556Srgrimes		}
1651556Srgrimes		if (rcount < 0) {
1661556Srgrimes			warn("%s", entp->fts_path);
1671556Srgrimes			rval = 1;
1681556Srgrimes		}
1691556Srgrimes	}
1701556Srgrimes
1719220Sbde	/*
1729220Sbde	 * Don't remove the target even after an error.  The target might
1739220Sbde	 * not be a regular file, or its attributes might be important,
1749220Sbde	 * or its contents might be irreplacable.  It would only be safe
1759220Sbde	 * to remove it if we created it and its length is 0.
1769220Sbde	 */
1771556Srgrimes
1781556Srgrimes	if (pflag && setfile(fs, to_fd))
1791556Srgrimes		rval = 1;
1801556Srgrimes	/*
1811556Srgrimes	 * If the source was setuid or setgid, lose the bits unless the
1821556Srgrimes	 * copy is owned by the same user and group.
1831556Srgrimes	 */
1841556Srgrimes#define	RETAINBITS \
1851556Srgrimes	(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
1861556Srgrimes	else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid)
1871556Srgrimes		if (fstat(to_fd, &to_stat)) {
1881556Srgrimes			warn("%s", to.p_path);
1891556Srgrimes			rval = 1;
1901556Srgrimes		} else if (fs->st_gid == to_stat.st_gid &&
1911556Srgrimes		    fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
1921556Srgrimes			warn("%s", to.p_path);
1931556Srgrimes			rval = 1;
1941556Srgrimes		}
1951556Srgrimes	(void)close(from_fd);
1961556Srgrimes	if (close(to_fd)) {
1971556Srgrimes		warn("%s", to.p_path);
1981556Srgrimes		rval = 1;
1991556Srgrimes	}
2001556Srgrimes	return (rval);
2011556Srgrimes}
2021556Srgrimes
2031556Srgrimesint
2041556Srgrimescopy_link(p, exists)
2051556Srgrimes	FTSENT *p;
2061556Srgrimes	int exists;
2071556Srgrimes{
2081556Srgrimes	int len;
2091556Srgrimes	char link[MAXPATHLEN];
2101556Srgrimes
2111556Srgrimes	if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
2121556Srgrimes		warn("readlink: %s", p->fts_path);
2131556Srgrimes		return (1);
2141556Srgrimes	}
2151556Srgrimes	link[len] = '\0';
2161556Srgrimes	if (exists && unlink(to.p_path)) {
2171556Srgrimes		warn("unlink: %s", to.p_path);
2181556Srgrimes		return (1);
2191556Srgrimes	}
2201556Srgrimes	if (symlink(link, to.p_path)) {
2211556Srgrimes		warn("symlink: %s", link);
2221556Srgrimes		return (1);
2231556Srgrimes	}
2241556Srgrimes	return (0);
2251556Srgrimes}
2261556Srgrimes
2271556Srgrimesint
2281556Srgrimescopy_fifo(from_stat, exists)
2291556Srgrimes	struct stat *from_stat;
2301556Srgrimes	int exists;
2311556Srgrimes{
2321556Srgrimes	if (exists && unlink(to.p_path)) {
2331556Srgrimes		warn("unlink: %s", to.p_path);
2341556Srgrimes		return (1);
2351556Srgrimes	}
2361556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
2371556Srgrimes		warn("mkfifo: %s", to.p_path);
2381556Srgrimes		return (1);
2391556Srgrimes	}
2401556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2411556Srgrimes}
2421556Srgrimes
2431556Srgrimesint
2441556Srgrimescopy_special(from_stat, exists)
2451556Srgrimes	struct stat *from_stat;
2461556Srgrimes	int exists;
2471556Srgrimes{
2481556Srgrimes	if (exists && unlink(to.p_path)) {
2491556Srgrimes		warn("unlink: %s", to.p_path);
2501556Srgrimes		return (1);
2511556Srgrimes	}
2521556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
2531556Srgrimes		warn("mknod: %s", to.p_path);
2541556Srgrimes		return (1);
2551556Srgrimes	}
2561556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2571556Srgrimes}
2581556Srgrimes
2591556Srgrimes
2601556Srgrimesint
2611556Srgrimessetfile(fs, fd)
2621556Srgrimes	register struct stat *fs;
2631556Srgrimes	int fd;
2641556Srgrimes{
2651556Srgrimes	static struct timeval tv[2];
2661556Srgrimes	int rval;
2671556Srgrimes
2681556Srgrimes	rval = 0;
26911146Sbde	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
27011146Sbde		       S_IRWXU | S_IRWXG | S_IRWXO;
2711556Srgrimes
2721556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
2731556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
2741556Srgrimes	if (utimes(to.p_path, tv)) {
2751556Srgrimes		warn("utimes: %s", to.p_path);
2761556Srgrimes		rval = 1;
2771556Srgrimes	}
2781556Srgrimes	/*
2791556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
2801556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
2811556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
2821556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
2831556Srgrimes	 */
2841556Srgrimes	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
2851556Srgrimes	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
2861556Srgrimes		if (errno != EPERM) {
2871556Srgrimes			warn("chown: %s", to.p_path);
2881556Srgrimes			rval = 1;
2891556Srgrimes		}
2901556Srgrimes		fs->st_mode &= ~(S_ISUID | S_ISGID);
2911556Srgrimes	}
2921556Srgrimes	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
2931556Srgrimes		warn("chown: %s", to.p_path);
2941556Srgrimes		rval = 1;
2951556Srgrimes	}
2961556Srgrimes
2971556Srgrimes	if (fd ?
2981556Srgrimes	    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
2991556Srgrimes		warn("chflags: %s", to.p_path);
3001556Srgrimes		rval = 1;
3011556Srgrimes	}
3021556Srgrimes	return (rval);
3031556Srgrimes}
3041556Srgrimes
3051556Srgrimesvoid
3061556Srgrimesusage()
3071556Srgrimes{
3081556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
30914416Swosch"usage: cp [-R [-H | -L | -P]] [-f | -i] [-p] src target",
31014416Swosch"       cp [-R [-H | -L | -P]] [-f | -i] [-p] src1 ... srcN directory");
3111556Srgrimes	exit(1);
3121556Srgrimes}
313