mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-03-26 06:44:51 +02:00
1037 lines
27 KiB
C
1037 lines
27 KiB
C
/* copy.c -- core functions for copying files and directories
|
|
Copyright (C) 89, 90, 91, 1995-2000 Free Software Foundation.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
/* Extracted from cp.c and librarified by Jim Meyering. */
|
|
|
|
#ifdef _AIX
|
|
#pragma alloca
|
|
#endif
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "system.h"
|
|
#include "error.h"
|
|
#include "backupfile.h"
|
|
#include "savedir.h"
|
|
#include "copy.h"
|
|
#include "cp-hash.h"
|
|
#include "dirname.h"
|
|
#include "path-concat.h"
|
|
#include "same.h"
|
|
|
|
#define DO_CHOWN(Chown, File, New_uid, New_gid) \
|
|
(Chown ((File), (x->myeuid == 0 ? (New_uid) : x->myeuid), (New_gid)) \
|
|
/* If non-root uses -p, it's ok if we can't preserve ownership. \
|
|
But root probably wants to know, e.g. if NFS disallows it, \
|
|
or if the target system doesn't support file ownership. */ \
|
|
&& ((errno != EPERM && errno != EINVAL) || x->myeuid == 0))
|
|
|
|
struct dir_list
|
|
{
|
|
struct dir_list *parent;
|
|
ino_t ino;
|
|
dev_t dev;
|
|
};
|
|
|
|
int full_write ();
|
|
int euidaccess ();
|
|
int yesno ();
|
|
|
|
static int copy_internal PARAMS ((const char *src_path, const char *dst_path,
|
|
int new_dst, dev_t device,
|
|
struct dir_list *ancestors,
|
|
const struct cp_options *x,
|
|
int move_mode,
|
|
int *copy_into_self,
|
|
int *rename_succeeded));
|
|
|
|
/* The invocation name of this program. */
|
|
extern char *program_name;
|
|
|
|
/* Encapsulate selection of the file mode to be applied to
|
|
new non-directories. */
|
|
|
|
static mode_t
|
|
new_nondir_mode (const struct cp_options *option, mode_t mode)
|
|
{
|
|
/* In some applications (e.g., install), use precisely the
|
|
specified mode. */
|
|
if (option->set_mode)
|
|
return option->mode;
|
|
|
|
/* In others (e.g., cp, mv), apply the user's umask. */
|
|
return mode & option->umask_kill;
|
|
}
|
|
|
|
/* FIXME: describe */
|
|
/* FIXME: rewrite this to use a hash table so we avoid the quadratic
|
|
performance hit that's probably noticeable only on trees deeper
|
|
than a few hundred levels. See use of active_dir_map in remove.c */
|
|
|
|
static int
|
|
is_ancestor (const struct stat *sb, const struct dir_list *ancestors)
|
|
{
|
|
while (ancestors != 0)
|
|
{
|
|
if (ancestors->ino == sb->st_ino && ancestors->dev == sb->st_dev)
|
|
return 1;
|
|
ancestors = ancestors->parent;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Read the contents of the directory SRC_PATH_IN, and recursively
|
|
copy the contents to DST_PATH_IN. NEW_DST is nonzero if
|
|
DST_PATH_IN is a directory that was created previously in the
|
|
recursion. SRC_SB and ANCESTORS describe SRC_PATH_IN.
|
|
Set *COPY_INTO_SELF to nonzero if SRC_PATH_IN is a parent of
|
|
(or the same as) DST_PATH_IN; otherwise, set it to zero.
|
|
Return 0 if successful, -1 if an error occurs. */
|
|
|
|
static int
|
|
copy_dir (const char *src_path_in, const char *dst_path_in, int new_dst,
|
|
const struct stat *src_sb, struct dir_list *ancestors,
|
|
const struct cp_options *x, int *copy_into_self)
|
|
{
|
|
char *name_space;
|
|
char *namep;
|
|
int ret = 0;
|
|
|
|
name_space = savedir (src_path_in, src_sb->st_size);
|
|
if (name_space == NULL)
|
|
{
|
|
error (0, errno, "%s", src_path_in);
|
|
return -1;
|
|
}
|
|
|
|
namep = name_space;
|
|
while (*namep != '\0')
|
|
{
|
|
int local_copy_into_self;
|
|
char *src_path = path_concat (src_path_in, namep, NULL);
|
|
char *dst_path = path_concat (dst_path_in, namep, NULL);
|
|
|
|
if (dst_path == NULL || src_path == NULL)
|
|
error (1, 0, _("virtual memory exhausted"));
|
|
|
|
ret |= copy_internal (src_path, dst_path, new_dst, src_sb->st_dev,
|
|
ancestors, x, 0, &local_copy_into_self, NULL);
|
|
*copy_into_self |= local_copy_into_self;
|
|
|
|
/* Free the memory for `src_path'. The memory for `dst_path'
|
|
cannot be deallocated, since it is used to create multiple
|
|
hard links. */
|
|
|
|
free (src_path);
|
|
|
|
namep += strlen (namep) + 1;
|
|
}
|
|
free (name_space);
|
|
return -ret;
|
|
}
|
|
|
|
/* Copy a regular file from SRC_PATH to DST_PATH.
|
|
If the source file contains holes, copies holes and blocks of zeros
|
|
in the source file as holes in the destination file.
|
|
(Holes are read as zeroes by the `read' system call.)
|
|
Return 0 if successful, -1 if an error occurred.
|
|
FIXME: describe sparse_mode. */
|
|
|
|
static int
|
|
copy_reg (const char *src_path, const char *dst_path,
|
|
enum Sparse_type sparse_mode)
|
|
{
|
|
char *buf;
|
|
int buf_size;
|
|
int dest_desc;
|
|
int source_desc;
|
|
int n_read;
|
|
struct stat sb;
|
|
char *cp;
|
|
int *ip;
|
|
int return_val = 0;
|
|
off_t n_read_total = 0;
|
|
int last_write_made_hole = 0;
|
|
int make_holes = (sparse_mode == SPARSE_ALWAYS);
|
|
|
|
source_desc = open (src_path, O_RDONLY);
|
|
if (source_desc < 0)
|
|
{
|
|
/* If SRC_PATH doesn't exist, then chances are good that the
|
|
user did something like this `cp --backup foo foo': and foo
|
|
existed to start with, but copy_internal renamed DST_PATH
|
|
with the backup suffix, thus also renaming SRC_PATH. */
|
|
if (errno == ENOENT)
|
|
error (0, 0, _("`%s' and `%s' are the same file"),
|
|
src_path, dst_path);
|
|
else
|
|
error (0, errno, _("cannot open `%s' for reading"), src_path);
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Create the new regular file with small permissions initially,
|
|
to not create a security hole. */
|
|
|
|
dest_desc = open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
|
if (dest_desc < 0)
|
|
{
|
|
error (0, errno, _("cannot create regular file `%s'"), dst_path);
|
|
return_val = -1;
|
|
goto ret2;
|
|
}
|
|
|
|
/* Find out the optimal buffer size. */
|
|
|
|
if (fstat (dest_desc, &sb))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return_val = -1;
|
|
goto ret;
|
|
}
|
|
|
|
buf_size = ST_BLKSIZE (sb);
|
|
|
|
#if HAVE_STRUCT_STAT_ST_BLOCKS
|
|
if (sparse_mode == SPARSE_AUTO && S_ISREG (sb.st_mode))
|
|
{
|
|
/* Use a heuristic to determine whether SRC_PATH contains any
|
|
sparse blocks. */
|
|
|
|
if (fstat (source_desc, &sb))
|
|
{
|
|
error (0, errno, "%s", src_path);
|
|
return_val = -1;
|
|
goto ret;
|
|
}
|
|
|
|
/* If the file has fewer blocks than would normally
|
|
be needed for a file of its size, then
|
|
at least one of the blocks in the file is a hole. */
|
|
if (S_ISREG (sb.st_mode)
|
|
&& sb.st_size / ST_NBLOCKSIZE > ST_NBLOCKS (sb))
|
|
make_holes = 1;
|
|
}
|
|
#endif
|
|
|
|
/* Make a buffer with space for a sentinel at the end. */
|
|
|
|
buf = (char *) alloca (buf_size + sizeof (int));
|
|
|
|
for (;;)
|
|
{
|
|
n_read = read (source_desc, buf, buf_size);
|
|
if (n_read < 0)
|
|
{
|
|
#ifdef EINTR
|
|
if (errno == EINTR)
|
|
continue;
|
|
#endif
|
|
error (0, errno, "%s", src_path);
|
|
return_val = -1;
|
|
goto ret;
|
|
}
|
|
if (n_read == 0)
|
|
break;
|
|
|
|
n_read_total += n_read;
|
|
|
|
ip = 0;
|
|
if (make_holes)
|
|
{
|
|
buf[n_read] = 1; /* Sentinel to stop loop. */
|
|
|
|
/* Find first nonzero *word*, or the word with the sentinel. */
|
|
|
|
ip = (int *) buf;
|
|
while (*ip++ == 0)
|
|
;
|
|
|
|
/* Find the first nonzero *byte*, or the sentinel. */
|
|
|
|
cp = (char *) (ip - 1);
|
|
while (*cp++ == 0)
|
|
;
|
|
|
|
/* If we found the sentinel, the whole input block was zero,
|
|
and we can make a hole. */
|
|
|
|
if (cp > buf + n_read)
|
|
{
|
|
/* Make a hole. */
|
|
if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L)
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return_val = -1;
|
|
goto ret;
|
|
}
|
|
last_write_made_hole = 1;
|
|
}
|
|
else
|
|
/* Clear to indicate that a normal write is needed. */
|
|
ip = 0;
|
|
}
|
|
if (ip == 0)
|
|
{
|
|
if (full_write (dest_desc, buf, n_read) < 0)
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return_val = -1;
|
|
goto ret;
|
|
}
|
|
last_write_made_hole = 0;
|
|
}
|
|
}
|
|
|
|
/* If the file ends with a `hole', something needs to be written at
|
|
the end. Otherwise the kernel would truncate the file at the end
|
|
of the last write operation. */
|
|
|
|
if (last_write_made_hole)
|
|
{
|
|
#if HAVE_FTRUNCATE
|
|
/* Write a null character and truncate it again. */
|
|
if (full_write (dest_desc, "", 1) < 0
|
|
|| ftruncate (dest_desc, n_read_total) < 0)
|
|
#else
|
|
/* Seek backwards one character and write a null. */
|
|
if (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L
|
|
|| full_write (dest_desc, "", 1) < 0)
|
|
#endif
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return_val = -1;
|
|
}
|
|
}
|
|
|
|
ret:
|
|
if (close (dest_desc) < 0)
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return_val = -1;
|
|
}
|
|
ret2:
|
|
if (close (source_desc) < 0)
|
|
{
|
|
error (0, errno, "%s", src_path);
|
|
return_val = -1;
|
|
}
|
|
|
|
return return_val;
|
|
}
|
|
|
|
/* Copy the file SRC_PATH to the file DST_PATH. The files may be of
|
|
any type. NEW_DST should be nonzero if the file DST_PATH cannot
|
|
exist because its parent directory was just created; NEW_DST should
|
|
be zero if DST_PATH might already exist. DEVICE is the device
|
|
number of the parent directory, or 0 if the parent of this file is
|
|
not known. ANCESTORS points to a linked, null terminated list of
|
|
devices and inodes of parent directories of SRC_PATH.
|
|
Set *COPY_INTO_SELF to nonzero if SRC_PATH is a parent of (or the
|
|
same as) DST_PATH; otherwise, set it to zero.
|
|
Return 0 if successful, 1 if an error occurs. */
|
|
|
|
static int
|
|
copy_internal (const char *src_path, const char *dst_path,
|
|
int new_dst,
|
|
dev_t device,
|
|
struct dir_list *ancestors,
|
|
const struct cp_options *x,
|
|
int move_mode,
|
|
int *copy_into_self,
|
|
int *rename_succeeded)
|
|
{
|
|
struct stat src_sb;
|
|
struct stat dst_sb;
|
|
int src_mode;
|
|
int src_type;
|
|
char *earlier_file;
|
|
char *dst_backup = NULL;
|
|
int backup_succeeded = 0;
|
|
int fix_mode = 0;
|
|
int rename_errno;
|
|
int delayed_fail;
|
|
|
|
if (move_mode && rename_succeeded)
|
|
*rename_succeeded = 0;
|
|
|
|
*copy_into_self = 0;
|
|
if ((*(x->xstat)) (src_path, &src_sb))
|
|
{
|
|
error (0, errno, "%s", src_path);
|
|
return 1;
|
|
}
|
|
|
|
/* We wouldn't insert a node unless nlink > 1, except that we need to
|
|
find created files so as to not copy infinitely if a directory is
|
|
copied into itself. */
|
|
|
|
earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev);
|
|
|
|
src_mode = src_sb.st_mode;
|
|
src_type = src_sb.st_mode;
|
|
|
|
if (S_ISDIR (src_type) && !x->recursive)
|
|
{
|
|
error (0, 0, _("%s: omitting directory"), src_path);
|
|
return 1;
|
|
}
|
|
|
|
if (!new_dst)
|
|
{
|
|
if ((*(x->xstat)) (dst_path, &dst_sb))
|
|
{
|
|
if (errno != ENOENT)
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
new_dst = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int same;
|
|
|
|
/* The destination file exists already. */
|
|
|
|
same = (SAME_INODE (src_sb, dst_sb));
|
|
|
|
#ifdef S_ISLNK
|
|
/* If we're preserving symlinks (--no-dereference) and either
|
|
file is a symlink, use stat (not xstat) to see if they refer
|
|
to the same file. */
|
|
if (!same
|
|
|
|
/* If we'll remove DST_PATH first, then this doesn't matter. */
|
|
&& ! x->force
|
|
|
|
/* Allow them to be the same (and don't set `same') if we're
|
|
in move mode and the target is a symlink. That is ok, since
|
|
we remove any existing destination file before opening it --
|
|
via `rename' if they're on the same file system,
|
|
via `unlink(DST_PATH)' otherwise. */
|
|
&& !(move_mode
|
|
&& S_ISLNK (dst_sb.st_mode))
|
|
|
|
/* If we're making a backup, we'll detect the problem case in
|
|
copy_reg because SRC_PATH will no longer exist. Allowing
|
|
the test to be deferred lets cp do some useful things.
|
|
But when creating hardlinks and SRC_PATH is a symlink
|
|
but DST_PATH is not we must test anyway. */
|
|
&& (x->backup_type == none
|
|
|| (x->hard_link
|
|
&& S_ISLNK (src_sb.st_mode)
|
|
&& !S_ISLNK (dst_sb.st_mode)))
|
|
&& !x->dereference
|
|
&& (S_ISLNK (dst_sb.st_mode) ^ S_ISLNK (src_sb.st_mode)))
|
|
{
|
|
struct stat dst2_sb;
|
|
struct stat src2_sb;
|
|
if (stat (dst_path, &dst2_sb) == 0
|
|
&& stat (src_path, &src2_sb) == 0
|
|
&& SAME_INODE (src2_sb, dst2_sb))
|
|
{
|
|
same = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (same)
|
|
{
|
|
if (x->hard_link)
|
|
return 0;
|
|
|
|
if (x->backup_type == none
|
|
&& (!x->force || same_name (src_path, dst_path)))
|
|
{
|
|
error (0, 0, _("`%s' and `%s' are the same file"),
|
|
src_path, dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!S_ISDIR (src_type))
|
|
{
|
|
if (S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
error (0, 0,
|
|
_("%s: cannot overwrite directory with non-directory"),
|
|
dst_path);
|
|
return 1;
|
|
}
|
|
|
|
if (x->update && MTIME_CMP (src_sb, dst_sb) <= 0)
|
|
return 0;
|
|
}
|
|
|
|
if (!S_ISDIR (src_type) && x->interactive)
|
|
{
|
|
if (euidaccess (dst_path, W_OK) != 0 && x->force)
|
|
{
|
|
fprintf (stderr,
|
|
_("%s: overwrite `%s', overriding mode %04lo? "),
|
|
program_name, dst_path,
|
|
(unsigned long) (dst_sb.st_mode & CHMOD_MODE_BITS));
|
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, _("%s: overwrite `%s'? "),
|
|
program_name, dst_path);
|
|
}
|
|
if (!yesno ())
|
|
return (move_mode ? 1 : 0);
|
|
}
|
|
|
|
if (move_mode)
|
|
{
|
|
/* In move_mode, DEST may not be an existing directory. */
|
|
if (S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
error (0, 0, _("%s: cannot overwrite directory"), dst_path);
|
|
return 1;
|
|
}
|
|
|
|
/* Don't allow user to move a directory onto a non-directory. */
|
|
if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
error (0, 0,
|
|
_("cannot move directory onto non-directory: %s -> %s"),
|
|
src_path, dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (x->backup_type != none && !S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
char *tmp_backup = find_backup_file_name (dst_path,
|
|
x->backup_type);
|
|
if (tmp_backup == NULL)
|
|
error (1, 0, _("virtual memory exhausted"));
|
|
|
|
/* Detect (and fail) when creating the backup file would
|
|
destroy the source file. Before, running the commands
|
|
cd /tmp; rm -f a a~; : > a; echo A > a~; cp -b -V simple a~ a
|
|
would leave two zero-length files: a and a~. */
|
|
if (STREQ (tmp_backup, src_path))
|
|
{
|
|
const char *fmt;
|
|
fmt = (move_mode
|
|
? _("backing up `%s' would destroy source; `%s' not moved")
|
|
: _("backing up `%s' would destroy source; `%s' not copied"));
|
|
error (0, 0, fmt, dst_path, src_path);
|
|
free (tmp_backup);
|
|
return 1;
|
|
}
|
|
|
|
dst_backup = (char *) alloca (strlen (tmp_backup) + 1);
|
|
strcpy (dst_backup, tmp_backup);
|
|
free (tmp_backup);
|
|
if (rename (dst_path, dst_backup))
|
|
{
|
|
if (errno != ENOENT)
|
|
{
|
|
error (0, errno, _("cannot backup `%s'"), dst_path);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
dst_backup = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
backup_succeeded = 1;
|
|
}
|
|
new_dst = 1;
|
|
}
|
|
else if (x->force)
|
|
{
|
|
if (S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
/* Temporarily change mode to allow overwriting. */
|
|
if (euidaccess (dst_path, W_OK | X_OK) != 0)
|
|
{
|
|
if (chmod (dst_path, S_IRWXU))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return 1;
|
|
}
|
|
else
|
|
fix_mode = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (unlink (dst_path) && errno != ENOENT)
|
|
{
|
|
error (0, errno, _("cannot remove old link to `%s'"),
|
|
dst_path);
|
|
if (x->failed_unlink_is_fatal)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
new_dst = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If the source is a directory, we don't always create the destination
|
|
directory. So --verbose should not announce anything until we're
|
|
sure we'll create a directory. */
|
|
if (x->verbose && !S_ISDIR (src_type))
|
|
{
|
|
printf ("%s -> %s", src_path, dst_path);
|
|
if (backup_succeeded)
|
|
printf (_(" (backup: %s)"), dst_backup);
|
|
putchar ('\n');
|
|
}
|
|
|
|
/* Did we copy this inode somewhere else (in this command line argument)
|
|
and therefore this is a second hard link to the inode? */
|
|
|
|
if (!x->dereference && src_sb.st_nlink > 1 && earlier_file)
|
|
{
|
|
/* Avoid damaging the destination filesystem by refusing to preserve
|
|
hard-linked directories (which are found at least in Netapp snapshot
|
|
directories). */
|
|
if (S_ISDIR (src_type))
|
|
{
|
|
error (0, 0, _("won't create hard link `%s' to directory `%s'"),
|
|
dst_path, earlier_file);
|
|
goto un_backup;
|
|
}
|
|
|
|
if (link (earlier_file, dst_path))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (move_mode)
|
|
{
|
|
if (rename (src_path, dst_path) == 0)
|
|
{
|
|
if (x->verbose && S_ISDIR (src_type))
|
|
printf ("%s -> %s\n", src_path, dst_path);
|
|
if (rename_succeeded)
|
|
*rename_succeeded = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* FIXME: someday, consider what to do when moving a directory into
|
|
itself but when source and destination are on different devices. */
|
|
|
|
/* This happens when attempting to rename a directory to a
|
|
subdirectory of itself. */
|
|
if (errno == EINVAL
|
|
|
|
/* When src_path is on an NFS file system, some types of
|
|
clients, e.g., SunOS4.1.4 and IRIX-5.3, set errno to EIO
|
|
instead. Testing for this here risks misinterpreting a real
|
|
I/O error as an attempt to move a directory into itself, so
|
|
FIXME: consider not doing this. */
|
|
|| errno == EIO
|
|
|
|
/* And with SunOS-4.1.4 client and OpenBSD-2.3 server,
|
|
we get ENOTEMPTY. */
|
|
|| errno == ENOTEMPTY)
|
|
{
|
|
/* FIXME: this is a little fragile in that it relies on rename(2)
|
|
failing with a specific errno value. Expect problems on
|
|
non-POSIX systems. */
|
|
*copy_into_self = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Ignore other types of failure (e.g. EXDEV), since the following
|
|
code will try to perform a copy, then remove. */
|
|
|
|
/* Save this value of errno to use in case the unlink fails. */
|
|
rename_errno = errno;
|
|
|
|
/* The rename attempt has failed. Remove any existing destination
|
|
file so that a cross-device `mv' acts as if it were really using
|
|
the rename syscall. */
|
|
if (unlink (dst_path) && errno != ENOENT)
|
|
{
|
|
/* Use the value of errno from the failed rename. */
|
|
error (0, rename_errno, _("cannot move `%s' to `%s'"),
|
|
src_path, dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
delayed_fail = 0;
|
|
if (S_ISDIR (src_type))
|
|
{
|
|
struct dir_list *dir;
|
|
|
|
/* If this directory has been copied before during the
|
|
recursion, there is a symbolic link to an ancestor
|
|
directory of the symbolic link. It is impossible to
|
|
continue to copy this, unless we've got an infinite disk. */
|
|
|
|
if (is_ancestor (&src_sb, ancestors))
|
|
{
|
|
error (0, 0, _("%s: cannot copy cyclic symbolic link"), src_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
/* Insert the current directory in the list of parents. */
|
|
|
|
dir = (struct dir_list *) alloca (sizeof (struct dir_list));
|
|
dir->parent = ancestors;
|
|
dir->ino = src_sb.st_ino;
|
|
dir->dev = src_sb.st_dev;
|
|
|
|
if (new_dst || !S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
/* Create the new directory writable and searchable, so
|
|
we can create new entries in it. */
|
|
|
|
if (mkdir (dst_path, (src_mode & x->umask_kill) | S_IRWXU))
|
|
{
|
|
error (0, errno, _("cannot create directory `%s'"), dst_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
/* Insert the created directory's inode and device
|
|
numbers into the search structure, so that we can
|
|
avoid copying it again. */
|
|
|
|
if (remember_created (dst_path))
|
|
goto un_backup;
|
|
|
|
if (x->verbose)
|
|
printf ("%s -> %s\n", src_path, dst_path);
|
|
}
|
|
|
|
/* Are we crossing a file system boundary? */
|
|
if (x->one_file_system && device != 0 && device != src_sb.st_dev)
|
|
return 0;
|
|
|
|
/* Copy the contents of the directory. */
|
|
|
|
if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir, x,
|
|
copy_into_self))
|
|
{
|
|
/* Don't just return here -- otherwise, the failure to read a
|
|
single file in a source directory would cause the containing
|
|
destination directory not to have owner/perms set properly. */
|
|
delayed_fail = 1;
|
|
}
|
|
}
|
|
#ifdef S_ISLNK
|
|
else if (x->symbolic_link)
|
|
{
|
|
if (*src_path != '/')
|
|
{
|
|
/* Check that DST_PATH denotes a file in the current directory. */
|
|
struct stat dot_sb;
|
|
struct stat dst_parent_sb;
|
|
char *dst_parent;
|
|
int in_current_dir;
|
|
|
|
dst_parent = dir_name (dst_path);
|
|
if (dst_parent == NULL)
|
|
error (1, 0, _("virtual memory exhausted"));
|
|
|
|
in_current_dir = (STREQ (".", dst_parent)
|
|
/* If either stat call fails, it's ok not to report
|
|
the failure and say dst_path is in the current
|
|
directory. Other things will fail later. */
|
|
|| stat (".", &dot_sb)
|
|
|| stat (dst_parent, &dst_parent_sb)
|
|
|| SAME_INODE (dot_sb, dst_parent_sb));
|
|
free (dst_parent);
|
|
|
|
if (! in_current_dir)
|
|
{
|
|
error (0, 0,
|
|
_("%s: can make relative symbolic links only in current directory"),
|
|
dst_path);
|
|
goto un_backup;
|
|
}
|
|
}
|
|
if (symlink (src_path, dst_path))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
else if (x->hard_link)
|
|
{
|
|
if (link (src_path, dst_path))
|
|
{
|
|
error (0, errno, _("cannot create link `%s'"), dst_path);
|
|
goto un_backup;
|
|
}
|
|
return 0;
|
|
}
|
|
else if (S_ISREG (src_type)
|
|
|| (x->copy_as_regular && !S_ISDIR (src_type)
|
|
#ifdef S_ISLNK
|
|
&& !S_ISLNK (src_type)
|
|
#endif
|
|
))
|
|
{
|
|
if (copy_reg (src_path, dst_path, x->sparse_mode))
|
|
goto un_backup;
|
|
}
|
|
else
|
|
#ifdef S_ISFIFO
|
|
if (S_ISFIFO (src_type))
|
|
{
|
|
if (mkfifo (dst_path, new_nondir_mode (x, src_mode)))
|
|
{
|
|
error (0, errno, _("cannot create fifo `%s'"), dst_path);
|
|
goto un_backup;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (S_ISBLK (src_type) || S_ISCHR (src_type)
|
|
#ifdef S_ISSOCK
|
|
|| S_ISSOCK (src_type)
|
|
#endif
|
|
)
|
|
{
|
|
if (mknod (dst_path, new_nondir_mode (x, src_mode), src_sb.st_rdev))
|
|
{
|
|
error (0, errno, _("cannot create special file `%s'"), dst_path);
|
|
goto un_backup;
|
|
}
|
|
}
|
|
else
|
|
#ifdef S_ISLNK
|
|
if (S_ISLNK (src_type))
|
|
{
|
|
char *link_val;
|
|
int link_size;
|
|
|
|
link_val = (char *) alloca (PATH_MAX + 2);
|
|
link_size = readlink (src_path, link_val, PATH_MAX + 1);
|
|
if (link_size < 0)
|
|
{
|
|
error (0, errno, _("cannot read symbolic link `%s'"), src_path);
|
|
goto un_backup;
|
|
}
|
|
link_val[link_size] = '\0';
|
|
|
|
if (symlink (link_val, dst_path))
|
|
{
|
|
int saved_errno = errno;
|
|
int same_link = 0;
|
|
if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode))
|
|
{
|
|
/* See if the destination is already the desired symlink. */
|
|
char *dest_link_name = (char *) alloca (PATH_MAX + 2);
|
|
int dest_link_len = readlink (dst_path, dest_link_name,
|
|
PATH_MAX + 1);
|
|
if (dest_link_len > 0)
|
|
{
|
|
dest_link_name[dest_link_len] = '\0';
|
|
if (STREQ (dest_link_name, link_val))
|
|
same_link = 1;
|
|
}
|
|
}
|
|
|
|
if (! same_link)
|
|
{
|
|
error (0, saved_errno, _("cannot create symbolic link `%s'"),
|
|
dst_path);
|
|
goto un_backup;
|
|
}
|
|
}
|
|
|
|
if (x->preserve_owner_and_group)
|
|
{
|
|
/* Preserve the owner and group of the just-`copied'
|
|
symbolic link, if possible. */
|
|
# if HAVE_LCHOWN
|
|
if (DO_CHOWN (lchown, dst_path, src_sb.st_uid, src_sb.st_gid))
|
|
{
|
|
error (0, errno, _("preserving ownership for %s"), dst_path);
|
|
goto un_backup;
|
|
}
|
|
# else
|
|
/* Can't preserve ownership of symlinks.
|
|
FIXME: maybe give a warning or even error for symlinks
|
|
in directories with the sticky bit set -- there, not
|
|
preserving owner/group is a potential security problem. */
|
|
# endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
error (0, 0, _("%s: unknown file type"), src_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
/* POSIX says that `cp -p' must restore the following:
|
|
- permission bits
|
|
- setuid, setgid bits
|
|
- owner and group
|
|
If it fails to restore any of those, we may give a warning but
|
|
the destination must not be removed.
|
|
FIXME: implement the above. */
|
|
|
|
/* Adjust the times (and if possible, ownership) for the copy.
|
|
chown turns off set[ug]id bits for non-root,
|
|
so do the chmod last. */
|
|
|
|
if (x->preserve_timestamps)
|
|
{
|
|
struct utimbuf utb;
|
|
|
|
/* There's currently no interface to set file timestamps with
|
|
better than 1-second resolution, so discard any fractional
|
|
part of the source timestamp. */
|
|
|
|
utb.actime = src_sb.st_atime;
|
|
utb.modtime = src_sb.st_mtime;
|
|
|
|
if (utime (dst_path, &utb))
|
|
{
|
|
error (0, errno, _("preserving times for %s"), dst_path);
|
|
if (x->require_preserve)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (x->preserve_owner_and_group)
|
|
{
|
|
if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid))
|
|
{
|
|
error (0, errno, _("preserving ownership for %s"), dst_path);
|
|
if (x->require_preserve)
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (x->set_mode)
|
|
{
|
|
/* This is so install's -m MODE option works. */
|
|
if (chmod (dst_path, x->mode))
|
|
{
|
|
error (0, errno, _("setting permissions for %s"), dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
else if ((x->preserve_chmod_bits || new_dst)
|
|
&& (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type)))
|
|
{
|
|
mode_t dst_mode = src_mode;
|
|
|
|
/* Honor the umask for `cp', but not for `mv'. */
|
|
if (!x->move_mode)
|
|
dst_mode &= x->umask_kill;
|
|
|
|
if (chmod (dst_path, dst_mode))
|
|
{
|
|
error (0, errno, _("preserving permissions for %s"), dst_path);
|
|
if (x->require_preserve)
|
|
return 1;
|
|
}
|
|
}
|
|
else if (fix_mode)
|
|
{
|
|
/* Reset the temporarily changed mode. */
|
|
if (chmod (dst_path, dst_sb.st_mode))
|
|
{
|
|
error (0, errno, _("restoring permissions of %s"), dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return delayed_fail;
|
|
|
|
un_backup:
|
|
if (dst_backup)
|
|
{
|
|
if (rename (dst_backup, dst_path))
|
|
error (0, errno, _("cannot un-backup `%s'"), dst_path);
|
|
else
|
|
{
|
|
if (x->verbose)
|
|
printf (_("%s -> %s (unbackup)\n"), dst_backup, dst_path);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
valid_options (const struct cp_options *co)
|
|
{
|
|
assert (co != NULL);
|
|
|
|
assert (VALID_BACKUP_TYPE (co->backup_type));
|
|
|
|
/* FIXME: for some reason this assertion always fails,
|
|
at least on Solaris2.5.1. Just disable it for now. */
|
|
/* assert (co->xstat == lstat || co->xstat == stat); */
|
|
|
|
/* Make sure xstat and dereference are consistent. */
|
|
/* FIXME */
|
|
|
|
assert (VALID_SPARSE_MODE (co->sparse_mode));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Copy the file SRC_PATH to the file DST_PATH. The files may be of
|
|
any type. NONEXISTENT_DST should be nonzero if the file DST_PATH
|
|
is known not to exist (e.g., because its parent directory was just
|
|
created); NONEXISTENT_DST should be zero if DST_PATH might already
|
|
exist. OPTIONS is ... FIXME-describe
|
|
Set *COPY_INTO_SELF to nonzero if SRC_PATH is a parent of (or the
|
|
same as) DST_PATH; otherwise, set it to zero.
|
|
Return 0 if successful, 1 if an error occurs. */
|
|
|
|
int
|
|
copy (const char *src_path, const char *dst_path,
|
|
int nonexistent_dst, const struct cp_options *options,
|
|
int *copy_into_self, int *rename_succeeded)
|
|
{
|
|
/* move_mode is set to the value from the `options' parameter for the
|
|
first copy_internal call. For all subsequent calls (if any), it must
|
|
be zero. */
|
|
int move_mode = options->move_mode;
|
|
|
|
assert (valid_options (options));
|
|
return copy_internal (src_path, dst_path, nonexistent_dst, 0, NULL,
|
|
options, move_mode, copy_into_self, rename_succeeded);
|
|
}
|