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