Subject: bug#17179: [PATCH] cp: use an invalid dev_t for an unknown device

From: Dave Reisner <[email-masked]>
Date: Thu, 3 Apr 2014 11:03:14 -0400

On the initial call to copy_internal, we must use an invalid device
number. As of linux 3.14, the mount table has shifted slightly, causing
the initramfs filesystem to have a devno of 0. This is valid, but
confuses cp when attempting to copy only a single filesystem (cp -x).
Since dev_t is defined to be an integer type, we can simply use a
negative value to identify the unknown device.

* src/copy.c (copy_internal): Make initial callers pass -1, compare to
  -1 instead of 0, and update documentation.
---
Hi,

This fixes a bug in Arch Linux's early userspace. We call 'cp -ax' to copy some
files from the early userspace into real root. Since 3.14, this is broken and
cp ignores the filesystem boundaries because it expects that 0 is an invalid
device number. If you're curious about why this changed, please see my analysis
on the util-linux mailing list:

http://www.spinics.net/lists/util-linux-ng/msg09074.html

Cheers,
Dave

 src/copy.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/copy.c b/src/copy.c
index 781cc1e..472e5f7 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1726,7 +1726,7 @@ should_dereference (const struct cp_options *x, bool command_line_arg)
    any type.  NEW_DST should be true if the file DST_NAME cannot
    exist because its parent directory was just created; NEW_DST should
    be false if DST_NAME might already exist.  DEVICE is the device
-   number of the parent directory, or 0 if the parent of this file is
+   number of the parent directory, or -1 if the parent of this file is
    not known.  ANCESTORS points to a linked, null terminated list of
    devices and inodes of parent directories of SRC_NAME.  COMMAND_LINE_ARG
    is true iff SRC_NAME was specified on the command line.
@@ -2434,7 +2434,7 @@ copy_internal (char const *src_name, char const *dst_name,
         }
 
       /* Decide whether to copy the contents of the directory.  */
-      if (x->one_file_system && device != 0 && device != src_sb.st_dev)
+      if (x->one_file_system && device != -1 && device != src_sb.st_dev)
         {
           /* Here, we are crossing a file system boundary and cp's -x option
              is in effect: so don't copy the contents of this directory. */
@@ -2827,7 +2827,7 @@ copy (char const *src_name, char const *dst_name,
   top_level_dst_name = dst_name;
 
   bool first_dir_created_per_command_line_arg = false;
-  return copy_internal (src_name, dst_name, nonexistent_dst, 0, NULL,
+  return copy_internal (src_name, dst_name, nonexistent_dst, -1, NULL,
                         options, true,
                         &first_dir_created_per_command_line_arg,
                         copy_into_self, rename_succeeded);
-- 
1.9.1