Date: Sun, 11 Feb 2001 21:48:55 +0100 (CET) From: mkamm@gmx.net To: FreeBSD-gnats-submit@freebsd.org Subject: bin/25015: cp: options -i and -f do not work as documented Message-ID: <200102112048.f1BKmtE01611@homebox.kammerhofer.org>
next in thread | raw e-mail | index | archive | help
>Number: 25015
>Category: bin
>Synopsis: cp: options -i and -f do not work as documented
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Sun Feb 11 14:50:02 PST 2001
>Closed-Date:
>Last-Modified:
>Originator: Martin Kammerhofer
>Release: FreeBSD 4.2-STABLE i386
>Organization:
Universität Graz
>Environment:
>Description:
I quote from "man cp":
## -f For each existing destination pathname, remove it and create a new
## file, without prompting for confirmation regardless of its permis-
## sions.
##
## -i Cause cp to write a prompt to the standard error output before
## copying a file that would overwrite an existing file.
There are two bugs:
------------------------------
Bug #1:
Option -f is supposed to unlink targets before the copy takes
place. (This makes a great difference with respect to permissions and
especially with targets that are neither plain files nor directories.)
However cp(1) doesn't recognize the existence of targets which are
broken symlinks -- because of improper use of stat(2) instead of
lstat(2) -- and doesn't unlink them with the -f option. This leads to
two kinds of errors:
Bug #1.1:
"cp -f source /dir/dlink" will ignore the -f option when dlink is a
dangling symlink, i.e. points to nowhere. Instead of creating
/dir/dlink as a copy of source the symlink will be followed and the
copy of source will be created wherever dlink points to. (Provided that
permissions allow it.) This can be a security risk.
Bug #1.2:
When recursively copying a special file (fifo, character device) or
symlink onto a broken symlink the target can not be created because
cp(1) misses the required unlink(2).
------------------------------
Bug #2:
Option -i is only respected when the source is a plain file. When the
source is a special file, fifo or symlink the target will be unlinked
without questions even if the target is plain file.
This bug will only show up in recursive mode (-R or -r).
>How-To-Repeat:
------------------------------
Bug #1.1
As anyuser:
$ ln -sf /hosts.equiv mumble.conf # /hosts.equiv doesn't exist
Later as root:
# cp -f /usr/local/etc/mumble.conf.distrib mumble.conf
Now despite of option -f the symlink mumble.conf remains and a file in
the root directory has been created.
------------------------------
Bug #1.2
$ ln -sf /NOSUCH dslink
$ mkfifo myfifo
$ cp -RPf myfifo dslink
Will not work despite -f.
------------------------------
Bug #2
Note: /etc/termcap is assumed to be a symlink.
$ date > veryverypreciousfile
$ cp -iR /etc/termcap veryverypreciousfile
Now it's gone (replaced by a symlink) without questions
despite using option -i.
------------------------------
>Fix:
Index: cp.c
===================================================================
RCS file: /home/ncvs/src/bin/cp/cp.c,v
retrieving revision 1.24
diff -u -r1.24 cp.c
--- cp.c 1999/11/28 09:34:21 1.24
+++ cp.c 2001/02/11 18:10:40
@@ -355,7 +355,7 @@
switch (curr->fts_statp->st_mode & S_IFMT) {
case S_IFLNK:
- if (copy_link(curr, !dne))
+ if (copy_link(curr))
badcp = rval = 1;
break;
case S_IFDIR:
@@ -397,7 +397,7 @@
case S_IFBLK:
case S_IFCHR:
if (Rflag) {
- if (copy_special(curr->fts_statp, !dne))
+ if (copy_special(curr->fts_statp))
badcp = rval = 1;
} else {
if (copy_file(curr, dne))
@@ -406,7 +406,7 @@
break;
case S_IFIFO:
if (Rflag) {
- if (copy_fifo(curr->fts_statp, !dne))
+ if (copy_fifo(curr->fts_statp))
badcp = rval = 1;
} else {
if (copy_file(curr, dne))
Index: extern.h
===================================================================
RCS file: /home/ncvs/src/bin/cp/extern.h,v
retrieving revision 1.9
diff -u -r1.9 extern.h
--- extern.h 1999/08/27 23:13:39 1.9
+++ extern.h 2001/02/11 18:11:30
@@ -47,10 +47,10 @@
#include <sys/cdefs.h>
__BEGIN_DECLS
-int copy_fifo __P((struct stat *, int));
+int copy_fifo __P((struct stat *));
int copy_file __P((FTSENT *, int));
-int copy_link __P((FTSENT *, int));
-int copy_special __P((struct stat *, int));
+int copy_link __P((FTSENT *));
+int copy_special __P((struct stat *));
int setfile __P((struct stat *, int));
void usage __P((void));
__END_DECLS
Index: utils.c
===================================================================
RCS file: /home/ncvs/src/bin/cp/utils.c,v
retrieving revision 1.28
diff -u -r1.28 utils.c
--- utils.c 2000/10/10 01:48:18 1.28
+++ utils.c 2001/02/11 20:32:15
@@ -56,6 +56,44 @@
#include "extern.h"
+#define YESNO "(y/n [n]) "
+
+/*
+ * If target does not exist return 0. Otherwise if iflag ask for user
+ * confirmation and return 1 if user does not affirm. If unlnk try to
+ * unlink target and return 2 on failure.
+ */
+
+static int
+check_unlink(unlnk)
+ int unlnk;
+{
+ struct stat ts;
+ int ch, checkch;
+
+ if (lstat(to.p_path, &ts))
+ return (0);
+ if (iflag) {
+ (void)fprintf(stderr, "overwrite %s? %s", to.p_path, YESNO);
+ checkch = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (checkch != 'y' && checkch != 'Y') {
+ (void)fprintf(stderr, "not overwritten\n");
+ return (1);
+ }
+ }
+ if (unlnk) {
+ /* remove existing destination file name, create a new file */
+ if (unlink(to.p_path)) {
+ warn("cannot unlink %s", to.p_path);
+ return (2);
+ }
+ }
+ return (0);
+}
+
+
int
copy_file(entp, dne)
FTSENT *entp;
@@ -63,7 +101,7 @@
{
static char buf[MAXBSIZE];
struct stat to_stat, *fs;
- int ch, checkch, from_fd, rcount, rval, to_fd, wcount, wresid;
+ int from_fd, rcount, rval, to_fd, wcount, wresid;
char *bufp;
#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
char *p;
@@ -84,33 +122,16 @@
* other choice is 666 or'ed with the execute bits on the from file
* modified by the umask.)
*/
- if (!dne) {
-#define YESNO "(y/n [n]) "
- if (iflag) {
- (void)fprintf(stderr, "overwrite %s? %s",
- to.p_path, YESNO);
- checkch = ch = getchar();
- while (ch != '\n' && ch != EOF)
- ch = getchar();
- if (checkch != 'y' && checkch != 'Y') {
- (void)close(from_fd);
- (void)fprintf(stderr, "not overwritten\n");
- return (1);
- }
- }
-
- if (fflag) {
- /* remove existing destination file name,
- * create a new file */
- (void)unlink(to.p_path);
+ if (check_unlink(fflag)) {
+ (void)close(from_fd);
+ return (1);
+ }
+ if (fflag || dne) {
to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
fs->st_mode & ~(S_ISUID | S_ISGID));
- } else
+ } else
/* overwrite existing destination file name */
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) {
warn("%s", to.p_path);
@@ -204,9 +225,8 @@
}
int
-copy_link(p, exists)
+copy_link(p)
FTSENT *p;
- int exists;
{
int len;
char link[MAXPATHLEN];
@@ -216,26 +236,21 @@
return (1);
}
link[len] = '\0';
- if (exists && unlink(to.p_path)) {
- warn("unlink: %s", to.p_path);
+ if (check_unlink(1))
return (1);
- }
if (symlink(link, to.p_path)) {
- warn("symlink: %s", link);
+ warn("symlink: %s -> %s", to.p_path, link);
return (1);
}
return (0);
}
int
-copy_fifo(from_stat, exists)
+copy_fifo(from_stat)
struct stat *from_stat;
- int exists;
{
- if (exists && unlink(to.p_path)) {
- warn("unlink: %s", to.p_path);
+ if (check_unlink(1))
return (1);
- }
if (mkfifo(to.p_path, from_stat->st_mode)) {
warn("mkfifo: %s", to.p_path);
return (1);
@@ -244,14 +259,11 @@
}
int
-copy_special(from_stat, exists)
+copy_special(from_stat)
struct stat *from_stat;
- int exists;
{
- if (exists && unlink(to.p_path)) {
- warn("unlink: %s", to.p_path);
+ if (check_unlink(1))
return (1);
- }
if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
warn("mknod: %s", to.p_path);
return (1);
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200102112048.f1BKmtE01611>
