blob: afac5c586cc0679545c6385c343bd140012aed68 [file] [log] [blame]
/*
* Copyright (c) 2000-2001,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>
#include "globals.h"
#include "agheader.h"
#include "protos.h"
#include "err_protos.h"
int
verify_set_agf(xfs_mount_t *mp, xfs_agf_t *agf, xfs_agnumber_t i)
{
xfs_drfsbno_t agblocks;
int retval = 0;
/* check common fields */
if (be32_to_cpu(agf->agf_magicnum) != XFS_AGF_MAGIC) {
retval = XR_AG_AGF;
do_warn(_("bad magic # 0x%x for agf %d\n"),
be32_to_cpu(agf->agf_magicnum), i);
if (!no_modify)
agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
}
if (!XFS_AGF_GOOD_VERSION(be32_to_cpu(agf->agf_versionnum))) {
retval = XR_AG_AGF;
do_warn(_("bad version # %d for agf %d\n"),
be32_to_cpu(agf->agf_versionnum), i);
if (!no_modify)
agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
}
if (be32_to_cpu(agf->agf_seqno) != i) {
retval = XR_AG_AGF;
do_warn(_("bad sequence # %d for agf %d\n"),
be32_to_cpu(agf->agf_seqno), i);
if (!no_modify)
agf->agf_seqno = cpu_to_be32(i);
}
if (be32_to_cpu(agf->agf_length) != mp->m_sb.sb_agblocks) {
if (i != mp->m_sb.sb_agcount - 1) {
retval = XR_AG_AGF;
do_warn(_("bad length %d for agf %d, should be %d\n"),
be32_to_cpu(agf->agf_length), i,
mp->m_sb.sb_agblocks);
if (!no_modify)
agf->agf_length =
cpu_to_be32(mp->m_sb.sb_agblocks);
} else {
agblocks = mp->m_sb.sb_dblocks -
(xfs_drfsbno_t) mp->m_sb.sb_agblocks * i;
if (be32_to_cpu(agf->agf_length) != agblocks) {
retval = XR_AG_AGF;
do_warn(
_("bad length %d for agf %d, should be %llu\n"),
be32_to_cpu(agf->agf_length),
i, agblocks);
if (!no_modify)
agf->agf_length = cpu_to_be32(agblocks);
}
}
}
/*
* check first/last AGF fields. if need be, lose the free
* space in the AGFL, we'll reclaim it later.
*/
if (be32_to_cpu(agf->agf_flfirst) >= XFS_AGFL_SIZE(mp)) {
do_warn(_("flfirst %d in agf %d too large (max = %d)\n"),
be32_to_cpu(agf->agf_flfirst), i, XFS_AGFL_SIZE(mp));
if (!no_modify)
agf->agf_flfirst = cpu_to_be32(0);
}
if (be32_to_cpu(agf->agf_fllast) >= XFS_AGFL_SIZE(mp)) {
do_warn(_("fllast %d in agf %d too large (max = %d)\n"),
be32_to_cpu(agf->agf_fllast), i, XFS_AGFL_SIZE(mp));
if (!no_modify)
agf->agf_fllast = cpu_to_be32(0);
}
/* don't check freespace btrees -- will be checked by caller */
return(retval);
}
int
verify_set_agi(xfs_mount_t *mp, xfs_agi_t *agi, xfs_agnumber_t agno)
{
xfs_drfsbno_t agblocks;
int retval = 0;
/* check common fields */
if (be32_to_cpu(agi->agi_magicnum) != XFS_AGI_MAGIC) {
retval = XR_AG_AGI;
do_warn(_("bad magic # 0x%x for agi %d\n"),
be32_to_cpu(agi->agi_magicnum), agno);
if (!no_modify)
agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
}
if (!XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum))) {
retval = XR_AG_AGI;
do_warn(_("bad version # %d for agi %d\n"),
be32_to_cpu(agi->agi_versionnum), agno);
if (!no_modify)
agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
}
if (be32_to_cpu(agi->agi_seqno) != agno) {
retval = XR_AG_AGI;
do_warn(_("bad sequence # %d for agi %d\n"),
be32_to_cpu(agi->agi_seqno), agno);
if (!no_modify)
agi->agi_seqno = cpu_to_be32(agno);
}
if (be32_to_cpu(agi->agi_length) != mp->m_sb.sb_agblocks) {
if (agno != mp->m_sb.sb_agcount - 1) {
retval = XR_AG_AGI;
do_warn(_("bad length # %d for agi %d, should be %d\n"),
be32_to_cpu(agi->agi_length), agno,
mp->m_sb.sb_agblocks);
if (!no_modify)
agi->agi_length =
cpu_to_be32(mp->m_sb.sb_agblocks);
} else {
agblocks = mp->m_sb.sb_dblocks -
(xfs_drfsbno_t) mp->m_sb.sb_agblocks * agno;
if (be32_to_cpu(agi->agi_length) != agblocks) {
retval = XR_AG_AGI;
do_warn(
_("bad length # %d for agi %d, should be %llu\n"),
be32_to_cpu(agi->agi_length),
agno, agblocks);
if (!no_modify)
agi->agi_length = cpu_to_be32(agblocks);
}
}
}
/* don't check inode btree -- will be checked by caller */
return(retval);
}
/*
* superblock comparison - compare arbitrary superblock with
* filesystem mount-point superblock
*
* the verified fields include id and geometry.
* the inprogress fields, version numbers, and counters
* are allowed to differ as well as all fields after the
* counters to cope with the pre-6.5 mkfs non-zeroed
* secondary superblock sectors.
*/
int
compare_sb(xfs_mount_t *mp, xfs_sb_t *sb)
{
fs_geometry_t fs_geo, sb_geo;
get_sb_geometry(&fs_geo, &mp->m_sb);
get_sb_geometry(&sb_geo, sb);
if (memcmp(&fs_geo, &sb_geo,
(char *) &fs_geo.sb_shared_vn - (char *) &fs_geo))
return(XR_SB_GEO_MISMATCH);
return(XR_OK);
}
/*
* Possible fields that may have been set at mkfs time,
* sb_inoalignmt, sb_unit, sb_width and sb_dirblklog.
* The quota inode fields in the secondaries should be zero.
* Likewise, the sb_flags and sb_shared_vn should also be
* zero and the shared version bit should be cleared for
* current mkfs's.
*
* And everything else in the buffer beyond either sb_width,
* sb_dirblklog (v2 dirs), or sb_logsectsize can be zeroed.
*
* Note: contrary to the name, this routine is called for all
* superblocks, not just the secondary superblocks.
*/
int
secondary_sb_wack(xfs_mount_t *mp, xfs_buf_t *sbuf, xfs_sb_t *sb,
xfs_agnumber_t i)
{
int do_bzero;
int size;
char *ip;
int rval;
rval = do_bzero = 0;
/*
* mkfs's that stamped a feature bit besides the ones in the mask
* (e.g. were pre-6.5 beta) could leave garbage in the secondary
* superblock sectors. Anything stamping the shared fs bit or better
* into the secondaries is ok and should generate clean secondary
* superblock sectors. so only run the zero check on the
* potentially garbaged secondaries.
*/
if (pre_65_beta ||
(sb->sb_versionnum & XR_GOOD_SECSB_VNMASK) == 0 ||
sb->sb_versionnum < XFS_SB_VERSION_4) {
/*
* Check for garbage beyond the last field.
* Use field addresses instead so this code will still
* work against older filesystems when the superblock
* gets rev'ed again with new fields appended.
*/
if (xfs_sb_version_hasmorebits(sb))
size = (__psint_t)&sb->sb_features2
+ sizeof(sb->sb_features2) - (__psint_t)sb;
else if (xfs_sb_version_haslogv2(sb))
size = (__psint_t)&sb->sb_logsunit
+ sizeof(sb->sb_logsunit) - (__psint_t)sb;
else if (xfs_sb_version_hassector(sb))
size = (__psint_t)&sb->sb_logsectsize
+ sizeof(sb->sb_logsectsize) - (__psint_t)sb;
else if (xfs_sb_version_hasdirv2(sb))
size = (__psint_t)&sb->sb_dirblklog
+ sizeof(sb->sb_dirblklog) - (__psint_t)sb;
else
size = (__psint_t)&sb->sb_width
+ sizeof(sb->sb_width) - (__psint_t)sb;
for (ip = (char *)((__psint_t)sb + size);
ip < (char *)((__psint_t)sb + mp->m_sb.sb_sectsize);
ip++) {
if (*ip) {
do_bzero = 1;
break;
}
}
if (do_bzero) {
rval |= XR_AG_SB_SEC;
if (!no_modify) {
do_warn(
_("zeroing unused portion of %s superblock (AG #%u)\n"),
!i ? _("primary") : _("secondary"), i);
memset((void *)((__psint_t)sb + size), 0,
mp->m_sb.sb_sectsize - size);
} else
do_warn(
_("would zero unused portion of %s superblock (AG #%u)\n"),
!i ? _("primary") : _("secondary"), i);
}
}
/*
* now look for the fields we can manipulate directly.
* if we did a zero and that zero could have included
* the field in question, just silently reset it. otherwise,
* complain.
*
* for now, just zero the flags field since only
* the readonly flag is used
*/
if (sb->sb_flags) {
if (!no_modify)
sb->sb_flags = 0;
if (sb->sb_versionnum & XR_PART_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(_("bad flags field in superblock %d\n"), i);
} else
rval |= XR_AG_SB_SEC;
}
/*
* quota inodes and flags in secondary superblocks
* are never set by mkfs. However, they could be set
* in a secondary if a fs with quotas was growfs'ed since
* growfs copies the new primary into the secondaries.
*/
if (sb->sb_inprogress == 1 && sb->sb_uquotino) {
if (!no_modify)
sb->sb_uquotino = 0;
if (sb->sb_versionnum & XR_PART_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(
_("non-null user quota inode field in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
if (sb->sb_inprogress == 1 && sb->sb_gquotino) {
if (!no_modify)
sb->sb_gquotino = 0;
if (sb->sb_versionnum & XR_PART_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(
_("non-null group quota inode field in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
if (sb->sb_inprogress == 1 && sb->sb_qflags) {
if (!no_modify)
sb->sb_qflags = 0;
if (sb->sb_versionnum & XR_PART_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(_("non-null quota flags in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
/*
* if the secondaries agree on a stripe unit/width or inode
* alignment, those fields ought to be valid since they are
* written at mkfs time (and the corresponding sb version bits
* are set).
*/
if (!xfs_sb_version_hasshared(sb) && sb->sb_shared_vn != 0) {
if (!no_modify)
sb->sb_shared_vn = 0;
if (sb->sb_versionnum & XR_PART_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(
_("bad shared version number in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
if (!xfs_sb_version_hasalign(sb) && sb->sb_inoalignmt != 0) {
if (!no_modify)
sb->sb_inoalignmt = 0;
if (sb->sb_versionnum & XR_PART_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(
_("bad inode alignment field in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
if (!xfs_sb_version_hasdalign(sb) &&
(sb->sb_unit != 0 || sb->sb_width != 0)) {
if (!no_modify)
sb->sb_unit = sb->sb_width = 0;
if (sb->sb_versionnum & XR_GOOD_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(
_("bad stripe unit/width fields in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
if (!xfs_sb_version_hassector(sb) &&
(sb->sb_sectsize != BBSIZE || sb->sb_sectlog != BBSHIFT ||
sb->sb_logsectsize != 0 || sb->sb_logsectlog != 0)) {
if (!no_modify) {
sb->sb_sectsize = BBSIZE;
sb->sb_sectlog = BBSHIFT;
sb->sb_logsectsize = 0;
sb->sb_logsectlog = 0;
}
if (sb->sb_versionnum & XR_GOOD_SECSB_VNMASK || !do_bzero) {
rval |= XR_AG_SB;
do_warn(
_("bad log/data device sector size fields in superblock %d\n"),
i);
} else
rval |= XR_AG_SB_SEC;
}
return(rval);
}
/*
* verify and reset the ag header if required.
*
* lower 4 bits of rval are set depending on what got modified.
* (see agheader.h for more details)
*
* NOTE -- this routine does not tell the user that it has
* altered things. Rather, it is up to the caller to do so
* using the bits encoded into the return value.
*/
int
verify_set_agheader(xfs_mount_t *mp, xfs_buf_t *sbuf, xfs_sb_t *sb,
xfs_agf_t *agf, xfs_agi_t *agi, xfs_agnumber_t i)
{
int rval = 0;
int status = XR_OK;
int status_sb = XR_OK;
status = verify_sb(sb, (i == 0));
if (status != XR_OK) {
do_warn(_("bad on-disk superblock %d - %s\n"),
i, err_string(status));
}
status_sb = compare_sb(mp, sb);
if (status_sb != XR_OK) {
do_warn(_("primary/secondary superblock %d conflict - %s\n"),
i, err_string(status_sb));
}
if (status != XR_OK || status_sb != XR_OK) {
if (!no_modify) {
*sb = mp->m_sb;
/*
* clear the more transient fields
*/
sb->sb_inprogress = 1;
sb->sb_icount = 0;
sb->sb_ifree = 0;
sb->sb_fdblocks = 0;
sb->sb_frextents = 0;
sb->sb_qflags = 0;
}
rval |= XR_AG_SB;
}
rval |= secondary_sb_wack(mp, sbuf, sb, i);
rval |= verify_set_agf(mp, agf, i);
rval |= verify_set_agi(mp, agi, i);
return(rval);
}