utils.c revision 163075
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 163075 2006-10-07 12:14:50Z maxim $");
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"
571556Srgrimes
58163074Smaxim#define	cp_pct(x, y)	((y == 0) ? 0 : (int)(100.0 * (x) / (y)))
59163074Smaxim
601556Srgrimesint
61105395Smarkmcopy_file(const FTSENT *entp, int dne)
621556Srgrimes{
631556Srgrimes	static char buf[MAXBSIZE];
6478070Sbde	struct stat *fs;
6599363Smarkm	ssize_t wcount;
6699363Smarkm	size_t wresid;
67163049Smaxim	off_t wtotal;
68163075Smaxim	int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
6932540Sbde	char *bufp;
701556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
711556Srgrimes	char *p;
721556Srgrimes#endif
738855Srgrimes
741556Srgrimes	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
751556Srgrimes		warn("%s", entp->fts_path);
761556Srgrimes		return (1);
771556Srgrimes	}
781556Srgrimes
791556Srgrimes	fs = entp->fts_statp;
801556Srgrimes
811556Srgrimes	/*
821556Srgrimes	 * If the file exists and we're interactive, verify with the user.
831556Srgrimes	 * If the file DNE, set the mode to be the from file, minus setuid
841556Srgrimes	 * bits, modified by the umask; arguably wrong, but it makes copying
851556Srgrimes	 * executables work right and it's been that way forever.  (The
861556Srgrimes	 * other choice is 666 or'ed with the execute bits on the from file
871556Srgrimes	 * modified by the umask.)
881556Srgrimes	 */
891556Srgrimes	if (!dne) {
9030088Swosch#define YESNO "(y/n [n]) "
91100538Sjohan		if (nflag) {
92100538Sjohan			if (vflag)
93100538Sjohan				printf("%s not overwritten\n", to.p_path);
94130102Stjr			(void)close(from_fd);
95100538Sjohan			return (0);
96100538Sjohan		} else if (iflag) {
9730088Swosch			(void)fprintf(stderr, "overwrite %s? %s",
9830088Swosch					to.p_path, YESNO);
991556Srgrimes			checkch = ch = getchar();
1001556Srgrimes			while (ch != '\n' && ch != EOF)
1011556Srgrimes				ch = getchar();
10214416Swosch			if (checkch != 'y' && checkch != 'Y') {
1031556Srgrimes				(void)close(from_fd);
10430088Swosch				(void)fprintf(stderr, "not overwritten\n");
10556420Smharo				return (1);
1061556Srgrimes			}
1071556Srgrimes		}
10814416Swosch
10914416Swosch		if (fflag) {
11014416Swosch		    /* remove existing destination file name,
11114416Swosch		     * create a new file  */
11214416Swosch		    (void)unlink(to.p_path);
113161586Sjulian				if (!lflag)
114161586Sjulian		    	to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
115161586Sjulian				  fs->st_mode & ~(S_ISUID | S_ISGID));
116161586Sjulian		} else {
117161586Sjulian				if (!lflag)
118161586Sjulian		    	/* overwrite existing destination file name */
119161586Sjulian		    	to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
120161586Sjulian		}
121161586Sjulian	} else {
122161586Sjulian		if (!lflag)
123161586Sjulian			to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
124161586Sjulian		  fs->st_mode & ~(S_ISUID | S_ISGID));
125161586Sjulian	}
126161586Sjulian
1271556Srgrimes	if (to_fd == -1) {
1281556Srgrimes		warn("%s", to.p_path);
1291556Srgrimes		(void)close(from_fd);
13091087Smarkm		return (1);
1311556Srgrimes	}
1321556Srgrimes
1331556Srgrimes	rval = 0;
1341556Srgrimes
135161586Sjulian	if (!lflag) {
136161586Sjulian		/*
137161586Sjulian		 * Mmap and write if less than 8M (the limit is so we don't totally
138161586Sjulian		 * trash memory on big files.  This is really a minor hack, but it
139161586Sjulian		 * wins some CPU back.
140161586Sjulian		 */
1411556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
142161586Sjulian		if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
143161586Sjulian	    	fs->st_size <= 8 * 1048576) {
144161586Sjulian			if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
145161586Sjulian		    	MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
1461556Srgrimes				warn("%s", entp->fts_path);
1471556Srgrimes				rval = 1;
148161586Sjulian			} else {
149161586Sjulian				wtotal = 0;
150161586Sjulian				for (bufp = p, wresid = fs->st_size; ;
151161586Sjulian			    	bufp += wcount, wresid -= (size_t)wcount) {
152161586Sjulian					wcount = write(to_fd, bufp, wresid);
153163074Smaxim					if (wcount <= 0)
154163074Smaxim						break;
155161586Sjulian					wtotal += wcount;
156161586Sjulian					if (info) {
157161586Sjulian						info = 0;
158161586Sjulian						(void)fprintf(stderr,
159163075Smaxim						    "%s -> %s %3d%%\n",
160163075Smaxim						    entp->fts_path, to.p_path,
161163075Smaxim						    cp_pct(wtotal, fs->st_size));
162161586Sjulian					}
163163074Smaxim					if (wcount >= (ssize_t)wresid)
164161586Sjulian						break;
165161586Sjulian				}
166161586Sjulian				if (wcount != (ssize_t)wresid) {
167161586Sjulian					warn("%s", to.p_path);
168161586Sjulian					rval = 1;
169161586Sjulian				}
170161586Sjulian				/* Some systems don't unmap on close(2). */
171161586Sjulian				if (munmap(p, fs->st_size) < 0) {
172161586Sjulian					warn("%s", entp->fts_path);
173161586Sjulian					rval = 1;
174161586Sjulian				}
1751556Srgrimes			}
176161586Sjulian		} else
1771556Srgrimes#endif
178161586Sjulian		{
179161586Sjulian			wtotal = 0;
180161586Sjulian			while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
181161586Sjulian				for (bufp = buf, wresid = rcount; ;
182161586Sjulian			    	bufp += wcount, wresid -= wcount) {
183161586Sjulian					wcount = write(to_fd, bufp, wresid);
184163074Smaxim					if (wcount <= 0)
185163074Smaxim						break;
186161586Sjulian					wtotal += wcount;
187161586Sjulian					if (info) {
188161586Sjulian						info = 0;
189161586Sjulian						(void)fprintf(stderr,
190163075Smaxim						    "%s -> %s %3d%%\n",
191163075Smaxim						    entp->fts_path, to.p_path,
192163075Smaxim						    cp_pct(wtotal, fs->st_size));
193161586Sjulian					}
194163074Smaxim					if (wcount >= (ssize_t)wresid)
195161586Sjulian						break;
196113209Smdodd				}
197161586Sjulian				if (wcount != (ssize_t)wresid) {
198161586Sjulian					warn("%s", to.p_path);
199161586Sjulian					rval = 1;
20032540Sbde					break;
201161586Sjulian				}
20232540Sbde			}
203161586Sjulian			if (rcount < 0) {
204161586Sjulian				warn("%s", entp->fts_path);
2051556Srgrimes				rval = 1;
2061556Srgrimes			}
2071556Srgrimes		}
208161586Sjulian	} else {
209161586Sjulian		if (link(entp->fts_path, to.p_path)) {
210161586Sjulian			warn("%s", to.p_path);
2111556Srgrimes			rval = 1;
2121556Srgrimes		}
2131556Srgrimes	}
214161586Sjulian	(void)close(from_fd);
215161586Sjulian
2169220Sbde	/*
2179220Sbde	 * Don't remove the target even after an error.  The target might
2189220Sbde	 * not be a regular file, or its attributes might be important,
21946684Skris	 * or its contents might be irreplaceable.  It would only be safe
2209220Sbde	 * to remove it if we created it and its length is 0.
2219220Sbde	 */
2221556Srgrimes
223161586Sjulian	if (!lflag) {
224161586Sjulian		if (pflag && setfile(fs, to_fd))
225161586Sjulian			rval = 1;
226161586Sjulian		if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
227161586Sjulian			rval = 1;
228161586Sjulian		(void)close(from_fd);
229161586Sjulian		if (close(to_fd)) {
230161586Sjulian			warn("%s", to.p_path);
231161586Sjulian			rval = 1;
232161586Sjulian		}
2331556Srgrimes	}
2341556Srgrimes	return (rval);
2351556Srgrimes}
2361556Srgrimes
2371556Srgrimesint
238105395Smarkmcopy_link(const FTSENT *p, int exists)
2391556Srgrimes{
2401556Srgrimes	int len;
24191087Smarkm	char llink[PATH_MAX];
2421556Srgrimes
24391087Smarkm	if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
2441556Srgrimes		warn("readlink: %s", p->fts_path);
2451556Srgrimes		return (1);
2461556Srgrimes	}
24791087Smarkm	llink[len] = '\0';
2481556Srgrimes	if (exists && unlink(to.p_path)) {
2491556Srgrimes		warn("unlink: %s", to.p_path);
2501556Srgrimes		return (1);
2511556Srgrimes	}
25291087Smarkm	if (symlink(llink, to.p_path)) {
25391087Smarkm		warn("symlink: %s", llink);
2541556Srgrimes		return (1);
2551556Srgrimes	}
256116673Sjmg	return (pflag ? setfile(p->fts_statp, -1) : 0);
2571556Srgrimes}
2581556Srgrimes
2591556Srgrimesint
26090107Simpcopy_fifo(struct stat *from_stat, int exists)
2611556Srgrimes{
2621556Srgrimes	if (exists && unlink(to.p_path)) {
2631556Srgrimes		warn("unlink: %s", to.p_path);
2641556Srgrimes		return (1);
2651556Srgrimes	}
2661556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
2671556Srgrimes		warn("mkfifo: %s", to.p_path);
2681556Srgrimes		return (1);
2691556Srgrimes	}
270116673Sjmg	return (pflag ? setfile(from_stat, -1) : 0);
2711556Srgrimes}
2721556Srgrimes
2731556Srgrimesint
27490107Simpcopy_special(struct stat *from_stat, int exists)
2751556Srgrimes{
2761556Srgrimes	if (exists && unlink(to.p_path)) {
2771556Srgrimes		warn("unlink: %s", to.p_path);
2781556Srgrimes		return (1);
2791556Srgrimes	}
2801556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
2811556Srgrimes		warn("mknod: %s", to.p_path);
2821556Srgrimes		return (1);
2831556Srgrimes	}
284116673Sjmg	return (pflag ? setfile(from_stat, -1) : 0);
2851556Srgrimes}
2861556Srgrimes
2871556Srgrimesint
28890107Simpsetfile(struct stat *fs, int fd)
2891556Srgrimes{
2901556Srgrimes	static struct timeval tv[2];
29136838Speter	struct stat ts;
292116673Sjmg	int rval, gotstat, islink, fdval;
2931556Srgrimes
2941556Srgrimes	rval = 0;
295116673Sjmg	fdval = fd != -1;
296116673Sjmg	islink = !fdval && S_ISLNK(fs->st_mode);
29711146Sbde	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
29811146Sbde		       S_IRWXU | S_IRWXG | S_IRWXO;
2991556Srgrimes
3001556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
3011556Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
302116673Sjmg	if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
303116673Sjmg		warn("%sutimes: %s", islink ? "l" : "", to.p_path);
3041556Srgrimes		rval = 1;
3051556Srgrimes	}
306116673Sjmg	if (fdval ? fstat(fd, &ts) :
307116673Sjmg	    (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
30836838Speter		gotstat = 0;
30936838Speter	else {
31036838Speter		gotstat = 1;
31136838Speter		ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
31236838Speter			      S_IRWXU | S_IRWXG | S_IRWXO;
31336838Speter	}
3141556Srgrimes	/*
3151556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
3161556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
3171556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
3181556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
3191556Srgrimes	 */
32036838Speter	if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
321116673Sjmg		if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
322116673Sjmg		    (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
323116673Sjmg		    chown(to.p_path, fs->st_uid, fs->st_gid))) {
32436838Speter			if (errno != EPERM) {
32536838Speter				warn("chown: %s", to.p_path);
32636838Speter				rval = 1;
32736838Speter			}
32836838Speter			fs->st_mode &= ~(S_ISUID | S_ISGID);
32936838Speter		}
33036838Speter
33136838Speter	if (!gotstat || fs->st_mode != ts.st_mode)
332116673Sjmg		if (fdval ? fchmod(fd, fs->st_mode) :
333116673Sjmg		    (islink ? lchmod(to.p_path, fs->st_mode) :
334116673Sjmg		    chmod(to.p_path, fs->st_mode))) {
33587652Smckay			warn("chmod: %s", to.p_path);
3361556Srgrimes			rval = 1;
3371556Srgrimes		}
3381556Srgrimes
33936838Speter	if (!gotstat || fs->st_flags != ts.st_flags)
340116673Sjmg		if (fdval ?
341116673Sjmg		    fchflags(fd, fs->st_flags) :
342116673Sjmg		    (islink ? (errno = ENOSYS) :
343116673Sjmg		    chflags(to.p_path, fs->st_flags))) {
34436838Speter			warn("chflags: %s", to.p_path);
34536838Speter			rval = 1;
34636838Speter		}
34736838Speter
3481556Srgrimes	return (rval);
3491556Srgrimes}
3501556Srgrimes
351149790Scsjpint
352149790Scsjppreserve_fd_acls(int source_fd, int dest_fd)
353149790Scsjp{
354149790Scsjp	struct acl *aclp;
355149790Scsjp	acl_t acl;
356149790Scsjp
357149790Scsjp	if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 ||
358149790Scsjp	    fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1)
359149790Scsjp		return (0);
360149790Scsjp	acl = acl_get_fd(source_fd);
361149790Scsjp	if (acl == NULL) {
362149790Scsjp		warn("failed to get acl entries while setting %s", to.p_path);
363149790Scsjp		return (1);
364149790Scsjp	}
365149790Scsjp	aclp = &acl->ats_acl;
366149790Scsjp	if (aclp->acl_cnt == 3)
367149790Scsjp		return (0);
368149790Scsjp	if (acl_set_fd(dest_fd, acl) < 0) {
369149790Scsjp		warn("failed to set acl entries for %s", to.p_path);
370149790Scsjp		return (1);
371149790Scsjp	}
372149790Scsjp	return (0);
373149790Scsjp}
374149790Scsjp
375149790Scsjpint
376149790Scsjppreserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
377149790Scsjp{
378149790Scsjp	acl_t (*aclgetf)(const char *, acl_type_t);
379149790Scsjp	int (*aclsetf)(const char *, acl_type_t, acl_t);
380149790Scsjp	struct acl *aclp;
381149790Scsjp	acl_t acl;
382149790Scsjp
383149790Scsjp	if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 ||
384149790Scsjp	    pathconf(dest_dir, _PC_ACL_EXTENDED) != 1)
385149790Scsjp		return (0);
386149790Scsjp	/*
387149790Scsjp	 * If the file is a link we will not follow it
388149790Scsjp	 */
389149790Scsjp	if (S_ISLNK(fs->st_mode)) {
390149790Scsjp		aclgetf = acl_get_link_np;
391149790Scsjp		aclsetf = acl_set_link_np;
392149790Scsjp	} else {
393149790Scsjp		aclgetf = acl_get_file;
394149790Scsjp		aclsetf = acl_set_file;
395149790Scsjp	}
396149790Scsjp	/*
397149790Scsjp	 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
398149790Scsjp	 * size ACL will be returned. So it is not safe to simply
399149790Scsjp	 * check the pointer to see if the default ACL is present.
400149790Scsjp	 */
401149790Scsjp	acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
402149790Scsjp	if (acl == NULL) {
403149790Scsjp		warn("failed to get default acl entries on %s",
404149790Scsjp		    source_dir);
405149790Scsjp		return (1);
406149790Scsjp	}
407149790Scsjp	aclp = &acl->ats_acl;
408149790Scsjp	if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
409149790Scsjp	    ACL_TYPE_DEFAULT, acl) < 0) {
410149790Scsjp		warn("failed to set default acl entries on %s",
411149790Scsjp		    dest_dir);
412149790Scsjp		return (1);
413149790Scsjp	}
414149790Scsjp	acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
415149790Scsjp	if (acl == NULL) {
416149790Scsjp		warn("failed to get acl entries on %s", source_dir);
417149790Scsjp		return (1);
418149790Scsjp	}
419149790Scsjp	aclp = &acl->ats_acl;
420149790Scsjp	if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
421149790Scsjp		warn("failed to set acl entries on %s", dest_dir);
422149790Scsjp		return (1);
423149790Scsjp	}
424149790Scsjp	return (0);
425149790Scsjp}
426149790Scsjp
4271556Srgrimesvoid
42890107Simpusage(void)
4291556Srgrimes{
43050543Smharo
4311556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
432161608Sru"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-lpv] source_file target_file",
433161608Sru"       cp [-R [-H | -L | -P]] [-f | -i | -n] [-lpv] source_file ... "
434141578Sru"target_directory");
43556420Smharo	exit(EX_USAGE);
4361556Srgrimes}
437