1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2019 Oracle.
6 #include "platform_defs.h"
11 #include "libfrog/paths.h"
12 #include "libfrog/fsgeom.h"
13 #include "libfrog/bulkstat.h"
16 static cmdinfo_t health_cmd
;
17 static unsigned long long reported
;
18 static bool comprehensive
;
21 static bool has_realtime(const struct xfs_fsop_geom
*g
)
23 return g
->rtblocks
> 0;
26 static bool has_finobt(const struct xfs_fsop_geom
*g
)
28 return g
->flags
& XFS_FSOP_GEOM_FLAGS_FINOBT
;
31 static bool has_rmapbt(const struct xfs_fsop_geom
*g
)
33 return g
->flags
& XFS_FSOP_GEOM_FLAGS_RMAPBT
;
36 static bool has_reflink(const struct xfs_fsop_geom
*g
)
38 return g
->flags
& XFS_FSOP_GEOM_FLAGS_REFLINK
;
43 bool (*has_fn
)(const struct xfs_fsop_geom
*g
);
47 static const struct flag_map fs_flags
[] = {
49 .mask
= XFS_FSOP_GEOM_SICK_COUNTERS
,
50 .descr
= "summary counters",
53 .mask
= XFS_FSOP_GEOM_SICK_UQUOTA
,
54 .descr
= "user quota",
57 .mask
= XFS_FSOP_GEOM_SICK_GQUOTA
,
58 .descr
= "group quota",
61 .mask
= XFS_FSOP_GEOM_SICK_PQUOTA
,
62 .descr
= "project quota",
65 .mask
= XFS_FSOP_GEOM_SICK_RT_BITMAP
,
66 .descr
= "realtime bitmap",
67 .has_fn
= has_realtime
,
70 .mask
= XFS_FSOP_GEOM_SICK_RT_SUMMARY
,
71 .descr
= "realtime summary",
72 .has_fn
= has_realtime
,
77 static const struct flag_map ag_flags
[] = {
79 .mask
= XFS_AG_GEOM_SICK_SB
,
80 .descr
= "superblock",
83 .mask
= XFS_AG_GEOM_SICK_AGF
,
84 .descr
= "AGF header",
87 .mask
= XFS_AG_GEOM_SICK_AGFL
,
88 .descr
= "AGFL header",
91 .mask
= XFS_AG_GEOM_SICK_AGI
,
92 .descr
= "AGI header",
95 .mask
= XFS_AG_GEOM_SICK_BNOBT
,
96 .descr
= "free space by block btree",
99 .mask
= XFS_AG_GEOM_SICK_CNTBT
,
100 .descr
= "free space by length btree",
103 .mask
= XFS_AG_GEOM_SICK_INOBT
,
104 .descr
= "inode btree",
107 .mask
= XFS_AG_GEOM_SICK_FINOBT
,
108 .descr
= "free inode btree",
109 .has_fn
= has_finobt
,
112 .mask
= XFS_AG_GEOM_SICK_RMAPBT
,
113 .descr
= "reverse mappings btree",
114 .has_fn
= has_rmapbt
,
117 .mask
= XFS_AG_GEOM_SICK_REFCNTBT
,
118 .descr
= "reference count btree",
119 .has_fn
= has_reflink
,
124 static const struct flag_map inode_flags
[] = {
126 .mask
= XFS_BS_SICK_INODE
,
127 .descr
= "inode core",
130 .mask
= XFS_BS_SICK_BMBTD
,
131 .descr
= "data fork",
134 .mask
= XFS_BS_SICK_BMBTA
,
135 .descr
= "extended attribute fork",
138 .mask
= XFS_BS_SICK_BMBTC
,
139 .descr
= "copy on write fork",
142 .mask
= XFS_BS_SICK_DIR
,
143 .descr
= "directory",
146 .mask
= XFS_BS_SICK_XATTR
,
147 .descr
= "extended attributes",
150 .mask
= XFS_BS_SICK_SYMLINK
,
151 .descr
= "symbolic link target",
154 .mask
= XFS_BS_SICK_PARENT
,
155 .descr
= "parent pointers",
160 /* Convert a flag mask to a report. */
164 const struct flag_map
*maps
,
166 unsigned int checked
)
168 const struct flag_map
*f
;
171 for (f
= maps
; f
->mask
!= 0; f
++) {
172 if (f
->has_fn
&& !f
->has_fn(&file
->xfd
.fsgeom
))
174 if (!(checked
& f
->mask
))
177 bad
= sick
& f
->mask
;
180 printf("%s %s: %s\n", descr
, _(f
->descr
),
181 bad
? _("unhealthy") : _("ok"));
185 /* Report on an AG's health. */
190 struct xfs_ag_geometry ageo
= { 0 };
194 ret
= xfrog_ag_geometry(file
->xfd
.fd
, agno
, &ageo
);
197 perror("ag_geometry");
200 snprintf(descr
, sizeof(descr
) - 1, _("AG %u"), agno
);
201 report_sick(descr
, ag_flags
, ageo
.ag_sick
, ageo
.ag_checked
);
205 /* Report on an inode's health. */
208 unsigned long long ino
,
211 struct xfs_bulkstat bs
;
216 snprintf(d
, sizeof(d
) - 1, _("inode %llu"), ino
);
220 ret
= xfrog_bulkstat_single(&file
->xfd
, ino
, 0, &bs
);
227 report_sick(descr
, inode_flags
, bs
.bs_sick
, bs
.bs_checked
);
231 /* Report on a file's health. */
236 struct stat stata
, statb
;
239 ret
= lstat(path
, &statb
);
245 ret
= fstat(file
->xfd
.fd
, &stata
);
251 if (stata
.st_dev
!= statb
.st_dev
) {
252 fprintf(stderr
, _("%s: not on the open filesystem"), path
);
256 return report_inode_health(statb
.st_ino
, path
);
259 #define BULKSTAT_NR (128)
262 * Report on all files' health for a given @agno. If @agno is NULLAGNUMBER,
263 * report on all files in the filesystem.
266 report_bulkstat_health(
269 struct xfs_bulkstat_req
*breq
;
274 breq
= xfrog_bulkstat_alloc_req(BULKSTAT_NR
, 0);
276 perror("bulk alloc req");
281 if (agno
!= NULLAGNUMBER
)
282 xfrog_bulkstat_set_ag(breq
, agno
);
285 error
= xfrog_bulkstat(&file
->xfd
, breq
);
288 for (i
= 0; i
< breq
->hdr
.ocount
; i
++) {
289 snprintf(descr
, sizeof(descr
) - 1, _("inode %"PRIu64
),
290 breq
->bulkstat
[i
].bs_ino
);
291 report_sick(descr
, inode_flags
,
292 breq
->bulkstat
[i
].bs_sick
,
293 breq
->bulkstat
[i
].bs_checked
);
295 } while (breq
->hdr
.ocount
> 0);
306 #define OPT_STRING ("a:cfi:q")
308 /* Report on health problems in XFS filesystem. */
314 unsigned long long x
;
316 bool default_report
= true;
322 if (file
->xfd
.fsgeom
.version
!= XFS_FSOP_GEOM_VERSION_V5
) {
327 /* Set our reporting options appropriately in the first pass. */
328 while ((c
= getopt(argc
, argv
, OPT_STRING
)) != EOF
) {
331 default_report
= false;
333 x
= strtoll(optarg
, NULL
, 10);
334 if (!errno
&& x
>= NULLAGNUMBER
)
342 comprehensive
= true;
345 default_report
= false;
348 default_report
= false;
350 x
= strtoll(optarg
, NULL
, 10);
352 perror("inode health");
360 return command_usage(&health_cmd
);
364 default_report
= false;
366 /* Reparse arguments, this time for reporting actions. */
368 while ((c
= getopt(argc
, argv
, OPT_STRING
)) != EOF
) {
371 agno
= strtoll(optarg
, NULL
, 10);
372 ret
= report_ag_sick(agno
);
373 if (!ret
&& comprehensive
)
374 ret
= report_bulkstat_health(agno
);
379 report_sick(_("filesystem"), fs_flags
,
380 file
->xfd
.fsgeom
.sick
,
381 file
->xfd
.fsgeom
.checked
);
383 ret
= report_bulkstat_health(NULLAGNUMBER
);
389 x
= strtoll(optarg
, NULL
, 10);
390 ret
= report_inode_health(x
, NULL
);
399 for (c
= optind
; c
< argc
; c
++) {
400 ret
= report_file_health(argv
[c
]);
405 /* No arguments gets us a summary of fs state. */
406 if (default_report
) {
407 report_sick(_("filesystem"), fs_flags
, file
->xfd
.fsgeom
.sick
,
408 file
->xfd
.fsgeom
.checked
);
410 for (agno
= 0; agno
< file
->xfd
.fsgeom
.agcount
; agno
++) {
411 ret
= report_ag_sick(agno
);
416 ret
= report_bulkstat_health(NULLAGNUMBER
);
424 _("Health status has not been collected for this filesystem.\n"));
426 _("Please run xfs_scrub(8) to remedy this situation.\n"));
437 "Report all observed filesystem health problems.\n"
439 " -a agno -- Report health of the given allocation group.\n"
440 " -c -- Report on the health of all inodes.\n"
441 " -f -- Report health of the overall filesystem.\n"
442 " -i inum -- Report health of a given inode number.\n"
443 " -q -- Only report unhealthy metadata.\n"
444 " paths -- Report health of the given file path.\n"
449 static cmdinfo_t health_cmd
= {
454 .args
= "[-a agno] [-c] [-f] [-i inum] [-q] [paths]",
455 .flags
= CMD_FLAG_ONESHOT
,
462 health_cmd
.oneline
= _("Report observed XFS health problems."),
463 add_command(&health_cmd
);