utils.c revision 284163
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 284163 2015-06-08 19:24:18Z 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 60284163Sbdrewery/* 61284163Sbdrewery * Memory strategy threshold, in pages: if physmem is larger then this, use a 62284163Sbdrewery * large buffer. 63284163Sbdrewery */ 64184471Sivoras#define PHYSPAGES_THRESHOLD (32*1024) 65184471Sivoras 66284163Sbdrewery/* Maximum buffer size in bytes - do not allow it to grow larger than this. */ 67184471Sivoras#define BUFSIZE_MAX (2*1024*1024) 68184471Sivoras 69284163Sbdrewery/* 70284163Sbdrewery * Small (default) buffer size in bytes. It's inefficient for this to be 71284163Sbdrewery * smaller than MAXPHYS. 72284163Sbdrewery */ 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; 84284106Sbdrewery 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 90284106Sbdrewery from_fd = to_fd = -1; 91284106Sbdrewery if (!lflag && !sflag && 92284106Sbdrewery (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); 112284106Sbdrewery rval = 1; 113284106Sbdrewery goto done; 114100538Sjohan } else if (iflag) { 11530088Swosch (void)fprintf(stderr, "overwrite %s? %s", 116284163Sbdrewery 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"); 122284106Sbdrewery rval = 1; 123284106Sbdrewery goto done; 1241556Srgrimes } 1251556Srgrimes } 126284163Sbdrewery 12714416Swosch if (fflag) { 128284163Sbdrewery /* 129284163Sbdrewery * Remove existing destination file name create a new 130284163Sbdrewery * file. 131284163Sbdrewery */ 132284163Sbdrewery (void)unlink(to.p_path); 133284163Sbdrewery if (!lflag && !sflag) { 134284163Sbdrewery to_fd = open(to.p_path, 135284163Sbdrewery O_WRONLY | O_TRUNC | O_CREAT, 136284163Sbdrewery fs->st_mode & ~(S_ISUID | S_ISGID)); 137284163Sbdrewery } 138284106Sbdrewery } else if (!lflag && !sflag) { 139284163Sbdrewery /* Overwrite existing destination file name. */ 140284105Sbdrewery to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 141161586Sjulian } 142284106Sbdrewery } else if (!lflag && !sflag) { 143284105Sbdrewery to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 144284105Sbdrewery fs->st_mode & ~(S_ISUID | S_ISGID)); 145161586Sjulian } 146284163Sbdrewery 147284106Sbdrewery if (!lflag && !sflag && to_fd == -1) { 1481556Srgrimes warn("%s", to.p_path); 149284106Sbdrewery rval = 1; 150284106Sbdrewery goto done; 1511556Srgrimes } 1521556Srgrimes 1531556Srgrimes rval = 0; 1541556Srgrimes 155284106Sbdrewery if (!lflag && !sflag) { 156161586Sjulian /* 157284163Sbdrewery * Mmap and write if less than 8M (the limit is so we don't 158284163Sbdrewery * totally trash memory on big files. This is really a minor 159284163Sbdrewery * 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 && 165284163Sbdrewery 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; ; 170284163Sbdrewery 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; ; 215284163Sbdrewery 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 } 241284106Sbdrewery } else if (lflag) { 242161586Sjulian if (link(entp->fts_path, to.p_path)) { 243161586Sjulian warn("%s", to.p_path); 2441556Srgrimes rval = 1; 2451556Srgrimes } 246284106Sbdrewery } else if (sflag) { 247284106Sbdrewery if (symlink(entp->fts_path, to.p_path)) { 248284106Sbdrewery warn("%s", to.p_path); 249284106Sbdrewery rval = 1; 250284106Sbdrewery } 2511556Srgrimes } 252284163Sbdrewery 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 260284106Sbdrewery 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 271284106Sbdrewerydone: 272284106Sbdrewery if (from_fd != -1) 273284106Sbdrewery (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{ 347277645Sjilles static struct timespec tspec[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 | 355284163Sbdrewery S_IRWXU | S_IRWXG | S_IRWXO; 3561556Srgrimes 357277645Sjilles tspec[0] = fs->st_atim; 358277645Sjilles tspec[1] = fs->st_mtim; 359280386Sjilles if (fdval ? futimens(fd, tspec) : utimensat(AT_FDCWD, to.p_path, tspec, 360277645Sjilles islink ? AT_SYMLINK_NOFOLLOW : 0)) { 361277645Sjilles warn("utimensat: %s", to.p_path); 3621556Srgrimes rval = 1; 3631556Srgrimes } 364116673Sjmg if (fdval ? fstat(fd, &ts) : 365116673Sjmg (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts))) 36636838Speter gotstat = 0; 36736838Speter else { 36836838Speter gotstat = 1; 36936838Speter ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | 370284163Sbdrewery S_IRWXU | S_IRWXG | S_IRWXO; 37136838Speter } 3721556Srgrimes /* 3731556Srgrimes * Changing the ownership probably won't succeed, unless we're root 3741556Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 3751556Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 3761556Srgrimes * chown. If chown fails, lose setuid/setgid bits. 3771556Srgrimes */ 37836838Speter if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) 379116673Sjmg if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : 380116673Sjmg (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) : 381116673Sjmg chown(to.p_path, fs->st_uid, fs->st_gid))) { 38236838Speter if (errno != EPERM) { 38336838Speter warn("chown: %s", to.p_path); 38436838Speter rval = 1; 38536838Speter } 38636838Speter fs->st_mode &= ~(S_ISUID | S_ISGID); 38736838Speter } 38836838Speter 38936838Speter if (!gotstat || fs->st_mode != ts.st_mode) 390116673Sjmg if (fdval ? fchmod(fd, fs->st_mode) : 391116673Sjmg (islink ? lchmod(to.p_path, fs->st_mode) : 392116673Sjmg chmod(to.p_path, fs->st_mode))) { 39387652Smckay warn("chmod: %s", to.p_path); 3941556Srgrimes rval = 1; 3951556Srgrimes } 3961556Srgrimes 39736838Speter if (!gotstat || fs->st_flags != ts.st_flags) 398116673Sjmg if (fdval ? 399116673Sjmg fchflags(fd, fs->st_flags) : 400193086Sjilles (islink ? lchflags(to.p_path, fs->st_flags) : 401116673Sjmg chflags(to.p_path, fs->st_flags))) { 40236838Speter warn("chflags: %s", to.p_path); 40336838Speter rval = 1; 40436838Speter } 40536838Speter 4061556Srgrimes return (rval); 4071556Srgrimes} 4081556Srgrimes 409149790Scsjpint 410149790Scsjppreserve_fd_acls(int source_fd, int dest_fd) 411149790Scsjp{ 412149790Scsjp acl_t acl; 413196754Strasz acl_type_t acl_type; 414196754Strasz int acl_supported = 0, ret, trivial; 415149790Scsjp 416196754Strasz ret = fpathconf(source_fd, _PC_ACL_NFS4); 417196754Strasz if (ret > 0 ) { 418196754Strasz acl_supported = 1; 419196754Strasz acl_type = ACL_TYPE_NFS4; 420196754Strasz } else if (ret < 0 && errno != EINVAL) { 421196754Strasz warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", to.p_path); 422196754Strasz return (1); 423196754Strasz } 424196754Strasz if (acl_supported == 0) { 425196754Strasz ret = fpathconf(source_fd, _PC_ACL_EXTENDED); 426196754Strasz if (ret > 0 ) { 427196754Strasz acl_supported = 1; 428196754Strasz acl_type = ACL_TYPE_ACCESS; 429196754Strasz } else if (ret < 0 && errno != EINVAL) { 430196754Strasz warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s", 431196754Strasz to.p_path); 432196754Strasz return (1); 433196754Strasz } 434196754Strasz } 435196754Strasz if (acl_supported == 0) 436149790Scsjp return (0); 437196754Strasz 438196754Strasz acl = acl_get_fd_np(source_fd, acl_type); 439149790Scsjp if (acl == NULL) { 440149790Scsjp warn("failed to get acl entries while setting %s", to.p_path); 441149790Scsjp return (1); 442149790Scsjp } 443196754Strasz if (acl_is_trivial_np(acl, &trivial)) { 444196754Strasz warn("acl_is_trivial() failed for %s", to.p_path); 445196754Strasz acl_free(acl); 446196754Strasz return (1); 447196754Strasz } 448196754Strasz if (trivial) { 449196754Strasz acl_free(acl); 450149790Scsjp return (0); 451196754Strasz } 452196754Strasz if (acl_set_fd_np(dest_fd, acl, acl_type) < 0) { 453149790Scsjp warn("failed to set acl entries for %s", to.p_path); 454196754Strasz acl_free(acl); 455149790Scsjp return (1); 456149790Scsjp } 457196754Strasz acl_free(acl); 458149790Scsjp return (0); 459149790Scsjp} 460149790Scsjp 461149790Scsjpint 462149790Scsjppreserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir) 463149790Scsjp{ 464149790Scsjp acl_t (*aclgetf)(const char *, acl_type_t); 465149790Scsjp int (*aclsetf)(const char *, acl_type_t, acl_t); 466149790Scsjp struct acl *aclp; 467149790Scsjp acl_t acl; 468196754Strasz acl_type_t acl_type; 469196754Strasz int acl_supported = 0, ret, trivial; 470149790Scsjp 471196754Strasz ret = pathconf(source_dir, _PC_ACL_NFS4); 472196754Strasz if (ret > 0) { 473196754Strasz acl_supported = 1; 474196754Strasz acl_type = ACL_TYPE_NFS4; 475196754Strasz } else if (ret < 0 && errno != EINVAL) { 476196754Strasz warn("fpathconf(..., _PC_ACL_NFS4) failed for %s", source_dir); 477196754Strasz return (1); 478196754Strasz } 479196754Strasz if (acl_supported == 0) { 480196754Strasz ret = pathconf(source_dir, _PC_ACL_EXTENDED); 481196754Strasz if (ret > 0) { 482196754Strasz acl_supported = 1; 483196754Strasz acl_type = ACL_TYPE_ACCESS; 484196754Strasz } else if (ret < 0 && errno != EINVAL) { 485196754Strasz warn("fpathconf(..., _PC_ACL_EXTENDED) failed for %s", 486196754Strasz source_dir); 487196754Strasz return (1); 488196754Strasz } 489196754Strasz } 490196754Strasz if (acl_supported == 0) 491149790Scsjp return (0); 492196754Strasz 493149790Scsjp /* 494284163Sbdrewery * If the file is a link we will not follow it. 495149790Scsjp */ 496149790Scsjp if (S_ISLNK(fs->st_mode)) { 497149790Scsjp aclgetf = acl_get_link_np; 498149790Scsjp aclsetf = acl_set_link_np; 499149790Scsjp } else { 500149790Scsjp aclgetf = acl_get_file; 501149790Scsjp aclsetf = acl_set_file; 502149790Scsjp } 503196754Strasz if (acl_type == ACL_TYPE_ACCESS) { 504196754Strasz /* 505196754Strasz * Even if there is no ACL_TYPE_DEFAULT entry here, a zero 506196754Strasz * size ACL will be returned. So it is not safe to simply 507196754Strasz * check the pointer to see if the default ACL is present. 508196754Strasz */ 509196754Strasz acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); 510196754Strasz if (acl == NULL) { 511196754Strasz warn("failed to get default acl entries on %s", 512196754Strasz source_dir); 513196754Strasz return (1); 514196754Strasz } 515196754Strasz aclp = &acl->ats_acl; 516196754Strasz if (aclp->acl_cnt != 0 && aclsetf(dest_dir, 517196754Strasz ACL_TYPE_DEFAULT, acl) < 0) { 518196754Strasz warn("failed to set default acl entries on %s", 519196754Strasz dest_dir); 520196754Strasz acl_free(acl); 521196754Strasz return (1); 522196754Strasz } 523196754Strasz acl_free(acl); 524196754Strasz } 525196754Strasz acl = aclgetf(source_dir, acl_type); 526149790Scsjp if (acl == NULL) { 527196754Strasz warn("failed to get acl entries on %s", source_dir); 528149790Scsjp return (1); 529149790Scsjp } 530196754Strasz if (acl_is_trivial_np(acl, &trivial)) { 531196754Strasz warn("acl_is_trivial() failed on %s", source_dir); 532196754Strasz acl_free(acl); 533149790Scsjp return (1); 534149790Scsjp } 535196754Strasz if (trivial) { 536196754Strasz acl_free(acl); 537196754Strasz return (0); 538149790Scsjp } 539196754Strasz if (aclsetf(dest_dir, acl_type, acl) < 0) { 540149790Scsjp warn("failed to set acl entries on %s", dest_dir); 541196754Strasz acl_free(acl); 542149790Scsjp return (1); 543149790Scsjp } 544196754Strasz acl_free(acl); 545149790Scsjp return (0); 546149790Scsjp} 547149790Scsjp 5481556Srgrimesvoid 54990107Simpusage(void) 5501556Srgrimes{ 55150543Smharo 5521556Srgrimes (void)fprintf(stderr, "%s\n%s\n", 553284163Sbdrewery "usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpsvx] " 554284163Sbdrewery "source_file target_file", 555284163Sbdrewery " cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpsvx] " 556284163Sbdrewery "source_file ... " 557284163Sbdrewery "target_directory"); 55856420Smharo exit(EX_USAGE); 5591556Srgrimes} 560