/* Check if FILE_NAME already exists and make a backup of it right now. Return success (nonzero) only if the backup is either unneeded, or successful. For now, directories are considered to never need backup. If THIS_IS_THE_ARCHIVE is nonzero, this is the archive and so, we do not have to backup block or character devices, nor remote entities. */ bool maybe_backup_file (const char *file_name, bool this_is_the_archive) { struct stat file_stat; assign_string (&before_backup_name, file_name); /* A run situation may exist between Emacs or other GNU programs trying to make a backup for the same file simultaneously. If theoretically possible, real problems are unlikely. Doing any better would require a convention, GNU-wide, for all programs doing backups. */ assign_string (&after_backup_name, 0); /* Check if we really need to backup the file. */ if (this_is_the_archive && _remdev (file_name)) return true; if (deref_stat (file_name, &file_stat) != 0) { if (errno == ENOENT) return true; stat_error (file_name); return false; } if (S_ISDIR (file_stat.st_mode)) return true; if (this_is_the_archive && (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode))) return true; after_backup_name = find_backup_file_name (file_name, backup_type); if (! after_backup_name) xalloc_die (); if (renameat (chdir_fd, before_backup_name, chdir_fd, after_backup_name) == 0) { if (verbose_option) fprintf (stdlis, _("Renaming %s to %s\n"), quote_n (0, before_backup_name), quote_n (1, after_backup_name)); return true; } else { /* The backup operation failed. */ int e = errno; ERROR ((0, e, _("%s: Cannot rename to %s"), quotearg_colon (before_backup_name), quote_n (1, after_backup_name))); assign_string (&after_backup_name, 0); return false; } }
int maybe_backup_file (const char *path, int archive) { struct L_STAT file_stat; /* Check if we really need to backup the file. */ if (archive && _remdev (path)) return 1; if (L_STAT (path, &file_stat)) { if (errno == ENOENT) return 1; ERROR ((0, errno, "%s", path)); return 0; } if (S_ISDIR (file_stat.st_mode)) return 1; #ifdef S_ISBLK if (archive && S_ISBLK (file_stat.st_mode)) return 1; #endif #ifdef S_ISCHR if (archive && S_ISCHR (file_stat.st_mode)) return 1; #endif assign_string (&before_backup_name, path); /* A run situation may exist between Emacs or other GNU programs trying to make a backup for the same file simultaneously. If theoretically possible, real problems are unlikely. Doing any better would require a convention, GNU-wide, for all programs doing backups. */ assign_string (&after_backup_name, NULL); after_backup_name = find_backup_file_name (path); if (after_backup_name == NULL) FATAL_ERROR ((0, 0, "Virtual memory exhausted")); if (rename (before_backup_name, after_backup_name) == 0) { if (verbose_option) fprintf (stdlis, _("Renaming previous `%s' to `%s'\n"), before_backup_name, after_backup_name); return 1; } /* The backup operation failed. */ ERROR ((0, errno, _("%s: Cannot rename for backup"), before_backup_name)); assign_string (&after_backup_name, NULL); return 0; }
int backup_file(const char *orig) { struct stat filestat; char bakname[MAXPATHLEN], *s, *simplename; dev_t orig_device; ino_t orig_inode; if (backup_type == none || stat(orig, &filestat) != 0) return 0; /* nothing to do */ orig_device = filestat.st_dev; orig_inode = filestat.st_ino; if (origprae) { if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) fatal("filename %s too long for buffer\n", origprae); } else { if ((s = find_backup_file_name(orig)) == NULL) fatal("out of memory\n"); if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) fatal("filename %s too long for buffer\n", s); free(s); } if ((simplename = strrchr(bakname, '/')) != NULL) simplename = simplename + 1; else simplename = bakname; /* * Find a backup name that is not the same file. Change the * first lowercase char into uppercase; if that isn't * sufficient, chop off the first char and try again. */ while (stat(bakname, &filestat) == 0 && orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { /* Skip initial non-lowercase chars. */ for (s = simplename; *s && !islower((unsigned char)*s); s++) ; if (*s) *s = toupper((unsigned char)*s); else memmove(simplename, simplename + 1, strlen(simplename + 1) + 1); } #ifdef DEBUGGING if (debug & 4) say("Moving %s to %s.\n", orig, bakname); #endif if (rename(orig, bakname) < 0) { if (errno != EXDEV || copy_file(orig, bakname) < 0) return -1; } return 0; }
/* Non failing call to find_backup_file_name */ char * xfind_backup_file_name (const char * file, enum backup_type backup_type) { char * res; res = find_backup_file_name (file, backup_type); if (!res) error (1, 0, _("Memory exhausted")); return res; }
/* Create FILE for output, using open flags OFLAG and default mode MODE. But before creating FILE, back up its previous contents if necessary. OFLAG must include the O_CREAT flag. */ int create_file_for_backup (char const *file, int oflag, mode_t mode, enum backup_type backup_type) { int fd; struct stat st; assert (oflag & O_CREAT); if (backup_type == none) return open (file, oflag, mode); #ifdef O_EXCL fd = open (file, oflag | O_EXCL, mode); #else { /* This substitute for O_EXCL allows races between `stat' and `open'. */ if (stat (file, &st) == 0) { errno = EEXIST; fd = -1; } else if (errno == ENOENT) fd = open (file, oflag, mode); else return -1; } #endif if (fd < 0 && errno == EEXIST) { /* We want to back up only regular files, even though this allows races between `stat' and `open'. */ if (stat (file, &st) == 0 && S_ISREG (st.st_mode)) { char *backup = find_backup_file_name (file, backup_type); if (rename (file, backup) != 0) return -1; free (backup); } fd = open (file, oflag, mode); } return fd; }
void create_backup (char const *to, const struct stat *to_st, bool leave_original) { /* When the input to patch modifies the same file more than once, patch only backs up the initial version of each file. To figure out which files have already been backed up, patch remembers the files that replace the original files. Files not known already are backed up; files already known have already been backed up before, and are skipped. When a patch tries to delete a file, in order to not break the above logic, we merely remember which file to delete. After the entire patch file has been read, we delete all files marked for deletion which have not been recreated in the meantime. */ if (to_st && ! (S_ISREG (to_st->st_mode) || S_ISLNK (to_st->st_mode))) fatal ("File %s is not a %s -- refusing to create backup", to, S_ISLNK (to_st->st_mode) ? "symbolic link" : "regular file"); if (to_st && lookup_file_id (to_st) == CREATED) { if (debug & 4) say ("File %s already seen\n", quotearg (to)); } else { int try_makedirs_errno = 0; char *bakname; if (origprae || origbase || origsuff) { char const *p = origprae ? origprae : ""; char const *b = origbase ? origbase : ""; char const *s = origsuff ? origsuff : ""; char const *t = to; size_t plen = strlen (p); size_t blen = strlen (b); size_t slen = strlen (s); size_t tlen = strlen (t); char const *o; size_t olen; for (o = t + tlen, olen = 0; o > t && ! ISSLASH (*(o - 1)); o--) /* do nothing */ ; olen = t + tlen - o; tlen -= olen; bakname = xmalloc (plen + tlen + blen + olen + slen + 1); memcpy (bakname, p, plen); memcpy (bakname + plen, t, tlen); memcpy (bakname + plen + tlen, b, blen); memcpy (bakname + plen + tlen + blen, o, olen); memcpy (bakname + plen + tlen + blen + olen, s, slen + 1); if ((origprae && (contains_slash (origprae + FILE_SYSTEM_PREFIX_LEN (origprae)) || contains_slash (to))) || (origbase && contains_slash (origbase))) try_makedirs_errno = ENOENT; } else { bakname = find_backup_file_name (to, backup_type); if (!bakname) xalloc_die (); } if (! to_st) { int fd; if (debug & 4) say ("Creating empty file %s\n", quotearg (bakname)); try_makedirs_errno = ENOENT; unlink (bakname); while ((fd = creat (bakname, 0666)) < 0) { if (errno != try_makedirs_errno) pfatal ("Can't create file %s", quotearg (bakname)); makedirs (bakname); try_makedirs_errno = 0; } if (close (fd) != 0) pfatal ("Can't close file %s", quotearg (bakname)); } else if (leave_original) create_backup_copy (to, bakname, to_st, try_makedirs_errno == 0); else { if (debug & 4) say ("Renaming file %s to %s\n", quotearg_n (0, to), quotearg_n (1, bakname)); while (rename (to, bakname) != 0) { if (errno == try_makedirs_errno) { makedirs (bakname); try_makedirs_errno = 0; } else if (errno == EXDEV) { create_backup_copy (to, bakname, to_st, try_makedirs_errno == 0); unlink (to); break; } else pfatal ("Can't rename file %s to %s", quotearg_n (0, to), quotearg_n (1, bakname)); } } free (bakname); } }
void move_file (char const *from, int volatile *from_needs_removal, char *to, mode_t mode, int backup) { struct stat to_st; int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno; if (backup) { int try_makedirs_errno = 0; char *bakname; if (origprae || origbase) { char const *p = origprae ? origprae : ""; char const *b = origbase ? origbase : ""; char const *o = base_name (to); size_t plen = strlen (p); size_t tlen = o - to; size_t blen = strlen (b); size_t osize = strlen (o) + 1; bakname = xmalloc (plen + tlen + blen + osize); memcpy (bakname, p, plen); memcpy (bakname + plen, to, tlen); memcpy (bakname + plen + tlen, b, blen); memcpy (bakname + plen + tlen + blen, o, osize); for (p += FILESYSTEM_PREFIX_LEN (p); *p; p++) if (ISSLASH (*p)) { try_makedirs_errno = ENOENT; break; } } else { bakname = find_backup_file_name (to, backup_type); if (!bakname) memory_fatal (); } if (to_errno) { int fd; if (debug & 4) say ("Creating empty unreadable file %s\n", quotearg (bakname)); try_makedirs_errno = ENOENT; unlink (bakname); while ((fd = creat (bakname, 0)) < 0) { if (errno != try_makedirs_errno) pfatal ("Can't create file %s", quotearg (bakname)); makedirs (bakname); try_makedirs_errno = 0; } if (close (fd) != 0) pfatal ("Can't close file %s", quotearg (bakname)); } else { if (debug & 4) say ("Renaming file %s to %s\n", quotearg_n (0, to), quotearg_n (1, bakname)); while (rename (to, bakname) != 0) { if (errno != try_makedirs_errno) pfatal ("Can't rename file %s to %s", quotearg_n (0, to), quotearg_n (1, bakname)); makedirs (bakname); try_makedirs_errno = 0; } } free (bakname); } if (from) { if (debug & 4) say ("Renaming file %s to %s\n", quotearg_n (0, from), quotearg_n (1, to)); if (rename (from, to) == 0) *from_needs_removal = 0; else { int to_dir_known_to_exist = 0; if (errno == ENOENT && (to_errno == -1 || to_errno == ENOENT)) { makedirs (to); to_dir_known_to_exist = 1; if (rename (from, to) == 0) { *from_needs_removal = 0; return; } } if (errno == EXDEV) { if (! backup) { if (unlink (to) == 0) to_dir_known_to_exist = 1; else if (errno != ENOENT) pfatal ("Can't remove file %s", quotearg (to)); } if (! to_dir_known_to_exist) makedirs (to); copy_file (from, to, 0, mode); return; } pfatal ("Can't rename file %s to %s", quotearg_n (0, from), quotearg_n (1, to)); } } else if (! backup) { if (debug & 4) say ("Removing file %s\n", quotearg (to)); if (unlink (to) != 0) pfatal ("Can't remove file %s", quotearg (to)); } }
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 command_line_arg, int *copy_into_self, int *rename_succeeded) { struct stat src_sb; struct stat dst_sb; mode_t src_mode; mode_t src_type; char *earlier_file = NULL; char *dst_backup = NULL; int backup_succeeded = 0; int delayed_fail; int copied_as_regular = 0; int ran_chown = 0; int preserve_metadata; if (x->move_mode && rename_succeeded) *rename_succeeded = 0; *copy_into_self = 0; if ((*(x->xstat)) (src_path, &src_sb)) { error (0, errno, _("cannot stat %s"), quote (src_path)); return 1; } src_type = src_sb.st_mode; src_mode = src_sb.st_mode; if (S_ISDIR (src_type) && !x->recursive) { error (0, 0, _("omitting directory %s"), quote (src_path)); return 1; } /* Detect the case in which the same source file appears more than once on the command line and no backup option has been selected. If so, simply warn and don't copy it the second time. This check is enabled only if x->src_info is non-NULL. */ if (command_line_arg) { if ( ! S_ISDIR (src_sb.st_mode) && x->backup_type == none && seen_file (x->src_info, src_path, &src_sb)) { error (0, 0, _("warning: source file %s specified more than once"), quote (src_path)); return 0; } record_file (x->src_info, src_path, &src_sb); } if (!new_dst) { if ((*(x->xstat)) (dst_path, &dst_sb)) { if (errno != ENOENT) { error (0, errno, _("cannot stat %s"), quote (dst_path)); return 1; } else { new_dst = 1; } } else { int return_now; int ok = same_file_ok (src_path, &src_sb, dst_path, &dst_sb, x, &return_now); if (return_now) return 0; if (! ok) { error (0, 0, _("%s and %s are the same file"), quote_n (0, src_path), quote_n (1, dst_path)); return 1; } if (!S_ISDIR (dst_sb.st_mode)) { if (S_ISDIR (src_type)) { error (0, 0, _("cannot overwrite non-directory %s with directory %s"), quote_n (0, dst_path), quote_n (1, src_path)); return 1; } /* Don't let the user destroy their data, even if they try hard: This mv command must fail (likewise for cp): rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c Otherwise, the contents of b/f would be lost. In the case of `cp', b/f would be lost if the user simulated a move using cp and rm. Note that it works fine if you use --backup=numbered. */ if (command_line_arg && x->backup_type != numbered && seen_file (x->dest_info, dst_path, &dst_sb)) { error (0, 0, _("will not overwrite just-created %s with %s"), quote_n (0, dst_path), quote_n (1, src_path)); return 1; } } if (!S_ISDIR (src_type)) { if (S_ISDIR (dst_sb.st_mode)) { error (0, 0, _("cannot overwrite directory %s with non-directory"), quote (dst_path)); return 1; } if (x->update && MTIME_CMP (src_sb, dst_sb) <= 0) { /* We're using --update and the source file is older than the destination file, so there is no need to copy or move. */ /* Pretend the rename succeeded, so the caller (mv) doesn't end up removing the source file. */ if (rename_succeeded) *rename_succeeded = 1; return 0; } } /* When there is an existing destination file, we may end up returning early, and hence not copying/moving the file. This may be due to an interactive `negative' reply to the prompt about the existing file. It may also be due to the use of the --reply=no option. */ if (!S_ISDIR (src_type)) { /* cp and mv treat -i and -f differently. */ if (x->move_mode) { if ((x->interactive == I_ALWAYS_NO && UNWRITABLE (dst_path, dst_sb.st_mode)) || ((x->interactive == I_ASK_USER || (x->interactive == I_UNSPECIFIED && x->stdin_tty && UNWRITABLE (dst_path, dst_sb.st_mode))) && (overwrite_prompt (dst_path, &dst_sb), 1) && ! yesno ())) { /* Pretend the rename succeeded, so the caller (mv) doesn't end up removing the source file. */ if (rename_succeeded) *rename_succeeded = 1; return 0; } } else { if (x->interactive == I_ALWAYS_NO || (x->interactive == I_ASK_USER && (overwrite_prompt (dst_path, &dst_sb), 1) && ! yesno ())) { return 0; } } } if (x->move_mode) { /* In move_mode, DEST may not be an existing directory. */ if (S_ISDIR (dst_sb.st_mode)) { error (0, 0, _("cannot overwrite directory %s"), quote (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"), quote_n (0, src_path), quote_n (0, 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) xalloc_die (); /* 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=simple a~ a would leave two zero-length files: a and a~. */ /* FIXME: but simply change e.g., the final a~ to `./a~' and the source will still be destroyed. */ if (STREQ (tmp_backup, src_path)) { const char *fmt; fmt = (x->move_mode ? _("backing up %s would destroy source; %s not moved") : _("backing up %s would destroy source; %s not copied")); error (0, 0, fmt, quote_n (0, dst_path), quote_n (1, 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"), quote (dst_path)); return 1; } else { dst_backup = NULL; } } else { backup_succeeded = 1; } new_dst = 1; } else if (! S_ISDIR (dst_sb.st_mode) && (x->unlink_dest_before_opening || (x->xstat == lstat && ! S_ISREG (src_sb.st_mode)))) { if (unlink (dst_path) && errno != ENOENT) { error (0, errno, _("cannot remove %s"), quote (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", quote_n (0, src_path), quote_n (1, dst_path)); if (backup_succeeded) printf (_(" (backup: %s)"), quote (dst_backup)); putchar ('\n'); } /* Associate the destination path with the source device and inode so that if we encounter a matching dev/ino pair in the source tree we can arrange to create a hard link between the corresponding names in the destination tree. Sometimes, when preserving links, we have to record dev/ino even though st_nlink == 1: - when using -H and processing a command line argument; that command line argument could be a symlink pointing to another command line argument. With `cp -H --preserve=link', we hard-link those two destination files. - likewise for -L except that it applies to all files, not just command line arguments. Also record directory dev/ino when using --recursive. We'll use that info to detect this problem: cp -R dir dir. FIXME-maybe: ideally, directory info would be recorded in a separate hash table, since such entries are useful only while a single command line hierarchy is being copied -- so that separate table could be cleared between command line args. Using the same hash table to preserve hard links means that it may not be cleared. */ if ((x->preserve_links && (1 < src_sb.st_nlink || (command_line_arg && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS) || x->dereference == DEREF_ALWAYS)) || (x->recursive && S_ISDIR (src_type))) { earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev); } /* Did we copy this inode somewhere else (in this command line argument) and therefore this is a second hard link to the inode? */ if (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)) { /* If src_path and earlier_file refer to the same directory entry, then warn about copying a directory into itself. */ if (same_name (src_path, earlier_file)) { error (0, 0, _("cannot copy a directory, %s, into itself, %s"), quote_n (0, top_level_src_path), quote_n (1, top_level_dst_path)); *copy_into_self = 1; } else { error (0, 0, _("will not create hard link %s to directory %s"), quote_n (0, dst_path), quote_n (1, earlier_file)); } goto un_backup; } { int link_failed; link_failed = link (earlier_file, dst_path); /* If the link failed because of an existing destination, remove that file and then call link again. */ if (link_failed && errno == EEXIST) { if (unlink (dst_path)) { error (0, errno, _("cannot remove %s"), quote (dst_path)); goto un_backup; } link_failed = link (earlier_file, dst_path); } if (link_failed) { error (0, errno, _("cannot create hard link %s to %s"), quote_n (0, dst_path), quote_n (1, earlier_file)); goto un_backup; } return 0; } } if (x->move_mode) { if (rename (src_path, dst_path) == 0) { if (x->verbose && S_ISDIR (src_type)) printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path)); if (rename_succeeded) *rename_succeeded = 1; if (command_line_arg) { /* Record destination dev/ino/filename, so that if we are asked to overwrite that file again, we can detect it and fail. */ /* It's fine to use the _source_ stat buffer (src_sb) to get the _destination_ dev/ino, since the rename above can't have changed those, and `mv' always uses lstat. We could limit it further by operating only on non-directories. */ record_file (x->dest_info, dst_path, &src_sb); } 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. */ error (0, 0, _("cannot move %s to a subdirectory of itself, %s"), quote_n (0, top_level_src_path), quote_n (1, top_level_dst_path)); /* Note that there is no need to call forget_created here, (compare with the other calls in this file) since the destination directory didn't exist before. */ *copy_into_self = 1; /* FIXME-cleanup: Don't return zero here; adjust mv.c accordingly. The only caller that uses this code (mv.c) ends up setting its exit status to nonzero when copy_into_self is nonzero. */ return 0; } /* WARNING: there probably exist systems for which an inter-device rename fails with a value of errno not handled here. If/as those are reported, add them to the condition below. If this happens to you, please do the following and send the output to the bug-reporting address (e.g., in the output of cp --help): touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"' where your current directory is on one partion and /tmp is the other. Also, please try to find the E* errno macro name corresponding to the diagnostic and parenthesized integer, and include that in your e-mail. One way to do that is to run a command like this find /usr/include/. -type f \ | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null where you'd replace `18' with the integer in parentheses that was output from the perl one-liner above. If necessary, of course, change `/tmp' to some other directory. */ if (errno != EXDEV) { /* There are many ways this can happen due to a race condition. When something happens between the initial xstat and the subsequent rename, we can get many different types of errors. For example, if the destination is initially a non-directory or non-existent, but it is created as a directory, the rename fails. If two `mv' commands try to rename the same file at about the same time, one will succeed and the other will fail. If the permissions on the directory containing the source or destination file are made too restrictive, the rename will fail. Etc. */ error (0, errno, _("cannot move %s to %s"), quote_n (0, src_path), quote_n (1, dst_path)); forget_created (src_sb.st_ino, src_sb.st_dev); return 1; } /* 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) { error (0, errno, _("inter-device move failed: %s to %s; unable to remove target"), quote_n (0, src_path), quote_n (1, dst_path)); forget_created (src_sb.st_ino, src_sb.st_dev); return 1; } new_dst = 1; } delayed_fail = 0; /* In certain modes (cp's --symbolic-link), and for certain file types (symlinks and hard links) it doesn't make sense to preserve metadata, or it's possible to preserve only some of it. In such cases, set this variable to zero. */ preserve_metadata = 1; 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, _("cannot copy cyclic symbolic link %s"), quote (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"), quote (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", quote_n (0, src_path), quote_n (1, 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) { preserve_metadata = 0; 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); 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"), quote (dst_path)); goto un_backup; } } if (symlink (src_path, dst_path)) { error (0, errno, _("cannot create symbolic link %s to %s"), quote_n (0, dst_path), quote_n (1, src_path)); goto un_backup; } } #endif else if (x->hard_link) { preserve_metadata = 0; if (link (src_path, dst_path)) { error (0, errno, _("cannot create link %s"), quote (dst_path)); goto un_backup; } } else if (S_ISREG (src_type) || (x->copy_as_regular && !S_ISDIR (src_type) #ifdef S_ISLNK && !S_ISLNK (src_type) #endif )) { copied_as_regular = 1; /* POSIX says the permission bits of the source file must be used as the 3rd argument in the open call, but that's not consistent with historical practice. */ if (copy_reg (src_path, dst_path, x, get_dest_mode (x, src_mode), &new_dst, &src_sb)) goto un_backup; } else #ifdef S_ISFIFO if (S_ISFIFO (src_type)) { if (mkfifo (dst_path, get_dest_mode (x, src_mode))) { error (0, errno, _("cannot create fifo %s"), quote (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, get_dest_mode (x, src_mode), src_sb.st_rdev)) { error (0, errno, _("cannot create special file %s"), quote (dst_path)); goto un_backup; } } else #ifdef S_ISLNK if (S_ISLNK (src_type)) { char *src_link_val = xreadlink (src_path); if (src_link_val == NULL) { error (0, errno, _("cannot read symbolic link %s"), quote (src_path)); goto un_backup; } if (!symlink (src_link_val, dst_path)) free (src_link_val); else { 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. */ size_t src_link_len = strlen (src_link_val); char *dest_link_val = (char *) alloca (src_link_len + 1); int dest_link_len = readlink (dst_path, dest_link_val, src_link_len + 1); if ((size_t) dest_link_len == src_link_len && strncmp (dest_link_val, src_link_val, src_link_len) == 0) same_link = 1; } free (src_link_val); if (! same_link) { error (0, saved_errno, _("cannot create symbolic link %s"), quote (dst_path)); goto un_backup; } } /* There's no need to preserve timestamps or permissions. */ preserve_metadata = 0; if (x->preserve_ownership) { /* 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, _("failed to preserve 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 } } else #endif { error (0, 0, _("%s has unknown file type"), quote (src_path)); goto un_backup; } if (command_line_arg) record_file (x->dest_info, dst_path, NULL); if ( ! preserve_metadata) return 0; /* 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"), quote (dst_path)); if (x->require_preserve) return 1; } } /* Avoid calling chown if we know it's not necessary. */ if (x->preserve_ownership && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb))) { ran_chown = 1; if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid)) { error (0, errno, _("failed to preserve ownership for %s"), quote (dst_path)); if (x->require_preserve) return 1; } } #if HAVE_STRUCT_STAT_ST_AUTHOR /* Preserve the st_author field. */ { file_t file = file_name_lookup (dst_path, 0, 0); if (file_chauthor (file, src_sb.st_author)) error (0, errno, _("failed to preserve authorship for %s"), quote (dst_path)); mach_port_deallocate (mach_task_self (), file); } #endif /* Permissions of newly-created regular files were set upon `open' in copy_reg. But don't return early if there were any special bits and we had to run chown, because the chown must have reset those bits. */ if ((new_dst && copied_as_regular) && !(ran_chown && (src_mode & ~S_IRWXUGO))) return delayed_fail; if ((x->preserve_mode || new_dst) && (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type))) { if (chmod (dst_path, get_dest_mode (x, src_mode))) { error (0, errno, _("setting permissions for %s"), quote (dst_path)); if (x->set_mode || x->require_preserve) return 1; } } return delayed_fail; un_backup: /* We have failed to create the destination file. If we've just added a dev/ino entry via the remember_copied call above (i.e., unless we've just failed to create a hard link), remove the entry associating the source dev/ino with the destination file name, so we don't try to `preserve' a link to a file we didn't create. */ if (earlier_file == NULL) forget_created (src_sb.st_ino, src_sb.st_dev); if (dst_backup) { if (rename (dst_backup, dst_path)) error (0, errno, _("cannot un-backup %s"), quote (dst_path)); else { if (x->verbose) printf (_("%s -> %s (unbackup)\n"), quote_n (0, dst_backup), quote_n (1, dst_path)); } } return 1; }