VirtualBox

source: kBuild/trunk/src/gmake/kmkbuiltin/cp_utils.c@ 269

Last change on this file since 269 was 229, checked in by bird, 20 years ago

cp.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
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
32static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
33#endif
34#endif /* not lint */
35#include <sys/cdefs.h>
36//__FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.43 2004/04/06 20:06:44 markm Exp $");
37
38#include <sys/param.h>
39#include <sys/stat.h>
40#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
41#include <sys/mman.h>
42#endif
43
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <fts.h>
48#include <limits.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <sysexits.h>
52#include <unistd.h>
53#include <signal.h>
54
55#include "cp_extern.h"
56#define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y))
57
58#ifndef MAXBSIZE
59#define MAXBSIZE (128*1024)
60#endif
61
62int
63copy_file(const FTSENT *entp, int dne)
64{
65 static char buf[MAXBSIZE];
66 struct stat *fs;
67 int ch, checkch, from_fd, rcount, rval, to_fd;
68 ssize_t wcount;
69 size_t wresid;
70 size_t wtotal;
71 char *bufp;
72#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
73 char *p;
74#endif
75
76 if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
77 fprintf(stderr, "%s: %s: %s\n", argv0, entp->fts_path, strerror(errno));
78 return (1);
79 }
80
81 fs = entp->fts_statp;
82
83 /*
84 * If the file exists and we're interactive, verify with the user.
85 * If the file DNE, set the mode to be the from file, minus setuid
86 * bits, modified by the umask; arguably wrong, but it makes copying
87 * executables work right and it's been that way forever. (The
88 * other choice is 666 or'ed with the execute bits on the from file
89 * modified by the umask.)
90 */
91 if (!dne) {
92#define YESNO "(y/n [n]) "
93 if (nflag) {
94 if (vflag)
95 printf("%s not overwritten\n", to.p_path);
96 return (0);
97 } else if (iflag) {
98 (void)fprintf(stderr, "overwrite %s? %s",
99 to.p_path, YESNO);
100 checkch = ch = getchar();
101 while (ch != '\n' && ch != EOF)
102 ch = getchar();
103 if (checkch != 'y' && checkch != 'Y') {
104 (void)close(from_fd);
105 (void)fprintf(stderr, "not overwritten\n");
106 return (1);
107 }
108 }
109
110 if (fflag) {
111 /* remove existing destination file name,
112 * create a new file */
113 (void)unlink(to.p_path);
114 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
115 fs->st_mode & ~(S_ISUID | S_ISGID));
116 } else
117 /* overwrite existing destination file name */
118 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
119 } else
120 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
121 fs->st_mode & ~(S_ISUID | S_ISGID));
122
123 if (to_fd == -1) {
124 fprintf(stderr, "%s: %s: %s\n", argv0, to.p_path, strerror(errno));
125 (void)close(from_fd);
126 return (1);
127 }
128
129 rval = 0;
130
131 /*
132 * Mmap and write if less than 8M (the limit is so we don't totally
133 * trash memory on big files. This is really a minor hack, but it
134 * wins some CPU back.
135 */
136#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
137 if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
138 fs->st_size <= 8 * 1048576) {
139 if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
140 MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
141 fprintf(stderr, "%s: %s: %s\n", argv0, entp->fts_path, strerror(errno));
142 rval = 1;
143 } else {
144 wtotal = 0;
145 for (bufp = p, wresid = fs->st_size; ;
146 bufp += wcount, wresid -= (size_t)wcount) {
147 wcount = write(to_fd, bufp, wresid);
148 wtotal += wcount;
149 if (info) {
150 info = 0;
151 (void)fprintf(stderr,
152 "%s -> %s %3d%%\n",
153 entp->fts_path, to.p_path,
154 cp_pct(wtotal, fs->st_size));
155
156 }
157 if (wcount >= (ssize_t)wresid || wcount <= 0)
158 break;
159 }
160 if (wcount != (ssize_t)wresid) {
161 fprintf(stderr, "%s: %s: %s\n", argv0, to.p_path, strerror(errno));
162 rval = 1;
163 }
164 /* Some systems don't unmap on close(2). */
165 if (munmap(p, fs->st_size) < 0) {
166 fprintf(stderr, "%s: %s: %s\n", entp->fts_path, strerror(errno));
167 rval = 1;
168 }
169 }
170 } else
171#endif
172 {
173 wtotal = 0;
174 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
175 for (bufp = buf, wresid = rcount; ;
176 bufp += wcount, wresid -= wcount) {
177 wcount = write(to_fd, bufp, wresid);
178 wtotal += wcount;
179 if (info) {
180 info = 0;
181 (void)fprintf(stderr,
182 "%s -> %s %3d%%\n",
183 entp->fts_path, to.p_path,
184 cp_pct(wtotal, fs->st_size));
185
186 }
187 if (wcount >= (ssize_t)wresid || wcount <= 0)
188 break;
189 }
190 if (wcount != (ssize_t)wresid) {
191 fprintf(stderr, "%s: %s: %s\n", argv0, to.p_path, strerror(errno));
192 rval = 1;
193 break;
194 }
195 }
196 if (rcount < 0) {
197 fprintf(stderr, "%s: %s: %s\n", argv0, entp->fts_path, strerror(errno));
198 rval = 1;
199 }
200 }
201
202 /*
203 * Don't remove the target even after an error. The target might
204 * not be a regular file, or its attributes might be important,
205 * or its contents might be irreplaceable. It would only be safe
206 * to remove it if we created it and its length is 0.
207 */
208
209 if (pflag && setfile(fs, to_fd))
210 rval = 1;
211 (void)close(from_fd);
212 if (close(to_fd)) {
213 fprintf(stderr, "%s: %s: %s\n", argv0, to.p_path, strerror(errno));
214 rval = 1;
215 }
216 return (rval);
217}
218
219int
220copy_link(const FTSENT *p, int exists)
221{
222 int len;
223 char llink[PATH_MAX];
224
225 if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
226 fprintf(stderr, "%s: readlink: %s: %s\n", argv0, p->fts_path, strerror(errno));
227 return (1);
228 }
229 llink[len] = '\0';
230 if (exists && unlink(to.p_path)) {
231 fprintf(stderr, "%s: unlink: %s: %s\n", argv0, to.p_path, strerror(errno));
232 return (1);
233 }
234 if (symlink(llink, to.p_path)) {
235 fprintf(stderr, "%s: symlink: %s: %s\n", argv0, llink, strerror(errno));
236 return (1);
237 }
238 return (pflag ? setfile(p->fts_statp, -1) : 0);
239}
240
241int
242copy_fifo(struct stat *from_stat, int exists)
243{
244 if (exists && unlink(to.p_path)) {
245 fprintf(stderr, "%s: unlink: %s: %s\n", argv0, to.p_path, strerror(errno));
246 return (1);
247 }
248 if (mkfifo(to.p_path, from_stat->st_mode)) {
249 fprintf(stderr, "%s: mkfifo: %s\n", argv0, to.p_path, strerror(errno));
250 return (1);
251 }
252 return (pflag ? setfile(from_stat, -1) : 0);
253}
254
255int
256copy_special(struct stat *from_stat, int exists)
257{
258 if (exists && unlink(to.p_path)) {
259 fprintf(stderr, "%s: unlink: %s: %s\n", argv0, to.p_path, strerror(errno));
260 return (1);
261 }
262 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
263 fprintf(stderr, "%s: mknod: %s: %s\n", argv0, to.p_path, strerror(errno));
264 return (1);
265 }
266 return (pflag ? setfile(from_stat, -1) : 0);
267}
268
269int
270setfile(struct stat *fs, int fd)
271{
272 static struct timeval tv[2];
273 struct stat ts;
274 int rval, gotstat, islink, fdval;
275
276 rval = 0;
277 fdval = fd != -1;
278 islink = !fdval && S_ISLNK(fs->st_mode);
279 fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
280 S_IRWXU | S_IRWXG | S_IRWXO;
281
282#ifdef HAVE_ST_TIMESPEC
283 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
284 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
285#else
286 tv[0].tv_sec = fs->st_atime;
287 tv[1].tv_sec = fs->st_mtime;
288 tv[0].tv_usec = tv[1].tv_usec = 0;
289#endif
290 if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
291 fprintf(stderr, "%s: %sutimes: %s: %s\n", argv0, islink ? "l" : "", to.p_path, strerror(errno));
292 rval = 1;
293 }
294 if (fdval ? fstat(fd, &ts) :
295 (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
296 gotstat = 0;
297 else {
298 gotstat = 1;
299 ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
300 S_IRWXU | S_IRWXG | S_IRWXO;
301 }
302 /*
303 * Changing the ownership probably won't succeed, unless we're root
304 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
305 * the mode; current BSD behavior is to remove all setuid bits on
306 * chown. If chown fails, lose setuid/setgid bits.
307 */
308 if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
309 if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
310 (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
311 chown(to.p_path, fs->st_uid, fs->st_gid))) {
312 if (errno != EPERM) {
313 fprintf(stderr, "%s: chown: %s: %s\n", argv0, to.p_path, strerror(errno));
314 rval = 1;
315 }
316 fs->st_mode &= ~(S_ISUID | S_ISGID);
317 }
318
319 if (!gotstat || fs->st_mode != ts.st_mode)
320 if (fdval ? fchmod(fd, fs->st_mode) :
321 (islink ? lchmod(to.p_path, fs->st_mode) :
322 chmod(to.p_path, fs->st_mode))) {
323 fprintf(stderr, "%s: chmod: %s: %s\n", to.p_path, strerror(errno));
324 rval = 1;
325 }
326
327#ifdef HAVE_ST_FLAGS
328 if (!gotstat || fs->st_flags != ts.st_flags)
329 if (fdval ?
330 fchflags(fd, fs->st_flags) :
331 (islink ? (errno = ENOSYS) :
332 chflags(to.p_path, fs->st_flags))) {
333 fprintf(stderr, "%s: chflags: %s: %s", argv0, to.p_path, strerror(errno));
334 rval = 1;
335 }
336#endif
337
338 return (rval);
339}
340
341void
342usage(void)
343{
344
345 (void)fprintf(stderr, "%s\n%s\n",
346"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] src target",
347" cp [-R [-H | -L | -P]] [-f | -i | -n] [-pv] src1 ... srcN directory");
348 exit(EX_USAGE);
349}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette