]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
du: add --inodes option
authorBernhard Voelker <mail@bernhard-voelker.de>
Sat, 27 Jul 2013 12:25:28 +0000 (14:25 +0200)
committerBernhard Voelker <mail@bernhard-voelker.de>
Sat, 27 Jul 2013 12:25:28 +0000 (14:25 +0200)
This new option can be used to find directories with a huge
amount of files.  The GNU find utility has the printf format
"%h" which prints the number of entries in a directory, but
this is non-cumulative and doesn't handle hard links.

* src/du.c (struct duinfo): Add new member for counting inodes.
(duinfo_init): Initialize inodes member with Zero.
(duinfo_set): Set inodes counter to 1.
(duinfo_add): Sum up the 2 given inodes counters.
(opt_inodes): Add new boolean flag to remember if the --inodes
option has been specified.
(INODES_OPTION): Add new enum value to be used ...
(long_options): ... here.
(usage): Add description of the new option.
(print_size): Pass inodes counter or size to print_only_size,
depending on the inodes mode.
(process_file): Adapt threshold handling: with --inodes, print or
elide the entries according to the struct member inodes.
(main): Add a case for accepting the new INODES_OPTION.
Print a warning diagnostic when --inodes is used together with the
option --apparent-size or -b.
Reset the output_block_size to 1 ... and thus ignoring the
options -m and -k.
* tests/du/inodes.sh: Add a new test.
* tests/local.mk (all_tests): Mention it.
* doc/coreutils.texi (du invocation): Document the new option.
* NEWS: Mention the new option.

NEWS
doc/coreutils.texi
src/du.c
tests/du/inodes.sh [new file with mode: 0755]
tests/local.mk

diff --git a/NEWS b/NEWS
index 3d0fb18b1ea003581993fa9af4a9d87632f9c8b5..4a78617c7b18c52e657f6d716e2f9afe594a689a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,9 @@ GNU coreutils NEWS                                    -*- outline -*-
 
 ** New features
 
+  du accepts a new option: --inodes to show the number of inodes instead
+  of the blocks used.
+
   id and ls with -Z report the SMACK security context where available.
   mkdir, mkfifo and mknod with -Z set the SMACK context where available.
 
index b8d40b4f5b876a6c6c58e7e31a16c597bc442ae2..40bff7ae98fcf701f914608ced067da3eec4d082 100644 (file)
@@ -11424,6 +11424,18 @@ Equivalent to @option{--dereference-args} (@option{-D}).
 
 @optHumanReadable
 
+@itemx --inodes
+@opindex --inodes
+@cindex inode usage, dereferencing in @command{du}
+List inode usage information instead of block usage.
+This option is useful for finding directories which contain many files, and
+therefore eat up most of the inodes space of a file system (see @command{df},
+option @option{--inodes}).
+It can well be combined with the options @option{-a}, @option{-c},
+@option{-h}, @option{-l}, @option{-s}, @option{-S}, @option{-t} and
+@option{-x}; however, passing other options regarding the block size, for
+example @option{-b}, @option{-m} and @option{--apparent-size}, is ignored.
+
 @item -k
 @opindex -k
 @cindex kibibytes for file sizes
@@ -11485,7 +11497,9 @@ Display only a total for each argument.
 @itemx --threshold=@var{size}
 @opindex -t
 @opindex --threshold
-Exclude entries based on a given @var{size} (@pxref{Block size}).
+Exclude entries based on a given @var{size}.  The @var{size} refers to used
+blocks in normal mode (@pxref{Block size}), or inodes count in conjunction
+with the @option{--inodes} option.
 
 If @var{size} is positive, then @command{du} will only print entries with a size
 greater than or equal to that.
@@ -11501,6 +11515,10 @@ Please note that the @option{--threshold} option can be combined with the
 @option{--apparent-size} option, and in this case would elide entries based on
 its apparent size.
 
+Please note that the @option{--threshold} option can be combined with the
+@option{--inodes} option, and in this case would elide entries based on
+its inodes count.
+
 Here's how you would use @option{--threshold} to find directories with a size
 greater than or equal to 200 megabytes:
 
@@ -11515,6 +11533,13 @@ note the @option{-a} - with an apparent size smaller than or equal to 500 bytes:
 du -a -t -500 --apparent-size
 @end example
 
+Here's how you would use @option{--threshold} to find directories on the root
+file system with more than 20000 inodes used in the directory tree below:
+
+@example
+du --inodes -x --threshold=20000 /
+@end example
+
 
 @item --time
 @opindex --time
index a6fa16b29d21993abaa380f083b0dd54ffb3c325..9f1f98c5c9afca3730e443c74be939cd623f9077 100644 (file)
--- a/src/du.c
+++ b/src/du.c
@@ -78,6 +78,9 @@ struct duinfo
   /* Size of files in directory.  */
   uintmax_t size;
 
+  /* Number of inodes in directory.  */
+  uintmax_t inodes;
+
   /* Latest time stamp found.  If tmax.tv_sec == TYPE_MINIMUM (time_t)
      && tmax.tv_nsec < 0, no time stamp has been found.  */
   struct timespec tmax;
@@ -88,6 +91,7 @@ static inline void
 duinfo_init (struct duinfo *a)
 {
   a->size = 0;
+  a->inodes = 0;
   a->tmax.tv_sec = TYPE_MINIMUM (time_t);
   a->tmax.tv_nsec = -1;
 }
@@ -97,6 +101,7 @@ static inline void
 duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
 {
   a->size = size;
+  a->inodes = 1;
   a->tmax = tmax;
 }
 
@@ -106,6 +111,7 @@ duinfo_add (struct duinfo *a, struct duinfo const *b)
 {
   uintmax_t sum = a->size + b->size;
   a->size = a->size <= sum ? sum : UINTMAX_MAX;
+  a->inodes = a->inodes + b->inodes;
   if (timespec_cmp (a->tmax, b->tmax) < 0)
     a->tmax = b->tmax;
 }
@@ -154,6 +160,9 @@ static intmax_t opt_threshold = 0;
 /* Human-readable options for output.  */
 static int human_output_opts;
 
+/* Output inodes count instead of blocks used.  */
+static bool opt_inodes = false;
+
 /* If true, print most recently modified date, using the specified format.  */
 static bool opt_time = false;
 
@@ -197,7 +206,8 @@ enum
   HUMAN_SI_OPTION,
   FTS_DEBUG,
   TIME_OPTION,
-  TIME_STYLE_OPTION
+  TIME_STYLE_OPTION,
+  INODES_OPTION
 };
 
 static struct option const long_options[] =
@@ -214,6 +224,7 @@ static struct option const long_options[] =
   {"exclude-from", required_argument, NULL, 'X'},
   {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
   {"human-readable", no_argument, NULL, 'h'},
+  {"inodes", no_argument, NULL, INODES_OPTION},
   {"si", no_argument, NULL, HUMAN_SI_OPTION},
   {"max-depth", required_argument, NULL, 'd'},
   {"null", no_argument, NULL, '0'},
@@ -306,6 +317,7 @@ Summarize disk usage of each FILE, recursively for directories.\n\
   -H                    equivalent to --dereference-args (-D)\n\
   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\
 \n\
+      --inodes          list inode usage information instead of block usage\n\
 "), stdout);
       fputs (_("\
   -k                    like --block-size=1K\n\
@@ -394,7 +406,10 @@ print_only_size (uintmax_t n_bytes)
 static void
 print_size (const struct duinfo *pdui, const char *string)
 {
-  print_only_size (pdui->size);
+  print_only_size (opt_inodes
+                   ? pdui->inodes
+                   : pdui->size);
+
   if (opt_time)
     {
       putchar ('\t');
@@ -589,9 +604,10 @@ process_file (FTS *fts, FTSENT *ent)
       || level == 0)
     {
       /* Print or elide this entry according to the --threshold option.  */
+      uintmax_t v = opt_inodes ? dui_to_print.inodes : dui_to_print.size;
       if (opt_threshold < 0
-          ? dui_to_print.size <= -opt_threshold
-          : dui_to_print.size >= opt_threshold)
+          ? v <= -opt_threshold
+          : v >= opt_threshold)
         print_size (&dui_to_print, file);
     }
 
@@ -853,6 +869,10 @@ main (int argc, char **argv)
           add_exclude (exclude, optarg, EXCLUDE_WILDCARDS);
           break;
 
+        case INODES_OPTION:
+          opt_inodes = true;
+          break;
+
         case TIME_OPTION:
           opt_time = true;
           time_type =
@@ -899,6 +919,16 @@ main (int argc, char **argv)
   if (opt_summarize_only)
     max_depth = 0;
 
+  if (opt_inodes)
+    {
+      if (apparent_size)
+        {
+          error (0, 0, _("warning: options --apparent-size and -b are "
+                         "ineffective with --inodes"));
+        }
+      output_block_size = 1;
+    }
+
   /* Process time style if printing last times.  */
   if (opt_time)
     {
diff --git a/tests/du/inodes.sh b/tests/du/inodes.sh
new file mode 100755 (executable)
index 0000000..2069e2b
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/sh
+# exercise du's --inodes option
+
+# Copyright (C) 2010-2013 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ du
+
+# An empty directory uses only 1 inode.
+mkdir d || framework_failure_
+printf '1\td\n' > exp || framework_failure_
+
+du --inodes d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Add a regular file: 2 inodes used.
+touch d/f || framework_failure_
+printf '2\td\n' > exp || framework_failure_
+
+du --inodes d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Add a hardlink to the file: still only 2 inodes used.
+ln -v d/f d/h || framework_failure_
+du --inodes d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Now count also hardlinks (-l,--count-links): 3 inodes.
+printf '3\td\n' > exp || framework_failure_
+du --inodes -l d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Create a directory and summarize: 3 inodes.
+mkdir d/d || framework_failure_
+du --inodes -s d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Count inodes separated: 1-2.
+printf '1\td/d\n2\td\n' > exp || framework_failure_
+du --inodes -S d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Count inodes cumulative (default): 1-3.
+printf '1\td/d\n3\td\n' > exp || framework_failure_
+du --inodes d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Count all items: 1-1-3.
+printf '1\td/d\n1\td/h\n3\td\n' > exp || framework_failure_
+du --inodes -a d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Count all items and hardlinks again: 1-1-1-4
+printf '1\td/d\n1\td/h\n1\td/f\n4\td\n' > exp || framework_failure_
+du --inodes -al d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Run with total (-c) line: 1-3-3
+printf '1\td/d\n3\td\n3\ttotal\n' > exp || framework_failure_
+du --inodes -c d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Create another file in the subdirectory: 2-4
+touch d/d/f || framework_failure_
+printf '2\td/d\n4\td\n' > exp || framework_failure_
+du --inodes d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Ensure human output (-h, --si) works.
+rm -rf d || framework_failure_
+mkdir d || framework_failure_
+seq --format="d/file%g" 1023 | xargs touch || framework_failure_
+printf '1.0K\td\n' > exp || framework_failure_
+du --inodes -h d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+printf '1.1k\td\n' > exp || framework_failure_
+du --inodes --si d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Verify --inodes ignores -B.
+printf '1024\td\n' > exp || framework_failure_
+du --inodes -B10 d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Verify --inodes works with --threshold.
+printf '1024\td\n' > exp || framework_failure_
+du --inodes --threshold=1000 d > out 2>err || fail=1
+compare exp out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+du --inodes --threshold=-1000 d > out 2>err || fail=1
+compare /dev/null out || { cat out; fail=1; }
+compare /dev/null err || fail=1
+
+# Verify --inodes raises a warning for --apparent-size and -b.
+du --inodes -b d > out 2>err || fail=1
+grep ' ineffective ' err >/dev/null || { fail=1; cat out err; }
+
+du --inodes --apparent-size d > out 2>err || fail=1
+grep ' ineffective ' err >/dev/null || { fail=1; cat out err; }
+
+# Ensure that --inodes is mentioned in the usage.
+du --help > out || fail=1
+grep ' --inodes ' out >/dev/null || { fail=1; cat out; }
+Exit $fail
index 58b7958c2954263bb81d89cfdc651a5748a7eb17..b00ff5958a3331ffe3015311bac464f6aa7d2985 100644 (file)
@@ -494,6 +494,7 @@ all_tests =                                 \
   tests/du/inacc-dest.sh                       \
   tests/du/inacc-dir.sh                                \
   tests/du/inaccessible-cwd.sh                 \
+  tests/du/inodes.sh                           \
   tests/du/long-from-unreadable.sh             \
   tests/du/long-sloop.sh                       \
   tests/du/max-depth.sh                                \