utils.c revision 99109
12061Sjkh/*-
250479Speter * Copyright (c) 1991, 1993, 1994
32061Sjkh *	The Regents of the University of California.  All rights reserved.
438666Sjb *
532427Sjb * Redistribution and use in source and binary forms, with or without
6103985Sphk * modification, are permitted provided that the following conditions
7103985Sphk * are met:
838666Sjb * 1. Redistributions of source code must retain the above copyright
938666Sjb *    notice, this list of conditions and the following disclaimer.
1038666Sjb * 2. Redistributions in binary form must reproduce the above copyright
1138666Sjb *    notice, this list of conditions and the following disclaimer in the
1264049Salex *    documentation and/or other materials provided with the distribution.
1364049Salex * 3. All advertising materials mentioning features or use of this software
1466071Smarkm *    must display the following acknowledgement:
1573504Sobrien *	This product includes software developed by the University of
1638666Sjb *	California, Berkeley and its contributors.
1744918Sjkh * 4. Neither the name of the University nor the names of its contributors
1838666Sjb *    may be used to endorse or promote products derived from this software
1938666Sjb *    without specific prior written permission.
20108451Sschweikh *
2138666Sjb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2238666Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2338666Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2438666Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2538978Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2638978Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2732427Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2838666Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29108451Sschweikh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3038666Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3138666Sjb * SUCH DAMAGE.
3238666Sjb */
3338666Sjb
3417308Speter#ifndef lint
3591606Skeramida#if 0
3619175Sbdestatic char sccsid[] = "@(#)utils.c	8.3 (Berkeley) 4/1/94";
3796205Sjwd#endif
3896205Sjwd#endif /* not lint */
3938042Sbde#include <sys/cdefs.h>
4096205Sjwd__FBSDID("$FreeBSD: head/bin/cp/utils.c 99109 2002-06-30 05:13:54Z obrien $");
4196205Sjwd
4238042Sbde#include <sys/param.h>
4396205Sjwd#include <sys/stat.h>
4496205Sjwd#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
4517308Speter#include <sys/mman.h>
4696205Sjwd#endif
4796205Sjwd
4817308Speter#include <err.h>
4996205Sjwd#include <errno.h>
5096205Sjwd#include <fcntl.h>
5196205Sjwd#include <fts.h>
5296205Sjwd#include <limits.h>
5396205Sjwd#include <stdio.h>
5496205Sjwd#include <stdlib.h>
5596205Sjwd#include <sysexits.h>
5696205Sjwd#include <unistd.h>
5796205Sjwd
5896205Sjwd#include "extern.h"
5996205Sjwd
6096205Sjwdint
6198775Sdilloncopy_file(FTSENT *entp, int dne)
6298723Sdillon{
6398723Sdillon	static char buf[MAXBSIZE];
6498723Sdillon	struct stat *fs;
6598723Sdillon	int ch, checkch, from_fd, rcount, rval, to_fd, wcount, wresid;
6638666Sjb	char *bufp;
6717308Speter#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
6838666Sjb	char *p;
6917308Speter#endif
7027910Sasami
7143226Sjkh	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
7243226Sjkh		warn("%s", entp->fts_path);
7343226Sjkh		return (1);
74108451Sschweikh	}
7527910Sasami
7638666Sjb	fs = entp->fts_statp;
7738666Sjb
7838666Sjb	/*
7927910Sasami	 * If the file exists and we're interactive, verify with the user.
8038666Sjb	 * If the file DNE, set the mode to be the from file, minus setuid
8138666Sjb	 * bits, modified by the umask; arguably wrong, but it makes copying
8243226Sjkh	 * executables work right and it's been that way forever.  (The
8343226Sjkh	 * other choice is 666 or'ed with the execute bits on the from file
8427910Sasami	 * modified by the umask.)
8538666Sjb	 */
8627910Sasami	if (!dne) {
8717308Speter#define YESNO "(y/n [n]) "
8838666Sjb		if (iflag) {
8938666Sjb			(void)fprintf(stderr, "overwrite %s? %s",
9017308Speter					to.p_path, YESNO);
9195509Sru			checkch = ch = getchar();
9295793Sru			while (ch != '\n' && ch != EOF)
9397252Sru				ch = getchar();
9496164Sru			if (checkch != 'y' && checkch != 'Y') {
9595146Sgshapiro				(void)close(from_fd);
962061Sjkh				(void)fprintf(stderr, "not overwritten\n");
9797769Sru				return (1);
9897252Sru			}
9997252Sru		}
10095730Sru
10195793Sru		if (fflag) {
10295730Sru		    /* remove existing destination file name,
10395730Sru		     * create a new file  */
10495730Sru		    (void)unlink(to.p_path);
105110035Sru		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
106107516Sru				 fs->st_mode & ~(S_ISUID | S_ISGID));
107110035Sru		} else
108110035Sru		    /* overwrite existing destination file name */
109110035Sru		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
110110035Sru	} else
11154324Smarcel		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
11217308Speter		    fs->st_mode & ~(S_ISUID | S_ISGID));
11338666Sjb
11417308Speter	if (to_fd == -1) {
11597252Sru		warn("%s", to.p_path);
11638666Sjb		(void)close(from_fd);
117110035Sru		return (1);
1182302Spaul	}
11939206Sjkh
12039206Sjkh	rval = 0;
12139206Sjkh
12273349Sru	/*
12317308Speter	 * Mmap and write if less than 8M (the limit is so we don't totally
12454324Smarcel	 * trash memory on big files.  This is really a minor hack, but it
12554324Smarcel	 * wins some CPU back.
12654324Smarcel	 */
12754324Smarcel#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
12854324Smarcel	if (S_ISREG(fs->st_mode) && fs->st_size <= 8 * 1048576) {
12954324Smarcel		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
13054324Smarcel		    MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
131103436Speter			warn("%s", entp->fts_path);
13254324Smarcel			rval = 1;
13354324Smarcel		} else {
13454324Smarcel			for (bufp = p, wresid = fs->st_size; ;
13554324Smarcel			    bufp += wcount, wresid -= wcount) {
13654324Smarcel				wcount = write(to_fd, bufp, wresid);
13754324Smarcel				if (wcount >= wresid || wcount <= 0)
138110035Sru					break;
13954324Smarcel			}
140110035Sru			if (wcount != wresid) {
141110035Sru				warn("%s", to.p_path);
14254324Smarcel				rval = 1;
14354324Smarcel			}
14454324Smarcel			/* Some systems don't unmap on close(2). */
14554324Smarcel			if (munmap(p, fs->st_size) < 0) {
14654324Smarcel				warn("%s", entp->fts_path);
147110035Sru				rval = 1;
14854324Smarcel			}
14954324Smarcel		}
15054324Smarcel	} else
151103436Speter#endif
15254324Smarcel	{
15354324Smarcel		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
15454324Smarcel			for (bufp = buf, wresid = rcount; ;
15595730Sru			    bufp += wcount, wresid -= wcount) {
15695730Sru				wcount = write(to_fd, bufp, wresid);
15795730Sru				if (wcount >= wresid || wcount <= 0)
15895730Sru					break;
15995730Sru			}
16095730Sru			if (wcount != wresid) {
16195730Sru				warn("%s", to.p_path);
16238666Sjb				rval = 1;
163107374Sru				break;
16417308Speter			}
16555678Smarcel		}
166110035Sru		if (rcount < 0) {
167110035Sru			warn("%s", entp->fts_path);
168110035Sru			rval = 1;
169110035Sru		}
170110035Sru	}
1712061Sjkh
17217308Speter	/*
173107516Sru	 * Don't remove the target even after an error.  The target might
174107374Sru	 * not be a regular file, or its attributes might be important,
17555678Smarcel	 * or its contents might be irreplaceable.  It would only be safe
176107516Sru	 * to remove it if we created it and its length is 0.
177107516Sru	 */
178107516Sru
179107516Sru	if (pflag && setfile(fs, to_fd))
180107516Sru		rval = 1;
181107516Sru	(void)close(from_fd);
182107516Sru	if (close(to_fd)) {
183107516Sru		warn("%s", to.p_path);
18455678Smarcel		rval = 1;
18555678Smarcel	}
18655678Smarcel	return (rval);
187107556Sbmah}
18855678Smarcel
18955678Smarcelint
190107516Srucopy_link(FTSENT *p, int exists)
191107516Sru{
192107516Sru	int len;
193107516Sru	char llink[PATH_MAX];
19455678Smarcel
19555678Smarcel	if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
19638666Sjb		warn("readlink: %s", p->fts_path);
19738666Sjb		return (1);
19817308Speter	}
19955678Smarcel	llink[len] = '\0';
20038978Sjb	if (exists && unlink(to.p_path)) {
2013626Swollman		warn("unlink: %s", to.p_path);
20217308Speter		return (1);
20338666Sjb	}
20417308Speter	if (symlink(llink, to.p_path)) {
20543226Sjkh		warn("symlink: %s", llink);
20643226Sjkh		return (1);
20743226Sjkh	}
20838666Sjb	return (0);
20938666Sjb}
210110035Sru
211103985Sphkint
212103985Sphkcopy_fifo(struct stat *from_stat, int exists)
213103985Sphk{
214103985Sphk	if (exists && unlink(to.p_path)) {
215103985Sphk		warn("unlink: %s", to.p_path);
216103985Sphk		return (1);
217111089Sphk	}
218103985Sphk	if (mkfifo(to.p_path, from_stat->st_mode)) {
219103985Sphk		warn("mkfifo: %s", to.p_path);
220103985Sphk		return (1);
221103985Sphk	}
222103985Sphk	return (pflag ? setfile(from_stat, 0) : 0);
223104277Sphk}
224104277Sphk
225103985Sphkint
226103985Sphkcopy_special(struct stat *from_stat, int exists)
227111089Sphk{
228103985Sphk	if (exists && unlink(to.p_path)) {
229103985Sphk		warn("unlink: %s", to.p_path);
230107884Simp		return (1);
231104370Sphk	}
232104370Sphk	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
233104370Sphk		warn("mknod: %s", to.p_path);
234107884Simp		return (1);
235107884Simp	}
236107884Simp	return (pflag ? setfile(from_stat, 0) : 0);
237107884Simp}
238107884Simp
239111089Sphkint
240107884Simpsetfile(struct stat *fs, int fd)
241107884Simp{
242103985Sphk	static struct timeval tv[2];
243107884Simp	struct stat ts;
244103985Sphk	int rval;
245103985Sphk	int gotstat;
246111089Sphk
247103985Sphk	rval = 0;
248103985Sphk	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
249103985Sphk		       S_IRWXU | S_IRWXG | S_IRWXO;
250103985Sphk
251111089Sphk	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
252103985Sphk	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
253111089Sphk	if (utimes(to.p_path, tv)) {
254103985Sphk		warn("utimes: %s", to.p_path);
255111089Sphk		rval = 1;
256103985Sphk	}
257103985Sphk	if (fd ? fstat(fd, &ts) : stat(to.p_path, &ts))
258		gotstat = 0;
259	else {
260		gotstat = 1;
261		ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
262			      S_IRWXU | S_IRWXG | S_IRWXO;
263	}
264	/*
265	 * Changing the ownership probably won't succeed, unless we're root
266	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
267	 * the mode; current BSD behavior is to remove all setuid bits on
268	 * chown.  If chown fails, lose setuid/setgid bits.
269	 */
270	if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
271		if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
272		    chown(to.p_path, fs->st_uid, fs->st_gid)) {
273			if (errno != EPERM) {
274				warn("chown: %s", to.p_path);
275				rval = 1;
276			}
277			fs->st_mode &= ~(S_ISUID | S_ISGID);
278		}
279
280	if (!gotstat || fs->st_mode != ts.st_mode)
281		if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
282			warn("chmod: %s", to.p_path);
283			rval = 1;
284		}
285
286	if (!gotstat || fs->st_flags != ts.st_flags)
287		if (fd ?
288		    fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) {
289			warn("chflags: %s", to.p_path);
290			rval = 1;
291		}
292
293	return (rval);
294}
295
296void
297usage(void)
298{
299
300	(void)fprintf(stderr, "%s\n%s\n",
301"usage: cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target",
302"       cp [-R [-H | -L | -P]] [-f | -i] [-pv] src1 ... srcN directory");
303	exit(EX_USAGE);
304}
305