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