utils.c revision 149790
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 149790 2005-09-05 04:36:08Z csjp $"); 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" 57113209Smdodd#define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y)) 581556Srgrimes 591556Srgrimesint 60105395Smarkmcopy_file(const FTSENT *entp, int dne) 611556Srgrimes{ 621556Srgrimes static char buf[MAXBSIZE]; 6378070Sbde struct stat *fs; 6499363Smarkm int ch, checkch, from_fd, rcount, rval, to_fd; 6599363Smarkm ssize_t wcount; 6699363Smarkm size_t wresid; 67113209Smdodd size_t wtotal; 6832540Sbde char *bufp; 691556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 701556Srgrimes char *p; 711556Srgrimes#endif 728855Srgrimes 731556Srgrimes if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 741556Srgrimes warn("%s", entp->fts_path); 751556Srgrimes return (1); 761556Srgrimes } 771556Srgrimes 781556Srgrimes fs = entp->fts_statp; 791556Srgrimes 801556Srgrimes /* 811556Srgrimes * If the file exists and we're interactive, verify with the user. 821556Srgrimes * If the file DNE, set the mode to be the from file, minus setuid 831556Srgrimes * bits, modified by the umask; arguably wrong, but it makes copying 841556Srgrimes * executables work right and it's been that way forever. (The 851556Srgrimes * other choice is 666 or'ed with the execute bits on the from file 861556Srgrimes * modified by the umask.) 871556Srgrimes */ 881556Srgrimes if (!dne) { 8930088Swosch#define YESNO "(y/n [n]) " 90100538Sjohan if (nflag) { 91100538Sjohan if (vflag) 92100538Sjohan printf("%s not overwritten\n", to.p_path); 93130102Stjr (void)close(from_fd); 94100538Sjohan return (0); 95100538Sjohan } else if (iflag) { 9630088Swosch (void)fprintf(stderr, "overwrite %s? %s", 9730088Swosch to.p_path, YESNO); 981556Srgrimes checkch = ch = getchar(); 991556Srgrimes while (ch != '\n' && ch != EOF) 1001556Srgrimes ch = getchar(); 10114416Swosch if (checkch != 'y' && checkch != 'Y') { 1021556Srgrimes (void)close(from_fd); 10330088Swosch (void)fprintf(stderr, "not overwritten\n"); 10456420Smharo return (1); 1051556Srgrimes } 1061556Srgrimes } 10714416Swosch 10814416Swosch if (fflag) { 10914416Swosch /* remove existing destination file name, 11014416Swosch * create a new file */ 11114416Swosch (void)unlink(to.p_path); 11214416Swosch to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 11314416Swosch fs->st_mode & ~(S_ISUID | S_ISGID)); 11414416Swosch } else 11514416Swosch /* overwrite existing destination file name */ 11614416Swosch to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 1171556Srgrimes } else 1181556Srgrimes to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 1191556Srgrimes fs->st_mode & ~(S_ISUID | S_ISGID)); 1201556Srgrimes 1211556Srgrimes if (to_fd == -1) { 1221556Srgrimes warn("%s", to.p_path); 1231556Srgrimes (void)close(from_fd); 12491087Smarkm return (1); 1251556Srgrimes } 1261556Srgrimes 1271556Srgrimes rval = 0; 1281556Srgrimes 1291556Srgrimes /* 1301556Srgrimes * Mmap and write if less than 8M (the limit is so we don't totally 1311556Srgrimes * trash memory on big files. This is really a minor hack, but it 1321556Srgrimes * wins some CPU back. 1331556Srgrimes */ 1341556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 135122601Salc if (S_ISREG(fs->st_mode) && fs->st_size > 0 && 136122601Salc fs->st_size <= 8 * 1048576) { 1371556Srgrimes if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 13821786Salex MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { 1391556Srgrimes warn("%s", entp->fts_path); 1401556Srgrimes rval = 1; 1411556Srgrimes } else { 142113209Smdodd wtotal = 0; 14332540Sbde for (bufp = p, wresid = fs->st_size; ; 14499363Smarkm bufp += wcount, wresid -= (size_t)wcount) { 14532540Sbde wcount = write(to_fd, bufp, wresid); 146113209Smdodd wtotal += wcount; 147113209Smdodd if (info) { 148113209Smdodd info = 0; 149113209Smdodd (void)fprintf(stderr, 150113209Smdodd "%s -> %s %3d%%\n", 151113209Smdodd entp->fts_path, to.p_path, 152113209Smdodd cp_pct(wtotal, fs->st_size)); 153113209Smdodd 154113209Smdodd } 155101093Smarkm if (wcount >= (ssize_t)wresid || wcount <= 0) 15632540Sbde break; 15732540Sbde } 158101093Smarkm if (wcount != (ssize_t)wresid) { 1591556Srgrimes warn("%s", to.p_path); 1601556Srgrimes rval = 1; 1611556Srgrimes } 1621556Srgrimes /* Some systems don't unmap on close(2). */ 1631556Srgrimes if (munmap(p, fs->st_size) < 0) { 1641556Srgrimes warn("%s", entp->fts_path); 1651556Srgrimes rval = 1; 1661556Srgrimes } 1671556Srgrimes } 1681556Srgrimes } else 1691556Srgrimes#endif 1701556Srgrimes { 171113209Smdodd wtotal = 0; 1721556Srgrimes while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 17332540Sbde for (bufp = buf, wresid = rcount; ; 17432540Sbde bufp += wcount, wresid -= wcount) { 17532540Sbde wcount = write(to_fd, bufp, wresid); 176113209Smdodd wtotal += wcount; 177113209Smdodd if (info) { 178113209Smdodd info = 0; 179113209Smdodd (void)fprintf(stderr, 180113209Smdodd "%s -> %s %3d%%\n", 181113209Smdodd entp->fts_path, to.p_path, 182113209Smdodd cp_pct(wtotal, fs->st_size)); 183113209Smdodd 184113209Smdodd } 185101093Smarkm if (wcount >= (ssize_t)wresid || wcount <= 0) 18632540Sbde break; 18732540Sbde } 188101093Smarkm if (wcount != (ssize_t)wresid) { 1891556Srgrimes warn("%s", to.p_path); 1901556Srgrimes rval = 1; 1911556Srgrimes break; 1921556Srgrimes } 1931556Srgrimes } 1941556Srgrimes if (rcount < 0) { 1951556Srgrimes warn("%s", entp->fts_path); 1961556Srgrimes rval = 1; 1971556Srgrimes } 1981556Srgrimes } 1991556Srgrimes 2009220Sbde /* 2019220Sbde * Don't remove the target even after an error. The target might 2029220Sbde * not be a regular file, or its attributes might be important, 20346684Skris * or its contents might be irreplaceable. It would only be safe 2049220Sbde * to remove it if we created it and its length is 0. 2059220Sbde */ 2061556Srgrimes 2071556Srgrimes if (pflag && setfile(fs, to_fd)) 2081556Srgrimes rval = 1; 209149790Scsjp if (pflag && preserve_fd_acls(from_fd, to_fd) != 0) 210149790Scsjp rval = 1; 2111556Srgrimes (void)close(from_fd); 2121556Srgrimes if (close(to_fd)) { 2131556Srgrimes warn("%s", to.p_path); 2141556Srgrimes rval = 1; 2151556Srgrimes } 2161556Srgrimes return (rval); 2171556Srgrimes} 2181556Srgrimes 2191556Srgrimesint 220105395Smarkmcopy_link(const FTSENT *p, int exists) 2211556Srgrimes{ 2221556Srgrimes int len; 22391087Smarkm char llink[PATH_MAX]; 2241556Srgrimes 22591087Smarkm if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) { 2261556Srgrimes warn("readlink: %s", p->fts_path); 2271556Srgrimes return (1); 2281556Srgrimes } 22991087Smarkm llink[len] = '\0'; 2301556Srgrimes if (exists && unlink(to.p_path)) { 2311556Srgrimes warn("unlink: %s", to.p_path); 2321556Srgrimes return (1); 2331556Srgrimes } 23491087Smarkm if (symlink(llink, to.p_path)) { 23591087Smarkm warn("symlink: %s", llink); 2361556Srgrimes return (1); 2371556Srgrimes } 238116673Sjmg return (pflag ? setfile(p->fts_statp, -1) : 0); 2391556Srgrimes} 2401556Srgrimes 2411556Srgrimesint 24290107Simpcopy_fifo(struct stat *from_stat, int exists) 2431556Srgrimes{ 2441556Srgrimes if (exists && unlink(to.p_path)) { 2451556Srgrimes warn("unlink: %s", to.p_path); 2461556Srgrimes return (1); 2471556Srgrimes } 2481556Srgrimes if (mkfifo(to.p_path, from_stat->st_mode)) { 2491556Srgrimes warn("mkfifo: %s", to.p_path); 2501556Srgrimes return (1); 2511556Srgrimes } 252116673Sjmg return (pflag ? setfile(from_stat, -1) : 0); 2531556Srgrimes} 2541556Srgrimes 2551556Srgrimesint 25690107Simpcopy_special(struct stat *from_stat, int exists) 2571556Srgrimes{ 2581556Srgrimes if (exists && unlink(to.p_path)) { 2591556Srgrimes warn("unlink: %s", to.p_path); 2601556Srgrimes return (1); 2611556Srgrimes } 2621556Srgrimes if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 2631556Srgrimes warn("mknod: %s", to.p_path); 2641556Srgrimes return (1); 2651556Srgrimes } 266116673Sjmg return (pflag ? setfile(from_stat, -1) : 0); 2671556Srgrimes} 2681556Srgrimes 2691556Srgrimesint 27090107Simpsetfile(struct stat *fs, int fd) 2711556Srgrimes{ 2721556Srgrimes static struct timeval tv[2]; 27336838Speter struct stat ts; 274116673Sjmg int rval, gotstat, islink, fdval; 2751556Srgrimes 2761556Srgrimes rval = 0; 277116673Sjmg fdval = fd != -1; 278116673Sjmg islink = !fdval && S_ISLNK(fs->st_mode); 27911146Sbde fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | 28011146Sbde S_IRWXU | S_IRWXG | S_IRWXO; 2811556Srgrimes 2821556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 2831556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 284116673Sjmg if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) { 285116673Sjmg warn("%sutimes: %s", islink ? "l" : "", to.p_path); 2861556Srgrimes rval = 1; 2871556Srgrimes } 288116673Sjmg if (fdval ? fstat(fd, &ts) : 289116673Sjmg (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts))) 29036838Speter gotstat = 0; 29136838Speter else { 29236838Speter gotstat = 1; 29336838Speter ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | 29436838Speter S_IRWXU | S_IRWXG | S_IRWXO; 29536838Speter } 2961556Srgrimes /* 2971556Srgrimes * Changing the ownership probably won't succeed, unless we're root 2981556Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 2991556Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 3001556Srgrimes * chown. If chown fails, lose setuid/setgid bits. 3011556Srgrimes */ 30236838Speter if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) 303116673Sjmg if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : 304116673Sjmg (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) : 305116673Sjmg chown(to.p_path, fs->st_uid, fs->st_gid))) { 30636838Speter if (errno != EPERM) { 30736838Speter warn("chown: %s", to.p_path); 30836838Speter rval = 1; 30936838Speter } 31036838Speter fs->st_mode &= ~(S_ISUID | S_ISGID); 31136838Speter } 31236838Speter 31336838Speter if (!gotstat || fs->st_mode != ts.st_mode) 314116673Sjmg if (fdval ? fchmod(fd, fs->st_mode) : 315116673Sjmg (islink ? lchmod(to.p_path, fs->st_mode) : 316116673Sjmg chmod(to.p_path, fs->st_mode))) { 31787652Smckay warn("chmod: %s", to.p_path); 3181556Srgrimes rval = 1; 3191556Srgrimes } 3201556Srgrimes 32136838Speter if (!gotstat || fs->st_flags != ts.st_flags) 322116673Sjmg if (fdval ? 323116673Sjmg fchflags(fd, fs->st_flags) : 324116673Sjmg (islink ? (errno = ENOSYS) : 325116673Sjmg chflags(to.p_path, fs->st_flags))) { 32636838Speter warn("chflags: %s", to.p_path); 32736838Speter rval = 1; 32836838Speter } 32936838Speter 3301556Srgrimes return (rval); 3311556Srgrimes} 3321556Srgrimes 333149790Scsjpint 334149790Scsjppreserve_fd_acls(int source_fd, int dest_fd) 335149790Scsjp{ 336149790Scsjp struct acl *aclp; 337149790Scsjp acl_t acl; 338149790Scsjp 339149790Scsjp if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || 340149790Scsjp fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) 341149790Scsjp return (0); 342149790Scsjp acl = acl_get_fd(source_fd); 343149790Scsjp if (acl == NULL) { 344149790Scsjp warn("failed to get acl entries while setting %s", to.p_path); 345149790Scsjp return (1); 346149790Scsjp } 347149790Scsjp aclp = &acl->ats_acl; 348149790Scsjp if (aclp->acl_cnt == 3) 349149790Scsjp return (0); 350149790Scsjp if (acl_set_fd(dest_fd, acl) < 0) { 351149790Scsjp warn("failed to set acl entries for %s", to.p_path); 352149790Scsjp return (1); 353149790Scsjp } 354149790Scsjp return (0); 355149790Scsjp} 356149790Scsjp 357149790Scsjpint 358149790Scsjppreserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir) 359149790Scsjp{ 360149790Scsjp acl_t (*aclgetf)(const char *, acl_type_t); 361149790Scsjp int (*aclsetf)(const char *, acl_type_t, acl_t); 362149790Scsjp struct acl *aclp; 363149790Scsjp acl_t acl; 364149790Scsjp 365149790Scsjp if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || 366149790Scsjp pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) 367149790Scsjp return (0); 368149790Scsjp /* 369149790Scsjp * If the file is a link we will not follow it 370149790Scsjp */ 371149790Scsjp if (S_ISLNK(fs->st_mode)) { 372149790Scsjp aclgetf = acl_get_link_np; 373149790Scsjp aclsetf = acl_set_link_np; 374149790Scsjp } else { 375149790Scsjp aclgetf = acl_get_file; 376149790Scsjp aclsetf = acl_set_file; 377149790Scsjp } 378149790Scsjp /* 379149790Scsjp * Even if there is no ACL_TYPE_DEFAULT entry here, a zero 380149790Scsjp * size ACL will be returned. So it is not safe to simply 381149790Scsjp * check the pointer to see if the default ACL is present. 382149790Scsjp */ 383149790Scsjp acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); 384149790Scsjp if (acl == NULL) { 385149790Scsjp warn("failed to get default acl entries on %s", 386149790Scsjp source_dir); 387149790Scsjp return (1); 388149790Scsjp } 389149790Scsjp aclp = &acl->ats_acl; 390149790Scsjp if (aclp->acl_cnt != 0 && aclsetf(dest_dir, 391149790Scsjp ACL_TYPE_DEFAULT, acl) < 0) { 392149790Scsjp warn("failed to set default acl entries on %s", 393149790Scsjp dest_dir); 394149790Scsjp return (1); 395149790Scsjp } 396149790Scsjp acl = aclgetf(source_dir, ACL_TYPE_ACCESS); 397149790Scsjp if (acl == NULL) { 398149790Scsjp warn("failed to get acl entries on %s", source_dir); 399149790Scsjp return (1); 400149790Scsjp } 401149790Scsjp aclp = &acl->ats_acl; 402149790Scsjp if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) { 403149790Scsjp warn("failed to set acl entries on %s", dest_dir); 404149790Scsjp return (1); 405149790Scsjp } 406149790Scsjp return (0); 407149790Scsjp} 408149790Scsjp 4091556Srgrimesvoid 41090107Simpusage(void) 4111556Srgrimes{ 41250543Smharo 4131556Srgrimes (void)fprintf(stderr, "%s\n%s\n", 414141578Sru"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] source_file target_file", 415141578Sru" cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] source_file ... " 416141578Sru"target_directory"); 41756420Smharo exit(EX_USAGE); 4181556Srgrimes} 419