]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - spaceman/freesp.c
xfs: don't assert when on-disk btree pointers are garbage
[thirdparty/xfsprogs-dev.git] / spaceman / freesp.c
CommitLineData
cccf6abc
DC
1/*
2 * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
3 * Copyright (c) 2012 Red Hat, Inc.
4 * Copyright (c) 2017 Oracle.
5 * All Rights Reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include "libxfs.h"
22#include <linux/fiemap.h>
23#include "command.h"
24#include "init.h"
25#include "path.h"
26#include "space.h"
27#include "input.h"
28
29typedef struct histent
30{
31 long long low;
32 long long high;
33 long long count;
34 long long blocks;
35} histent_t;
36
37static int agcount;
38static xfs_agnumber_t *aglist;
39static histent_t *hist;
40static int dumpflag;
41static long long equalsize;
42static long long multsize;
43static int histcount;
44static int seen1;
45static int summaryflag;
f77f9908 46static int gflag;
cccf6abc
DC
47static bool rtflag;
48static long long totblocks;
49static long long totexts;
50
51static cmdinfo_t freesp_cmd;
52
53static void
54addhistent(
55 long long h)
56{
57 if (histcount == INT_MAX) {
58 printf(_("Too many histogram buckets.\n"));
59 return;
60 }
61 hist = realloc(hist, (histcount + 1) * sizeof(*hist));
62 if (h == 0)
63 h = 1;
64 hist[histcount].low = h;
65 hist[histcount].count = hist[histcount].blocks = 0;
66 histcount++;
67 if (h == 1)
68 seen1 = 1;
69}
70
71static void
72addtohist(
73 xfs_agnumber_t agno,
74 xfs_agblock_t agbno,
75 off64_t len)
76{
77 long i;
78
79 if (dumpflag)
80 printf("%8d %8d %8"PRId64"\n", agno, agbno, len);
81 totexts++;
82 totblocks += len;
83 for (i = 0; i < histcount; i++) {
84 if (hist[i].high >= len) {
85 hist[i].count++;
86 hist[i].blocks += len;
87 break;
88 }
89 }
90}
91
92static int
93hcmp(
94 const void *a,
95 const void *b)
96{
97 return ((histent_t *)a)->low - ((histent_t *)b)->low;
98}
99
100static void
101histinit(
102 long long maxlen)
103{
104 long long i;
105
106 if (equalsize) {
107 for (i = 1; i < maxlen; i += equalsize)
108 addhistent(i);
109 } else if (multsize) {
110 for (i = 1; i < maxlen; i *= multsize)
111 addhistent(i);
112 } else {
113 if (!seen1)
114 addhistent(1);
115 qsort(hist, histcount, sizeof(*hist), hcmp);
116 }
117 for (i = 0; i < histcount; i++) {
118 if (i < histcount - 1)
119 hist[i].high = hist[i + 1].low - 1;
120 else
121 hist[i].high = maxlen;
122 }
123}
124
125static void
126printhist(void)
127{
128 int i;
129
130 printf("%7s %7s %7s %7s %6s\n",
131 _("from"), _("to"), _("extents"), _("blocks"), _("pct"));
132 for (i = 0; i < histcount; i++) {
133 if (hist[i].count)
134 printf("%7lld %7lld %7lld %7lld %6.2f\n", hist[i].low,
135 hist[i].high, hist[i].count, hist[i].blocks,
136 hist[i].blocks * 100.0 / totblocks);
137 }
138}
139
140static int
141inaglist(
142 xfs_agnumber_t agno)
143{
144 int i;
145
146 if (agcount == 0)
147 return 1;
148 for (i = 0; i < agcount; i++)
149 if (aglist[i] == agno)
150 return 1;
151 return 0;
152}
153
154#define NR_EXTENTS 128
155
156static void
157scan_ag(
158 xfs_agnumber_t agno)
159{
160 struct fsmap_head *fsmap;
161 struct fsmap *extent;
162 struct fsmap *l, *h;
163 struct fsmap *p;
164 off64_t blocksize = file->geom.blocksize;
165 off64_t bperag;
166 off64_t aglen;
167 xfs_agblock_t agbno;
f77f9908
DW
168 unsigned long long freeblks = 0;
169 unsigned long long freeexts = 0;
cccf6abc
DC
170 int ret;
171 int i;
172
173 bperag = (off64_t)file->geom.agblocks * blocksize;
174
175 fsmap = malloc(fsmap_sizeof(NR_EXTENTS));
176 if (!fsmap) {
177 fprintf(stderr, _("%s: fsmap malloc failed.\n"), progname);
178 exitcode = 1;
179 return;
180 }
181
182 memset(fsmap, 0, sizeof(*fsmap));
183 fsmap->fmh_count = NR_EXTENTS;
184 l = fsmap->fmh_keys;
185 h = fsmap->fmh_keys + 1;
186 if (agno != NULLAGNUMBER) {
187 l->fmr_physical = agno * bperag;
188 h->fmr_physical = ((agno + 1) * bperag) - 1;
189 l->fmr_device = h->fmr_device = file->fs_path.fs_datadev;
190 } else {
191 l->fmr_physical = 0;
192 h->fmr_physical = ULLONG_MAX;
193 l->fmr_device = h->fmr_device = file->fs_path.fs_rtdev;
194 }
195 h->fmr_owner = ULLONG_MAX;
196 h->fmr_flags = UINT_MAX;
197 h->fmr_offset = ULLONG_MAX;
198
199 while (true) {
200 ret = ioctl(file->fd, FS_IOC_GETFSMAP, fsmap);
201 if (ret < 0) {
202 fprintf(stderr, _("%s: FS_IOC_GETFSMAP [\"%s\"]: %s\n"),
203 progname, file->name, strerror(errno));
204 free(fsmap);
205 exitcode = 1;
206 return;
207 }
208
209 /* No more extents to map, exit */
210 if (!fsmap->fmh_entries)
211 break;
212
213 for (i = 0, extent = fsmap->fmh_recs;
214 i < fsmap->fmh_entries;
215 i++, extent++) {
216 if (!(extent->fmr_flags & FMR_OF_SPECIAL_OWNER) ||
217 extent->fmr_owner != XFS_FMR_OWN_FREE)
218 continue;
219 agbno = (extent->fmr_physical - (bperag * agno)) /
220 blocksize;
221 aglen = extent->fmr_length / blocksize;
f77f9908
DW
222 freeblks += aglen;
223 freeexts++;
cccf6abc
DC
224
225 addtohist(agno, agbno, aglen);
226 }
227
228 p = &fsmap->fmh_recs[fsmap->fmh_entries - 1];
229 if (p->fmr_flags & FMR_OF_LAST)
230 break;
231 fsmap_advance(fsmap);
232 }
f77f9908
DW
233
234 if (gflag) {
235 if (agno == NULLAGNUMBER)
236 printf(_(" rtdev %10llu %10llu\n"), freeexts,
237 freeblks);
238 else
239 printf(_("%10u %10llu %10llu\n"), agno, freeexts,
240 freeblks);
241 }
e177680d 242 free(fsmap);
cccf6abc 243}
e177680d 244
cccf6abc
DC
245static void
246aglistadd(
247 char *a)
248{
249 xfs_agnumber_t x;
250
251 aglist = realloc(aglist, (agcount + 1) * sizeof(*aglist));
252 x = cvt_u32(a, 0);
253 if (errno) {
254 printf(_("Unrecognized AG number: %s\n"), a);
255 return;
256 }
257 aglist[agcount] = x;
258 agcount++;
259}
260
261static int
262init(
263 int argc,
264 char **argv)
265{
266 long long x;
267 int c;
268 int speced = 0; /* only one of -b -e -h or -m */
269
f77f9908 270 agcount = dumpflag = equalsize = multsize = optind = gflag = 0;
cccf6abc
DC
271 histcount = seen1 = summaryflag = 0;
272 totblocks = totexts = 0;
273 aglist = NULL;
274 hist = NULL;
275 rtflag = false;
276
f77f9908 277 while ((c = getopt(argc, argv, "a:bde:gh:m:rs")) != EOF) {
cccf6abc
DC
278 switch (c) {
279 case 'a':
280 aglistadd(optarg);
281 break;
282 case 'b':
283 if (speced)
284 goto many_spec;
285 multsize = 2;
286 speced = 1;
287 break;
288 case 'd':
289 dumpflag = 1;
290 break;
291 case 'e':
292 if (speced)
293 goto many_spec;
294 equalsize = cvt_s64(optarg, 0);
295 if (errno)
296 return command_usage(&freesp_cmd);
297 speced = 1;
298 break;
f77f9908
DW
299 case 'g':
300 histcount = 0;
301 gflag++;
302 break;
cccf6abc
DC
303 case 'h':
304 if (speced && !histcount)
305 goto many_spec;
306 /* addhistent increments histcount */
307 x = cvt_s64(optarg, 0);
308 if (errno)
309 return command_usage(&freesp_cmd);
310 addhistent(x);
311 speced = 1;
312 break;
313 case 'm':
314 if (speced)
315 goto many_spec;
316 multsize = cvt_s64(optarg, 0);
317 if (errno)
318 return command_usage(&freesp_cmd);
319 speced = 1;
320 break;
321 case 'r':
322 rtflag = true;
323 break;
324 case 's':
325 summaryflag = 1;
326 break;
327 case '?':
328 default:
329 return command_usage(&freesp_cmd);
330 }
331 }
332 if (optind != argc)
333 return 0;
334 if (!speced)
335 multsize = 2;
336 histinit(file->geom.agblocks);
337 return 1;
338many_spec:
339 return command_usage(&freesp_cmd);
340}
341
342/*
343 * Report on freespace usage in xfs filesystem.
344 */
345static int
346freesp_f(
347 int argc,
348 char **argv)
349{
350 xfs_agnumber_t agno;
351
352 if (!init(argc, argv))
353 return 0;
f77f9908
DW
354 if (gflag)
355 printf(_(" AG extents blocks\n"));
cccf6abc
DC
356 if (rtflag)
357 scan_ag(NULLAGNUMBER);
358 for (agno = 0; !rtflag && agno < file->geom.agcount; agno++) {
359 if (inaglist(agno))
360 scan_ag(agno);
361 }
f77f9908 362 if (histcount && !gflag)
cccf6abc
DC
363 printhist();
364 if (summaryflag) {
365 printf(_("total free extents %lld\n"), totexts);
366 printf(_("total free blocks %lld\n"), totblocks);
367 printf(_("average free extent size %g\n"),
368 (double)totblocks / (double)totexts);
369 }
370 if (aglist)
371 free(aglist);
372 if (hist)
373 free(hist);
374 return 0;
375}
376
377static void
378freesp_help(void)
379{
380 printf(_(
381"\n"
382"Examine filesystem free space\n"
383"\n"
384" -a agno -- Scan only the given AG agno.\n"
385" -b -- binary histogram bin size\n"
386" -d -- debug output\n"
387" -e bsize -- Use fixed histogram bin size of bsize\n"
f77f9908 388" -g -- Print only a per-AG summary.\n"
cccf6abc
DC
389" -h hbsz -- Use custom histogram bin size of h1.\n"
390" Multiple specifications are allowed.\n"
391" -m bmult -- Use histogram bin size multiplier of bmult.\n"
392" -r -- Display realtime device free space information.\n"
393" -s -- Emit freespace summary information.\n"
394"\n"
395"Only one of -b, -e, -h, or -m may be specified.\n"
396"\n"));
397
398}
399
400void
401freesp_init(void)
402{
403 freesp_cmd.name = "freesp";
404 freesp_cmd.altname = "fsp";
405 freesp_cmd.cfunc = freesp_f;
406 freesp_cmd.argmin = 0;
407 freesp_cmd.argmax = -1;
f77f9908 408 freesp_cmd.args = "[-dgrs] [-a agno]... [ -b | -e bsize | -h h1... | -m bmult ]";
cccf6abc
DC
409 freesp_cmd.flags = CMD_FLAG_ONESHOT;
410 freesp_cmd.oneline = _("Examine filesystem free space");
411 freesp_cmd.help = freesp_help;
412
413 add_command(&freesp_cmd);
414}
415