1 | /*-
|
---|
2 | * Copyright (c) 1991, 1993, 1994
|
---|
3 | * The Regents of the University of California. All rights reserved.
|
---|
4 | *
|
---|
5 | * Redistribution and use in source and binary forms, with or without
|
---|
6 | * modification, are permitted provided that the following conditions
|
---|
7 | * are met:
|
---|
8 | * 1. Redistributions of source code must retain the above copyright
|
---|
9 | * notice, this list of conditions and the following disclaimer.
|
---|
10 | * 2. Redistributions in binary form must reproduce the above copyright
|
---|
11 | * notice, this list of conditions and the following disclaimer in the
|
---|
12 | * documentation and/or other materials provided with the distribution.
|
---|
13 | * 4. Neither the name of the University nor the names of its contributors
|
---|
14 | * may be used to endorse or promote products derived from this software
|
---|
15 | * without specific prior written permission.
|
---|
16 | *
|
---|
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
---|
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
---|
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
---|
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
---|
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
---|
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
---|
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
---|
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
---|
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
---|
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
---|
27 | * SUCH DAMAGE.
|
---|
28 | */
|
---|
29 |
|
---|
30 | #ifndef lint
|
---|
31 | #if 0
|
---|
32 | static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
|
---|
33 | #include <sys/cdefs.h>
|
---|
34 | __FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.43 2004/04/06 20:06:44 markm Exp $");
|
---|
35 | #endif
|
---|
36 | #endif /* not lint */
|
---|
37 |
|
---|
38 | #ifndef _MSC_VER
|
---|
39 | #include <sys/param.h>
|
---|
40 | #endif
|
---|
41 | #include <sys/stat.h>
|
---|
42 | #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
|
---|
43 | #include <sys/mman.h>
|
---|
44 | #endif
|
---|
45 |
|
---|
46 | #include "err.h"
|
---|
47 | #include <errno.h>
|
---|
48 | #include <fcntl.h>
|
---|
49 | #ifndef _MSC_VER
|
---|
50 | #include <fts.h>
|
---|
51 | #endif
|
---|
52 | #include <limits.h>
|
---|
53 | #include <stdio.h>
|
---|
54 | #include <stdlib.h>
|
---|
55 | #include <signal.h>
|
---|
56 | #ifndef _MSC_VER
|
---|
57 | #include <sysexits.h>
|
---|
58 | #include <unistd.h>
|
---|
59 | #else
|
---|
60 | #include "mscfakes.h"
|
---|
61 | #include "ftsfake.h"
|
---|
62 | #endif
|
---|
63 |
|
---|
64 | #include "cp_extern.h"
|
---|
65 | #define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y))
|
---|
66 |
|
---|
67 | #ifndef MAXBSIZE
|
---|
68 | # define MAXBSIZE 16384
|
---|
69 | #endif
|
---|
70 | #ifndef O_BINARY
|
---|
71 | # define O_BINARY 0
|
---|
72 | #endif
|
---|
73 |
|
---|
74 | #ifndef S_ISVTX
|
---|
75 | # define S_ISVTX 0
|
---|
76 | #endif
|
---|
77 |
|
---|
78 | int
|
---|
79 | copy_file(const FTSENT *entp, int dne)
|
---|
80 | {
|
---|
81 | static char buf[MAXBSIZE];
|
---|
82 | struct stat *fs;
|
---|
83 | int ch, checkch, from_fd, rcount, rval, to_fd;
|
---|
84 | ssize_t wcount;
|
---|
85 | size_t wresid;
|
---|
86 | size_t wtotal;
|
---|
87 | char *bufp;
|
---|
88 | #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
|
---|
89 | char *p;
|
---|
90 | #endif
|
---|
91 |
|
---|
92 | if ((from_fd = open(entp->fts_path, O_RDONLY | O_BINARY, 0)) == -1) {
|
---|
93 | warn("%s", entp->fts_path);
|
---|
94 | return (1);
|
---|
95 | }
|
---|
96 |
|
---|
97 | fs = entp->fts_statp;
|
---|
98 |
|
---|
99 | /*
|
---|
100 | * If the file exists and we're interactive, verify with the user.
|
---|
101 | * If the file DNE, set the mode to be the from file, minus setuid
|
---|
102 | * bits, modified by the umask; arguably wrong, but it makes copying
|
---|
103 | * executables work right and it's been that way forever. (The
|
---|
104 | * other choice is 666 or'ed with the execute bits on the from file
|
---|
105 | * modified by the umask.)
|
---|
106 | */
|
---|
107 | if (!dne) {
|
---|
108 | #define YESNO "(y/n [n]) "
|
---|
109 | if (nflag) {
|
---|
110 | if (vflag)
|
---|
111 | printf("%s not overwritten\n", to.p_path);
|
---|
112 | return (0);
|
---|
113 | } else if (iflag) {
|
---|
114 | (void)fprintf(stderr, "overwrite %s? %s",
|
---|
115 | to.p_path, YESNO);
|
---|
116 | checkch = ch = getchar();
|
---|
117 | while (ch != '\n' && ch != EOF)
|
---|
118 | ch = getchar();
|
---|
119 | if (checkch != 'y' && checkch != 'Y') {
|
---|
120 | (void)close(from_fd);
|
---|
121 | (void)fprintf(stderr, "not overwritten\n");
|
---|
122 | return (1);
|
---|
123 | }
|
---|
124 | }
|
---|
125 |
|
---|
126 | if (fflag) {
|
---|
127 | /* remove existing destination file name,
|
---|
128 | * create a new file */
|
---|
129 | (void)unlink(to.p_path);
|
---|
130 | to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY,
|
---|
131 | fs->st_mode & ~(S_ISUID | S_ISGID));
|
---|
132 | } else
|
---|
133 | /* overwrite existing destination file name */
|
---|
134 | to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_BINARY, 0);
|
---|
135 | } else
|
---|
136 | to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY,
|
---|
137 | fs->st_mode & ~(S_ISUID | S_ISGID));
|
---|
138 |
|
---|
139 | if (to_fd == -1) {
|
---|
140 | warn("%s", to.p_path);
|
---|
141 | (void)close(from_fd);
|
---|
142 | return (1);
|
---|
143 | }
|
---|
144 |
|
---|
145 | rval = 0;
|
---|
146 |
|
---|
147 | /*
|
---|
148 | * Mmap and write if less than 8M (the limit is so we don't totally
|
---|
149 | * trash memory on big files. This is really a minor hack, but it
|
---|
150 | * wins some CPU back.
|
---|
151 | */
|
---|
152 | #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
|
---|
153 | if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
|
---|
154 | fs->st_size <= 8 * 1048576) {
|
---|
155 | if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
|
---|
156 | MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
|
---|
157 | warn("%s", entp->fts_path);
|
---|
158 | rval = 1;
|
---|
159 | } else {
|
---|
160 | wtotal = 0;
|
---|
161 | for (bufp = p, wresid = fs->st_size; ;
|
---|
162 | bufp += wcount, wresid -= (size_t)wcount) {
|
---|
163 | wcount = write(to_fd, bufp, wresid);
|
---|
164 | wtotal += wcount;
|
---|
165 | if (info) {
|
---|
166 | info = 0;
|
---|
167 | (void)fprintf(stderr,
|
---|
168 | "%s -> %s %3d%%\n",
|
---|
169 | entp->fts_path, to.p_path,
|
---|
170 | cp_pct(wtotal, fs->st_size));
|
---|
171 |
|
---|
172 | }
|
---|
173 | if (wcount >= (ssize_t)wresid || wcount <= 0)
|
---|
174 | break;
|
---|
175 | }
|
---|
176 | if (wcount != (ssize_t)wresid) {
|
---|
177 | warn("%s", to.p_path);
|
---|
178 | rval = 1;
|
---|
179 | }
|
---|
180 | /* Some systems don't unmap on close(2). */
|
---|
181 | if (munmap(p, fs->st_size) < 0) {
|
---|
182 | warn("%s", entp->fts_path);
|
---|
183 | rval = 1;
|
---|
184 | }
|
---|
185 | }
|
---|
186 | } else
|
---|
187 | #endif
|
---|
188 | {
|
---|
189 | wtotal = 0;
|
---|
190 | while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
|
---|
191 | for (bufp = buf, wresid = rcount; ;
|
---|
192 | bufp += wcount, wresid -= wcount) {
|
---|
193 | wcount = write(to_fd, bufp, wresid);
|
---|
194 | wtotal += wcount;
|
---|
195 | if (info) {
|
---|
196 | info = 0;
|
---|
197 | (void)fprintf(stderr,
|
---|
198 | "%s -> %s %3d%%\n",
|
---|
199 | entp->fts_path, to.p_path,
|
---|
200 | cp_pct(wtotal, fs->st_size));
|
---|
201 |
|
---|
202 | }
|
---|
203 | if (wcount >= (ssize_t)wresid || wcount <= 0)
|
---|
204 | break;
|
---|
205 | }
|
---|
206 | if (wcount != (ssize_t)wresid) {
|
---|
207 | warn("%s", to.p_path);
|
---|
208 | rval = 1;
|
---|
209 | break;
|
---|
210 | }
|
---|
211 | }
|
---|
212 | if (rcount < 0) {
|
---|
213 | warn("%s", entp->fts_path);
|
---|
214 | rval = 1;
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | /*
|
---|
219 | * Don't remove the target even after an error. The target might
|
---|
220 | * not be a regular file, or its attributes might be important,
|
---|
221 | * or its contents might be irreplaceable. It would only be safe
|
---|
222 | * to remove it if we created it and its length is 0.
|
---|
223 | */
|
---|
224 |
|
---|
225 | if (pflag && setfile(fs, to_fd))
|
---|
226 | rval = 1;
|
---|
227 | (void)close(from_fd);
|
---|
228 | if (close(to_fd)) {
|
---|
229 | warn("%s", to.p_path);
|
---|
230 | rval = 1;
|
---|
231 | }
|
---|
232 | return (rval);
|
---|
233 | }
|
---|
234 |
|
---|
235 | int
|
---|
236 | copy_link(const FTSENT *p, int exists)
|
---|
237 | {
|
---|
238 | int len;
|
---|
239 | char llink[PATH_MAX];
|
---|
240 |
|
---|
241 | if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
|
---|
242 | warn("readlink: %s", p->fts_path);
|
---|
243 | return (1);
|
---|
244 | }
|
---|
245 | llink[len] = '\0';
|
---|
246 | if (exists && unlink(to.p_path)) {
|
---|
247 | warn("unlink: %s", to.p_path);
|
---|
248 | return (1);
|
---|
249 | }
|
---|
250 | if (symlink(llink, to.p_path)) {
|
---|
251 | warn("symlink: %s", llink);
|
---|
252 | return (1);
|
---|
253 | }
|
---|
254 | return (pflag ? setfile(p->fts_statp, -1) : 0);
|
---|
255 | }
|
---|
256 |
|
---|
257 | int
|
---|
258 | copy_fifo(struct stat *from_stat, int exists)
|
---|
259 | {
|
---|
260 | if (exists && unlink(to.p_path)) {
|
---|
261 | warn("unlink: %s", to.p_path);
|
---|
262 | return (1);
|
---|
263 | }
|
---|
264 | if (mkfifo(to.p_path, from_stat->st_mode)) {
|
---|
265 | warn("mkfifo: %s", to.p_path);
|
---|
266 | return (1);
|
---|
267 | }
|
---|
268 | return (pflag ? setfile(from_stat, -1) : 0);
|
---|
269 | }
|
---|
270 |
|
---|
271 | int
|
---|
272 | copy_special(struct stat *from_stat, int exists)
|
---|
273 | {
|
---|
274 | if (exists && unlink(to.p_path)) {
|
---|
275 | warn("unlink: %s", to.p_path);
|
---|
276 | return (1);
|
---|
277 | }
|
---|
278 | if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
|
---|
279 | warn("mknod: %s", to.p_path);
|
---|
280 | return (1);
|
---|
281 | }
|
---|
282 | return (pflag ? setfile(from_stat, -1) : 0);
|
---|
283 | }
|
---|
284 |
|
---|
285 | int
|
---|
286 | setfile(struct stat *fs, int fd)
|
---|
287 | {
|
---|
288 | static struct timeval tv[2];
|
---|
289 | struct stat ts;
|
---|
290 | int rval, gotstat, islink, fdval;
|
---|
291 |
|
---|
292 | rval = 0;
|
---|
293 | fdval = fd != -1;
|
---|
294 | islink = !fdval && S_ISLNK(fs->st_mode);
|
---|
295 | fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
|
---|
296 | S_IRWXU | S_IRWXG | S_IRWXO;
|
---|
297 |
|
---|
298 | #ifdef HAVE_ST_TIMESPEC
|
---|
299 | TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
|
---|
300 | TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
|
---|
301 | #else
|
---|
302 | tv[0].tv_sec = fs->st_atime;
|
---|
303 | tv[1].tv_sec = fs->st_mtime;
|
---|
304 | tv[0].tv_usec = tv[1].tv_usec = 0;
|
---|
305 | #endif
|
---|
306 | if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
|
---|
307 | warn("%sutimes: %s", islink ? "l" : "", to.p_path);
|
---|
308 | rval = 1;
|
---|
309 | }
|
---|
310 | if (fdval ? fstat(fd, &ts) :
|
---|
311 | (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
|
---|
312 | gotstat = 0;
|
---|
313 | else {
|
---|
314 | gotstat = 1;
|
---|
315 | ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
|
---|
316 | S_IRWXU | S_IRWXG | S_IRWXO;
|
---|
317 | }
|
---|
318 | /*
|
---|
319 | * Changing the ownership probably won't succeed, unless we're root
|
---|
320 | * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
|
---|
321 | * the mode; current BSD behavior is to remove all setuid bits on
|
---|
322 | * chown. If chown fails, lose setuid/setgid bits.
|
---|
323 | */
|
---|
324 | if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
|
---|
325 | if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
|
---|
326 | (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
|
---|
327 | chown(to.p_path, fs->st_uid, fs->st_gid))) {
|
---|
328 | if (errno != EPERM) {
|
---|
329 | warn("chown: %s", to.p_path);
|
---|
330 | rval = 1;
|
---|
331 | }
|
---|
332 | fs->st_mode &= ~(S_ISUID | S_ISGID);
|
---|
333 | }
|
---|
334 |
|
---|
335 | if (!gotstat || fs->st_mode != ts.st_mode)
|
---|
336 | if (fdval ? fchmod(fd, fs->st_mode) :
|
---|
337 | (islink ? lchmod(to.p_path, fs->st_mode) :
|
---|
338 | chmod(to.p_path, fs->st_mode))) {
|
---|
339 | warn("chmod: %s", to.p_path);
|
---|
340 | rval = 1;
|
---|
341 | }
|
---|
342 |
|
---|
343 | #ifdef HAVE_ST_FLAGS
|
---|
344 | if (!gotstat || fs->st_flags != ts.st_flags)
|
---|
345 | if (fdval ?
|
---|
346 | fchflags(fd, fs->st_flags) :
|
---|
347 | (islink ? (errno = ENOSYS) :
|
---|
348 | chflags(to.p_path, fs->st_flags))) {
|
---|
349 | warn("chflags: %s", to.p_path);
|
---|
350 | rval = 1;
|
---|
351 | }
|
---|
352 | #endif
|
---|
353 |
|
---|
354 | return (rval);
|
---|
355 | }
|
---|
356 |
|
---|
357 | int
|
---|
358 | usage(void)
|
---|
359 | {
|
---|
360 |
|
---|
361 | (void)fprintf(stderr, "%s\n%s\n",
|
---|
362 | "usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] src target",
|
---|
363 | " cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] src1 ... srcN directory");
|
---|
364 | return EX_USAGE;
|
---|
365 | }
|
---|