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$");
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
60184471Sivoras/* Memory strategy threshold, in pages: if physmem is larger then this, use a
61184471Sivoras * large buffer */
62184471Sivoras#define PHYSPAGES_THRESHOLD (32*1024)
63184471Sivoras
64184471Sivoras/* Maximum buffer size in bytes - do not allow it to grow larger than this */
65184471Sivoras#define BUFSIZE_MAX (2*1024*1024)
66184471Sivoras
67184471Sivoras/* Small (default) buffer size in bytes. It's inefficient for this to be
68184471Sivoras * smaller than MAXPHYS */
69184471Sivoras#define BUFSIZE_SMALL (MAXPHYS)
70184471Sivoras
711556Srgrimesint
72105395Smarkmcopy_file(const FTSENT *entp, int dne)
731556Srgrimes{
74184471Sivoras	static char *buf = NULL;
75184471Sivoras	static size_t bufsize;
7678070Sbde	struct stat *fs;
7799363Smarkm	ssize_t wcount;
7899363Smarkm	size_t wresid;
79163049Smaxim	off_t wtotal;
80163075Smaxim	int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
8132540Sbde	char *bufp;
821556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
831556Srgrimes	char *p;
841556Srgrimes#endif
858855Srgrimes
861556Srgrimes	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
871556Srgrimes		warn("%s", entp->fts_path);
881556Srgrimes		return (1);
891556Srgrimes	}
901556Srgrimes
911556Srgrimes	fs = entp->fts_statp;
921556Srgrimes
931556Srgrimes	/*
941556Srgrimes	 * If the file exists and we're interactive, verify with the user.
951556Srgrimes	 * If the file DNE, set the mode to be the from file, minus setuid
961556Srgrimes	 * bits, modified by the umask; arguably wrong, but it makes copying
971556Srgrimes	 * executables work right and it's been that way forever.  (The
981556Srgrimes	 * other choice is 666 or'ed with the execute bits on the from file
991556Srgrimes	 * modified by the umask.)
1001556Srgrimes	 */
1011556Srgrimes	if (!dne) {
10230088Swosch#define YESNO "(y/n [n]) "
103100538Sjohan		if (nflag) {
104100538Sjohan			if (vflag)
105100538Sjohan				printf("%s not overwritten\n", to.p_path);
106130102Stjr			(void)close(from_fd);
107245832Sobrien			return (1);
108100538Sjohan		} else if (iflag) {
10930088Swosch			(void)fprintf(stderr, "overwrite %s? %s",
11030088Swosch					to.p_path, YESNO);
1111556Srgrimes			checkch = ch = getchar();
1121556Srgrimes			while (ch != '\n' && ch != EOF)
1131556Srgrimes				ch = getchar();
11414416Swosch			if (checkch != 'y' && checkch != 'Y') {
1151556Srgrimes				(void)close(from_fd);
11630088Swosch				(void)fprintf(stderr, "not overwritten\n");
11756420Smharo				return (1);
1181556Srgrimes			}
1191556Srgrimes		}
12014416Swosch
12114416Swosch		if (fflag) {
12214416Swosch		    /* remove existing destination file name,
12314416Swosch		     * create a new file  */
12414416Swosch		    (void)unlink(to.p_path);
125161586Sjulian				if (!lflag)
126161586Sjulian		    	to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
127161586Sjulian				  fs->st_mode & ~(S_ISUID | S_ISGID));
128161586Sjulian		} else {
129161586Sjulian				if (!lflag)
130161586Sjulian		    	/* overwrite existing destination file name */
131161586Sjulian		    	to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
132161586Sjulian		}
133161586Sjulian	} else {
134161586Sjulian		if (!lflag)
135161586Sjulian			to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
136161586Sjulian		  fs->st_mode & ~(S_ISUID | S_ISGID));
137161586Sjulian	}
138161586Sjulian
1391556Srgrimes	if (to_fd == -1) {
1401556Srgrimes		warn("%s", to.p_path);
1411556Srgrimes		(void)close(from_fd);
14291087Smarkm		return (1);
1431556Srgrimes	}
1441556Srgrimes
1451556Srgrimes	rval = 0;
1461556Srgrimes
147161586Sjulian	if (!lflag) {
148161586Sjulian		/*
149161586Sjulian		 * Mmap and write if less than 8M (the limit is so we don't totally
150161586Sjulian		 * trash memory on big files.  This is really a minor hack, but it
151161586Sjulian		 * wins some CPU back.
152184342Sdds		 * Some filesystems, such as smbnetfs, don't support mmap,
153184342Sdds		 * so this is a best-effort attempt.
154161586Sjulian		 */
1551556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
156161586Sjulian		if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
157184342Sdds	    	    fs->st_size <= 8 * 1024 * 1024 &&
158184342Sdds		    (p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
159184342Sdds		    MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) {
160184342Sdds			wtotal = 0;
161184342Sdds			for (bufp = p, wresid = fs->st_size; ;
162184342Sdds			bufp += wcount, wresid -= (size_t)wcount) {
163184342Sdds				wcount = write(to_fd, bufp, wresid);
164184342Sdds				if (wcount <= 0)
165184342Sdds					break;
166184342Sdds				wtotal += wcount;
167184342Sdds				if (info) {
168184342Sdds					info = 0;
169184342Sdds					(void)fprintf(stderr,
170184342Sdds					    "%s -> %s %3d%%\n",
171184342Sdds					    entp->fts_path, to.p_path,
172184342Sdds					    cp_pct(wtotal, fs->st_size));
173184342Sdds				}
174184342Sdds				if (wcount >= (ssize_t)wresid)
175184342Sdds					break;
176184342Sdds			}
177184342Sdds			if (wcount != (ssize_t)wresid) {
178184342Sdds				warn("%s", to.p_path);
179184342Sdds				rval = 1;
180184342Sdds			}
181184342Sdds			/* Some systems don't unmap on close(2). */
182184342Sdds			if (munmap(p, fs->st_size) < 0) {
1831556Srgrimes				warn("%s", entp->fts_path);
1841556Srgrimes				rval = 1;
1851556Srgrimes			}
186161586Sjulian		} else
1871556Srgrimes#endif
188161586Sjulian		{
189184471Sivoras			if (buf == NULL) {
190184471Sivoras				/*
191184471Sivoras				 * Note that buf and bufsize are static. If
192184471Sivoras				 * malloc() fails, it will fail at the start
193184471Sivoras				 * and not copy only some files.
194184471Sivoras				 */
195184471Sivoras				if (sysconf(_SC_PHYS_PAGES) >
196184471Sivoras				    PHYSPAGES_THRESHOLD)
197184471Sivoras					bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
198184471Sivoras				else
199184471Sivoras					bufsize = BUFSIZE_SMALL;
200184471Sivoras				buf = malloc(bufsize);
201184471Sivoras				if (buf == NULL)
202184471Sivoras					err(1, "Not enough memory");
203184471Sivoras			}
204161586Sjulian			wtotal = 0;
205184471Sivoras			while ((rcount = read(from_fd, buf, bufsize)) > 0) {
206161586Sjulian				for (bufp = buf, wresid = rcount; ;
207161586Sjulian			    	bufp += wcount, wresid -= wcount) {
208161586Sjulian					wcount = write(to_fd, bufp, wresid);
209163074Smaxim					if (wcount <= 0)
210163074Smaxim						break;
211161586Sjulian					wtotal += wcount;
212161586Sjulian					if (info) {
213161586Sjulian						info = 0;
214161586Sjulian						(void)fprintf(stderr,
215163075Smaxim						    "%s -> %s %3d%%\n",
216163075Smaxim						    entp->fts_path, to.p_path,
217163075Smaxim						    cp_pct(wtotal, fs->st_size));
218161586Sjulian					}
219163074Smaxim					if (wcount >= (ssize_t)wresid)
220161586Sjulian						break;
221113209Smdodd				}
222161586Sjulian				if (wcount != (ssize_t)wresid) {
223161586Sjulian					warn("%s", to.p_path);
224161586Sjulian					rval = 1;
22532540Sbde					break;
226161586Sjulian				}
22732540Sbde			}
228161586Sjulian			if (rcount < 0) {
229161586Sjulian				warn("%s", entp->fts_path);
2301556Srgrimes				rval = 1;
2311556Srgrimes			}
2321556Srgrimes		}
233161586Sjulian	} else {
234161586Sjulian		if (link(entp->fts_path, to.p_path)) {
235161586Sjulian			warn("%s", to.p_path);
2361556Srgrimes			rval = 1;
2371556Srgrimes		}
2381556Srgrimes	}
239161586Sjulian
2409220Sbde	/*
2419220Sbde	 * Don't remove the target even after an error.  The target might
2429220Sbde	 * not be a regular file, or its attributes might be important,
24346684Skris	 * or its contents might be irreplaceable.  It would only be safe
2449220Sbde	 * to remove it if we created it and its length is 0.
2459220Sbde	 */
2461556Srgrimes
247161586Sjulian	if (!lflag) {
248161586Sjulian		if (pflag && setfile(fs, to_fd))
249161586Sjulian			rval = 1;
250161586Sjulian		if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
251161586Sjulian			rval = 1;
252161586Sjulian		if (close(to_fd)) {
253161586Sjulian			warn("%s", to.p_path);
254161586Sjulian			rval = 1;
255161586Sjulian		}
2561556Srgrimes	}
257181361Strasz
258181361Strasz	(void)close(from_fd);
259181361Strasz
2601556Srgrimes	return (rval);
2611556Srgrimes}
2621556Srgrimes
2631556Srgrimesint
264105395Smarkmcopy_link(const FTSENT *p, int exists)
2651556Srgrimes{
2661556Srgrimes	int len;
26791087Smarkm	char llink[PATH_MAX];
2681556Srgrimes
269245960Smarkj	if (exists && nflag) {
270245960Smarkj		if (vflag)
271245960Smarkj			printf("%s not overwritten\n", to.p_path);
272245960Smarkj		return (1);
273245960Smarkj	}
27491087Smarkm	if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
2751556Srgrimes		warn("readlink: %s", p->fts_path);
2761556Srgrimes		return (1);
2771556Srgrimes	}
27891087Smarkm	llink[len] = '\0';
2791556Srgrimes	if (exists && unlink(to.p_path)) {
2801556Srgrimes		warn("unlink: %s", to.p_path);
2811556Srgrimes		return (1);
2821556Srgrimes	}
28391087Smarkm	if (symlink(llink, to.p_path)) {
28491087Smarkm		warn("symlink: %s", llink);
2851556Srgrimes		return (1);
2861556Srgrimes	}
287116673Sjmg	return (pflag ? setfile(p->fts_statp, -1) : 0);
2881556Srgrimes}
2891556Srgrimes
2901556Srgrimesint
29190107Simpcopy_fifo(struct stat *from_stat, int exists)
2921556Srgrimes{
293245960Smarkj
294245960Smarkj	if (exists && nflag) {
295245960Smarkj		if (vflag)
296245960Smarkj			printf("%s not overwritten\n", to.p_path);
297245960Smarkj		return (1);
298245960Smarkj	}
2991556Srgrimes	if (exists && unlink(to.p_path)) {
3001556Srgrimes		warn("unlink: %s", to.p_path);
3011556Srgrimes		return (1);
3021556Srgrimes	}
3031556Srgrimes	if (mkfifo(to.p_path, from_stat->st_mode)) {
3041556Srgrimes		warn("mkfifo: %s", to.p_path);
3051556Srgrimes		return (1);
3061556Srgrimes	}
307116673Sjmg	return (pflag ? setfile(from_stat, -1) : 0);
3081556Srgrimes}
3091556Srgrimes
3101556Srgrimesint
31190107Simpcopy_special(struct stat *from_stat, int exists)
3121556Srgrimes{
313245960Smarkj
314245960Smarkj	if (exists && nflag) {
315245960Smarkj		if (vflag)
316245960Smarkj			printf("%s not overwritten\n", to.p_path);
317245960Smarkj		return (1);
318245960Smarkj	}
3191556Srgrimes	if (exists && unlink(to.p_path)) {
3201556Srgrimes		warn("unlink: %s", to.p_path);
3211556Srgrimes		return (1);
3221556Srgrimes	}
3231556Srgrimes	if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
3241556Srgrimes		warn("mknod: %s", to.p_path);
3251556Srgrimes		return (1);
3261556Srgrimes	}
327116673Sjmg	return (pflag ? setfile(from_stat, -1) : 0);
3281556Srgrimes}
3291556Srgrimes
3301556Srgrimesint
33190107Simpsetfile(struct stat *fs, int fd)
3321556Srgrimes{
3331556Srgrimes	static struct timeval tv[2];
33436838Speter	struct stat ts;
335116673Sjmg	int rval, gotstat, islink, fdval;
3361556Srgrimes
3371556Srgrimes	rval = 0;
338116673Sjmg	fdval = fd != -1;
339116673Sjmg	islink = !fdval && S_ISLNK(fs->st_mode);
34011146Sbde	fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
34111146Sbde		       S_IRWXU | S_IRWXG | S_IRWXO;
3421556Srgrimes
343205793Sed	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim);
344205793Sed	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim);
345116673Sjmg	if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
346116673Sjmg		warn("%sutimes: %s", islink ? "l" : "", to.p_path);
3471556Srgrimes		rval = 1;
3481556Srgrimes	}
349116673Sjmg	if (fdval ? fstat(fd, &ts) :
350116673Sjmg	    (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
35136838Speter		gotstat = 0;
35236838Speter	else {
35336838Speter		gotstat = 1;
35436838Speter		ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
35536838Speter			      S_IRWXU | S_IRWXG | S_IRWXO;
35636838Speter	}
3571556Srgrimes	/*
3581556Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
3591556Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
3601556Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
3611556Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
3621556Srgrimes	 */
36336838Speter	if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
364116673Sjmg		if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
365116673Sjmg		    (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
366116673Sjmg		    chown(to.p_path, fs->st_uid, fs->st_gid))) {
36736838Speter			if (errno != EPERM) {
36836838Speter				warn("chown: %s", to.p_path);
36936838Speter				rval = 1;
37036838Speter			}
37136838Speter			fs->st_mode &= ~(S_ISUID | S_ISGID);
37236838Speter		}
37336838Speter
37436838Speter	if (!gotstat || fs->st_mode != ts.st_mode)
375116673Sjmg		if (fdval ? fchmod(fd, fs->st_mode) :
376116673Sjmg		    (islink ? lchmod(to.p_path, fs->st_mode) :
377116673Sjmg		    chmod(to.p_path, fs->st_mode))) {
37887652Smckay			warn("chmod: %s", to.p_path);
3791556Srgrimes			rval = 1;
3801556Srgrimes		}
3811556Srgrimes
38236838Speter	if (!gotstat || fs->st_flags != ts.st_flags)
383116673Sjmg		if (fdval ?
384116673Sjmg		    fchflags(fd, fs->st_flags) :
385193086Sjilles		    (islink ? lchflags(to.p_path, fs->st_flags) :
386116673Sjmg		    chflags(to.p_path, fs->st_flags))) {
38736838Speter			warn("chflags: %s", to.p_path);
38836838Speter			rval = 1;
38936838Speter		}
39036838Speter
3911556Srgrimes	return (rval);
3921556Srgrimes}
3931556Srgrimes
394149790Scsjpint
395149790Scsjppreserve_fd_acls(int source_fd, int dest_fd)
396149790Scsjp{
397149790Scsjp	acl_t acl;
398196754Strasz	acl_type_t acl_type;
399196754Strasz	int acl_supported = 0, ret, trivial;
400149790Scsjp
401196754Strasz	ret = fpathconf(source_fd, _PC_ACL_NFS4);
402196754Strasz	if (ret > 0 ) {
403196754Strasz		acl_supported = 1;
404196754Strasz		acl_type = ACL_TYPE_NFS4;
405196754Strasz	} else if (ret < 0 && errno != EINVAL) {
406196754Strasz		warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path);
407196754Strasz		return (1);
408196754Strasz	}
409196754Strasz	if (acl_supported == 0) {
410196754Strasz		ret = fpathconf(source_fd, _PC_ACL_EXTENDED);
411196754Strasz		if (ret > 0 ) {
412196754Strasz			acl_supported = 1;
413196754Strasz			acl_type = ACL_TYPE_ACCESS;
414196754Strasz		} else if (ret < 0 && errno != EINVAL) {
415196754Strasz			warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
416196754Strasz			    to.p_path);
417196754Strasz			return (1);
418196754Strasz		}
419196754Strasz	}
420196754Strasz	if (acl_supported == 0)
421149790Scsjp		return (0);
422196754Strasz
423196754Strasz	acl = acl_get_fd_np(source_fd, acl_type);
424149790Scsjp	if (acl == NULL) {
425149790Scsjp		warn("failed to get acl entries while setting %s", to.p_path);
426149790Scsjp		return (1);
427149790Scsjp	}
428196754Strasz	if (acl_is_trivial_np(acl, &trivial)) {
429196754Strasz		warn("acl_is_trivial() failed for %s", to.p_path);
430196754Strasz		acl_free(acl);
431196754Strasz		return (1);
432196754Strasz	}
433196754Strasz	if (trivial) {
434196754Strasz		acl_free(acl);
435149790Scsjp		return (0);
436196754Strasz	}
437196754Strasz	if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) {
438149790Scsjp		warn("failed to set acl entries for %s", to.p_path);
439196754Strasz		acl_free(acl);
440149790Scsjp		return (1);
441149790Scsjp	}
442196754Strasz	acl_free(acl);
443149790Scsjp	return (0);
444149790Scsjp}
445149790Scsjp
446149790Scsjpint
447149790Scsjppreserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir)
448149790Scsjp{
449149790Scsjp	acl_t (*aclgetf)(const char *, acl_type_t);
450149790Scsjp	int (*aclsetf)(const char *, acl_type_t, acl_t);
451149790Scsjp	struct acl *aclp;
452149790Scsjp	acl_t acl;
453196754Strasz	acl_type_t acl_type;
454196754Strasz	int acl_supported = 0, ret, trivial;
455149790Scsjp
456196754Strasz	ret = pathconf(source_dir, _PC_ACL_NFS4);
457196754Strasz	if (ret > 0) {
458196754Strasz		acl_supported = 1;
459196754Strasz		acl_type = ACL_TYPE_NFS4;
460196754Strasz	} else if (ret < 0 && errno != EINVAL) {
461196754Strasz		warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir);
462196754Strasz		return (1);
463196754Strasz	}
464196754Strasz	if (acl_supported == 0) {
465196754Strasz		ret = pathconf(source_dir, _PC_ACL_EXTENDED);
466196754Strasz		if (ret > 0) {
467196754Strasz			acl_supported = 1;
468196754Strasz			acl_type = ACL_TYPE_ACCESS;
469196754Strasz		} else if (ret < 0 && errno != EINVAL) {
470196754Strasz			warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s",
471196754Strasz			    source_dir);
472196754Strasz			return (1);
473196754Strasz		}
474196754Strasz	}
475196754Strasz	if (acl_supported == 0)
476149790Scsjp		return (0);
477196754Strasz
478149790Scsjp	/*
479149790Scsjp	 * If the file is a link we will not follow it
480149790Scsjp	 */
481149790Scsjp	if (S_ISLNK(fs->st_mode)) {
482149790Scsjp		aclgetf = acl_get_link_np;
483149790Scsjp		aclsetf = acl_set_link_np;
484149790Scsjp	} else {
485149790Scsjp		aclgetf = acl_get_file;
486149790Scsjp		aclsetf = acl_set_file;
487149790Scsjp	}
488196754Strasz	if (acl_type == ACL_TYPE_ACCESS) {
489196754Strasz		/*
490196754Strasz		 * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
491196754Strasz		 * size ACL will be returned. So it is not safe to simply
492196754Strasz		 * check the pointer to see if the default ACL is present.
493196754Strasz		 */
494196754Strasz		acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
495196754Strasz		if (acl == NULL) {
496196754Strasz			warn("failed to get default acl entries on %s",
497196754Strasz			    source_dir);
498196754Strasz			return (1);
499196754Strasz		}
500196754Strasz		aclp = &acl->ats_acl;
501196754Strasz		if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
502196754Strasz		    ACL_TYPE_DEFAULT, acl) < 0) {
503196754Strasz			warn("failed to set default acl entries on %s",
504196754Strasz			    dest_dir);
505196754Strasz			acl_free(acl);
506196754Strasz			return (1);
507196754Strasz		}
508196754Strasz		acl_free(acl);
509196754Strasz	}
510196754Strasz	acl = aclgetf(source_dir, acl_type);
511149790Scsjp	if (acl == NULL) {
512196754Strasz		warn("failed to get acl entries on %s", source_dir);
513149790Scsjp		return (1);
514149790Scsjp	}
515196754Strasz	if (acl_is_trivial_np(acl, &trivial)) {
516196754Strasz		warn("acl_is_trivial() failed on %s", source_dir);
517196754Strasz		acl_free(acl);
518149790Scsjp		return (1);
519149790Scsjp	}
520196754Strasz	if (trivial) {
521196754Strasz		acl_free(acl);
522196754Strasz		return (0);
523149790Scsjp	}
524196754Strasz	if (aclsetf(dest_dir, acl_type, acl) < 0) {
525149790Scsjp		warn("failed to set acl entries on %s", dest_dir);
526196754Strasz		acl_free(acl);
527149790Scsjp		return (1);
528149790Scsjp	}
529196754Strasz	acl_free(acl);
530149790Scsjp	return (0);
531149790Scsjp}
532149790Scsjp
5331556Srgrimesvoid
53490107Simpusage(void)
5351556Srgrimes{
53650543Smharo
5371556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
538202461Sgavin"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpvx] source_file target_file",
539202461Sgavin"       cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpvx] source_file ... "
540141578Sru"target_directory");
54156420Smharo	exit(EX_USAGE);
5421556Srgrimes}
543