utils.c revision 99109
12061Sjkh/*- 250479Speter * Copyright (c) 1991, 1993, 1994 32061Sjkh * The Regents of the University of California. All rights reserved. 438666Sjb * 532427Sjb * Redistribution and use in source and binary forms, with or without 6103985Sphk * modification, are permitted provided that the following conditions 7103985Sphk * are met: 838666Sjb * 1. Redistributions of source code must retain the above copyright 938666Sjb * notice, this list of conditions and the following disclaimer. 1038666Sjb * 2. Redistributions in binary form must reproduce the above copyright 1138666Sjb * notice, this list of conditions and the following disclaimer in the 1264049Salex * documentation and/or other materials provided with the distribution. 1364049Salex * 3. All advertising materials mentioning features or use of this software 1466071Smarkm * must display the following acknowledgement: 1573504Sobrien * This product includes software developed by the University of 1638666Sjb * California, Berkeley and its contributors. 1744918Sjkh * 4. Neither the name of the University nor the names of its contributors 1838666Sjb * may be used to endorse or promote products derived from this software 1938666Sjb * without specific prior written permission. 20108451Sschweikh * 2138666Sjb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2238666Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2338666Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2438666Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2538978Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2638978Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2732427Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2838666Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29108451Sschweikh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3038666Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3138666Sjb * SUCH DAMAGE. 3238666Sjb */ 3338666Sjb 3417308Speter#ifndef lint 3591606Skeramida#if 0 3619175Sbdestatic char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; 3796205Sjwd#endif 3896205Sjwd#endif /* not lint */ 3938042Sbde#include <sys/cdefs.h> 4096205Sjwd__FBSDID("$FreeBSD: head/bin/cp/utils.c 99109 2002-06-30 05:13:54Z obrien $"); 4196205Sjwd 4238042Sbde#include <sys/param.h> 4396205Sjwd#include <sys/stat.h> 4496205Sjwd#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 4517308Speter#include <sys/mman.h> 4696205Sjwd#endif 4796205Sjwd 4817308Speter#include <err.h> 4996205Sjwd#include <errno.h> 5096205Sjwd#include <fcntl.h> 5196205Sjwd#include <fts.h> 5296205Sjwd#include <limits.h> 5396205Sjwd#include <stdio.h> 5496205Sjwd#include <stdlib.h> 5596205Sjwd#include <sysexits.h> 5696205Sjwd#include <unistd.h> 5796205Sjwd 5896205Sjwd#include "extern.h" 5996205Sjwd 6096205Sjwdint 6198775Sdilloncopy_file(FTSENT *entp, int dne) 6298723Sdillon{ 6398723Sdillon static char buf[MAXBSIZE]; 6498723Sdillon struct stat *fs; 6598723Sdillon int ch, checkch, from_fd, rcount, rval, to_fd, wcount, wresid; 6638666Sjb char *bufp; 6717308Speter#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 6838666Sjb char *p; 6917308Speter#endif 7027910Sasami 7143226Sjkh if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 7243226Sjkh warn("%s", entp->fts_path); 7343226Sjkh return (1); 74108451Sschweikh } 7527910Sasami 7638666Sjb fs = entp->fts_statp; 7738666Sjb 7838666Sjb /* 7927910Sasami * If the file exists and we're interactive, verify with the user. 8038666Sjb * If the file DNE, set the mode to be the from file, minus setuid 8138666Sjb * bits, modified by the umask; arguably wrong, but it makes copying 8243226Sjkh * executables work right and it's been that way forever. (The 8343226Sjkh * other choice is 666 or'ed with the execute bits on the from file 8427910Sasami * modified by the umask.) 8538666Sjb */ 8627910Sasami if (!dne) { 8717308Speter#define YESNO "(y/n [n]) " 8838666Sjb if (iflag) { 8938666Sjb (void)fprintf(stderr, "overwrite %s? %s", 9017308Speter to.p_path, YESNO); 9195509Sru checkch = ch = getchar(); 9295793Sru while (ch != '\n' && ch != EOF) 9397252Sru ch = getchar(); 9496164Sru if (checkch != 'y' && checkch != 'Y') { 9595146Sgshapiro (void)close(from_fd); 962061Sjkh (void)fprintf(stderr, "not overwritten\n"); 9797769Sru return (1); 9897252Sru } 9997252Sru } 10095730Sru 10195793Sru if (fflag) { 10295730Sru /* remove existing destination file name, 10395730Sru * create a new file */ 10495730Sru (void)unlink(to.p_path); 105110035Sru to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 106107516Sru fs->st_mode & ~(S_ISUID | S_ISGID)); 107110035Sru } else 108110035Sru /* overwrite existing destination file name */ 109110035Sru to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 110110035Sru } else 11154324Smarcel to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 11217308Speter fs->st_mode & ~(S_ISUID | S_ISGID)); 11338666Sjb 11417308Speter if (to_fd == -1) { 11597252Sru warn("%s", to.p_path); 11638666Sjb (void)close(from_fd); 117110035Sru return (1); 1182302Spaul } 11939206Sjkh 12039206Sjkh rval = 0; 12139206Sjkh 12273349Sru /* 12317308Speter * Mmap and write if less than 8M (the limit is so we don't totally 12454324Smarcel * trash memory on big files. This is really a minor hack, but it 12554324Smarcel * wins some CPU back. 12654324Smarcel */ 12754324Smarcel#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 12854324Smarcel if (S_ISREG(fs->st_mode) && fs->st_size <= 8 * 1048576) { 12954324Smarcel if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 13054324Smarcel MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { 131103436Speter warn("%s", entp->fts_path); 13254324Smarcel rval = 1; 13354324Smarcel } else { 13454324Smarcel for (bufp = p, wresid = fs->st_size; ; 13554324Smarcel bufp += wcount, wresid -= wcount) { 13654324Smarcel wcount = write(to_fd, bufp, wresid); 13754324Smarcel if (wcount >= wresid || wcount <= 0) 138110035Sru break; 13954324Smarcel } 140110035Sru if (wcount != wresid) { 141110035Sru warn("%s", to.p_path); 14254324Smarcel rval = 1; 14354324Smarcel } 14454324Smarcel /* Some systems don't unmap on close(2). */ 14554324Smarcel if (munmap(p, fs->st_size) < 0) { 14654324Smarcel warn("%s", entp->fts_path); 147110035Sru rval = 1; 14854324Smarcel } 14954324Smarcel } 15054324Smarcel } else 151103436Speter#endif 15254324Smarcel { 15354324Smarcel while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 15454324Smarcel for (bufp = buf, wresid = rcount; ; 15595730Sru bufp += wcount, wresid -= wcount) { 15695730Sru wcount = write(to_fd, bufp, wresid); 15795730Sru if (wcount >= wresid || wcount <= 0) 15895730Sru break; 15995730Sru } 16095730Sru if (wcount != wresid) { 16195730Sru warn("%s", to.p_path); 16238666Sjb rval = 1; 163107374Sru break; 16417308Speter } 16555678Smarcel } 166110035Sru if (rcount < 0) { 167110035Sru warn("%s", entp->fts_path); 168110035Sru rval = 1; 169110035Sru } 170110035Sru } 1712061Sjkh 17217308Speter /* 173107516Sru * Don't remove the target even after an error. The target might 174107374Sru * not be a regular file, or its attributes might be important, 17555678Smarcel * or its contents might be irreplaceable. It would only be safe 176107516Sru * to remove it if we created it and its length is 0. 177107516Sru */ 178107516Sru 179107516Sru if (pflag && setfile(fs, to_fd)) 180107516Sru rval = 1; 181107516Sru (void)close(from_fd); 182107516Sru if (close(to_fd)) { 183107516Sru warn("%s", to.p_path); 18455678Smarcel rval = 1; 18555678Smarcel } 18655678Smarcel return (rval); 187107556Sbmah} 18855678Smarcel 18955678Smarcelint 190107516Srucopy_link(FTSENT *p, int exists) 191107516Sru{ 192107516Sru int len; 193107516Sru char llink[PATH_MAX]; 19455678Smarcel 19555678Smarcel if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) { 19638666Sjb warn("readlink: %s", p->fts_path); 19738666Sjb return (1); 19817308Speter } 19955678Smarcel llink[len] = '\0'; 20038978Sjb if (exists && unlink(to.p_path)) { 2013626Swollman warn("unlink: %s", to.p_path); 20217308Speter return (1); 20338666Sjb } 20417308Speter if (symlink(llink, to.p_path)) { 20543226Sjkh warn("symlink: %s", llink); 20643226Sjkh return (1); 20743226Sjkh } 20838666Sjb return (0); 20938666Sjb} 210110035Sru 211103985Sphkint 212103985Sphkcopy_fifo(struct stat *from_stat, int exists) 213103985Sphk{ 214103985Sphk if (exists && unlink(to.p_path)) { 215103985Sphk warn("unlink: %s", to.p_path); 216103985Sphk return (1); 217111089Sphk } 218103985Sphk if (mkfifo(to.p_path, from_stat->st_mode)) { 219103985Sphk warn("mkfifo: %s", to.p_path); 220103985Sphk return (1); 221103985Sphk } 222103985Sphk return (pflag ? setfile(from_stat, 0) : 0); 223104277Sphk} 224104277Sphk 225103985Sphkint 226103985Sphkcopy_special(struct stat *from_stat, int exists) 227111089Sphk{ 228103985Sphk if (exists && unlink(to.p_path)) { 229103985Sphk warn("unlink: %s", to.p_path); 230107884Simp return (1); 231104370Sphk } 232104370Sphk if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 233104370Sphk warn("mknod: %s", to.p_path); 234107884Simp return (1); 235107884Simp } 236107884Simp return (pflag ? setfile(from_stat, 0) : 0); 237107884Simp} 238107884Simp 239111089Sphkint 240107884Simpsetfile(struct stat *fs, int fd) 241107884Simp{ 242103985Sphk static struct timeval tv[2]; 243107884Simp struct stat ts; 244103985Sphk int rval; 245103985Sphk int gotstat; 246111089Sphk 247103985Sphk rval = 0; 248103985Sphk fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | 249103985Sphk S_IRWXU | S_IRWXG | S_IRWXO; 250103985Sphk 251111089Sphk TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 252103985Sphk TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 253111089Sphk if (utimes(to.p_path, tv)) { 254103985Sphk warn("utimes: %s", to.p_path); 255111089Sphk rval = 1; 256103985Sphk } 257103985Sphk if (fd ? fstat(fd, &ts) : stat(to.p_path, &ts)) 258 gotstat = 0; 259 else { 260 gotstat = 1; 261 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | 262 S_IRWXU | S_IRWXG | S_IRWXO; 263 } 264 /* 265 * Changing the ownership probably won't succeed, unless we're root 266 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 267 * the mode; current BSD behavior is to remove all setuid bits on 268 * chown. If chown fails, lose setuid/setgid bits. 269 */ 270 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) 271 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 272 chown(to.p_path, fs->st_uid, fs->st_gid)) { 273 if (errno != EPERM) { 274 warn("chown: %s", to.p_path); 275 rval = 1; 276 } 277 fs->st_mode &= ~(S_ISUID | S_ISGID); 278 } 279 280 if (!gotstat || fs->st_mode != ts.st_mode) 281 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 282 warn("chmod: %s", to.p_path); 283 rval = 1; 284 } 285 286 if (!gotstat || fs->st_flags != ts.st_flags) 287 if (fd ? 288 fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) { 289 warn("chflags: %s", to.p_path); 290 rval = 1; 291 } 292 293 return (rval); 294} 295 296void 297usage(void) 298{ 299 300 (void)fprintf(stderr, "%s\n%s\n", 301"usage: cp [-R [-H | -L | -P]] [-f | -i] [-pv] src target", 302" cp [-R [-H | -L | -P]] [-f | -i] [-pv] src1 ... srcN directory"); 303 exit(EX_USAGE); 304} 305