Subject: bug#19508: [bug-diffutils] bug#19508: [PATCH] diff: compare major, minor numbers of block/character special files

From: Ondřej Svoboda <[email-masked]>
Date: Sat, 12 Mar 2016 14:55:56 +0100

Hi everybody,

I rebased the patch on current master and tested it (as root) like this:

# mkdir /tmp/a /tmp/b
# mknod /tmp/a/block1 b 1 2; mknod /tmp/b/block1 b 1 2
# mknod /tmp/a/block2 b 2 3; mknod /tmp/b/block2 b 2 4
# mknod /tmp/a/block3 b 3 4; mknod /tmp/b/block3 b 5 4
# mknod /tmp/a/char1 c 1 2; mknod /tmp/b/char1 c 1 2
# mknod /tmp/a/char2 c 2 3; mknod /tmp/b/char2 c 2 4
# mknod /tmp/a/char3 c 3 4; mknod /tmp/b/char3 c 5 4

# LC_ALL=C ls -l /tmp/a
total 0
brw-r--r-- 1 root root 1, 2 Mar 12 14:51 block1
brw-r--r-- 1 root root 2, 3 Mar 12 14:51 block2
brw-r--r-- 1 root root 3, 4 Mar 12 14:51 block3
crw-r--r-- 1 root root 1, 2 Mar 12 14:51 char1
crw-r--r-- 1 root root 2, 3 Mar 12 14:51 char2
crw-r--r-- 1 root root 3, 4 Mar 12 14:51 char3

# LC_ALL=C ls -l /tmp/b
total 0
brw-r--r-- 1 root root 1, 2 Mar 12 14:51 block1
brw-r--r-- 1 root root 2, 4 Mar 12 14:51 block2
brw-r--r-- 1 root root 5, 4 Mar 12 14:51 block3
crw-r--r-- 1 root root 1, 2 Mar 12 14:51 char1
crw-r--r-- 1 root root 2, 4 Mar 12 14:51 char2
crw-r--r-- 1 root root 5, 4 Mar 12 14:51 char3

# LC_ALL=C diff -s -r /tmp/a /tmp/b
Files /tmp/a/block1 and /tmp/b/block1 are identical
File /tmp/a/block2 is a block special file while file /tmp/b/block2 is a 
block special file
File /tmp/a/block3 is a block special file while file /tmp/b/block3 is a 
block special file
Files /tmp/a/char1 and /tmp/b/char1 are identical
File /tmp/a/char2 is a character special file while file /tmp/b/char2 is 
a character special file
File /tmp/a/char3 is a character special file while file /tmp/b/char3 is 
a character special file

# LC_ALL=C /usr/local/bin/diff -s -r /tmp/a /tmp/b
Files /tmp/a/block1 and /tmp/b/block1 are identical
Block special files /tmp/a/block2 and /tmp/b/block2 differ: 2,3 (2,4)
Block special files /tmp/a/block3 and /tmp/b/block3 differ: 3,4 (5,4)
Files /tmp/a/char1 and /tmp/b/char1 are identical
Character special files /tmp/a/char2 and /tmp/b/char2 differ: 2,3 (2,4)
Character special files /tmp/a/char3 and /tmp/b/char3 differ: 3,4 (5,4)

The reason why diff compared block1 as identical perhaps because it 
descended to diff_2_files, I would know if I stepped through the code 
which I currently don't have much time to do.

Please share your comments!

Cheers,
Ondra
>From 36f26c15551d5c32d3197b3f0008a96fe3440240 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Svoboda?= <[email-masked]>
Date: Sun, 21 Sep 2014 18:06:38 +0200
Subject: [PATCH] diff: compare major, minor numbers of block/character special
 files

This deals with situation such as

  File dev1/block1 is a block special file while file dev2/block1 is
  a block special file

in case the major and minor numbers are identical.
If stat.st_rdev does not match, a message is given:

  Block special files dev1/block1 and dev2/block1 differ: 1,2 (3,2)
---
 src/diff.c | 23 +++++++++++++++++++++++
 src/diff.h |  1 +
 src/util.c |  9 +++++++++
 3 files changed, 33 insertions(+)

diff --git a/src/diff.c b/src/diff.c
index 9bc1d96..110b8e1 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1318,6 +1318,29 @@ compare_files (struct comparison const *parent,
 	      status = EXIT_FAILURE;
 	    }
 	}
+      else if ((S_ISBLK (cmp.file[0].stat.st_mode)
+	        && S_ISBLK (cmp.file[1].stat.st_mode))
+	       || (S_ISCHR (cmp.file[0].stat.st_mode)
+	           && S_ISCHR (cmp.file[1].stat.st_mode)))
+	{
+	  if (cmp.file[0].stat.st_rdev != cmp.file[1].stat.st_rdev)
+	    {
+	      char major_minor0[20], major_minor1[20];
+	      format_major_minor (major_minor0, cmp.file[0].stat.st_rdev);
+	      format_major_minor (major_minor1, cmp.file[1].stat.st_rdev);
+
+	      const char *message = S_ISBLK (cmp.file[0].stat.st_mode) ?
+			"Block special files %s and %s differ: %s (%s)\n" :
+			"Character special files %s and %s differ: %s (%s)\n";
+	      message5 (message,
+			file_label[0] ? file_label[0] : cmp.file[0].name,
+			file_label[1] ? file_label[1] : cmp.file[1].name,
+			major_minor0, major_minor1);
+
+	      /* This is a difference.  */
+	      status = EXIT_FAILURE;
+	    }
+	}
       else
 	{
 	  /* We have two files that are not to be compared.  */
diff --git a/src/diff.h b/src/diff.h
index 0983e7c..12ed085 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -391,6 +391,7 @@ extern void begin_output (void);
 extern void debug_script (struct change *);
 extern void fatal (char const *) __attribute__((noreturn));
 extern void finish_output (void);
+extern void format_major_minor (char *buffer, const dev_t st_rdev);
 extern void message (char const *, char const *, char const *);
 extern void message5 (char const *, char const *, char const *,
                       char const *, char const *);
diff --git a/src/util.c b/src/util.c
index d7b8925..85a47d3 100644
--- a/src/util.c
+++ b/src/util.c
@@ -26,6 +26,7 @@
 #include <xalloc.h>
 #include "xvasprintf.h"
 #include <signal.h>
+#include <sys/types.h>
 
 /* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
    present.  */
@@ -1577,3 +1578,11 @@ debug_script (struct change *sp)
 
   fflush (stderr);
 }
+
+/* Format a device number as major,minor. */
+
+void
+format_major_minor (char *buffer, const dev_t st_rdev)
+{
+  sprintf (buffer, "%u,%u", (major (st_rdev)), (minor (st_rdev)));
+}
-- 
2.7.2