From owner-freebsd-bugs@FreeBSD.ORG Thu Feb 14 08:00:00 2013 Return-Path: Delivered-To: freebsd-bugs@smarthost.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id 7E8F0667 for ; Thu, 14 Feb 2013 08:00:00 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) by mx1.freebsd.org (Postfix) with ESMTP id 667E3C11 for ; Thu, 14 Feb 2013 08:00:00 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.6/8.14.6) with ESMTP id r1E800HQ067775 for ; Thu, 14 Feb 2013 08:00:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.6/8.14.6/Submit) id r1E800FO067774; Thu, 14 Feb 2013 08:00:00 GMT (envelope-from gnats) Resent-Date: Thu, 14 Feb 2013 08:00:00 GMT Resent-Message-Id: <201302140800.r1E800FO067774@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Akinori MUSHA Received: from mx1.freebsd.org (mx1.FreeBSD.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id C339C585 for ; Thu, 14 Feb 2013 07:55:46 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22]) by mx1.freebsd.org (Postfix) with ESMTP id B58CDB9E for ; Thu, 14 Feb 2013 07:55:46 +0000 (UTC) Received: from red.freebsd.org (localhost [127.0.0.1]) by red.freebsd.org (8.14.5/8.14.5) with ESMTP id r1E7tkxs093159 for ; Thu, 14 Feb 2013 07:55:46 GMT (envelope-from nobody@red.freebsd.org) Received: (from nobody@localhost) by red.freebsd.org (8.14.5/8.14.5/Submit) id r1E7tkRU093158; Thu, 14 Feb 2013 07:55:46 GMT (envelope-from nobody) Message-Id: <201302140755.r1E7tkRU093158@red.freebsd.org> Date: Thu, 14 Feb 2013 07:55:46 GMT From: Akinori MUSHA To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: bin/176136: cp(1) fails to overwrite a symlnk pointing to a directory X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Feb 2013 08:00:00 -0000 >Number: 176136 >Category: bin >Synopsis: cp(1) fails to overwrite a symlnk pointing to a directory >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Thu Feb 14 08:00:00 UTC 2013 >Closed-Date: >Last-Modified: >Originator: Akinori MUSHA >Release: 9.1-STABLE >Organization: >Environment: FreeBSD 9.1-STABLE/amd64 >Description: Our implementation of cp(1) cannot overwrite a symlink pointing to an existing directory. This is because it always calls stat(2) on a destination file path even if -R (with -P by default) is given, where lstat(2) should be used instead. Dragonfly BSD, OS X and OpenBSD all share the same problem with us. NetBSD cp(1) has once had this problem but fixed it in 2006 (bin/cp.c rev.1.42). GNU cp does not have this problem. Solaris/OpenIndiana's cp(1) (/usr/xpg4/bin/cp) seems to have this problem. As far as I read, SUSv4 is not very clear as to how cp(1) should do about the situation, but there should be no reason a symlink cannot be overwritten. >How-To-Repeat: % ln -s . foo % mkdir bar % ln -s .. bar/foo Now we have and . Let's try copying to . % cp -RP foo bar/ cp: cannot overwrite directory bar/foo with non-directory foo It failed! % ls -l bar total 1 lrwxr-xr-x 1 knu knu 2 Feb 14 16:18 foo -> .. It seems cp(1) decided not to copy the source file just because the destination symlink was resolved to a directory, but it is certainly not what you would expect. >Fix: Index: cp.c =================================================================== --- cp.c (revision 246770) +++ cp.c (working copy) @@ -262,7 +262,7 @@ copy(char *argv[], enum op type, int fts struct stat to_stat; FTS *ftsp; FTSENT *curr; - int base = 0, dne, badcp, rval; + int base = 0, dne, badcp, rval, sval; size_t nlen; char *p, *target_mid; mode_t mask, mode; @@ -383,8 +383,18 @@ copy(char *argv[], enum op type, int fts continue; } + /* + * lstat(2) should be used if neither -H or -L is + * given, or we will fail to overwrite an existing + * symlink pointing to a directory. + */ + if (fts_options & (FTS_LOGICAL | FTS_COMFOLLOW)) + sval = stat(to.p_path, &to_stat); + else + sval = lstat(to.p_path, &to_stat); + /* Not an error but need to remember it happened */ - if (stat(to.p_path, &to_stat) == -1) + if (sval == -1) dne = 1; else { if (to_stat.st_dev == curr->fts_statp->st_dev && >Release-Note: >Audit-Trail: >Unformatted: