0001
0002
0003
0004
0005
0006 #include "xfs.h"
0007 #include "xfs_fs.h"
0008 #include "xfs_shared.h"
0009 #include "xfs_format.h"
0010 #include "xfs_trans_resv.h"
0011 #include "xfs_mount.h"
0012 #include "xfs_log_format.h"
0013 #include "xfs_trans.h"
0014 #include "xfs_inode.h"
0015 #include "xfs_quota.h"
0016 #include "xfs_qm.h"
0017 #include "xfs_errortag.h"
0018 #include "xfs_error.h"
0019 #include "xfs_scrub.h"
0020 #include "scrub/scrub.h"
0021 #include "scrub/common.h"
0022 #include "scrub/trace.h"
0023 #include "scrub/repair.h"
0024 #include "scrub/health.h"
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134 static int
0135 xchk_probe(
0136 struct xfs_scrub *sc)
0137 {
0138 int error = 0;
0139
0140 if (xchk_should_terminate(sc, &error))
0141 return error;
0142
0143 return 0;
0144 }
0145
0146
0147
0148
0149 STATIC int
0150 xchk_teardown(
0151 struct xfs_scrub *sc,
0152 int error)
0153 {
0154 struct xfs_inode *ip_in = XFS_I(file_inode(sc->file));
0155
0156 xchk_ag_free(sc, &sc->sa);
0157 if (sc->tp) {
0158 if (error == 0 && (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
0159 error = xfs_trans_commit(sc->tp);
0160 else
0161 xfs_trans_cancel(sc->tp);
0162 sc->tp = NULL;
0163 }
0164 if (sc->ip) {
0165 if (sc->ilock_flags)
0166 xfs_iunlock(sc->ip, sc->ilock_flags);
0167 if (sc->ip != ip_in &&
0168 !xfs_internal_inum(sc->mp, sc->ip->i_ino))
0169 xfs_irele(sc->ip);
0170 sc->ip = NULL;
0171 }
0172 if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
0173 mnt_drop_write_file(sc->file);
0174 if (sc->flags & XCHK_REAPING_DISABLED)
0175 xchk_start_reaping(sc);
0176 if (sc->buf) {
0177 kmem_free(sc->buf);
0178 sc->buf = NULL;
0179 }
0180 return error;
0181 }
0182
0183
0184
0185 static const struct xchk_meta_ops meta_scrub_ops[] = {
0186 [XFS_SCRUB_TYPE_PROBE] = {
0187 .type = ST_NONE,
0188 .setup = xchk_setup_fs,
0189 .scrub = xchk_probe,
0190 .repair = xrep_probe,
0191 },
0192 [XFS_SCRUB_TYPE_SB] = {
0193 .type = ST_PERAG,
0194 .setup = xchk_setup_fs,
0195 .scrub = xchk_superblock,
0196 .repair = xrep_superblock,
0197 },
0198 [XFS_SCRUB_TYPE_AGF] = {
0199 .type = ST_PERAG,
0200 .setup = xchk_setup_fs,
0201 .scrub = xchk_agf,
0202 .repair = xrep_agf,
0203 },
0204 [XFS_SCRUB_TYPE_AGFL]= {
0205 .type = ST_PERAG,
0206 .setup = xchk_setup_fs,
0207 .scrub = xchk_agfl,
0208 .repair = xrep_agfl,
0209 },
0210 [XFS_SCRUB_TYPE_AGI] = {
0211 .type = ST_PERAG,
0212 .setup = xchk_setup_fs,
0213 .scrub = xchk_agi,
0214 .repair = xrep_agi,
0215 },
0216 [XFS_SCRUB_TYPE_BNOBT] = {
0217 .type = ST_PERAG,
0218 .setup = xchk_setup_ag_allocbt,
0219 .scrub = xchk_bnobt,
0220 .repair = xrep_notsupported,
0221 },
0222 [XFS_SCRUB_TYPE_CNTBT] = {
0223 .type = ST_PERAG,
0224 .setup = xchk_setup_ag_allocbt,
0225 .scrub = xchk_cntbt,
0226 .repair = xrep_notsupported,
0227 },
0228 [XFS_SCRUB_TYPE_INOBT] = {
0229 .type = ST_PERAG,
0230 .setup = xchk_setup_ag_iallocbt,
0231 .scrub = xchk_inobt,
0232 .repair = xrep_notsupported,
0233 },
0234 [XFS_SCRUB_TYPE_FINOBT] = {
0235 .type = ST_PERAG,
0236 .setup = xchk_setup_ag_iallocbt,
0237 .scrub = xchk_finobt,
0238 .has = xfs_has_finobt,
0239 .repair = xrep_notsupported,
0240 },
0241 [XFS_SCRUB_TYPE_RMAPBT] = {
0242 .type = ST_PERAG,
0243 .setup = xchk_setup_ag_rmapbt,
0244 .scrub = xchk_rmapbt,
0245 .has = xfs_has_rmapbt,
0246 .repair = xrep_notsupported,
0247 },
0248 [XFS_SCRUB_TYPE_REFCNTBT] = {
0249 .type = ST_PERAG,
0250 .setup = xchk_setup_ag_refcountbt,
0251 .scrub = xchk_refcountbt,
0252 .has = xfs_has_reflink,
0253 .repair = xrep_notsupported,
0254 },
0255 [XFS_SCRUB_TYPE_INODE] = {
0256 .type = ST_INODE,
0257 .setup = xchk_setup_inode,
0258 .scrub = xchk_inode,
0259 .repair = xrep_notsupported,
0260 },
0261 [XFS_SCRUB_TYPE_BMBTD] = {
0262 .type = ST_INODE,
0263 .setup = xchk_setup_inode_bmap,
0264 .scrub = xchk_bmap_data,
0265 .repair = xrep_notsupported,
0266 },
0267 [XFS_SCRUB_TYPE_BMBTA] = {
0268 .type = ST_INODE,
0269 .setup = xchk_setup_inode_bmap,
0270 .scrub = xchk_bmap_attr,
0271 .repair = xrep_notsupported,
0272 },
0273 [XFS_SCRUB_TYPE_BMBTC] = {
0274 .type = ST_INODE,
0275 .setup = xchk_setup_inode_bmap,
0276 .scrub = xchk_bmap_cow,
0277 .repair = xrep_notsupported,
0278 },
0279 [XFS_SCRUB_TYPE_DIR] = {
0280 .type = ST_INODE,
0281 .setup = xchk_setup_directory,
0282 .scrub = xchk_directory,
0283 .repair = xrep_notsupported,
0284 },
0285 [XFS_SCRUB_TYPE_XATTR] = {
0286 .type = ST_INODE,
0287 .setup = xchk_setup_xattr,
0288 .scrub = xchk_xattr,
0289 .repair = xrep_notsupported,
0290 },
0291 [XFS_SCRUB_TYPE_SYMLINK] = {
0292 .type = ST_INODE,
0293 .setup = xchk_setup_symlink,
0294 .scrub = xchk_symlink,
0295 .repair = xrep_notsupported,
0296 },
0297 [XFS_SCRUB_TYPE_PARENT] = {
0298 .type = ST_INODE,
0299 .setup = xchk_setup_parent,
0300 .scrub = xchk_parent,
0301 .repair = xrep_notsupported,
0302 },
0303 [XFS_SCRUB_TYPE_RTBITMAP] = {
0304 .type = ST_FS,
0305 .setup = xchk_setup_rt,
0306 .scrub = xchk_rtbitmap,
0307 .has = xfs_has_realtime,
0308 .repair = xrep_notsupported,
0309 },
0310 [XFS_SCRUB_TYPE_RTSUM] = {
0311 .type = ST_FS,
0312 .setup = xchk_setup_rt,
0313 .scrub = xchk_rtsummary,
0314 .has = xfs_has_realtime,
0315 .repair = xrep_notsupported,
0316 },
0317 [XFS_SCRUB_TYPE_UQUOTA] = {
0318 .type = ST_FS,
0319 .setup = xchk_setup_quota,
0320 .scrub = xchk_quota,
0321 .repair = xrep_notsupported,
0322 },
0323 [XFS_SCRUB_TYPE_GQUOTA] = {
0324 .type = ST_FS,
0325 .setup = xchk_setup_quota,
0326 .scrub = xchk_quota,
0327 .repair = xrep_notsupported,
0328 },
0329 [XFS_SCRUB_TYPE_PQUOTA] = {
0330 .type = ST_FS,
0331 .setup = xchk_setup_quota,
0332 .scrub = xchk_quota,
0333 .repair = xrep_notsupported,
0334 },
0335 [XFS_SCRUB_TYPE_FSCOUNTERS] = {
0336 .type = ST_FS,
0337 .setup = xchk_setup_fscounters,
0338 .scrub = xchk_fscounters,
0339 .repair = xrep_notsupported,
0340 },
0341 };
0342
0343 static int
0344 xchk_validate_inputs(
0345 struct xfs_mount *mp,
0346 struct xfs_scrub_metadata *sm)
0347 {
0348 int error;
0349 const struct xchk_meta_ops *ops;
0350
0351 error = -EINVAL;
0352
0353 sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
0354 if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN)
0355 goto out;
0356
0357 if (memchr_inv(sm->sm_reserved, 0, sizeof(sm->sm_reserved)))
0358 goto out;
0359
0360 error = -ENOENT;
0361
0362 if (sm->sm_type >= XFS_SCRUB_TYPE_NR)
0363 goto out;
0364 ops = &meta_scrub_ops[sm->sm_type];
0365 if (ops->setup == NULL || ops->scrub == NULL)
0366 goto out;
0367
0368 if (ops->has && !ops->has(mp))
0369 goto out;
0370
0371 error = -EINVAL;
0372
0373 switch (ops->type) {
0374 case ST_NONE:
0375 case ST_FS:
0376 if (sm->sm_ino || sm->sm_gen || sm->sm_agno)
0377 goto out;
0378 break;
0379 case ST_PERAG:
0380 if (sm->sm_ino || sm->sm_gen ||
0381 sm->sm_agno >= mp->m_sb.sb_agcount)
0382 goto out;
0383 break;
0384 case ST_INODE:
0385 if (sm->sm_agno || (sm->sm_gen && !sm->sm_ino))
0386 goto out;
0387 break;
0388 default:
0389 goto out;
0390 }
0391
0392
0393
0394
0395
0396
0397
0398 if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
0399 error = -EOPNOTSUPP;
0400 if (!xfs_has_crc(mp))
0401 goto out;
0402
0403 error = -EROFS;
0404 if (xfs_is_readonly(mp))
0405 goto out;
0406 }
0407
0408 error = 0;
0409 out:
0410 return error;
0411 }
0412
0413 #ifdef CONFIG_XFS_ONLINE_REPAIR
0414 static inline void xchk_postmortem(struct xfs_scrub *sc)
0415 {
0416
0417
0418
0419
0420
0421 if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
0422 (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
0423 XFS_SCRUB_OFLAG_XCORRUPT)))
0424 xrep_failure(sc->mp);
0425 }
0426 #else
0427 static inline void xchk_postmortem(struct xfs_scrub *sc)
0428 {
0429
0430
0431
0432
0433 if (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
0434 XFS_SCRUB_OFLAG_XCORRUPT))
0435 xfs_alert_ratelimited(sc->mp,
0436 "Corruption detected during scrub.");
0437 }
0438 #endif
0439
0440
0441 int
0442 xfs_scrub_metadata(
0443 struct file *file,
0444 struct xfs_scrub_metadata *sm)
0445 {
0446 struct xfs_scrub *sc;
0447 struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
0448 int error = 0;
0449
0450 BUILD_BUG_ON(sizeof(meta_scrub_ops) !=
0451 (sizeof(struct xchk_meta_ops) * XFS_SCRUB_TYPE_NR));
0452
0453 trace_xchk_start(XFS_I(file_inode(file)), sm, error);
0454
0455
0456 error = -ESHUTDOWN;
0457 if (xfs_is_shutdown(mp))
0458 goto out;
0459 error = -ENOTRECOVERABLE;
0460 if (xfs_has_norecovery(mp))
0461 goto out;
0462
0463 error = xchk_validate_inputs(mp, sm);
0464 if (error)
0465 goto out;
0466
0467 xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SCRUB,
0468 "EXPERIMENTAL online scrub feature in use. Use at your own risk!");
0469
0470 sc = kmem_zalloc(sizeof(struct xfs_scrub), KM_NOFS | KM_MAYFAIL);
0471 if (!sc) {
0472 error = -ENOMEM;
0473 goto out;
0474 }
0475
0476 sc->mp = mp;
0477 sc->file = file;
0478 sc->sm = sm;
0479 sc->ops = &meta_scrub_ops[sm->sm_type];
0480 sc->sick_mask = xchk_health_mask_for_scrub_type(sm->sm_type);
0481 retry_op:
0482
0483
0484
0485
0486 if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
0487 error = mnt_want_write_file(sc->file);
0488 if (error)
0489 goto out_sc;
0490 }
0491
0492
0493 error = sc->ops->setup(sc);
0494 if (error)
0495 goto out_teardown;
0496
0497
0498 error = sc->ops->scrub(sc);
0499 if (!(sc->flags & XCHK_TRY_HARDER) && error == -EDEADLOCK) {
0500
0501
0502
0503
0504
0505 error = xchk_teardown(sc, 0);
0506 if (error)
0507 goto out_sc;
0508 sc->flags |= XCHK_TRY_HARDER;
0509 goto retry_op;
0510 } else if (error || (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE))
0511 goto out_teardown;
0512
0513 xchk_update_health(sc);
0514
0515 if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
0516 !(sc->flags & XREP_ALREADY_FIXED)) {
0517 bool needs_fix;
0518
0519
0520 if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
0521 sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
0522
0523 needs_fix = (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
0524 XFS_SCRUB_OFLAG_XCORRUPT |
0525 XFS_SCRUB_OFLAG_PREEN));
0526
0527
0528
0529
0530 if (!needs_fix) {
0531 sc->sm->sm_flags |= XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED;
0532 goto out_nofix;
0533 }
0534
0535
0536
0537
0538
0539 error = xrep_attempt(sc);
0540 if (error == -EAGAIN) {
0541
0542
0543
0544
0545
0546 error = xchk_teardown(sc, 0);
0547 if (error) {
0548 xrep_failure(mp);
0549 goto out_sc;
0550 }
0551 goto retry_op;
0552 }
0553 }
0554
0555 out_nofix:
0556 xchk_postmortem(sc);
0557 out_teardown:
0558 error = xchk_teardown(sc, error);
0559 out_sc:
0560 kmem_free(sc);
0561 out:
0562 trace_xchk_done(XFS_I(file_inode(file)), sm, error);
0563 if (error == -EFSCORRUPTED || error == -EFSBADCRC) {
0564 sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
0565 error = 0;
0566 }
0567 return error;
0568 }