summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernhard Voelker <mail@bernhard-voelker.de>2013-07-27 14:25:28 +0200
committerBernhard Voelker <mail@bernhard-voelker.de>2013-07-27 14:25:28 +0200
commit333dc83d52e014a0b532e316ea8cd93b048f1ac6 (patch)
tree3bf519653280e660e7311e436b6e0e0fee8160a5
parent2bdb74ec1a453f6c6084d042e573de436ec205f3 (diff)
downloadcoreutils-333dc83d52e014a0b532e316ea8cd93b048f1ac6.tar.xz
du: add --inodes option
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.
-rw-r--r--NEWS3
-rw-r--r--doc/coreutils.texi27
-rw-r--r--src/du.c38
-rwxr-xr-xtests/du/inodes.sh133
-rw-r--r--tests/local.mk1
5 files changed, 197 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 3d0fb18b1..4a78617c7 100644
--- 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.
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index b8d40b4f5..40bff7ae9 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -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
diff --git a/src/du.c b/src/du.c
index a6fa16b29..9f1f98c5c 100644
--- 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
index 000000000..2069e2b2b
--- /dev/null
+++ b/tests/du/inodes.sh
@@ -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
diff --git a/tests/local.mk b/tests/local.mk
index 58b7958c2..b00ff5958 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -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 \