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