Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  quota_repair.c   Sprache: C

 

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2018-2023 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <djwong@kernel.org>
 */

#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_defer.h"
#include "xfs_btree.h"
#include "xfs_bit.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_inode_fork.h"
#include "xfs_alloc.h"
#include "xfs_bmap.h"
#include "xfs_quota.h"
#include "xfs_qm.h"
#include "xfs_dquot.h"
#include "xfs_dquot_item.h"
#include "xfs_reflink.h"
#include "xfs_bmap_btree.h"
#include "xfs_trans_space.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/quota.h"
#include "scrub/trace.h"
#include "scrub/repair.h"

/*
 * Quota Repair
 * ============
 *
 * Quota repairs are fairly simplistic; we fix everything that the dquot
 * verifiers complain about, cap any counters or limits that make no sense,
 * and schedule a quotacheck if we had to fix anything.  We also repair any
 * data fork extent records that don't apply to metadata files.
 */


struct xrep_quota_info {
 struct xfs_scrub *sc;
 bool   need_quotacheck;
};

/*
 * Allocate a new block into a sparse hole in the quota file backing this
 * dquot, initialize the block, and commit the whole mess.
 */

STATIC int
xrep_quota_item_fill_bmap_hole(
 struct xfs_scrub *sc,
 struct xfs_dquot *dq,
 struct xfs_bmbt_irec *irec)
{
 struct xfs_buf  *bp;
 struct xfs_mount *mp = sc->mp;
 int   nmaps = 1;
 int   error;

 xfs_trans_ijoin(sc->tp, sc->ip, 0);

 /* Map a block into the file. */
 error = xfs_trans_reserve_more(sc->tp, XFS_QM_DQALLOC_SPACE_RES(mp),
   0);
 if (error)
  return error;

 error = xfs_bmapi_write(sc->tp, sc->ip, dq->q_fileoffset,
   XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA, 0,
   irec, &nmaps);
 if (error)
  return error;

 dq->q_blkno = XFS_FSB_TO_DADDR(mp, irec->br_startblock);

 trace_xrep_dquot_item_fill_bmap_hole(sc->mp, dq->q_type, dq->q_id);

 /* Initialize the new block. */
 error = xfs_trans_get_buf(sc->tp, mp->m_ddev_targp, dq->q_blkno,
   mp->m_quotainfo->qi_dqchunklen, 0, &bp);
 if (error)
  return error;
 bp->b_ops = &xfs_dquot_buf_ops;

 xfs_qm_init_dquot_blk(sc->tp, dq->q_id, dq->q_type, bp);
 xfs_buf_set_ref(bp, XFS_DQUOT_REF);

 /*
  * Finish the mapping transactions and roll one more time to
  * disconnect sc->ip from sc->tp.
 */

 error = xrep_defer_finish(sc);
 if (error)
  return error;
 return xfs_trans_roll(&sc->tp);
}

/* Make sure there's a written block backing this dquot */
STATIC int
xrep_quota_item_bmap(
 struct xfs_scrub *sc,
 struct xfs_dquot *dq,
 bool   *dirty)
{
 struct xfs_bmbt_irec irec;
 struct xfs_mount *mp = sc->mp;
 struct xfs_quotainfo *qi = mp->m_quotainfo;
 xfs_fileoff_t  offset = dq->q_id / qi->qi_dqperchunk;
 int   nmaps = 1;
 int   error;

 /* The computed file offset should always be valid. */
 if (!xfs_verify_fileoff(mp, offset)) {
  ASSERT(xfs_verify_fileoff(mp, offset));
  return -EFSCORRUPTED;
 }
 dq->q_fileoffset = offset;

 error = xfs_bmapi_read(sc->ip, offset, 1, &irec, &nmaps, 0);
 if (error)
  return error;

 if (nmaps < 1 || !xfs_bmap_is_real_extent(&irec)) {
  /* Hole/delalloc extent; allocate a real block. */
  error = xrep_quota_item_fill_bmap_hole(sc, dq, &irec);
  if (error)
   return error;
 } else if (irec.br_state != XFS_EXT_NORM) {
  /* Unwritten extent, which we already took care of? */
  ASSERT(irec.br_state == XFS_EXT_NORM);
  return -EFSCORRUPTED;
 } else if (dq->q_blkno != XFS_FSB_TO_DADDR(mp, irec.br_startblock)) {
  /*
   * If the cached daddr is incorrect, repair probably punched a
   * hole out of the quota file and filled it back in with a new
   * block.  Update the block mapping in the dquot.
 */

  dq->q_blkno = XFS_FSB_TO_DADDR(mp, irec.br_startblock);
 }

 *dirty = true;
 return 0;
}

/* Reset quota timers if incorrectly set. */
static inline void
xrep_quota_item_timer(
 struct xfs_scrub  *sc,
 const struct xfs_dquot_res *res,
 bool    *dirty)
{
 if ((res->softlimit && res->count > res->softlimit) ||
     (res->hardlimit && res->count > res->hardlimit)) {
  if (!res->timer)
   *dirty = true;
 } else {
  if (res->timer)
   *dirty = true;
 }
}

/* Scrub the fields in an individual quota item. */
STATIC int
xrep_quota_item(
 struct xrep_quota_info *rqi,
 struct xfs_dquot *dq)
{
 struct xfs_scrub *sc = rqi->sc;
 struct xfs_mount *mp = sc->mp;
 xfs_ino_t  fs_icount;
 bool   dirty = false;
 int   error = 0;

 /* Last chance to abort before we start committing fixes. */
 if (xchk_should_terminate(sc, &error))
  return error;

 /*
  * We might need to fix holes in the bmap record for the storage
  * backing this dquot, so we need to lock the dquot and the quota file.
  * dqiterate gave us a locked dquot, so drop the dquot lock to get the
  * ILOCK_EXCL.
 */

 xfs_dqunlock(dq);
 xchk_ilock(sc, XFS_ILOCK_EXCL);
 xfs_dqlock(dq);

 error = xrep_quota_item_bmap(sc, dq, &dirty);
 xchk_iunlock(sc, XFS_ILOCK_EXCL);
 if (error)
  return error;

 /* Check the limits. */
 if (dq->q_blk.softlimit > dq->q_blk.hardlimit) {
  dq->q_blk.softlimit = dq->q_blk.hardlimit;
  dirty = true;
 }

 if (dq->q_ino.softlimit > dq->q_ino.hardlimit) {
  dq->q_ino.softlimit = dq->q_ino.hardlimit;
  dirty = true;
 }

 if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit) {
  dq->q_rtb.softlimit = dq->q_rtb.hardlimit;
  dirty = true;
 }

 /*
  * Check that usage doesn't exceed physical limits.  However, on
  * a reflink filesystem we're allowed to exceed physical space
  * if there are no quota limits.  We don't know what the real number
  * is, but we can make quotacheck find out for us.
 */

 if (!xfs_has_reflink(mp) && dq->q_blk.count > mp->m_sb.sb_dblocks) {
  dq->q_blk.reserved -= dq->q_blk.count;
  dq->q_blk.reserved += mp->m_sb.sb_dblocks;
  dq->q_blk.count = mp->m_sb.sb_dblocks;
  rqi->need_quotacheck = true;
  dirty = true;
 }
 fs_icount = percpu_counter_sum(&mp->m_icount);
 if (dq->q_ino.count > fs_icount) {
  dq->q_ino.reserved -= dq->q_ino.count;
  dq->q_ino.reserved += fs_icount;
  dq->q_ino.count = fs_icount;
  rqi->need_quotacheck = true;
  dirty = true;
 }
 if (!xfs_has_reflink(mp) && dq->q_rtb.count > mp->m_sb.sb_rblocks) {
  dq->q_rtb.reserved -= dq->q_rtb.count;
  dq->q_rtb.reserved += mp->m_sb.sb_rblocks;
  dq->q_rtb.count = mp->m_sb.sb_rblocks;
  rqi->need_quotacheck = true;
  dirty = true;
 }

 xrep_quota_item_timer(sc, &dq->q_blk, &dirty);
 xrep_quota_item_timer(sc, &dq->q_ino, &dirty);
 xrep_quota_item_timer(sc, &dq->q_rtb, &dirty);

 if (!dirty)
  return 0;

 trace_xrep_dquot_item(sc->mp, dq->q_type, dq->q_id);

 dq->q_flags |= XFS_DQFLAG_DIRTY;
 xfs_trans_dqjoin(sc->tp, dq);
 if (dq->q_id) {
  xfs_qm_adjust_dqlimits(dq);
  xfs_qm_adjust_dqtimers(dq);
 }
 xfs_trans_log_dquot(sc->tp, dq);
 error = xfs_trans_roll(&sc->tp);
 xfs_dqlock(dq);
 return error;
}

/* Fix a quota timer so that we can pass the verifier. */
STATIC void
xrep_quota_fix_timer(
 struct xfs_mount *mp,
 const struct xfs_disk_dquot *ddq,
 __be64   softlimit,
 __be64   countnow,
 __be32   *timer,
 time64_t  timelimit)
{
 uint64_t  soft = be64_to_cpu(softlimit);
 uint64_t  count = be64_to_cpu(countnow);
 time64_t  new_timer;
 uint32_t  t;

 if (!soft || count <= soft || *timer != 0)
  return;

 new_timer = xfs_dquot_set_timeout(mp,
    ktime_get_real_seconds() + timelimit);
 if (ddq->d_type & XFS_DQTYPE_BIGTIME)
  t = xfs_dq_unix_to_bigtime(new_timer);
 else
  t = new_timer;

 *timer = cpu_to_be32(t);
}

/* Fix anything the verifiers complain about. */
STATIC int
xrep_quota_block(
 struct xfs_scrub *sc,
 xfs_daddr_t  daddr,
 xfs_dqtype_t  dqtype,
 xfs_dqid_t  id)
{
 struct xfs_dqblk *dqblk;
 struct xfs_disk_dquot *ddq;
 struct xfs_quotainfo *qi = sc->mp->m_quotainfo;
 struct xfs_def_quota *defq = xfs_get_defquota(qi, dqtype);
 struct xfs_buf  *bp = NULL;
 enum xfs_blft  buftype = 0;
 int   i;
 int   error;

 error = xfs_trans_read_buf(sc->mp, sc->tp, sc->mp->m_ddev_targp, daddr,
   qi->qi_dqchunklen, 0, &bp, &xfs_dquot_buf_ops);
 switch (error) {
 case -EFSBADCRC:
 case -EFSCORRUPTED:
  /* Failed verifier, retry read with no ops. */
  error = xfs_trans_read_buf(sc->mp, sc->tp,
    sc->mp->m_ddev_targp, daddr, qi->qi_dqchunklen,
    0, &bp, NULL);
  if (error)
   return error;
  break;
 case 0:
  dqblk = bp->b_addr;
  ddq = &dqblk[0].dd_diskdq;

  /*
   * If there's nothing that would impede a dqiterate, we're
   * done.
 */

  if ((ddq->d_type & XFS_DQTYPE_REC_MASK) != dqtype ||
      id == be32_to_cpu(ddq->d_id)) {
   xfs_trans_brelse(sc->tp, bp);
   return 0;
  }
  break;
 default:
  return error;
 }

 /* Something's wrong with the block, fix the whole thing. */
 dqblk = bp->b_addr;
 bp->b_ops = &xfs_dquot_buf_ops;
 for (i = 0; i < qi->qi_dqperchunk; i++, dqblk++) {
  ddq = &dqblk->dd_diskdq;

  trace_xrep_disk_dquot(sc->mp, dqtype, id + i);

  ddq->d_magic = cpu_to_be16(XFS_DQUOT_MAGIC);
  ddq->d_version = XFS_DQUOT_VERSION;
  ddq->d_type = dqtype;
  ddq->d_id = cpu_to_be32(id + i);

  if (xfs_has_bigtime(sc->mp) && ddq->d_id)
   ddq->d_type |= XFS_DQTYPE_BIGTIME;

  xrep_quota_fix_timer(sc->mp, ddq, ddq->d_blk_softlimit,
    ddq->d_bcount, &ddq->d_btimer,
    defq->blk.time);

  xrep_quota_fix_timer(sc->mp, ddq, ddq->d_ino_softlimit,
    ddq->d_icount, &ddq->d_itimer,
    defq->ino.time);

  xrep_quota_fix_timer(sc->mp, ddq, ddq->d_rtb_softlimit,
    ddq->d_rtbcount, &ddq->d_rtbtimer,
    defq->rtb.time);

  /* We only support v5 filesystems so always set these. */
  uuid_copy(&dqblk->dd_uuid, &sc->mp->m_sb.sb_meta_uuid);
  xfs_update_cksum((char *)dqblk, sizeof(struct xfs_dqblk),
     XFS_DQUOT_CRC_OFF);
  dqblk->dd_lsn = 0;
 }
 switch (dqtype) {
 case XFS_DQTYPE_USER:
  buftype = XFS_BLFT_UDQUOT_BUF;
  break;
 case XFS_DQTYPE_GROUP:
  buftype = XFS_BLFT_GDQUOT_BUF;
  break;
 case XFS_DQTYPE_PROJ:
  buftype = XFS_BLFT_PDQUOT_BUF;
  break;
 }
 xfs_trans_buf_set_type(sc->tp, bp, buftype);
 xfs_trans_log_buf(sc->tp, bp, 0, BBTOB(bp->b_length) - 1);
 return xrep_roll_trans(sc);
}

/*
 * Repair a quota file's data fork.  The function returns with the inode
 * joined.
 */

STATIC int
xrep_quota_data_fork(
 struct xfs_scrub *sc,
 xfs_dqtype_t  dqtype)
{
 struct xfs_bmbt_irec irec = { 0 };
 struct xfs_iext_cursor icur;
 struct xfs_quotainfo *qi = sc->mp->m_quotainfo;
 struct xfs_ifork *ifp;
 xfs_fileoff_t  max_dqid_off;
 xfs_fileoff_t  off;
 xfs_fsblock_t  fsbno;
 bool   truncate = false;
 bool   joined = false;
 int   error = 0;

 error = xrep_metadata_inode_forks(sc);
 if (error)
  goto out;

 /* Check for data fork problems that apply only to quota files. */
 max_dqid_off = XFS_DQ_ID_MAX / qi->qi_dqperchunk;
 ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
 for_each_xfs_iext(ifp, &icur, &irec) {
  if (isnullstartblock(irec.br_startblock)) {
   error = -EFSCORRUPTED;
   goto out;
  }

  if (irec.br_startoff > max_dqid_off ||
      irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
   truncate = true;
   break;
  }

  /* Convert unwritten extents to real ones. */
  if (irec.br_state == XFS_EXT_UNWRITTEN) {
   struct xfs_bmbt_irec nrec;
   int   nmap = 1;

   if (!joined) {
    xfs_trans_ijoin(sc->tp, sc->ip, 0);
    joined = true;
   }

   error = xfs_bmapi_write(sc->tp, sc->ip,
     irec.br_startoff, irec.br_blockcount,
     XFS_BMAPI_CONVERT, 0, &nrec, &nmap);
   if (error)
    goto out;
   ASSERT(nrec.br_startoff == irec.br_startoff);
   ASSERT(nrec.br_blockcount == irec.br_blockcount);

   error = xfs_defer_finish(&sc->tp);
   if (error)
    goto out;
  }
 }

 if (!joined) {
  xfs_trans_ijoin(sc->tp, sc->ip, 0);
  joined = true;
 }

 if (truncate) {
  /* Erase everything after the block containing the max dquot */
  error = xfs_bunmapi_range(&sc->tp, sc->ip, 0,
    max_dqid_off * sc->mp->m_sb.sb_blocksize,
    XFS_MAX_FILEOFF);
  if (error)
   goto out;

  /* Remove all CoW reservations. */
  error = xfs_reflink_cancel_cow_blocks(sc->ip, &sc->tp, 0,
    XFS_MAX_FILEOFF, true);
  if (error)
   goto out;
  sc->ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;

  /*
   * Always re-log the inode so that our permanent transaction
   * can keep on rolling it forward in the log.
 */

  xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
 }

 /* Now go fix anything that fails the verifiers. */
 for_each_xfs_iext(ifp, &icur, &irec) {
  for (fsbno = irec.br_startblock, off = irec.br_startoff;
       fsbno < irec.br_startblock + irec.br_blockcount;
       fsbno += XFS_DQUOT_CLUSTER_SIZE_FSB,
    off += XFS_DQUOT_CLUSTER_SIZE_FSB) {
   error = xrep_quota_block(sc,
     XFS_FSB_TO_DADDR(sc->mp, fsbno),
     dqtype, off * qi->qi_dqperchunk);
   if (error)
    goto out;
  }
 }

out:
 return error;
}

/*
 * Go fix anything in the quota items that we could have been mad about.  Now
 * that we've checked the quota inode data fork we have to drop ILOCK_EXCL to
 * use the regular dquot functions.
 */

STATIC int
xrep_quota_problems(
 struct xfs_scrub *sc,
 xfs_dqtype_t  dqtype)
{
 struct xchk_dqiter cursor = { };
 struct xrep_quota_info rqi = { .sc = sc };
 struct xfs_dquot *dq;
 int   error;

 xchk_dqiter_init(&cursor, sc, dqtype);
 while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) {
  error = xrep_quota_item(&rqi, dq);
  xfs_qm_dqput(dq);
  if (error)
   break;
 }
 if (error)
  return error;

 /* Make a quotacheck happen. */
 if (rqi.need_quotacheck)
  xrep_force_quotacheck(sc, dqtype);
 return 0;
}

/* Repair all of a quota type's items. */
int
xrep_quota(
 struct xfs_scrub *sc)
{
 xfs_dqtype_t  dqtype;
 int   error;

 dqtype = xchk_quota_to_dqtype(sc);

 /*
  * Re-take the ILOCK so that we can fix any problems that we found
  * with the data fork mappings, or with the dquot bufs themselves.
 */

 if (!(sc->ilock_flags & XFS_ILOCK_EXCL))
  xchk_ilock(sc, XFS_ILOCK_EXCL);
 error = xrep_quota_data_fork(sc, dqtype);
 if (error)
  return error;

 /*
  * Finish deferred items and roll the transaction to unjoin the quota
  * inode from transaction so that we can unlock the quota inode; we
  * play only with dquots from now on.
 */

 error = xrep_defer_finish(sc);
 if (error)
  return error;
 error = xfs_trans_roll(&sc->tp);
 if (error)
  return error;
 xchk_iunlock(sc, sc->ilock_flags);

 /* Fix anything the dquot verifiers don't complain about. */
 error = xrep_quota_problems(sc, dqtype);
 if (error)
  return error;

 return xrep_trans_commit(sc);
}

Messung V0.5 in Prozent
C=96 H=96 G=95

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.13Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-12) ¤

*Bot Zugriff






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1127926
#Domains=2039723