blob: b682bbeb0e3c2985f7eac5c045981b020c3af570 [file] [log] [blame]
Jeff Sharkey57df14c2012-07-13 16:25:33 -07001/* $NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
36#else
37__RCSID("$NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $");
38#endif
39#endif /* not lint */
40
41#include <sys/mman.h>
42#include <sys/param.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#ifndef ANDROID
46#include <sys/extattr.h>
47#endif
48
49#include <err.h>
50#include <errno.h>
51#include <fcntl.h>
52#include <fts.h>
53#include <stdbool.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59#include "extern.h"
60
61#ifdef ANDROID
62#define MAXBSIZE 65536
63#endif
64
65#define MMAP_MAX_SIZE (8 * 1048576)
66#define MMAP_MAX_WRITE (64 * 1024)
67
68int
69set_utimes(const char *file, struct stat *fs)
70{
71 static struct timeval tv[2];
72
73#ifdef ANDROID
74 tv[0].tv_sec = fs->st_atime;
75 tv[0].tv_usec = 0;
76 tv[1].tv_sec = fs->st_mtime;
77 tv[1].tv_usec = 0;
78
79 if (utimes(file, tv)) {
80 warn("utimes: %s", file);
81 return 1;
82 }
83#else
84 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
85 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
86
87 if (lutimes(file, tv)) {
88 warn("lutimes: %s", file);
89 return (1);
90 }
91#endif
92 return (0);
93}
94
95struct finfo {
96 const char *from;
97 const char *to;
98 size_t size;
99};
100
101static void
102progress(const struct finfo *fi, size_t written)
103{
104 int pcent = (int)((100.0 * written) / fi->size);
105
106 pinfo = 0;
107 (void)fprintf(stderr, "%s => %s %zu/%zu bytes %d%% written\n",
108 fi->from, fi->to, written, fi->size, pcent);
109}
110
111int
112copy_file(FTSENT *entp, int dne)
113{
114 static char buf[MAXBSIZE];
115 struct stat to_stat, *fs;
116 int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount;
117 char *p;
118 size_t ptotal = 0;
119
120 if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
121 warn("%s", entp->fts_path);
122 return (1);
123 }
124
125 to_fd = -1;
126 fs = entp->fts_statp;
127 tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag);
128
129 /*
130 * If the file exists and we're interactive, verify with the user.
131 * If the file DNE, set the mode to be the from file, minus setuid
132 * bits, modified by the umask; arguably wrong, but it makes copying
133 * executables work right and it's been that way forever. (The
134 * other choice is 666 or'ed with the execute bits on the from file
135 * modified by the umask.)
136 */
137 if (!dne) {
138 struct stat sb;
139 int sval;
140
141 if (iflag) {
142 (void)fprintf(stderr, "overwrite %s? ", to.p_path);
143 checkch = ch = getchar();
144 while (ch != '\n' && ch != EOF)
145 ch = getchar();
146 if (checkch != 'y' && checkch != 'Y') {
147 (void)close(from_fd);
148 return (0);
149 }
150 }
151
152 sval = tolnk ?
153 lstat(to.p_path, &sb) : stat(to.p_path, &sb);
154 if (sval == -1) {
155 warn("stat: %s", to.p_path);
156 (void)close(from_fd);
157 return (1);
158 }
159
160 if (!(tolnk && S_ISLNK(sb.st_mode)))
161 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
162 } else
163 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
164 fs->st_mode & ~(S_ISUID | S_ISGID));
165
166 if (to_fd == -1 && (fflag || tolnk)) {
167 /*
168 * attempt to remove existing destination file name and
169 * create a new file
170 */
171 (void)unlink(to.p_path);
172 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
173 fs->st_mode & ~(S_ISUID | S_ISGID));
174 }
175
176 if (to_fd == -1) {
177 warn("%s", to.p_path);
178 (void)close(from_fd);
179 return (1);
180 }
181
182 rval = 0;
183
184 /* if hard linking then simply close the open fds, link and return */
185 if (lflag) {
186 (void)close(from_fd);
187 (void)close(to_fd);
188 (void)unlink(to.p_path);
189 if (link(entp->fts_path, to.p_path)) {
190 warn("%s", to.p_path);
191 return (1);
192 }
193 return (0);
194 }
195 /* NOTREACHED */
196
197 /*
198 * There's no reason to do anything other than close the file
199 * now if it's empty, so let's not bother.
200 */
201 if (fs->st_size > 0) {
202 struct finfo fi;
203
204 fi.from = entp->fts_path;
205 fi.to = to.p_path;
206 fi.size = (size_t)fs->st_size;
207
208 /*
209 * Mmap and write if less than 8M (the limit is so
210 * we don't totally trash memory on big files).
211 * This is really a minor hack, but it wins some CPU back.
212 */
213 bool use_read;
214
215 use_read = true;
216 if (fs->st_size <= MMAP_MAX_SIZE) {
217 size_t fsize = (size_t)fs->st_size;
218 p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED,
219 from_fd, (off_t)0);
220 if (p != MAP_FAILED) {
221 size_t remainder;
222
223 use_read = false;
224
225 (void) madvise(p, (size_t)fs->st_size,
226 MADV_SEQUENTIAL);
227
228 /*
229 * Write out the data in small chunks to
230 * avoid locking the output file for a
231 * long time if the reading the data from
232 * the source is slow.
233 */
234 remainder = fsize;
235 do {
236 ssize_t chunk;
237
238 chunk = (remainder > MMAP_MAX_WRITE) ?
239 MMAP_MAX_WRITE : remainder;
240 if (write(to_fd, &p[fsize - remainder],
241 chunk) != chunk) {
242 warn("%s", to.p_path);
243 rval = 1;
244 break;
245 }
246 remainder -= chunk;
247 ptotal += chunk;
248 if (pinfo)
249 progress(&fi, ptotal);
250 } while (remainder > 0);
251
252 if (munmap(p, fsize) < 0) {
253 warn("%s", entp->fts_path);
254 rval = 1;
255 }
256 }
257 }
258
259 if (use_read) {
260 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
261 wcount = write(to_fd, buf, (size_t)rcount);
262 if (rcount != wcount || wcount == -1) {
263 warn("%s", to.p_path);
264 rval = 1;
265 break;
266 }
267 ptotal += wcount;
268 if (pinfo)
269 progress(&fi, ptotal);
270 }
271 if (rcount < 0) {
272 warn("%s", entp->fts_path);
273 rval = 1;
274 }
275 }
276 }
277
278#ifndef ANDROID
279 if (pflag && (fcpxattr(from_fd, to_fd) != 0))
280 warn("%s: error copying extended attributes", to.p_path);
281#endif
282
283 (void)close(from_fd);
284
285 if (rval == 1) {
286 (void)close(to_fd);
287 return (1);
288 }
289
290 if (pflag && setfile(fs, to_fd))
291 rval = 1;
292 /*
293 * If the source was setuid or setgid, lose the bits unless the
294 * copy is owned by the same user and group.
295 */
296#define RETAINBITS \
297 (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
298 if (!pflag && dne
299 && fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
300 if (fstat(to_fd, &to_stat)) {
301 warn("%s", to.p_path);
302 rval = 1;
303 } else if (fs->st_gid == to_stat.st_gid &&
304 fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
305 warn("%s", to.p_path);
306 rval = 1;
307 }
308 }
309 if (close(to_fd)) {
310 warn("%s", to.p_path);
311 rval = 1;
312 }
313 /* set the mod/access times now after close of the fd */
314 if (pflag && set_utimes(to.p_path, fs)) {
315 rval = 1;
316 }
317 return (rval);
318}
319
320int
321copy_link(FTSENT *p, int exists)
322{
323 int len;
324 char target[MAXPATHLEN];
325
326 if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) {
327 warn("readlink: %s", p->fts_path);
328 return (1);
329 }
330 target[len] = '\0';
331 if (exists && unlink(to.p_path)) {
332 warn("unlink: %s", to.p_path);
333 return (1);
334 }
335 if (symlink(target, to.p_path)) {
336 warn("symlink: %s", target);
337 return (1);
338 }
339 return (pflag ? setfile(p->fts_statp, 0) : 0);
340}
341
342int
343copy_fifo(struct stat *from_stat, int exists)
344{
345 if (exists && unlink(to.p_path)) {
346 warn("unlink: %s", to.p_path);
347 return (1);
348 }
349 if (mkfifo(to.p_path, from_stat->st_mode)) {
350 warn("mkfifo: %s", to.p_path);
351 return (1);
352 }
353 return (pflag ? setfile(from_stat, 0) : 0);
354}
355
356int
357copy_special(struct stat *from_stat, int exists)
358{
359 if (exists && unlink(to.p_path)) {
360 warn("unlink: %s", to.p_path);
361 return (1);
362 }
363 if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
364 warn("mknod: %s", to.p_path);
365 return (1);
366 }
367 return (pflag ? setfile(from_stat, 0) : 0);
368}
369
370
371/*
372 * Function: setfile
373 *
374 * Purpose:
375 * Set the owner/group/permissions for the "to" file to the information
376 * in the stat structure. If fd is zero, also call set_utimes() to set
377 * the mod/access times. If fd is non-zero, the caller must do a utimes
378 * itself after close(fd).
379 */
380int
381setfile(struct stat *fs, int fd)
382{
383 int rval, islink;
384
385 rval = 0;
386 islink = S_ISLNK(fs->st_mode);
387 fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
388
389 /*
390 * Changing the ownership probably won't succeed, unless we're root
391 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
392 * the mode; current BSD behavior is to remove all setuid bits on
393 * chown. If chown fails, lose setuid/setgid bits.
394 */
395 if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
396 lchown(to.p_path, fs->st_uid, fs->st_gid)) {
397 if (errno != EPERM) {
398 warn("chown: %s", to.p_path);
399 rval = 1;
400 }
401 fs->st_mode &= ~(S_ISUID | S_ISGID);
402 }
403#ifdef ANDROID
404 if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) {
405#else
406 if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) {
407#endif
408 warn("chmod: %s", to.p_path);
409 rval = 1;
410 }
411
412#ifndef ANDROID
413 if (!islink && !Nflag) {
414 unsigned long fflags = fs->st_flags;
415 /*
416 * XXX
417 * NFS doesn't support chflags; ignore errors unless
418 * there's reason to believe we're losing bits.
419 * (Note, this still won't be right if the server
420 * supports flags and we were trying to *remove* flags
421 * on a file that we copied, i.e., that we didn't create.)
422 */
423 errno = 0;
424 if ((fd ? fchflags(fd, fflags) :
425 chflags(to.p_path, fflags)) == -1)
426 if (errno != EOPNOTSUPP || fs->st_flags != 0) {
427 warn("chflags: %s", to.p_path);
428 rval = 1;
429 }
430 }
431#endif
432 /* if fd is non-zero, caller must call set_utimes() after close() */
433 if (fd == 0 && set_utimes(to.p_path, fs))
434 rval = 1;
435 return (rval);
436}
437
438void
439cp_usage(void)
440{
441 (void)fprintf(stderr,
442 "usage: cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n"
443 " cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n");
444 exit(1);
445 /* NOTREACHED */
446}