5 # Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
7 # This program is free software; you can redistribute it and/or modify it
8 # under the terms of version 2 of the GNU General Public License as
9 # published by the Free Software Foundation.
11 # This program is distributed in the hope that it would be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 # Further, this software is distributed without any warranty that it is
16 # free of the rightful claim of any third person regarding infringement
17 # or the like. Any license provided herein, whether implied or
18 # otherwise, applies only to this software file. Patent licenses, if
19 # any, provided herein do not apply to combinations of this program with
20 # other software, or any other product whatsoever.
22 # You should have received a copy of the GNU General Public License along
23 # with this program; if not, write the Free Software Foundation, Inc., 59
24 # Temple Place - Suite 330, Boston MA 02111-1307, USA.
26 # Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
27 # Mountain View, CA 94043, or:
31 # For further information regarding this notice, see:
33 # http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
39 # Fill a filesystem, or write a specified number of bits.
40 # Script runs deterministically given a seed for the random number
41 # generator. Will generate the same set of directories and files,
42 # with the only source of variation the precise point at which the
43 # filesystem fills up. When used with XFS filesystems, and when
44 # the filesize is small, XFS pre-allocation may cause the filesystem
45 # to "fill up" before the actual disk space is gone. Using this
46 # script repeatedly, with a delay between invocations, to account
47 # for extent flushing, will allow more of the filesystem to be filled.
49 # All generated files are checksummed and this sum is stored in the
50 # filename (files are named: <sequence number>.<checksum>.data.
51 # Normally script is invoked using the --list option, which causes all
52 # data files to be written to standard output (--list=-) or a file
53 # (--list=filename). This list can be fed into fill2fs_check, which
54 # recomputes the checksums and flags any errors.
56 # The --stddev=0 forces all files to be precisely --filesize bytes in
57 # length, some other value (--stddev=val) produces filesizes with a
58 # normal distribution with standard deviation val. If not specified
59 # most file sizes are set to lie within 30% of the requested file size.
61 # fill2fs is not guaranteed to write the requested number of bytes, or
62 # fill the filesystem completely. However, it and fill2 are both very
63 # careful about establishing when write operations fail. When the
64 # --verbose option is used the number of bytes "actually written"
65 # is guaranteed to be the number of bytes on disk.
76 # fsinfo: get filesystem info put it into the global namespace, initialises:
77 # $dev, $type, $blocks, $used, $avail, $cap, $mnt, $mnt_options
78 # $fsblocks, $fsblocksize, $agblocks, $agcount, $imax_pct, $logblocks
79 # $logstart, $internal
81 # if $verbose is set then output fs info to STDERR
87 # filesystem space and mount point
89 $cmd = "df" if ($os =~ /IRIX/);
90 $cmd = "df -P -T --block-size=512" if ($os =~ /Linux/);
91 chomp($_ = `$cmd $file | tail -1`);
92 $n = ($dev, $type, $blocks, $used, $avail, $cap, $mnt) = split(/ +/);
93 die("df failed") if ($n != 7);
95 if ($fscheck && $type ne "xfs") {
96 die("Error: $progname: filesystem is not xfs")
99 # how was this filesystem mounted
100 $_ = `grep $dev /etc/mtab`;
102 $mnt_options = $mtab[3];
104 # if we're running as root run xfs_db on the filesystem
106 # xfs_db: read only, use the default superblock, print everything
107 die("Error: $progname: can't read device: \"$dev\"\n") if (! -r $dev);
108 $_=`xfs_db -r -c sb -c p $dev`;
109 # multiline matching ^$ refers to individual lines...
110 /^dblocks = (\d+)$/m; $fsblocks=$1;
111 /^blocksize = (\d+)$/m; $fsblocksize=$1;
112 /^agblocks = (\d+)$/m; $agblocks=$1;
113 /^agcount = (\d+)$/m; $agcount=$1;
114 /^imax_pct = (\d+)$/m; $imax_pct=$1;
115 /^logblocks = (\d+)$/m; $logblocks=$1;
116 /^logstart = (\d+)$/m; $logstart=$1;
117 $internal = $logstart > 0 ? " (internal)" : "";
119 $verbose && print STDERR <<"EOF"
120 Filesystem information:
121 type=$type; device=$dev
122 mount point=$mnt; mount options=$mnt_options
123 percent full=$cap; size (512 byte blocks)=$blocks; used=$used; avail=$avail
124 total filesystem size (fs blocks)=$fsblocks; fs block size=$fsblocksize; imax_pct=$imax_pct
125 agcount=$agcount; agblocks=$agblocks; logblocks=$logblocks; logstart=$logstart$internal
131 # returns numbers with a normal distribution
137 for ($i = 0; $i < 12; $i++) {
141 $x = $mean + $stddev * $x;
146 # determine script location and find fill2
150 chomp($_ = `which fill2 2>&1 | head -1`);
156 # in the same directory - get absolute path
157 chomp($dirname = dirname $0);
158 if ($dirname =~ m!^/.*!) {
159 $fill2 = $dirname . "/fill2";
163 $fill2 = $cwd . "/" . $dirname . "/fill2";
166 die("Error: $progname: can't find fill2, tried \"$fill2\"\n");
175 GetOptions("bytes=f" => \$bytes,
177 "filesize=i" => \$filesize,
181 "fscheck!" => \$fscheck,
182 "percent=f" => \$percentage,
184 "stddev=i" => \$stddev,
185 "verbose!" => \$verbose);
188 # check/remove output directory, get filesystem info
190 || (! defined $root && @ARGV != 1)
191 || (defined $root && @ARGV == 1))
193 # newline at end of die message suppresses line number
194 print STDERR <<"EOF";
195 Usage: $progname [options] root_dir
197 --bytes=num total number of bytes to write
198 --dir=name where to write files
199 --filesize=num set all files to num bytes in size
200 --force overwrite any existing files
201 --help print this help message
202 --list=filename store created files to filename (- for stdout)
203 --percent=num percentage of filesystem to fill
204 --seed=num seed for random number generator
205 --stddev set file size standard deviation
206 --verbose verbose output
208 exit(1) unless defined $help;
216 # lots of boring argument checking
219 # root directory and filesystem info
220 $root = $ARGV[0] if (@ARGV == 1);
223 die("Error: $progname: \"$root\" already exists\n");
226 $verbose && print STDERR "Removing \"$root\"... ";
227 system("rm -rf $root");
228 $verbose && print STDERR "done\n";
231 chomp($root_dir = dirname $root);
234 # $list can be "-" for stdout, perl open ">-" opens stdout
235 open LIST, ">$list" if (defined $list);
237 # how many bytes should we write
238 if (defined $bytes && defined $percentage) {
239 die("Error: $progname: can't specify --bytes and --percent\n");
242 if (defined $percentage && ($percentage < 0 || $percentage > 100)) {
243 die("Error: $progname: invalid percentage\n");
245 if (! defined $bytes && ! defined $percentage) {
246 $bytes = $avail * 512;
247 $verbose && print STDERR <<"EOF";
248 Neither --bytes nor --percent specified: filling filesystem ($bytes bytes)
251 elsif (! defined $bytes) {
252 $bytes = int($blocks * 512 * $percentage / 100.0);
254 if (($bytes > $blocks * 512) || (! $force && $bytes > $avail * 512))
256 die("Error: $progname: not enough free disk space, disk is $cap full\n");
262 # To get fix sized files set stddev to 0. The default is to make most files
263 # within 30% of the requested filesize (or 4k if filesize is not specified).
264 # Set the standard deviation to be half of the required percentage: ~95% of
265 # samples lie within 2 standard deviations of the mean.
268 $filesize = 4096 if (! defined $filesize);
269 die("Error: $progname: --filesize must be >= 1 byte") if ($filesize < 1);
270 $stddev = 0.5 * 0.3 * $filesize if (! defined $stddev);
271 $seed = time ^ $$ if (! defined $seed);
285 $verbose && print STDERR "Writing $bytes bytes (seed is $seed)... ";
286 while ($total < $bytes) {
289 if (($d == 0 && $r < 0.5) || ($d > 0 && $r >= 0.0 && $r < 2.4)) {
290 # create a new data file
291 $n = sprintf("%04d", $names[$d]++);
296 $size = int(normal($filesize, $stddev));
298 $left = $bytes - $total;
299 $size = 0 if ($size < 0);
300 $size = $left if ($size > $left);
302 # fill2 will fail if the filesystem is full - not an error!
303 $cmd = "$fill2 -d nbytes=$size,linelength=72,seed=$n -b 4k $n";
304 $cmd .= " > /dev/null 2>&1" if (! $verbose);
305 if (system($cmd) != 0) {
307 warn("can't create a file - assuming filesystem is full\n");
309 if (-e $n && unlink($n) != 1) {
310 warn("couldn't delete \"$n\"");
315 ($sum) = split(/ +/);
316 $name = "$n.$sum.data";
317 $cmd = "mv $n $name"; # perl rename failed earlier than using mv
318 $cmd .= " > /dev/null 2>&1" if (! $verbose);
319 if (system($cmd) != 0) {
321 warn("can't rename a file - assuming filesystem is full\n");
323 if (-e $name && unlink($name) != 1) {
324 warn("couldn't delete \"$name\"");
330 printf LIST ("%s/%s\n", $_, $name);
335 # note that if d==0 create directories more frequently than files
336 elsif (($d == 0 && $r >= 0.5) || ($d > 0 && $r >= 2.4 && $r < 2.7)) {
337 # create a new directory and descend
338 $name = sprintf("%04d.d", $names[$d]++);
339 if (! mkdir($name, 0777)) {
340 warn("can't make a directory - assuming filesystem is full\n");
343 chdir($name) or die();
347 elsif ($r >= 2.7 && $r < 3.0) {
348 # pop up to the parent directory same probability as descend
349 die("Error: $progname: panic: shouldn't be here!") if ($d == 0);
350 chdir("..") or die();
354 # make sure we return to the original working directory
355 chdir($cwd) or die();
356 $verbose && print STDERR "done\n";
357 $verbose && print STDERR "$total bytes (in $files files and $dirs directories) were actually written\n";
359 exit(0) if ($total = $bytes);
360 exit(1) if ($total == 0);
361 exit(2) if ($total > 0 && $total < $bytes);
363 # - to sum all generated data:
364 # find /home/fill/ -name \*data | xargs ls -al | awk '{total = total + $5; } END { printf("total = %d bytes\n", total); }'
365 # - to find any files not of the required size
366 # find . -name \*data -a \! -size 4096c
368 # find ./fill -name \*.data | wc
369 # - count new directories
370 # find ./fill -name \*.d | wc