162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc. 462306a36Sopenharmony_ci * All Rights Reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_log_format.h" 1162306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1262306a36Sopenharmony_ci#include "xfs_mount.h" 1362306a36Sopenharmony_ci#include "xfs_inode.h" 1462306a36Sopenharmony_ci#include "xfs_trans.h" 1562306a36Sopenharmony_ci#include "xfs_inode_item.h" 1662306a36Sopenharmony_ci#include "xfs_trace.h" 1762306a36Sopenharmony_ci#include "xfs_trans_priv.h" 1862306a36Sopenharmony_ci#include "xfs_buf_item.h" 1962306a36Sopenharmony_ci#include "xfs_log.h" 2062306a36Sopenharmony_ci#include "xfs_error.h" 2162306a36Sopenharmony_ci#include "xfs_log_priv.h" 2262306a36Sopenharmony_ci#include "xfs_log_recover.h" 2362306a36Sopenharmony_ci#include "xfs_icache.h" 2462306a36Sopenharmony_ci#include "xfs_bmap_btree.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciSTATIC void 2762306a36Sopenharmony_cixlog_recover_inode_ra_pass2( 2862306a36Sopenharmony_ci struct xlog *log, 2962306a36Sopenharmony_ci struct xlog_recover_item *item) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) { 3262306a36Sopenharmony_ci struct xfs_inode_log_format *ilfp = item->ri_buf[0].i_addr; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len, 3562306a36Sopenharmony_ci &xfs_inode_buf_ra_ops); 3662306a36Sopenharmony_ci } else { 3762306a36Sopenharmony_ci struct xfs_inode_log_format_32 *ilfp = item->ri_buf[0].i_addr; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len, 4062306a36Sopenharmony_ci &xfs_inode_buf_ra_ops); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * Inode fork owner changes 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * If we have been told that we have to reparent the inode fork, it's because an 4862306a36Sopenharmony_ci * extent swap operation on a CRC enabled filesystem has been done and we are 4962306a36Sopenharmony_ci * replaying it. We need to walk the BMBT of the appropriate fork and change the 5062306a36Sopenharmony_ci * owners of it. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * The complexity here is that we don't have an inode context to work with, so 5362306a36Sopenharmony_ci * after we've replayed the inode we need to instantiate one. This is where the 5462306a36Sopenharmony_ci * fun begins. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * We are in the middle of log recovery, so we can't run transactions. That 5762306a36Sopenharmony_ci * means we cannot use cache coherent inode instantiation via xfs_iget(), as 5862306a36Sopenharmony_ci * that will result in the corresponding iput() running the inode through 5962306a36Sopenharmony_ci * xfs_inactive(). If we've just replayed an inode core that changes the link 6062306a36Sopenharmony_ci * count to zero (i.e. it's been unlinked), then xfs_inactive() will run 6162306a36Sopenharmony_ci * transactions (bad!). 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * So, to avoid this, we instantiate an inode directly from the inode core we've 6462306a36Sopenharmony_ci * just recovered. We have the buffer still locked, and all we really need to 6562306a36Sopenharmony_ci * instantiate is the inode core and the forks being modified. We can do this 6662306a36Sopenharmony_ci * manually, then run the inode btree owner change, and then tear down the 6762306a36Sopenharmony_ci * xfs_inode without having to run any transactions at all. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * Also, because we don't have a transaction context available here but need to 7062306a36Sopenharmony_ci * gather all the buffers we modify for writeback so we pass the buffer_list 7162306a36Sopenharmony_ci * instead for the operation to use. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciSTATIC int 7562306a36Sopenharmony_cixfs_recover_inode_owner_change( 7662306a36Sopenharmony_ci struct xfs_mount *mp, 7762306a36Sopenharmony_ci struct xfs_dinode *dip, 7862306a36Sopenharmony_ci struct xfs_inode_log_format *in_f, 7962306a36Sopenharmony_ci struct list_head *buffer_list) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct xfs_inode *ip; 8262306a36Sopenharmony_ci int error; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ASSERT(in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER)); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ip = xfs_inode_alloc(mp, in_f->ilf_ino); 8762306a36Sopenharmony_ci if (!ip) 8862306a36Sopenharmony_ci return -ENOMEM; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* instantiate the inode */ 9162306a36Sopenharmony_ci ASSERT(dip->di_version >= 3); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci error = xfs_inode_from_disk(ip, dip); 9462306a36Sopenharmony_ci if (error) 9562306a36Sopenharmony_ci goto out_free_ip; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (in_f->ilf_fields & XFS_ILOG_DOWNER) { 9862306a36Sopenharmony_ci ASSERT(in_f->ilf_fields & XFS_ILOG_DBROOT); 9962306a36Sopenharmony_ci error = xfs_bmbt_change_owner(NULL, ip, XFS_DATA_FORK, 10062306a36Sopenharmony_ci ip->i_ino, buffer_list); 10162306a36Sopenharmony_ci if (error) 10262306a36Sopenharmony_ci goto out_free_ip; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (in_f->ilf_fields & XFS_ILOG_AOWNER) { 10662306a36Sopenharmony_ci ASSERT(in_f->ilf_fields & XFS_ILOG_ABROOT); 10762306a36Sopenharmony_ci error = xfs_bmbt_change_owner(NULL, ip, XFS_ATTR_FORK, 10862306a36Sopenharmony_ci ip->i_ino, buffer_list); 10962306a36Sopenharmony_ci if (error) 11062306a36Sopenharmony_ci goto out_free_ip; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciout_free_ip: 11462306a36Sopenharmony_ci xfs_inode_free(ip); 11562306a36Sopenharmony_ci return error; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic inline bool xfs_log_dinode_has_bigtime(const struct xfs_log_dinode *ld) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return ld->di_version >= 3 && 12162306a36Sopenharmony_ci (ld->di_flags2 & XFS_DIFLAG2_BIGTIME); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* Convert a log timestamp to an ondisk timestamp. */ 12562306a36Sopenharmony_cistatic inline xfs_timestamp_t 12662306a36Sopenharmony_cixfs_log_dinode_to_disk_ts( 12762306a36Sopenharmony_ci struct xfs_log_dinode *from, 12862306a36Sopenharmony_ci const xfs_log_timestamp_t its) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct xfs_legacy_timestamp *lts; 13162306a36Sopenharmony_ci struct xfs_log_legacy_timestamp *lits; 13262306a36Sopenharmony_ci xfs_timestamp_t ts; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (xfs_log_dinode_has_bigtime(from)) 13562306a36Sopenharmony_ci return cpu_to_be64(its); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci lts = (struct xfs_legacy_timestamp *)&ts; 13862306a36Sopenharmony_ci lits = (struct xfs_log_legacy_timestamp *)&its; 13962306a36Sopenharmony_ci lts->t_sec = cpu_to_be32(lits->t_sec); 14062306a36Sopenharmony_ci lts->t_nsec = cpu_to_be32(lits->t_nsec); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return ts; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic inline bool xfs_log_dinode_has_large_extent_counts( 14662306a36Sopenharmony_ci const struct xfs_log_dinode *ld) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return ld->di_version >= 3 && 14962306a36Sopenharmony_ci (ld->di_flags2 & XFS_DIFLAG2_NREXT64); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic inline void 15362306a36Sopenharmony_cixfs_log_dinode_to_disk_iext_counters( 15462306a36Sopenharmony_ci struct xfs_log_dinode *from, 15562306a36Sopenharmony_ci struct xfs_dinode *to) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (xfs_log_dinode_has_large_extent_counts(from)) { 15862306a36Sopenharmony_ci to->di_big_nextents = cpu_to_be64(from->di_big_nextents); 15962306a36Sopenharmony_ci to->di_big_anextents = cpu_to_be32(from->di_big_anextents); 16062306a36Sopenharmony_ci to->di_nrext64_pad = cpu_to_be16(from->di_nrext64_pad); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci to->di_nextents = cpu_to_be32(from->di_nextents); 16362306a36Sopenharmony_ci to->di_anextents = cpu_to_be16(from->di_anextents); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciSTATIC void 16962306a36Sopenharmony_cixfs_log_dinode_to_disk( 17062306a36Sopenharmony_ci struct xfs_log_dinode *from, 17162306a36Sopenharmony_ci struct xfs_dinode *to, 17262306a36Sopenharmony_ci xfs_lsn_t lsn) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci to->di_magic = cpu_to_be16(from->di_magic); 17562306a36Sopenharmony_ci to->di_mode = cpu_to_be16(from->di_mode); 17662306a36Sopenharmony_ci to->di_version = from->di_version; 17762306a36Sopenharmony_ci to->di_format = from->di_format; 17862306a36Sopenharmony_ci to->di_onlink = 0; 17962306a36Sopenharmony_ci to->di_uid = cpu_to_be32(from->di_uid); 18062306a36Sopenharmony_ci to->di_gid = cpu_to_be32(from->di_gid); 18162306a36Sopenharmony_ci to->di_nlink = cpu_to_be32(from->di_nlink); 18262306a36Sopenharmony_ci to->di_projid_lo = cpu_to_be16(from->di_projid_lo); 18362306a36Sopenharmony_ci to->di_projid_hi = cpu_to_be16(from->di_projid_hi); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci to->di_atime = xfs_log_dinode_to_disk_ts(from, from->di_atime); 18662306a36Sopenharmony_ci to->di_mtime = xfs_log_dinode_to_disk_ts(from, from->di_mtime); 18762306a36Sopenharmony_ci to->di_ctime = xfs_log_dinode_to_disk_ts(from, from->di_ctime); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci to->di_size = cpu_to_be64(from->di_size); 19062306a36Sopenharmony_ci to->di_nblocks = cpu_to_be64(from->di_nblocks); 19162306a36Sopenharmony_ci to->di_extsize = cpu_to_be32(from->di_extsize); 19262306a36Sopenharmony_ci to->di_forkoff = from->di_forkoff; 19362306a36Sopenharmony_ci to->di_aformat = from->di_aformat; 19462306a36Sopenharmony_ci to->di_dmevmask = cpu_to_be32(from->di_dmevmask); 19562306a36Sopenharmony_ci to->di_dmstate = cpu_to_be16(from->di_dmstate); 19662306a36Sopenharmony_ci to->di_flags = cpu_to_be16(from->di_flags); 19762306a36Sopenharmony_ci to->di_gen = cpu_to_be32(from->di_gen); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (from->di_version == 3) { 20062306a36Sopenharmony_ci to->di_changecount = cpu_to_be64(from->di_changecount); 20162306a36Sopenharmony_ci to->di_crtime = xfs_log_dinode_to_disk_ts(from, 20262306a36Sopenharmony_ci from->di_crtime); 20362306a36Sopenharmony_ci to->di_flags2 = cpu_to_be64(from->di_flags2); 20462306a36Sopenharmony_ci to->di_cowextsize = cpu_to_be32(from->di_cowextsize); 20562306a36Sopenharmony_ci to->di_ino = cpu_to_be64(from->di_ino); 20662306a36Sopenharmony_ci to->di_lsn = cpu_to_be64(lsn); 20762306a36Sopenharmony_ci memset(to->di_pad2, 0, sizeof(to->di_pad2)); 20862306a36Sopenharmony_ci uuid_copy(&to->di_uuid, &from->di_uuid); 20962306a36Sopenharmony_ci to->di_v3_pad = 0; 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci to->di_flushiter = cpu_to_be16(from->di_flushiter); 21262306a36Sopenharmony_ci memset(to->di_v2_pad, 0, sizeof(to->di_v2_pad)); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci xfs_log_dinode_to_disk_iext_counters(from, to); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciSTATIC int 21962306a36Sopenharmony_cixlog_dinode_verify_extent_counts( 22062306a36Sopenharmony_ci struct xfs_mount *mp, 22162306a36Sopenharmony_ci struct xfs_log_dinode *ldip) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci xfs_extnum_t nextents; 22462306a36Sopenharmony_ci xfs_aextnum_t anextents; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (xfs_log_dinode_has_large_extent_counts(ldip)) { 22762306a36Sopenharmony_ci if (!xfs_has_large_extent_counts(mp) || 22862306a36Sopenharmony_ci (ldip->di_nrext64_pad != 0)) { 22962306a36Sopenharmony_ci XFS_CORRUPTION_ERROR( 23062306a36Sopenharmony_ci "Bad log dinode large extent count format", 23162306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, ldip, sizeof(*ldip)); 23262306a36Sopenharmony_ci xfs_alert(mp, 23362306a36Sopenharmony_ci "Bad inode 0x%llx, large extent counts %d, padding 0x%x", 23462306a36Sopenharmony_ci ldip->di_ino, xfs_has_large_extent_counts(mp), 23562306a36Sopenharmony_ci ldip->di_nrext64_pad); 23662306a36Sopenharmony_ci return -EFSCORRUPTED; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci nextents = ldip->di_big_nextents; 24062306a36Sopenharmony_ci anextents = ldip->di_big_anextents; 24162306a36Sopenharmony_ci } else { 24262306a36Sopenharmony_ci if (ldip->di_version == 3 && ldip->di_v3_pad != 0) { 24362306a36Sopenharmony_ci XFS_CORRUPTION_ERROR( 24462306a36Sopenharmony_ci "Bad log dinode di_v3_pad", 24562306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, ldip, sizeof(*ldip)); 24662306a36Sopenharmony_ci xfs_alert(mp, 24762306a36Sopenharmony_ci "Bad inode 0x%llx, di_v3_pad 0x%llx", 24862306a36Sopenharmony_ci ldip->di_ino, ldip->di_v3_pad); 24962306a36Sopenharmony_ci return -EFSCORRUPTED; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci nextents = ldip->di_nextents; 25362306a36Sopenharmony_ci anextents = ldip->di_anextents; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (unlikely(nextents + anextents > ldip->di_nblocks)) { 25762306a36Sopenharmony_ci XFS_CORRUPTION_ERROR("Bad log dinode extent counts", 25862306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, ldip, sizeof(*ldip)); 25962306a36Sopenharmony_ci xfs_alert(mp, 26062306a36Sopenharmony_ci "Bad inode 0x%llx, large extent counts %d, nextents 0x%llx, anextents 0x%x, nblocks 0x%llx", 26162306a36Sopenharmony_ci ldip->di_ino, xfs_has_large_extent_counts(mp), nextents, 26262306a36Sopenharmony_ci anextents, ldip->di_nblocks); 26362306a36Sopenharmony_ci return -EFSCORRUPTED; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciSTATIC int 27062306a36Sopenharmony_cixlog_recover_inode_commit_pass2( 27162306a36Sopenharmony_ci struct xlog *log, 27262306a36Sopenharmony_ci struct list_head *buffer_list, 27362306a36Sopenharmony_ci struct xlog_recover_item *item, 27462306a36Sopenharmony_ci xfs_lsn_t current_lsn) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct xfs_inode_log_format *in_f; 27762306a36Sopenharmony_ci struct xfs_mount *mp = log->l_mp; 27862306a36Sopenharmony_ci struct xfs_buf *bp; 27962306a36Sopenharmony_ci struct xfs_dinode *dip; 28062306a36Sopenharmony_ci int len; 28162306a36Sopenharmony_ci char *src; 28262306a36Sopenharmony_ci char *dest; 28362306a36Sopenharmony_ci int error; 28462306a36Sopenharmony_ci int attr_index; 28562306a36Sopenharmony_ci uint fields; 28662306a36Sopenharmony_ci struct xfs_log_dinode *ldip; 28762306a36Sopenharmony_ci uint isize; 28862306a36Sopenharmony_ci int need_free = 0; 28962306a36Sopenharmony_ci xfs_failaddr_t fa; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) { 29262306a36Sopenharmony_ci in_f = item->ri_buf[0].i_addr; 29362306a36Sopenharmony_ci } else { 29462306a36Sopenharmony_ci in_f = kmem_alloc(sizeof(struct xfs_inode_log_format), 0); 29562306a36Sopenharmony_ci need_free = 1; 29662306a36Sopenharmony_ci error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f); 29762306a36Sopenharmony_ci if (error) 29862306a36Sopenharmony_ci goto error; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Inode buffers can be freed, look out for it, 30362306a36Sopenharmony_ci * and do not replay the inode. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci if (xlog_is_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len)) { 30662306a36Sopenharmony_ci error = 0; 30762306a36Sopenharmony_ci trace_xfs_log_recover_inode_cancel(log, in_f); 30862306a36Sopenharmony_ci goto error; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci trace_xfs_log_recover_inode_recover(log, in_f); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci error = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len, 31362306a36Sopenharmony_ci 0, &bp, &xfs_inode_buf_ops); 31462306a36Sopenharmony_ci if (error) 31562306a36Sopenharmony_ci goto error; 31662306a36Sopenharmony_ci ASSERT(in_f->ilf_fields & XFS_ILOG_CORE); 31762306a36Sopenharmony_ci dip = xfs_buf_offset(bp, in_f->ilf_boffset); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* 32062306a36Sopenharmony_ci * Make sure the place we're flushing out to really looks 32162306a36Sopenharmony_ci * like an inode! 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) { 32462306a36Sopenharmony_ci xfs_alert(mp, 32562306a36Sopenharmony_ci "%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %lld", 32662306a36Sopenharmony_ci __func__, dip, bp, in_f->ilf_ino); 32762306a36Sopenharmony_ci error = -EFSCORRUPTED; 32862306a36Sopenharmony_ci goto out_release; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci ldip = item->ri_buf[1].i_addr; 33162306a36Sopenharmony_ci if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) { 33262306a36Sopenharmony_ci xfs_alert(mp, 33362306a36Sopenharmony_ci "%s: Bad inode log record, rec ptr "PTR_FMT", ino %lld", 33462306a36Sopenharmony_ci __func__, item, in_f->ilf_ino); 33562306a36Sopenharmony_ci error = -EFSCORRUPTED; 33662306a36Sopenharmony_ci goto out_release; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * If the inode has an LSN in it, recover the inode only if the on-disk 34162306a36Sopenharmony_ci * inode's LSN is older than the lsn of the transaction we are 34262306a36Sopenharmony_ci * replaying. We can have multiple checkpoints with the same start LSN, 34362306a36Sopenharmony_ci * so the current LSN being equal to the on-disk LSN doesn't necessarily 34462306a36Sopenharmony_ci * mean that the on-disk inode is more recent than the change being 34562306a36Sopenharmony_ci * replayed. 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * We must check the current_lsn against the on-disk inode 34862306a36Sopenharmony_ci * here because the we can't trust the log dinode to contain a valid LSN 34962306a36Sopenharmony_ci * (see comment below before replaying the log dinode for details). 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Note: we still need to replay an owner change even though the inode 35262306a36Sopenharmony_ci * is more recent than the transaction as there is no guarantee that all 35362306a36Sopenharmony_ci * the btree blocks are more recent than this transaction, too. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci if (dip->di_version >= 3) { 35662306a36Sopenharmony_ci xfs_lsn_t lsn = be64_to_cpu(dip->di_lsn); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) > 0) { 35962306a36Sopenharmony_ci trace_xfs_log_recover_inode_skip(log, in_f); 36062306a36Sopenharmony_ci error = 0; 36162306a36Sopenharmony_ci goto out_owner_change; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* 36662306a36Sopenharmony_ci * di_flushiter is only valid for v1/2 inodes. All changes for v3 inodes 36762306a36Sopenharmony_ci * are transactional and if ordering is necessary we can determine that 36862306a36Sopenharmony_ci * more accurately by the LSN field in the V3 inode core. Don't trust 36962306a36Sopenharmony_ci * the inode versions we might be changing them here - use the 37062306a36Sopenharmony_ci * superblock flag to determine whether we need to look at di_flushiter 37162306a36Sopenharmony_ci * to skip replay when the on disk inode is newer than the log one 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci if (!xfs_has_v3inodes(mp)) { 37462306a36Sopenharmony_ci if (ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) { 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Deal with the wrap case, DI_MAX_FLUSH is less 37762306a36Sopenharmony_ci * than smaller numbers 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH && 38062306a36Sopenharmony_ci ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) { 38162306a36Sopenharmony_ci /* do nothing */ 38262306a36Sopenharmony_ci } else { 38362306a36Sopenharmony_ci trace_xfs_log_recover_inode_skip(log, in_f); 38462306a36Sopenharmony_ci error = 0; 38562306a36Sopenharmony_ci goto out_release; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Take the opportunity to reset the flush iteration count */ 39062306a36Sopenharmony_ci ldip->di_flushiter = 0; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (unlikely(S_ISREG(ldip->di_mode))) { 39562306a36Sopenharmony_ci if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) && 39662306a36Sopenharmony_ci (ldip->di_format != XFS_DINODE_FMT_BTREE)) { 39762306a36Sopenharmony_ci XFS_CORRUPTION_ERROR( 39862306a36Sopenharmony_ci "Bad log dinode data fork format for regular file", 39962306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, ldip, sizeof(*ldip)); 40062306a36Sopenharmony_ci xfs_alert(mp, 40162306a36Sopenharmony_ci "Bad inode 0x%llx, data fork format 0x%x", 40262306a36Sopenharmony_ci in_f->ilf_ino, ldip->di_format); 40362306a36Sopenharmony_ci error = -EFSCORRUPTED; 40462306a36Sopenharmony_ci goto out_release; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } else if (unlikely(S_ISDIR(ldip->di_mode))) { 40762306a36Sopenharmony_ci if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) && 40862306a36Sopenharmony_ci (ldip->di_format != XFS_DINODE_FMT_BTREE) && 40962306a36Sopenharmony_ci (ldip->di_format != XFS_DINODE_FMT_LOCAL)) { 41062306a36Sopenharmony_ci XFS_CORRUPTION_ERROR( 41162306a36Sopenharmony_ci "Bad log dinode data fork format for directory", 41262306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, ldip, sizeof(*ldip)); 41362306a36Sopenharmony_ci xfs_alert(mp, 41462306a36Sopenharmony_ci "Bad inode 0x%llx, data fork format 0x%x", 41562306a36Sopenharmony_ci in_f->ilf_ino, ldip->di_format); 41662306a36Sopenharmony_ci error = -EFSCORRUPTED; 41762306a36Sopenharmony_ci goto out_release; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci error = xlog_dinode_verify_extent_counts(mp, ldip); 42262306a36Sopenharmony_ci if (error) 42362306a36Sopenharmony_ci goto out_release; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (unlikely(ldip->di_forkoff > mp->m_sb.sb_inodesize)) { 42662306a36Sopenharmony_ci XFS_CORRUPTION_ERROR("Bad log dinode fork offset", 42762306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, ldip, sizeof(*ldip)); 42862306a36Sopenharmony_ci xfs_alert(mp, 42962306a36Sopenharmony_ci "Bad inode 0x%llx, di_forkoff 0x%x", 43062306a36Sopenharmony_ci in_f->ilf_ino, ldip->di_forkoff); 43162306a36Sopenharmony_ci error = -EFSCORRUPTED; 43262306a36Sopenharmony_ci goto out_release; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci isize = xfs_log_dinode_size(mp); 43562306a36Sopenharmony_ci if (unlikely(item->ri_buf[1].i_len > isize)) { 43662306a36Sopenharmony_ci XFS_CORRUPTION_ERROR("Bad log dinode size", XFS_ERRLEVEL_LOW, 43762306a36Sopenharmony_ci mp, ldip, sizeof(*ldip)); 43862306a36Sopenharmony_ci xfs_alert(mp, 43962306a36Sopenharmony_ci "Bad inode 0x%llx log dinode size 0x%x", 44062306a36Sopenharmony_ci in_f->ilf_ino, item->ri_buf[1].i_len); 44162306a36Sopenharmony_ci error = -EFSCORRUPTED; 44262306a36Sopenharmony_ci goto out_release; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Recover the log dinode inode into the on disk inode. 44762306a36Sopenharmony_ci * 44862306a36Sopenharmony_ci * The LSN in the log dinode is garbage - it can be zero or reflect 44962306a36Sopenharmony_ci * stale in-memory runtime state that isn't coherent with the changes 45062306a36Sopenharmony_ci * logged in this transaction or the changes written to the on-disk 45162306a36Sopenharmony_ci * inode. Hence we write the current lSN into the inode because that 45262306a36Sopenharmony_ci * matches what xfs_iflush() would write inode the inode when flushing 45362306a36Sopenharmony_ci * the changes in this transaction. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci xfs_log_dinode_to_disk(ldip, dip, current_lsn); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci fields = in_f->ilf_fields; 45862306a36Sopenharmony_ci if (fields & XFS_ILOG_DEV) 45962306a36Sopenharmony_ci xfs_dinode_put_rdev(dip, in_f->ilf_u.ilfu_rdev); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (in_f->ilf_size == 2) 46262306a36Sopenharmony_ci goto out_owner_change; 46362306a36Sopenharmony_ci len = item->ri_buf[2].i_len; 46462306a36Sopenharmony_ci src = item->ri_buf[2].i_addr; 46562306a36Sopenharmony_ci ASSERT(in_f->ilf_size <= 4); 46662306a36Sopenharmony_ci ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK)); 46762306a36Sopenharmony_ci ASSERT(!(fields & XFS_ILOG_DFORK) || 46862306a36Sopenharmony_ci (len == xlog_calc_iovec_len(in_f->ilf_dsize))); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci switch (fields & XFS_ILOG_DFORK) { 47162306a36Sopenharmony_ci case XFS_ILOG_DDATA: 47262306a36Sopenharmony_ci case XFS_ILOG_DEXT: 47362306a36Sopenharmony_ci memcpy(XFS_DFORK_DPTR(dip), src, len); 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci case XFS_ILOG_DBROOT: 47762306a36Sopenharmony_ci xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, len, 47862306a36Sopenharmony_ci (struct xfs_bmdr_block *)XFS_DFORK_DPTR(dip), 47962306a36Sopenharmony_ci XFS_DFORK_DSIZE(dip, mp)); 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci default: 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * There are no data fork flags set. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ci ASSERT((fields & XFS_ILOG_DFORK) == 0); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * If we logged any attribute data, recover it. There may or 49262306a36Sopenharmony_ci * may not have been any other non-core data logged in this 49362306a36Sopenharmony_ci * transaction. 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_ci if (in_f->ilf_fields & XFS_ILOG_AFORK) { 49662306a36Sopenharmony_ci if (in_f->ilf_fields & XFS_ILOG_DFORK) { 49762306a36Sopenharmony_ci attr_index = 3; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci attr_index = 2; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci len = item->ri_buf[attr_index].i_len; 50262306a36Sopenharmony_ci src = item->ri_buf[attr_index].i_addr; 50362306a36Sopenharmony_ci ASSERT(len == xlog_calc_iovec_len(in_f->ilf_asize)); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci switch (in_f->ilf_fields & XFS_ILOG_AFORK) { 50662306a36Sopenharmony_ci case XFS_ILOG_ADATA: 50762306a36Sopenharmony_ci case XFS_ILOG_AEXT: 50862306a36Sopenharmony_ci dest = XFS_DFORK_APTR(dip); 50962306a36Sopenharmony_ci ASSERT(len <= XFS_DFORK_ASIZE(dip, mp)); 51062306a36Sopenharmony_ci memcpy(dest, src, len); 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci case XFS_ILOG_ABROOT: 51462306a36Sopenharmony_ci dest = XFS_DFORK_APTR(dip); 51562306a36Sopenharmony_ci xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, 51662306a36Sopenharmony_ci len, (struct xfs_bmdr_block *)dest, 51762306a36Sopenharmony_ci XFS_DFORK_ASIZE(dip, mp)); 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci default: 52162306a36Sopenharmony_ci xfs_warn(log->l_mp, "%s: Invalid flag", __func__); 52262306a36Sopenharmony_ci ASSERT(0); 52362306a36Sopenharmony_ci error = -EFSCORRUPTED; 52462306a36Sopenharmony_ci goto out_release; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ciout_owner_change: 52962306a36Sopenharmony_ci /* Recover the swapext owner change unless inode has been deleted */ 53062306a36Sopenharmony_ci if ((in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER)) && 53162306a36Sopenharmony_ci (dip->di_mode != 0)) 53262306a36Sopenharmony_ci error = xfs_recover_inode_owner_change(mp, dip, in_f, 53362306a36Sopenharmony_ci buffer_list); 53462306a36Sopenharmony_ci /* re-generate the checksum and validate the recovered inode. */ 53562306a36Sopenharmony_ci xfs_dinode_calc_crc(log->l_mp, dip); 53662306a36Sopenharmony_ci fa = xfs_dinode_verify(log->l_mp, in_f->ilf_ino, dip); 53762306a36Sopenharmony_ci if (fa) { 53862306a36Sopenharmony_ci XFS_CORRUPTION_ERROR( 53962306a36Sopenharmony_ci "Bad dinode after recovery", 54062306a36Sopenharmony_ci XFS_ERRLEVEL_LOW, mp, dip, sizeof(*dip)); 54162306a36Sopenharmony_ci xfs_alert(mp, 54262306a36Sopenharmony_ci "Metadata corruption detected at %pS, inode 0x%llx", 54362306a36Sopenharmony_ci fa, in_f->ilf_ino); 54462306a36Sopenharmony_ci error = -EFSCORRUPTED; 54562306a36Sopenharmony_ci goto out_release; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ASSERT(bp->b_mount == mp); 54962306a36Sopenharmony_ci bp->b_flags |= _XBF_LOGRECOVERY; 55062306a36Sopenharmony_ci xfs_buf_delwri_queue(bp, buffer_list); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ciout_release: 55362306a36Sopenharmony_ci xfs_buf_relse(bp); 55462306a36Sopenharmony_cierror: 55562306a36Sopenharmony_ci if (need_free) 55662306a36Sopenharmony_ci kmem_free(in_f); 55762306a36Sopenharmony_ci return error; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ciconst struct xlog_recover_item_ops xlog_inode_item_ops = { 56162306a36Sopenharmony_ci .item_type = XFS_LI_INODE, 56262306a36Sopenharmony_ci .ra_pass2 = xlog_recover_inode_ra_pass2, 56362306a36Sopenharmony_ci .commit_pass2 = xlog_recover_inode_commit_pass2, 56462306a36Sopenharmony_ci}; 565