Import NetBSD cp command.

Change-Id: I61943a2ef6d5945b9e10e520e5ebf85e99ac668a
This commit is contained in:
Zachary Storer 2014-07-27 06:19:54 -06:00 committed by Lionel Sambuc
parent 9abd355d6b
commit 5da400ecaa
13 changed files with 1308 additions and 1626 deletions

View file

@ -1,7 +1,7 @@
# $NetBSD: Makefile,v 1.22 2007/12/31 15:31:24 ad Exp $
# @(#)Makefile 8.1 (Berkeley) 5/31/93
SUBDIR= cat chmod date df echo ed expr hostname \
SUBDIR= cat chmod cp date df echo ed expr hostname \
kill ksh ln ls mkdir mv pax pwd rm rmdir \
sleep stty sync test

7
bin/cp/Makefile Normal file
View file

@ -0,0 +1,7 @@
# $NetBSD: Makefile,v 1.9 1997/07/20 22:36:37 christos Exp $
# @(#)Makefile 8.1 (Berkeley) 5/31/93
PROG= cp
SRCS= cp.c utils.c
.include <bsd.prog.mk>

254
bin/cp/cp.1 Normal file
View file

@ -0,0 +1,254 @@
.\" $NetBSD: cp.1,v 1.42 2012/03/25 22:37:08 wiz Exp $
.\"
.\" 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.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 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. 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
.\" SUCH DAMAGE.
.\"
.\" @(#)cp.1 8.3 (Berkeley) 4/18/94
.\"
.Dd March 25, 2012
.Dt CP 1
.Os
.Sh NAME
.Nm cp
.Nd copy files
.Sh SYNOPSIS
.Nm
.Oo
.Fl R
.Op Fl H | Fl L | Fl P
.Oc
.Op Fl f | i
.Op Fl alNpv
.Ar source_file target_file
.Nm cp
.Oo
.Fl R
.Op Fl H | Fl L | Fl P
.Oc
.Op Fl f | i
.Op Fl alNpv
.Ar source_file ... target_directory
.Sh DESCRIPTION
In the first synopsis form, the
.Nm
utility copies the contents of the
.Ar source_file
to the
.Ar target_file .
In the second synopsis form,
the contents of each named
.Ar source_file
is copied to the destination
.Ar target_directory .
The names of the files themselves are not changed.
If
.Nm
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 a
Archive mode.
Same as
.Fl RpP .
.It Fl f
For each existing destination pathname, attempt to overwrite it.
If permissions do not allow copy to succeed, remove it and create a new
file, without prompting for confirmation.
(The
.Fl i
option is ignored if the
.Fl f
option is specified.)
.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 i
Causes
.Nm
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 copy is attempted.
.It Fl L
If the
.Fl R
option is specified, all symbolic links are followed.
.It Fl l
Create hard links to regular files in a hierarchy instead of copying.
.It Fl N
When used with
.Fl p ,
don't copy file flags.
.It Fl P
No symbolic links are followed.
This is the default.
.It Fl p
Causes
.Nm
to preserve in the copy as many of the modification time, access time,
file flags, file mode, user ID, group ID, and extended attributes,
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.
.Pp
If the source file has its set user ID bit on and the user ID cannot
be preserved, the set user ID bit is not preserved
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 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.
.Pp
Extended attributes from all accessible namespaces are copied;
others are ignored.
If an error occurs during this copy, a message is displayed and
.Nm
skips the other extended attributes for that file.
.It Fl R
If
.Ar source_file
designates a directory,
.Nm
copies the directory and the entire subtree connected at that point.
This option also causes symbolic links to be copied, rather than
followed, and for
.Nm
to create special files rather than copying them as normal files.
Created directories have the same mode as the corresponding source
directory, unmodified by the process's umask.
.Pp
Note that
.Nm
copies hard linked files as separate files.
If you need to preserve hard links, consider using a utility like
.Xr pax 1
instead.
.It Fl v
Causes
.Nm
to be verbose, showing files as they are copied.
.El
.Pp
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
.Ic ( umask ,
see
.Xr csh 1 ) .
If the source file has its set user ID bit on, that bit is removed
unless both the source file and the destination file are owned by the
same user.
If the source file has its set group ID bit on, that bit is removed
unless both the source file and the destination file are in the same
group and the user is a member of that group.
If both the set user ID and set group ID bits are set, all of the above
conditions must be fulfilled or both bits are removed.
.Pp
Appropriate permissions are required for file creation or overwriting.
.Pp
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
flag), as well as the
.Fl P
flag cause symbolic links to be followed as described above.
The
.Fl H
and
.Fl L
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.
.Sh EXIT STATUS
.Ex -std cp
.Sh COMPATIBILITY
Historic versions of the
.Nm
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 pax 1 ,
.Xr rcp 1 ,
.Xr umask 2 ,
.Xr fts 3 ,
.Xr symlink 7
.Sh STANDARDS
The
.Nm
utility is expected to be
.St -p1003.2
compatible.
.Pp
The
.Fl a
and
.Fl l
flags are non-standard extensions.
They are intended to be compatible with the same options which
other implementations, namely GNU coreutils and
.Fx ,
of this utility have.
.Pp
The
.Fl v
option is an extension to
.St -p1003.2 .

548
bin/cp/cp.c Normal file
View file

@ -0,0 +1,548 @@
/* $NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $ */
/*
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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. 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
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT(
"@(#) Copyright (c) 1988, 1993, 1994\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95";
#else
__RCSID("$NetBSD: cp.c,v 1.58 2012/01/04 15:58:37 christos Exp $");
#endif
#endif /* not lint */
/*
* Cp copies source files to target files.
*
* 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 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 <assert.h>
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <locale.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#define STRIP_TRAILING_SLASH(p) { \
while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \
*--(p).p_end = '\0'; \
}
static char empty[] = "";
PATH_T to = { .p_end = to.p_path, .target_end = empty };
uid_t myuid;
int Hflag, Lflag, Rflag, Pflag, fflag, iflag, lflag, pflag, rflag, vflag, Nflag;
mode_t myumask;
sig_atomic_t pinfo;
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
static int copy(char *[], enum op, int);
static void
progress(int sig __unused)
{
pinfo++;
}
int
main(int argc, char *argv[])
{
struct stat to_stat, tmp_stat;
enum op type;
int ch, fts_options, r, have_trailing_slash;
char *target, **src;
setprogname(argv[0]);
(void)setlocale(LC_ALL, "");
Hflag = Lflag = Pflag = Rflag = 0;
while ((ch = getopt(argc, argv, "HLNPRfailprv")) != -1)
switch (ch) {
case 'H':
Hflag = 1;
Lflag = Pflag = 0;
break;
case 'L':
Lflag = 1;
Hflag = Pflag = 0;
break;
case 'N':
Nflag = 1;
break;
case 'P':
Pflag = 1;
Hflag = Lflag = 0;
break;
case 'R':
Rflag = 1;
break;
case 'a':
Pflag = 1;
pflag = 1;
Rflag = 1;
Hflag = Lflag = 0;
break;
case 'f':
fflag = 1;
iflag = 0;
break;
case 'i':
iflag = isatty(fileno(stdin));
fflag = 0;
break;
case 'l':
lflag = 1;
break;
case 'p':
pflag = 1;
break;
case 'r':
rflag = 1;
break;
case 'v':
vflag = 1;
break;
case '?':
default:
usage();
/* NOTREACHED */
}
argc -= optind;
argv += optind;
if (argc < 2)
usage();
fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
if (rflag) {
if (Rflag) {
errx(EXIT_FAILURE,
"the -R and -r options may not be specified together.");
/* NOTREACHED */
}
if (Hflag || Lflag || Pflag) {
errx(EXIT_FAILURE,
"the -H, -L, and -P options may not be specified with the -r option.");
/* NOTREACHED */
}
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 if (!Pflag) {
fts_options &= ~FTS_PHYSICAL;
fts_options |= FTS_LOGICAL | FTS_COMFOLLOW;
}
myuid = getuid();
/* Copy the umask for explicit mode setting. */
myumask = umask(0);
(void)umask(myumask);
/* Save the target base in "to". */
target = argv[--argc];
if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
errx(EXIT_FAILURE, "%s: name too long", target);
to.p_end = to.p_path + strlen(to.p_path);
have_trailing_slash = (to.p_end[-1] == '/');
if (have_trailing_slash)
STRIP_TRAILING_SLASH(to);
to.target_end = to.p_end;
/* Set end of argument list for fts(3). */
argv[argc] = NULL;
(void)signal(SIGINFO, progress);
/*
* Cp has two distinct cases:
*
* cp [-R] source target
* cp [-R] source1 ... sourceN directory
*
* In both cases, source can be either a file or a directory.
*
* In (1), the target becomes a copy of the source. That is, if the
* source is a file, the target will be a file, and likewise for
* directories.
*
* In (2), the real target is not directory, but "directory/source".
*/
if (Pflag)
r = lstat(to.p_path, &to_stat);
else
r = stat(to.p_path, &to_stat);
if (r == -1 && errno != ENOENT) {
err(EXIT_FAILURE, "%s", to.p_path);
/* NOTREACHED */
}
if (r == -1 || !S_ISDIR(to_stat.st_mode)) {
/*
* Case (1). Target is not a directory.
*/
if (argc > 1)
usage();
/*
* 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)))
r = stat(*argv, &tmp_stat);
else
r = lstat(*argv, &tmp_stat);
if (r == -1) {
err(EXIT_FAILURE, "%s", *argv);
/* NOTREACHED */
}
if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag))
type = DIR_TO_DNE;
else
type = FILE_TO_FILE;
} else
type = FILE_TO_FILE;
if (have_trailing_slash && type == FILE_TO_FILE) {
if (r == -1)
errx(1, "directory %s does not exist",
to.p_path);
else
errx(1, "%s is not a directory", to.p_path);
}
} else {
/*
* Case (2). Target is a directory.
*/
type = FILE_TO_DIR;
}
/*
* make "cp -rp src/ dst" behave like "cp -rp src dst" not
* like "cp -rp src/. dst"
*/
for (src = argv; *src; src++) {
size_t len = strlen(*src);
while (len-- > 1 && (*src)[len] == '/')
(*src)[len] = '\0';
}
exit(copy(argv, type, fts_options));
/* NOTREACHED */
}
static int dnestack[MAXPATHLEN]; /* unlikely we'll have more nested dirs */
static ssize_t dnesp;
static void
pushdne(int dne)
{
dnestack[dnesp++] = dne;
assert(dnesp < MAXPATHLEN);
}
static int
popdne(void)
{
int rv;
rv = dnestack[--dnesp];
assert(dnesp >= 0);
return rv;
}
static int
copy(char *argv[], enum op type, int fts_options)
{
struct stat to_stat;
FTS *ftsp;
FTSENT *curr;
int base, dne, sval;
int this_failed, any_failed;
size_t nlen;
char *p, *target_mid;
base = 0; /* XXX gcc -Wuninitialized (see comment below) */
if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
err(EXIT_FAILURE, "%s", argv[0]);
/* NOTREACHED */
for (any_failed = 0; (curr = fts_read(ftsp)) != NULL;) {
this_failed = 0;
switch (curr->fts_info) {
case FTS_NS:
case FTS_DNR:
case FTS_ERR:
warnx("%s: %s", curr->fts_path,
strerror(curr->fts_errno));
this_failed = any_failed = 1;
continue;
case FTS_DC: /* Warn, continue. */
warnx("%s: directory causes a cycle", curr->fts_path);
this_failed = any_failed = 1;
continue;
}
/*
* 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);
this_failed = any_failed = 1;
continue;
}
/*
* 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;
}
p = &curr->fts_path[base];
nlen = curr->fts_pathlen - base;
target_mid = to.target_end;
if (*p != '/' && target_mid[-1] != '/')
*target_mid++ = '/';
*target_mid = 0;
if (target_mid - to.p_path + nlen >= PATH_MAX) {
warnx("%s%s: name too long (not copied)",
to.p_path, p);
this_failed = any_failed = 1;
continue;
}
(void)strncat(target_mid, p, nlen);
to.p_end = target_mid + nlen;
*to.p_end = 0;
STRIP_TRAILING_SLASH(to);
}
sval = Pflag ? lstat(to.p_path, &to_stat) : stat(to.p_path, &to_stat);
/* Not an error but need to remember it happened */
if (sval == -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);
this_failed = any_failed = 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);
this_failed = any_failed = 1;
continue;
}
dne = 0;
}
switch (curr->fts_statp->st_mode & S_IFMT) {
case S_IFLNK:
/* Catch special case of a non dangling symlink */
if((fts_options & FTS_LOGICAL) ||
((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) {
if (copy_file(curr, dne))
this_failed = any_failed = 1;
} else {
if (copy_link(curr, !dne))
this_failed = any_failed = 1;
}
break;
case S_IFDIR:
if (!Rflag && !rflag) {
if (curr->fts_info == FTS_D)
warnx("%s is a directory (not copied).",
curr->fts_path);
(void)fts_set(ftsp, curr, FTS_SKIP);
this_failed = any_failed = 1;
break;
}
/*
* Directories get noticed twice:
* In the first pass, create it if needed.
* In the second pass, after the children have been copied, set the permissions.
*/
if (curr->fts_info == FTS_D) /* First pass */
{
/*
* 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, we fail..
*/
pushdne(dne);
if (dne) {
if (mkdir(to.p_path,
curr->fts_statp->st_mode | S_IRWXU) < 0)
err(EXIT_FAILURE, "%s",
to.p_path);
/* NOTREACHED */
} else if (!S_ISDIR(to_stat.st_mode)) {
errno = ENOTDIR;
err(EXIT_FAILURE, "%s",
to.p_path);
/* NOTREACHED */
}
}
else if (curr->fts_info == FTS_DP) /* Second pass */
{
/*
* 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))
this_failed = any_failed = 1;
else if ((dne = popdne()))
(void)chmod(to.p_path,
curr->fts_statp->st_mode);
}
else
{
warnx("directory %s encountered when not expected.",
curr->fts_path);
this_failed = any_failed = 1;
break;
}
break;
case S_IFBLK:
case S_IFCHR:
if (Rflag) {
if (copy_special(curr->fts_statp, !dne))
this_failed = any_failed = 1;
} else
if (copy_file(curr, dne))
this_failed = any_failed = 1;
break;
case S_IFIFO:
if (Rflag) {
if (copy_fifo(curr->fts_statp, !dne))
this_failed = any_failed = 1;
} else
if (copy_file(curr, dne))
this_failed = any_failed = 1;
break;
default:
if (copy_file(curr, dne))
this_failed = any_failed = 1;
break;
}
if (vflag && !this_failed)
(void)printf("%s -> %s\n", curr->fts_path, to.p_path);
}
if (errno) {
err(EXIT_FAILURE, "fts_read");
/* NOTREACHED */
}
(void)fts_close(ftsp);
return (any_failed);
}

61
bin/cp/extern.h Normal file
View file

@ -0,0 +1,61 @@
/* $NetBSD: extern.h,v 1.17 2012/01/04 15:58:37 christos Exp $ */
/*-
* 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
* are met:
* 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. 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
* SUCH DAMAGE.
*
* @(#)extern.h 8.2 (Berkeley) 4/1/94
*/
#ifndef _EXTERN_H_
#define _EXTERN_H_
typedef struct {
char *p_end; /* pointer to NULL at end of path */
char *target_end; /* pointer to end of target base */
char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */
} PATH_T;
extern PATH_T to;
extern uid_t myuid;
extern int Rflag, rflag, Hflag, Lflag, Pflag, fflag, iflag, lflag, pflag, Nflag;
extern mode_t myumask;
extern sig_atomic_t pinfo;
#include <sys/cdefs.h>
__BEGIN_DECLS
int copy_fifo(struct stat *, int);
int copy_file(FTSENT *, int);
int copy_link(FTSENT *, int);
int copy_special(struct stat *, int);
int set_utimes(const char *, struct stat *);
int setfile(struct stat *, int);
void usage(void) __attribute__((__noreturn__));
__END_DECLS
#endif /* !_EXTERN_H_ */

429
bin/cp/utils.c Normal file
View file

@ -0,0 +1,429 @@
/* $NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $ */
/*-
* 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
* are met:
* 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. 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
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
#else
__RCSID("$NetBSD: utils.c,v 1.41 2012/01/04 15:58:37 christos Exp $");
#endif
#endif /* not lint */
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/extattr.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
#define MMAP_MAX_SIZE (8 * 1048576)
#define MMAP_MAX_WRITE (64 * 1024)
int
set_utimes(const char *file, struct stat *fs)
{
static struct timeval tv[2];
TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
if (lutimes(file, tv)) {
warn("lutimes: %s", file);
return (1);
}
return (0);
}
struct finfo {
const char *from;
const char *to;
size_t size;
};
static void
progress(const struct finfo *fi, size_t written)
{
int pcent = (int)((100.0 * written) / fi->size);
pinfo = 0;
(void)fprintf(stderr, "%s => %s %zu/%zu bytes %d%% written\n",
fi->from, fi->to, written, fi->size, pcent);
}
int
copy_file(FTSENT *entp, int dne)
{
static char buf[MAXBSIZE];
struct stat to_stat, *fs;
int ch, checkch, from_fd, rcount, rval, to_fd, tolnk, wcount;
char *p;
size_t ptotal = 0;
if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
warn("%s", entp->fts_path);
return (1);
}
to_fd = -1;
fs = entp->fts_statp;
tolnk = ((Rflag && !(Lflag || Hflag)) || Pflag);
/*
* 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) {
struct stat sb;
int sval;
if (iflag) {
(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 (0);
}
}
sval = tolnk ?
lstat(to.p_path, &sb) : stat(to.p_path, &sb);
if (sval == -1) {
warn("stat: %s", to.p_path);
(void)close(from_fd);
return (1);
}
if (!(tolnk && S_ISLNK(sb.st_mode)))
to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
} else
to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
fs->st_mode & ~(S_ISUID | S_ISGID));
if (to_fd == -1 && (fflag || tolnk)) {
/*
* attempt to remove existing destination file name and
* create a new file
*/
(void)unlink(to.p_path);
to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
fs->st_mode & ~(S_ISUID | S_ISGID));
}
if (to_fd == -1) {
warn("%s", to.p_path);
(void)close(from_fd);
return (1);
}
rval = 0;
/* if hard linking then simply close the open fds, link and return */
if (lflag) {
(void)close(from_fd);
(void)close(to_fd);
(void)unlink(to.p_path);
if (link(entp->fts_path, to.p_path)) {
warn("%s", to.p_path);
return (1);
}
return (0);
}
/* NOTREACHED */
/*
* There's no reason to do anything other than close the file
* now if it's empty, so let's not bother.
*/
if (fs->st_size > 0) {
struct finfo fi;
fi.from = entp->fts_path;
fi.to = to.p_path;
fi.size = (size_t)fs->st_size;
/*
* 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.
*/
bool use_read;
use_read = true;
if (fs->st_size <= MMAP_MAX_SIZE) {
size_t fsize = (size_t)fs->st_size;
p = mmap(NULL, fsize, PROT_READ, MAP_FILE|MAP_SHARED,
from_fd, (off_t)0);
if (p != MAP_FAILED) {
size_t remainder;
use_read = false;
#if !defined(__minix)
(void) madvise(p, (size_t)fs->st_size,
MADV_SEQUENTIAL);
#endif /* !defined(__minix) */
/*
* Write out the data in small chunks to
* avoid locking the output file for a
* long time if the reading the data from
* the source is slow.
*/
remainder = fsize;
do {
ssize_t chunk;
chunk = (remainder > MMAP_MAX_WRITE) ?
MMAP_MAX_WRITE : remainder;
if (write(to_fd, &p[fsize - remainder],
chunk) != chunk) {
warn("%s", to.p_path);
rval = 1;
break;
}
remainder -= chunk;
ptotal += chunk;
if (pinfo)
progress(&fi, ptotal);
} while (remainder > 0);
if (munmap(p, fsize) < 0) {
warn("%s", entp->fts_path);
rval = 1;
}
}
}
if (use_read) {
while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
wcount = write(to_fd, buf, (size_t)rcount);
if (rcount != wcount || wcount == -1) {
warn("%s", to.p_path);
rval = 1;
break;
}
ptotal += wcount;
if (pinfo)
progress(&fi, ptotal);
}
if (rcount < 0) {
warn("%s", entp->fts_path);
rval = 1;
}
}
}
#if !defined(__minix)
if (pflag && (fcpxattr(from_fd, to_fd) != 0))
warn("%s: error copying extended attributes", to.p_path);
#endif /* !defined(__minix) */
(void)close(from_fd);
if (rval == 1) {
(void)close(to_fd);
return (1);
}
if (pflag && setfile(fs, to_fd))
rval = 1;
/*
* If the source was setuid or setgid, lose the bits unless the
* copy is owned by the same user and group.
*/
#define RETAINBITS \
(S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
if (!pflag && dne
&& fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
if (fstat(to_fd, &to_stat)) {
warn("%s", to.p_path);
rval = 1;
} else if (fs->st_gid == to_stat.st_gid &&
fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
warn("%s", to.p_path);
rval = 1;
}
}
if (close(to_fd)) {
warn("%s", to.p_path);
rval = 1;
}
/* set the mod/access times now after close of the fd */
if (pflag && set_utimes(to.p_path, fs)) {
rval = 1;
}
return (rval);
}
int
copy_link(FTSENT *p, int exists)
{
int len;
char target[MAXPATHLEN];
if ((len = readlink(p->fts_path, target, sizeof(target)-1)) == -1) {
warn("readlink: %s", p->fts_path);
return (1);
}
target[len] = '\0';
if (exists && unlink(to.p_path)) {
warn("unlink: %s", to.p_path);
return (1);
}
if (symlink(target, to.p_path)) {
warn("symlink: %s", target);
return (1);
}
return (pflag ? setfile(p->fts_statp, 0) : 0);
}
int
copy_fifo(struct stat *from_stat, int exists)
{
if (exists && unlink(to.p_path)) {
warn("unlink: %s", to.p_path);
return (1);
}
if (mkfifo(to.p_path, from_stat->st_mode)) {
warn("mkfifo: %s", to.p_path);
return (1);
}
return (pflag ? setfile(from_stat, 0) : 0);
}
int
copy_special(struct stat *from_stat, int exists)
{
if (exists && unlink(to.p_path)) {
warn("unlink: %s", to.p_path);
return (1);
}
if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
warn("mknod: %s", to.p_path);
return (1);
}
return (pflag ? setfile(from_stat, 0) : 0);
}
/*
* Function: setfile
*
* Purpose:
* Set the owner/group/permissions for the "to" file to the information
* in the stat structure. If fd is zero, also call set_utimes() to set
* the mod/access times. If fd is non-zero, the caller must do a utimes
* itself after close(fd).
*/
int
setfile(struct stat *fs, int fd)
{
int rval, islink;
rval = 0;
islink = S_ISLNK(fs->st_mode);
fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
/*
* 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) :
lchown(to.p_path, fs->st_uid, fs->st_gid)) {
if (errno != EPERM) {
warn("chown: %s", to.p_path);
rval = 1;
}
fs->st_mode &= ~(S_ISUID | S_ISGID);
}
if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) {
warn("chmod: %s", to.p_path);
rval = 1;
}
#if !defined(__minix)
if (!islink && !Nflag) {
unsigned long fflags = fs->st_flags;
/*
* XXX
* NFS doesn't support chflags; ignore errors unless
* there's reason to believe we're losing bits.
* (Note, this still won't be right if the server
* supports flags and we were trying to *remove* flags
* on a file that we copied, i.e., that we didn't create.)
*/
errno = 0;
if ((fd ? fchflags(fd, fflags) :
chflags(to.p_path, fflags)) == -1)
if (errno != EOPNOTSUPP || fs->st_flags != 0) {
warn("chflags: %s", to.p_path);
rval = 1;
}
}
#endif /* !defined(__minix) */
/* if fd is non-zero, caller must call set_utimes() after close() */
if (fd == 0 && set_utimes(to.p_path, fs))
rval = 1;
return (rval);
}
void
usage(void)
{
(void)fprintf(stderr,
"usage: %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n"
" %s [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n",
getprogname(), getprogname());
exit(1);
/* NOTREACHED */
}

View file

@ -5,7 +5,7 @@
SUBDIR= add_route arp ash at backup btrace \
cawf cd cdprobe \
ci cleantmp cmp co \
compress cp crc cron crontab \
compress crc cron crontab \
dd decomp16 DESCRIBE devmand devsize dhcpd \
dhrystone diff diskctl \
eject fbdctl \

View file

@ -1,7 +0,0 @@
# LSC For Now...
NOGCCERROR:= yes
PROG= cp
BINDIR= /bin
MAN=
.include <bsd.prog.mk>

File diff suppressed because it is too large Load diff

View file

@ -695,17 +695,14 @@ mount /dev/$usr /mnt >/dev/null || exit # Mount the intended /usr.
(cd /usr || exit 1
list="`ls | fgrep -v install`"
for d in $list
do
cp -psmr -v $d /mnt/$d
done
pax -rw -pe -v $list /mnt 2>&1
) | progressbar "$USRFILES" || exit # Copy the usr floppy.
umount /dev/$usr >/dev/null || exit # Unmount the intended /usr.
mount /dev/$root /mnt >/dev/null || exit
# Running from the installation CD.
cp -psmr -vx / /mnt | progressbar "$ROOTFILES" || exit
pax -rw -pe -vX / /mnt 2>&1 | progressbar "$ROOTFILES" || exit
chmod o-w /mnt/usr
cp /mnt/etc/motd.install /mnt/etc/motd
@ -753,7 +750,7 @@ then if mount /dev/$home /home 2>/dev/null
do h=`eval echo "~$u"`
if mkdir $h
then echo " * Creating home directory for $u in $h"
cp -psmr /usr/ast $h
pax -rw -pe /usr/ast $h
chown -R $u:operator $h
else echo " * Couldn't create $h"
fi

View file

@ -1,7 +1,7 @@
MAN= ash.1 at.1 \
bsfilt.1 cawf.1 chgrp.1 \
cmp.1 compress.1 \
cp.1 crc.1 crontab.1 dd.1 \
crc.1 crontab.1 dd.1 \
dhrystone.1 dosdir.1 dosread.1 doswrite.1 \
eject.1 \
flexdoc.1 format.1 \

View file

@ -1,223 +0,0 @@
.TH CP 1
.SH NAME
cp, mv, rm, ln, cpdir, clone \- copy, move, remove, link
.SH SYNOPSIS
.B cp
.RB [ \-pifsmrRvx ]
.I file1 file2
.br
.B cp
.RB [ \-pifsrRvx ]
.IR file " ... " dir
.PP
.B mv
.RB [ \-ifsmvx ]
.I file1 file2
.br
.B mv
.RB [ \-ifsvx ]
.IR file " ... " dir
.PP
.B rm
.RB [ \-ifrRvx ]
.IR file " ..."
.PP
.B ln
.RB [ \-ifsSmrRvx ]
.I file1 file2
.br
.B ln
.RB [ \-ifsSrRvx ]
.IR file " ... " dir
.PP
.B cpdir
.RB [ \-ifvx ]
.I file1 file2
.PP
.B clone
.RB [ \-ifsSvx ]
.I file1 file2
.SH DESCRIPTION
.de SP
.if t .sp 0.4
.if n .sp
..
The utilities
.BR cp ,
.BR mv ,
.BR rm ,
and
.B ln
do basic file management: copying, renaming or moving, deletion, and
creating links. (The
.B cpdir
and
.B clone
utilities are easy to use aliases for copying or linking whole trees.
They are the same as
.B cp \-psmr
and
.BR "ln \-fmr" )
.PP
The first synopsis form of the utilities
.BR cp ,
.BR mv ,
and
.B ln
is used if only two arguments are given, and the second argument is not a
directory. The source and target file are then the two files given.
.PP
If the second synopsis form is used then the last argument must be a
directory. Each of the files is copied, moved or linked into this directory.
.PP
A file is by default copied by
.B cp
without looking at its type, so symlinks are followed and devices are opened
and read from or written to. Links between files are ignored. This
behavior can be changed by using the proper options.
.PP
The
.B mv
utility uses the
.BR rename (2)
call to rename or move files. If source and target are on different devices
however, then
.B mv
will use
.B cp \-pr
to copy the files or directory trees.
.PP
Each utility continues with the next file on errors, except on I/O errors.
.SH OPTIONS
.TP
.B \-p
Copy the file attributes like mode, owner, group and time of last
modification. Normally only the mode is copied to a new file with the file
creation mask applied. Setuid bits are cleared if setting the ownership
fails.
.TP
.B \-i
Ask if ok to overwrite, replace or remove.
.B Mv
and
.B rm
will ask this automatically if interactive and the target file is writable.
.B Cp
will fail if the target cannot be written,
.B ln
will always fail if the target exists.
.TP
.B \-f
Makes
.B cp
remove a target file before copying if it is not writable,
.B mv
removes an existing target without asking,
.B rm
does not report any errors, and
.B ln
removes an existing target file before linking. The last of
.B \-i
and
.B \-f
wins for
.B mv
if both flags are set, the other utilities do something sensible, like asking
before forcefully removing.
.TP
.B \-s
Make a symlink instead of a normal link. For utilities other than
.B ln
this flag means "copy similar". The modified time is always copied for
.B cp \-s
and the other attributes are copied if a new file is created. The normal
\s-2POSIX\s+2 required patronizing like applying the file creation mask or
clearing setuid bits is not done.
.TP
.B \-S
Make a symlink if a normal link cannot be made because source and target are
on different devices. The symlink is required to really refer back to the
source, meaning that a/b must exist in the call
.BR "ln \-S a/b c/d" ,
and that the symlink from c/d must lead back to a/b. So the symlink will be
created as if
.B "ln \-s ../a/b c/d"
was called. If the target is a full path, but the source is not then an
error will be given saying that this is "too difficult."
.TP
.B \-m
Merge trees. The first synopsis form is assumed, and the files from one
tree are merged into the other. There is no "if it's a directory the put
it into that directory" trickery here.
.TP
.BR \-r ", " \-R
Recursively copy, remove, or link. If the source is a directory then the
files in this directory are copied to similarly named files in the target
directory. Special files are copied as new special files, they are not read
or written. Symlinks are still expanded and the link structure ignored with
.BR \-R .
The
.B \-r
flag does copy symlinks as symlinks and keeps the link structure intact.
(Note that
.B \-R
is invented by \s-2POSIX\s+2 as a replacement for the classic
.B \-r
option of older copy commands that did read special files. The standard
says that
.B \-r
is implementation defined, so that's why this flag is better than
.B \-R
in this implementation of
.BR cp .)
For
.B rm
and
.B ln
both flags mean the same.
.B Ln
will recursively link the files in the trees, except symlinks, they are
copied. If symlinks are created with
.B ln \-rs
or
.B ln \-rS
then they are required "to work" as described with the
.B \-S
flag.
.TP
.B \-v
Verbose. Show what is done on standard output.
.TP
.B \-x
Do not cross mount points. Empty directories will be created if the source
directory is a mount point on a copy, move or link. A mount point will not
be removed or traversed recursively. This flag allows one to copy the root
device, e.g.
.BR "cpdir \-x / /mnt" .
.SH "SEE ALSO"
.BR cat (1),
.BR mkdir (1),
.BR rmdir (1),
.BR mkdir (2),
.BR rmdir (2),
.BR link (2),
.BR unlink (2),
.BR rename (2),
.BR open (2),
.BR read (2),
.BR write (2),
.BR opendir (3).
.SH NOTES
All the utilities described are links to the same program.
.SH BUGS
.B Mv
should first copy a tree across devices and then remove the source tree if
there was no error. Instead, each file in the tree is copied and
immediately removed. On error you may be left with two half-filled trees,
together containing all of the files. You may have to restart the move with
.BR "mv \-m" .
.PP
.B Rm
should be able to remove arbitrarily deep trees.
.SH AUTHOR
Kees J. Bot (kjb@cs.vu.nl)

View file

@ -311,7 +311,7 @@ Next make a file system for on-disk /usr and copy the floppy /usr on to it.
.PP
.XB "mkfs\0/dev/c0d0p1s2"
.XB "mount\0/dev/c0d0p1s2\0/mnt"
.XB "cp -psmr\0\-v\0/usr\0/mnt"
.XB "pax -rw -pe\0\-v\0/usr\0/mnt"
.PP
This will create a file system on /dev/c0d0p1s2, mount it on /mnt, and copy the
contents of the \s-2USR\s+2 floppy onto it.
@ -330,7 +330,7 @@ fill it from the floppy:
.XB "mkfs\0\-i\0512\0/dev/c0d0p1s0"
.XB "mount\0/dev/fd0\0/fd0"
.XB "mount\0/dev/c0d0p1s0\0/mnt"
.XB "cp -psmr\0\-v\0/fd0\0/mnt"
.XB "pax -rw -pe\0\-v\0/fd0\0/mnt"
.XB "umount\0/dev/fd0"
.PP
Remove
@ -646,7 +646,7 @@ This is slower then a RAM disk, but saves a lot of memory.
The automatic installation script knows how to handle this new situation.
If you install manually then you have to use
.PP
.XB "cp -psmr\0\-vx\0/\0/mnt"
.XB "pax -rw -pe\0\-vx\0/\0/mnt"
.PP
to copy the root device to disk. When it is time to fill /usr and you only
have one floppy drive then hit DEL to get out of the installation script and