xref: /original-bsd/bin/cp/utils.c (revision c98fd05d)
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.6 (Berkeley) 08/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 #ifdef VM_AND_BUFFER_CACHE_FIXED
80 	if (fs->st_size <= 8 * 1048576) {
81 		if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
82 		    0, from_fd, (off_t)0)) == (char *)-1)
83 			err("%s: %s", entp->fts_path, strerror(errno));
84 		if (write(to_fd, p, fs->st_size) != fs->st_size)
85 			err("%s: %s", to.p_path, strerror(errno));
86 	} else
87 #endif
88 	{
89 		while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
90 			wcount = write(to_fd, buf, rcount);
91 			if (rcount != wcount || wcount == -1) {
92 				err("%s: %s", to.p_path, strerror(errno));
93 				break;
94 			}
95 		}
96 		if (rcount < 0)
97 			err("%s: %s", entp->fts_path, strerror(errno));
98 	}
99 	if (pflag)
100 		setfile(fs, to_fd);
101 	/*
102 	 * If the source was setuid or setgid, lose the bits unless the
103 	 * copy is owned by the same user and group.
104 	 */
105 	else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
106 		if (fstat(to_fd, &to_stat))
107 			err("%s: %s", to.p_path, strerror(errno));
108 #define	RETAINBITS	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
109 		else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
110 		    fs->st_mode & RETAINBITS & ~myumask))
111 			err("%s: %s", to.p_path, strerror(errno));
112 	(void)close(from_fd);
113 	if (close(to_fd))
114 		err("%s: %s", to.p_path, strerror(errno));
115 }
116 
117 void
118 copy_link(p, exists)
119 	FTSENT *p;
120 	int exists;
121 {
122 	int len;
123 	char link[MAXPATHLEN];
124 
125 	if ((len = readlink(p->fts_path, link, sizeof(link))) == -1) {
126 		err("readlink: %s: %s", p->fts_path, strerror(errno));
127 		return;
128 	}
129 	link[len] = '\0';
130 	if (exists && unlink(to.p_path)) {
131 		err("unlink: %s: %s", to.p_path, strerror(errno));
132 		return;
133 	}
134 	if (symlink(link, to.p_path)) {
135 		err("symlink: %s: %s", link, strerror(errno));
136 		return;
137 	}
138 }
139 
140 void
141 copy_fifo(from_stat, exists)
142 	struct stat *from_stat;
143 	int exists;
144 {
145 	if (exists && unlink(to.p_path)) {
146 		err("unlink: %s: %s", to.p_path, strerror(errno));
147 		return;
148 	}
149 	if (mkfifo(to.p_path, from_stat->st_mode)) {
150 		err("mkfifo: %s: %s", to.p_path, strerror(errno));
151 		return;
152 	}
153 	if (pflag)
154 		setfile(from_stat, 0);
155 }
156 
157 void
158 copy_special(from_stat, exists)
159 	struct stat *from_stat;
160 	int exists;
161 {
162 	if (exists && unlink(to.p_path)) {
163 		err("unlink: %s: %s", to.p_path, strerror(errno));
164 		return;
165 	}
166 	if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
167 		err("mknod: %s: %s", to.p_path, strerror(errno));
168 		return;
169 	}
170 	if (pflag)
171 		setfile(from_stat, 0);
172 }
173 
174 
175 void
176 setfile(fs, fd)
177 	register struct stat *fs;
178 	int fd;
179 {
180 	static struct timeval tv[2];
181 
182 	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
183 
184 	tv[0].tv_sec = fs->st_atime;
185 	tv[1].tv_sec = fs->st_mtime;
186 	if (utimes(to.p_path, tv))
187 		err("utimes: %s: %s", to.p_path, strerror(errno));
188 	/*
189 	 * Changing the ownership probably won't succeed, unless we're root
190 	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
191 	 * the mode; current BSD behavior is to remove all setuid bits on
192 	 * chown.  If chown fails, lose setuid/setgid bits.
193 	 */
194 	if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
195 	    chown(to.p_path, fs->st_uid, fs->st_gid)) {
196 		if (errno != EPERM)
197 			err("chown: %s: %s", to.p_path, strerror(errno));
198 		fs->st_mode &= ~(S_ISUID|S_ISGID);
199 	}
200 	if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
201 		err("chown: %s: %s", to.p_path, strerror(errno));
202 
203 	if (fd ? fchflags(fd, fs->st_flags) : chflags(to.p_path, fs->st_flags))
204 		err("chflags: %s: %s", to.p_path, strerror(errno));
205 }
206 
207 void
208 usage()
209 {
210 	(void)fprintf(stderr,
211 "usage: cp [-HRfhip] src target;\n       cp [-HRfhip] src1 ... srcN directory\n");
212 	exit(1);
213 }
214 
215 #if __STDC__
216 #include <stdarg.h>
217 #else
218 #include <varargs.h>
219 #endif
220 
221 void
222 #if __STDC__
223 err(const char *fmt, ...)
224 #else
225 err(fmt, va_alist)
226 	char *fmt;
227         va_dcl
228 #endif
229 {
230 	va_list ap;
231 #if __STDC__
232 	va_start(ap, fmt);
233 #else
234 	va_start(ap);
235 #endif
236 	(void)fprintf(stderr, "%s: ", progname);
237 	(void)vfprintf(stderr, fmt, ap);
238 	va_end(ap);
239 	(void)fprintf(stderr, "\n");
240 	exit_val = 1;
241 }
242