xfs: iget for metadata inodes
authorDarrick J. Wong <djwong@kernel.org>
Mon, 4 Nov 2024 04:18:50 +0000 (20:18 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 5 Nov 2024 21:38:31 +0000 (13:38 -0800)
Create a xfs_trans_metafile_iget function for metadata inodes to ensure
that when we try to iget a metadata file, the inode is allocated and its
file mode matches the metadata file type the caller expects.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_metafile.h [new file with mode: 0644]
fs/xfs/xfs_icache.c
fs/xfs/xfs_inode.c
fs/xfs/xfs_qm.c
fs/xfs/xfs_rtalloc.c

diff --git a/fs/xfs/libxfs/xfs_metafile.h b/fs/xfs/libxfs/xfs_metafile.h
new file mode 100644 (file)
index 0000000..60fe189
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2018-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_METAFILE_H__
+#define __XFS_METAFILE_H__
+
+/* Code specific to kernel/userspace; must be provided externally. */
+
+int xfs_trans_metafile_iget(struct xfs_trans *tp, xfs_ino_t ino,
+               enum xfs_metafile_type metafile_type, struct xfs_inode **ipp);
+int xfs_metafile_iget(struct xfs_mount *mp, xfs_ino_t ino,
+               enum xfs_metafile_type metafile_type, struct xfs_inode **ipp);
+
+#endif /* __XFS_METAFILE_H__ */
index 383c245482027b6df94c23ea03c90acc4ddef93d..aa645e357812027a5e6dbe99d533f424c0ae218e 100644 (file)
@@ -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 <linux/iversion.h>
 
@@ -828,6 +831,68 @@ out_error_or_again:
        return error;
 }
 
+/*
+ * Get a metadata inode.
+ *
+ * The metafile type must match the file mode exactly.
+ */
+int
+xfs_trans_metafile_iget(
+       struct xfs_trans        *tp,
+       xfs_ino_t               ino,
+       enum xfs_metafile_type  metafile_type,
+       struct xfs_inode        **ipp)
+{
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_inode        *ip;
+       umode_t                 mode;
+       int                     error;
+
+       error = xfs_iget(mp, tp, ino, 0, 0, &ip);
+       if (error == -EFSCORRUPTED)
+               goto whine;
+       if (error)
+               return error;
+
+       if (VFS_I(ip)->i_nlink == 0)
+               goto bad_rele;
+
+       if (metafile_type == XFS_METAFILE_DIR)
+               mode = S_IFDIR;
+       else
+               mode = S_IFREG;
+       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 a metadata file if the caller doesn't already have a transaction. */
+int
+xfs_metafile_iget(
+       struct xfs_mount        *mp,
+       xfs_ino_t               ino,
+       enum xfs_metafile_type  metafile_type,
+       struct xfs_inode        **ipp)
+{
+       struct xfs_trans        *tp;
+       int                     error;
+
+       error = xfs_trans_alloc_empty(mp, &tp);
+       if (error)
+               return error;
+
+       error = xfs_trans_metafile_iget(tp, ino, metafile_type, ipp);
+       xfs_trans_cancel(tp);
+       return error;
+}
+
 /*
  * Grab the inode for reclaim exclusively.
  *
index 0465546010554a6deeacbf46c7007522e3c4e308..12c5ff151edf4c1d5e21890ed5dcd881fd3fd712 100644 (file)
@@ -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;
 
index 7e2307921deb2fbb7cc2660837158c459c5b7853..d0674d84af3ec5670174e8a2479dcde9cbee3981 100644 (file)
@@ -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
@@ -733,6 +735,17 @@ xfs_qm_destroy_quotainfo(
        mp->m_quotainfo = NULL;
 }
 
+static inline enum xfs_metafile_type
+xfs_qm_metafile_type(
+       unsigned int            flags)
+{
+       if (flags & XFS_QMOPT_UQUOTA)
+               return XFS_METAFILE_USRQUOTA;
+       else if (flags & XFS_QMOPT_GQUOTA)
+               return XFS_METAFILE_GRPQUOTA;
+       return XFS_METAFILE_PRJQUOTA;
+}
+
 /*
  * Create an inode and return with a reference already taken, but unlocked
  * This is how we create quota inodes
@@ -744,6 +757,7 @@ xfs_qm_qino_alloc(
        unsigned int            flags)
 {
        struct xfs_trans        *tp;
+       enum xfs_metafile_type  metafile_type = xfs_qm_metafile_type(flags);
        int                     error;
        bool                    need_alloc = true;
 
@@ -777,9 +791,10 @@ xfs_qm_qino_alloc(
                        }
                }
                if (ino != NULLFSINO) {
-                       error = xfs_iget(mp, NULL, ino, 0, 0, ipp);
+                       error = xfs_metafile_iget(mp, ino, metafile_type, ipp);
                        if (error)
                                return error;
+
                        mp->m_sb.sb_gquotino = NULLFSINO;
                        mp->m_sb.sb_pquotino = NULLFSINO;
                        need_alloc = false;
@@ -1553,16 +1568,20 @@ xfs_qm_qino_load(
        struct xfs_inode        **ipp)
 {
        xfs_ino_t               ino = NULLFSINO;
+       enum xfs_metafile_type  metafile_type = XFS_METAFILE_UNKNOWN;
 
        switch (type) {
        case XFS_DQTYPE_USER:
                ino = mp->m_sb.sb_uquotino;
+               metafile_type = XFS_METAFILE_USRQUOTA;
                break;
        case XFS_DQTYPE_GROUP:
                ino = mp->m_sb.sb_gquotino;
+               metafile_type = XFS_METAFILE_GRPQUOTA;
                break;
        case XFS_DQTYPE_PROJ:
                ino = mp->m_sb.sb_pquotino;
+               metafile_type = XFS_METAFILE_PRJQUOTA;
                break;
        default:
                ASSERT(0);
@@ -1572,7 +1591,7 @@ xfs_qm_qino_load(
        if (ino == NULLFSINO)
                return -ENOENT;
 
-       return xfs_iget(mp, NULL, ino, 0, 0, ipp);
+       return xfs_metafile_iget(mp, ino, metafile_type, ipp);
 }
 
 /*
index 3a2005a1e673dcc5843a8e8e386c17ec0297e9ad..46a920b192d1914e515b5286725a61b33abc9a53 100644 (file)
@@ -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
@@ -1101,16 +1103,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);
@@ -1125,7 +1123,6 @@ xfs_rtmount_iread_extents(
 
 out_unlock:
        xfs_iunlock(ip, XFS_ILOCK_EXCL | lock_class);
-       xfs_trans_cancel(tp);
        return error;
 }
 
@@ -1133,45 +1130,54 @@ 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_trans_metafile_iget(tp, mp->m_sb.sb_rbmino,
+                       XFS_METAFILE_RTBITMAP, &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_trans_metafile_iget(tp, mp->m_sb.sb_rsumino,
+                       XFS_METAFILE_RTSUMMARY, &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;
 
        error = xfs_alloc_rsum_cache(mp, sbp->sb_rbmblocks);
        if (error)
                goto out_rele_summary;
+       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;
 }