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