Merge 4.4-Lite version.
This commit is contained in:
parent
e40b06360c
commit
051219a643
|
@ -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>
|
||||
|
|
96
bin/cp/cp.1
96
bin/cp/cp.1
|
@ -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
|
||||
|
|
758
bin/cp/cp.c
758
bin/cp/cp.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue