* Copyright (c) 1988 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* David Hitz of Auspex Systems Inc.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)cp.c 5.8 (Berkeley) %G%";
* 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-realative.
* 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.
char *p_path
; /* pointer to the start of a path. */
char *p_end
; /* pointer to NULL at end of path. */
#define type(st) ((st).st_mode&S_IFMT)
char *path_append(), *path_basename();
int interactive_flag
, preserve_flag
, recursive_flag
;
int (*statfcn
)(); /* stat function to use */
char *buf
; /* I/O; malloc for best alignment. */
char from_buf
[MAXPATHLEN
+ 1], /* source path buffer */
to_buf
[MAXPATHLEN
+ 1]; /* target path buffer */
path_t from
= {from_buf
, from_buf
};
path_t to
= {to_buf
, to_buf
};
extern int optind
, errno
;
int force_flag
, symfollow
, lstat(), stat();
force_flag
= symfollow
= 0;
while ((c
= getopt(argc
, argv
, "Rfhipr")) != EOF
) {
interactive_flag
= isatty(fileno(stdin
));
buf
= (char *)malloc(MAXBSIZE
);
(void)fprintf(stderr
, "cp: out of space.\n");
/* consume last argument first. */
if (!path_set(&to
, argv
[--argc
]))
statfcn
= symfollow
|| !recursive_flag
? stat
: lstat
;
* Cp has two distinct cases:
* Case (1) $ cp [-rip] source target
* Case (2) $ cp [-rip] source1 ... 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
* In (2), the real target is not directory, but "directory/source".
r
= stat(to
.p_path
, &to_stat
);
if (r
== -1 && errno
!= ENOENT
) {
if (r
== -1 || type(to_stat
) != S_IFDIR
) {
* Case (1). Target is not a directory.
if (!path_set(&from
, *argv
))
* Case (2). Target is a directory.
if (!path_set(&from
, *argv
))
old_to
= path_append(&to
, path_basename(&from
), -1);
path_restore(&to
, old_to
);
/* copy file or directory at "from" to "to". */
struct stat from_stat
, to_stat
;
statval
= statfcn(from
.p_path
, &from_stat
);
/* not an error, but need to remember it happened */
if (stat(to
.p_path
, &to_stat
) == -1)
if (to_stat
.st_dev
== from_stat
.st_dev
&&
to_stat
.st_ino
== from_stat
.st_ino
) {
"cp: %s and %s are identical (not copied).\n",
switch(type(from_stat
)) {
"cp: %s is a directory (not copied).\n",
* 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.
if (mkdir(to
.p_path
, from_stat
.st_mode
|S_IRWXU
) < 0) {
else if (type(to_stat
) != S_IFDIR
) {
(void)fprintf(stderr
, "cp: %s: not a directory.\n",
* 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.
(void)chmod(to
.p_path
, from_stat
.st_mode
);
* if recursive flag on, try and create the special device
* otherwise copy the contents.
copy_special(&from_stat
, &to_stat
);
register int from_fd
, to_fd
, rcount
, wcount
;
if ((from_fd
= open(from
.p_path
, O_RDONLY
, 0)) == -1) {
* In the interactive case, use O_EXCL to notice existing files.
* If the file exists, verify with the user.
* If the file DNE, create it with the mode of the from file 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.
(interactive_flag
? O_EXCL
: 0) | O_WRONLY
| O_CREAT
| O_TRUNC
,
if (to_fd
== -1 && errno
== EEXIST
&& interactive_flag
) {
(void)fprintf(stderr
, "overwrite %s? ", to
.p_path
);
checkch
= ch
= getchar();
while (ch
!= '\n' && ch
!= EOF
)
to_fd
= open(to
.p_path
, O_WRONLY
| O_CREAT
| O_TRUNC
,
while ((rcount
= read(from_fd
, buf
, MAXBSIZE
)) > 0) {
wcount
= write(to_fd
, buf
, rcount
);
if (rcount
!= wcount
|| wcount
== -1) {
struct direct
*dp
, **dir_list
;
dir_cnt
= scandir(from
.p_path
, &dir_list
, NULL
, NULL
);
(void)fprintf(stderr
, "cp: can't read directory %s.\n",
* 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.
for (i
= 0; i
< dir_cnt
; ++i
) {
if (dp
->d_namlen
<= 2 && dp
->d_name
[0] == '.'
&& (dp
->d_name
[1] == NULL
|| dp
->d_name
[1] == '.'))
old_from
= path_append(&from
, dp
->d_name
, (int)dp
->d_namlen
);
if (statfcn(from
.p_path
, &from_stat
) < 0) {
path_restore(&from
, old_from
);
if (type(from_stat
) == S_IFDIR
) {
path_restore(&from
, old_from
);
old_to
= path_append(&to
, dp
->d_name
, (int)dp
->d_namlen
);
path_restore(&to
, old_to
);
path_restore(&from
, old_from
);
done
: dir_list
[i
] = NULL
;
for (i
= 0; i
< dir_cnt
; ++i
) {
old_from
= path_append(&from
, dp
->d_name
, (int) dp
->d_namlen
);
old_to
= path_append(&to
, dp
->d_name
, (int) dp
->d_namlen
);
path_restore(&from
, old_from
);
path_restore(&from
, old_from
);
path_restore(&to
, old_to
);
if (readlink(from
.p_path
, link
, sizeof(link
)) == -1) {
if (exists
&& unlink(to
.p_path
)) {
if (symlink(link
, to
.p_path
)) {
copy_special(from_stat
, to_stat
)
struct stat
*from_stat
, *to_stat
;
if (to_stat
->st_ino
!= -1 && unlink(to
.p_path
)) {
if (mknod(to
.p_path
, from_stat
->st_mode
, from_stat
->st_rdev
)) {
register struct stat
*fs
;
static struct timeval tv
[2];
tv
[0].tv_sec
= fs
->st_atime
;
tv
[1].tv_sec
= fs
->st_mtime
;
if (utimes(to
.p_path
, tv
))
* Changing the ownership probably won't succeed, unless we're
* root or POSIX_CHOWN_RESTRICTED is not set. Try it last so
* everything else gets set first.
if (fchmod(fd
, fs
->st_mode
))
if (dochown
&& fchown(fd
, fs
->st_uid
, fs
->st_gid
) == -1)
if (chmod(to
.p_path
, fs
->st_mode
))
if (dochown
&& chown(to
.p_path
, fs
->st_uid
, fs
->st_gid
) == -1)
(void)fprintf(stderr
, "cp: %s: %s\n", s
, strerror(errno
));
/********************************************************************
* Path Manipulation Routines.
********************************************************************/
* These functions manipulate paths in "path_t" structures.
* They eliminate multiple slashes in paths when they notice them, and keep
* the path non-slash terminated.
* Both path_set() and path_append() return 0 if the requested name
#define STRIP_TRAILING_SLASH(p) { \
while ((p)->p_end > (p)->p_path && (p)->p_end[-1] == '/') \
* Move specified string into path. Convert "" to "." to handle BSD
* semantics for a null path. Strip trailing slashes.
if (strlen(string
) > MAXPATHLEN
) {
(void)fprintf(stderr
, "cp: %s: name too long.\n", string
);
(void)strcpy(p
->p_path
, string
);
p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
if (p
->p_path
== p
->p_end
) {
* Append specified string to path, inserting '/' if necessary. Return a
* pointer to the old end of path for restoration.
path_append(p
, name
, len
)
* The final "+ 1" accounts for the '/' between old path and name.
if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAXPATHLEN
) {
"cp: %s/%s: name too long.\n", p
->p_path
, name
);
* This code should always be executed, since paths shouldn't
if (p
->p_end
[-1] != '/') {
(void)strncat(p
->p_end
, name
, len
);
* Restore path to previous value. (As returned by path_append.)
* Return basename of path. (Like basename(1).)
basename
= rindex(p
->p_path
, '/');
return(basename
? ++basename
: p
->p_path
);
"usage: cp [-ip] f1 f2; or: cp [-irp] f1 ... fn directory\n");