]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/frag.c
metadump: bounds check btree block regions being zeroed
[thirdparty/xfsprogs-dev.git] / db / frag.c
CommitLineData
2bd0ea18 1/*
da23017d
NS
2 * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
dfc130f3 4 *
da23017d
NS
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
2bd0ea18 7 * published by the Free Software Foundation.
dfc130f3 8 *
da23017d
NS
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
dfc130f3 13 *
da23017d
NS
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2bd0ea18
NS
17 */
18
6b803e5a 19#include "libxfs.h"
2bd0ea18
NS
20#include <sys/time.h>
21#include "bmap.h"
22#include "command.h"
2bd0ea18
NS
23#include "frag.h"
24#include "io.h"
25#include "output.h"
26#include "type.h"
4ca431fc 27#include "init.h"
2bd0ea18
NS
28#include "malloc.h"
29
30typedef struct extent {
31 xfs_fileoff_t startoff;
32 xfs_filblks_t blockcount;
33} extent_t;
34
35typedef struct extmap {
36 int naents;
37 int nents;
38 extent_t ents[1];
39} extmap_t;
40#define EXTMAP_SIZE(n) \
41 (offsetof(extmap_t, ents) + (sizeof(extent_t) * (n)))
42
43static int aflag;
44static int dflag;
45static __uint64_t extcount_actual;
46static __uint64_t extcount_ideal;
47static int fflag;
48static int lflag;
49static int qflag;
50static int Rflag;
51static int rflag;
52static int vflag;
53
b3563c19 54typedef void (*scan_lbtree_f_t)(struct xfs_btree_block *block,
2bd0ea18
NS
55 int level,
56 extmap_t **extmapp,
57 typnm_t btype);
58
b3563c19 59typedef void (*scan_sbtree_f_t)(struct xfs_btree_block *block,
2bd0ea18
NS
60 int level,
61 xfs_agf_t *agf);
62
63static extmap_t *extmap_alloc(xfs_extnum_t nex);
64static xfs_extnum_t extmap_ideal(extmap_t *extmap);
65static void extmap_set_ext(extmap_t **extmapp, xfs_fileoff_t o,
66 xfs_extlen_t c);
67static int frag_f(int argc, char **argv);
68static int init(int argc, char **argv);
b9652a81 69static void process_bmbt_reclist(xfs_bmbt_rec_t *rp, int numrecs,
2bd0ea18
NS
70 extmap_t **extmapp);
71static void process_btinode(xfs_dinode_t *dip, extmap_t **extmapp,
72 int whichfork);
73static void process_exinode(xfs_dinode_t *dip, extmap_t **extmapp,
74 int whichfork);
75static void process_fork(xfs_dinode_t *dip, int whichfork);
76static void process_inode(xfs_agf_t *agf, xfs_agino_t agino,
77 xfs_dinode_t *dip);
78static void scan_ag(xfs_agnumber_t agno);
79static void scan_lbtree(xfs_fsblock_t root, int nlevels,
80 scan_lbtree_f_t func, extmap_t **extmapp,
81 typnm_t btype);
82static void scan_sbtree(xfs_agf_t *agf, xfs_agblock_t root,
83 int nlevels, scan_sbtree_f_t func,
84 typnm_t btype);
b3563c19 85static void scanfunc_bmap(struct xfs_btree_block *block, int level,
2bd0ea18 86 extmap_t **extmapp, typnm_t btype);
b3563c19 87static void scanfunc_ino(struct xfs_btree_block *block, int level,
2bd0ea18
NS
88 xfs_agf_t *agf);
89
dfc130f3 90static const cmdinfo_t frag_cmd =
2bd0ea18 91 { "frag", NULL, frag_f, 0, -1, 0,
2c794e6e 92 "[-a] [-d] [-f] [-l] [-q] [-R] [-r] [-v]",
2bd0ea18
NS
93 "get file fragmentation data", NULL };
94
95static extmap_t *
96extmap_alloc(
97 xfs_extnum_t nex)
98{
99 extmap_t *extmap;
100
101 if (nex < 1)
102 nex = 1;
103 extmap = xmalloc(EXTMAP_SIZE(nex));
104 extmap->naents = nex;
105 extmap->nents = 0;
106 return extmap;
107}
108
109static xfs_extnum_t
110extmap_ideal(
111 extmap_t *extmap)
112{
113 extent_t *ep;
114 xfs_extnum_t rval;
115
116 for (ep = &extmap->ents[0], rval = 0;
117 ep < &extmap->ents[extmap->nents];
118 ep++) {
119 if (ep == &extmap->ents[0] ||
120 ep->startoff != ep[-1].startoff + ep[-1].blockcount)
121 rval++;
122 }
123 return rval;
124}
125
126static void
127extmap_set_ext(
128 extmap_t **extmapp,
129 xfs_fileoff_t o,
130 xfs_extlen_t c)
131{
132 extmap_t *extmap;
133 extent_t *ent;
134
135 extmap = *extmapp;
136 if (extmap->nents == extmap->naents) {
137 extmap->naents++;
138 extmap = xrealloc(extmap, EXTMAP_SIZE(extmap->naents));
139 *extmapp = extmap;
140 }
141 ent = &extmap->ents[extmap->nents];
142 ent->startoff = o;
143 ent->blockcount = c;
144 extmap->nents++;
145}
146
147void
148frag_init(void)
149{
150 add_command(&frag_cmd);
151}
152
153/*
154 * Get file fragmentation information.
155 */
156static int
157frag_f(
158 int argc,
159 char **argv)
160{
161 xfs_agnumber_t agno;
162 double answer;
163
164 if (!init(argc, argv))
165 return 0;
166 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
167 scan_ag(agno);
168 if (extcount_actual)
169 answer = (double)(extcount_actual - extcount_ideal) * 100.0 /
170 (double)extcount_actual;
171 else
172 answer = 0.0;
9ee7055c 173 dbprintf(_("actual %llu, ideal %llu, fragmentation factor %.2f%%\n"),
2bd0ea18
NS
174 extcount_actual, extcount_ideal, answer);
175 return 0;
176}
177
178static int
179init(
180 int argc,
181 char **argv)
182{
183 int c;
184
185 aflag = dflag = fflag = lflag = qflag = Rflag = rflag = vflag = 0;
186 optind = 0;
187 while ((c = getopt(argc, argv, "adflqRrv")) != EOF) {
188 switch (c) {
189 case 'a':
190 aflag = 1;
191 break;
192 case 'd':
193 dflag = 1;
194 break;
195 case 'f':
196 fflag = 1;
197 break;
198 case 'l':
199 lflag = 1;
200 break;
201 case 'q':
202 qflag = 1;
203 break;
204 case 'R':
205 Rflag = 1;
206 break;
207 case 'r':
208 rflag = 1;
209 break;
210 case 'v':
211 vflag = 1;
212 break;
213 default:
9ee7055c 214 dbprintf(_("bad option for frag command\n"));
2bd0ea18
NS
215 return 0;
216 }
217 }
218 if (!aflag && !dflag && !fflag && !lflag && !qflag && !Rflag && !rflag)
219 aflag = dflag = fflag = lflag = qflag = Rflag = rflag = 1;
220 extcount_actual = extcount_ideal = 0;
221 return 1;
222}
223
224static void
225process_bmbt_reclist(
b9652a81 226 xfs_bmbt_rec_t *rp,
2bd0ea18
NS
227 int numrecs,
228 extmap_t **extmapp)
229{
5a35bf2c 230 xfs_filblks_t c;
2bd0ea18
NS
231 int f;
232 int i;
5a35bf2c
DC
233 xfs_fileoff_t o;
234 xfs_fsblock_t s;
2bd0ea18
NS
235
236 for (i = 0; i < numrecs; i++, rp++) {
b9652a81 237 convert_extent(rp, &o, &s, &c, &f);
2bd0ea18
NS
238 extmap_set_ext(extmapp, (xfs_fileoff_t)o, (xfs_extlen_t)c);
239 }
240}
241
242static void
243process_btinode(
244 xfs_dinode_t *dip,
245 extmap_t **extmapp,
246 int whichfork)
247{
248 xfs_bmdr_block_t *dib;
249 int i;
250 xfs_bmbt_ptr_t *pp;
2bd0ea18
NS
251
252 dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
5e656dbb 253 if (be16_to_cpu(dib->bb_level) == 0) {
b9652a81 254 xfs_bmbt_rec_t *rp = XFS_BMDR_REC_ADDR(dib, 1);
5e656dbb 255 process_bmbt_reclist(rp, be16_to_cpu(dib->bb_numrecs), extmapp);
2bd0ea18
NS
256 return;
257 }
b3563c19 258 pp = XFS_BMDR_PTR_ADDR(dib, 1,
ff105f75 259 xfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0));
5e656dbb 260 for (i = 0; i < be16_to_cpu(dib->bb_numrecs); i++)
f8149110 261 scan_lbtree(be64_to_cpu(pp[i]), be16_to_cpu(dib->bb_level),
5e656dbb 262 scanfunc_bmap, extmapp,
2bd0ea18
NS
263 whichfork == XFS_DATA_FORK ? TYP_BMAPBTD : TYP_BMAPBTA);
264}
265
266static void
267process_exinode(
268 xfs_dinode_t *dip,
269 extmap_t **extmapp,
270 int whichfork)
271{
b9652a81 272 xfs_bmbt_rec_t *rp;
2bd0ea18 273
b9652a81 274 rp = (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork);
5e656dbb 275 process_bmbt_reclist(rp, XFS_DFORK_NEXTENTS(dip, whichfork), extmapp);
2bd0ea18
NS
276}
277
278static void
279process_fork(
280 xfs_dinode_t *dip,
281 int whichfork)
282{
283 extmap_t *extmap;
284 int nex;
285
5e656dbb 286 nex = XFS_DFORK_NEXTENTS(dip, whichfork);
2bd0ea18
NS
287 if (!nex)
288 return;
289 extmap = extmap_alloc(nex);
290 switch (XFS_DFORK_FORMAT(dip, whichfork)) {
291 case XFS_DINODE_FMT_EXTENTS:
292 process_exinode(dip, &extmap, whichfork);
293 break;
294 case XFS_DINODE_FMT_BTREE:
295 process_btinode(dip, &extmap, whichfork);
296 break;
297 }
298 extcount_actual += extmap->nents;
299 extcount_ideal += extmap_ideal(extmap);
300 xfree(extmap);
301}
302
303static void
304process_inode(
305 xfs_agf_t *agf,
306 xfs_agino_t agino,
307 xfs_dinode_t *dip)
308{
309 __uint64_t actual;
2bd0ea18
NS
310 __uint64_t ideal;
311 xfs_ino_t ino;
312 int skipa;
313 int skipd;
314
5e656dbb 315 ino = XFS_AGINO_TO_INO(mp, be32_to_cpu(agf->agf_seqno), agino);
56b2de80 316 switch (be16_to_cpu(dip->di_mode) & S_IFMT) {
322f2a29 317 case S_IFDIR:
2bd0ea18
NS
318 skipd = !dflag;
319 break;
322f2a29 320 case S_IFREG:
56b2de80 321 if (!rflag && (be16_to_cpu(dip->di_flags) & XFS_DIFLAG_REALTIME))
2bd0ea18
NS
322 skipd = 1;
323 else if (!Rflag &&
324 (ino == mp->m_sb.sb_rbmino ||
325 ino == mp->m_sb.sb_rsumino))
326 skipd = 1;
327 else if (!qflag &&
328 (ino == mp->m_sb.sb_uquotino ||
0340d706
CS
329 ino == mp->m_sb.sb_gquotino ||
330 ino == mp->m_sb.sb_pquotino))
2bd0ea18
NS
331 skipd = 1;
332 else
333 skipd = !fflag;
334 break;
322f2a29 335 case S_IFLNK:
2bd0ea18
NS
336 skipd = !lflag;
337 break;
338 default:
339 skipd = 1;
340 break;
341 }
342 actual = extcount_actual;
343 ideal = extcount_ideal;
344 if (!skipd)
345 process_fork(dip, XFS_DATA_FORK);
346 skipa = !aflag || !XFS_DFORK_Q(dip);
347 if (!skipa)
348 process_fork(dip, XFS_ATTR_FORK);
349 if (vflag && (!skipd || !skipa))
9ee7055c 350 dbprintf(_("inode %lld actual %lld ideal %lld\n"),
2bd0ea18
NS
351 ino, extcount_actual - actual, extcount_ideal - ideal);
352}
353
354static void
355scan_ag(
356 xfs_agnumber_t agno)
357{
358 xfs_agf_t *agf;
359 xfs_agi_t *agi;
360
361 push_cur();
9440d84d
NS
362 set_cur(&typtab[TYP_AGF],
363 XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
364 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
2bd0ea18 365 if ((agf = iocur_top->data) == NULL) {
9ee7055c 366 dbprintf(_("can't read agf block for ag %u\n"), agno);
2bd0ea18
NS
367 pop_cur();
368 return;
369 }
370 push_cur();
9440d84d
NS
371 set_cur(&typtab[TYP_AGI],
372 XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
373 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
2bd0ea18 374 if ((agi = iocur_top->data) == NULL) {
9ee7055c 375 dbprintf(_("can't read agi block for ag %u\n"), agno);
2bd0ea18
NS
376 pop_cur();
377 pop_cur();
378 return;
379 }
f8149110 380 scan_sbtree(agf, be32_to_cpu(agi->agi_root),
5e656dbb 381 be32_to_cpu(agi->agi_level), scanfunc_ino, TYP_INOBT);
2bd0ea18
NS
382 pop_cur();
383 pop_cur();
384}
385
386static void
387scan_lbtree(
388 xfs_fsblock_t root,
389 int nlevels,
390 scan_lbtree_f_t func,
391 extmap_t **extmapp,
392 typnm_t btype)
393{
394 push_cur();
395 set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, root), blkbb, DB_RING_IGN,
396 NULL);
397 if (iocur_top->data == NULL) {
9ee7055c 398 dbprintf(_("can't read btree block %u/%u\n"),
2bd0ea18
NS
399 XFS_FSB_TO_AGNO(mp, root),
400 XFS_FSB_TO_AGBNO(mp, root));
401 return;
402 }
403 (*func)(iocur_top->data, nlevels - 1, extmapp, btype);
404 pop_cur();
405}
406
407static void
408scan_sbtree(
409 xfs_agf_t *agf,
410 xfs_agblock_t root,
411 int nlevels,
412 scan_sbtree_f_t func,
413 typnm_t btype)
414{
5e656dbb 415 xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
2bd0ea18
NS
416
417 push_cur();
418 set_cur(&typtab[btype], XFS_AGB_TO_DADDR(mp, seqno, root),
419 blkbb, DB_RING_IGN, NULL);
420 if (iocur_top->data == NULL) {
9ee7055c 421 dbprintf(_("can't read btree block %u/%u\n"), seqno, root);
2bd0ea18
NS
422 return;
423 }
424 (*func)(iocur_top->data, nlevels - 1, agf);
425 pop_cur();
426}
427
428static void
429scanfunc_bmap(
b3563c19 430 struct xfs_btree_block *block,
2bd0ea18
NS
431 int level,
432 extmap_t **extmapp,
433 typnm_t btype)
434{
2bd0ea18
NS
435 int i;
436 xfs_bmbt_ptr_t *pp;
5e656dbb 437 xfs_bmbt_rec_t *rp;
8ad2cf44
ES
438 int nrecs;
439
440 nrecs = be16_to_cpu(block->bb_numrecs);
2bd0ea18
NS
441
442 if (level == 0) {
8ad2cf44
ES
443 if (nrecs > mp->m_bmap_dmxr[0]) {
444 dbprintf(_("invalid numrecs (%u) in %s block\n"),
445 nrecs, typtab[btype].name);
446 return;
447 }
b3563c19 448 rp = XFS_BMBT_REC_ADDR(mp, block, 1);
b9652a81 449 process_bmbt_reclist(rp, nrecs, extmapp);
8ad2cf44
ES
450 return;
451 }
452
453 if (nrecs > mp->m_bmap_dmxr[1]) {
454 dbprintf(_("invalid numrecs (%u) in %s block\n"),
455 nrecs, typtab[btype].name);
2bd0ea18
NS
456 return;
457 }
b3563c19 458 pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[0]);
8ad2cf44 459 for (i = 0; i < nrecs; i++)
f8149110 460 scan_lbtree(be64_to_cpu(pp[i]), level, scanfunc_bmap, extmapp,
5e656dbb 461 btype);
2bd0ea18
NS
462}
463
464static void
465scanfunc_ino(
b3563c19 466 struct xfs_btree_block *block,
2bd0ea18
NS
467 int level,
468 xfs_agf_t *agf)
469{
470 xfs_agino_t agino;
5e656dbb 471 xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
2bd0ea18
NS
472 int i;
473 int j;
474 int off;
475 xfs_inobt_ptr_t *pp;
476 xfs_inobt_rec_t *rp;
477
478 if (level == 0) {
b3563c19 479 rp = XFS_INOBT_REC_ADDR(mp, block, 1);
5e656dbb
BN
480 for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++) {
481 agino = be32_to_cpu(rp[i].ir_startino);
2bd0ea18
NS
482 off = XFS_INO_TO_OFFSET(mp, agino);
483 push_cur();
484 set_cur(&typtab[TYP_INODE],
485 XFS_AGB_TO_DADDR(mp, seqno,
486 XFS_AGINO_TO_AGBNO(mp, agino)),
ff105f75 487 XFS_FSB_TO_BB(mp, mp->m_ialloc_blks),
2bd0ea18
NS
488 DB_RING_IGN, NULL);
489 if (iocur_top->data == NULL) {
9ee7055c 490 dbprintf(_("can't read inode block %u/%u\n"),
2bd0ea18
NS
491 seqno, XFS_AGINO_TO_AGBNO(mp, agino));
492 continue;
493 }
494 for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
b34acbba 495 if (XFS_INOBT_IS_FREE_DISK(&rp[i], j))
2bd0ea18 496 continue;
5e656dbb 497 process_inode(agf, agino + j, (xfs_dinode_t *)
f8149110 498 ((char *)iocur_top->data +
5e656dbb 499 ((off + j) << mp->m_sb.sb_inodelog)));
2bd0ea18
NS
500 }
501 pop_cur();
502 }
503 return;
504 }
b3563c19 505 pp = XFS_INOBT_PTR_ADDR(mp, block, 1, mp->m_inobt_mxr[1]);
5e656dbb 506 for (i = 0; i < be16_to_cpu(block->bb_numrecs); i++)
f8149110 507 scan_sbtree(agf, be32_to_cpu(pp[i]), level, scanfunc_ino,
5e656dbb 508 TYP_INOBT);
2bd0ea18 509}