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