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