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*54191Sbostic static char sccsid[] = "@(#)utils.c 5.3 (Berkeley) 06/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 */ 7953607Selan if (fs->st_size <= 8 * 1048576) { 80*54191Sbostic if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 8153607Selan MAP_FILE, from_fd, (off_t)0)) == (char *)-1) 8253766Selan err("%s: %s", entp->fts_path, strerror(errno)); 8353607Selan if (write(to_fd, p, fs->st_size) != fs->st_size) 8453607Selan err("%s: %s", to.p_path, strerror(errno)); 8553607Selan } else { 8653607Selan while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 8753607Selan wcount = write(to_fd, buf, rcount); 8853607Selan if (rcount != wcount || wcount == -1) { 8953607Selan err("%s: %s", to.p_path, strerror(errno)); 9053607Selan break; 9153607Selan } 9253607Selan } 9353607Selan if (rcount < 0) 9453766Selan err("%s: %s", entp->fts_path, strerror(errno)); 9553607Selan } 9653607Selan if (pflag) 9753607Selan setfile(fs, to_fd); 9853607Selan /* 9953607Selan * If the source was setuid or setgid, lose the bits unless the 10053607Selan * copy is owned by the same user and group. 10153607Selan */ 10253607Selan else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) 10353607Selan if (fstat(to_fd, &to_stat)) 10453607Selan err("%s: %s", to.p_path, strerror(errno)); 10553607Selan #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 10653607Selan else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, 10753607Selan fs->st_mode & RETAINBITS & ~myumask)) 10853607Selan err("%s: %s", to.p_path, strerror(errno)); 10953607Selan (void)close(from_fd); 11053607Selan if (close(to_fd)) 11153607Selan err("%s: %s", to.p_path, strerror(errno)); 11253607Selan } 11353607Selan 11453607Selan void 11553766Selan copy_link(p, exists) 11653766Selan FTSENT *p; 11753607Selan int exists; 11853607Selan { 11953607Selan int len; 12053607Selan char link[MAXPATHLEN]; 12153607Selan 12253766Selan if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 12353766Selan err("readlink: %s: %s", p->fts_path, strerror(errno)); 12453607Selan return; 12553607Selan } 12653607Selan link[len] = '\0'; 12753607Selan if (exists && unlink(to.p_path)) { 12853607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 12953607Selan return; 13053607Selan } 13153607Selan if (symlink(link, to.p_path)) { 13253607Selan err("symlink: %s: %s", link, strerror(errno)); 13353607Selan return; 13453607Selan } 13553607Selan } 13653607Selan 13753607Selan void 13853607Selan copy_fifo(from_stat, exists) 13953607Selan struct stat *from_stat; 14053607Selan int exists; 14153607Selan { 14253607Selan if (exists && unlink(to.p_path)) { 14353607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 14453607Selan return; 14553607Selan } 14653607Selan if (mkfifo(to.p_path, from_stat->st_mode)) { 14753607Selan err("mkfifo: %s: %s", to.p_path, strerror(errno)); 14853607Selan return; 14953607Selan } 15053607Selan if (pflag) 15153607Selan setfile(from_stat, 0); 15253607Selan } 15353607Selan 15453607Selan void 15553607Selan copy_special(from_stat, exists) 15653607Selan struct stat *from_stat; 15753607Selan int exists; 15853607Selan { 15953607Selan if (exists && unlink(to.p_path)) { 16053607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 16153607Selan return; 16253607Selan } 16353607Selan if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 16453607Selan err("mknod: %s: %s", to.p_path, strerror(errno)); 16553607Selan return; 16653607Selan } 16753607Selan if (pflag) 16853607Selan setfile(from_stat, 0); 16953607Selan } 17053607Selan 17153607Selan 17253607Selan void 17353607Selan setfile(fs, fd) 17453607Selan register struct stat *fs; 17553607Selan int fd; 17653607Selan { 17753607Selan static struct timeval tv[2]; 17853607Selan 17953607Selan fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 18053607Selan 18153607Selan tv[0].tv_sec = fs->st_atime; 18253607Selan tv[1].tv_sec = fs->st_mtime; 18353607Selan if (utimes(to.p_path, tv)) 18453607Selan err("utimes: %s: %s", to.p_path, strerror(errno)); 18553607Selan /* 18653607Selan * Changing the ownership probably won't succeed, unless we're root 18753607Selan * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 18853607Selan * the mode; current BSD behavior is to remove all setuid bits on 18953607Selan * chown. If chown fails, lose setuid/setgid bits. 19053607Selan */ 19153607Selan if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 19253607Selan chown(to.p_path, fs->st_uid, fs->st_gid)) { 19353607Selan if (errno != EPERM) 19453607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 19553607Selan fs->st_mode &= ~(S_ISUID|S_ISGID); 19653607Selan } 19753607Selan if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) 19853607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 19953607Selan } 20053607Selan 20153607Selan void 20253607Selan usage() 20353607Selan { 20453607Selan (void)fprintf(stderr, 20553766Selan "usage: cp [-HRfhip] src target;\n cp [-HRfhip] src1 ... srcN directory\n"); 20653607Selan exit(1); 20753607Selan } 20853607Selan 20953607Selan #if __STDC__ 21053607Selan #include <stdarg.h> 21153607Selan #else 21253607Selan #include <varargs.h> 21353607Selan #endif 21453607Selan 21553607Selan void 21653607Selan #if __STDC__ 21753607Selan err(const char *fmt, ...) 21853607Selan #else 21953607Selan err(fmt, va_alist) 22053607Selan char *fmt; 22153607Selan va_dcl 22253607Selan #endif 22353607Selan { 22453607Selan va_list ap; 22553607Selan #if __STDC__ 22653607Selan va_start(ap, fmt); 22753607Selan #else 22853607Selan va_start(ap); 22953607Selan #endif 23053607Selan (void)fprintf(stderr, "%s: ", progname); 23153607Selan (void)vfprintf(stderr, fmt, ap); 23253607Selan va_end(ap); 23353607Selan (void)fprintf(stderr, "\n"); 23453607Selan exit_val = 1; 23553607Selan } 236