mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-04-12 15:06:44 +02:00
804 lines
20 KiB
C
804 lines
20 KiB
C
#ifdef _AIX
|
|
#pragma alloca
|
|
#endif
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
|
|
#include "cp.h"
|
|
#include "copy.h"
|
|
#include "backupfile.h"
|
|
|
|
/* On Linux (from slackware-1.2.13 to 2.0.2?) there is no lchown function.
|
|
To change ownership of symlinks, you must run chown with an effective
|
|
UID of 0. */
|
|
#ifdef __linux__
|
|
# define ROOT_CHOWN_AFFECTS_SYMLINKS
|
|
#endif
|
|
|
|
#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. */ \
|
|
&& (errno != EPERM || x->myeuid == 0))
|
|
|
|
/* Control creation of sparse files (files with holes). */
|
|
enum Sparse_type
|
|
{
|
|
/* Never create holes in DEST. */
|
|
SPARSE_NEVER,
|
|
|
|
/* This is the default. Use a crude (and sometimes inaccurate)
|
|
heuristic to determine if SOURCE has holes. If so, try to create
|
|
holes in DEST. */
|
|
SPARSE_AUTO,
|
|
|
|
/* For every sufficiently long sequence of bytes in SOURCE, try to
|
|
create a corresponding hole in DEST. There is a performance penalty
|
|
here because CP has to search for holes in SRC. But if the holes are
|
|
big enough, that penalty can be offset by the decrease in the amount
|
|
of data written to disk. */
|
|
SPARSE_ALWAYS
|
|
};
|
|
|
|
struct flag
|
|
{
|
|
/* If nonzero, copy all files except (directories and, if not dereferencing
|
|
them, symbolic links,) as if they were regular files. */
|
|
int copy_as_regular;
|
|
|
|
/* If nonzero, dereference symbolic links (copy the files they point to). */
|
|
int dereference;
|
|
|
|
/* If nonzero, remove existing destination nondirectories. */
|
|
int force;
|
|
|
|
/* If nonzero, create hard links instead of copying files.
|
|
Create destination directories as usual. */
|
|
int hard_link;
|
|
|
|
/* If nonzero, query before overwriting existing destinations
|
|
with regular files. */
|
|
int interactive;
|
|
|
|
/* If nonzero, when copying recursively, skip any subdirectories that are
|
|
on different filesystems from the one we started on. */
|
|
int one_file_system;
|
|
|
|
/* If nonzero, give the copies the original files' permissions,
|
|
ownership, and timestamps. */
|
|
int preserve;
|
|
|
|
/* If nonzero, copy directories recursively and copy special files
|
|
as themselves rather than copying their contents. */
|
|
int recursive;
|
|
|
|
/* Control creation of sparse files. */
|
|
enum Sparse_type sparse_mode;
|
|
|
|
/* If nonzero, create symbolic links instead of copying files.
|
|
Create destination directories as usual. */
|
|
int symbolic_link;
|
|
|
|
/* The bits to preserve in created files' modes. */
|
|
int umask_kill;
|
|
|
|
/* If nonzero, do not copy a nondirectory that has an existing destination
|
|
with the same or newer modification time. */
|
|
int update;
|
|
|
|
/* If nonzero, display the names of the files before copying them. */
|
|
int verbose;
|
|
|
|
/* This process's effective user ID. */
|
|
uid_t myeuid;
|
|
|
|
/* A pointer to either lstat or stat, depending on
|
|
whether dereferencing of symlinks is done. */
|
|
int (*xstat) ();
|
|
};
|
|
|
|
struct dir_list
|
|
{
|
|
struct dir_list *parent;
|
|
ino_t ino;
|
|
dev_t dev;
|
|
};
|
|
|
|
int full_write ();
|
|
int euidaccess ();
|
|
|
|
/* The invocation name of this program. */
|
|
extern char *program_name;
|
|
|
|
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.
|
|
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 flag *x)
|
|
{
|
|
char *name_space;
|
|
char *namep;
|
|
char *src_path;
|
|
char *dst_path;
|
|
int ret = 0;
|
|
|
|
errno = 0;
|
|
name_space = savedir (src_path_in, src_sb->st_size);
|
|
if (name_space == 0)
|
|
{
|
|
if (errno)
|
|
{
|
|
error (0, errno, "%s", src_path_in);
|
|
return -1;
|
|
}
|
|
else
|
|
error (1, 0, _("virtual memory exhausted"));
|
|
}
|
|
|
|
namep = name_space;
|
|
while (*namep != '\0')
|
|
{
|
|
int fn_length = strlen (namep) + 1;
|
|
|
|
dst_path = xmalloc (strlen (dst_path_in) + fn_length + 1);
|
|
src_path = xmalloc (strlen (src_path_in) + fn_length + 1);
|
|
|
|
stpcpy (stpcpy (stpcpy (src_path, src_path_in), "/"), namep);
|
|
stpcpy (stpcpy (stpcpy (dst_path, dst_path_in), "/"), namep);
|
|
|
|
ret |= copy (src_path, dst_path, new_dst, src_sb->st_dev, ancestors, x);
|
|
|
|
/* 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 += fn_length;
|
|
}
|
|
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;
|
|
long 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)
|
|
{
|
|
error (0, errno, "%s", 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, 0600);
|
|
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);
|
|
|
|
#ifdef HAVE_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)
|
|
&& (size_t) (sb.st_size / 512) > (size_t) 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)
|
|
{
|
|
#ifdef 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.
|
|
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 flag *x)
|
|
{
|
|
struct stat src_sb;
|
|
struct stat dst_sb;
|
|
int src_mode;
|
|
int src_type;
|
|
char *earlier_file;
|
|
char *dst_backup = NULL;
|
|
int fix_mode = 0;
|
|
|
|
if ((*(x->xstat)) (src_path, &src_sb))
|
|
{
|
|
error (0, errno, "%s", src_path);
|
|
return 1;
|
|
}
|
|
|
|
/* Are we crossing a file system boundary? */
|
|
if (x->one_file_system && device != 0 && device != src_sb.st_dev)
|
|
return 0;
|
|
|
|
/* 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);
|
|
|
|
/* Did we just create this file? */
|
|
|
|
if (earlier_file == &new_file)
|
|
return 0;
|
|
|
|
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
|
|
{
|
|
/* The file exists already. */
|
|
|
|
if (src_sb.st_ino == dst_sb.st_ino && src_sb.st_dev == dst_sb.st_dev)
|
|
{
|
|
if (x->hard_link)
|
|
return 0;
|
|
|
|
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 && src_sb.st_mtime <= dst_sb.st_mtime)
|
|
return 0;
|
|
}
|
|
|
|
if (!S_ISDIR (src_type) && !x->force && x->interactive)
|
|
{
|
|
if (euidaccess (dst_path, W_OK) != 0)
|
|
{
|
|
fprintf (stderr,
|
|
_("%s: overwrite `%s', overriding mode %04o? "),
|
|
program_name, dst_path,
|
|
(unsigned int) (dst_sb.st_mode & 07777));
|
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, _("%s: overwrite `%s'? "),
|
|
program_name, dst_path);
|
|
}
|
|
if (!yesno ())
|
|
return 0;
|
|
}
|
|
|
|
if (backup_type != none && !S_ISDIR (dst_sb.st_mode))
|
|
{
|
|
char *tmp_backup = find_backup_file_name (dst_path);
|
|
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))
|
|
{
|
|
error (0, 0,
|
|
_("backing up `%s' would destroy source; `%s' not copied"),
|
|
dst_path, src_path);
|
|
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;
|
|
}
|
|
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, 0700))
|
|
{
|
|
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);
|
|
return 1;
|
|
}
|
|
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\n", src_path, dst_path);
|
|
|
|
/* 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)
|
|
{
|
|
if (link (earlier_file, dst_path))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
goto un_backup;
|
|
}
|
|
return 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) | 0700))
|
|
{
|
|
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);
|
|
}
|
|
|
|
/* Copy the contents of the directory. */
|
|
|
|
if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir, x))
|
|
return 1;
|
|
}
|
|
#ifdef S_ISLNK
|
|
else if (x->symbolic_link)
|
|
{
|
|
if (*src_path == '/'
|
|
|| (!strncmp (dst_path, "./", 2) && strchr (dst_path + 2, '/') == 0)
|
|
|| strchr (dst_path, '/') == 0)
|
|
{
|
|
if (symlink (src_path, dst_path))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
error (0, 0,
|
|
_("%s: can make relative symbolic links only in current directory"),
|
|
dst_path);
|
|
goto un_backup;
|
|
}
|
|
}
|
|
#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, src_mode & x->umask_kill))
|
|
{
|
|
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, src_mode & x->umask_kill, 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))
|
|
{
|
|
error (0, errno, _("cannot create symbolic link `%s'"), dst_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
if (x->preserve)
|
|
{
|
|
/* Preserve the owner and group of the just-`copied'
|
|
symbolic link, if possible. */
|
|
#ifdef HAVE_LCHOWN
|
|
if (DO_CHOWN (lchown, dst_path, src_sb.st_uid, src_sb.st_gid))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
goto un_backup;
|
|
}
|
|
#else
|
|
# ifdef ROOT_CHOWN_AFFECTS_SYMLINKS
|
|
if (x->myeuid == 0)
|
|
{
|
|
if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
goto un_backup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* FIXME: maybe give a diagnostic: you must be root
|
|
to preserve ownership and group of symlinks. */
|
|
}
|
|
# 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
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
error (0, 0, _("%s: unknown file type"), src_path);
|
|
goto un_backup;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
struct utimbuf utb;
|
|
|
|
utb.actime = src_sb.st_atime;
|
|
utb.modtime = src_sb.st_mtime;
|
|
|
|
if (utime (dst_path, &utb))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return 1;
|
|
}
|
|
|
|
if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if ((x->preserve || new_dst)
|
|
&& (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type)))
|
|
{
|
|
if (chmod (dst_path, src_mode & x->umask_kill))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
else if (fix_mode)
|
|
{
|
|
/* Reset the temporarily changed mode. */
|
|
if (chmod (dst_path, dst_sb.st_mode))
|
|
{
|
|
error (0, errno, "%s", dst_path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
un_backup:
|
|
if (dst_backup)
|
|
{
|
|
if (rename (dst_backup, dst_path))
|
|
error (0, errno, _("cannot un-backup `%s'"), dst_path);
|
|
}
|
|
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
|
|
not to exist (e.g., because its parent directory was just created);
|
|
NONEXISTENT_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.
|
|
Return 0 if successful, 1 if an error occurs. */
|
|
|
|
int
|
|
copy (const char *src_path, const char *dst_path, int nonexistent_dst,
|
|
const struct flag *x)
|
|
{
|
|
copy_internal (src_path, dst_path, ... , x);
|
|
}
|