utils.c revision 30088
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 * 3330088Swosch * $Id: utils.c,v 1.13 1997/02/22 14:01:34 peter Exp $ 341556Srgrimes */ 351556Srgrimes 361556Srgrimes#ifndef lint 3720412Sstevestatic char const 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) { 8430088Swosch#define YESNO "(y/n [n]) " 851556Srgrimes if (iflag) { 8630088Swosch (void)fprintf(stderr, "overwrite %s? %s", 8730088Swosch to.p_path, YESNO); 881556Srgrimes checkch = ch = getchar(); 891556Srgrimes while (ch != '\n' && ch != EOF) 901556Srgrimes ch = getchar(); 9114416Swosch if (checkch != 'y' && checkch != 'Y') { 921556Srgrimes (void)close(from_fd); 9330088Swosch (void)fprintf(stderr, "not overwritten\n"); 941556Srgrimes return (0); 951556Srgrimes } 961556Srgrimes } 9714416Swosch 9814416Swosch if (fflag) { 9914416Swosch /* remove existing destination file name, 10014416Swosch * create a new file */ 10114416Swosch (void)unlink(to.p_path); 10214416Swosch to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 10314416Swosch fs->st_mode & ~(S_ISUID | S_ISGID)); 10414416Swosch } else 10514416Swosch /* overwrite existing destination file name */ 10614416Swosch to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 1071556Srgrimes } else 1081556Srgrimes to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 1091556Srgrimes fs->st_mode & ~(S_ISUID | S_ISGID)); 1101556Srgrimes 1111556Srgrimes if (to_fd == -1) { 1121556Srgrimes warn("%s", to.p_path); 1131556Srgrimes (void)close(from_fd); 1141556Srgrimes return (1);; 1151556Srgrimes } 1161556Srgrimes 1171556Srgrimes rval = 0; 1181556Srgrimes 1191556Srgrimes /* 1201556Srgrimes * Mmap and write if less than 8M (the limit is so we don't totally 1211556Srgrimes * trash memory on big files. This is really a minor hack, but it 1221556Srgrimes * wins some CPU back. 1231556Srgrimes */ 1241556Srgrimes#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 1251556Srgrimes if (fs->st_size <= 8 * 1048576) { 1261556Srgrimes if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 12721786Salex MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { 1281556Srgrimes warn("%s", entp->fts_path); 1291556Srgrimes rval = 1; 1301556Srgrimes } else { 1311556Srgrimes if (write(to_fd, p, fs->st_size) != fs->st_size) { 1321556Srgrimes warn("%s", to.p_path); 1331556Srgrimes rval = 1; 1341556Srgrimes } 1351556Srgrimes /* Some systems don't unmap on close(2). */ 1361556Srgrimes if (munmap(p, fs->st_size) < 0) { 1371556Srgrimes warn("%s", entp->fts_path); 1381556Srgrimes rval = 1; 1391556Srgrimes } 1401556Srgrimes } 1411556Srgrimes } else 1421556Srgrimes#endif 1431556Srgrimes { 1441556Srgrimes while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 1451556Srgrimes wcount = write(to_fd, buf, rcount); 1461556Srgrimes if (rcount != wcount || wcount == -1) { 1471556Srgrimes warn("%s", to.p_path); 1481556Srgrimes rval = 1; 1491556Srgrimes break; 1501556Srgrimes } 1511556Srgrimes } 1521556Srgrimes if (rcount < 0) { 1531556Srgrimes warn("%s", entp->fts_path); 1541556Srgrimes rval = 1; 1551556Srgrimes } 1561556Srgrimes } 1571556Srgrimes 1589220Sbde /* 1599220Sbde * Don't remove the target even after an error. The target might 1609220Sbde * not be a regular file, or its attributes might be important, 1619220Sbde * or its contents might be irreplacable. It would only be safe 1629220Sbde * to remove it if we created it and its length is 0. 1639220Sbde */ 1641556Srgrimes 1651556Srgrimes if (pflag && setfile(fs, to_fd)) 1661556Srgrimes rval = 1; 1671556Srgrimes /* 1681556Srgrimes * If the source was setuid or setgid, lose the bits unless the 1691556Srgrimes * copy is owned by the same user and group. 1701556Srgrimes */ 1711556Srgrimes#define RETAINBITS \ 1721556Srgrimes (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 1731556Srgrimes else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) 1741556Srgrimes if (fstat(to_fd, &to_stat)) { 1751556Srgrimes warn("%s", to.p_path); 1761556Srgrimes rval = 1; 1771556Srgrimes } else if (fs->st_gid == to_stat.st_gid && 1781556Srgrimes fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { 1791556Srgrimes warn("%s", to.p_path); 1801556Srgrimes rval = 1; 1811556Srgrimes } 1821556Srgrimes (void)close(from_fd); 1831556Srgrimes if (close(to_fd)) { 1841556Srgrimes warn("%s", to.p_path); 1851556Srgrimes rval = 1; 1861556Srgrimes } 1871556Srgrimes return (rval); 1881556Srgrimes} 1891556Srgrimes 1901556Srgrimesint 1911556Srgrimescopy_link(p, exists) 1921556Srgrimes FTSENT *p; 1931556Srgrimes int exists; 1941556Srgrimes{ 1951556Srgrimes int len; 1961556Srgrimes char link[MAXPATHLEN]; 1971556Srgrimes 1981556Srgrimes if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 1991556Srgrimes warn("readlink: %s", p->fts_path); 2001556Srgrimes return (1); 2011556Srgrimes } 2021556Srgrimes link[len] = '\0'; 2031556Srgrimes if (exists && unlink(to.p_path)) { 2041556Srgrimes warn("unlink: %s", to.p_path); 2051556Srgrimes return (1); 2061556Srgrimes } 2071556Srgrimes if (symlink(link, to.p_path)) { 2081556Srgrimes warn("symlink: %s", link); 2091556Srgrimes return (1); 2101556Srgrimes } 2111556Srgrimes return (0); 2121556Srgrimes} 2131556Srgrimes 2141556Srgrimesint 2151556Srgrimescopy_fifo(from_stat, exists) 2161556Srgrimes struct stat *from_stat; 2171556Srgrimes int exists; 2181556Srgrimes{ 2191556Srgrimes if (exists && unlink(to.p_path)) { 2201556Srgrimes warn("unlink: %s", to.p_path); 2211556Srgrimes return (1); 2221556Srgrimes } 2231556Srgrimes if (mkfifo(to.p_path, from_stat->st_mode)) { 2241556Srgrimes warn("mkfifo: %s", to.p_path); 2251556Srgrimes return (1); 2261556Srgrimes } 2271556Srgrimes return (pflag ? setfile(from_stat, 0) : 0); 2281556Srgrimes} 2291556Srgrimes 2301556Srgrimesint 2311556Srgrimescopy_special(from_stat, exists) 2321556Srgrimes struct stat *from_stat; 2331556Srgrimes int exists; 2341556Srgrimes{ 2351556Srgrimes if (exists && unlink(to.p_path)) { 2361556Srgrimes warn("unlink: %s", to.p_path); 2371556Srgrimes return (1); 2381556Srgrimes } 2391556Srgrimes if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 2401556Srgrimes warn("mknod: %s", to.p_path); 2411556Srgrimes return (1); 2421556Srgrimes } 2431556Srgrimes return (pflag ? setfile(from_stat, 0) : 0); 2441556Srgrimes} 2451556Srgrimes 2461556Srgrimes 2471556Srgrimesint 2481556Srgrimessetfile(fs, fd) 2491556Srgrimes register struct stat *fs; 2501556Srgrimes int fd; 2511556Srgrimes{ 2521556Srgrimes static struct timeval tv[2]; 2531556Srgrimes int rval; 2541556Srgrimes 2551556Srgrimes rval = 0; 25611146Sbde fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | 25711146Sbde S_IRWXU | S_IRWXG | S_IRWXO; 2581556Srgrimes 2591556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 2601556Srgrimes TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 2611556Srgrimes if (utimes(to.p_path, tv)) { 2621556Srgrimes warn("utimes: %s", to.p_path); 2631556Srgrimes rval = 1; 2641556Srgrimes } 2651556Srgrimes /* 2661556Srgrimes * Changing the ownership probably won't succeed, unless we're root 2671556Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 2681556Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 2691556Srgrimes * chown. If chown fails, lose setuid/setgid bits. 2701556Srgrimes */ 2711556Srgrimes if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 2721556Srgrimes chown(to.p_path, fs->st_uid, fs->st_gid)) { 2731556Srgrimes if (errno != EPERM) { 2741556Srgrimes warn("chown: %s", to.p_path); 2751556Srgrimes rval = 1; 2761556Srgrimes } 2771556Srgrimes fs->st_mode &= ~(S_ISUID | S_ISGID); 2781556Srgrimes } 2791556Srgrimes if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 2801556Srgrimes warn("chown: %s", to.p_path); 2811556Srgrimes rval = 1; 2821556Srgrimes } 2831556Srgrimes 2841556Srgrimes if (fd ? 2851556Srgrimes fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) { 2861556Srgrimes warn("chflags: %s", to.p_path); 2871556Srgrimes rval = 1; 2881556Srgrimes } 2891556Srgrimes return (rval); 2901556Srgrimes} 2911556Srgrimes 2921556Srgrimesvoid 2931556Srgrimesusage() 2941556Srgrimes{ 2951556Srgrimes (void)fprintf(stderr, "%s\n%s\n", 29614416Swosch"usage: cp [-R [-H | -L | -P]] [-f | -i] [-p] src target", 29714416Swosch" cp [-R [-H | -L | -P]] [-f | -i] [-p] src1 ... srcN directory"); 2981556Srgrimes exit(1); 2991556Srgrimes} 300