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>