diff options
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | src/du.c | 15 | ||||
-rwxr-xr-x | tests/du/files0-from | 8 | ||||
-rwxr-xr-x | tests/du/hard-link | 44 |
4 files changed, 52 insertions, 20 deletions
@@ -38,6 +38,11 @@ GNU coreutils NEWS -*- outline -*- Also errors are no longer suppressed for unsupported file types, and relative sizes are restricted to supported file types. +** Bug fixes + + du no longer multiply counts a file that is a directory or whose + link count is 1, even if the file is reached multiple times by + following symlinks or via multiple arguments. * Noteworthy changes in release 8.5 (2010-04-23) [stable] @@ -132,6 +132,9 @@ static bool apparent_size = false; /* If true, count each hard link of files with multiple links. */ static bool opt_count_all = false; +/* If true, hash all files to look for hard links. */ +static bool hash_all; + /* If true, output the NUL byte instead of a newline at the end of each line. */ static bool opt_nul_terminate_output = false; @@ -518,8 +521,7 @@ process_file (FTS *fts, FTSENT *ent) via a hard link, then don't let it contribute to the sums. */ if (skip || (!opt_count_all - && ! S_ISDIR (sb->st_mode) - && 1 < sb->st_nlink + && (hash_all || (! S_ISDIR (sb->st_mode) && 1 < sb->st_nlink)) && ! hash_ins (sb->st_ino, sb->st_dev))) { /* Note that we must not simply return here. @@ -937,11 +939,20 @@ main (int argc, char **argv) quote (files_from)); ai = argv_iter_init_stream (stdin); + + /* It's not easy here to count the arguments, so assume the + worst. */ + hash_all = true; } else { char **files = (optind < argc ? argv + optind : cwd_only); ai = argv_iter_init_argv (files); + + /* Hash all dev,ino pairs if there are multiple arguments, or if + following non-command-line symlinks, because in either case a + file with just one hard link might be seen more than once. */ + hash_all = (optind + 1 < argc || symlink_deref_bits == FTS_LOGICAL); } if (!ai) diff --git a/tests/du/files0-from b/tests/du/files0-from index 620246d56..860fc6aad 100755 --- a/tests/du/files0-from +++ b/tests/du/files0-from @@ -70,15 +70,15 @@ my @Tests = {IN=>{f=>"g\0"}}, {AUX=>{g=>''}}, {OUT=>"0\tg\n"}, {OUT_SUBST=>'s/^\d+/0/'} ], - # two file names, no final NUL + # two identical file names, no final NUL ['2', '--files0-from=-', '<', {IN=>{f=>"g\0g"}}, {AUX=>{g=>''}}, - {OUT=>"0\tg\n0\tg\n"}, {OUT_SUBST=>'s/^\d+/0/'} ], + {OUT=>"0\tg\n"}, {OUT_SUBST=>'s/^\d+/0/'} ], - # two file names, with final NUL + # two identical file names, with final NUL ['2a', '--files0-from=-', '<', {IN=>{f=>"g\0g\0"}}, {AUX=>{g=>''}}, - {OUT=>"0\tg\n0\tg\n"}, {OUT_SUBST=>'s/^\d+/0/'} ], + {OUT=>"0\tg\n"}, {OUT_SUBST=>'s/^\d+/0/'} ], # Ensure that $prog processes FILEs following a zero-length name. ['zero-len', '--files0-from=-', '<', diff --git a/tests/du/hard-link b/tests/du/hard-link index 7e4f51aee..e22320b56 100755 --- a/tests/du/hard-link +++ b/tests/du/hard-link @@ -26,24 +26,40 @@ fi . $srcdir/test-lib.sh mkdir -p dir/sub -( cd dir && { echo non-empty > f1; ln f1 f2; echo non-empty > sub/F; } ) - - -# Note that for this first test, we transform f1 or f2 -# (whichever name we find first) to f_. That is necessary because, -# depending on the type of file system, du could encounter either of those -# two hard-linked files first, thus listing that one and not the other. -du -a --exclude=sub dir \ - | sed 's/^[0-9][0-9]* //' | sed 's/f[12]/f_/' > out || fail=1 -echo === >> out -du -a --exclude=sub --count-links dir \ - | sed 's/^[0-9][0-9]* //' | sort -r >> out || fail=1 +( cd dir && + { echo non-empty > f1 + ln f1 f2 + ln -s f1 f3 + echo non-empty > sub/F; } ) + +du -a -L --exclude=sub --count-links dir \ + | sed 's/^[0-9][0-9]* //' | sort -r > out || fail=1 + +# For these tests, transform f1 or f2 or f3 (whichever name is find +# first) to f_. That is necessary because, depending on the type of +# file system, du could encounter any of those linked files first, +# thus listing that one and not the others. +for args in '-L' 'dir' '-L dir' +do + echo === >> out + du -a --exclude=sub $args dir \ + | sed 's/^[0-9][0-9]* //' | sed 's/f[123]/f_/' >> out || fail=1 +done + cat <<\EOF > exp +dir/f3 +dir/f2 +dir/f1 +dir +=== dir/f_ dir === -dir/f2 -dir/f1 +dir/f_ +dir/f_ +dir +=== +dir/f_ dir EOF |