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