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