]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/phase6.c
xfs: remove xfs_bmapi_write() dfops param
[thirdparty/xfsprogs-dev.git] / repair / phase6.c
index f3b837805c765161ef84beca911bfd21fec5d7e1..57d00989682752f71e57c9365f73ffa8a1b26c84 100644 (file)
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
  * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #include "libxfs.h"
@@ -38,6 +26,61 @@ static struct xfs_name               xfs_name_dot = {(unsigned char *)".",
                                                1,
                                                XFS_DIR3_FT_DIR};
 
+/*
+ * When we're checking directory inodes, we're allowed to set a directory's
+ * dotdot entry to zero to signal that the parent needs to be reconnected
+ * during phase 6.  If we're handling a shortform directory the ifork
+ * verifiers will fail, so temporarily patch out this canary so that we can
+ * verify the rest of the fork and move on to fixing the dir.
+ */
+static xfs_failaddr_t
+phase6_verify_dir(
+       struct xfs_inode                *ip)
+{
+       struct xfs_mount                *mp = ip->i_mount;
+       const struct xfs_dir_ops        *dops;
+       struct xfs_ifork                *ifp;
+       struct xfs_dir2_sf_hdr          *sfp;
+       xfs_failaddr_t                  fa;
+       xfs_ino_t                       old_parent;
+       bool                            parent_bypass = false;
+       int                             size;
+
+       dops = libxfs_dir_get_ops(mp, NULL);
+
+       ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data;
+       size = ifp->if_bytes;
+
+       /*
+        * If this is a shortform directory, phase4 may have set the parent
+        * inode to zero to indicate that it must be fixed.  Temporarily
+        * set a valid parent so that the directory verifier will pass.
+        */
+       if (size > offsetof(struct xfs_dir2_sf_hdr, parent) &&
+           size >= xfs_dir2_sf_hdr_size(sfp->i8count)) {
+               old_parent = dops->sf_get_parent_ino(sfp);
+               if (old_parent == 0) {
+                       dops->sf_put_parent_ino(sfp, mp->m_sb.sb_rootino);
+                       parent_bypass = true;
+               }
+       }
+
+       fa = libxfs_default_ifork_ops.verify_dir(ip);
+
+       /* Put it back. */
+       if (parent_bypass)
+               dops->sf_put_parent_ino(sfp, old_parent);
+
+       return fa;
+}
+
+static struct xfs_ifork_ops phase6_ifork_ops = {
+       .verify_attr    = xfs_attr_shortform_verify,
+       .verify_dir     = phase6_verify_dir,
+       .verify_symlink = xfs_symlink_shortform_verify,
+};
+
 /*
  * Data structures used to keep track of directories where the ".."
  * entries are updated. These must be rebuilt after the initial pass
@@ -485,12 +528,12 @@ mk_rbmino(xfs_mount_t *mp)
        xfs_bmbt_irec_t map[XFS_BMAP_MAX_NMAP];
        int             vers;
        int             times;
-       struct xfs_trans_res tres = {0};
+       uint            blocks;
 
        /*
         * first set up inode
         */
-       i = -libxfs_trans_alloc(mp, &tres, 10, 0, 0, &tp);
+       i = -libxfs_trans_alloc_rollable(mp, 10, &tp);
        if (i)
                res_failed(i);
 
@@ -538,21 +581,21 @@ mk_rbmino(xfs_mount_t *mp)
         * then allocate blocks for file and fill with zeroes (stolen
         * from mkfs)
         */
-       error = -libxfs_trans_alloc(mp, &tres,
-               mp->m_sb.sb_rbmblocks + (XFS_BM_MAXLEVELS(mp,XFS_DATA_FORK) - 1),
-                                  0, 0, &tp);
+       blocks = mp->m_sb.sb_rbmblocks +
+                       XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1;
+       error = -libxfs_trans_alloc_rollable(mp, blocks, &tp);
        if (error)
                res_failed(error);
 
        libxfs_trans_ijoin(tp, ip, 0);
        bno = 0;
        libxfs_defer_init(&dfops, &first);
+       tp->t_dfops = &dfops;
        while (bno < mp->m_sb.sb_rbmblocks) {
                nmap = XFS_BMAP_MAX_NMAP;
                error = -libxfs_bmapi_write(tp, ip, bno,
                          (xfs_extlen_t)(mp->m_sb.sb_rbmblocks - bno),
-                         0, &first, mp->m_sb.sb_rbmblocks,
-                         map, &nmap, &dfops);
+                         0, &first, mp->m_sb.sb_rbmblocks, map, &nmap);
                if (error) {
                        do_error(
                        _("couldn't allocate realtime bitmap, error = %d\n"),
@@ -588,12 +631,11 @@ fill_rbmino(xfs_mount_t *mp)
        int             error;
        xfs_fileoff_t   bno;
        xfs_bmbt_irec_t map;
-       struct xfs_trans_res tres = {0};
 
        bmp = btmcompute;
        bno = 0;
 
-       error = -libxfs_trans_alloc(mp, &tres, 10, 0, 0, &tp);
+       error = -libxfs_trans_alloc_rollable(mp, 10, &tp);
        if (error)
                res_failed(error);
 
@@ -611,7 +653,7 @@ fill_rbmino(xfs_mount_t *mp)
                 */
                nmap = 1;
                error = -libxfs_bmapi_write(tp, ip, bno, 1, 0,
-                                       &first, 1, &map, &nmap, NULL);
+                                       &first, 1, &map, &nmap);
                if (error || nmap != 1) {
                        do_error(
        _("couldn't map realtime bitmap block %" PRIu64 ", error = %d\n"),
@@ -632,7 +674,7 @@ _("can't access block %" PRIu64 " (fsbno %" PRIu64 ") of realtime bitmap inode %
                        return(1);
                }
 
-               memmove(XFS_BUF_PTR(bp), bmp, mp->m_sb.sb_blocksize);
+               memmove(bp->b_addr, bmp, mp->m_sb.sb_blocksize);
 
                libxfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
 
@@ -658,13 +700,12 @@ fill_rsumino(xfs_mount_t *mp)
        xfs_fileoff_t   bno;
        xfs_fileoff_t   end_bno;
        xfs_bmbt_irec_t map;
-       struct xfs_trans_res tres = {0};
 
        smp = sumcompute;
        bno = 0;
        end_bno = mp->m_rsumsize >> mp->m_sb.sb_blocklog;
 
-       error = -libxfs_trans_alloc(mp, &tres, 10, 0, 0, &tp);
+       error = -libxfs_trans_alloc_rollable(mp, 10, &tp);
        if (error)
                res_failed(error);
 
@@ -682,7 +723,7 @@ fill_rsumino(xfs_mount_t *mp)
                 */
                nmap = 1;
                error = -libxfs_bmapi_write(tp, ip, bno, 1, 0,
-                                       &first, 1, &map, &nmap, NULL);
+                                       &first, 1, &map, &nmap);
                if (error || nmap != 1) {
                        do_error(
        _("couldn't map realtime summary inode block %" PRIu64 ", error = %d\n"),
@@ -704,7 +745,7 @@ _("can't access block %" PRIu64 " (fsbno %" PRIu64 ") of realtime summary inode
                        return(1);
                }
 
-               memmove(XFS_BUF_PTR(bp), smp, mp->m_sb.sb_blocksize);
+               memmove(bp->b_addr, smp, mp->m_sb.sb_blocksize);
 
                libxfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
 
@@ -733,7 +774,7 @@ mk_rsumino(xfs_mount_t *mp)
        xfs_bmbt_irec_t map[XFS_BMAP_MAX_NMAP];
        int             vers;
        int             times;
-       struct xfs_trans_res tres = {0};
+       uint            blocks;
 
        /*
         * first set up inode
@@ -786,26 +827,21 @@ mk_rsumino(xfs_mount_t *mp)
         * then allocate blocks for file and fill with zeroes (stolen
         * from mkfs)
         */
-       libxfs_defer_init(&dfops, &first);
-
        nsumblocks = mp->m_rsumsize >> mp->m_sb.sb_blocklog;
-       tres.tr_logres = BBTOB(128);
-       tres.tr_logcount = XFS_DEFAULT_PERM_LOG_COUNT;
-       tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
-       error = -libxfs_trans_alloc(mp, &tres,
-               mp->m_sb.sb_rbmblocks + (XFS_BM_MAXLEVELS(mp,XFS_DATA_FORK) - 1),
-                                   0, 0, &tp);
+       blocks = nsumblocks + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1;
+       error = -libxfs_trans_alloc_rollable(mp, blocks, &tp);
        if (error)
                res_failed(error);
 
        libxfs_trans_ijoin(tp, ip, 0);
        bno = 0;
        libxfs_defer_init(&dfops, &first);
+       tp->t_dfops = &dfops;
        while (bno < nsumblocks) {
                nmap = XFS_BMAP_MAX_NMAP;
                error = -libxfs_bmapi_write(tp, ip, bno,
                          (xfs_extlen_t)(nsumblocks - bno),
-                         0, &first, nsumblocks, map, &nmap, &dfops);
+                         0, &first, nsumblocks, map, &nmap);
                if (error) {
                        do_error(
                _("couldn't allocate realtime summary inode, error = %d\n"),
@@ -927,7 +963,9 @@ mk_orphanage(xfs_mount_t *mp)
         * would have been cleared in phase3 and phase4.
         */
 
-       if ((i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip)))
+       i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip,
+                       &xfs_default_ifork_ops);
+       if (i)
                do_error(_("%d - couldn't iget root inode to obtain %s\n"),
                        i, ORPHANAGE);
 
@@ -951,7 +989,9 @@ mk_orphanage(xfs_mount_t *mp)
         * use iget/ijoin instead of trans_iget because the ialloc
         * wrapper can commit the transaction and start a new one
         */
-/*     if ((i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip)))
+/*     i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip,
+                       &xfs_default_ifork_ops);
+       if (i)
                do_error(_("%d - couldn't iget root inode to make %s\n"),
                        i, ORPHANAGE);*/
 
@@ -1002,7 +1042,7 @@ mk_orphanage(xfs_mount_t *mp)
         * create the actual entry
         */
        error = -libxfs_dir_createname(tp, pip, &xname, ip->i_ino, &first,
-                                       &dfops, nres);
+                                       nres);
        if (error)
                do_error(
                _("can't make %s, createname error %d\n"),
@@ -1066,7 +1106,8 @@ mv_orphanage(
        xname.len = snprintf((char *)fname, sizeof(fname), "%llu",
                                (unsigned long long)ino);
 
-       err = -libxfs_iget(mp, NULL, orphanage_ino, 0, &orphanage_ip);
+       err = -libxfs_iget(mp, NULL, orphanage_ino, 0, &orphanage_ip,
+                       &xfs_default_ifork_ops);
        if (err)
                do_error(_("%d - couldn't iget orphanage inode\n"), err);
        /*
@@ -1078,10 +1119,12 @@ mv_orphanage(
                xname.len = snprintf((char *)fname, sizeof(fname), "%llu.%d",
                                        (unsigned long long)ino, ++incr);
 
-       if ((err = -libxfs_iget(mp, NULL, ino, 0, &ino_p)))
+       /* Orphans may not have a proper parent, so use custom ops here */
+       err = -libxfs_iget(mp, NULL, ino, 0, &ino_p, &phase6_ifork_ops);
+       if (err)
                do_error(_("%d - couldn't iget disconnected inode\n"), err);
 
-       xname.type = xfs_mode_to_ftype(VFS_I(ino_p)->i_mode);
+       xname.type = libxfs_mode_to_ftype(VFS_I(ino_p)->i_mode);
 
        if (isa_dir)  {
                irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, orphanage_ino),
@@ -1108,7 +1151,7 @@ mv_orphanage(
 
                        libxfs_defer_init(&dfops, &first);
                        err = -libxfs_dir_createname(tp, orphanage_ip, &xname,
-                                               ino, &first, &dfops, nres);
+                                               ino, &first, nres);
                        if (err)
                                do_error(
        _("name create failed in %s (%d), filesystem may be out of space\n"),
@@ -1121,7 +1164,7 @@ mv_orphanage(
                        libxfs_trans_log_inode(tp, orphanage_ip, XFS_ILOG_CORE);
 
                        err = -libxfs_dir_createname(tp, ino_p, &xfs_name_dotdot,
-                                       orphanage_ino, &first, &dfops, nres);
+                                       orphanage_ino, &first, nres);
                        if (err)
                                do_error(
        _("creation of .. entry failed (%d), filesystem may be out of space\n"),
@@ -1152,7 +1195,7 @@ mv_orphanage(
                        libxfs_defer_init(&dfops, &first);
 
                        err = -libxfs_dir_createname(tp, orphanage_ip, &xname,
-                                               ino, &first, &dfops, nres);
+                                               ino, &first, nres);
                        if (err)
                                do_error(
        _("name create failed in %s (%d), filesystem may be out of space\n"),
@@ -1171,7 +1214,7 @@ mv_orphanage(
                        if (entry_ino_num != orphanage_ino)  {
                                err = -libxfs_dir_replace(tp, ino_p,
                                                &xfs_name_dotdot, orphanage_ino,
-                                               &first, &dfops, nres);
+                                               &first, nres);
                                if (err)
                                        do_error(
        _("name replace op failed (%d), filesystem may be out of space\n"),
@@ -1208,7 +1251,7 @@ mv_orphanage(
 
                libxfs_defer_init(&dfops, &first);
                err = -libxfs_dir_createname(tp, orphanage_ip, &xname, ino,
-                                               &first, &dfops, nres);
+                                               &first, nres);
                if (err)
                        do_error(
        _("name create failed in %s (%d), filesystem may be out of space\n"),
@@ -1249,6 +1292,48 @@ entry_junked(
        return !no_modify;
 }
 
+/* Find and invalidate all the directory's buffers. */
+static int
+dir_binval(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       int                     whichfork)
+{
+       struct xfs_iext_cursor  icur;
+       struct xfs_bmbt_irec    rec;
+       struct xfs_ifork        *ifp;
+       struct xfs_da_geometry  *geo;
+       struct xfs_buf          *bp;
+       xfs_dablk_t             dabno, end_dabno;
+       int                     error = 0;
+
+       if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
+           ip->i_d.di_format != XFS_DINODE_FMT_BTREE)
+               return 0;
+
+       geo = tp->t_mountp->m_dir_geo;
+       ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       for_each_xfs_iext(ifp, &icur, &rec) {
+               dabno = xfs_dir2_db_to_da(geo, rec.br_startoff +
+                               geo->fsbcount - 1);
+               end_dabno = xfs_dir2_db_to_da(geo, rec.br_startoff +
+                               rec.br_blockcount);
+               for (; dabno <= end_dabno; dabno += geo->fsbcount) {
+                       bp = NULL;
+                       error = -libxfs_da_get_buf(tp, ip, dabno, -2, &bp,
+                                       whichfork);
+                       if (error)
+                               return error;
+                       if (!bp)
+                               continue;
+                       libxfs_trans_binval(tp, bp);
+                       libxfs_trans_brelse(tp, bp);
+               }
+       }
+
+       return error;
+}
+
 /*
  * Unexpected failure during the rebuild will leave the entries in
  * lost+found on the next run
@@ -1299,6 +1384,10 @@ longform_dir2_rebuild(
                res_failed(error);
        libxfs_trans_ijoin(tp, ip, 0);
 
+       error = dir_binval(tp, ip, XFS_DATA_FORK);
+       if (error)
+               res_failed(error);
+
        if ((error = -libxfs_bmap_last_offset(ip, &lastblock, XFS_DATA_FORK)))
                do_error(_("xfs_bmap_last_offset failed -- error - %d\n"),
                        error);
@@ -1346,7 +1435,7 @@ longform_dir2_rebuild(
 
                libxfs_defer_init(&dfops, &firstblock);
                error = -libxfs_dir_createname(tp, ip, &p->name, p->inum,
-                                               &firstblock, &dfops, nres);
+                                               &firstblock, nres);
                if (error) {
                        do_warn(
 _("name create failed in ino %" PRIu64 " (%d), filesystem may be out of space\n"),
@@ -1404,7 +1493,6 @@ dir2_kill_block(
        args.dp = ip;
        args.trans = tp;
        args.firstblock = &firstblock;
-       args.dfops = &dfops;
        args.whichfork = XFS_DATA_FORK;
        args.geo = mp->m_dir_geo;
        if (da_bno >= mp->m_dir_geo->leafblk && da_bno < mp->m_dir_geo->freeblk)
@@ -1776,7 +1864,8 @@ longform_dir2_entry_check_data(
                 * of when directory is moved to orphanage.
                 */
                if (ip->i_ino == inum)  {
-                       ASSERT(dep->name[0] == '.' && dep->namelen == 1);
+                       ASSERT(no_modify ||
+                              (dep->name[0] == '.' && dep->namelen == 1));
                        add_inode_ref(current_irec, current_ino_offset);
                        if (da_bno != 0 ||
                            dep != M_DIROPS(mp)->data_entry_p(d)) {
@@ -2827,7 +2916,7 @@ process_dir_inode(
 
        ASSERT(!is_inode_refchecked(irec, ino_offset) || dotdot_update);
 
-       error = -libxfs_iget(mp, NULL, ino, 0, &ip);
+       error = -libxfs_iget(mp, NULL, ino, 0, &ip, &phase6_ifork_ops);
        if (error) {
                if (!no_modify)
                        do_error(
@@ -2949,7 +3038,7 @@ process_dir_inode(
                libxfs_defer_init(&dfops, &first);
 
                error = -libxfs_dir_createname(tp, ip, &xfs_name_dotdot,
-                                       ip->i_ino, &first, &dfops, nres);
+                                       ip->i_ino, &first, nres);
                if (error)
                        do_error(
        _("can't make \"..\" entry in root inode %" PRIu64 ", createname error %d\n"), ino, error);
@@ -3007,7 +3096,7 @@ process_dir_inode(
                        libxfs_defer_init(&dfops, &first);
 
                        error = -libxfs_dir_createname(tp, ip, &xfs_name_dot,
-                                       ip->i_ino, &first, &dfops, nres);
+                                       ip->i_ino, &first, nres);
                        if (error)
                                do_error(
        _("can't make \".\" entry in dir ino %" PRIu64 ", createname error %d\n"),
@@ -3125,7 +3214,7 @@ check_for_orphaned_inodes(
 
 static void
 traverse_function(
-       work_queue_t            *wq,
+       struct workqueue        *wq,
        xfs_agnumber_t          agno,
        void                    *arg)
 {
@@ -3154,7 +3243,7 @@ traverse_function(
 
                for (i = 0; i < XFS_INODES_PER_CHUNK; i++)  {
                        if (inode_isadir(irec, i))
-                               process_dir_inode(wq->mp, agno, irec, i);
+                               process_dir_inode(wq->wq_ctx, agno, irec, i);
                }
        }
        cleanup_inode_prefetch(pf_args);