* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
static char buf
[MAXBSIZE
];
struct stat to_stat
, *fs
;
int ch
, checkch
, from_fd
, rcount
, rval
, to_fd
, wcount
;
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
if ((from_fd
= open(entp
->fts_path
, O_RDONLY
, 0)) == -1) {
warn("%s", entp
->fts_path
);
* If the file exists and we're interactive, verify with the user.
* If the file DNE, set the mode to be the from file, minus setuid
* bits, modified by the umask; arguably wrong, but it makes copying
* executables work right and it's been that way forever. (The
* other choice is 666 or'ed with the execute bits on the from file
* modified by the umask.)
(void)fprintf(stderr
, "overwrite %s? ", to
.p_path
);
checkch
= ch
= getchar();
while (ch
!= '\n' && ch
!= EOF
)
to_fd
= open(to
.p_path
, O_WRONLY
| O_TRUNC
, 0);
to_fd
= open(to
.p_path
, O_WRONLY
| O_TRUNC
| O_CREAT
,
fs
->st_mode
& ~(S_ISUID
| S_ISGID
));
* Mmap and write if less than 8M (the limit is so we don't totally
* trash memory on big files. This is really a minor hack, but it
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
if (fs
->st_size
<= 8 * 1048576) {
if ((p
= mmap(NULL
, (size_t)fs
->st_size
, PROT_READ
,
0, from_fd
, (off_t
)0)) == (char *)-1) {
warn("%s", entp
->fts_path
);
if (write(to_fd
, p
, fs
->st_size
) != fs
->st_size
) {
/* Some systems don't unmap on close(2). */
if (munmap(p
, fs
->st_size
) < 0) {
warn("%s", entp
->fts_path
);
while ((rcount
= read(from_fd
, buf
, MAXBSIZE
)) > 0) {
wcount
= write(to_fd
, buf
, rcount
);
if (rcount
!= wcount
|| wcount
== -1) {
warn("%s", entp
->fts_path
);
/* If the copy went bad, lose the file. */
if (pflag
&& setfile(fs
, to_fd
))
* If the source was setuid or setgid, lose the bits unless the
* copy is owned by the same user and group.
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
else if (fs
->st_mode
& (S_ISUID
| S_ISGID
) && fs
->st_uid
== myuid
)
if (fstat(to_fd
, &to_stat
)) {
} else if (fs
->st_gid
== to_stat
.st_gid
&&
fchmod(to_fd
, fs
->st_mode
& RETAINBITS
& ~myumask
)) {
if ((len
= readlink(p
->fts_path
, link
, sizeof(link
))) == -1) {
warn("readlink: %s", p
->fts_path
);
if (exists
&& unlink(to
.p_path
)) {
warn("unlink: %s", to
.p_path
);
if (symlink(link
, to
.p_path
)) {
warn("symlink: %s", link
);
copy_fifo(from_stat
, exists
)
if (exists
&& unlink(to
.p_path
)) {
warn("unlink: %s", to
.p_path
);
if (mkfifo(to
.p_path
, from_stat
->st_mode
)) {
warn("mkfifo: %s", to
.p_path
);
return (pflag
? setfile(from_stat
, 0) : 0);
copy_special(from_stat
, exists
)
if (exists
&& unlink(to
.p_path
)) {
warn("unlink: %s", to
.p_path
);
if (mknod(to
.p_path
, from_stat
->st_mode
, from_stat
->st_rdev
)) {
warn("mknod: %s", to
.p_path
);
return (pflag
? setfile(from_stat
, 0) : 0);
register struct stat
*fs
;
static struct timeval tv
[2];
fs
->st_mode
&= S_ISUID
| S_ISGID
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
TIMESPEC_TO_TIMEVAL(&tv
[0], &fs
->st_atimespec
);
TIMESPEC_TO_TIMEVAL(&tv
[1], &fs
->st_mtimespec
);
if (utimes(to
.p_path
, tv
)) {
warn("utimes: %s", to
.p_path
);
* Changing the ownership probably won't succeed, unless we're root
* or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
* the mode; current BSD behavior is to remove all setuid bits on
* chown. If chown fails, lose setuid/setgid bits.
if (fd
? fchown(fd
, fs
->st_uid
, fs
->st_gid
) :
chown(to
.p_path
, fs
->st_uid
, fs
->st_gid
)) {
warn("chown: %s", to
.p_path
);
fs
->st_mode
&= ~(S_ISUID
| S_ISGID
);
if (fd
? fchmod(fd
, fs
->st_mode
) : chmod(to
.p_path
, fs
->st_mode
)) {
warn("chown: %s", to
.p_path
);
fchflags(fd
, fs
->st_flags
) : chflags(to
.p_path
, fs
->st_flags
)) {
warn("chflags: %s", to
.p_path
);
(void)fprintf(stderr
, "%s\n%s\n",
"usage: cp [-R [-H | -L | -P] [-fip] src target",
" cp [-R [-H | -L | -P] [-fip] src1 ... srcN directory");