• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* $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
35 static 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 
68 int
set_utimes(const char * file,struct stat * fs)69 set_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 
95 struct finfo {
96 	const char *from;
97 	const char *to;
98 	size_t size;
99 };
100 
101 static void
progress(const struct finfo * fi,size_t written)102 progress(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 
111 int
copy_file(FTSENT * entp,int dne)112 copy_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 
320 int
copy_link(FTSENT * p,int exists)321 copy_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 
342 int
copy_fifo(struct stat * from_stat,int exists)343 copy_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 
356 int
copy_special(struct stat * from_stat,int exists)357 copy_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  */
380 int
setfile(struct stat * fs,int fd)381 setfile(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 
438 void
439 cp_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 }
447