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

From: Pádraig Brady <[email-masked]>
Date: Thu, 08 Mar 2012 11:04:38 +0000

On 03/08/2012 10:36 AM, Jim Meyering wrote:
> 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

I find it a bit easier to parse if the summary describes
the change rather than the bug. So something like:

du: fix -x to include 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.

While I can `git show` the above revision format,
gitk doesn't hyperlink it. Does gitweb auto link the above format?
I guess they may in future at least?

> ---
>  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

Maybe this is enough?

du $opt f | grep . > /dev/null || fail=1

cheers,
Pádraig.