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