utils.c revision 9220
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. 323044Sdg * 339220Sbde * $Id: utils.c,v 1.3 1995/05/30 00:06:22 rgrimes Exp $ 341556Srgrimes */ 351556Srgrimes 361556Srgrimes#ifndef lint 371556Srgrimesstatic char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; 381556Srgrimes#endif /* not lint */ 391556Srgrimes 401556Srgrimes#include <sys/param.h> 411556Srgrimes#include <sys/stat.h> 421556Srgrimes#include <sys/mman.h> 431556Srgrimes#include <sys/time.h> 441556Srgrimes 451556Srgrimes#include <err.h> 461556Srgrimes#include <errno.h> 471556Srgrimes#include <fcntl.h> 481556Srgrimes#include <fts.h> 491556Srgrimes#include <stdio.h> 501556Srgrimes#include <stdlib.h> 511556Srgrimes#include <string.h> 521556Srgrimes#include <unistd.h> 531556Srgrimes 541556Srgrimes#include "extern.h" 551556Srgrimes 561556Srgrimesint 571556Srgrimescopy_file(entp, dne) 581556Srgrimes FTSENT *entp; 591556Srgrimes int dne; 601556Srgrimes{ 611556Srgrimes static char buf[MAXBSIZE]; 621556Srgrimes struct stat to_stat, *fs; 631556Srgrimes int ch, checkch, from_fd, rcount, rval, to_fd, wcount; 641556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 651556Srgrimes char *p; 661556Srgrimes#endif 678855Srgrimes 681556Srgrimes if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 691556Srgrimes warn("%s", entp->fts_path); 701556Srgrimes return (1); 711556Srgrimes } 721556Srgrimes 731556Srgrimes fs = entp->fts_statp; 741556Srgrimes 751556Srgrimes /* 761556Srgrimes * If the file exists and we're interactive, verify with the user. 771556Srgrimes * If the file DNE, set the mode to be the from file, minus setuid 781556Srgrimes * bits, modified by the umask; arguably wrong, but it makes copying 791556Srgrimes * executables work right and it's been that way forever. (The 801556Srgrimes * other choice is 666 or'ed with the execute bits on the from file 811556Srgrimes * modified by the umask.) 821556Srgrimes */ 831556Srgrimes if (!dne) { 841556Srgrimes if (iflag) { 851556Srgrimes (void)fprintf(stderr, "overwrite %s? ", to.p_path); 861556Srgrimes checkch = ch = getchar(); 871556Srgrimes while (ch != '\n' && ch != EOF) 881556Srgrimes ch = getchar(); 891556Srgrimes if (checkch != 'y') { 901556Srgrimes (void)close(from_fd); 911556Srgrimes return (0); 921556Srgrimes } 931556Srgrimes } 941556Srgrimes to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 951556Srgrimes } else 961556Srgrimes to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 971556Srgrimes fs->st_mode & ~(S_ISUID | S_ISGID)); 981556Srgrimes 991556Srgrimes if (to_fd == -1) { 1001556Srgrimes warn("%s", to.p_path); 1011556Srgrimes (void)close(from_fd); 1021556Srgrimes return (1);; 1031556Srgrimes } 1041556Srgrimes 1051556Srgrimes rval = 0; 1061556Srgrimes 1071556Srgrimes /* 1081556Srgrimes * Mmap and write if less than 8M (the limit is so we don't totally 1091556Srgrimes * trash memory on big files. This is really a minor hack, but it 1101556Srgrimes * wins some CPU back. 1111556Srgrimes */ 1121556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 1131556Srgrimes if (fs->st_size <= 8 * 1048576) { 1141556Srgrimes if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 1151556Srgrimes 0, from_fd, (off_t)0)) == (char *)-1) { 1161556Srgrimes warn("%s", entp->fts_path); 1171556Srgrimes rval = 1; 1181556Srgrimes } else { 1191556Srgrimes if (write(to_fd, p, fs->st_size) != fs->st_size) { 1201556Srgrimes warn("%s", to.p_path); 1211556Srgrimes rval = 1; 1221556Srgrimes } 1231556Srgrimes /* Some systems don't unmap on close(2). */ 1241556Srgrimes if (munmap(p, fs->st_size) < 0) { 1251556Srgrimes warn("%s", entp->fts_path); 1261556Srgrimes rval = 1; 1271556Srgrimes } 1281556Srgrimes } 1291556Srgrimes } else 1301556Srgrimes#endif 1311556Srgrimes { 1321556Srgrimes while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 1331556Srgrimes wcount = write(to_fd, buf, rcount); 1341556Srgrimes if (rcount != wcount || wcount == -1) { 1351556Srgrimes warn("%s", to.p_path); 1361556Srgrimes rval = 1; 1371556Srgrimes break; 1381556Srgrimes } 1391556Srgrimes } 1401556Srgrimes if (rcount < 0) { 1411556Srgrimes warn("%s", entp->fts_path); 1421556Srgrimes rval = 1; 1431556Srgrimes } 1441556Srgrimes } 1451556Srgrimes 1469220Sbde /* 1479220Sbde * Don't remove the target even after an error. The target might 1489220Sbde * not be a regular file, or its attributes might be important, 1499220Sbde * or its contents might be irreplacable. It would only be safe 1509220Sbde * to remove it if we created it and its length is 0. 1519220Sbde */ 1521556Srgrimes 1531556Srgrimes if (pflag && setfile(fs, to_fd)) 1541556Srgrimes rval = 1; 1551556Srgrimes /* 1561556Srgrimes * If the source was setuid or setgid, lose the bits unless the 1571556Srgrimes * copy is owned by the same user and group. 1581556Srgrimes */ 1591556Srgrimes#define RETAINBITS \ 1601556Srgrimes (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 1611556Srgrimes else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) 1621556Srgrimes if (fstat(to_fd, &to_stat)) { 1631556Srgrimes warn("%s", to.p_path); 1641556Srgrimes rval = 1; 1651556Srgrimes } else if (fs->st_gid == to_stat.st_gid && 1661556Srgrimes fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { 1671556Srgrimes warn("%s", to.p_path); 1681556Srgrimes rval = 1; 1691556Srgrimes } 1701556Srgrimes (void)close(from_fd); 1711556Srgrimes if (close(to_fd)) { 1721556Srgrimes warn("%s", to.p_path); 1731556Srgrimes rval = 1; 1741556Srgrimes } 1751556Srgrimes return (rval); 1761556Srgrimes} 1771556Srgrimes 1781556Srgrimesint 1791556Srgrimescopy_link(p, exists) 1801556Srgrimes FTSENT *p; 1811556Srgrimes int exists; 1821556Srgrimes{ 1831556Srgrimes int len; 1841556Srgrimes char link[MAXPATHLEN]; 1851556Srgrimes 1861556Srgrimes if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 1871556Srgrimes warn("readlink: %s", p->fts_path); 1881556Srgrimes return (1); 1891556Srgrimes } 1901556Srgrimes link[len] = '\0'; 1911556Srgrimes if (exists && unlink(to.p_path)) { 1921556Srgrimes warn("unlink: %s", to.p_path); 1931556Srgrimes return (1); 1941556Srgrimes } 1951556Srgrimes if (symlink(link, to.p_path)) { 1961556Srgrimes warn("symlink: %s", link); 1971556Srgrimes return (1); 1981556Srgrimes } 1991556Srgrimes return (0); 2001556Srgrimes} 2011556Srgrimes 2021556Srgrimesint 2031556Srgrimescopy_fifo(from_stat, exists) 2041556Srgrimes struct stat *from_stat; 2051556Srgrimes int exists; 2061556Srgrimes{ 2071556Srgrimes if (exists && unlink(to.p_path)) { 2081556Srgrimes warn("unlink: %s", to.p_path); 2091556Srgrimes return (1); 2101556Srgrimes } 2111556Srgrimes if (mkfifo(to.p_path, from_stat->st_mode)) { 2121556Srgrimes warn("mkfifo: %s", to.p_path); 2131556Srgrimes return (1); 2141556Srgrimes } 2151556Srgrimes return (pflag ? setfile(from_stat, 0) : 0); 2161556Srgrimes} 2171556Srgrimes 2181556Srgrimesint 2191556Srgrimescopy_special(from_stat, exists) 2201556Srgrimes struct stat *from_stat; 2211556Srgrimes int exists; 2221556Srgrimes{ 2231556Srgrimes if (exists && unlink(to.p_path)) { 2241556Srgrimes warn("unlink: %s", to.p_path); 2251556Srgrimes return (1); 2261556Srgrimes } 2271556Srgrimes if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 2281556Srgrimes warn("mknod: %s", to.p_path); 2291556Srgrimes return (1); 2301556Srgrimes } 2311556Srgrimes return (pflag ? setfile(from_stat, 0) : 0); 2321556Srgrimes} 2331556Srgrimes 2341556Srgrimes 2351556Srgrimesint 2361556Srgrimessetfile(fs, fd) 2371556Srgrimes register struct stat *fs; 2381556Srgrimes int fd; 2391556Srgrimes{ 2401556Srgrimes static struct timeval tv[2]; 2411556Srgrimes int rval; 2421556Srgrimes 2431556Srgrimes rval = 0; 2441556Srgrimes fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 2451556Srgrimes 2461556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 2471556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 2481556Srgrimes if (utimes(to.p_path, tv)) { 2491556Srgrimes warn("utimes: %s", to.p_path); 2501556Srgrimes rval = 1; 2511556Srgrimes } 2521556Srgrimes /* 2531556Srgrimes * Changing the ownership probably won't succeed, unless we're root 2541556Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 2551556Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 2561556Srgrimes * chown. If chown fails, lose setuid/setgid bits. 2571556Srgrimes */ 2581556Srgrimes if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 2591556Srgrimes chown(to.p_path, fs->st_uid, fs->st_gid)) { 2601556Srgrimes if (errno != EPERM) { 2611556Srgrimes warn("chown: %s", to.p_path); 2621556Srgrimes rval = 1; 2631556Srgrimes } 2641556Srgrimes fs->st_mode &= ~(S_ISUID | S_ISGID); 2651556Srgrimes } 2661556Srgrimes if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 2671556Srgrimes warn("chown: %s", to.p_path); 2681556Srgrimes rval = 1; 2691556Srgrimes } 2701556Srgrimes 2711556Srgrimes if (fd ? 2721556Srgrimes fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) { 2731556Srgrimes warn("chflags: %s", to.p_path); 2741556Srgrimes rval = 1; 2751556Srgrimes } 2761556Srgrimes return (rval); 2771556Srgrimes} 2781556Srgrimes 2791556Srgrimesvoid 2801556Srgrimesusage() 2811556Srgrimes{ 2821556Srgrimes (void)fprintf(stderr, "%s\n%s\n", 2831556Srgrimes"usage: cp [-R [-H | -L | -P] [-fip] src target", 2841556Srgrimes" cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory"); 2851556Srgrimes exit(1); 2861556Srgrimes} 287