utils.c revision 149790
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 * 4. Neither the name of the University nor the names of its contributors
141556Srgrimes *    may be used to endorse or promote products derived from this software
151556Srgrimes *    without specific prior written permission.
161556Srgrimes *
171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271556Srgrimes * SUCH DAMAGE.
281556Srgrimes */
291556Srgrimes
301556Srgrimes#ifndef lint
3135773Scharnier#if 0
3236003Scharnierstatic char sccsid[] = "@(#)utils.c	8.3 (Berkeley) 4/1/94";
3335773Scharnier#endif
341556Srgrimes#endif /* not lint */
3599109Sobrien#include <sys/cdefs.h>
3699109Sobrien__FBSDID("$FreeBSD: head/bin/cp/utils.c 149790 2005-09-05 04:36:08Z csjp $");
371556Srgrimes
38149790Scsjp#include <sys/types.h>
39149790Scsjp#include <sys/acl.h>
401556Srgrimes#include <sys/param.h>
411556Srgrimes#include <sys/stat.h>
4235773Scharnier#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
431556Srgrimes#include <sys/mman.h>
4435773Scharnier#endif
451556Srgrimes
461556Srgrimes#include <err.h>
471556Srgrimes#include <errno.h>
481556Srgrimes#include <fcntl.h>
491556Srgrimes#include <fts.h>
5076693Simp#include <limits.h>
511556Srgrimes#include <stdio.h>
5278469Sdes#include <stdlib.h>
5356420Smharo#include <sysexits.h>
541556Srgrimes#include <unistd.h>
551556Srgrimes
561556Srgrimes#include "extern.h"
57113209Smdodd#define	cp_pct(x,y)	(int)(100.0 * (double)(x) / (double)(y))
581556Srgrimes
591556Srgrimesint
60105395Smarkmcopy_file(const FTSENT *entp, int dne)
611556Srgrimes{
621556Srgrimes	static char buf[MAXBSIZE];
6378070Sbde	struct stat *fs;
6499363Smarkm	int ch, checkch, from_fd, rcount, rval, to_fd;
6599363Smarkm	ssize_t wcount;
6699363Smarkm	size_t wresid;
67113209Smdodd	size_t wtotal;
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]) "
90100538Sjohan		if (nflag) {
91100538Sjohan			if (vflag)
92100538Sjohan				printf("%s not overwritten\n", to.p_path);
93130102Stjr			(void)close(from_fd);
94100538Sjohan			return (0);
95100538Sjohan		} else if (iflag) {
9630088Swosch			(void)fprintf(stderr, "overwrite %s? %s",
9730088Swosch					to.p_path, YESNO);
981556Srgrimes			checkch = ch = getchar();
991556Srgrimes			while (ch != '\n' && ch != EOF)
1001556Srgrimes				ch = getchar();
10114416Swosch			if (checkch != 'y' && checkch != 'Y') {
1021556Srgrimes				(void)close(from_fd);
10330088Swosch				(void)fprintf(stderr, "not overwritten\n");
10456420Smharo				return (1);
1051556Srgrimes			}
1061556Srgrimes		}
10714416Swosch
10814416Swosch		if (fflag) {
10914416Swosch		    /* remove existing destination file name,
11014416Swosch		     * create a new file  */
11114416Swosch		    (void)unlink(to.p_path);
11214416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
11314416Swosch				 fs->st_mode & ~(S_ISUID | S_ISGID));
11414416Swosch		} else
11514416Swosch		    /* overwrite existing destination file name */
11614416Swosch		    to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
1171556Srgrimes	} else
1181556Srgrimes		to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
1191556Srgrimes		    fs->st_mode & ~(S_ISUID | S_ISGID));
1201556Srgrimes
1211556Srgrimes	if (to_fd == -1) {
1221556Srgrimes		warn("%s", to.p_path);
1231556Srgrimes		(void)close(from_fd);
12491087Smarkm		return (1);
1251556Srgrimes	}
1261556Srgrimes
1271556Srgrimes	rval = 0;
1281556Srgrimes
1291556Srgrimes	/*
1301556Srgrimes	 * Mmap and write if less than 8M (the limit is so we don't totally
1311556Srgrimes	 * trash memory on big files.  This is really a minor hack, but it
1321556Srgrimes	 * wins some CPU back.
1331556Srgrimes	 */
1341556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
135122601Salc	if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
136122601Salc	    fs->st_size <= 8 * 1048576) {
1371556Srgrimes		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
13821786Salex		    MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
1391556Srgrimes			warn("%s", entp->fts_path);
1401556Srgrimes			rval = 1;
1411556Srgrimes		} else {
142113209Smdodd			wtotal = 0;
14332540Sbde			for (bufp = p, wresid = fs->st_size; ;
14499363Smarkm			    bufp += wcount, wresid -= (size_t)wcount) {
14532540Sbde				wcount = write(to_fd, bufp, wresid);
146113209Smdodd				wtotal += wcount;
147113209Smdodd				if (info) {
148113209Smdodd					info = 0;
149113209Smdodd					(void)fprintf(stderr,
150113209Smdodd						"%s -> %s %3d%%\n",
151113209Smdodd						entp->fts_path, to.p_path,
152113209Smdodd						cp_pct(wtotal, fs->st_size));
153113209Smdodd
154113209Smdodd				}
155101093Smarkm				if (wcount >= (ssize_t)wresid || wcount <= 0)
15632540Sbde					break;
15732540Sbde			}
158101093Smarkm			if (wcount != (ssize_t)wresid) {
1591556Srgrimes				warn("%s", to.p_path);
1601556Srgrimes				rval = 1;
1611556Srgrimes			}
1621556Srgrimes			/* Some systems don't unmap on close(2). */
1631556Srgrimes			if (munmap(p, fs->st_size) < 0) {
1641556Srgrimes				warn("%s", entp->fts_path);
1651556Srgrimes				rval = 1;
1661556Srgrimes			}
1671556Srgrimes		}
1681556Srgrimes	} else
1691556Srgrimes#endif
1701556Srgrimes	{
171113209Smdodd		wtotal = 0;
1721556Srgrimes		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
17332540Sbde			for (bufp = buf, wresid = rcount; ;
17432540Sbde			    bufp += wcount, wresid -= wcount) {
17532540Sbde				wcount = write(to_fd, bufp, wresid);
176113209Smdodd				wtotal += wcount;
177113209Smdodd				if (info) {
178113209Smdodd					info = 0;
179113209Smdodd					(void)fprintf(stderr,
180113209Smdodd						"%s -> %s %3d%%\n",
181113209Smdodd						entp->fts_path, to.p_path,
182113209Smdodd						cp_pct(wtotal, fs->st_size));
183113209Smdodd
184113209Smdodd				}
185101093Smarkm				if (wcount >= (ssize_t)wresid || wcount <= 0)
18632540Sbde					break;
18732540Sbde			}
188101093Smarkm			if (wcount != (ssize_t)wresid) {
1891556Srgrimes				warn("%s", to.p_path);
1901556Srgrimes				rval = 1;
1911556Srgrimes				break;
1921556Srgrimes			}
1931556Srgrimes		}
1941556Srgrimes		if (rcount < 0) {
1951556Srgrimes			warn("%s", entp->fts_path);
1961556Srgrimes			rval = 1;
1971556Srgrimes		}
1981556Srgrimes	}
1991556Srgrimes
2009220Sbde	/*
2019220Sbde	 * Don't remove the target even after an error.  The target might
2029220Sbde	 * not be a regular file, or its attributes might be important,
20346684Skris	 * or its contents might be irreplaceable.  It would only be safe
2049220Sbde	 * to remove it if we created it and its length is 0.
2059220Sbde	 */
2061556Srgrimes
2071556Srgrimes	if (pflag && setfile(fs, to_fd))
2081556Srgrimes		rval = 1;
209149790Scsjp	if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
210149790Scsjp		rval = 1;
2111556Srgrimes	(void)close(from_fd);
2121556Srgrimes	if (close(to_fd)) {
2131556Srgrimes		warn("%s", to.p_path);
2141556Srgrimes		rval = 1;
2151556Srgrimes	}
2161556Srgrimes	return (rval);
2171556Srgrimes}
2181556Srgrimes
2191556Srgrimesint
220105395Smarkmcopy_link(const FTSENT *p, int exists)
2211556Srgrimes{
2221556Srgrimes	int len;
22391087Smarkm	char llink[PATH_MAX];
2241556Srgrimes
22591087Smarkm	if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
2261556Srgrimes		warn("readlink: %s", p->fts_path);
2271556Srgrimes		return (1);
2281556Srgrimes	}
22991087Smarkm	llink[len] = '\0';
2301556Srgrimes	if (exists && unlink(to.p_path)) {
2311556Srgrimes		warn("unlink: %s", to.p_path);
2321556Srgrimes		return (1);
2331556Srgrimes	}
23491087Smarkm	if (symlink(llink, to.p_path)) {
23591087Smarkm		warn("symlink: %s", llink);
2361556Srgrimes		return (1);
2371556Srgrimes	}
238116673Sjmg	return (pflag ? setfile(p->fts_statp, -1) : 0);
2391556Srgrimes}
2401556Srgrimes
2411556Srgrimesint
24290107Simpcopy_fifo(struct stat *from_stat, int exists)
2431556Srgrimes{
2441556Srgrimes	if (exists && unlink(to.p_path)) {
2451556Srgrimes		warn("unlink: %s", to.p_path);
2461556Srgrimes		return (1);
2471556Srgrimes	}
2481556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
2491556Srgrimes		warn("mkfifo: %s", to.p_path);
2501556Srgrimes		return (1);
2511556Srgrimes	}
252116673Sjmg	return (pflag ? setfile(from_stat, -1) : 0);
2531556Srgrimes}
2541556Srgrimes
2551556Srgrimesint
25690107Simpcopy_special(struct stat *from_stat, int exists)
2571556Srgrimes{
2581556Srgrimes	if (exists && unlink(to.p_path)) {
2591556Srgrimes		warn("unlink: %s", to.p_path);
2601556Srgrimes		return (1);
2611556Srgrimes	}
2621556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
2631556Srgrimes		warn("mknod: %s", to.p_path);
2641556Srgrimes		return (1);
2651556Srgrimes	}
266116673Sjmg	return (pflag ? setfile(from_stat, -1) : 0);
2671556Srgrimes}
2681556Srgrimes
2691556Srgrimesint
27090107Simpsetfile(struct stat *fs, int fd)
2711556Srgrimes{
2721556Srgrimes	static struct timeval tv[2];
27336838Speter	struct stat ts;
274116673Sjmg	int rval, gotstat, islink, fdval;
2751556Srgrimes
2761556Srgrimes	rval = 0;
277116673Sjmg	fdval = fd != -1;
278116673Sjmg	islink = !fdval && S_ISLNK(fs->st_mode);
27911146Sbde	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
28011146Sbde		       S_IRWXU | S_IRWXG | S_IRWXO;
2811556Srgrimes
2821556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
2831556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
284116673Sjmg	if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
285116673Sjmg		warn("%sutimes: %s", islink ? "l" : "", to.p_path);
2861556Srgrimes		rval = 1;
2871556Srgrimes	}
288116673Sjmg	if (fdval ? fstat(fd, &ts) :
289116673Sjmg	    (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
29036838Speter		gotstat = 0;
29136838Speter	else {
29236838Speter		gotstat = 1;
29336838Speter		ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
29436838Speter			      S_IRWXU | S_IRWXG | S_IRWXO;
29536838Speter	}
2961556Srgrimes	/*
2971556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
2981556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
2991556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
3001556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
3011556Srgrimes	 */
30236838Speter	if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
303116673Sjmg		if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
304116673Sjmg		    (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
305116673Sjmg		    chown(to.p_path, fs->st_uid, fs->st_gid))) {
30636838Speter			if (errno != EPERM) {
30736838Speter				warn("chown: %s", to.p_path);
30836838Speter				rval = 1;
30936838Speter			}
31036838Speter			fs->st_mode &= ~(S_ISUID | S_ISGID);
31136838Speter		}
31236838Speter
31336838Speter	if (!gotstat || fs->st_mode != ts.st_mode)
314116673Sjmg		if (fdval ? fchmod(fd, fs->st_mode) :
315116673Sjmg		    (islink ? lchmod(to.p_path, fs->st_mode) :
316116673Sjmg		    chmod(to.p_path, fs->st_mode))) {
31787652Smckay			warn("chmod: %s", to.p_path);
3181556Srgrimes			rval = 1;
3191556Srgrimes		}
3201556Srgrimes
32136838Speter	if (!gotstat || fs->st_flags != ts.st_flags)
322116673Sjmg		if (fdval ?
323116673Sjmg		    fchflags(fd, fs->st_flags) :
324116673Sjmg		    (islink ? (errno = ENOSYS) :
325116673Sjmg		    chflags(to.p_path, fs->st_flags))) {
32636838Speter			warn("chflags: %s", to.p_path);
32736838Speter			rval = 1;
32836838Speter		}
32936838Speter
3301556Srgrimes	return (rval);
3311556Srgrimes}
3321556Srgrimes
333149790Scsjpint
334149790Scsjppreserve_fd_acls(int source_fd, int dest_fd)
335149790Scsjp{
336149790Scsjp	struct acl *aclp;
337149790Scsjp	acl_t acl;
338149790Scsjp
339149790Scsjp	if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 ||
340149790Scsjp	    fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1)
341149790Scsjp		return (0);
342149790Scsjp	acl = acl_get_fd(source_fd);
343149790Scsjp	if (acl == NULL) {
344149790Scsjp		warn("failed to get acl entries while setting %s", to.p_path);
345149790Scsjp		return (1);
346149790Scsjp	}
347149790Scsjp	aclp = &acl->ats_acl;
348149790Scsjp	if (aclp->acl_cnt == 3)
349149790Scsjp		return (0);
350149790Scsjp	if (acl_set_fd(dest_fd, acl) < 0) {
351149790Scsjp		warn("failed to set acl entries for %s", to.p_path);
352149790Scsjp		return (1);
353149790Scsjp	}
354149790Scsjp	return (0);
355149790Scsjp}
356149790Scsjp
357149790Scsjpint
358149790Scsjppreserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
359149790Scsjp{
360149790Scsjp	acl_t (*aclgetf)(const char *, acl_type_t);
361149790Scsjp	int (*aclsetf)(const char *, acl_type_t, acl_t);
362149790Scsjp	struct acl *aclp;
363149790Scsjp	acl_t acl;
364149790Scsjp
365149790Scsjp	if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 ||
366149790Scsjp	    pathconf(dest_dir, _PC_ACL_EXTENDED) != 1)
367149790Scsjp		return (0);
368149790Scsjp	/*
369149790Scsjp	 * If the file is a link we will not follow it
370149790Scsjp	 */
371149790Scsjp	if (S_ISLNK(fs->st_mode)) {
372149790Scsjp		aclgetf = acl_get_link_np;
373149790Scsjp		aclsetf = acl_set_link_np;
374149790Scsjp	} else {
375149790Scsjp		aclgetf = acl_get_file;
376149790Scsjp		aclsetf = acl_set_file;
377149790Scsjp	}
378149790Scsjp	/*
379149790Scsjp	 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
380149790Scsjp	 * size ACL will be returned. So it is not safe to simply
381149790Scsjp	 * check the pointer to see if the default ACL is present.
382149790Scsjp	 */
383149790Scsjp	acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
384149790Scsjp	if (acl == NULL) {
385149790Scsjp		warn("failed to get default acl entries on %s",
386149790Scsjp		    source_dir);
387149790Scsjp		return (1);
388149790Scsjp	}
389149790Scsjp	aclp = &acl->ats_acl;
390149790Scsjp	if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
391149790Scsjp	    ACL_TYPE_DEFAULT, acl) < 0) {
392149790Scsjp		warn("failed to set default acl entries on %s",
393149790Scsjp		    dest_dir);
394149790Scsjp		return (1);
395149790Scsjp	}
396149790Scsjp	acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
397149790Scsjp	if (acl == NULL) {
398149790Scsjp		warn("failed to get acl entries on %s", source_dir);
399149790Scsjp		return (1);
400149790Scsjp	}
401149790Scsjp	aclp = &acl->ats_acl;
402149790Scsjp	if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
403149790Scsjp		warn("failed to set acl entries on %s", dest_dir);
404149790Scsjp		return (1);
405149790Scsjp	}
406149790Scsjp	return (0);
407149790Scsjp}
408149790Scsjp
4091556Srgrimesvoid
41090107Simpusage(void)
4111556Srgrimes{
41250543Smharo
4131556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
414141578Sru"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] source_file target_file",
415141578Sru"       cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] source_file ... "
416141578Sru"target_directory");
41756420Smharo	exit(EX_USAGE);
4181556Srgrimes}
419