@@ -31,6 +31,9 @@
#include "xfs_attr.h"
#include "xfs_reflink.h"
#include "xfs_health.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_dir2.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
@@ -1502,6 +1505,125 @@ xrep_metadata_inode_forks(
return error;
}
+/* Create a temporary file or directory. */
+int
+xrep_create_tempfile(
+ struct xfs_scrub *sc,
+ uint16_t mode)
+{
+ struct xfs_ialloc_args args = {
+ .pip = sc->mp->m_rootip,
+ .nlink = 0,
+ .mode = mode,
+ };
+ struct xfs_mount *mp = sc->mp;
+ struct xfs_trans *tp = NULL;
+ struct xfs_dquot *udqp = NULL;
+ struct xfs_dquot *gdqp = NULL;
+ struct xfs_dquot *pdqp = NULL;
+ struct xfs_trans_res *tres;
+ unsigned int resblks;
+ bool is_dir = S_ISDIR(mode);
+ int error;
+
+ ASSERT(sc->tp == NULL);
+ ASSERT(sc->tempip == NULL);
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return -EIO;
+
+ /*
+ * Make sure that we have allocated dquot(s) on disk. The temporary
+ * inode should be completely root owned, but we'll still go through
+ * the motions to keep the quota accounting accurate.
+ */
+ error = xfs_qm_vop_dqalloc(sc->mp->m_rootip, args.uid, args.gid,
+ args.prid, XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
+ &udqp, &gdqp, &pdqp);
+ if (error)
+ return error;
+
+ if (is_dir) {
+ resblks = XFS_MKDIR_SPACE_RES(mp, 0);
+ tres = &M_RES(mp)->tr_mkdir;
+ } else {
+ resblks = XFS_IALLOC_SPACE_RES(mp);
+ tres = &M_RES(mp)->tr_create_tmpfile;
+ }
+
+ error = xfs_trans_alloc(mp, tres, resblks, 0, 0, &tp);
+ if (error)
+ goto out_release_inode;
+
+ error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, pdqp, resblks,
+ 1, 0);
+ if (error)
+ goto out_trans_cancel;
+
+ /* Allocate inode, set up directory. */
+ error = xfs_dir_ialloc(&tp, &args, &sc->tempip);
+ if (error)
+ goto out_trans_cancel;
+
+ if (is_dir) {
+ error = xfs_dir_init(tp, sc->tempip, sc->mp->m_rootip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
+ /*
+ * Attach the dquot(s) to the inodes and modify them incore.
+ * These ids of the inode couldn't have changed since the new
+ * inode has been locked ever since it was created.
+ */
+ xfs_qm_vop_create_dqattach(tp, sc->tempip, udqp, gdqp, pdqp);
+
+ /*
+ * Put our temp file on the unlinked list so it's purged automatically.
+ * Anything being reconstructed using this file must be atomically
+ * swapped with the original file because the contents here will be
+ * purged when the inode is dropped or log recovery cleans out the
+ * unlinked list.
+ */
+ error = xfs_iunlink(tp, sc->tempip);
+ if (error)
+ goto out_trans_cancel;
+
+ error = xfs_trans_commit(tp);
+ if (error)
+ goto out_release_inode;
+
+ xfs_qm_dqrele(udqp);
+ xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
+
+ /* Finish setting up the incore / vfs context. */
+ xfs_setup_iops(sc->tempip);
+ xfs_finish_inode_setup(sc->tempip);
+
+ sc->temp_ilock_flags = 0;
+ return error;
+
+out_trans_cancel:
+ xfs_trans_cancel(tp);
+out_release_inode:
+ /*
+ * Wait until after the current transaction is aborted to finish the
+ * setup of the inode and release the inode. This prevents recursive
+ * transactions and deadlocks from xfs_inactive.
+ */
+ if (sc->tempip) {
+ xfs_finish_inode_setup(sc->tempip);
+ xfs_irele(sc->tempip);
+ }
+
+ xfs_qm_dqrele(udqp);
+ xfs_qm_dqrele(gdqp);
+ xfs_qm_dqrele(pdqp);
+
+ return error;
+}
+
/*
* Make sure that the given range of the data fork of the metadata file being
* checked is mapped to written blocks. The caller must ensure that the inode
@@ -32,6 +32,7 @@ int xrep_alloc_ag_block(struct xfs_scrub *sc,
int xrep_init_btblock(struct xfs_scrub *sc, xfs_fsblock_t fsb,
struct xfs_buf **bpp, xfs_btnum_t btnum,
const struct xfs_buf_ops *ops);
+int xrep_create_tempfile(struct xfs_scrub *sc, uint16_t mode);
int xrep_fallocate(struct xfs_scrub *sc, xfs_fileoff_t off, xfs_filblks_t len);
typedef int (*xrep_setfile_getbuf_fn)(struct xfs_scrub *sc,
@@ -194,6 +194,12 @@ xchk_teardown(
kmem_free(sc->buf);
sc->buf = NULL;
}
+ if (sc->tempip) {
+ if (sc->temp_ilock_flags)
+ xfs_iunlock(sc->tempip, sc->temp_ilock_flags);
+ xfs_irele(sc->tempip);
+ sc->tempip = NULL;
+ }
return error;
}
@@ -72,6 +72,9 @@ struct xfs_scrub {
struct file *xfile;
uint ilock_flags;
+ struct xfs_inode *tempip;
+ uint temp_ilock_flags;
+
/* See the XCHK/XREP state flags below. */
unsigned int flags;