* NEWS: Document fix for cp --parents.
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 3 Feb 2007 17:12:11 +0000 (18:12 +0100)
committerJim Meyering <jim@meyering.net>
Sat, 3 Feb 2007 17:12:11 +0000 (18:12 +0100)
* src/cp.c (make_dir_parents_private): Report the error sooner with
"cp --parents DIR/FILE DEST" when DIR is a non-directory, thus not
creating the directory, DEST/DIR.
* tests/cp/cp-parents: Test for the non-race-condition bug fixed
by the above change.

ChangeLog
NEWS
src/cp.c
tests/cp/cp-parents

index c7f04aa..c2ad0b5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2007-02-02  Paul Eggert  <eggert@cs.ucla.edu>
+
+       * NEWS: Document fix for cp --parents.
+       * src/cp.c (make_dir_parents_private): Report the error sooner with
+       "cp --parents DIR/FILE DEST" when DIR is a non-directory, thus not
+       creating the directory, DEST/DIR.
+       * tests/cp/cp-parents: Test for the non-race-condition bug fixed
+       by the above change.
+
 2007-02-02  Jim Meyering  <jim@meyering.net>
 
        * src/nl.c (proc_text): Use "NULL", not "(struct re_registers *) 0".
diff --git a/NEWS b/NEWS
index ad0fd1d..c90a1b3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,9 @@ GNU coreutils NEWS                                    -*- outline -*-
   chmod no longer fails in an environment (e.g., a chroot) with openat
   support but with insufficient /proc support.
 
+  "cp --parents F/G D" no longer creates a directory D/F when F is not
+  a directory (and F/G is therefore invalid).
+
   cut no longer dumps core for usage like "cut -f2- f1 f2" with two or
   more file arguments.  This was due to a double-free bug, introduced
   in coreutils-5.3.0.
index 8fe11a1..aea63dc 100644 (file)
--- a/src/cp.c
+++ b/src/cp.c
@@ -1,5 +1,5 @@
 /* cp.c  -- file copying (main routines)
-   Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation.
+   Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -415,6 +415,7 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
              mode_t src_mode;
              mode_t omitted_permissions;
              mode_t mkdir_mode;
+             int src_errno;
 
              /* This component does not exist.  We must set
                 *new_dst and new->mode inside this loop because,
@@ -422,9 +423,14 @@ make_dir_parents_private (char const *const_dir, size_t src_offset,
                 make_dir_parents_private creates only e_dir/../a if
                 ./b already exists. */
              *new_dst = true;
-             if (XSTAT (x, src, &stats))
+             src_errno = (XSTAT (x, src, &stats) != 0
+                          ? errno
+                          : S_ISDIR (stats.st_mode)
+                          ? 0
+                          : ENOTDIR);
+             if (src_errno)
                {
-                 error (0, errno, _("failed to get attributes of %s"),
+                 error (0, src_errno, _("failed to get attributes of %s"),
                         quote (src));
                  return false;
                }
index 373e607..6c123d2 100755 (executable)
@@ -2,7 +2,8 @@
 # cp -R --parents dir-specified-with-trailing-slash/ other-dir
 # would get a failed assertion.
 
-# Copyright (C) 2000, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+# Copyright (C) 2000, 2002, 2004, 2005, 2006, 2007 Free Software
+# Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -48,6 +49,7 @@ cd $tmp || framework_failure=1
 
 mkdir foo bar || framework_failure=1
 mkdir -p a/b/c d e || framework_failure=1
+touch f || framework_failure=1
 
 if test $framework_failure = 1; then
   echo 'failure in testing framework'
@@ -65,6 +67,11 @@ cp -R --parents foo/ bar || fail=1
 cp --verbose -a --parents a/b/c d > /dev/null 2>&1 || fail=1
 test -d d/a/b/c || fail=1
 
+# With 6.7 and earlier, cp --parents f/g d would mistakenly create a
+# directory d/f, even though f is a regular file.
+cp --parents f/g d 2>/dev/null && fail=1
+test -d d/f && fail=1
+
 # Check that re_protect works.
 chmod go=w d/a
 cp -a --parents d/a/b/c e || fail=1