Home | History | Annotate | Download | only in cp
      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
     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
    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
    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
    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
    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
    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
    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