// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <[email protected]> */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_defer.h" #include "xfs_btree.h" #include "xfs_bit.h" #include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" #include "xfs_icache.h" #include "xfs_inode_buf.h" #include "xfs_inode_fork.h" #include "xfs_ialloc.h" #include "xfs_da_format.h" #include "xfs_reflink.h" #include "xfs_alloc.h" #include "xfs_rmap.h" #include "xfs_rmap_btree.h" #include "xfs_bmap.h" #include "xfs_bmap_btree.h" #include "xfs_bmap_util.h" #include "xfs_dir2.h" #include "xfs_dir2_priv.h" #include "xfs_quota_defs.h" #include "xfs_quota.h" #include "xfs_ag.h" #include "xfs_rtbitmap.h" #include "xfs_attr_leaf.h" #include "xfs_log_priv.h" #include "xfs_health.h" #include "xfs_symlink_remote.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/btree.h" #include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/iscan.h" #include "scrub/readdir.h" #include "scrub/tempfile.h" /* * Inode Record Repair * =================== * * Roughly speaking, inode problems can be classified based on whether or not * they trip the dinode verifiers. If those trip, then we won't be able to * xfs_iget ourselves the inode. * * Therefore, the xrep_dinode_* functions fix anything that will cause the * inode buffer verifier or the dinode verifier. The xrep_inode_* functions * fix things on live incore inodes. The inode repair functions make decisions * with security and usability implications when reviving a file: * * - Files with zero di_mode or a garbage di_mode are converted to regular file * that only root can read. This file may not actually contain user data, * if the file was not previously a regular file. Setuid and setgid bits * are cleared. * * - Zero-size directories can be truncated to look empty. It is necessary to * run the bmapbtd and directory repair functions to fully rebuild the * directory. * * - Zero-size symbolic link targets can be truncated to '?'. It is necessary * to run the bmapbtd and symlink repair functions to salvage the symlink. * * - Invalid extent size hints will be removed. * * - Quotacheck will be scheduled if we repaired an inode that was so badly * damaged that the ondisk inode had to be rebuilt. * * - Invalid user, group, or project IDs (aka -1U) will be reset to zero. * Setuid and setgid bits are cleared. * * - Data and attr forks are reset to extents format with zero extents if the * fork data is inconsistent. It is necessary to run the bmapbtd or bmapbta * repair functions to recover the space mapping. * * - ACLs will not be recovered if the attr fork is zapped or the extended * attribute structure itself requires salvaging. * * - If the attr fork is zapped, the user and group ids are reset to root and * the setuid and setgid bits are removed. */ /* * All the information we need to repair the ondisk inode if we can't iget the * incore inode. We don't allocate this buffer unless we're going to perform * a repair to the ondisk inode cluster buffer. */ struct xrep_inode { … }; /* * Setup function for inode repair. @imap contains the ondisk inode mapping * information so that we can correct the ondisk inode cluster buffer if * necessary to make iget work. */ int xrep_setup_inode( struct xfs_scrub *sc, const struct xfs_imap *imap) { … } /* * Make sure this ondisk inode can pass the inode buffer verifier. This is * not the same as the dinode verifier. */ STATIC void xrep_dinode_buf_core( struct xfs_scrub *sc, struct xfs_buf *bp, unsigned int ioffset) { … } /* Make sure this inode cluster buffer can pass the inode buffer verifier. */ STATIC void xrep_dinode_buf( struct xfs_scrub *sc, struct xfs_buf *bp) { … } /* Reinitialize things that never change in an inode. */ STATIC void xrep_dinode_header( struct xfs_scrub *sc, struct xfs_dinode *dip) { … } /* * If this directory entry points to the scrub target inode, then the directory * we're scanning is the parent of the scrub target inode. */ STATIC int xrep_dinode_findmode_dirent( struct xfs_scrub *sc, struct xfs_inode *dp, xfs_dir2_dataptr_t dapos, const struct xfs_name *name, xfs_ino_t ino, void *priv) { … } /* Try to lock a directory, or wait a jiffy. */ static inline int xrep_dinode_ilock_nowait( struct xfs_inode *dp, unsigned int lock_mode) { … } /* * Try to lock a directory to look for ftype hints. Since we already hold the * AGI buffer, we cannot block waiting for the ILOCK because rename can take * the ILOCK and then try to lock AGIs. */ STATIC int xrep_dinode_trylock_directory( struct xrep_inode *ri, struct xfs_inode *dp, unsigned int *lock_modep) { … } /* * If this is a directory, walk the dirents looking for any that point to the * scrub target inode. */ STATIC int xrep_dinode_findmode_walk_directory( struct xrep_inode *ri, struct xfs_inode *dp) { … } /* * Try to find the mode of the inode being repaired by looking for directories * that point down to this file. */ STATIC int xrep_dinode_find_mode( struct xrep_inode *ri, uint16_t *mode) { … } /* Turn di_mode into /something/ recognizable. Returns true if we succeed. */ STATIC int xrep_dinode_mode( struct xrep_inode *ri, struct xfs_dinode *dip) { … } /* Fix unused link count fields having nonzero values. */ STATIC void xrep_dinode_nlinks( struct xfs_dinode *dip) { … } /* Fix any conflicting flags that the verifiers complain about. */ STATIC void xrep_dinode_flags( struct xfs_scrub *sc, struct xfs_dinode *dip, bool isrt) { … } /* * Blow out symlink; now it points nowhere. We don't have to worry about * incore state because this inode is failing the verifiers. */ STATIC void xrep_dinode_zap_symlink( struct xrep_inode *ri, struct xfs_dinode *dip) { … } /* * Blow out dir, make the parent point to the root. In the future repair will * reconstruct this directory for us. Note that there's no in-core directory * inode because the sf verifier tripped, so we don't have to worry about the * dentry cache. */ STATIC void xrep_dinode_zap_dir( struct xrep_inode *ri, struct xfs_dinode *dip) { … } /* Make sure we don't have a garbage file size. */ STATIC void xrep_dinode_size( struct xrep_inode *ri, struct xfs_dinode *dip) { … } /* Fix extent size hints. */ STATIC void xrep_dinode_extsize_hints( struct xfs_scrub *sc, struct xfs_dinode *dip) { … } /* Count extents and blocks for an inode given an rmap. */ STATIC int xrep_dinode_walk_rmap( struct xfs_btree_cur *cur, const struct xfs_rmap_irec *rec, void *priv) { … } /* Count extents and blocks for an inode from all AG rmap data. */ STATIC int xrep_dinode_count_ag_rmaps( struct xrep_inode *ri, struct xfs_perag *pag) { … } /* Count extents and blocks for a given inode from all rmap data. */ STATIC int xrep_dinode_count_rmaps( struct xrep_inode *ri) { … } /* Return true if this extents-format ifork looks like garbage. */ STATIC bool xrep_dinode_bad_extents_fork( struct xfs_scrub *sc, struct xfs_dinode *dip, unsigned int dfork_size, int whichfork) { … } /* Return true if this btree-format ifork looks like garbage. */ STATIC bool xrep_dinode_bad_bmbt_fork( struct xfs_scrub *sc, struct xfs_dinode *dip, unsigned int dfork_size, int whichfork) { … } /* * Check the data fork for things that will fail the ifork verifiers or the * ifork formatters. */ STATIC bool xrep_dinode_check_dfork( struct xfs_scrub *sc, struct xfs_dinode *dip, uint16_t mode) { … } static void xrep_dinode_set_data_nextents( struct xfs_dinode *dip, xfs_extnum_t nextents) { … } static void xrep_dinode_set_attr_nextents( struct xfs_dinode *dip, xfs_extnum_t nextents) { … } /* Reset the data fork to something sane. */ STATIC void xrep_dinode_zap_dfork( struct xrep_inode *ri, struct xfs_dinode *dip, uint16_t mode) { … } /* * Check the attr fork for things that will fail the ifork verifiers or the * ifork formatters. */ STATIC bool xrep_dinode_check_afork( struct xfs_scrub *sc, struct xfs_dinode *dip) { … } /* * Reset the attr fork to empty. Since the attr fork could have contained * ACLs, make the file readable only by root. */ STATIC void xrep_dinode_zap_afork( struct xrep_inode *ri, struct xfs_dinode *dip, uint16_t mode) { … } /* Make sure the fork offset is a sensible value. */ STATIC void xrep_dinode_ensure_forkoff( struct xrep_inode *ri, struct xfs_dinode *dip, uint16_t mode) { … } /* * Zap the data/attr forks if we spot anything that isn't going to pass the * ifork verifiers or the ifork formatters, because we need to get the inode * into good enough shape that the higher level repair functions can run. */ STATIC void xrep_dinode_zap_forks( struct xrep_inode *ri, struct xfs_dinode *dip) { … } /* Inode didn't pass dinode verifiers, so fix the raw buffer and retry iget. */ STATIC int xrep_dinode_core( struct xrep_inode *ri) { … } /* Fix everything xfs_dinode_verify cares about. */ STATIC int xrep_dinode_problems( struct xrep_inode *ri) { … } /* * Fix problems that the verifiers don't care about. In general these are * errors that don't cause problems elsewhere in the kernel that we can easily * detect, so we don't check them all that rigorously. */ /* Make sure block and extent counts are ok. */ STATIC int xrep_inode_blockcounts( struct xfs_scrub *sc) { … } /* Check for invalid uid/gid/prid. */ STATIC void xrep_inode_ids( struct xfs_scrub *sc) { … } static inline void xrep_clamp_timestamp( struct xfs_inode *ip, struct timespec64 *ts) { … } /* Nanosecond counters can't have more than 1 billion. */ STATIC void xrep_inode_timestamps( struct xfs_inode *ip) { … } /* Fix inode flags that don't make sense together. */ STATIC void xrep_inode_flags( struct xfs_scrub *sc) { … } /* * Fix size problems with block/node format directories. If we fail to find * the extent list, just bail out and let the bmapbtd repair functions clean * up that mess. */ STATIC void xrep_inode_blockdir_size( struct xfs_scrub *sc) { … } /* Fix size problems with short format directories. */ STATIC void xrep_inode_sfdir_size( struct xfs_scrub *sc) { … } /* * Fix any irregularities in a directory inode's size now that we can iterate * extent maps and access other regular inode data. */ STATIC void xrep_inode_dir_size( struct xfs_scrub *sc) { … } /* Fix extent size hint problems. */ STATIC void xrep_inode_extsize( struct xfs_scrub *sc) { … } /* Ensure this file has an attr fork if it needs to hold a parent pointer. */ STATIC int xrep_inode_pptr( struct xfs_scrub *sc) { … } /* Fix any irregularities in an inode that the verifiers don't catch. */ STATIC int xrep_inode_problems( struct xfs_scrub *sc) { … } /* * Make sure this inode's unlinked list pointers are consistent with its * link count. */ STATIC int xrep_inode_unlinked( struct xfs_scrub *sc) { … } /* Repair an inode's fields. */ int xrep_inode( struct xfs_scrub *sc) { … }