153766Selan /*- 2*60658Sbostic * Copyright (c) 1991, 1993 3*60658Sbostic * The Regents of the University of California. All rights reserved. 453766Selan * 553766Selan * %sccs.include.redist.c% 653766Selan */ 753766Selan 853766Selan #ifndef lint 9*60658Sbostic static char sccsid[] = "@(#)utils.c 8.1 (Berkeley) 05/31/93"; 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> 1656975Sbostic 1756975Sbostic #include <errno.h> 1853766Selan #include <fcntl.h> 1956975Sbostic #include <fts.h> 2053607Selan #include <stdio.h> 2153766Selan #include <stdlib.h> 2259456Sbostic #include <string.h> 2356975Sbostic #include <unistd.h> 2456975Sbostic 2553766Selan #include "extern.h" 2653607Selan 2753607Selan void 2853766Selan copy_file(entp, dne) 2953766Selan FTSENT *entp; 3053607Selan int dne; 3153607Selan { 3253607Selan static char buf[MAXBSIZE]; 3353607Selan register int from_fd, to_fd, rcount, wcount; 3453766Selan struct stat to_stat, *fs; 3553607Selan char *p; 3653766Selan 3753607Selan 3853766Selan if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { 3953766Selan err("%s: %s", entp->fts_path, strerror(errno)); 4053607Selan return; 4153607Selan } 4253607Selan 4353766Selan fs = entp->fts_statp; 4453766Selan 4553607Selan /* 4653607Selan * If the file exists and we're interactive, verify with the user. 4753607Selan * If the file DNE, set the mode to be the from file, minus setuid 4853607Selan * bits, modified by the umask; arguably wrong, but it makes copying 4953607Selan * executables work right and it's been that way forever. (The 5053607Selan * other choice is 666 or'ed with the execute bits on the from file 5153607Selan * modified by the umask.) 5253607Selan */ 5353607Selan if (!dne) { 5453607Selan if (iflag) { 5553607Selan int checkch, ch; 5653607Selan 5753607Selan (void)fprintf(stderr, "overwrite %s? ", to.p_path); 5853607Selan checkch = ch = getchar(); 5953607Selan while (ch != '\n' && ch != EOF) 6053607Selan ch = getchar(); 6153607Selan if (checkch != 'y') { 6253607Selan (void)close(from_fd); 6353607Selan return; 6453607Selan } 6553607Selan } 6653607Selan to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0); 6753607Selan } else 6853607Selan to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC, 6953607Selan fs->st_mode & ~(S_ISUID|S_ISGID)); 7053607Selan 7153607Selan if (to_fd == -1) { 7253607Selan err("%s: %s", to.p_path, strerror(errno)); 7353607Selan (void)close(from_fd); 7453607Selan return; 7553607Selan } 7653607Selan 7753607Selan /* 7853607Selan * Mmap and write if less than 8M (the limit is so we don't totally 7953607Selan * trash memory on big files. This is really a minor hack, but it 8053607Selan * wins some CPU back. 8153607Selan */ 8255928Sbostic #ifdef VM_AND_BUFFER_CACHE_FIXED 8353607Selan if (fs->st_size <= 8 * 1048576) { 8454191Sbostic if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, 8554293Sbostic 0, from_fd, (off_t)0)) == (char *)-1) 8653766Selan err("%s: %s", entp->fts_path, strerror(errno)); 8753607Selan if (write(to_fd, p, fs->st_size) != fs->st_size) 8853607Selan err("%s: %s", to.p_path, strerror(errno)); 8955928Sbostic } else 9055928Sbostic #endif 9155928Sbostic { 9253607Selan while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 9353607Selan wcount = write(to_fd, buf, rcount); 9453607Selan if (rcount != wcount || wcount == -1) { 9553607Selan err("%s: %s", to.p_path, strerror(errno)); 9653607Selan break; 9753607Selan } 9853607Selan } 9953607Selan if (rcount < 0) 10053766Selan err("%s: %s", entp->fts_path, strerror(errno)); 10153607Selan } 10253607Selan if (pflag) 10353607Selan setfile(fs, to_fd); 10453607Selan /* 10553607Selan * If the source was setuid or setgid, lose the bits unless the 10653607Selan * copy is owned by the same user and group. 10753607Selan */ 10853607Selan else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) 10953607Selan if (fstat(to_fd, &to_stat)) 11053607Selan err("%s: %s", to.p_path, strerror(errno)); 11153607Selan #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 11253607Selan else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, 11353607Selan fs->st_mode & RETAINBITS & ~myumask)) 11453607Selan err("%s: %s", to.p_path, strerror(errno)); 11553607Selan (void)close(from_fd); 11653607Selan if (close(to_fd)) 11753607Selan err("%s: %s", to.p_path, strerror(errno)); 11853607Selan } 11953607Selan 12053607Selan void 12153766Selan copy_link(p, exists) 12253766Selan FTSENT *p; 12353607Selan int exists; 12453607Selan { 12553607Selan int len; 12653607Selan char link[MAXPATHLEN]; 12753607Selan 12853766Selan if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) { 12953766Selan err("readlink: %s: %s", p->fts_path, strerror(errno)); 13053607Selan return; 13153607Selan } 13253607Selan link[len] = '\0'; 13353607Selan if (exists && unlink(to.p_path)) { 13453607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 13553607Selan return; 13653607Selan } 13753607Selan if (symlink(link, to.p_path)) { 13853607Selan err("symlink: %s: %s", link, strerror(errno)); 13953607Selan return; 14053607Selan } 14153607Selan } 14253607Selan 14353607Selan void 14453607Selan copy_fifo(from_stat, exists) 14553607Selan struct stat *from_stat; 14653607Selan int exists; 14753607Selan { 14853607Selan if (exists && unlink(to.p_path)) { 14953607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 15053607Selan return; 15153607Selan } 15253607Selan if (mkfifo(to.p_path, from_stat->st_mode)) { 15353607Selan err("mkfifo: %s: %s", to.p_path, strerror(errno)); 15453607Selan return; 15553607Selan } 15653607Selan if (pflag) 15753607Selan setfile(from_stat, 0); 15853607Selan } 15953607Selan 16053607Selan void 16153607Selan copy_special(from_stat, exists) 16253607Selan struct stat *from_stat; 16353607Selan int exists; 16453607Selan { 16553607Selan if (exists && unlink(to.p_path)) { 16653607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 16753607Selan return; 16853607Selan } 16953607Selan if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 17053607Selan err("mknod: %s: %s", to.p_path, strerror(errno)); 17153607Selan return; 17253607Selan } 17353607Selan if (pflag) 17453607Selan setfile(from_stat, 0); 17553607Selan } 17653607Selan 17753607Selan 17853607Selan void 17953607Selan setfile(fs, fd) 18053607Selan register struct stat *fs; 18153607Selan int fd; 18253607Selan { 18353607Selan static struct timeval tv[2]; 18453607Selan 18553607Selan fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 18653607Selan 18756975Sbostic TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 18856975Sbostic TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 18953607Selan if (utimes(to.p_path, tv)) 19053607Selan err("utimes: %s: %s", to.p_path, strerror(errno)); 19153607Selan /* 19253607Selan * Changing the ownership probably won't succeed, unless we're root 19353607Selan * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 19453607Selan * the mode; current BSD behavior is to remove all setuid bits on 19553607Selan * chown. If chown fails, lose setuid/setgid bits. 19653607Selan */ 19753607Selan if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 19853607Selan chown(to.p_path, fs->st_uid, fs->st_gid)) { 19953607Selan if (errno != EPERM) 20053607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 20153607Selan fs->st_mode &= ~(S_ISUID|S_ISGID); 20253607Selan } 20353607Selan if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) 20453607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 20554540Sbostic 20654540Sbostic if (fd ? fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags)) 20754540Sbostic err("chflags: %s: %s", to.p_path, strerror(errno)); 20853607Selan } 20953607Selan 21053607Selan void 21153607Selan usage() 21253607Selan { 21353607Selan (void)fprintf(stderr, 21453766Selan "usage: cp [-HRfhip] src target;\n cp [-HRfhip] src1 ... srcN directory\n"); 21553607Selan exit(1); 21653607Selan } 21753607Selan 21853607Selan #if __STDC__ 21953607Selan #include <stdarg.h> 22053607Selan #else 22153607Selan #include <varargs.h> 22253607Selan #endif 22353607Selan 22453607Selan void 22553607Selan #if __STDC__ 22653607Selan err(const char *fmt, ...) 22753607Selan #else 22853607Selan err(fmt, va_alist) 22953607Selan char *fmt; 23053607Selan va_dcl 23153607Selan #endif 23253607Selan { 23353607Selan va_list ap; 23453607Selan #if __STDC__ 23553607Selan va_start(ap, fmt); 23653607Selan #else 23753607Selan va_start(ap); 23853607Selan #endif 23953607Selan (void)fprintf(stderr, "%s: ", progname); 24053607Selan (void)vfprintf(stderr, fmt, ap); 24153607Selan va_end(ap); 24253607Selan (void)fprintf(stderr, "\n"); 24353607Selan exit_val = 1; 24453607Selan } 245