utils.c revision 30088
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 *
3330088Swosch *	$Id: utils.c,v 1.13 1997/02/22 14:01:34 peter Exp $
341556Srgrimes */
351556Srgrimes
361556Srgrimes#ifndef lint
3720412Sstevestatic char const 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) {
8430088Swosch#define YESNO "(y/n [n]) "
851556Srgrimes		if (iflag) {
8630088Swosch			(void)fprintf(stderr, "overwrite %s? %s",
8730088Swosch					to.p_path, YESNO);
881556Srgrimes			checkch = ch = getchar();
891556Srgrimes			while (ch != '\n' && ch != EOF)
901556Srgrimes				ch = getchar();
9114416Swosch			if (checkch != 'y' && checkch != 'Y') {
921556Srgrimes				(void)close(from_fd);
9330088Swosch				(void)fprintf(stderr, "not overwritten\n");
941556Srgrimes				return (0);
951556Srgrimes			}
961556Srgrimes		}
9714416Swosch
9814416Swosch		if (fflag) {
9914416Swosch		    /* remove existing destination file name,
10014416Swosch		     * create a new file  */
10114416Swosch		    (void)unlink(to.p_path);
10214416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
10314416Swosch				 fs->st_mode & ~(S_ISUID | S_ISGID));
10414416Swosch		} else
10514416Swosch		    /* overwrite existing destination file name */
10614416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
1071556Srgrimes	} else
1081556Srgrimes		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
1091556Srgrimes		    fs->st_mode & ~(S_ISUID | S_ISGID));
1101556Srgrimes
1111556Srgrimes	if (to_fd == -1) {
1121556Srgrimes		warn("%s", to.p_path);
1131556Srgrimes		(void)close(from_fd);
1141556Srgrimes		return (1);;
1151556Srgrimes	}
1161556Srgrimes
1171556Srgrimes	rval = 0;
1181556Srgrimes
1191556Srgrimes	/*
1201556Srgrimes	 * Mmap and write if less than 8M (the limit is so we don't totally
1211556Srgrimes	 * trash memory on big files.  This is really a minor hack, but it
1221556Srgrimes	 * wins some CPU back.
1231556Srgrimes	 */
1241556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
1251556Srgrimes	if (fs->st_size <= 8 * 1048576) {
1261556Srgrimes		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
12721786Salex		    MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
1281556Srgrimes			warn("%s", entp->fts_path);
1291556Srgrimes			rval = 1;
1301556Srgrimes		} else {
1311556Srgrimes			if (write(to_fd, p, fs->st_size) != fs->st_size) {
1321556Srgrimes				warn("%s", to.p_path);
1331556Srgrimes				rval = 1;
1341556Srgrimes			}
1351556Srgrimes			/* Some systems don't unmap on close(2). */
1361556Srgrimes			if (munmap(p, fs->st_size) < 0) {
1371556Srgrimes				warn("%s", entp->fts_path);
1381556Srgrimes				rval = 1;
1391556Srgrimes			}
1401556Srgrimes		}
1411556Srgrimes	} else
1421556Srgrimes#endif
1431556Srgrimes	{
1441556Srgrimes		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
1451556Srgrimes			wcount = write(to_fd, buf, rcount);
1461556Srgrimes			if (rcount != wcount || wcount == -1) {
1471556Srgrimes				warn("%s", to.p_path);
1481556Srgrimes				rval = 1;
1491556Srgrimes				break;
1501556Srgrimes			}
1511556Srgrimes		}
1521556Srgrimes		if (rcount < 0) {
1531556Srgrimes			warn("%s", entp->fts_path);
1541556Srgrimes			rval = 1;
1551556Srgrimes		}
1561556Srgrimes	}
1571556Srgrimes
1589220Sbde	/*
1599220Sbde	 * Don't remove the target even after an error.  The target might
1609220Sbde	 * not be a regular file, or its attributes might be important,
1619220Sbde	 * or its contents might be irreplacable.  It would only be safe
1629220Sbde	 * to remove it if we created it and its length is 0.
1639220Sbde	 */
1641556Srgrimes
1651556Srgrimes	if (pflag && setfile(fs, to_fd))
1661556Srgrimes		rval = 1;
1671556Srgrimes	/*
1681556Srgrimes	 * If the source was setuid or setgid, lose the bits unless the
1691556Srgrimes	 * copy is owned by the same user and group.
1701556Srgrimes	 */
1711556Srgrimes#define	RETAINBITS \
1721556Srgrimes	(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
1731556Srgrimes	else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid)
1741556Srgrimes		if (fstat(to_fd, &to_stat)) {
1751556Srgrimes			warn("%s", to.p_path);
1761556Srgrimes			rval = 1;
1771556Srgrimes		} else if (fs->st_gid == to_stat.st_gid &&
1781556Srgrimes		    fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
1791556Srgrimes			warn("%s", to.p_path);
1801556Srgrimes			rval = 1;
1811556Srgrimes		}
1821556Srgrimes	(void)close(from_fd);
1831556Srgrimes	if (close(to_fd)) {
1841556Srgrimes		warn("%s", to.p_path);
1851556Srgrimes		rval = 1;
1861556Srgrimes	}
1871556Srgrimes	return (rval);
1881556Srgrimes}
1891556Srgrimes
1901556Srgrimesint
1911556Srgrimescopy_link(p, exists)
1921556Srgrimes	FTSENT *p;
1931556Srgrimes	int exists;
1941556Srgrimes{
1951556Srgrimes	int len;
1961556Srgrimes	char link[MAXPATHLEN];
1971556Srgrimes
1981556Srgrimes	if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
1991556Srgrimes		warn("readlink: %s", p->fts_path);
2001556Srgrimes		return (1);
2011556Srgrimes	}
2021556Srgrimes	link[len] = '\0';
2031556Srgrimes	if (exists && unlink(to.p_path)) {
2041556Srgrimes		warn("unlink: %s", to.p_path);
2051556Srgrimes		return (1);
2061556Srgrimes	}
2071556Srgrimes	if (symlink(link, to.p_path)) {
2081556Srgrimes		warn("symlink: %s", link);
2091556Srgrimes		return (1);
2101556Srgrimes	}
2111556Srgrimes	return (0);
2121556Srgrimes}
2131556Srgrimes
2141556Srgrimesint
2151556Srgrimescopy_fifo(from_stat, exists)
2161556Srgrimes	struct stat *from_stat;
2171556Srgrimes	int exists;
2181556Srgrimes{
2191556Srgrimes	if (exists && unlink(to.p_path)) {
2201556Srgrimes		warn("unlink: %s", to.p_path);
2211556Srgrimes		return (1);
2221556Srgrimes	}
2231556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
2241556Srgrimes		warn("mkfifo: %s", to.p_path);
2251556Srgrimes		return (1);
2261556Srgrimes	}
2271556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2281556Srgrimes}
2291556Srgrimes
2301556Srgrimesint
2311556Srgrimescopy_special(from_stat, exists)
2321556Srgrimes	struct stat *from_stat;
2331556Srgrimes	int exists;
2341556Srgrimes{
2351556Srgrimes	if (exists && unlink(to.p_path)) {
2361556Srgrimes		warn("unlink: %s", to.p_path);
2371556Srgrimes		return (1);
2381556Srgrimes	}
2391556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
2401556Srgrimes		warn("mknod: %s", to.p_path);
2411556Srgrimes		return (1);
2421556Srgrimes	}
2431556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2441556Srgrimes}
2451556Srgrimes
2461556Srgrimes
2471556Srgrimesint
2481556Srgrimessetfile(fs, fd)
2491556Srgrimes	register struct stat *fs;
2501556Srgrimes	int fd;
2511556Srgrimes{
2521556Srgrimes	static struct timeval tv[2];
2531556Srgrimes	int rval;
2541556Srgrimes
2551556Srgrimes	rval = 0;
25611146Sbde	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
25711146Sbde		       S_IRWXU | S_IRWXG | S_IRWXO;
2581556Srgrimes
2591556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
2601556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
2611556Srgrimes	if (utimes(to.p_path, tv)) {
2621556Srgrimes		warn("utimes: %s", to.p_path);
2631556Srgrimes		rval = 1;
2641556Srgrimes	}
2651556Srgrimes	/*
2661556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
2671556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
2681556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
2691556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
2701556Srgrimes	 */
2711556Srgrimes	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
2721556Srgrimes	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
2731556Srgrimes		if (errno != EPERM) {
2741556Srgrimes			warn("chown: %s", to.p_path);
2751556Srgrimes			rval = 1;
2761556Srgrimes		}
2771556Srgrimes		fs->st_mode &= ~(S_ISUID | S_ISGID);
2781556Srgrimes	}
2791556Srgrimes	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
2801556Srgrimes		warn("chown: %s", to.p_path);
2811556Srgrimes		rval = 1;
2821556Srgrimes	}
2831556Srgrimes
2841556Srgrimes	if (fd ?
2851556Srgrimes	    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
2861556Srgrimes		warn("chflags: %s", to.p_path);
2871556Srgrimes		rval = 1;
2881556Srgrimes	}
2891556Srgrimes	return (rval);
2901556Srgrimes}
2911556Srgrimes
2921556Srgrimesvoid
2931556Srgrimesusage()
2941556Srgrimes{
2951556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
29614416Swosch"usage: cp [-R [-H | -L | -P]] [-f | -i] [-p] src target",
29714416Swosch"       cp [-R [-H | -L | -P]] [-f | -i] [-p] src1 ... srcN directory");
2981556Srgrimes	exit(1);
2991556Srgrimes}
300