Subject: bug#10967: Weird 'du' behavior. Bug in coreutils-8.15 ?

From: Jim Meyering <[email-masked]>
Date: Thu, 08 Mar 2012 11:36:46 +0100

Jim Meyering wrote:
...
>> du (GNU coreutils) 8.15
>
> Oh!  That's a regression.
> Thank you for finding/reporting it!
>
> Here's the fix I expect to use:
>
> diff --git a/src/du.c b/src/du.c
> index e4e36df..c1c0417 100644
> --- a/src/du.c
> +++ b/src/du.c
> @@ -443,7 +443,9 @@ process_file (FTS *fts, FTSENT *ent)
>                return false;
>              }
>
> -          if (fts->fts_options & FTS_XDEV && fts->fts_dev != sb->st_dev)
> +          if (fts->fts_options & FTS_XDEV
> +              && FTS_ROOTLEVEL < ent->fts_level
> +              && fts->fts_dev != sb->st_dev)
>              excluded = true;
>          }

Thanks again.
A small reproducer is to run this:

    du -x /etc/passwd

If it prints nothing (coreutils-8.15), you have the bug.

Here is a complete patch:

>From b51e77776fbc4c8fa6727388b735427596595477 Mon Sep 17 00:00:00 2001
From: Jim Meyering <[email-masked]>
Date: Thu, 8 Mar 2012 10:33:50 +0100
Subject: [PATCH] du: --one-file-system (-x) ignores non-directory arguments

Surprise!  "du -x non-DIR" would print nothing.
Note that the problem arises only when processing a non-directory
specified on the command line.  Not surprisingly, "du -x" still
works as expected for any directory argument.

When performing its same-file-system check, du may skip an entry
only if it is at fts_level 1 or greater.  Command-line arguments
are at fts_level == 0 (FTS_ROOTLEVEL).

* src/du.c (process_file): Don't use the top-level FTS->fts_dev
when testing for --one-file-system (-x).  It happens to be valid
for directories, but it is always 0 for a non-directory.
* tests/du/one-file-system: Add tests for this.
* NEWS (Bug fixes): Mention it.
Reported by Daniel Stavrovski in http://bugs.gnu.org/10967.
Introduced by commit v8.14-95-gcfe1040.
---
 NEWS                     |    4 ++++
 THANKS.in                |    1 +
 src/du.c                 |    9 ++++++++-
 tests/du/one-file-system |   10 +++++++++-
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 04c911f..3224b30 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,10 @@ GNU coreutils NEWS                                    -*- outline -*-

 ** Bug fixes

+  du --one-file-system (-x) would ignore any non-directory specified on
+  the command line.  For example, "touch f; du -x f" would print nothing.
+  [bug introduced in coreutils-8.14]
+
   mv now lets you move a symlink onto a same-inode destination file that
   has two or more hard links.  Before, it would reject that, saying that
   they are the same, implicitly warning you that the move would result in
diff --git a/THANKS.in b/THANKS.in
index c8dd75f..d23f7b3 100644
--- a/THANKS.in
+++ b/THANKS.in
@@ -134,6 +134,7 @@ Dan Hagerty                         [email-masked]
 Dan Pascu                           [email-masked]
 Daniel Bergstrom                    [email-masked]
 Daniel P. Berrangé                  [email-masked]
+Daniel Stavrovski                   [email-masked]
 Dániel Varga                        [email-masked]
 Danny Levinson                      [email-masked]
 Darrel Francis                      [email-masked]
diff --git a/src/du.c b/src/du.c
index e4e36df..41c9535 100644
--- a/src/du.c
+++ b/src/du.c
@@ -443,7 +443,14 @@ process_file (FTS *fts, FTSENT *ent)
               return false;
             }

-          if (fts->fts_options & FTS_XDEV && fts->fts_dev != sb->st_dev)
+          /* The --one-file-system (-x) option cannot exclude anything
+             specified on the command-line.  By definition, it can exclude
+             a file or directory only when its device number is different
+             from that of its just-processed parent directory, and du does
+             not process the parent of a command-line argument.  */
+          if (fts->fts_options & FTS_XDEV
+              && FTS_ROOTLEVEL < ent->fts_level
+              && fts->fts_dev != sb->st_dev)
             excluded = true;
         }

diff --git a/tests/du/one-file-system b/tests/du/one-file-system
index f0d264a..110080f 100755
--- a/tests/du/one-file-system
+++ b/tests/du/one-file-system
@@ -43,7 +43,15 @@ compare exp out || fail=1
 du -xL d > u || fail=1
 sed 's/^[0-9][0-9]*	//' u > out1
 echo d > exp1 || fail=1
-
 compare exp1 out1 || fail=1

+# With coreutils-8.15, "du -xs FILE" would print no output.
+touch f
+for opt in -x -xs; do
+  du $opt f > u || fail=1
+  sed 's/^[0-9][0-9]*	//' u > out2
+  echo f > exp2 || fail=1
+  compare exp2 out2 || fail=1
+done
+
 Exit $fail
--
1.7.9.3.363.g121f0