xref: /csrg-svn/bin/cp/utils.c (revision 60658)
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