xref: /original-bsd/bin/cp/utils.c (revision 94e7bb75)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)utils.c	5.3 (Berkeley) 06/21/92";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <sys/stat.h>
14 #include <sys/mman.h>
15 #include <sys/time.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <fts.h>
22 #include "extern.h"
23 
24 void
25 copy_file(entp, dne)
26 	FTSENT *entp;
27 	int dne;
28 {
29 	static char buf[MAXBSIZE];
30 	register int from_fd, to_fd, rcount, wcount;
31 	struct stat to_stat, *fs;
32 	char *p;
33 
34 
35 	if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
36 		err("%s: %s", entp->fts_path, strerror(errno));
37 		return;
38 	}
39 
40 	fs = entp->fts_statp;
41 
42 	/*
43 	 * If the file exists and we're interactive, verify with the user.
44 	 * If the file DNE, set the mode to be the from file, minus setuid
45 	 * bits, modified by the umask; arguably wrong, but it makes copying
46 	 * executables work right and it's been that way forever.  (The
47 	 * other choice is 666 or'ed with the execute bits on the from file
48 	 * modified by the umask.)
49 	 */
50 	if (!dne) {
51 		if (iflag) {
52 			int checkch, ch;
53 
54 			(void)fprintf(stderr, "overwrite %s? ", to.p_path);
55 			checkch = ch = getchar();
56 			while (ch != '\n' && ch != EOF)
57 				ch = getchar();
58 			if (checkch != 'y') {
59 				(void)close(from_fd);
60 				return;
61 			}
62 		}
63 		to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
64 	} else
65 		to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
66 		    fs->st_mode & ~(S_ISUID|S_ISGID));
67 
68 	if (to_fd == -1) {
69 		err("%s: %s", to.p_path, strerror(errno));
70 		(void)close(from_fd);
71 		return;
72 	}
73 
74 	/*
75 	 * Mmap and write if less than 8M (the limit is so we don't totally
76 	 * trash memory on big files.  This is really a minor hack, but it
77 	 * wins some CPU back.
78 	 */
79 	if (fs->st_size <= 8 * 1048576) {
80 		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
81 		    MAP_FILE, from_fd, (off_t)0)) == (char *)-1)
82 			err("%s: %s", entp->fts_path, strerror(errno));
83 		if (write(to_fd, p, fs->st_size) != fs->st_size)
84 			err("%s: %s", to.p_path, strerror(errno));
85 	} else {
86 		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
87 			wcount = write(to_fd, buf, rcount);
88 			if (rcount != wcount || wcount == -1) {
89 				err("%s: %s", to.p_path, strerror(errno));
90 				break;
91 			}
92 		}
93 		if (rcount < 0)
94 			err("%s: %s", entp->fts_path, strerror(errno));
95 	}
96 	if (pflag)
97 		setfile(fs, to_fd);
98 	/*
99 	 * If the source was setuid or setgid, lose the bits unless the
100 	 * copy is owned by the same user and group.
101 	 */
102 	else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
103 		if (fstat(to_fd, &to_stat))
104 			err("%s: %s", to.p_path, strerror(errno));
105 #define	RETAINBITS	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
106 		else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
107 		    fs->st_mode & RETAINBITS & ~myumask))
108 			err("%s: %s", to.p_path, strerror(errno));
109 	(void)close(from_fd);
110 	if (close(to_fd))
111 		err("%s: %s", to.p_path, strerror(errno));
112 }
113 
114 void
115 copy_link(p, exists)
116 	FTSENT *p;
117 	int exists;
118 {
119 	int len;
120 	char link[MAXPATHLEN];
121 
122 	if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
123 		err("readlink: %s: %s", p->fts_path, strerror(errno));
124 		return;
125 	}
126 	link[len] = '\0';
127 	if (exists && unlink(to.p_path)) {
128 		err("unlink: %s: %s", to.p_path, strerror(errno));
129 		return;
130 	}
131 	if (symlink(link, to.p_path)) {
132 		err("symlink: %s: %s", link, strerror(errno));
133 		return;
134 	}
135 }
136 
137 void
138 copy_fifo(from_stat, exists)
139 	struct stat *from_stat;
140 	int exists;
141 {
142 	if (exists && unlink(to.p_path)) {
143 		err("unlink: %s: %s", to.p_path, strerror(errno));
144 		return;
145 	}
146 	if (mkfifo(to.p_path, from_stat->st_mode)) {
147 		err("mkfifo: %s: %s", to.p_path, strerror(errno));
148 		return;
149 	}
150 	if (pflag)
151 		setfile(from_stat, 0);
152 }
153 
154 void
155 copy_special(from_stat, exists)
156 	struct stat *from_stat;
157 	int exists;
158 {
159 	if (exists && unlink(to.p_path)) {
160 		err("unlink: %s: %s", to.p_path, strerror(errno));
161 		return;
162 	}
163 	if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
164 		err("mknod: %s: %s", to.p_path, strerror(errno));
165 		return;
166 	}
167 	if (pflag)
168 		setfile(from_stat, 0);
169 }
170 
171 
172 void
173 setfile(fs, fd)
174 	register struct stat *fs;
175 	int fd;
176 {
177 	static struct timeval tv[2];
178 
179 	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
180 
181 	tv[0].tv_sec = fs->st_atime;
182 	tv[1].tv_sec = fs->st_mtime;
183 	if (utimes(to.p_path, tv))
184 		err("utimes: %s: %s", to.p_path, strerror(errno));
185 	/*
186 	 * Changing the ownership probably won't succeed, unless we're root
187 	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
188 	 * the mode; current BSD behavior is to remove all setuid bits on
189 	 * chown.  If chown fails, lose setuid/setgid bits.
190 	 */
191 	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
192 	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
193 		if (errno != EPERM)
194 			err("chown: %s: %s", to.p_path, strerror(errno));
195 		fs->st_mode &= ~(S_ISUID|S_ISGID);
196 	}
197 	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
198 		err("chown: %s: %s", to.p_path, strerror(errno));
199 }
200 
201 void
202 usage()
203 {
204 	(void)fprintf(stderr,
205 "usage: cp [-HRfhip] src target;\n       cp [-HRfhip] src1 ... srcN directory\n");
206 	exit(1);
207 }
208 
209 #if __STDC__
210 #include <stdarg.h>
211 #else
212 #include <varargs.h>
213 #endif
214 
215 void
216 #if __STDC__
217 err(const char *fmt, ...)
218 #else
219 err(fmt, va_alist)
220 	char *fmt;
221         va_dcl
222 #endif
223 {
224 	va_list ap;
225 #if __STDC__
226 	va_start(ap, fmt);
227 #else
228 	va_start(ap);
229 #endif
230 	(void)fprintf(stderr, "%s: ", progname);
231 	(void)vfprintf(stderr, fmt, ap);
232 	va_end(ap);
233 	(void)fprintf(stderr, "\n");
234 	exit_val = 1;
235 }
236