utils.c revision 99363
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
3636003Scharnierstatic char sccsid[] = "@(#)utils.c	8.3 (Berkeley) 4/1/94";
3735773Scharnier#endif
381556Srgrimes#endif /* not lint */
3999109Sobrien#include <sys/cdefs.h>
4099109Sobrien__FBSDID("$FreeBSD: head/bin/cp/utils.c 99363 2002-07-03 16:35:20Z markm $");
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>
5276693Simp#include <limits.h>
531556Srgrimes#include <stdio.h>
5478469Sdes#include <stdlib.h>
5556420Smharo#include <sysexits.h>
561556Srgrimes#include <unistd.h>
571556Srgrimes
581556Srgrimes#include "extern.h"
591556Srgrimes
601556Srgrimesint
6190107Simpcopy_file(FTSENT *entp, int dne)
621556Srgrimes{
631556Srgrimes	static char buf[MAXBSIZE];
6478070Sbde	struct stat *fs;
6599363Smarkm	int ch, checkch, from_fd, rcount, rval, to_fd;
6699363Smarkm	ssize_t wcount;
6799363Smarkm	size_t wresid;
6832540Sbde	char *bufp;
691556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
701556Srgrimes	char *p;
711556Srgrimes#endif
728855Srgrimes
731556Srgrimes	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
741556Srgrimes		warn("%s", entp->fts_path);
751556Srgrimes		return (1);
761556Srgrimes	}
771556Srgrimes
781556Srgrimes	fs = entp->fts_statp;
791556Srgrimes
801556Srgrimes	/*
811556Srgrimes	 * If the file exists and we're interactive, verify with the user.
821556Srgrimes	 * If the file DNE, set the mode to be the from file, minus setuid
831556Srgrimes	 * bits, modified by the umask; arguably wrong, but it makes copying
841556Srgrimes	 * executables work right and it's been that way forever.  (The
851556Srgrimes	 * other choice is 666 or'ed with the execute bits on the from file
861556Srgrimes	 * modified by the umask.)
871556Srgrimes	 */
881556Srgrimes	if (!dne) {
8930088Swosch#define YESNO "(y/n [n]) "
901556Srgrimes		if (iflag) {
9130088Swosch			(void)fprintf(stderr, "overwrite %s? %s",
9230088Swosch					to.p_path, YESNO);
931556Srgrimes			checkch = ch = getchar();
941556Srgrimes			while (ch != '\n' && ch != EOF)
951556Srgrimes				ch = getchar();
9614416Swosch			if (checkch != 'y' && checkch != 'Y') {
971556Srgrimes				(void)close(from_fd);
9830088Swosch				(void)fprintf(stderr, "not overwritten\n");
9956420Smharo				return (1);
1001556Srgrimes			}
1011556Srgrimes		}
10214416Swosch
10314416Swosch		if (fflag) {
10414416Swosch		    /* remove existing destination file name,
10514416Swosch		     * create a new file  */
10614416Swosch		    (void)unlink(to.p_path);
10714416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
10814416Swosch				 fs->st_mode & ~(S_ISUID | S_ISGID));
10914416Swosch		} else
11014416Swosch		    /* overwrite existing destination file name */
11114416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
1121556Srgrimes	} else
1131556Srgrimes		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
1141556Srgrimes		    fs->st_mode & ~(S_ISUID | S_ISGID));
1151556Srgrimes
1161556Srgrimes	if (to_fd == -1) {
1171556Srgrimes		warn("%s", to.p_path);
1181556Srgrimes		(void)close(from_fd);
11991087Smarkm		return (1);
1201556Srgrimes	}
1211556Srgrimes
1221556Srgrimes	rval = 0;
1231556Srgrimes
1241556Srgrimes	/*
1251556Srgrimes	 * Mmap and write if less than 8M (the limit is so we don't totally
1261556Srgrimes	 * trash memory on big files.  This is really a minor hack, but it
1271556Srgrimes	 * wins some CPU back.
1281556Srgrimes	 */
1291556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
13041238Sbde	if (S_ISREG(fs->st_mode) && fs->st_size <= 8 * 1048576) {
1311556Srgrimes		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
13221786Salex		    MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
1331556Srgrimes			warn("%s", entp->fts_path);
1341556Srgrimes			rval = 1;
1351556Srgrimes		} else {
13632540Sbde			for (bufp = p, wresid = fs->st_size; ;
13799363Smarkm			    bufp += wcount, wresid -= (size_t)wcount) {
13832540Sbde				wcount = write(to_fd, bufp, wresid);
13932540Sbde				if (wcount >= wresid || wcount <= 0)
14032540Sbde					break;
14132540Sbde			}
14232540Sbde			if (wcount != wresid) {
1431556Srgrimes				warn("%s", to.p_path);
1441556Srgrimes				rval = 1;
1451556Srgrimes			}
1461556Srgrimes			/* Some systems don't unmap on close(2). */
1471556Srgrimes			if (munmap(p, fs->st_size) < 0) {
1481556Srgrimes				warn("%s", entp->fts_path);
1491556Srgrimes				rval = 1;
1501556Srgrimes			}
1511556Srgrimes		}
1521556Srgrimes	} else
1531556Srgrimes#endif
1541556Srgrimes	{
1551556Srgrimes		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
15632540Sbde			for (bufp = buf, wresid = rcount; ;
15732540Sbde			    bufp += wcount, wresid -= wcount) {
15832540Sbde				wcount = write(to_fd, bufp, wresid);
15932540Sbde				if (wcount >= wresid || wcount <= 0)
16032540Sbde					break;
16132540Sbde			}
16232540Sbde			if (wcount != wresid) {
1631556Srgrimes				warn("%s", to.p_path);
1641556Srgrimes				rval = 1;
1651556Srgrimes				break;
1661556Srgrimes			}
1671556Srgrimes		}
1681556Srgrimes		if (rcount < 0) {
1691556Srgrimes			warn("%s", entp->fts_path);
1701556Srgrimes			rval = 1;
1711556Srgrimes		}
1721556Srgrimes	}
1731556Srgrimes
1749220Sbde	/*
1759220Sbde	 * Don't remove the target even after an error.  The target might
1769220Sbde	 * not be a regular file, or its attributes might be important,
17746684Skris	 * or its contents might be irreplaceable.  It would only be safe
1789220Sbde	 * to remove it if we created it and its length is 0.
1799220Sbde	 */
1801556Srgrimes
1811556Srgrimes	if (pflag && setfile(fs, to_fd))
1821556Srgrimes		rval = 1;
1831556Srgrimes	(void)close(from_fd);
1841556Srgrimes	if (close(to_fd)) {
1851556Srgrimes		warn("%s", to.p_path);
1861556Srgrimes		rval = 1;
1871556Srgrimes	}
1881556Srgrimes	return (rval);
1891556Srgrimes}
1901556Srgrimes
1911556Srgrimesint
19290107Simpcopy_link(FTSENT *p, int exists)
1931556Srgrimes{
1941556Srgrimes	int len;
19591087Smarkm	char llink[PATH_MAX];
1961556Srgrimes
19791087Smarkm	if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
1981556Srgrimes		warn("readlink: %s", p->fts_path);
1991556Srgrimes		return (1);
2001556Srgrimes	}
20191087Smarkm	llink[len] = '\0';
2021556Srgrimes	if (exists && unlink(to.p_path)) {
2031556Srgrimes		warn("unlink: %s", to.p_path);
2041556Srgrimes		return (1);
2051556Srgrimes	}
20691087Smarkm	if (symlink(llink, to.p_path)) {
20791087Smarkm		warn("symlink: %s", llink);
2081556Srgrimes		return (1);
2091556Srgrimes	}
2101556Srgrimes	return (0);
2111556Srgrimes}
2121556Srgrimes
2131556Srgrimesint
21490107Simpcopy_fifo(struct stat *from_stat, int exists)
2151556Srgrimes{
2161556Srgrimes	if (exists && unlink(to.p_path)) {
2171556Srgrimes		warn("unlink: %s", to.p_path);
2181556Srgrimes		return (1);
2191556Srgrimes	}
2201556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
2211556Srgrimes		warn("mkfifo: %s", to.p_path);
2221556Srgrimes		return (1);
2231556Srgrimes	}
2241556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2251556Srgrimes}
2261556Srgrimes
2271556Srgrimesint
22890107Simpcopy_special(struct stat *from_stat, int exists)
2291556Srgrimes{
2301556Srgrimes	if (exists && unlink(to.p_path)) {
2311556Srgrimes		warn("unlink: %s", to.p_path);
2321556Srgrimes		return (1);
2331556Srgrimes	}
2341556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
2351556Srgrimes		warn("mknod: %s", to.p_path);
2361556Srgrimes		return (1);
2371556Srgrimes	}
2381556Srgrimes	return (pflag ? setfile(from_stat, 0) : 0);
2391556Srgrimes}
2401556Srgrimes
2411556Srgrimesint
24290107Simpsetfile(struct stat *fs, int fd)
2431556Srgrimes{
2441556Srgrimes	static struct timeval tv[2];
24536838Speter	struct stat ts;
2461556Srgrimes	int rval;
24736838Speter	int gotstat;
2481556Srgrimes
2491556Srgrimes	rval = 0;
25011146Sbde	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
25111146Sbde		       S_IRWXU | S_IRWXG | S_IRWXO;
2521556Srgrimes
2531556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
2541556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
2551556Srgrimes	if (utimes(to.p_path, tv)) {
2561556Srgrimes		warn("utimes: %s", to.p_path);
2571556Srgrimes		rval = 1;
2581556Srgrimes	}
25936838Speter	if (fd ? fstat(fd, &ts) : stat(to.p_path, &ts))
26036838Speter		gotstat = 0;
26136838Speter	else {
26236838Speter		gotstat = 1;
26336838Speter		ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
26436838Speter			      S_IRWXU | S_IRWXG | S_IRWXO;
26536838Speter	}
2661556Srgrimes	/*
2671556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
2681556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
2691556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
2701556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
2711556Srgrimes	 */
27236838Speter	if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
27336838Speter		if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
27436838Speter		    chown(to.p_path, fs->st_uid, fs->st_gid)) {
27536838Speter			if (errno != EPERM) {
27636838Speter				warn("chown: %s", to.p_path);
27736838Speter				rval = 1;
27836838Speter			}
27936838Speter			fs->st_mode &= ~(S_ISUID | S_ISGID);
28036838Speter		}
28136838Speter
28236838Speter	if (!gotstat || fs->st_mode != ts.st_mode)
28336838Speter		if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
28487652Smckay			warn("chmod: %s", to.p_path);
2851556Srgrimes			rval = 1;
2861556Srgrimes		}
2871556Srgrimes
28836838Speter	if (!gotstat || fs->st_flags != ts.st_flags)
28936838Speter		if (fd ?
29036838Speter		    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
29136838Speter			warn("chflags: %s", to.p_path);
29236838Speter			rval = 1;
29336838Speter		}
29436838Speter
2951556Srgrimes	return (rval);
2961556Srgrimes}
2971556Srgrimes
2981556Srgrimesvoid
29990107Simpusage(void)
3001556Srgrimes{
30150543Smharo
3021556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
30350543Smharo"usage: cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target",
30450543Smharo"       cp [-R [-H | -L | -P]] [-f | -i] [-pv] src1 ... srcN directory");
30556420Smharo	exit(EX_USAGE);
3061556Srgrimes}
307