153766Selan /*- 253766Selan * Copyright (c) 1991 The Regents of the University of California. 353766Selan * All rights reserved. 453766Selan * 553766Selan * %sccs.include.redist.c% 653766Selan */ 753766Selan 853766Selan #ifndef lint 9*55928Sbostic static char sccsid[] = "@(#)utils.c 5.6 (Berkeley) 08/21/92"; 1053766Selan #endif /* not lint */ 1153766Selan 1253766Selan #include <sys/param.h> 1353766Selan #include <sys/stat.h> 1453766Selan #include <sys/mman.h> 1553766Selan #include <sys/time.h> 1653766Selan #include <fcntl.h> 1753766Selan #include <errno.h> 1853766Selan #include <unistd.h> 1953607Selan #include <stdio.h> 2053766Selan #include <stdlib.h> 2153607Selan #include <fts.h> 2253766Selan #include "extern.h" 2353607Selan 2453607Selan void 2553766Selan copy_file(entp, dne) 2653766Selan FTSENT *entp; 2753607Selan int dne; 2853607Selan { 2953607Selan static char buf[MAXBSIZE]; 3053607Selan register int from_fd, to_fd, rcount, wcount; 3153766Selan struct stat to_stat, *fs; 3253607Selan char *p; 3353766Selan 3453607Selan 3553766Selan if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 3653766Selan err("%s: %s", entp->fts_path, strerror(errno)); 3753607Selan return; 3853607Selan } 3953607Selan 4053766Selan fs = entp->fts_statp; 4153766Selan 4253607Selan /* 4353607Selan * If the file exists and we're interactive, verify with the user. 4453607Selan * If the file DNE, set the mode to be the from file, minus setuid 4553607Selan * bits, modified by the umask; arguably wrong, but it makes copying 4653607Selan * executables work right and it's been that way forever. (The 4753607Selan * other choice is 666 or'ed with the execute bits on the from file 4853607Selan * modified by the umask.) 4953607Selan */ 5053607Selan if (!dne) { 5153607Selan if (iflag) { 5253607Selan int checkch, ch; 5353607Selan 5453607Selan (void)fprintf(stderr, "overwrite %s? ", to.p_path); 5553607Selan checkch = ch = getchar(); 5653607Selan while (ch != '\n' && ch != EOF) 5753607Selan ch = getchar(); 5853607Selan if (checkch != 'y') { 5953607Selan (void)close(from_fd); 6053607Selan return; 6153607Selan } 6253607Selan } 6353607Selan to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0); 6453607Selan } else 6553607Selan to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC, 6653607Selan fs->st_mode & ~(S_ISUID|S_ISGID)); 6753607Selan 6853607Selan if (to_fd == -1) { 6953607Selan err("%s: %s", to.p_path, strerror(errno)); 7053607Selan (void)close(from_fd); 7153607Selan return; 7253607Selan } 7353607Selan 7453607Selan /* 7553607Selan * Mmap and write if less than 8M (the limit is so we don't totally 7653607Selan * trash memory on big files. This is really a minor hack, but it 7753607Selan * wins some CPU back. 7853607Selan */ 79*55928Sbostic #ifdef VM_AND_BUFFER_CACHE_FIXED 8053607Selan if (fs->st_size <= 8 * 1048576) { 8154191Sbostic if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 8254293Sbostic 0, from_fd, (off_t)0)) == (char *)-1) 8353766Selan err("%s: %s", entp->fts_path, strerror(errno)); 8453607Selan if (write(to_fd, p, fs->st_size) != fs->st_size) 8553607Selan err("%s: %s", to.p_path, strerror(errno)); 86*55928Sbostic } else 87*55928Sbostic #endif 88*55928Sbostic { 8953607Selan while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 9053607Selan wcount = write(to_fd, buf, rcount); 9153607Selan if (rcount != wcount || wcount == -1) { 9253607Selan err("%s: %s", to.p_path, strerror(errno)); 9353607Selan break; 9453607Selan } 9553607Selan } 9653607Selan if (rcount < 0) 9753766Selan err("%s: %s", entp->fts_path, strerror(errno)); 9853607Selan } 9953607Selan if (pflag) 10053607Selan setfile(fs, to_fd); 10153607Selan /* 10253607Selan * If the source was setuid or setgid, lose the bits unless the 10353607Selan * copy is owned by the same user and group. 10453607Selan */ 10553607Selan else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) 10653607Selan if (fstat(to_fd, &to_stat)) 10753607Selan err("%s: %s", to.p_path, strerror(errno)); 10853607Selan #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 10953607Selan else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, 11053607Selan fs->st_mode & RETAINBITS & ~myumask)) 11153607Selan err("%s: %s", to.p_path, strerror(errno)); 11253607Selan (void)close(from_fd); 11353607Selan if (close(to_fd)) 11453607Selan err("%s: %s", to.p_path, strerror(errno)); 11553607Selan } 11653607Selan 11753607Selan void 11853766Selan copy_link(p, exists) 11953766Selan FTSENT *p; 12053607Selan int exists; 12153607Selan { 12253607Selan int len; 12353607Selan char link[MAXPATHLEN]; 12453607Selan 12553766Selan if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 12653766Selan err("readlink: %s: %s", p->fts_path, strerror(errno)); 12753607Selan return; 12853607Selan } 12953607Selan link[len] = '\0'; 13053607Selan if (exists && unlink(to.p_path)) { 13153607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 13253607Selan return; 13353607Selan } 13453607Selan if (symlink(link, to.p_path)) { 13553607Selan err("symlink: %s: %s", link, strerror(errno)); 13653607Selan return; 13753607Selan } 13853607Selan } 13953607Selan 14053607Selan void 14153607Selan copy_fifo(from_stat, exists) 14253607Selan struct stat *from_stat; 14353607Selan int exists; 14453607Selan { 14553607Selan if (exists && unlink(to.p_path)) { 14653607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 14753607Selan return; 14853607Selan } 14953607Selan if (mkfifo(to.p_path, from_stat->st_mode)) { 15053607Selan err("mkfifo: %s: %s", to.p_path, strerror(errno)); 15153607Selan return; 15253607Selan } 15353607Selan if (pflag) 15453607Selan setfile(from_stat, 0); 15553607Selan } 15653607Selan 15753607Selan void 15853607Selan copy_special(from_stat, exists) 15953607Selan struct stat *from_stat; 16053607Selan int exists; 16153607Selan { 16253607Selan if (exists && unlink(to.p_path)) { 16353607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 16453607Selan return; 16553607Selan } 16653607Selan if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 16753607Selan err("mknod: %s: %s", to.p_path, strerror(errno)); 16853607Selan return; 16953607Selan } 17053607Selan if (pflag) 17153607Selan setfile(from_stat, 0); 17253607Selan } 17353607Selan 17453607Selan 17553607Selan void 17653607Selan setfile(fs, fd) 17753607Selan register struct stat *fs; 17853607Selan int fd; 17953607Selan { 18053607Selan static struct timeval tv[2]; 18153607Selan 18253607Selan fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 18353607Selan 18453607Selan tv[0].tv_sec = fs->st_atime; 18553607Selan tv[1].tv_sec = fs->st_mtime; 18653607Selan if (utimes(to.p_path, tv)) 18753607Selan err("utimes: %s: %s", to.p_path, strerror(errno)); 18853607Selan /* 18953607Selan * Changing the ownership probably won't succeed, unless we're root 19053607Selan * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 19153607Selan * the mode; current BSD behavior is to remove all setuid bits on 19253607Selan * chown. If chown fails, lose setuid/setgid bits. 19353607Selan */ 19453607Selan if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 19553607Selan chown(to.p_path, fs->st_uid, fs->st_gid)) { 19653607Selan if (errno != EPERM) 19753607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 19853607Selan fs->st_mode &= ~(S_ISUID|S_ISGID); 19953607Selan } 20053607Selan if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) 20153607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 20254540Sbostic 20354540Sbostic if (fd ? fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) 20454540Sbostic err("chflags: %s: %s", to.p_path, strerror(errno)); 20553607Selan } 20653607Selan 20753607Selan void 20853607Selan usage() 20953607Selan { 21053607Selan (void)fprintf(stderr, 21153766Selan "usage: cp [-HRfhip] src target;\n cp [-HRfhip] src1 ... srcN directory\n"); 21253607Selan exit(1); 21353607Selan } 21453607Selan 21553607Selan #if __STDC__ 21653607Selan #include <stdarg.h> 21753607Selan #else 21853607Selan #include <varargs.h> 21953607Selan #endif 22053607Selan 22153607Selan void 22253607Selan #if __STDC__ 22353607Selan err(const char *fmt, ...) 22453607Selan #else 22553607Selan err(fmt, va_alist) 22653607Selan char *fmt; 22753607Selan va_dcl 22853607Selan #endif 22953607Selan { 23053607Selan va_list ap; 23153607Selan #if __STDC__ 23253607Selan va_start(ap, fmt); 23353607Selan #else 23453607Selan va_start(ap); 23553607Selan #endif 23653607Selan (void)fprintf(stderr, "%s: ", progname); 23753607Selan (void)vfprintf(stderr, fmt, ap); 23853607Selan va_end(ap); 23953607Selan (void)fprintf(stderr, "\n"); 24053607Selan exit_val = 1; 24153607Selan } 242