Merge 4.4-Lite version.

This commit is contained in:
mycroft 1994-09-22 09:24:35 +00:00
parent e40b06360c
commit 051219a643
5 changed files with 389 additions and 479 deletions

View File

@ -1,7 +1,7 @@
# from: @(#)Makefile 5.3 (Berkeley) 4/3/91
# $Id: Makefile,v 1.5 1993/08/07 03:14:51 mycroft Exp $
# from: @(#)Makefile 8.1 (Berkeley) 5/31/93
# $Id: Makefile,v 1.6 1994/09/22 09:24:35 mycroft Exp $
PROG= cp
SRCS= cp.c path.c
SRCS= cp.c utils.c
.include <bsd.prog.mk>

View File

@ -1,5 +1,5 @@
.\" Copyright (c) 1989, 1990 The Regents of the University of California.
.\" All rights reserved.
.\" Copyright (c) 1989, 1990, 1993, 1994
.\" The Regents of the University of California. All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" the Institute of Electrical and Electronics Engineers, Inc.
@ -32,10 +32,10 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" from: @(#)cp.1 6.15 (Berkeley) 7/30/91
.\" $Id: cp.1,v 1.5 1993/08/07 03:14:54 mycroft Exp $
.\" from: @(#)cp.1 8.3 (Berkeley) 4/18/94
.\" $Id: cp.1,v 1.6 1994/09/22 09:24:38 mycroft Exp $
.\"
.Dd July 30, 1991
.Dd April 18, 1994
.Dt CP 1
.Os BSD 4
.Sh NAME
@ -43,10 +43,18 @@
.Nd copy files
.Sh SYNOPSIS
.Nm cp
.Op Fl Rfhip
.Oo
.Fl R
.Op Fl H | Fl L | Fl P
.Oc
.Op Fl fip
.Ar source_file target_file
.Nm cp
.Op Fl Rfhip
.Oo
.Fl R
.Op Fl H | Fl L | Fl P
.Oc
.Op Fl fip
.Ar source_file ... target_directory
.Sh DESCRIPTION
In the first synopsis form, the
@ -67,6 +75,19 @@ detects an attempt to copy a file to itself, the copy will fail.
.Pp
The following options are available:
.Bl -tag -width flag
.It Fl H
If the
.Fl R
option is specified, symbolic links on the command line are followed.
(Symbolic links encountered in the tree traversal are not followed.)
.It Fl L
If the
.Fl R
option is specified, all symbolic links are followed.
.It Fl P
If the
.Fl R
option is specified, no symbolic links are followed.
.It Fl R
If
.Ar source_file
@ -88,26 +109,19 @@ regardless of its permissions.
option is ignored if the
.Fl f
option is specified.)
.It Fl h
Forces
.Nm cp
to follow symbolic links.
Provided for the
.Fl R
option which does not follow symbolic links by default.
.It Fl i
Causes
.Nm cp
to write a prompt to standard error before copying a file that would
overwrite an existing file.
to write a prompt to the standard error output before copying a file
that would overwrite an existing file.
If the response from the standard input begins with the character
.Sq Li y ,
the file is copied if permissions allow the copy.
the file copy is attempted.
.It Fl p
Causes
.Nm cp
to preserve in the copy as many of the modification time, access time,
file mode, user ID, and group ID as allowed by permissions.
file flags, file mode, user ID, and group ID as allowed by permissions.
.Pp
If the user ID and group ID cannot be preserved, no error message
is displayed and the exit value is not altered.
@ -118,8 +132,8 @@ in the copy's permissions.
If the source file has its set group ID bit on and the group ID cannot
be preserved, the set group ID bit is not preserved
in the copy's permissions.
If the source file has both the set user ID and set group ID bits
on and either the user ID or group ID cannot be preserved, neither
If the source file has both its set user ID and set group ID bits on,
and either the user ID or group ID cannot be preserved, neither
the set user ID or set group ID bits are preserved in the copy's
permissions.
.El
@ -128,6 +142,14 @@ For each destination file that already exists, its contents are
overwritten if permissions allow, but its mode, user ID, and group
ID are unchanged.
.Pp
In the second synopsis form,
.Ar target_directory
must exist unless there is only one named
.Ar source_file
which is a directory and the
.Fl R
flag is specified.
.Pp
If the destination file does not exist, the mode of the source file is
used as modified by the file mode creation mask
.Pf ( Ic umask ,
@ -144,16 +166,44 @@ conditions must be fulfilled or both bits are removed.
.Pp
Appropriate permissions are required for file creation or overwriting.
.Pp
Symbolic links are followed unless the
Symbolic links are always followed unless the
.Fl R
flag is set, in which case symbolic links are not followed, by default.
The
.Fl H
or
.Fl L
flags (in conjunction with the
.Fl R
option is specified, in which case the link itself is copied.
flag) cause symbolic links to be followed as described above.
The
.Fl H ,
.Fl L
and
.Fl P
options are ignored unless the
.Fl R
option is specified.
In addition, these options override each other and the
command's actions are determined by the last one specified.
.Pp
.Nm Cp
exits 0 on success, >0 if an error occurred.
.Sh COMPATIBILITY
Historic versions of the
.Nm cp
utility had a
.Fl r
option.
This implementation supports that option, however, its use is strongly
discouraged, as it does not correctly copy special files, symbolic links
or fifo's.
.Sh SEE ALSO
.Xr mv 1 ,
.Xr rcp 1 ,
.Xr umask 2
.Xr umask 2 ,
.Xr fts 3 ,
.Xr symlink 7
.Sh HISTORY
The
.Nm cp

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 1988 The Regents of the University of California.
* All rights reserved.
* Copyright (c) 1988, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* David Hitz of Auspex Systems Inc.
@ -35,133 +35,165 @@
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
All rights reserved.\n";
static char copyright[] =
"@(#) Copyright (c) 1988, 1993, 1994\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
/*static char sccsid[] = "from: @(#)cp.c 5.26 (Berkeley) 10/27/91";*/
static char rcsid[] = "$Id: cp.c,v 1.9 1994/03/28 02:07:04 cgd Exp $";
/*static char sccsid[] = "from: @(#)cp.c 8.2 (Berkeley) 4/1/94";*/
static char *rcsid = "$Id: cp.c,v 1.10 1994/09/22 09:24:40 mycroft Exp $";
#endif /* not lint */
/*
* cp copies source files to target files.
* Cp copies source files to target files.
*
* The global PATH_T structures "to" and "from" always contain paths to the
* current source and target files, respectively. Since cp does not change
* directories, these paths can be either absolute or dot-relative.
* The global PATH_T structure "to" always contains the path to the
* current target file. Since fts(3) does not change directories,
* this path can be either absolute or dot-relative.
*
* The basic algorithm is to initialize "to" and "from", and then call the
* recursive copy() function to do the actual work. If "from" is a file,
* copy copies the data. If "from" is a directory, copy creates the
* corresponding "to" directory, and calls itself recursively on all of
* the entries in the "from" directory.
* The basic algorithm is to initialize "to" and use fts(3) to traverse
* the file hierarchy rooted in the argument list. A trivial case is the
* case of 'cp file1 file2'. The more interesting case is the case of
* 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the
* path (relative to the root of the traversal) is appended to dir (stored
* in "to") to form the final target path.
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <dirent.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <fts.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
static void copy __P((void));
static void copy_dir __P((void));
static void copy_fifo __P((struct stat *, int));
static void copy_file __P((struct stat *, int));
static void copy_link __P((int));
static void copy_special __P((struct stat *, int));
static void setfile __P((struct stat *, int));
static void usage __P((void));
#define STRIP_TRAILING_SLASH(p) { \
while ((p).p_end > (p).p_path && (p).p_end[-1] == '/') \
*--(p).p_end = 0; \
}
PATH_T from = { from.p_path, "" };
PATH_T to = { to.p_path, "" };
uid_t myuid;
int exit_val, myumask;
int iflag, pflag, orflag, rflag;
int (*statfcn)();
char *progname;
int Rflag, iflag, pflag, rflag;
int myumask;
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
int copy __P((char *[], enum op, int));
int mastercmp __P((const FTSENT **, const FTSENT **));
int
main(argc, argv)
int argc;
char **argv;
char *argv[];
{
extern int optind;
struct stat to_stat;
register int c, r;
int symfollow, lstat(), stat();
char *old_to, *p;
struct stat to_stat, tmp_stat;
enum op type;
int Hflag, Lflag, Pflag, ch, fts_options, r;
char *target;
/*
* The utility cp(1) is used by mv(1) -- except for usage statements,
* print the "called as" program name.
*/
progname = (p = rindex(*argv,'/')) ? ++p : *argv;
symfollow = 0;
while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
switch ((char)c) {
Hflag = Lflag = Pflag = Rflag = 0;
while ((ch = getopt(argc, argv, "HLPRfipr")) != EOF)
switch (ch) {
case 'H':
Hflag = 1;
Lflag = Pflag = 0;
break;
case 'L':
Lflag = 1;
Hflag = Pflag = 0;
break;
case 'P':
Pflag = 1;
Hflag = Lflag = 0;
break;
case 'R':
Rflag = 1;
break;
case 'f':
iflag = 0;
break;
case 'h':
symfollow = 1;
break;
case 'i':
iflag = isatty(fileno(stdin));
break;
case 'p':
pflag = 1;
break;
case 'R':
rflag = 1;
break;
case 'r':
orflag = 1;
rflag = 1;
break;
case '?':
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc < 2)
usage();
if (rflag && orflag) {
(void)fprintf(stderr,
"cp: the -R and -r options are mutually exclusive.\n");
exit(1);
fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
if (rflag) {
if (Rflag)
errx(1,
"the -R and -r options may not be specified together.");
if (Hflag || Lflag || Pflag)
errx(1,
"the -H, -L, and -P options may not be specified with the -r option.");
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL;
}
if (Rflag) {
if (Hflag)
fts_options |= FTS_COMFOLLOW;
if (Lflag) {
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL;
}
} else {
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL;
}
myuid = getuid();
/* copy the umask for explicit mode setting */
/* Copy the umask for explicit mode setting. */
myumask = umask(0);
(void)umask(myumask);
/* consume last argument first. */
if (!path_set(&to, argv[--argc]))
exit(1);
statfcn = symfollow || !rflag ? stat : lstat;
/* Save the target base in "to". */
target = argv[--argc];
if (strlen(target) > MAXPATHLEN)
errx(1, "%s: name too long", target);
(void)strcpy(to.p_path, target);
to.p_end = to.p_path + strlen(to.p_path);
if (to.p_path == to.p_end) {
*to.p_end++ = '.';
*to.p_end = 0;
}
STRIP_TRAILING_SLASH(to);
to.target_end = to.p_end;
/* Set end of argument list for fts(3). */
argv[argc] = NULL;
/*
* Cp has two distinct cases:
*
* % cp [-rip] source target
* % cp [-rip] source1 ... directory
* cp [-R] source target
* cp [-R] source1 ... sourceN directory
*
* In both cases, source can be either a file or a directory.
*
@ -171,421 +203,247 @@ main(argc, argv)
*
* In (2), the real target is not directory, but "directory/source".
*/
r = stat(to.p_path, &to_stat);
if (r == -1 && errno != ENOENT) {
err("%s: %s", to.p_path, strerror(errno));
exit(1);
}
if (r == -1 && errno != ENOENT)
err(1, "%s", to.p_path);
if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
/*
* Case (1). Target is not a directory.
*/
*/
if (argc > 1) {
usage();
exit(1);
}
if (!path_set(&from, *argv))
exit(1);
copy();
}
else {
/*
* Need to detect the case:
* cp -R dir foo
* Where dir is a directory and foo does not exist, where
* we want pathname concatenations turned on but not for
* the initial mkdir().
*/
if (r == -1) {
if (rflag || (Rflag && (Lflag || Hflag)))
stat(*argv, &tmp_stat);
else
lstat(*argv, &tmp_stat);
if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
type = DIR_TO_DNE;
else
type = FILE_TO_FILE;
} else
type = FILE_TO_FILE;
} else
/*
* Case (2). Target is a directory.
*/
for (;; ++argv) {
if (!path_set(&from, *argv))
continue;
if (!(old_to =
path_append(&to, path_basename(&from), -1)))
continue;
copy();
if (!--argc)
break;
path_restore(&to, old_to);
}
}
exit(exit_val);
type = FILE_TO_DIR;
exit (copy(argv, type, fts_options));
}
/* copy file or directory at "from" to "to". */
static void
copy()
int
copy(argv, type, fts_options)
char *argv[];
enum op type;
int fts_options;
{
struct stat from_stat, to_stat;
int dne, statval;
struct stat to_stat;
FTS *ftsp;
FTSENT *curr;
int base, dne, nlen, rval;
char *p;
statval = statfcn(from.p_path, &from_stat);
if (statval == -1) {
err("%s: %s", from.p_path, strerror(errno));
return;
}
/* not an error, but need to remember it happened */
if (stat(to.p_path, &to_stat) == -1)
dne = 1;
else {
if (to_stat.st_dev == from_stat.st_dev &&
to_stat.st_ino == from_stat.st_ino) {
(void)fprintf(stderr,
"%s: %s and %s are identical (not copied).\n",
progname, to.p_path, from.p_path);
exit_val = 1;
return;
if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL)
err(1, NULL);
for (rval = 0; (curr = fts_read(ftsp)) != NULL;) {
switch (curr->fts_info) {
case FTS_NS:
case FTS_ERR:
warnx("%s: %s",
curr->fts_path, strerror(curr->fts_errno));
rval = 1;
continue;
case FTS_DC: /* Warn, continue. */
warnx("%s: directory causes a cycle", curr->fts_path);
rval = 1;
continue;
case FTS_DP: /* Ignore, continue. */
continue;
}
if (!S_ISDIR(from_stat.st_mode) && S_ISDIR(to_stat.st_mode)) {
(void)fprintf(stderr,
"%s: %s: cannot overwrite directory with non-directory.\n",
progname, to.p_path);
exit_val = 1;
return;
}
dne = 0;
}
/*
* If we are in case (2) or (3) above, we need to append the
* source name to the target name.
*/
if (type != FILE_TO_FILE) {
if ((curr->fts_namelen +
to.target_end - to.p_path + 1) > MAXPATHLEN) {
warnx("%s/%s: name too long (not copied)",
to.p_path, curr->fts_name);
rval = 1;
continue;
}
switch(from_stat.st_mode & S_IFMT) {
case S_IFLNK:
copy_link(!dne);
return;
case S_IFDIR:
if (!rflag && !orflag) {
(void)fprintf(stderr,
"%s: %s is a directory (not copied).\n",
progname, from.p_path);
exit_val = 1;
return;
/*
* Need to remember the roots of traversals to create
* correct pathnames. If there's a directory being
* copied to a non-existent directory, e.g.
* cp -R a/dir noexist
* the resulting path name should be noexist/foo, not
* noexist/dir/foo (where foo is a file in dir), which
* is the case where the target exists.
*
* Also, check for "..". This is for correct path
* concatentation for paths ending in "..", e.g.
* cp -R .. /tmp
* Paths ending in ".." are changed to ".". This is
* tricky, but seems the easiest way to fix the problem.
*
* XXX
* Since the first level MUST be FTS_ROOTLEVEL, base
* is always initialized.
*/
if (curr->fts_level == FTS_ROOTLEVEL)
if (type != DIR_TO_DNE) {
p = strrchr(curr->fts_path, '/');
base = (p == NULL) ? 0 :
(int)(p - curr->fts_path + 1);
if (!strcmp(&curr->fts_path[base],
".."))
base += 1;
} else
base = curr->fts_pathlen;
if (to.target_end[-1] != '/') {
*to.target_end = '/';
*(to.target_end + 1) = 0;
}
p = &curr->fts_path[base];
nlen = curr->fts_pathlen - base;
(void)strncat(to.target_end + 1, p, nlen);
to.p_end = to.target_end + nlen + 1;
*to.p_end = 0;
STRIP_TRAILING_SLASH(to);
}
if (dne) {
/* Not an error but need to remember it happened */
if (stat(to.p_path, &to_stat) == -1)
dne = 1;
else {
if (to_stat.st_dev == curr->fts_statp->st_dev &&
to_stat.st_ino == curr->fts_statp->st_ino) {
warnx("%s and %s are identical (not copied).",
to.p_path, curr->fts_path);
rval = 1;
if (S_ISDIR(curr->fts_statp->st_mode))
(void)fts_set(ftsp, curr, FTS_SKIP);
continue;
}
if (!S_ISDIR(curr->fts_statp->st_mode) &&
S_ISDIR(to_stat.st_mode)) {
warnx("cannot overwrite directory %s with non-directory %s.",
to.p_path, curr->fts_path);
rval = 1;
continue;
}
dne = 0;
}
switch (curr->fts_statp->st_mode & S_IFMT) {
case S_IFLNK:
if (copy_link(curr, !dne))
rval = 1;
break;
case S_IFDIR:
if (!Rflag && !rflag) {
warnx("%s is a directory (not copied).",
curr->fts_path);
(void)fts_set(ftsp, curr, FTS_SKIP);
rval = 1;
break;
}
/*
* If the directory doesn't exist, create the new
* one with the from file mode plus owner RWX bits,
* modified by the umask. Trade-off between being
* able to write the directory (if from directory is
* 555) and not causing a permissions race. If the
* umask blocks owner writes cp fails.
* umask blocks owner writes, we fail..
*/
if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
err("%s: %s", to.p_path, strerror(errno));
return;
if (dne) {
if (mkdir(to.p_path,
curr->fts_statp->st_mode | S_IRWXU) < 0)
err(1, "%s", to.p_path);
} else if (!S_ISDIR(to_stat.st_mode)) {
errno = ENOTDIR;
err(1, "%s: %s", to.p_path);
}
/*
* If not -p and directory didn't exist, set it to be
* the same as the from directory, umodified by the
* umask; arguably wrong, but it's been that way
* forever.
*/
if (pflag && setfile(curr->fts_statp, 0))
rval = 1;
else if (dne)
(void)chmod(to.p_path,
curr->fts_statp->st_mode);
break;
case S_IFBLK:
case S_IFCHR:
if (Rflag) {
if (copy_special(curr->fts_statp, !dne))
rval = 1;
} else
if (copy_file(curr, dne))
rval = 1;
break;
case S_IFIFO:
if (Rflag)
if (copy_fifo(curr->fts_statp, !dne))
rval = 1;
else
if (copy_file(curr, dne))
rval = 1;
break;
default:
if (copy_file(curr, dne))
rval = 1;
break;
}
else if (!S_ISDIR(to_stat.st_mode)) {
(void)fprintf(stderr, "%s: %s: not a directory.\n",
progname, to.p_path);
return;
}
copy_dir();
/*
* If not -p and directory didn't exist, set it to be the
* same as the from directory, umodified by the umask;
* arguably wrong, but it's been that way forever.
*/
if (pflag)
setfile(&from_stat, 0);
else if (dne)
(void)chmod(to.p_path, from_stat.st_mode);
return;
case S_IFCHR:
case S_IFBLK:
if (rflag) {
copy_special(&from_stat, !dne);
return;
}
break;
case S_IFIFO:
if (rflag) {
copy_fifo(&from_stat, !dne);
return;
}
break;
}
copy_file(&from_stat, dne);
if (errno)
err(1, "fts_read");
return (rval);
}
static void
copy_file(fs, dne)
struct stat *fs;
int dne;
/*
* mastercmp --
* The comparison function for the copy order. The order is to copy
* non-directory files before directory files. The reason for this
* is because files tend to be in the same cylinder group as their
* parent directory, whereas directories tend not to be. Copying the
* files first reduces seeking.
*/
int
mastercmp(a, b)
const FTSENT **a, **b;
{
static char buf[MAXBSIZE];
register int from_fd, to_fd, rcount, wcount;
struct stat to_stat;
char *p;
int a_info, b_info;
if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
err("%s: %s", from.p_path, strerror(errno));
return;
}
/*
* 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.)
*/
if (!dne) {
if (iflag) {
int checkch, ch;
(void)fprintf(stderr, "overwrite %s? ", to.p_path);
checkch = ch = getchar();
while (ch != '\n' && ch != EOF)
ch = getchar();
if (checkch != 'y' && checkch != 'Y') {
(void)close(from_fd);
return;
}
}
to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
} else
to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
fs->st_mode & ~(S_ISUID|S_ISGID));
if (to_fd == -1) {
err("%s: %s", to.p_path, strerror(errno));
(void)close(from_fd);
return;
}
/*
* 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
* wins some CPU back.
*/
if (fs->st_size <= 8 * 1048576) {
if ((p = mmap(NULL, fs->st_size, PROT_READ, 0, from_fd,
(off_t)0)) == (char *)-1)
err("%s: %s", from.p_path, strerror(errno));
if (write(to_fd, p, fs->st_size) != fs->st_size)
err("%s: %s", to.p_path, strerror(errno));
if (munmap(p, fs->st_size) < 0)
err("%s: %s", from.p_path, strerror(errno));
} else {
while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
wcount = write(to_fd, buf, rcount);
if (rcount != wcount || wcount == -1) {
err("%s: %s", to.p_path, strerror(errno));
break;
}
}
if (rcount < 0)
err("%s: %s", from.p_path, strerror(errno));
}
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.
*/
else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
if (fstat(to_fd, &to_stat))
err("%s: %s", to.p_path, strerror(errno));
#define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
fs->st_mode & RETAINBITS & ~myumask))
err("%s: %s", to.p_path, strerror(errno));
(void)close(from_fd);
if (close(to_fd))
err("%s: %s", to.p_path, strerror(errno));
}
static void
copy_dir()
{
struct stat from_stat;
struct dirent *dp, **dir_list;
register int dir_cnt, i;
char *old_from, *old_to;
dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
if (dir_cnt == -1) {
(void)fprintf(stderr, "%s: can't read directory %s.\n",
progname, from.p_path);
exit_val = 1;
}
/*
* Instead of handling directory entries in the order they appear
* on disk, do non-directory files before directory files.
* There are two reasons to do directories last. The first is
* efficiency. Files tend to be in the same cylinder group as
* their parent, whereas directories tend not to be. Copying files
* all at once reduces seeking. Second, deeply nested tree's
* could use up all the file descriptors if we didn't close one
* directory before recursivly starting on the next.
*/
/* copy files */
for (i = 0; i < dir_cnt; ++i) {
dp = dir_list[i];
if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
&& (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
goto done;
if (!(old_from =
path_append(&from, dp->d_name, (int)dp->d_namlen)))
goto done;
if (statfcn(from.p_path, &from_stat) < 0) {
err("%s: %s", dp->d_name, strerror(errno));
path_restore(&from, old_from);
goto done;
}
if (S_ISDIR(from_stat.st_mode)) {
path_restore(&from, old_from);
continue;
}
if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
copy();
path_restore(&to, old_to);
}
path_restore(&from, old_from);
done: dir_list[i] = NULL;
free(dp);
}
/* copy directories */
for (i = 0; i < dir_cnt; ++i) {
dp = dir_list[i];
if (!dp)
continue;
if (!(old_from =
path_append(&from, dp->d_name, (int)dp->d_namlen))) {
free(dp);
continue;
}
if (!(old_to =
path_append(&to, dp->d_name, (int)dp->d_namlen))) {
free(dp);
path_restore(&from, old_from);
continue;
}
copy();
free(dp);
path_restore(&from, old_from);
path_restore(&to, old_to);
}
free(dir_list);
}
static void
copy_link(exists)
int exists;
{
int len;
char link[MAXPATHLEN];
if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
err("readlink: %s: %s", from.p_path, strerror(errno));
return;
}
link[len] = '\0';
if (exists && unlink(to.p_path)) {
err("unlink: %s: %s", to.p_path, strerror(errno));
return;
}
if (symlink(link, to.p_path)) {
err("symlink: %s: %s", link, strerror(errno));
return;
}
}
static void
copy_fifo(from_stat, exists)
struct stat *from_stat;
int exists;
{
if (exists && unlink(to.p_path)) {
err("unlink: %s: %s", to.p_path, strerror(errno));
return;
}
if (mkfifo(to.p_path, from_stat->st_mode)) {
err("mkfifo: %s: %s", to.p_path, strerror(errno));
return;
}
if (pflag)
setfile(from_stat, 0);
}
static void
copy_special(from_stat, exists)
struct stat *from_stat;
int exists;
{
if (exists && unlink(to.p_path)) {
err("unlink: %s: %s", to.p_path, strerror(errno));
return;
}
if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
err("mknod: %s: %s", to.p_path, strerror(errno));
return;
}
if (pflag)
setfile(from_stat, 0);
}
static void
setfile(fs, fd)
register struct stat *fs;
int fd;
{
static struct timeval tv[2];
fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
tv[0].tv_sec = fs->st_atime;
tv[1].tv_sec = fs->st_mtime;
if (utimes(to.p_path, tv))
err("utimes: %s: %s", to.p_path, strerror(errno));
/*
* 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)) {
if (errno != EPERM)
err("chown: %s: %s", to.p_path, strerror(errno));
fs->st_mode &= ~(S_ISUID|S_ISGID);
}
if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
err("chown: %s: %s", to.p_path, strerror(errno));
}
static void
usage()
{
(void)fprintf(stderr,
"usage: cp [-Rfhip] src target;\n cp [-Rfhip] src1 ... srcN directory\n");
exit(1);
}
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
void
#if __STDC__
err(const char *fmt, ...)
#else
err(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
va_list ap;
#if __STDC__
va_start(ap, fmt);
#else
va_start(ap);
#endif
(void)fprintf(stderr, "%s: ", progname);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
exit_val = 1;
a_info = (*a)->fts_info;
if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR)
return (0);
b_info = (*b)->fts_info;
if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR)
return (0);
if (a_info == FTS_D)
return (-1);
if (b_info == FTS_D)
return (1);
return (0);
}

View File

@ -30,7 +30,8 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)extern.h 8.2 (Berkeley) 4/1/94
* from: @(#)extern.h 8.2 (Berkeley) 4/1/94
* $Id: extern.h,v 1.2 1994/09/22 09:24:41 mycroft Exp $
*/
typedef struct {

View File

@ -32,7 +32,8 @@
*/
#ifndef lint
static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
/*static char sccsid[] = "from: @(#)utils.c 8.3 (Berkeley) 4/1/94";*/
static char *rcsid = "$Id: utils.c,v 1.2 1994/09/22 09:24:43 mycroft Exp $";
#endif /* not lint */
#include <sys/param.h>
@ -84,7 +85,7 @@ copy_file(entp, dne)
checkch = ch = getchar();
while (ch != '\n' && ch != EOF)
ch = getchar();
if (checkch != 'y') {
if (checkch != 'y' && checkch != 'Y') {
(void)close(from_fd);
return (0);
}