1 /* $NetBSD: utils.c,v 1.8 1997/07/20 05:13:37 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; 40 #else 41 __RCSID("$NetBSD: utils.c,v 1.8 1997/07/20 05:13:37 thorpej Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/mman.h> 48 #include <sys/time.h> 49 50 #include <err.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <fts.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include "extern.h" 60 61 int 62 copy_file(entp, dne) 63 FTSENT *entp; 64 int dne; 65 { 66 static char buf[MAXBSIZE]; 67 struct stat to_stat, *fs; 68 int ch, checkch, from_fd, rcount, rval, to_fd, wcount; 69 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 70 char *p; 71 #endif 72 73 if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 74 warn("%s", entp->fts_path); 75 return (1); 76 } 77 78 fs = entp->fts_statp; 79 80 /* 81 * If the file exists and we're interactive, verify with the user. 82 * If the file DNE, set the mode to be the from file, minus setuid 83 * bits, modified by the umask; arguably wrong, but it makes copying 84 * executables work right and it's been that way forever. (The 85 * other choice is 666 or'ed with the execute bits on the from file 86 * modified by the umask.) 87 */ 88 if (!dne) { 89 if (iflag) { 90 (void)fprintf(stderr, "overwrite %s? ", to.p_path); 91 checkch = ch = getchar(); 92 while (ch != '\n' && ch != EOF) 93 ch = getchar(); 94 if (checkch != 'y' && checkch != 'Y') { 95 (void)close(from_fd); 96 return (0); 97 } 98 } 99 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); 100 } else 101 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, 102 fs->st_mode & ~(S_ISUID | S_ISGID)); 103 104 if (to_fd == -1) { 105 warn("%s", to.p_path); 106 (void)close(from_fd); 107 return (1);; 108 } 109 110 rval = 0; 111 112 /* 113 * Mmap and write if less than 8M (the limit is so we don't totally 114 * trash memory on big files. This is really a minor hack, but it 115 * wins some CPU back. 116 */ 117 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED 118 if (fs->st_size <= 8 * 1048576) { 119 if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 120 0, from_fd, (off_t)0)) == (char *)-1) { 121 warn("%s", entp->fts_path); 122 rval = 1; 123 } else { 124 if (write(to_fd, p, fs->st_size) != fs->st_size) { 125 warn("%s", to.p_path); 126 rval = 1; 127 } 128 /* Some systems don't unmap on close(2). */ 129 if (munmap(p, fs->st_size) < 0) { 130 warn("%s", entp->fts_path); 131 rval = 1; 132 } 133 } 134 } else 135 #endif 136 { 137 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 138 wcount = write(to_fd, buf, rcount); 139 if (rcount != wcount || wcount == -1) { 140 warn("%s", to.p_path); 141 rval = 1; 142 break; 143 } 144 } 145 if (rcount < 0) { 146 warn("%s", entp->fts_path); 147 rval = 1; 148 } 149 } 150 151 if (rval == 1) { 152 (void)close(from_fd); 153 (void)close(to_fd); 154 return (1); 155 } 156 157 if (pflag && setfile(fs, to_fd)) 158 rval = 1; 159 /* 160 * If the source was setuid or setgid, lose the bits unless the 161 * copy is owned by the same user and group. 162 */ 163 #define RETAINBITS \ 164 (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) 165 else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) 166 if (fstat(to_fd, &to_stat)) { 167 warn("%s", to.p_path); 168 rval = 1; 169 } else if (fs->st_gid == to_stat.st_gid && 170 fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { 171 warn("%s", to.p_path); 172 rval = 1; 173 } 174 (void)close(from_fd); 175 if (close(to_fd)) { 176 warn("%s", to.p_path); 177 rval = 1; 178 } 179 return (rval); 180 } 181 182 int 183 copy_link(p, exists) 184 FTSENT *p; 185 int exists; 186 { 187 int len; 188 char link[MAXPATHLEN]; 189 190 if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 191 warn("readlink: %s", p->fts_path); 192 return (1); 193 } 194 link[len] = '\0'; 195 if (exists && unlink(to.p_path)) { 196 warn("unlink: %s", to.p_path); 197 return (1); 198 } 199 if (symlink(link, to.p_path)) { 200 warn("symlink: %s", link); 201 return (1); 202 } 203 return (0); 204 } 205 206 int 207 copy_fifo(from_stat, exists) 208 struct stat *from_stat; 209 int exists; 210 { 211 if (exists && unlink(to.p_path)) { 212 warn("unlink: %s", to.p_path); 213 return (1); 214 } 215 if (mkfifo(to.p_path, from_stat->st_mode)) { 216 warn("mkfifo: %s", to.p_path); 217 return (1); 218 } 219 return (pflag ? setfile(from_stat, 0) : 0); 220 } 221 222 int 223 copy_special(from_stat, exists) 224 struct stat *from_stat; 225 int exists; 226 { 227 if (exists && unlink(to.p_path)) { 228 warn("unlink: %s", to.p_path); 229 return (1); 230 } 231 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 232 warn("mknod: %s", to.p_path); 233 return (1); 234 } 235 return (pflag ? setfile(from_stat, 0) : 0); 236 } 237 238 239 int 240 setfile(fs, fd) 241 struct stat *fs; 242 int fd; 243 { 244 static struct timeval tv[2]; 245 int rval; 246 247 rval = 0; 248 fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 249 250 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 251 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 252 if (utimes(to.p_path, tv)) { 253 warn("utimes: %s", to.p_path); 254 rval = 1; 255 } 256 /* 257 * Changing the ownership probably won't succeed, unless we're root 258 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 259 * the mode; current BSD behavior is to remove all setuid bits on 260 * chown. If chown fails, lose setuid/setgid bits. 261 */ 262 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 263 chown(to.p_path, fs->st_uid, fs->st_gid)) { 264 if (errno != EPERM) { 265 warn("chown: %s", to.p_path); 266 rval = 1; 267 } 268 fs->st_mode &= ~(S_ISUID | S_ISGID); 269 } 270 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { 271 warn("chown: %s", to.p_path); 272 rval = 1; 273 } 274 275 /* 276 * XXX 277 * NFS doesn't support chflags; ignore errors unless there's reason 278 * to believe we're losing bits. (Note, this still won't be right 279 * if the server supports flags and we were trying to *remove* flags 280 * on a file that we copied, i.e., that we didn't create.) 281 */ 282 errno = 0; 283 if (fd ? fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) 284 if (errno != EOPNOTSUPP || fs->st_flags != 0) { 285 warn("chflags: %s", to.p_path); 286 rval = 1; 287 } 288 return (rval); 289 } 290 291 void 292 usage() 293 { 294 (void)fprintf(stderr, "%s\n%s\n", 295 "usage: cp [-R [-H | -L | -P]] [-fip] src target", 296 " cp [-R [-H | -L | -P]] [-fip] src1 ... srcN directory"); 297 exit(1); 298 } 299