utils.c revision 8855
1118611Snjl/*- 2118611Snjl * Copyright (c) 1991, 1993, 1994 3245582Sjkim * The Regents of the University of California. All rights reserved. 4118611Snjl * 5118611Snjl * Redistribution and use in source and binary forms, with or without 6118611Snjl * modification, are permitted provided that the following conditions 7217365Sjkim * are met: 8245582Sjkim * 1. Redistributions of source code must retain the above copyright 9118611Snjl * notice, this list of conditions and the following disclaimer. 10118611Snjl * 2. Redistributions in binary form must reproduce the above copyright 11217365Sjkim * notice, this list of conditions and the following disclaimer in the 12217365Sjkim * documentation and/or other materials provided with the distribution. 13217365Sjkim * 3. All advertising materials mentioning features or use of this software 14217365Sjkim * must display the following acknowledgement: 15217365Sjkim * This product includes software developed by the University of 16217365Sjkim * California, Berkeley and its contributors. 17217365Sjkim * 4. Neither the name of the University nor the names of its contributors 18217365Sjkim * may be used to endorse or promote products derived from this software 19217365Sjkim * without specific prior written permission. 20217365Sjkim * 21217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24217365Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27217365Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31217365Sjkim * SUCH DAMAGE. 32217365Sjkim * 33217365Sjkim * $Id: utils.c,v 1.2 1994/09/24 02:53:42 davidg Exp $ 34217365Sjkim */ 35217365Sjkim 36217365Sjkim#ifndef lint 37217365Sjkimstatic char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; 38217365Sjkim#endif /* not lint */ 39217365Sjkim 40217365Sjkim#include <sys/param.h> 41217365Sjkim#include <sys/stat.h> 42217365Sjkim#include <sys/mman.h> 43118611Snjl#include <sys/time.h> 44118611Snjl 45151937Sjkim#include <err.h> 46118611Snjl#include <errno.h> 47193529Sjkim#include <fcntl.h> 48193529Sjkim#include <fts.h> 49193529Sjkim#include <stdio.h> 50193529Sjkim#include <stdlib.h> 51118611Snjl#include <string.h> 52118611Snjl#include <unistd.h> 53118611Snjl 54118611Snjl#include "extern.h" 55118611Snjl 56151937Sjkimint 57118611Snjlcopy_file(entp, dne) 58151937Sjkim FTSENT *entp; 59167802Sjkim int dne; 60167802Sjkim{ 61167802Sjkim static char buf[MAXBSIZE]; 62167802Sjkim struct stat to_stat, *fs; 63167802Sjkim int ch, checkch, from_fd, rcount, rval, to_fd, wcount; 64151937Sjkim#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 65212761Sjkim char *p; 66193529Sjkim#endif 67193529Sjkim 68193529Sjkim if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 69193529Sjkim warn("%s", entp->fts_path); 70118611Snjl return (1); 71118611Snjl } 72245582Sjkim 73118611Snjl fs = entp->fts_statp; 74245582Sjkim 75118611Snjl /* 76193529Sjkim * If the file exists and we're interactive, verify with the user. 77193529Sjkim * If the file DNE, set the mode to be the from file, minus setuid 78245582Sjkim * bits, modified by the umask; arguably wrong, but it makes copying 79245582Sjkim * executables work right and it's been that way forever. (The 80245582Sjkim * other choice is 666 or'ed with the execute bits on the from file 81193529Sjkim * modified by the umask.) 82193529Sjkim */ 83193529Sjkim if (!dne) { 84167802Sjkim if (iflag) { 85245582Sjkim (void)fprintf(stderr, "overwrite %s? ", to.p_path); 86118611Snjl checkch = ch = getchar(); 87118611Snjl while (ch != '\n' && ch != EOF) 88118611Snjl ch = getchar(); 89245582Sjkim if (checkch != 'y') { 90118611Snjl (void)close(from_fd); 91245582Sjkim return (0); 92245582Sjkim } 93151937Sjkim } 94118611Snjl to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 95118611Snjl } else 96118611Snjl to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 97118611Snjl fs->st_mode & ~(S_ISUID | S_ISGID)); 98118611Snjl 99167802Sjkim if (to_fd == -1) { 100167802Sjkim warn("%s", to.p_path); 101167802Sjkim (void)close(from_fd); 102167802Sjkim return (1);; 103167802Sjkim } 104167802Sjkim 105167802Sjkim rval = 0; 106167802Sjkim 107167802Sjkim /* 108167802Sjkim * Mmap and write if less than 8M (the limit is so we don't totally 109167802Sjkim * trash memory on big files. This is really a minor hack, but it 110167802Sjkim * wins some CPU back. 111167802Sjkim */ 112167802Sjkim#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 113167802Sjkim if (fs->st_size <= 8 * 1048576) { 114167802Sjkim if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 115167802Sjkim 0, from_fd, (off_t)0)) == (char *)-1) { 116167802Sjkim warn("%s", entp->fts_path); 117167802Sjkim rval = 1; 118167802Sjkim } else { 119167802Sjkim if (write(to_fd, p, fs->st_size) != fs->st_size) { 120167802Sjkim warn("%s", to.p_path); 121167802Sjkim rval = 1; 122167802Sjkim } 123167802Sjkim /* Some systems don't unmap on close(2). */ 124167802Sjkim if (munmap(p, fs->st_size) < 0) { 125167802Sjkim warn("%s", entp->fts_path); 126167802Sjkim rval = 1; 127167802Sjkim } 128167802Sjkim } 129167802Sjkim } else 130167802Sjkim#endif 131167802Sjkim { 132167802Sjkim while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 133167802Sjkim wcount = write(to_fd, buf, rcount); 134167802Sjkim if (rcount != wcount || wcount == -1) { 135167802Sjkim warn("%s", to.p_path); 136167802Sjkim rval = 1; 137167802Sjkim break; 138167802Sjkim } 139167802Sjkim } 140167802Sjkim if (rcount < 0) { 141167802Sjkim warn("%s", entp->fts_path); 142167802Sjkim rval = 1; 143167802Sjkim } 144167802Sjkim } 145167802Sjkim 146167802Sjkim /* If the copy went bad, lose the file. */ 147167802Sjkim if (rval == 1) { 148167802Sjkim (void)unlink(to.p_path); 149167802Sjkim (void)close(from_fd); 150167802Sjkim (void)close(to_fd); 151167802Sjkim return (1); 152167802Sjkim } 153167802Sjkim 154167802Sjkim if (pflag && setfile(fs, to_fd)) 155167802Sjkim rval = 1; 156167802Sjkim /* 157167802Sjkim * If the source was setuid or setgid, lose the bits unless the 158167802Sjkim * copy is owned by the same user and group. 159167802Sjkim */ 160167802Sjkim#define RETAINBITS \ 161167802Sjkim (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 162167802Sjkim else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) 163167802Sjkim if (fstat(to_fd, &to_stat)) { 164167802Sjkim warn("%s", to.p_path); 165245582Sjkim rval = 1; 166167802Sjkim } else if (fs->st_gid == to_stat.st_gid && 167245582Sjkim fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { 168167802Sjkim warn("%s", to.p_path); 169245582Sjkim rval = 1; 170167802Sjkim } 171245582Sjkim (void)close(from_fd); 172167802Sjkim if (close(to_fd)) { 173167802Sjkim warn("%s", to.p_path); 174167802Sjkim rval = 1; 175245582Sjkim } 176245582Sjkim return (rval); 177245582Sjkim} 178167802Sjkim 179118611Snjlint 180245582Sjkimcopy_link(p, exists) 181118611Snjl FTSENT *p; 182118611Snjl int exists; 183118611Snjl{ 184118611Snjl int len; 185118611Snjl char link[MAXPATHLEN]; 186118611Snjl 187118611Snjl if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 188118611Snjl warn("readlink: %s", p->fts_path); 189118611Snjl return (1); 190167802Sjkim } 191118611Snjl link[len] = '\0'; 192245582Sjkim if (exists && unlink(to.p_path)) { 193118611Snjl warn("unlink: %s", to.p_path); 194118611Snjl return (1); 195118611Snjl } 196118611Snjl if (symlink(link, to.p_path)) { 197118611Snjl warn("symlink: %s", link); 198118611Snjl return (1); 199245582Sjkim } 200118611Snjl return (0); 201118611Snjl} 202118611Snjl 203118611Snjlint 204118611Snjlcopy_fifo(from_stat, exists) 205118611Snjl struct stat *from_stat; 206245582Sjkim int exists; 207245582Sjkim{ 208118611Snjl if (exists && unlink(to.p_path)) { 209245582Sjkim warn("unlink: %s", to.p_path); 210118611Snjl return (1); 211118611Snjl } 212118611Snjl if (mkfifo(to.p_path, from_stat->st_mode)) { 213245582Sjkim warn("mkfifo: %s", to.p_path); 214118611Snjl return (1); 215 } 216 return (pflag ? setfile(from_stat, 0) : 0); 217} 218 219int 220copy_special(from_stat, exists) 221 struct stat *from_stat; 222 int exists; 223{ 224 if (exists && unlink(to.p_path)) { 225 warn("unlink: %s", to.p_path); 226 return (1); 227 } 228 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 229 warn("mknod: %s", to.p_path); 230 return (1); 231 } 232 return (pflag ? setfile(from_stat, 0) : 0); 233} 234 235 236int 237setfile(fs, fd) 238 register struct stat *fs; 239 int fd; 240{ 241 static struct timeval tv[2]; 242 int rval; 243 244 rval = 0; 245 fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 246 247 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 248 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 249 if (utimes(to.p_path, tv)) { 250 warn("utimes: %s", to.p_path); 251 rval = 1; 252 } 253 /* 254 * Changing the ownership probably won't succeed, unless we're root 255 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 256 * the mode; current BSD behavior is to remove all setuid bits on 257 * chown. If chown fails, lose setuid/setgid bits. 258 */ 259 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 260 chown(to.p_path, fs->st_uid, fs->st_gid)) { 261 if (errno != EPERM) { 262 warn("chown: %s", to.p_path); 263 rval = 1; 264 } 265 fs->st_mode &= ~(S_ISUID | S_ISGID); 266 } 267 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 268 warn("chown: %s", to.p_path); 269 rval = 1; 270 } 271 272 if (fd ? 273 fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) { 274 warn("chflags: %s", to.p_path); 275 rval = 1; 276 } 277 return (rval); 278} 279 280void 281usage() 282{ 283 (void)fprintf(stderr, "%s\n%s\n", 284"usage: cp [-R [-H | -L | -P] [-fip] src target", 285" cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory"); 286 exit(1); 287} 288