Fix ag-wipe so that it runs on IRIX (getopt reorders args in glibc).
[xfstests-dev.git] / tools / ag-wipe
1 #!/usr/bin/perl -w
2 use strict;
3 use IO::File;
4 use Getopt::Std;
5
6 #
7 #  Copyright (c) 2003-2004 Silicon Graphics, Inc.  All Rights Reserved.
8 #  
9 #  This program is free software; you can redistribute it and/or modify it
10 #  under the terms of version 2 of the GNU General Public License as
11 #  published by the Free Software Foundation.
12 #  
13 #  This program is distributed in the hope that it would be useful, but
14 #  WITHOUT ANY WARRANTY; without even the implied warranty of
15 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 #  
17 #  Further, this software is distributed without any warranty that it is
18 #  free of the rightful claim of any third person regarding infringement
19 #  or the like.  Any license provided herein, whether implied or
20 #  otherwise, applies only to this software file.  Patent licenses, if
21 #  any, provided herein do not apply to combinations of this program with
22 #  other software, or any other product whatsoever.
23 #  
24 #  You should have received a copy of the GNU General Public License along
25 #  with this program; if not, write the Free Software Foundation, Inc., 59
26 #  Temple Place - Suite 330, Boston MA 02111-1307, USA.
27 #  
28 #  Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
29 #  Mountain View, CA  94043, or:
30 #  
31 #  http://www.sgi.com 
32 #  
33 #  For further information regarding this notice, see: 
34 #  
35 #  http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
36 #
37
38 #
39 # Modify a filesystem's superblock and AGF metadata structures
40 # so that only a subset of the allocation groups will be used.
41 # Intended use is in testing large virtual devices (eg. loop)
42 # with extremely large filesystems, where we want to ensure
43 # high allocation groups are used as much as possible (where
44 # the block addresses are large).
45 #
46
47 my %opt;
48 getopts('cf:l:qr:v', \%opt);
49
50 die "Usage: $0 [-f AG] [-l AG] [-r bytes] [-cqv] device\n" unless (@ARGV == 1);
51 my $device = shift @ARGV;
52 die "$device: no such file\n" unless (-e $device);
53
54 my $clearall = defined($opt{'c'}) ? 1 : 0;
55 my $retain = defined($opt{'r'}) ? $opt{'r'} : -1;
56 my $quiet = defined($opt{'q'}) ? 1 : 0;
57 my $verbose = defined($opt{'v'}) ? 1 : 0;
58 my $nagfirst = defined($opt{'f'}) ? $opt{'f'} : 0;
59 my $naglast = defined($opt{'l'}) ? $opt{'l'} : 0;
60
61
62 # "clearall" means clear everything barring the final AG.
63 # "retain" means clearall and retain a specified amount in the
64 # second-from-last AG as well (value is the number of bytes).
65 # [NB: retain with a value of zero is now the same as clearall].
66
67 if ($retain >= 0) {
68         $clearall = 1;
69 }
70
71 sub xfs_db {
72         my $xfsdb = 'xfs_db -x';
73         my %hash;
74
75         foreach (@_) {
76                 $xfsdb .= ' -c ' . $_;
77         }
78         print $xfsdb, ' ', $device, "\n" if ($verbose);
79
80         die unless open(DB, "$xfsdb $device 2>/dev/null |");
81         while (<DB>) {
82                 if (/^(\S+) = (.*)$/) {
83                         print if ($verbose);
84                         $hash{$1} = $2;
85                 }
86         }
87         return %hash;
88 }
89
90
91
92 # Stage 1: Get control information from the superblock
93
94
95 my @sbprint = ( '"print fdblocks"', '"print agcount"', '"print blocksize"' );
96 my @agffree = ( '"print freeblks"' );
97
98 my %sb = xfs_db 'sb', @sbprint;
99 print "=== Initially ", $sb{'fdblocks'}, " blocks free across ",
100         $sb{'agcount'}, " AGs\n" unless $quiet;
101 if ($clearall && ($nagfirst || $naglast)) {
102         print STDERR "  o Clearall/retain specified with first/last AG\n";
103         exit(1);
104 }
105 if ($nagfirst >= $sb{'agcount'}) {
106         print STDERR "  o First AG number is too large\n";
107         exit(1);
108 }
109 if ($naglast >= $sb{'agcount'}) {
110         print STDERR "  o Last AG number is too large\n";
111         exit(1);
112 }
113 if ($naglast - $nagfirst < 0) {
114         print STDERR "  o No AGs to clear\n";
115         exit(1);
116 }
117 if ($clearall) {
118         $naglast = $sb{'agcount'} - 2;
119         if ($retain > 0) {
120                 my %check;
121
122                 $naglast--;
123                 $retain /= $sb{'blocksize'};    # convert to fsblocks
124                 %check = xfs_db "'agf $naglast'", @agffree;
125                 if ($check{'freeblks'} < $retain) {
126                         print STDERR "  o Insufficient space to retain\n";
127                         exit(1);
128                 }
129         }
130 }
131
132
133
134 # Stage 2: Wipe out all completely masked allocation groups.
135
136
137 my @agfprint = ( '"print freeblks"', '"print flcount"' );
138 my @agfcommands = ( '"write freeblks 0"',
139                         '"write longest 0"', '"write flcount 0"',
140                         '"write bnolevel 1"', '"write cntlevel 1"',
141                         '"write flfirst 0"', '"write fllast 0"' );
142 my @bnoprint = ( '"addr bnoroot"', '"print numrecs"' );
143 my @bnocommands = ( '"addr bnoroot"', '"write numrecs 0"',
144                         '"write leftsib -1"', '"write rightsib -1"' );
145 my @cntprint = ( '"addr cntroot"', '"print numrecs"' );
146 my @cntcommands = ( '"addr cntroot"', '"write numrecs 0"',
147                         '"write leftsib -1"', '"write rightsib -1"' );
148
149 print "=== Wiping ", $naglast - $nagfirst + 1,
150         " AGs starting from AG #", $nagfirst, "\n" unless $quiet;
151
152 my $ag = $nagfirst;
153 while ($ag <= $naglast) {
154         print "  o AG#", $ag, " AGF fields\n" unless $quiet;
155
156         my %agf = xfs_db "'agf $ag'", @agfprint;
157         xfs_db "'agf $ag'", @agfcommands;
158
159         my $blockcnt = $agf{'freeblks'} + $agf{'flcount'};
160         $sb{'fdblocks'} -= $blockcnt;
161         print "     cleared ", $blockcnt, " blocks from AG#", $ag, "\n"
162                 unless $quiet;
163
164         my %btree = xfs_db "'agf $ag'", @bnoprint;
165         xfs_db "'agf $ag'", @bnocommands, "'agf $ag'", @cntcommands;
166         print "     cleared ", $btree{'numrecs'}, " BNO/CNT btree recs in AG#",
167                 $ag, "\n" unless $quiet;
168
169         $ag++;
170 }
171
172
173
174 # Stage 3: Wipe out any partially masked allocation group.
175
176
177 if ($retain > 0) {
178         print "  o AG#", $ag, " AGF fields (partial)\n" unless $quiet;
179
180         my %ragf = xfs_db "'agf $ag'", '"print freeblks"',
181                 '"addr bnoroot"', '"print recs[1].startblock"';
182         my $maskblks = $ragf{'freeblks'} - $retain;
183         my $newstart = $ragf{'recs[1].startblock'} + $maskblks;
184         xfs_db "'agf $ag'",
185                 "'write freeblks $retain'", "'write longest $retain'",
186                 "'agf $ag'", '"addr bnoroot"',
187                 "'write recs[1].startblock $newstart'",
188                 "'write recs[1].blockcount $retain'",
189                 "'agf $ag'", '"addr cntroot"',
190                 "'write recs[1].startblock $newstart'",
191                 "'write recs[1].blockcount $retain'";
192
193         $sb{'fdblocks'} -= $maskblks;
194         print "     cleared ", $maskblks, " blocks from AG#", $ag, "\n"
195                 unless $quiet;
196 }
197
198 print "=== Updating final freespace count, ", $sb{'fdblocks'}, " blocks\n"
199         unless $quiet;
200 xfs_db "'sb 0'", "'write fdblocks $sb{'fdblocks'}'"