utils.c revision 35773
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 * 3. All advertising materials mentioning features or use of this software 141556Srgrimes * must display the following acknowledgement: 151556Srgrimes * This product includes software developed by the University of 161556Srgrimes * California, Berkeley and its contributors. 171556Srgrimes * 4. Neither the name of the University nor the names of its contributors 181556Srgrimes * may be used to endorse or promote products derived from this software 191556Srgrimes * without specific prior written permission. 201556Srgrimes * 211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311556Srgrimes * SUCH DAMAGE. 321556Srgrimes */ 331556Srgrimes 341556Srgrimes#ifndef lint 3535773Scharnier#if 0 3620412Sstevestatic char const sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; 3735773Scharnier#endif 3835773Scharnierstatic const char rcsid[] = 3935773Scharnier "$Id$"; 401556Srgrimes#endif /* not lint */ 411556Srgrimes 421556Srgrimes#include <sys/param.h> 431556Srgrimes#include <sys/stat.h> 4435773Scharnier#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 451556Srgrimes#include <sys/mman.h> 4635773Scharnier#endif 471556Srgrimes 481556Srgrimes#include <err.h> 491556Srgrimes#include <errno.h> 501556Srgrimes#include <fcntl.h> 511556Srgrimes#include <fts.h> 521556Srgrimes#include <stdio.h> 531556Srgrimes#include <unistd.h> 541556Srgrimes 551556Srgrimes#include "extern.h" 561556Srgrimes 571556Srgrimesint 581556Srgrimescopy_file(entp, dne) 591556Srgrimes FTSENT *entp; 601556Srgrimes int dne; 611556Srgrimes{ 621556Srgrimes static char buf[MAXBSIZE]; 631556Srgrimes struct stat to_stat, *fs; 6432540Sbde int ch, checkch, from_fd, rcount, rval, to_fd, wcount, wresid; 6532540Sbde char *bufp; 661556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 671556Srgrimes char *p; 681556Srgrimes#endif 698855Srgrimes 701556Srgrimes if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 711556Srgrimes warn("%s", entp->fts_path); 721556Srgrimes return (1); 731556Srgrimes } 741556Srgrimes 751556Srgrimes fs = entp->fts_statp; 761556Srgrimes 771556Srgrimes /* 781556Srgrimes * If the file exists and we're interactive, verify with the user. 791556Srgrimes * If the file DNE, set the mode to be the from file, minus setuid 801556Srgrimes * bits, modified by the umask; arguably wrong, but it makes copying 811556Srgrimes * executables work right and it's been that way forever. (The 821556Srgrimes * other choice is 666 or'ed with the execute bits on the from file 831556Srgrimes * modified by the umask.) 841556Srgrimes */ 851556Srgrimes if (!dne) { 8630088Swosch#define YESNO "(y/n [n]) " 871556Srgrimes if (iflag) { 8830088Swosch (void)fprintf(stderr, "overwrite %s? %s", 8930088Swosch to.p_path, YESNO); 901556Srgrimes checkch = ch = getchar(); 911556Srgrimes while (ch != '\n' && ch != EOF) 921556Srgrimes ch = getchar(); 9314416Swosch if (checkch != 'y' && checkch != 'Y') { 941556Srgrimes (void)close(from_fd); 9530088Swosch (void)fprintf(stderr, "not overwritten\n"); 961556Srgrimes return (0); 971556Srgrimes } 981556Srgrimes } 9914416Swosch 10014416Swosch if (fflag) { 10114416Swosch /* remove existing destination file name, 10214416Swosch * create a new file */ 10314416Swosch (void)unlink(to.p_path); 10414416Swosch to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 10514416Swosch fs->st_mode & ~(S_ISUID | S_ISGID)); 10614416Swosch } else 10714416Swosch /* overwrite existing destination file name */ 10814416Swosch to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 1091556Srgrimes } else 1101556Srgrimes to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 1111556Srgrimes fs->st_mode & ~(S_ISUID | S_ISGID)); 1121556Srgrimes 1131556Srgrimes if (to_fd == -1) { 1141556Srgrimes warn("%s", to.p_path); 1151556Srgrimes (void)close(from_fd); 1161556Srgrimes return (1);; 1171556Srgrimes } 1181556Srgrimes 1191556Srgrimes rval = 0; 1201556Srgrimes 1211556Srgrimes /* 1221556Srgrimes * Mmap and write if less than 8M (the limit is so we don't totally 1231556Srgrimes * trash memory on big files. This is really a minor hack, but it 1241556Srgrimes * wins some CPU back. 1251556Srgrimes */ 1261556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 1271556Srgrimes if (fs->st_size <= 8 * 1048576) { 1281556Srgrimes if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 12921786Salex MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { 1301556Srgrimes warn("%s", entp->fts_path); 1311556Srgrimes rval = 1; 1321556Srgrimes } else { 13332540Sbde for (bufp = p, wresid = fs->st_size; ; 13432588Sbde bufp += wcount, wresid -= wcount) { 13532540Sbde wcount = write(to_fd, bufp, wresid); 13632540Sbde if (wcount >= wresid || wcount <= 0) 13732540Sbde break; 13832540Sbde } 13932540Sbde if (wcount != wresid) { 1401556Srgrimes warn("%s", to.p_path); 1411556Srgrimes rval = 1; 1421556Srgrimes } 1431556Srgrimes /* Some systems don't unmap on close(2). */ 1441556Srgrimes if (munmap(p, fs->st_size) < 0) { 1451556Srgrimes warn("%s", entp->fts_path); 1461556Srgrimes rval = 1; 1471556Srgrimes } 1481556Srgrimes } 1491556Srgrimes } else 1501556Srgrimes#endif 1511556Srgrimes { 1521556Srgrimes while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 15332540Sbde for (bufp = buf, wresid = rcount; ; 15432540Sbde bufp += wcount, wresid -= wcount) { 15532540Sbde wcount = write(to_fd, bufp, wresid); 15632540Sbde if (wcount >= wresid || wcount <= 0) 15732540Sbde break; 15832540Sbde } 15932540Sbde if (wcount != wresid) { 1601556Srgrimes warn("%s", to.p_path); 1611556Srgrimes rval = 1; 1621556Srgrimes break; 1631556Srgrimes } 1641556Srgrimes } 1651556Srgrimes if (rcount < 0) { 1661556Srgrimes warn("%s", entp->fts_path); 1671556Srgrimes rval = 1; 1681556Srgrimes } 1691556Srgrimes } 1701556Srgrimes 1719220Sbde /* 1729220Sbde * Don't remove the target even after an error. The target might 1739220Sbde * not be a regular file, or its attributes might be important, 1749220Sbde * or its contents might be irreplacable. It would only be safe 1759220Sbde * to remove it if we created it and its length is 0. 1769220Sbde */ 1771556Srgrimes 1781556Srgrimes if (pflag && setfile(fs, to_fd)) 1791556Srgrimes rval = 1; 1801556Srgrimes /* 1811556Srgrimes * If the source was setuid or setgid, lose the bits unless the 1821556Srgrimes * copy is owned by the same user and group. 1831556Srgrimes */ 1841556Srgrimes#define RETAINBITS \ 1851556Srgrimes (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 1861556Srgrimes else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) 1871556Srgrimes if (fstat(to_fd, &to_stat)) { 1881556Srgrimes warn("%s", to.p_path); 1891556Srgrimes rval = 1; 1901556Srgrimes } else if (fs->st_gid == to_stat.st_gid && 1911556Srgrimes fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { 1921556Srgrimes warn("%s", to.p_path); 1931556Srgrimes rval = 1; 1941556Srgrimes } 1951556Srgrimes (void)close(from_fd); 1961556Srgrimes if (close(to_fd)) { 1971556Srgrimes warn("%s", to.p_path); 1981556Srgrimes rval = 1; 1991556Srgrimes } 2001556Srgrimes return (rval); 2011556Srgrimes} 2021556Srgrimes 2031556Srgrimesint 2041556Srgrimescopy_link(p, exists) 2051556Srgrimes FTSENT *p; 2061556Srgrimes int exists; 2071556Srgrimes{ 2081556Srgrimes int len; 2091556Srgrimes char link[MAXPATHLEN]; 2101556Srgrimes 2111556Srgrimes if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 2121556Srgrimes warn("readlink: %s", p->fts_path); 2131556Srgrimes return (1); 2141556Srgrimes } 2151556Srgrimes link[len] = '\0'; 2161556Srgrimes if (exists && unlink(to.p_path)) { 2171556Srgrimes warn("unlink: %s", to.p_path); 2181556Srgrimes return (1); 2191556Srgrimes } 2201556Srgrimes if (symlink(link, to.p_path)) { 2211556Srgrimes warn("symlink: %s", link); 2221556Srgrimes return (1); 2231556Srgrimes } 2241556Srgrimes return (0); 2251556Srgrimes} 2261556Srgrimes 2271556Srgrimesint 2281556Srgrimescopy_fifo(from_stat, exists) 2291556Srgrimes struct stat *from_stat; 2301556Srgrimes int exists; 2311556Srgrimes{ 2321556Srgrimes if (exists && unlink(to.p_path)) { 2331556Srgrimes warn("unlink: %s", to.p_path); 2341556Srgrimes return (1); 2351556Srgrimes } 2361556Srgrimes if (mkfifo(to.p_path, from_stat->st_mode)) { 2371556Srgrimes warn("mkfifo: %s", to.p_path); 2381556Srgrimes return (1); 2391556Srgrimes } 2401556Srgrimes return (pflag ? setfile(from_stat, 0) : 0); 2411556Srgrimes} 2421556Srgrimes 2431556Srgrimesint 2441556Srgrimescopy_special(from_stat, exists) 2451556Srgrimes struct stat *from_stat; 2461556Srgrimes int exists; 2471556Srgrimes{ 2481556Srgrimes if (exists && unlink(to.p_path)) { 2491556Srgrimes warn("unlink: %s", to.p_path); 2501556Srgrimes return (1); 2511556Srgrimes } 2521556Srgrimes if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 2531556Srgrimes warn("mknod: %s", to.p_path); 2541556Srgrimes return (1); 2551556Srgrimes } 2561556Srgrimes return (pflag ? setfile(from_stat, 0) : 0); 2571556Srgrimes} 2581556Srgrimes 2591556Srgrimes 2601556Srgrimesint 2611556Srgrimessetfile(fs, fd) 2621556Srgrimes register struct stat *fs; 2631556Srgrimes int fd; 2641556Srgrimes{ 2651556Srgrimes static struct timeval tv[2]; 2661556Srgrimes int rval; 2671556Srgrimes 2681556Srgrimes rval = 0; 26911146Sbde fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | 27011146Sbde S_IRWXU | S_IRWXG | S_IRWXO; 2711556Srgrimes 2721556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 2731556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 2741556Srgrimes if (utimes(to.p_path, tv)) { 2751556Srgrimes warn("utimes: %s", to.p_path); 2761556Srgrimes rval = 1; 2771556Srgrimes } 2781556Srgrimes /* 2791556Srgrimes * Changing the ownership probably won't succeed, unless we're root 2801556Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 2811556Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 2821556Srgrimes * chown. If chown fails, lose setuid/setgid bits. 2831556Srgrimes */ 2841556Srgrimes if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 2851556Srgrimes chown(to.p_path, fs->st_uid, fs->st_gid)) { 2861556Srgrimes if (errno != EPERM) { 2871556Srgrimes warn("chown: %s", to.p_path); 2881556Srgrimes rval = 1; 2891556Srgrimes } 2901556Srgrimes fs->st_mode &= ~(S_ISUID | S_ISGID); 2911556Srgrimes } 2921556Srgrimes if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 2931556Srgrimes warn("chown: %s", to.p_path); 2941556Srgrimes rval = 1; 2951556Srgrimes } 2961556Srgrimes 2971556Srgrimes if (fd ? 2981556Srgrimes fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) { 2991556Srgrimes warn("chflags: %s", to.p_path); 3001556Srgrimes rval = 1; 3011556Srgrimes } 3021556Srgrimes return (rval); 3031556Srgrimes} 3041556Srgrimes 3051556Srgrimesvoid 3061556Srgrimesusage() 3071556Srgrimes{ 3081556Srgrimes (void)fprintf(stderr, "%s\n%s\n", 30914416Swosch"usage: cp [-R [-H | -L | -P]] [-f | -i] [-p] src target", 31014416Swosch" cp [-R [-H | -L | -P]] [-f | -i] [-p] src1 ... srcN directory"); 3111556Srgrimes exit(1); 3121556Srgrimes} 313