Merge tag 'mips-fixes_5.14_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips...
[linux-2.6-microblaze.git] / fs / xfs / libxfs / xfs_ialloc.c
index 57d9cb6..aaf8805 100644 (file)
@@ -2928,3 +2928,58 @@ xfs_ialloc_calc_rootino(
 
        return XFS_AGINO_TO_INO(mp, 0, XFS_AGB_TO_AGINO(mp, first_bno));
 }
+
+/*
+ * Ensure there are not sparse inode clusters that cross the new EOAG.
+ *
+ * This is a no-op for non-spinode filesystems since clusters are always fully
+ * allocated and checking the bnobt suffices.  However, a spinode filesystem
+ * could have a record where the upper inodes are free blocks.  If those blocks
+ * were removed from the filesystem, the inode record would extend beyond EOAG,
+ * which will be flagged as corruption.
+ */
+int
+xfs_ialloc_check_shrink(
+       struct xfs_trans        *tp,
+       xfs_agnumber_t          agno,
+       struct xfs_buf          *agibp,
+       xfs_agblock_t           new_length)
+{
+       struct xfs_inobt_rec_incore rec;
+       struct xfs_btree_cur    *cur;
+       struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_perag        *pag;
+       xfs_agino_t             agino = XFS_AGB_TO_AGINO(mp, new_length);
+       int                     has;
+       int                     error;
+
+       if (!xfs_sb_version_hassparseinodes(&mp->m_sb))
+               return 0;
+
+       pag = xfs_perag_get(mp, agno);
+       cur = xfs_inobt_init_cursor(mp, tp, agibp, pag, XFS_BTNUM_INO);
+
+       /* Look up the inobt record that would correspond to the new EOFS. */
+       error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &has);
+       if (error || !has)
+               goto out;
+
+       error = xfs_inobt_get_rec(cur, &rec, &has);
+       if (error)
+               goto out;
+
+       if (!has) {
+               error = -EFSCORRUPTED;
+               goto out;
+       }
+
+       /* If the record covers inodes that would be beyond EOFS, bail out. */
+       if (rec.ir_startino + XFS_INODES_PER_CHUNK > agino) {
+               error = -ENOSPC;
+               goto out;
+       }
+out:
+       xfs_btree_del_cursor(cur, error);
+       xfs_perag_put(pag);
+       return error;
+}