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: releng/10.3/bin/cp/utils.c 291774 2015-12-04 17:36:35Z bdrewery $"); 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 60291774Sbdrewery/* 61291774Sbdrewery * Memory strategy threshold, in pages: if physmem is larger then this, use a 62291774Sbdrewery * large buffer. 63291774Sbdrewery */ 64184471Sivoras#define PHYSPAGES_THRESHOLD (32*1024) 65184471Sivoras 66291774Sbdrewery/* Maximum buffer size in bytes - do not allow it to grow larger than this. */ 67184471Sivoras#define BUFSIZE_MAX (2*1024*1024) 68184471Sivoras 69291774Sbdrewery/* 70291774Sbdrewery * Small (default) buffer size in bytes. It's inefficient for this to be 71291774Sbdrewery * smaller than MAXPHYS. 72291774Sbdrewery */ 73184471Sivoras#define BUFSIZE_SMALL (MAXPHYS) 74184471Sivoras 751556Srgrimesint 76105395Smarkmcopy_file(const FTSENT *entp, int dne) 771556Srgrimes{ 78184471Sivoras static char *buf = NULL; 79184471Sivoras static size_t bufsize; 8078070Sbde struct stat *fs; 8199363Smarkm ssize_t wcount; 8299363Smarkm size_t wresid; 83163049Smaxim off_t wtotal; 84291774Sbdrewery int ch, checkch, from_fd, rcount, rval, to_fd; 8532540Sbde char *bufp; 861556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 871556Srgrimes char *p; 881556Srgrimes#endif 898855Srgrimes 90291774Sbdrewery from_fd = to_fd = -1; 91291774Sbdrewery if (!lflag && !sflag && 92291774Sbdrewery (from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 931556Srgrimes warn("%s", entp->fts_path); 941556Srgrimes return (1); 951556Srgrimes } 961556Srgrimes 971556Srgrimes fs = entp->fts_statp; 981556Srgrimes 991556Srgrimes /* 1001556Srgrimes * If the file exists and we're interactive, verify with the user. 1011556Srgrimes * If the file DNE, set the mode to be the from file, minus setuid 1021556Srgrimes * bits, modified by the umask; arguably wrong, but it makes copying 1031556Srgrimes * executables work right and it's been that way forever. (The 1041556Srgrimes * other choice is 666 or'ed with the execute bits on the from file 1051556Srgrimes * modified by the umask.) 1061556Srgrimes */ 1071556Srgrimes if (!dne) { 10830088Swosch#define YESNO "(y/n [n]) " 109100538Sjohan if (nflag) { 110100538Sjohan if (vflag) 111100538Sjohan printf("%s not overwritten\n", to.p_path); 112291774Sbdrewery rval = 1; 113291774Sbdrewery goto done; 114100538Sjohan } else if (iflag) { 11530088Swosch (void)fprintf(stderr, "overwrite %s? %s", 116291774Sbdrewery to.p_path, YESNO); 1171556Srgrimes checkch = ch = getchar(); 1181556Srgrimes while (ch != '\n' && ch != EOF) 1191556Srgrimes ch = getchar(); 12014416Swosch if (checkch != 'y' && checkch != 'Y') { 12130088Swosch (void)fprintf(stderr, "not overwritten\n"); 122291774Sbdrewery rval = 1; 123291774Sbdrewery goto done; 1241556Srgrimes } 1251556Srgrimes } 126291774Sbdrewery 12714416Swosch if (fflag) { 128291774Sbdrewery /* 129291774Sbdrewery * Remove existing destination file name create a new 130291774Sbdrewery * file. 131291774Sbdrewery */ 132291774Sbdrewery (void)unlink(to.p_path); 133291774Sbdrewery if (!lflag && !sflag) { 134291774Sbdrewery to_fd = open(to.p_path, 135291774Sbdrewery O_WRONLY | O_TRUNC | O_CREAT, 136291774Sbdrewery fs->st_mode & ~(S_ISUID | S_ISGID)); 137291774Sbdrewery } 138291774Sbdrewery } else if (!lflag && !sflag) { 139291774Sbdrewery /* Overwrite existing destination file name. */ 140291774Sbdrewery to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 141161586Sjulian } 142291774Sbdrewery } else if (!lflag && !sflag) { 143291774Sbdrewery to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 144291774Sbdrewery fs->st_mode & ~(S_ISUID | S_ISGID)); 145161586Sjulian } 146291774Sbdrewery 147291774Sbdrewery if (!lflag && !sflag && to_fd == -1) { 1481556Srgrimes warn("%s", to.p_path); 149291774Sbdrewery rval = 1; 150291774Sbdrewery goto done; 1511556Srgrimes } 1521556Srgrimes 1531556Srgrimes rval = 0; 1541556Srgrimes 155291774Sbdrewery if (!lflag && !sflag) { 156161586Sjulian /* 157291774Sbdrewery * Mmap and write if less than 8M (the limit is so we don't 158291774Sbdrewery * totally trash memory on big files. This is really a minor 159291774Sbdrewery * hack, but it wins some CPU back. 160184342Sdds * Some filesystems, such as smbnetfs, don't support mmap, 161184342Sdds * so this is a best-effort attempt. 162161586Sjulian */ 1631556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 164161586Sjulian if (S_ISREG(fs->st_mode) && fs->st_size > 0 && 165291774Sbdrewery fs->st_size <= 8 * 1024 * 1024 && 166184342Sdds (p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 167184342Sdds MAP_SHARED, from_fd, (off_t)0)) != MAP_FAILED) { 168184342Sdds wtotal = 0; 169184342Sdds for (bufp = p, wresid = fs->st_size; ; 170291774Sbdrewery bufp += wcount, wresid -= (size_t)wcount) { 171184342Sdds wcount = write(to_fd, bufp, wresid); 172184342Sdds if (wcount <= 0) 173184342Sdds break; 174184342Sdds wtotal += wcount; 175184342Sdds if (info) { 176184342Sdds info = 0; 177184342Sdds (void)fprintf(stderr, 178184342Sdds "%s -> %s %3d%%\n", 179184342Sdds entp->fts_path, to.p_path, 180184342Sdds cp_pct(wtotal, fs->st_size)); 181184342Sdds } 182184342Sdds if (wcount >= (ssize_t)wresid) 183184342Sdds break; 184184342Sdds } 185184342Sdds if (wcount != (ssize_t)wresid) { 186184342Sdds warn("%s", to.p_path); 187184342Sdds rval = 1; 188184342Sdds } 189184342Sdds /* Some systems don't unmap on close(2). */ 190184342Sdds if (munmap(p, fs->st_size) < 0) { 1911556Srgrimes warn("%s", entp->fts_path); 1921556Srgrimes rval = 1; 1931556Srgrimes } 194161586Sjulian } else 1951556Srgrimes#endif 196161586Sjulian { 197184471Sivoras if (buf == NULL) { 198184471Sivoras /* 199184471Sivoras * Note that buf and bufsize are static. If 200184471Sivoras * malloc() fails, it will fail at the start 201184471Sivoras * and not copy only some files. 202184471Sivoras */ 203184471Sivoras if (sysconf(_SC_PHYS_PAGES) > 204184471Sivoras PHYSPAGES_THRESHOLD) 205184471Sivoras bufsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 206184471Sivoras else 207184471Sivoras bufsize = BUFSIZE_SMALL; 208184471Sivoras buf = malloc(bufsize); 209184471Sivoras if (buf == NULL) 210184471Sivoras err(1, "Not enough memory"); 211184471Sivoras } 212161586Sjulian wtotal = 0; 213184471Sivoras while ((rcount = read(from_fd, buf, bufsize)) > 0) { 214161586Sjulian for (bufp = buf, wresid = rcount; ; 215291774Sbdrewery bufp += wcount, wresid -= wcount) { 216161586Sjulian wcount = write(to_fd, bufp, wresid); 217163074Smaxim if (wcount <= 0) 218163074Smaxim break; 219161586Sjulian wtotal += wcount; 220161586Sjulian if (info) { 221161586Sjulian info = 0; 222161586Sjulian (void)fprintf(stderr, 223163075Smaxim "%s -> %s %3d%%\n", 224163075Smaxim entp->fts_path, to.p_path, 225163075Smaxim cp_pct(wtotal, fs->st_size)); 226161586Sjulian } 227163074Smaxim if (wcount >= (ssize_t)wresid) 228161586Sjulian break; 229113209Smdodd } 230161586Sjulian if (wcount != (ssize_t)wresid) { 231161586Sjulian warn("%s", to.p_path); 232161586Sjulian rval = 1; 23332540Sbde break; 234161586Sjulian } 23532540Sbde } 236161586Sjulian if (rcount < 0) { 237161586Sjulian warn("%s", entp->fts_path); 2381556Srgrimes rval = 1; 2391556Srgrimes } 2401556Srgrimes } 241291774Sbdrewery } else if (lflag) { 242161586Sjulian if (link(entp->fts_path, to.p_path)) { 243161586Sjulian warn("%s", to.p_path); 2441556Srgrimes rval = 1; 2451556Srgrimes } 246291774Sbdrewery } else if (sflag) { 247291774Sbdrewery if (symlink(entp->fts_path, to.p_path)) { 248291774Sbdrewery warn("%s", to.p_path); 249291774Sbdrewery rval = 1; 250291774Sbdrewery } 2511556Srgrimes } 252291774Sbdrewery 2539220Sbde /* 2549220Sbde * Don't remove the target even after an error. The target might 2559220Sbde * not be a regular file, or its attributes might be important, 25646684Skris * or its contents might be irreplaceable. It would only be safe 2579220Sbde * to remove it if we created it and its length is 0. 2589220Sbde */ 2591556Srgrimes 260291774Sbdrewery if (!lflag && !sflag) { 261161586Sjulian if (pflag && setfile(fs, to_fd)) 262161586Sjulian rval = 1; 263161586Sjulian if (pflag && preserve_fd_acls(from_fd, to_fd) != 0) 264161586Sjulian rval = 1; 265161586Sjulian if (close(to_fd)) { 266161586Sjulian warn("%s", to.p_path); 267161586Sjulian rval = 1; 268161586Sjulian } 2691556Srgrimes } 270181361Strasz 271291774Sbdrewerydone: 272291774Sbdrewery if (from_fd != -1) 273291774Sbdrewery (void)close(from_fd); 2741556Srgrimes return (rval); 2751556Srgrimes} 2761556Srgrimes 2771556Srgrimesint 278105395Smarkmcopy_link(const FTSENT *p, int exists) 2791556Srgrimes{ 2801556Srgrimes int len; 28191087Smarkm char llink[PATH_MAX]; 2821556Srgrimes 283245960Smarkj if (exists && nflag) { 284245960Smarkj if (vflag) 285245960Smarkj printf("%s not overwritten\n", to.p_path); 286245960Smarkj return (1); 287245960Smarkj } 28891087Smarkm if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) { 2891556Srgrimes warn("readlink: %s", p->fts_path); 2901556Srgrimes return (1); 2911556Srgrimes } 29291087Smarkm llink[len] = '\0'; 2931556Srgrimes if (exists && unlink(to.p_path)) { 2941556Srgrimes warn("unlink: %s", to.p_path); 2951556Srgrimes return (1); 2961556Srgrimes } 29791087Smarkm if (symlink(llink, to.p_path)) { 29891087Smarkm warn("symlink: %s", llink); 2991556Srgrimes return (1); 3001556Srgrimes } 301116673Sjmg return (pflag ? setfile(p->fts_statp, -1) : 0); 3021556Srgrimes} 3031556Srgrimes 3041556Srgrimesint 30590107Simpcopy_fifo(struct stat *from_stat, int exists) 3061556Srgrimes{ 307245960Smarkj 308245960Smarkj if (exists && nflag) { 309245960Smarkj if (vflag) 310245960Smarkj printf("%s not overwritten\n", to.p_path); 311245960Smarkj return (1); 312245960Smarkj } 3131556Srgrimes if (exists && unlink(to.p_path)) { 3141556Srgrimes warn("unlink: %s", to.p_path); 3151556Srgrimes return (1); 3161556Srgrimes } 3171556Srgrimes if (mkfifo(to.p_path, from_stat->st_mode)) { 3181556Srgrimes warn("mkfifo: %s", to.p_path); 3191556Srgrimes return (1); 3201556Srgrimes } 321116673Sjmg return (pflag ? setfile(from_stat, -1) : 0); 3221556Srgrimes} 3231556Srgrimes 3241556Srgrimesint 32590107Simpcopy_special(struct stat *from_stat, int exists) 3261556Srgrimes{ 327245960Smarkj 328245960Smarkj if (exists && nflag) { 329245960Smarkj if (vflag) 330245960Smarkj printf("%s not overwritten\n", to.p_path); 331245960Smarkj return (1); 332245960Smarkj } 3331556Srgrimes if (exists && unlink(to.p_path)) { 3341556Srgrimes warn("unlink: %s", to.p_path); 3351556Srgrimes return (1); 3361556Srgrimes } 3371556Srgrimes if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 3381556Srgrimes warn("mknod: %s", to.p_path); 3391556Srgrimes return (1); 3401556Srgrimes } 341116673Sjmg return (pflag ? setfile(from_stat, -1) : 0); 3421556Srgrimes} 3431556Srgrimes 3441556Srgrimesint 34590107Simpsetfile(struct stat *fs, int fd) 3461556Srgrimes{ 3471556Srgrimes static struct timeval tv[2]; 34836838Speter struct stat ts; 349116673Sjmg int rval, gotstat, islink, fdval; 3501556Srgrimes 3511556Srgrimes rval = 0; 352116673Sjmg fdval = fd != -1; 353116673Sjmg islink = !fdval && S_ISLNK(fs->st_mode); 35411146Sbde fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | 355291774Sbdrewery S_IRWXU | S_IRWXG | S_IRWXO; 3561556Srgrimes 357205793Sed TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim); 358205793Sed TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim); 359116673Sjmg if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) { 360116673Sjmg warn("%sutimes: %s", islink ? "l" : "", to.p_path); 3611556Srgrimes rval = 1; 3621556Srgrimes } 363116673Sjmg if (fdval ? fstat(fd, &ts) : 364116673Sjmg (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts))) 36536838Speter gotstat = 0; 36636838Speter else { 36736838Speter gotstat = 1; 36836838Speter ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | 369291774Sbdrewery S_IRWXU | S_IRWXG | S_IRWXO; 37036838Speter } 3711556Srgrimes /* 3721556Srgrimes * Changing the ownership probably won't succeed, unless we're root 3731556Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 3741556Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 3751556Srgrimes * chown. If chown fails, lose setuid/setgid bits. 3761556Srgrimes */ 37736838Speter if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) 378116673Sjmg if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : 379116673Sjmg (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) : 380116673Sjmg chown(to.p_path, fs->st_uid, fs->st_gid))) { 38136838Speter if (errno != EPERM) { 38236838Speter warn("chown: %s", to.p_path); 38336838Speter rval = 1; 38436838Speter } 38536838Speter fs->st_mode &= ~(S_ISUID | S_ISGID); 38636838Speter } 38736838Speter 38836838Speter if (!gotstat || fs->st_mode != ts.st_mode) 389116673Sjmg if (fdval ? fchmod(fd, fs->st_mode) : 390116673Sjmg (islink ? lchmod(to.p_path, fs->st_mode) : 391116673Sjmg chmod(to.p_path, fs->st_mode))) { 39287652Smckay warn("chmod: %s", to.p_path); 3931556Srgrimes rval = 1; 3941556Srgrimes } 3951556Srgrimes 39636838Speter if (!gotstat || fs->st_flags != ts.st_flags) 397116673Sjmg if (fdval ? 398116673Sjmg fchflags(fd, fs->st_flags) : 399193086Sjilles (islink ? lchflags(to.p_path, fs->st_flags) : 400116673Sjmg chflags(to.p_path, fs->st_flags))) { 40136838Speter warn("chflags: %s", to.p_path); 40236838Speter rval = 1; 40336838Speter } 40436838Speter 4051556Srgrimes return (rval); 4061556Srgrimes} 4071556Srgrimes 408149790Scsjpint 409149790Scsjppreserve_fd_acls(int source_fd, int dest_fd) 410149790Scsjp{ 411149790Scsjp acl_t acl; 412196754Strasz acl_type_t acl_type; 413196754Strasz int acl_supported = 0, ret, trivial; 414149790Scsjp 415196754Strasz ret = fpathconf(source_fd, _PC_ACL_NFS4); 416196754Strasz if (ret > 0 ) { 417196754Strasz acl_supported = 1; 418196754Strasz acl_type = ACL_TYPE_NFS4; 419196754Strasz } else if (ret < 0 && errno != EINVAL) { 420196754Strasz warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path); 421196754Strasz return (1); 422196754Strasz } 423196754Strasz if (acl_supported == 0) { 424196754Strasz ret = fpathconf(source_fd, _PC_ACL_EXTENDED); 425196754Strasz if (ret > 0 ) { 426196754Strasz acl_supported = 1; 427196754Strasz acl_type = ACL_TYPE_ACCESS; 428196754Strasz } else if (ret < 0 && errno != EINVAL) { 429196754Strasz warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s", 430196754Strasz to.p_path); 431196754Strasz return (1); 432196754Strasz } 433196754Strasz } 434196754Strasz if (acl_supported == 0) 435149790Scsjp return (0); 436196754Strasz 437196754Strasz acl = acl_get_fd_np(source_fd, acl_type); 438149790Scsjp if (acl == NULL) { 439149790Scsjp warn("failed to get acl entries while setting %s", to.p_path); 440149790Scsjp return (1); 441149790Scsjp } 442196754Strasz if (acl_is_trivial_np(acl, &trivial)) { 443196754Strasz warn("acl_is_trivial() failed for %s", to.p_path); 444196754Strasz acl_free(acl); 445196754Strasz return (1); 446196754Strasz } 447196754Strasz if (trivial) { 448196754Strasz acl_free(acl); 449149790Scsjp return (0); 450196754Strasz } 451196754Strasz if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) { 452149790Scsjp warn("failed to set acl entries for %s", to.p_path); 453196754Strasz acl_free(acl); 454149790Scsjp return (1); 455149790Scsjp } 456196754Strasz acl_free(acl); 457149790Scsjp return (0); 458149790Scsjp} 459149790Scsjp 460149790Scsjpint 461149790Scsjppreserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir) 462149790Scsjp{ 463149790Scsjp acl_t (*aclgetf)(const char *, acl_type_t); 464149790Scsjp int (*aclsetf)(const char *, acl_type_t, acl_t); 465149790Scsjp struct acl *aclp; 466149790Scsjp acl_t acl; 467196754Strasz acl_type_t acl_type; 468196754Strasz int acl_supported = 0, ret, trivial; 469149790Scsjp 470196754Strasz ret = pathconf(source_dir, _PC_ACL_NFS4); 471196754Strasz if (ret > 0) { 472196754Strasz acl_supported = 1; 473196754Strasz acl_type = ACL_TYPE_NFS4; 474196754Strasz } else if (ret < 0 && errno != EINVAL) { 475196754Strasz warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir); 476196754Strasz return (1); 477196754Strasz } 478196754Strasz if (acl_supported == 0) { 479196754Strasz ret = pathconf(source_dir, _PC_ACL_EXTENDED); 480196754Strasz if (ret > 0) { 481196754Strasz acl_supported = 1; 482196754Strasz acl_type = ACL_TYPE_ACCESS; 483196754Strasz } else if (ret < 0 && errno != EINVAL) { 484196754Strasz warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s", 485196754Strasz source_dir); 486196754Strasz return (1); 487196754Strasz } 488196754Strasz } 489196754Strasz if (acl_supported == 0) 490149790Scsjp return (0); 491196754Strasz 492149790Scsjp /* 493291774Sbdrewery * If the file is a link we will not follow it. 494149790Scsjp */ 495149790Scsjp if (S_ISLNK(fs->st_mode)) { 496149790Scsjp aclgetf = acl_get_link_np; 497149790Scsjp aclsetf = acl_set_link_np; 498149790Scsjp } else { 499149790Scsjp aclgetf = acl_get_file; 500149790Scsjp aclsetf = acl_set_file; 501149790Scsjp } 502196754Strasz if (acl_type == ACL_TYPE_ACCESS) { 503196754Strasz /* 504196754Strasz * Even if there is no ACL_TYPE_DEFAULT entry here, a zero 505196754Strasz * size ACL will be returned. So it is not safe to simply 506196754Strasz * check the pointer to see if the default ACL is present. 507196754Strasz */ 508196754Strasz acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); 509196754Strasz if (acl == NULL) { 510196754Strasz warn("failed to get default acl entries on %s", 511196754Strasz source_dir); 512196754Strasz return (1); 513196754Strasz } 514196754Strasz aclp = &acl->ats_acl; 515196754Strasz if (aclp->acl_cnt != 0 && aclsetf(dest_dir, 516196754Strasz ACL_TYPE_DEFAULT, acl) < 0) { 517196754Strasz warn("failed to set default acl entries on %s", 518196754Strasz dest_dir); 519196754Strasz acl_free(acl); 520196754Strasz return (1); 521196754Strasz } 522196754Strasz acl_free(acl); 523196754Strasz } 524196754Strasz acl = aclgetf(source_dir, acl_type); 525149790Scsjp if (acl == NULL) { 526196754Strasz warn("failed to get acl entries on %s", source_dir); 527149790Scsjp return (1); 528149790Scsjp } 529196754Strasz if (acl_is_trivial_np(acl, &trivial)) { 530196754Strasz warn("acl_is_trivial() failed on %s", source_dir); 531196754Strasz acl_free(acl); 532149790Scsjp return (1); 533149790Scsjp } 534196754Strasz if (trivial) { 535196754Strasz acl_free(acl); 536196754Strasz return (0); 537149790Scsjp } 538196754Strasz if (aclsetf(dest_dir, acl_type, acl) < 0) { 539149790Scsjp warn("failed to set acl entries on %s", dest_dir); 540196754Strasz acl_free(acl); 541149790Scsjp return (1); 542149790Scsjp } 543196754Strasz acl_free(acl); 544149790Scsjp return (0); 545149790Scsjp} 546149790Scsjp 5471556Srgrimesvoid 54890107Simpusage(void) 5491556Srgrimes{ 55050543Smharo 5511556Srgrimes (void)fprintf(stderr, "%s\n%s\n", 552291774Sbdrewery "usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpsvx] " 553291774Sbdrewery "source_file target_file", 554291774Sbdrewery " cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpsvx] " 555291774Sbdrewery "source_file ... " 556291774Sbdrewery "target_directory"); 55756420Smharo exit(EX_USAGE); 5581556Srgrimes} 559