]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs: merge xfs_imeta_utils.[ch] into xfs_imeta.[ch]
authorChristoph Hellwig <hch@lst.de>
Sun, 14 Apr 2024 04:08:46 +0000 (06:08 +0200)
committerChristoph Hellwig <hch@lst.de>
Mon, 15 Apr 2024 17:08:49 +0000 (19:08 +0200)
Source kernel commit: 59528f21f4ff1fdb6eff1262bd5add77d733be8c

The xfs_imeta_utils code is also used in xfsprogs and copied into
libxfs.  Make sure it gets picked up automatically.

Signed-off-by: Christoph Hellwig <hch@lst.de>
libxfs/xfs_imeta.c
libxfs/xfs_imeta.h

index 83e3c3b407d4a646ad2a5f33b106cb06b3c47fe7..ae0e517fbe5938af7879471f49764a97ba3f2664 100644 (file)
@@ -29,6 +29,7 @@
 #include "xfs_errortag.h"
 #include "xfs_btree.h"
 #include "xfs_alloc.h"
+#include "xfs_parent.h"
 
 /*
  * Metadata File Management
@@ -793,3 +794,318 @@ xfs_imeta_resv_init_inode(
        trace_xfs_imeta_resv_init(ip, ask);
        return 0;
 }
+
+/* Initialize a metadata update structure. */
+static inline int
+xfs_imeta_init(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path,
+       struct xfs_imeta_update         *upd)
+{
+       struct xfs_trans                *tp;
+       int                             error;
+
+       memset(upd, 0, sizeof(struct xfs_imeta_update));
+       upd->mp = mp;
+       upd->path = path;
+
+       if (!xfs_has_metadir(mp))
+               return 0;
+
+       /*
+        * Find the parent of the last path component.  If the parent path does
+        * not exist, we consider this corruption because paths are supposed
+        * to exist.  For example, if the path is /quota/user, we require that
+        * /quota already exists.
+        */
+       error = xfs_trans_alloc_empty(mp, &tp);
+       if (error)
+               return error;
+       error = xfs_imeta_dir_parent(tp, upd->path, &upd->dp);
+       xfs_trans_cancel(tp);
+       if (error == -ENOENT) {
+               xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
+               return -EFSCORRUPTED;
+       }
+       if (error)
+               return error;
+
+       return xfs_parent_start(mp, &upd->ppargs);
+}
+
+/*
+ * Unlock and release resources after committing (or cancelling) a metadata
+ * directory tree operation.  The caller retains its reference to @upd->ip
+ * and must release it explicitly.
+ */
+static inline void
+xfs_imeta_teardown(
+       struct xfs_imeta_update         *upd,
+       int                             error)
+{
+       trace_xfs_imeta_teardown(upd, error);
+
+       if (upd->ppargs) {
+               xfs_parent_finish(upd->mp, upd->ppargs);
+               upd->ppargs = NULL;
+       }
+
+       if (upd->ip) {
+               if (upd->ip_locked)
+                       xfs_iunlock(upd->ip, XFS_ILOCK_EXCL);
+               upd->ip_locked = false;
+       }
+
+       if (upd->dp) {
+               if (upd->dp_locked)
+                       xfs_iunlock(upd->dp, XFS_ILOCK_EXCL);
+               upd->dp_locked = false;
+
+               xfs_imeta_irele(upd->dp);
+               upd->dp = NULL;
+       }
+}
+
+/*
+ * Begin the process of creating a metadata file by allocating transactions
+ * and taking whatever resources we're going to need.
+ */
+int
+xfs_imeta_start_create(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path,
+       struct xfs_imeta_update         *upd)
+{
+       int                             error;
+
+       error = xfs_imeta_init(mp, path, upd);
+       if (error)
+               return error;
+
+       /*
+        * If we ever need the ability to create rt metadata files on a
+        * pre-metadir filesystem, we'll need to dqattach the parent here.
+        * Currently we assume that mkfs will create the files and quotacheck
+        * will account for them.
+        */
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_imeta_create,
+                       xfs_create_space_res(mp, MAXNAMELEN), 0, 0, &upd->tp);
+       if (error)
+               goto out_teardown;
+
+       /*
+        * Lock the parent directory if there is one.  We can't ijoin it to
+        * the transaction until after the child file has been created.
+        */
+       if (upd->dp) {
+               xfs_ilock(upd->dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
+               upd->dp_locked = true;
+       }
+
+       trace_xfs_imeta_start_create(upd);
+       return 0;
+out_teardown:
+       xfs_imeta_teardown(upd, error);
+       return error;
+}
+
+/*
+ * Begin the process of linking a metadata file by allocating transactions
+ * and locking whatever resources we're going to need.
+ */
+static inline int
+xfs_imeta_start_dir_update(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path,
+       struct xfs_inode                *ip,
+       struct xfs_trans_res            *tr_resv,
+       unsigned int                    resblks,
+       struct xfs_imeta_update         *upd)
+{
+       int                             error;
+
+       error = xfs_imeta_init(mp, path, upd);
+       if (error)
+               return error;
+
+       upd->ip = ip;
+
+       if (upd->dp) {
+               int                     nospace_error = 0;
+
+               error = xfs_trans_alloc_dir(upd->dp, tr_resv, upd->ip,
+                               &resblks, &upd->tp, &nospace_error);
+               if (error)
+                       goto out_teardown;
+               if (!resblks) {
+                       /* We don't allow reservationless updates. */
+                       xfs_trans_cancel(upd->tp);
+                       upd->tp = NULL;
+                       xfs_iunlock(upd->dp, XFS_ILOCK_EXCL);
+                       xfs_iunlock(upd->ip, XFS_ILOCK_EXCL);
+                       error = nospace_error;
+                       goto out_teardown;
+               }
+
+               upd->dp_locked = true;
+       } else {
+               error = xfs_trans_alloc_inode(upd->ip, tr_resv, resblks, 0,
+                               false, &upd->tp);
+               if (error)
+                       goto out_teardown;
+       }
+
+       upd->ip_locked = true;
+       return 0;
+out_teardown:
+       xfs_imeta_teardown(upd, error);
+       return error;
+}
+
+/*
+ * Begin the process of linking a metadata file by allocating transactions
+ * and locking whatever resources we're going to need.
+ */
+int
+xfs_imeta_start_link(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path,
+       struct xfs_inode                *ip,
+       struct xfs_imeta_update         *upd)
+{
+       int                             error;
+
+       error = xfs_imeta_start_dir_update(mp, path, ip,
+                       &M_RES(mp)->tr_imeta_link,
+                       xfs_link_space_res(mp, MAXNAMELEN), upd);
+       if (error)
+               return error;
+
+       trace_xfs_imeta_start_link(upd);
+       return 0;
+}
+
+/*
+ * Begin the process of unlinking a metadata file by allocating transactions
+ * and locking whatever resources we're going to need.
+ */
+int
+xfs_imeta_start_unlink(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path,
+       struct xfs_inode                *ip,
+       struct xfs_imeta_update         *upd)
+{
+       int                             error;
+
+       error = xfs_imeta_start_dir_update(mp, path, ip,
+                       &M_RES(mp)->tr_imeta_unlink,
+                       xfs_remove_space_res(mp, MAXNAMELEN), upd);
+       if (error)
+               return error;
+
+       trace_xfs_imeta_start_unlink(upd);
+       return 0;
+}
+
+/* Commit a metadir update and unlock/drop all resources. */
+int
+xfs_imeta_commit_update(
+       struct xfs_imeta_update         *upd)
+{
+       int                             error;
+
+       trace_xfs_imeta_update_commit(upd);
+
+       error = xfs_trans_commit(upd->tp);
+       upd->tp = NULL;
+
+       xfs_imeta_teardown(upd, error);
+       return error;
+}
+
+/* Cancel a metadir update and unlock/drop all resources. */
+void
+xfs_imeta_cancel_update(
+       struct xfs_imeta_update         *upd,
+       int                             error)
+{
+       trace_xfs_imeta_update_cancel(upd);
+
+       xfs_trans_cancel(upd->tp);
+       upd->tp = NULL;
+
+       xfs_imeta_teardown(upd, error);
+}
+
+/* Create a metadata for the last component of the path. */
+STATIC int
+xfs_imeta_mkdir(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path)
+{
+       struct xfs_imeta_update         upd;
+       struct xfs_inode                *ip = NULL;
+       int                             error;
+
+       if (xfs_is_shutdown(mp))
+               return -EIO;
+
+       /* Allocate a transaction to create the last directory. */
+       error = xfs_imeta_start_create(mp, path, &upd);
+       if (error)
+               return error;
+
+       /* Create the subdirectory and take our reference. */
+       error = xfs_imeta_create(&upd, S_IFDIR, &ip);
+       if (error)
+               goto out_cancel;
+
+       error = xfs_imeta_commit_update(&upd);
+
+       /*
+        * We don't pass the directory we just created to the caller, so finish
+        * setting up the inode, then release the dir and the dquots.
+        */
+       goto out_irele;
+
+out_cancel:
+       xfs_imeta_cancel_update(&upd, error);
+out_irele:
+       /* Have to finish setting up the inode to ensure it's deleted. */
+       if (ip) {
+               xfs_finish_inode_setup(ip);
+               xfs_irele(ip);
+       }
+       return error;
+}
+
+/*
+ * Make sure that every metadata directory path component exists and is a
+ * directory.
+ */
+int
+xfs_imeta_ensure_dirpath(
+       struct xfs_mount                *mp,
+       const struct xfs_imeta_path     *path)
+{
+       struct xfs_imeta_path           temp_path = {
+               .im_path                = path->im_path,
+               .im_depth               = 1,
+               .im_ftype               = XFS_DIR3_FT_DIR,
+       };
+       unsigned int                    i;
+       int                             error = 0;
+
+       if (!xfs_has_metadir(mp))
+               return 0;
+
+       for (i = 0; i < path->im_depth - 1; i++, temp_path.im_depth++) {
+               error = xfs_imeta_mkdir(mp, &temp_path);
+               if (error && error != -EEXIST)
+                       return error;
+       }
+
+       return 0;
+}
index 2d5572270717b7795d4403ea1669a3c32b60e852..feb4baced89972a78de4f7eb0d413d551fe1f47b 100644 (file)
@@ -112,4 +112,22 @@ int xfs_imeta_iget(struct xfs_trans *tp, xfs_ino_t ino, umode_t mode,
                struct xfs_inode **ipp);
 void xfs_imeta_irele(struct xfs_inode *ip);
 
+int xfs_imeta_start_create(struct xfs_mount *mp,
+               const struct xfs_imeta_path *path,
+               struct xfs_imeta_update *upd);
+
+int xfs_imeta_start_link(struct xfs_mount *mp,
+               const struct xfs_imeta_path *path,
+               struct xfs_inode *ip, struct xfs_imeta_update *upd);
+
+int xfs_imeta_start_unlink(struct xfs_mount *mp,
+               const struct xfs_imeta_path *path,
+               struct xfs_inode *ip, struct xfs_imeta_update *upd);
+
+int xfs_imeta_ensure_dirpath(struct xfs_mount *mp,
+               const struct xfs_imeta_path *path);
+
+int xfs_imeta_commit_update(struct xfs_imeta_update *upd);
+void xfs_imeta_cancel_update(struct xfs_imeta_update *upd, int error);
+
 #endif /* __XFS_IMETA_H__ */