From 4bede24ae27f7394dcca7eb2fc3576c50a80ba53 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 28 May 2024 21:10:54 -0700 Subject: [PATCH] xfs: iget for metadata inodes Create a xfs_imeta_iget function for metadata inodes to ensure that when we try to iget a metadata file, the inobt thinks a metadata inode is in use and that the file type matches what we are expecting. Signed-off-by: Darrick J. Wong --- fs/xfs/libxfs/xfs_metafile.h | 14 +++++++++++++ fs/xfs/xfs_icache.c | 39 ++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_inode.c | 1 + fs/xfs/xfs_qm.c | 36 +++++++++++++++++++++++++-------- fs/xfs/xfs_qm_syscalls.c | 9 ++++++++- fs/xfs/xfs_rtalloc.c | 36 ++++++++++++++++++--------------- 6 files changed, 110 insertions(+), 25 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_metafile.h diff --git a/fs/xfs/libxfs/xfs_metafile.h b/fs/xfs/libxfs/xfs_metafile.h new file mode 100644 index 000000000000..ea0ba76be6e8 --- /dev/null +++ b/fs/xfs/libxfs/xfs_metafile.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2018-2024 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#ifndef __XFS_METAFILE_H__ +#define __XFS_METAFILE_H__ + +/* Code specific to kernel/userspace; must be provided externally. */ + +int xfs_metafile_iget(struct xfs_trans *tp, xfs_ino_t ino, umode_t mode, + struct xfs_inode **ipp); + +#endif /* __XFS_METAFILE_H__ */ diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 42a1d2e8cd56..7db98d4167fa 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -25,6 +25,9 @@ #include "xfs_ag.h" #include "xfs_log_priv.h" #include "xfs_health.h" +#include "xfs_da_format.h" +#include "xfs_dir2.h" +#include "xfs_metafile.h" #include @@ -808,6 +811,42 @@ out_error_or_again: return error; } +/* + * Get a metadata inode. The file type part of @mode must match the inode + * exactly. Caller must supply a transaction (even if empty) to avoid + * livelocking if the inobt has a cycle. + */ +int +xfs_metafile_iget( + struct xfs_trans *tp, + xfs_ino_t ino, + umode_t mode, + struct xfs_inode **ipp) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_inode *ip; + int error; + + error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED, 0, &ip); + if (error == -EFSCORRUPTED) + goto whine; + if (error) + return error; + + if (VFS_I(ip)->i_nlink == 0) + goto bad_rele; + if (inode_wrong_type(VFS_I(ip), mode)) + goto bad_rele; + + *ipp = ip; + return 0; +bad_rele: + xfs_irele(ip); +whine: + xfs_err(mp, "metadata inode 0x%llx is corrupt", ino); + return -EFSCORRUPTED; +} + /* * Grab the inode for reclaim exclusively. * diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 9ea7a18f5da1..e1c65507479c 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -43,6 +43,7 @@ #include "xfs_parent.h" #include "xfs_xattr.h" #include "xfs_inode_util.h" +#include "xfs_metafile.h" struct kmem_cache *xfs_inode_cache; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 63f6ca2db251..9c232e758af7 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -27,6 +27,8 @@ #include "xfs_ialloc.h" #include "xfs_log_priv.h" #include "xfs_health.h" +#include "xfs_da_format.h" +#include "xfs_metafile.h" /* * The global quota manager. There is only one of these for the entire @@ -777,9 +779,17 @@ xfs_qm_qino_alloc( } } if (ino != NULLFSINO) { - error = xfs_iget(mp, NULL, ino, 0, 0, ipp); + struct xfs_trans *tp; + + error = xfs_trans_alloc_empty(mp, &tp); + if (error) + return error; + + error = xfs_metafile_iget(tp, ino, S_IFREG, ipp); + xfs_trans_cancel(tp); if (error) return error; + mp->m_sb.sb_gquotino = NULLFSINO; mp->m_sb.sb_pquotino = NULLFSINO; need_alloc = false; @@ -1549,6 +1559,7 @@ xfs_qm_init_quotainos( struct xfs_inode *uip = NULL; struct xfs_inode *gip = NULL; struct xfs_inode *pip = NULL; + struct xfs_trans *tp = NULL; int error; uint flags = 0; @@ -1558,30 +1569,37 @@ xfs_qm_init_quotainos( * Get the uquota and gquota inodes */ if (xfs_has_quota(mp)) { + error = xfs_trans_alloc_empty(mp, &tp); + if (error) + return error; + if (XFS_IS_UQUOTA_ON(mp) && mp->m_sb.sb_uquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_uquotino > 0); - error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, - 0, 0, &uip); + error = xfs_metafile_iget(tp, mp->m_sb.sb_uquotino, + S_IFREG, &uip); if (error) - return error; + goto error_rele; } if (XFS_IS_GQUOTA_ON(mp) && mp->m_sb.sb_gquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_gquotino > 0); - error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, - 0, 0, &gip); + error = xfs_metafile_iget(tp, mp->m_sb.sb_gquotino, + S_IFREG, &gip); if (error) goto error_rele; } if (XFS_IS_PQUOTA_ON(mp) && mp->m_sb.sb_pquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_pquotino > 0); - error = xfs_iget(mp, NULL, mp->m_sb.sb_pquotino, - 0, 0, &pip); + error = xfs_metafile_iget(tp, mp->m_sb.sb_pquotino, + S_IFREG, &pip); if (error) goto error_rele; } + + xfs_trans_cancel(tp); + tp = NULL; } else { flags |= XFS_QMOPT_SBVERSION; } @@ -1622,6 +1640,8 @@ xfs_qm_init_quotainos( return 0; error_rele: + if (tp) + xfs_trans_cancel(tp); if (uip) xfs_irele(uip); if (gip) diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 392cb39cc10c..87253427800b 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -18,6 +18,8 @@ #include "xfs_quota.h" #include "xfs_qm.h" #include "xfs_icache.h" +#include "xfs_da_format.h" +#include "xfs_metafile.h" int xfs_qm_scall_quotaoff( @@ -62,7 +64,12 @@ xfs_qm_scall_trunc_qfile( if (ino == NULLFSINO) return 0; - error = xfs_iget(mp, NULL, ino, 0, 0, &ip); + error = xfs_trans_alloc_empty(mp, &tp); + if (error) + return error; + + error = xfs_metafile_iget(tp, ino, S_IFREG, &ip); + xfs_trans_cancel(tp); if (error) return error; diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 0c3e96c621a6..3eb402cce2f5 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -25,6 +25,8 @@ #include "xfs_quota.h" #include "xfs_log_priv.h" #include "xfs_health.h" +#include "xfs_da_format.h" +#include "xfs_metafile.h" /* * Return whether there are any free extents in the size range given @@ -1154,16 +1156,12 @@ xfs_rtalloc_reinit_frextents( */ static inline int xfs_rtmount_iread_extents( + struct xfs_trans *tp, struct xfs_inode *ip, unsigned int lock_class) { - struct xfs_trans *tp; int error; - error = xfs_trans_alloc_empty(ip->i_mount, &tp); - if (error) - return error; - xfs_ilock(ip, XFS_ILOCK_EXCL | lock_class); error = xfs_iread_extents(tp, ip, XFS_DATA_FORK); @@ -1178,7 +1176,6 @@ xfs_rtmount_iread_extents( out_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL | lock_class); - xfs_trans_cancel(tp); return error; } @@ -1186,43 +1183,50 @@ out_unlock: * Get the bitmap and summary inodes and the summary cache into the mount * structure at mount time. */ -int /* error */ +int xfs_rtmount_inodes( - xfs_mount_t *mp) /* file system mount structure */ + struct xfs_mount *mp) { - int error; /* error return value */ - xfs_sb_t *sbp; + struct xfs_trans *tp; + struct xfs_sb *sbp = &mp->m_sb; + int error; - sbp = &mp->m_sb; - error = xfs_iget(mp, NULL, sbp->sb_rbmino, 0, 0, &mp->m_rbmip); + error = xfs_trans_alloc_empty(mp, &tp); + if (error) + return error; + + error = xfs_metafile_iget(tp, mp->m_sb.sb_rbmino, S_IFREG, &mp->m_rbmip); if (xfs_metadata_is_sick(error)) xfs_rt_mark_sick(mp, XFS_SICK_RT_BITMAP); if (error) - return error; + goto out_trans; ASSERT(mp->m_rbmip != NULL); - error = xfs_rtmount_iread_extents(mp->m_rbmip, XFS_ILOCK_RTBITMAP); + error = xfs_rtmount_iread_extents(tp, mp->m_rbmip, XFS_ILOCK_RTBITMAP); if (error) goto out_rele_bitmap; - error = xfs_iget(mp, NULL, sbp->sb_rsumino, 0, 0, &mp->m_rsumip); + error = xfs_metafile_iget(tp, mp->m_sb.sb_rsumino, S_IFREG, &mp->m_rsumip); if (xfs_metadata_is_sick(error)) xfs_rt_mark_sick(mp, XFS_SICK_RT_SUMMARY); if (error) goto out_rele_bitmap; ASSERT(mp->m_rsumip != NULL); - error = xfs_rtmount_iread_extents(mp->m_rsumip, XFS_ILOCK_RTSUM); + error = xfs_rtmount_iread_extents(tp, mp->m_rsumip, XFS_ILOCK_RTSUM); if (error) goto out_rele_summary; xfs_alloc_rsum_cache(mp, sbp->sb_rbmblocks); + xfs_trans_cancel(tp); return 0; out_rele_summary: xfs_irele(mp->m_rsumip); out_rele_bitmap: xfs_irele(mp->m_rbmip); +out_trans: + xfs_trans_cancel(tp); return error; } -- 2.50.1