libxfs: move source files
Move all the source files that are shared with userspace into libxfs/. This is done as one big chunk simpy to get it done quickly Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:

committed by
Dave Chinner

parent
84be0ffc90
commit
30f712c9dd
2630
fs/xfs/libxfs/xfs_alloc.c
Normal file
2630
fs/xfs/libxfs/xfs_alloc.c
Normal file
File diff suppressed because it is too large
Load Diff
504
fs/xfs/libxfs/xfs_alloc_btree.c
Normal file
504
fs/xfs/libxfs/xfs_alloc_btree.c
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
* 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_btree.h"
|
||||
#include "xfs_alloc_btree.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_extent_busy.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_trans.h"
|
||||
|
||||
|
||||
STATIC struct xfs_btree_cur *
|
||||
xfs_allocbt_dup_cursor(
|
||||
struct xfs_btree_cur *cur)
|
||||
{
|
||||
return xfs_allocbt_init_cursor(cur->bc_mp, cur->bc_tp,
|
||||
cur->bc_private.a.agbp, cur->bc_private.a.agno,
|
||||
cur->bc_btnum);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_allocbt_set_root(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *ptr,
|
||||
int inc)
|
||||
{
|
||||
struct xfs_buf *agbp = cur->bc_private.a.agbp;
|
||||
struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
|
||||
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
|
||||
int btnum = cur->bc_btnum;
|
||||
struct xfs_perag *pag = xfs_perag_get(cur->bc_mp, seqno);
|
||||
|
||||
ASSERT(ptr->s != 0);
|
||||
|
||||
agf->agf_roots[btnum] = ptr->s;
|
||||
be32_add_cpu(&agf->agf_levels[btnum], inc);
|
||||
pag->pagf_levels[btnum] += inc;
|
||||
xfs_perag_put(pag);
|
||||
|
||||
xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_ROOTS | XFS_AGF_LEVELS);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_allocbt_alloc_block(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *start,
|
||||
union xfs_btree_ptr *new,
|
||||
int *stat)
|
||||
{
|
||||
int error;
|
||||
xfs_agblock_t bno;
|
||||
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
|
||||
|
||||
/* Allocate the new block from the freelist. If we can't, give up. */
|
||||
error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_private.a.agbp,
|
||||
&bno, 1);
|
||||
if (error) {
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (bno == NULLAGBLOCK) {
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
|
||||
*stat = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xfs_extent_busy_reuse(cur->bc_mp, cur->bc_private.a.agno, bno, 1, false);
|
||||
|
||||
xfs_trans_agbtree_delta(cur->bc_tp, 1);
|
||||
new->s = cpu_to_be32(bno);
|
||||
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
|
||||
*stat = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_allocbt_free_block(
|
||||
struct xfs_btree_cur *cur,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_buf *agbp = cur->bc_private.a.agbp;
|
||||
struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
|
||||
xfs_agblock_t bno;
|
||||
int error;
|
||||
|
||||
bno = xfs_daddr_to_agbno(cur->bc_mp, XFS_BUF_ADDR(bp));
|
||||
error = xfs_alloc_put_freelist(cur->bc_tp, agbp, NULL, bno, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_extent_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1,
|
||||
XFS_EXTENT_BUSY_SKIP_DISCARD);
|
||||
xfs_trans_agbtree_delta(cur->bc_tp, -1);
|
||||
|
||||
xfs_trans_binval(cur->bc_tp, bp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the longest extent in the AGF
|
||||
*/
|
||||
STATIC void
|
||||
xfs_allocbt_update_lastrec(
|
||||
struct xfs_btree_cur *cur,
|
||||
struct xfs_btree_block *block,
|
||||
union xfs_btree_rec *rec,
|
||||
int ptr,
|
||||
int reason)
|
||||
{
|
||||
struct xfs_agf *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
|
||||
xfs_agnumber_t seqno = be32_to_cpu(agf->agf_seqno);
|
||||
struct xfs_perag *pag;
|
||||
__be32 len;
|
||||
int numrecs;
|
||||
|
||||
ASSERT(cur->bc_btnum == XFS_BTNUM_CNT);
|
||||
|
||||
switch (reason) {
|
||||
case LASTREC_UPDATE:
|
||||
/*
|
||||
* If this is the last leaf block and it's the last record,
|
||||
* then update the size of the longest extent in the AG.
|
||||
*/
|
||||
if (ptr != xfs_btree_get_numrecs(block))
|
||||
return;
|
||||
len = rec->alloc.ar_blockcount;
|
||||
break;
|
||||
case LASTREC_INSREC:
|
||||
if (be32_to_cpu(rec->alloc.ar_blockcount) <=
|
||||
be32_to_cpu(agf->agf_longest))
|
||||
return;
|
||||
len = rec->alloc.ar_blockcount;
|
||||
break;
|
||||
case LASTREC_DELREC:
|
||||
numrecs = xfs_btree_get_numrecs(block);
|
||||
if (ptr <= numrecs)
|
||||
return;
|
||||
ASSERT(ptr == numrecs + 1);
|
||||
|
||||
if (numrecs) {
|
||||
xfs_alloc_rec_t *rrp;
|
||||
|
||||
rrp = XFS_ALLOC_REC_ADDR(cur->bc_mp, block, numrecs);
|
||||
len = rrp->ar_blockcount;
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
agf->agf_longest = len;
|
||||
pag = xfs_perag_get(cur->bc_mp, seqno);
|
||||
pag->pagf_longest = be32_to_cpu(len);
|
||||
xfs_perag_put(pag);
|
||||
xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp, XFS_AGF_LONGEST);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_allocbt_get_minrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
return cur->bc_mp->m_alloc_mnr[level != 0];
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_allocbt_get_maxrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
return cur->bc_mp->m_alloc_mxr[level != 0];
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_allocbt_init_key_from_rec(
|
||||
union xfs_btree_key *key,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
ASSERT(rec->alloc.ar_startblock != 0);
|
||||
|
||||
key->alloc.ar_startblock = rec->alloc.ar_startblock;
|
||||
key->alloc.ar_blockcount = rec->alloc.ar_blockcount;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_allocbt_init_rec_from_key(
|
||||
union xfs_btree_key *key,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
ASSERT(key->alloc.ar_startblock != 0);
|
||||
|
||||
rec->alloc.ar_startblock = key->alloc.ar_startblock;
|
||||
rec->alloc.ar_blockcount = key->alloc.ar_blockcount;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_allocbt_init_rec_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
ASSERT(cur->bc_rec.a.ar_startblock != 0);
|
||||
|
||||
rec->alloc.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock);
|
||||
rec->alloc.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_allocbt_init_ptr_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *ptr)
|
||||
{
|
||||
struct xfs_agf *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
|
||||
|
||||
ASSERT(cur->bc_private.a.agno == be32_to_cpu(agf->agf_seqno));
|
||||
ASSERT(agf->agf_roots[cur->bc_btnum] != 0);
|
||||
|
||||
ptr->s = agf->agf_roots[cur->bc_btnum];
|
||||
}
|
||||
|
||||
STATIC __int64_t
|
||||
xfs_allocbt_key_diff(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_key *key)
|
||||
{
|
||||
xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
|
||||
xfs_alloc_key_t *kp = &key->alloc;
|
||||
__int64_t diff;
|
||||
|
||||
if (cur->bc_btnum == XFS_BTNUM_BNO) {
|
||||
return (__int64_t)be32_to_cpu(kp->ar_startblock) -
|
||||
rec->ar_startblock;
|
||||
}
|
||||
|
||||
diff = (__int64_t)be32_to_cpu(kp->ar_blockcount) - rec->ar_blockcount;
|
||||
if (diff)
|
||||
return diff;
|
||||
|
||||
return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
|
||||
}
|
||||
|
||||
static bool
|
||||
xfs_allocbt_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
|
||||
struct xfs_perag *pag = bp->b_pag;
|
||||
unsigned int level;
|
||||
|
||||
/*
|
||||
* magic number and level verification
|
||||
*
|
||||
* During growfs operations, we can't verify the exact level or owner as
|
||||
* the perag is not fully initialised and hence not attached to the
|
||||
* buffer. In this case, check against the maximum tree depth.
|
||||
*
|
||||
* Similarly, during log recovery we will have a perag structure
|
||||
* attached, but the agf information will not yet have been initialised
|
||||
* from the on disk AGF. Again, we can only check against maximum limits
|
||||
* in this case.
|
||||
*/
|
||||
level = be16_to_cpu(block->bb_level);
|
||||
switch (block->bb_magic) {
|
||||
case cpu_to_be32(XFS_ABTB_CRC_MAGIC):
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
|
||||
return false;
|
||||
if (pag &&
|
||||
be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
|
||||
return false;
|
||||
/* fall through */
|
||||
case cpu_to_be32(XFS_ABTB_MAGIC):
|
||||
if (pag && pag->pagf_init) {
|
||||
if (level >= pag->pagf_levels[XFS_BTNUM_BNOi])
|
||||
return false;
|
||||
} else if (level >= mp->m_ag_maxlevels)
|
||||
return false;
|
||||
break;
|
||||
case cpu_to_be32(XFS_ABTC_CRC_MAGIC):
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
|
||||
return false;
|
||||
if (pag &&
|
||||
be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
|
||||
return false;
|
||||
/* fall through */
|
||||
case cpu_to_be32(XFS_ABTC_MAGIC):
|
||||
if (pag && pag->pagf_init) {
|
||||
if (level >= pag->pagf_levels[XFS_BTNUM_CNTi])
|
||||
return false;
|
||||
} else if (level >= mp->m_ag_maxlevels)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* numrecs verification */
|
||||
if (be16_to_cpu(block->bb_numrecs) > mp->m_alloc_mxr[level != 0])
|
||||
return false;
|
||||
|
||||
/* sibling pointer verification */
|
||||
if (!block->bb_u.s.bb_leftsib ||
|
||||
(be32_to_cpu(block->bb_u.s.bb_leftsib) >= mp->m_sb.sb_agblocks &&
|
||||
block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK)))
|
||||
return false;
|
||||
if (!block->bb_u.s.bb_rightsib ||
|
||||
(be32_to_cpu(block->bb_u.s.bb_rightsib) >= mp->m_sb.sb_agblocks &&
|
||||
block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_allocbt_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
if (!xfs_btree_sblock_verify_crc(bp))
|
||||
xfs_buf_ioerror(bp, EFSBADCRC);
|
||||
else if (!xfs_allocbt_verify(bp))
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
|
||||
if (bp->b_error) {
|
||||
trace_xfs_btree_corrupt(bp, _RET_IP_);
|
||||
xfs_verifier_error(bp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_allocbt_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
if (!xfs_allocbt_verify(bp)) {
|
||||
trace_xfs_btree_corrupt(bp, _RET_IP_);
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
return;
|
||||
}
|
||||
xfs_btree_sblock_calc_crc(bp);
|
||||
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_allocbt_buf_ops = {
|
||||
.verify_read = xfs_allocbt_read_verify,
|
||||
.verify_write = xfs_allocbt_write_verify,
|
||||
};
|
||||
|
||||
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
STATIC int
|
||||
xfs_allocbt_keys_inorder(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_key *k1,
|
||||
union xfs_btree_key *k2)
|
||||
{
|
||||
if (cur->bc_btnum == XFS_BTNUM_BNO) {
|
||||
return be32_to_cpu(k1->alloc.ar_startblock) <
|
||||
be32_to_cpu(k2->alloc.ar_startblock);
|
||||
} else {
|
||||
return be32_to_cpu(k1->alloc.ar_blockcount) <
|
||||
be32_to_cpu(k2->alloc.ar_blockcount) ||
|
||||
(k1->alloc.ar_blockcount == k2->alloc.ar_blockcount &&
|
||||
be32_to_cpu(k1->alloc.ar_startblock) <
|
||||
be32_to_cpu(k2->alloc.ar_startblock));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_allocbt_recs_inorder(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec *r1,
|
||||
union xfs_btree_rec *r2)
|
||||
{
|
||||
if (cur->bc_btnum == XFS_BTNUM_BNO) {
|
||||
return be32_to_cpu(r1->alloc.ar_startblock) +
|
||||
be32_to_cpu(r1->alloc.ar_blockcount) <=
|
||||
be32_to_cpu(r2->alloc.ar_startblock);
|
||||
} else {
|
||||
return be32_to_cpu(r1->alloc.ar_blockcount) <
|
||||
be32_to_cpu(r2->alloc.ar_blockcount) ||
|
||||
(r1->alloc.ar_blockcount == r2->alloc.ar_blockcount &&
|
||||
be32_to_cpu(r1->alloc.ar_startblock) <
|
||||
be32_to_cpu(r2->alloc.ar_startblock));
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
static const struct xfs_btree_ops xfs_allocbt_ops = {
|
||||
.rec_len = sizeof(xfs_alloc_rec_t),
|
||||
.key_len = sizeof(xfs_alloc_key_t),
|
||||
|
||||
.dup_cursor = xfs_allocbt_dup_cursor,
|
||||
.set_root = xfs_allocbt_set_root,
|
||||
.alloc_block = xfs_allocbt_alloc_block,
|
||||
.free_block = xfs_allocbt_free_block,
|
||||
.update_lastrec = xfs_allocbt_update_lastrec,
|
||||
.get_minrecs = xfs_allocbt_get_minrecs,
|
||||
.get_maxrecs = xfs_allocbt_get_maxrecs,
|
||||
.init_key_from_rec = xfs_allocbt_init_key_from_rec,
|
||||
.init_rec_from_key = xfs_allocbt_init_rec_from_key,
|
||||
.init_rec_from_cur = xfs_allocbt_init_rec_from_cur,
|
||||
.init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur,
|
||||
.key_diff = xfs_allocbt_key_diff,
|
||||
.buf_ops = &xfs_allocbt_buf_ops,
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
.keys_inorder = xfs_allocbt_keys_inorder,
|
||||
.recs_inorder = xfs_allocbt_recs_inorder,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocate a new allocation btree cursor.
|
||||
*/
|
||||
struct xfs_btree_cur * /* new alloc btree cursor */
|
||||
xfs_allocbt_init_cursor(
|
||||
struct xfs_mount *mp, /* file system mount point */
|
||||
struct xfs_trans *tp, /* transaction pointer */
|
||||
struct xfs_buf *agbp, /* buffer for agf structure */
|
||||
xfs_agnumber_t agno, /* allocation group number */
|
||||
xfs_btnum_t btnum) /* btree identifier */
|
||||
{
|
||||
struct xfs_agf *agf = XFS_BUF_TO_AGF(agbp);
|
||||
struct xfs_btree_cur *cur;
|
||||
|
||||
ASSERT(btnum == XFS_BTNUM_BNO || btnum == XFS_BTNUM_CNT);
|
||||
|
||||
cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
|
||||
|
||||
cur->bc_tp = tp;
|
||||
cur->bc_mp = mp;
|
||||
cur->bc_btnum = btnum;
|
||||
cur->bc_blocklog = mp->m_sb.sb_blocklog;
|
||||
cur->bc_ops = &xfs_allocbt_ops;
|
||||
|
||||
if (btnum == XFS_BTNUM_CNT) {
|
||||
cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]);
|
||||
cur->bc_flags = XFS_BTREE_LASTREC_UPDATE;
|
||||
} else {
|
||||
cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]);
|
||||
}
|
||||
|
||||
cur->bc_private.a.agbp = agbp;
|
||||
cur->bc_private.a.agno = agno;
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate number of records in an alloc btree block.
|
||||
*/
|
||||
int
|
||||
xfs_allocbt_maxrecs(
|
||||
struct xfs_mount *mp,
|
||||
int blocklen,
|
||||
int leaf)
|
||||
{
|
||||
blocklen -= XFS_ALLOC_BLOCK_LEN(mp);
|
||||
|
||||
if (leaf)
|
||||
return blocklen / sizeof(xfs_alloc_rec_t);
|
||||
return blocklen / (sizeof(xfs_alloc_key_t) + sizeof(xfs_alloc_ptr_t));
|
||||
}
|
1459
fs/xfs/libxfs/xfs_attr.c
Normal file
1459
fs/xfs/libxfs/xfs_attr.c
Normal file
File diff suppressed because it is too large
Load Diff
2697
fs/xfs/libxfs/xfs_attr_leaf.c
Normal file
2697
fs/xfs/libxfs/xfs_attr_leaf.c
Normal file
File diff suppressed because it is too large
Load Diff
628
fs/xfs/libxfs/xfs_attr_remote.c
Normal file
628
fs/xfs/libxfs/xfs_attr_remote.c
Normal file
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
||||
* Copyright (c) 2013 Red Hat, 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_bit.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_da_format.h"
|
||||
#include "xfs_da_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_inode_item.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "xfs_bmap_util.h"
|
||||
#include "xfs_attr.h"
|
||||
#include "xfs_attr_leaf.h"
|
||||
#include "xfs_attr_remote.h"
|
||||
#include "xfs_trans_space.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_buf_item.h"
|
||||
#include "xfs_error.h"
|
||||
|
||||
#define ATTR_RMTVALUE_MAPSIZE 1 /* # of map entries at once */
|
||||
|
||||
/*
|
||||
* Each contiguous block has a header, so it is not just a simple attribute
|
||||
* length to FSB conversion.
|
||||
*/
|
||||
int
|
||||
xfs_attr3_rmt_blocks(
|
||||
struct xfs_mount *mp,
|
||||
int attrlen)
|
||||
{
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
|
||||
return (attrlen + buflen - 1) / buflen;
|
||||
}
|
||||
return XFS_B_TO_FSB(mp, attrlen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checking of the remote attribute header is split into two parts. The verifier
|
||||
* does CRC, location and bounds checking, the unpacking function checks the
|
||||
* attribute parameters and owner.
|
||||
*/
|
||||
static bool
|
||||
xfs_attr3_rmt_hdr_ok(
|
||||
void *ptr,
|
||||
xfs_ino_t ino,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
xfs_daddr_t bno)
|
||||
{
|
||||
struct xfs_attr3_rmt_hdr *rmt = ptr;
|
||||
|
||||
if (bno != be64_to_cpu(rmt->rm_blkno))
|
||||
return false;
|
||||
if (offset != be32_to_cpu(rmt->rm_offset))
|
||||
return false;
|
||||
if (size != be32_to_cpu(rmt->rm_bytes))
|
||||
return false;
|
||||
if (ino != be64_to_cpu(rmt->rm_owner))
|
||||
return false;
|
||||
|
||||
/* ok */
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
xfs_attr3_rmt_verify(
|
||||
struct xfs_mount *mp,
|
||||
void *ptr,
|
||||
int fsbsize,
|
||||
xfs_daddr_t bno)
|
||||
{
|
||||
struct xfs_attr3_rmt_hdr *rmt = ptr;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (rmt->rm_magic != cpu_to_be32(XFS_ATTR3_RMT_MAGIC))
|
||||
return false;
|
||||
if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
if (be64_to_cpu(rmt->rm_blkno) != bno)
|
||||
return false;
|
||||
if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
|
||||
return false;
|
||||
if (be32_to_cpu(rmt->rm_offset) +
|
||||
be32_to_cpu(rmt->rm_bytes) > XATTR_SIZE_MAX)
|
||||
return false;
|
||||
if (rmt->rm_owner == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_attr3_rmt_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
char *ptr;
|
||||
int len;
|
||||
xfs_daddr_t bno;
|
||||
int blksize = mp->m_attr_geo->blksize;
|
||||
|
||||
/* no verification of non-crc buffers */
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return;
|
||||
|
||||
ptr = bp->b_addr;
|
||||
bno = bp->b_bn;
|
||||
len = BBTOB(bp->b_length);
|
||||
ASSERT(len >= blksize);
|
||||
|
||||
while (len > 0) {
|
||||
if (!xfs_verify_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF)) {
|
||||
xfs_buf_ioerror(bp, EFSBADCRC);
|
||||
break;
|
||||
}
|
||||
if (!xfs_attr3_rmt_verify(mp, ptr, blksize, bno)) {
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
break;
|
||||
}
|
||||
len -= blksize;
|
||||
ptr += blksize;
|
||||
bno += BTOBB(blksize);
|
||||
}
|
||||
|
||||
if (bp->b_error)
|
||||
xfs_verifier_error(bp);
|
||||
else
|
||||
ASSERT(len == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_attr3_rmt_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
struct xfs_buf_log_item *bip = bp->b_fspriv;
|
||||
char *ptr;
|
||||
int len;
|
||||
xfs_daddr_t bno;
|
||||
int blksize = mp->m_attr_geo->blksize;
|
||||
|
||||
/* no verification of non-crc buffers */
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return;
|
||||
|
||||
ptr = bp->b_addr;
|
||||
bno = bp->b_bn;
|
||||
len = BBTOB(bp->b_length);
|
||||
ASSERT(len >= blksize);
|
||||
|
||||
while (len > 0) {
|
||||
if (!xfs_attr3_rmt_verify(mp, ptr, blksize, bno)) {
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
return;
|
||||
}
|
||||
if (bip) {
|
||||
struct xfs_attr3_rmt_hdr *rmt;
|
||||
|
||||
rmt = (struct xfs_attr3_rmt_hdr *)ptr;
|
||||
rmt->rm_lsn = cpu_to_be64(bip->bli_item.li_lsn);
|
||||
}
|
||||
xfs_update_cksum(ptr, blksize, XFS_ATTR3_RMT_CRC_OFF);
|
||||
|
||||
len -= blksize;
|
||||
ptr += blksize;
|
||||
bno += BTOBB(blksize);
|
||||
}
|
||||
ASSERT(len == 0);
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
|
||||
.verify_read = xfs_attr3_rmt_read_verify,
|
||||
.verify_write = xfs_attr3_rmt_write_verify,
|
||||
};
|
||||
|
||||
STATIC int
|
||||
xfs_attr3_rmt_hdr_set(
|
||||
struct xfs_mount *mp,
|
||||
void *ptr,
|
||||
xfs_ino_t ino,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
xfs_daddr_t bno)
|
||||
{
|
||||
struct xfs_attr3_rmt_hdr *rmt = ptr;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return 0;
|
||||
|
||||
rmt->rm_magic = cpu_to_be32(XFS_ATTR3_RMT_MAGIC);
|
||||
rmt->rm_offset = cpu_to_be32(offset);
|
||||
rmt->rm_bytes = cpu_to_be32(size);
|
||||
uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_uuid);
|
||||
rmt->rm_owner = cpu_to_be64(ino);
|
||||
rmt->rm_blkno = cpu_to_be64(bno);
|
||||
|
||||
return sizeof(struct xfs_attr3_rmt_hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper functions to copy attribute data in and out of the one disk extents
|
||||
*/
|
||||
STATIC int
|
||||
xfs_attr_rmtval_copyout(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_buf *bp,
|
||||
xfs_ino_t ino,
|
||||
int *offset,
|
||||
int *valuelen,
|
||||
__uint8_t **dst)
|
||||
{
|
||||
char *src = bp->b_addr;
|
||||
xfs_daddr_t bno = bp->b_bn;
|
||||
int len = BBTOB(bp->b_length);
|
||||
int blksize = mp->m_attr_geo->blksize;
|
||||
|
||||
ASSERT(len >= blksize);
|
||||
|
||||
while (len > 0 && *valuelen > 0) {
|
||||
int hdr_size = 0;
|
||||
int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
|
||||
|
||||
byte_cnt = min(*valuelen, byte_cnt);
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
if (!xfs_attr3_rmt_hdr_ok(src, ino, *offset,
|
||||
byte_cnt, bno)) {
|
||||
xfs_alert(mp,
|
||||
"remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
|
||||
bno, *offset, byte_cnt, ino);
|
||||
return EFSCORRUPTED;
|
||||
}
|
||||
hdr_size = sizeof(struct xfs_attr3_rmt_hdr);
|
||||
}
|
||||
|
||||
memcpy(*dst, src + hdr_size, byte_cnt);
|
||||
|
||||
/* roll buffer forwards */
|
||||
len -= blksize;
|
||||
src += blksize;
|
||||
bno += BTOBB(blksize);
|
||||
|
||||
/* roll attribute data forwards */
|
||||
*valuelen -= byte_cnt;
|
||||
*dst += byte_cnt;
|
||||
*offset += byte_cnt;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_attr_rmtval_copyin(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_buf *bp,
|
||||
xfs_ino_t ino,
|
||||
int *offset,
|
||||
int *valuelen,
|
||||
__uint8_t **src)
|
||||
{
|
||||
char *dst = bp->b_addr;
|
||||
xfs_daddr_t bno = bp->b_bn;
|
||||
int len = BBTOB(bp->b_length);
|
||||
int blksize = mp->m_attr_geo->blksize;
|
||||
|
||||
ASSERT(len >= blksize);
|
||||
|
||||
while (len > 0 && *valuelen > 0) {
|
||||
int hdr_size;
|
||||
int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, blksize);
|
||||
|
||||
byte_cnt = min(*valuelen, byte_cnt);
|
||||
hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
|
||||
byte_cnt, bno);
|
||||
|
||||
memcpy(dst + hdr_size, *src, byte_cnt);
|
||||
|
||||
/*
|
||||
* If this is the last block, zero the remainder of it.
|
||||
* Check that we are actually the last block, too.
|
||||
*/
|
||||
if (byte_cnt + hdr_size < blksize) {
|
||||
ASSERT(*valuelen - byte_cnt == 0);
|
||||
ASSERT(len == blksize);
|
||||
memset(dst + hdr_size + byte_cnt, 0,
|
||||
blksize - hdr_size - byte_cnt);
|
||||
}
|
||||
|
||||
/* roll buffer forwards */
|
||||
len -= blksize;
|
||||
dst += blksize;
|
||||
bno += BTOBB(blksize);
|
||||
|
||||
/* roll attribute data forwards */
|
||||
*valuelen -= byte_cnt;
|
||||
*src += byte_cnt;
|
||||
*offset += byte_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the value associated with an attribute from the out-of-line buffer
|
||||
* that we stored it in.
|
||||
*/
|
||||
int
|
||||
xfs_attr_rmtval_get(
|
||||
struct xfs_da_args *args)
|
||||
{
|
||||
struct xfs_bmbt_irec map[ATTR_RMTVALUE_MAPSIZE];
|
||||
struct xfs_mount *mp = args->dp->i_mount;
|
||||
struct xfs_buf *bp;
|
||||
xfs_dablk_t lblkno = args->rmtblkno;
|
||||
__uint8_t *dst = args->value;
|
||||
int valuelen;
|
||||
int nmap;
|
||||
int error;
|
||||
int blkcnt = args->rmtblkcnt;
|
||||
int i;
|
||||
int offset = 0;
|
||||
|
||||
trace_xfs_attr_rmtval_get(args);
|
||||
|
||||
ASSERT(!(args->flags & ATTR_KERNOVAL));
|
||||
ASSERT(args->rmtvaluelen == args->valuelen);
|
||||
|
||||
valuelen = args->rmtvaluelen;
|
||||
while (valuelen > 0) {
|
||||
nmap = ATTR_RMTVALUE_MAPSIZE;
|
||||
error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
|
||||
blkcnt, map, &nmap,
|
||||
XFS_BMAPI_ATTRFORK);
|
||||
if (error)
|
||||
return error;
|
||||
ASSERT(nmap >= 1);
|
||||
|
||||
for (i = 0; (i < nmap) && (valuelen > 0); i++) {
|
||||
xfs_daddr_t dblkno;
|
||||
int dblkcnt;
|
||||
|
||||
ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) &&
|
||||
(map[i].br_startblock != HOLESTARTBLOCK));
|
||||
dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
|
||||
dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
|
||||
error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
|
||||
dblkno, dblkcnt, 0, &bp,
|
||||
&xfs_attr3_rmt_buf_ops);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino,
|
||||
&offset, &valuelen,
|
||||
&dst);
|
||||
xfs_buf_relse(bp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* roll attribute extent map forwards */
|
||||
lblkno += map[i].br_blockcount;
|
||||
blkcnt -= map[i].br_blockcount;
|
||||
}
|
||||
}
|
||||
ASSERT(valuelen == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the value associated with an attribute into the out-of-line buffer
|
||||
* that we have defined for it.
|
||||
*/
|
||||
int
|
||||
xfs_attr_rmtval_set(
|
||||
struct xfs_da_args *args)
|
||||
{
|
||||
struct xfs_inode *dp = args->dp;
|
||||
struct xfs_mount *mp = dp->i_mount;
|
||||
struct xfs_bmbt_irec map;
|
||||
xfs_dablk_t lblkno;
|
||||
xfs_fileoff_t lfileoff = 0;
|
||||
__uint8_t *src = args->value;
|
||||
int blkcnt;
|
||||
int valuelen;
|
||||
int nmap;
|
||||
int error;
|
||||
int offset = 0;
|
||||
|
||||
trace_xfs_attr_rmtval_set(args);
|
||||
|
||||
/*
|
||||
* Find a "hole" in the attribute address space large enough for
|
||||
* us to drop the new attribute's value into. Because CRC enable
|
||||
* attributes have headers, we can't just do a straight byte to FSB
|
||||
* conversion and have to take the header space into account.
|
||||
*/
|
||||
blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
|
||||
error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
|
||||
XFS_ATTR_FORK);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff;
|
||||
args->rmtblkcnt = blkcnt;
|
||||
|
||||
/*
|
||||
* Roll through the "value", allocating blocks on disk as required.
|
||||
*/
|
||||
while (blkcnt > 0) {
|
||||
int committed;
|
||||
|
||||
/*
|
||||
* Allocate a single extent, up to the size of the value.
|
||||
*/
|
||||
xfs_bmap_init(args->flist, args->firstblock);
|
||||
nmap = 1;
|
||||
error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno,
|
||||
blkcnt,
|
||||
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
|
||||
args->firstblock, args->total, &map, &nmap,
|
||||
args->flist);
|
||||
if (!error) {
|
||||
error = xfs_bmap_finish(&args->trans, args->flist,
|
||||
&committed);
|
||||
}
|
||||
if (error) {
|
||||
ASSERT(committed);
|
||||
args->trans = NULL;
|
||||
xfs_bmap_cancel(args->flist);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* bmap_finish() may have committed the last trans and started
|
||||
* a new one. We need the inode to be in all transactions.
|
||||
*/
|
||||
if (committed)
|
||||
xfs_trans_ijoin(args->trans, dp, 0);
|
||||
|
||||
ASSERT(nmap == 1);
|
||||
ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
|
||||
(map.br_startblock != HOLESTARTBLOCK));
|
||||
lblkno += map.br_blockcount;
|
||||
blkcnt -= map.br_blockcount;
|
||||
|
||||
/*
|
||||
* Start the next trans in the chain.
|
||||
*/
|
||||
error = xfs_trans_roll(&args->trans, dp);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Roll through the "value", copying the attribute value to the
|
||||
* already-allocated blocks. Blocks are written synchronously
|
||||
* so that we can know they are all on disk before we turn off
|
||||
* the INCOMPLETE flag.
|
||||
*/
|
||||
lblkno = args->rmtblkno;
|
||||
blkcnt = args->rmtblkcnt;
|
||||
valuelen = args->rmtvaluelen;
|
||||
while (valuelen > 0) {
|
||||
struct xfs_buf *bp;
|
||||
xfs_daddr_t dblkno;
|
||||
int dblkcnt;
|
||||
|
||||
ASSERT(blkcnt > 0);
|
||||
|
||||
xfs_bmap_init(args->flist, args->firstblock);
|
||||
nmap = 1;
|
||||
error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno,
|
||||
blkcnt, &map, &nmap,
|
||||
XFS_BMAPI_ATTRFORK);
|
||||
if (error)
|
||||
return error;
|
||||
ASSERT(nmap == 1);
|
||||
ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
|
||||
(map.br_startblock != HOLESTARTBLOCK));
|
||||
|
||||
dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
|
||||
dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
|
||||
|
||||
bp = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, 0);
|
||||
if (!bp)
|
||||
return ENOMEM;
|
||||
bp->b_ops = &xfs_attr3_rmt_buf_ops;
|
||||
|
||||
xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset,
|
||||
&valuelen, &src);
|
||||
|
||||
error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */
|
||||
xfs_buf_relse(bp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
||||
/* roll attribute extent map forwards */
|
||||
lblkno += map.br_blockcount;
|
||||
blkcnt -= map.br_blockcount;
|
||||
}
|
||||
ASSERT(valuelen == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the value associated with an attribute by deleting the
|
||||
* out-of-line buffer that it is stored on.
|
||||
*/
|
||||
int
|
||||
xfs_attr_rmtval_remove(
|
||||
struct xfs_da_args *args)
|
||||
{
|
||||
struct xfs_mount *mp = args->dp->i_mount;
|
||||
xfs_dablk_t lblkno;
|
||||
int blkcnt;
|
||||
int error;
|
||||
int done;
|
||||
|
||||
trace_xfs_attr_rmtval_remove(args);
|
||||
|
||||
/*
|
||||
* Roll through the "value", invalidating the attribute value's blocks.
|
||||
*/
|
||||
lblkno = args->rmtblkno;
|
||||
blkcnt = args->rmtblkcnt;
|
||||
while (blkcnt > 0) {
|
||||
struct xfs_bmbt_irec map;
|
||||
struct xfs_buf *bp;
|
||||
xfs_daddr_t dblkno;
|
||||
int dblkcnt;
|
||||
int nmap;
|
||||
|
||||
/*
|
||||
* Try to remember where we decided to put the value.
|
||||
*/
|
||||
nmap = 1;
|
||||
error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
|
||||
blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK);
|
||||
if (error)
|
||||
return error;
|
||||
ASSERT(nmap == 1);
|
||||
ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
|
||||
(map.br_startblock != HOLESTARTBLOCK));
|
||||
|
||||
dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
|
||||
dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
|
||||
|
||||
/*
|
||||
* If the "remote" value is in the cache, remove it.
|
||||
*/
|
||||
bp = xfs_incore(mp->m_ddev_targp, dblkno, dblkcnt, XBF_TRYLOCK);
|
||||
if (bp) {
|
||||
xfs_buf_stale(bp);
|
||||
xfs_buf_relse(bp);
|
||||
bp = NULL;
|
||||
}
|
||||
|
||||
lblkno += map.br_blockcount;
|
||||
blkcnt -= map.br_blockcount;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep de-allocating extents until the remote-value region is gone.
|
||||
*/
|
||||
lblkno = args->rmtblkno;
|
||||
blkcnt = args->rmtblkcnt;
|
||||
done = 0;
|
||||
while (!done) {
|
||||
int committed;
|
||||
|
||||
xfs_bmap_init(args->flist, args->firstblock);
|
||||
error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
|
||||
XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
|
||||
1, args->firstblock, args->flist,
|
||||
&done);
|
||||
if (!error) {
|
||||
error = xfs_bmap_finish(&args->trans, args->flist,
|
||||
&committed);
|
||||
}
|
||||
if (error) {
|
||||
ASSERT(committed);
|
||||
args->trans = NULL;
|
||||
xfs_bmap_cancel(args->flist);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* bmap_finish() may have committed the last trans and started
|
||||
* a new one. We need the inode to be in all transactions.
|
||||
*/
|
||||
if (committed)
|
||||
xfs_trans_ijoin(args->trans, args->dp, 0);
|
||||
|
||||
/*
|
||||
* Close out trans and start the next one in the chain.
|
||||
*/
|
||||
error = xfs_trans_roll(&args->trans, args->dp);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
5609
fs/xfs/libxfs/xfs_bmap.c
Normal file
5609
fs/xfs/libxfs/xfs_bmap.c
Normal file
File diff suppressed because it is too large
Load Diff
967
fs/xfs/libxfs/xfs_bmap_btree.c
Normal file
967
fs/xfs/libxfs/xfs_bmap_btree.c
Normal file
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2003,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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_bit.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_inode_item.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_btree.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_dinode.h"
|
||||
|
||||
/*
|
||||
* Determine the extent state.
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
STATIC xfs_exntst_t
|
||||
xfs_extent_state(
|
||||
xfs_filblks_t blks,
|
||||
int extent_flag)
|
||||
{
|
||||
if (extent_flag) {
|
||||
ASSERT(blks != 0); /* saved for DMIG */
|
||||
return XFS_EXT_UNWRITTEN;
|
||||
}
|
||||
return XFS_EXT_NORM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert on-disk form of btree root to in-memory form.
|
||||
*/
|
||||
void
|
||||
xfs_bmdr_to_bmbt(
|
||||
struct xfs_inode *ip,
|
||||
xfs_bmdr_block_t *dblock,
|
||||
int dblocklen,
|
||||
struct xfs_btree_block *rblock,
|
||||
int rblocklen)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
int dmxr;
|
||||
xfs_bmbt_key_t *fkp;
|
||||
__be64 *fpp;
|
||||
xfs_bmbt_key_t *tkp;
|
||||
__be64 *tpp;
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
xfs_btree_init_block_int(mp, rblock, XFS_BUF_DADDR_NULL,
|
||||
XFS_BMAP_CRC_MAGIC, 0, 0, ip->i_ino,
|
||||
XFS_BTREE_LONG_PTRS | XFS_BTREE_CRC_BLOCKS);
|
||||
else
|
||||
xfs_btree_init_block_int(mp, rblock, XFS_BUF_DADDR_NULL,
|
||||
XFS_BMAP_MAGIC, 0, 0, ip->i_ino,
|
||||
XFS_BTREE_LONG_PTRS);
|
||||
|
||||
rblock->bb_level = dblock->bb_level;
|
||||
ASSERT(be16_to_cpu(rblock->bb_level) > 0);
|
||||
rblock->bb_numrecs = dblock->bb_numrecs;
|
||||
dmxr = xfs_bmdr_maxrecs(dblocklen, 0);
|
||||
fkp = XFS_BMDR_KEY_ADDR(dblock, 1);
|
||||
tkp = XFS_BMBT_KEY_ADDR(mp, rblock, 1);
|
||||
fpp = XFS_BMDR_PTR_ADDR(dblock, 1, dmxr);
|
||||
tpp = XFS_BMAP_BROOT_PTR_ADDR(mp, rblock, 1, rblocklen);
|
||||
dmxr = be16_to_cpu(dblock->bb_numrecs);
|
||||
memcpy(tkp, fkp, sizeof(*fkp) * dmxr);
|
||||
memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a compressed bmap extent record to an uncompressed form.
|
||||
* This code must be in sync with the routines xfs_bmbt_get_startoff,
|
||||
* xfs_bmbt_get_startblock, xfs_bmbt_get_blockcount and xfs_bmbt_get_state.
|
||||
*/
|
||||
STATIC void
|
||||
__xfs_bmbt_get_all(
|
||||
__uint64_t l0,
|
||||
__uint64_t l1,
|
||||
xfs_bmbt_irec_t *s)
|
||||
{
|
||||
int ext_flag;
|
||||
xfs_exntst_t st;
|
||||
|
||||
ext_flag = (int)(l0 >> (64 - BMBT_EXNTFLAG_BITLEN));
|
||||
s->br_startoff = ((xfs_fileoff_t)l0 &
|
||||
xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
|
||||
#if XFS_BIG_BLKNOS
|
||||
s->br_startblock = (((xfs_fsblock_t)l0 & xfs_mask64lo(9)) << 43) |
|
||||
(((xfs_fsblock_t)l1) >> 21);
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
{
|
||||
xfs_dfsbno_t b;
|
||||
|
||||
b = (((xfs_dfsbno_t)l0 & xfs_mask64lo(9)) << 43) |
|
||||
(((xfs_dfsbno_t)l1) >> 21);
|
||||
ASSERT((b >> 32) == 0 || isnulldstartblock(b));
|
||||
s->br_startblock = (xfs_fsblock_t)b;
|
||||
}
|
||||
#else /* !DEBUG */
|
||||
s->br_startblock = (xfs_fsblock_t)(((xfs_dfsbno_t)l1) >> 21);
|
||||
#endif /* DEBUG */
|
||||
#endif /* XFS_BIG_BLKNOS */
|
||||
s->br_blockcount = (xfs_filblks_t)(l1 & xfs_mask64lo(21));
|
||||
/* This is xfs_extent_state() in-line */
|
||||
if (ext_flag) {
|
||||
ASSERT(s->br_blockcount != 0); /* saved for DMIG */
|
||||
st = XFS_EXT_UNWRITTEN;
|
||||
} else
|
||||
st = XFS_EXT_NORM;
|
||||
s->br_state = st;
|
||||
}
|
||||
|
||||
void
|
||||
xfs_bmbt_get_all(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_bmbt_irec_t *s)
|
||||
{
|
||||
__xfs_bmbt_get_all(r->l0, r->l1, s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the blockcount field from an in memory bmap extent record.
|
||||
*/
|
||||
xfs_filblks_t
|
||||
xfs_bmbt_get_blockcount(
|
||||
xfs_bmbt_rec_host_t *r)
|
||||
{
|
||||
return (xfs_filblks_t)(r->l1 & xfs_mask64lo(21));
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the startblock field from an in memory bmap extent record.
|
||||
*/
|
||||
xfs_fsblock_t
|
||||
xfs_bmbt_get_startblock(
|
||||
xfs_bmbt_rec_host_t *r)
|
||||
{
|
||||
#if XFS_BIG_BLKNOS
|
||||
return (((xfs_fsblock_t)r->l0 & xfs_mask64lo(9)) << 43) |
|
||||
(((xfs_fsblock_t)r->l1) >> 21);
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
xfs_dfsbno_t b;
|
||||
|
||||
b = (((xfs_dfsbno_t)r->l0 & xfs_mask64lo(9)) << 43) |
|
||||
(((xfs_dfsbno_t)r->l1) >> 21);
|
||||
ASSERT((b >> 32) == 0 || isnulldstartblock(b));
|
||||
return (xfs_fsblock_t)b;
|
||||
#else /* !DEBUG */
|
||||
return (xfs_fsblock_t)(((xfs_dfsbno_t)r->l1) >> 21);
|
||||
#endif /* DEBUG */
|
||||
#endif /* XFS_BIG_BLKNOS */
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the startoff field from an in memory bmap extent record.
|
||||
*/
|
||||
xfs_fileoff_t
|
||||
xfs_bmbt_get_startoff(
|
||||
xfs_bmbt_rec_host_t *r)
|
||||
{
|
||||
return ((xfs_fileoff_t)r->l0 &
|
||||
xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
|
||||
}
|
||||
|
||||
xfs_exntst_t
|
||||
xfs_bmbt_get_state(
|
||||
xfs_bmbt_rec_host_t *r)
|
||||
{
|
||||
int ext_flag;
|
||||
|
||||
ext_flag = (int)((r->l0) >> (64 - BMBT_EXNTFLAG_BITLEN));
|
||||
return xfs_extent_state(xfs_bmbt_get_blockcount(r),
|
||||
ext_flag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the blockcount field from an on disk bmap extent record.
|
||||
*/
|
||||
xfs_filblks_t
|
||||
xfs_bmbt_disk_get_blockcount(
|
||||
xfs_bmbt_rec_t *r)
|
||||
{
|
||||
return (xfs_filblks_t)(be64_to_cpu(r->l1) & xfs_mask64lo(21));
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the startoff field from a disk format bmap extent record.
|
||||
*/
|
||||
xfs_fileoff_t
|
||||
xfs_bmbt_disk_get_startoff(
|
||||
xfs_bmbt_rec_t *r)
|
||||
{
|
||||
return ((xfs_fileoff_t)be64_to_cpu(r->l0) &
|
||||
xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set all the fields in a bmap extent record from the arguments.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_set_allf(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_fileoff_t startoff,
|
||||
xfs_fsblock_t startblock,
|
||||
xfs_filblks_t blockcount,
|
||||
xfs_exntst_t state)
|
||||
{
|
||||
int extent_flag = (state == XFS_EXT_NORM) ? 0 : 1;
|
||||
|
||||
ASSERT(state == XFS_EXT_NORM || state == XFS_EXT_UNWRITTEN);
|
||||
ASSERT((startoff & xfs_mask64hi(64-BMBT_STARTOFF_BITLEN)) == 0);
|
||||
ASSERT((blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)) == 0);
|
||||
|
||||
#if XFS_BIG_BLKNOS
|
||||
ASSERT((startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)) == 0);
|
||||
|
||||
r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
|
||||
((xfs_bmbt_rec_base_t)startoff << 9) |
|
||||
((xfs_bmbt_rec_base_t)startblock >> 43);
|
||||
r->l1 = ((xfs_bmbt_rec_base_t)startblock << 21) |
|
||||
((xfs_bmbt_rec_base_t)blockcount &
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(21));
|
||||
#else /* !XFS_BIG_BLKNOS */
|
||||
if (isnullstartblock(startblock)) {
|
||||
r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
|
||||
((xfs_bmbt_rec_base_t)startoff << 9) |
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(9);
|
||||
r->l1 = xfs_mask64hi(11) |
|
||||
((xfs_bmbt_rec_base_t)startblock << 21) |
|
||||
((xfs_bmbt_rec_base_t)blockcount &
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(21));
|
||||
} else {
|
||||
r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
|
||||
((xfs_bmbt_rec_base_t)startoff << 9);
|
||||
r->l1 = ((xfs_bmbt_rec_base_t)startblock << 21) |
|
||||
((xfs_bmbt_rec_base_t)blockcount &
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(21));
|
||||
}
|
||||
#endif /* XFS_BIG_BLKNOS */
|
||||
}
|
||||
|
||||
/*
|
||||
* Set all the fields in a bmap extent record from the uncompressed form.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_set_all(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_bmbt_irec_t *s)
|
||||
{
|
||||
xfs_bmbt_set_allf(r, s->br_startoff, s->br_startblock,
|
||||
s->br_blockcount, s->br_state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set all the fields in a disk format bmap extent record from the arguments.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_disk_set_allf(
|
||||
xfs_bmbt_rec_t *r,
|
||||
xfs_fileoff_t startoff,
|
||||
xfs_fsblock_t startblock,
|
||||
xfs_filblks_t blockcount,
|
||||
xfs_exntst_t state)
|
||||
{
|
||||
int extent_flag = (state == XFS_EXT_NORM) ? 0 : 1;
|
||||
|
||||
ASSERT(state == XFS_EXT_NORM || state == XFS_EXT_UNWRITTEN);
|
||||
ASSERT((startoff & xfs_mask64hi(64-BMBT_STARTOFF_BITLEN)) == 0);
|
||||
ASSERT((blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)) == 0);
|
||||
|
||||
#if XFS_BIG_BLKNOS
|
||||
ASSERT((startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)) == 0);
|
||||
|
||||
r->l0 = cpu_to_be64(
|
||||
((xfs_bmbt_rec_base_t)extent_flag << 63) |
|
||||
((xfs_bmbt_rec_base_t)startoff << 9) |
|
||||
((xfs_bmbt_rec_base_t)startblock >> 43));
|
||||
r->l1 = cpu_to_be64(
|
||||
((xfs_bmbt_rec_base_t)startblock << 21) |
|
||||
((xfs_bmbt_rec_base_t)blockcount &
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(21)));
|
||||
#else /* !XFS_BIG_BLKNOS */
|
||||
if (isnullstartblock(startblock)) {
|
||||
r->l0 = cpu_to_be64(
|
||||
((xfs_bmbt_rec_base_t)extent_flag << 63) |
|
||||
((xfs_bmbt_rec_base_t)startoff << 9) |
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(9));
|
||||
r->l1 = cpu_to_be64(xfs_mask64hi(11) |
|
||||
((xfs_bmbt_rec_base_t)startblock << 21) |
|
||||
((xfs_bmbt_rec_base_t)blockcount &
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(21)));
|
||||
} else {
|
||||
r->l0 = cpu_to_be64(
|
||||
((xfs_bmbt_rec_base_t)extent_flag << 63) |
|
||||
((xfs_bmbt_rec_base_t)startoff << 9));
|
||||
r->l1 = cpu_to_be64(
|
||||
((xfs_bmbt_rec_base_t)startblock << 21) |
|
||||
((xfs_bmbt_rec_base_t)blockcount &
|
||||
(xfs_bmbt_rec_base_t)xfs_mask64lo(21)));
|
||||
}
|
||||
#endif /* XFS_BIG_BLKNOS */
|
||||
}
|
||||
|
||||
/*
|
||||
* Set all the fields in a bmap extent record from the uncompressed form.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_bmbt_disk_set_all(
|
||||
xfs_bmbt_rec_t *r,
|
||||
xfs_bmbt_irec_t *s)
|
||||
{
|
||||
xfs_bmbt_disk_set_allf(r, s->br_startoff, s->br_startblock,
|
||||
s->br_blockcount, s->br_state);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the blockcount field in a bmap extent record.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_set_blockcount(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_filblks_t v)
|
||||
{
|
||||
ASSERT((v & xfs_mask64hi(43)) == 0);
|
||||
r->l1 = (r->l1 & (xfs_bmbt_rec_base_t)xfs_mask64hi(43)) |
|
||||
(xfs_bmbt_rec_base_t)(v & xfs_mask64lo(21));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the startblock field in a bmap extent record.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_set_startblock(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_fsblock_t v)
|
||||
{
|
||||
#if XFS_BIG_BLKNOS
|
||||
ASSERT((v & xfs_mask64hi(12)) == 0);
|
||||
r->l0 = (r->l0 & (xfs_bmbt_rec_base_t)xfs_mask64hi(55)) |
|
||||
(xfs_bmbt_rec_base_t)(v >> 43);
|
||||
r->l1 = (r->l1 & (xfs_bmbt_rec_base_t)xfs_mask64lo(21)) |
|
||||
(xfs_bmbt_rec_base_t)(v << 21);
|
||||
#else /* !XFS_BIG_BLKNOS */
|
||||
if (isnullstartblock(v)) {
|
||||
r->l0 |= (xfs_bmbt_rec_base_t)xfs_mask64lo(9);
|
||||
r->l1 = (xfs_bmbt_rec_base_t)xfs_mask64hi(11) |
|
||||
((xfs_bmbt_rec_base_t)v << 21) |
|
||||
(r->l1 & (xfs_bmbt_rec_base_t)xfs_mask64lo(21));
|
||||
} else {
|
||||
r->l0 &= ~(xfs_bmbt_rec_base_t)xfs_mask64lo(9);
|
||||
r->l1 = ((xfs_bmbt_rec_base_t)v << 21) |
|
||||
(r->l1 & (xfs_bmbt_rec_base_t)xfs_mask64lo(21));
|
||||
}
|
||||
#endif /* XFS_BIG_BLKNOS */
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the startoff field in a bmap extent record.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_set_startoff(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_fileoff_t v)
|
||||
{
|
||||
ASSERT((v & xfs_mask64hi(9)) == 0);
|
||||
r->l0 = (r->l0 & (xfs_bmbt_rec_base_t) xfs_mask64hi(1)) |
|
||||
((xfs_bmbt_rec_base_t)v << 9) |
|
||||
(r->l0 & (xfs_bmbt_rec_base_t)xfs_mask64lo(9));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the extent state field in a bmap extent record.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_set_state(
|
||||
xfs_bmbt_rec_host_t *r,
|
||||
xfs_exntst_t v)
|
||||
{
|
||||
ASSERT(v == XFS_EXT_NORM || v == XFS_EXT_UNWRITTEN);
|
||||
if (v == XFS_EXT_NORM)
|
||||
r->l0 &= xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN);
|
||||
else
|
||||
r->l0 |= xfs_mask64hi(BMBT_EXNTFLAG_BITLEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert in-memory form of btree root to on-disk form.
|
||||
*/
|
||||
void
|
||||
xfs_bmbt_to_bmdr(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_btree_block *rblock,
|
||||
int rblocklen,
|
||||
xfs_bmdr_block_t *dblock,
|
||||
int dblocklen)
|
||||
{
|
||||
int dmxr;
|
||||
xfs_bmbt_key_t *fkp;
|
||||
__be64 *fpp;
|
||||
xfs_bmbt_key_t *tkp;
|
||||
__be64 *tpp;
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
ASSERT(rblock->bb_magic == cpu_to_be32(XFS_BMAP_CRC_MAGIC));
|
||||
ASSERT(uuid_equal(&rblock->bb_u.l.bb_uuid, &mp->m_sb.sb_uuid));
|
||||
ASSERT(rblock->bb_u.l.bb_blkno ==
|
||||
cpu_to_be64(XFS_BUF_DADDR_NULL));
|
||||
} else
|
||||
ASSERT(rblock->bb_magic == cpu_to_be32(XFS_BMAP_MAGIC));
|
||||
ASSERT(rblock->bb_u.l.bb_leftsib == cpu_to_be64(NULLDFSBNO));
|
||||
ASSERT(rblock->bb_u.l.bb_rightsib == cpu_to_be64(NULLDFSBNO));
|
||||
ASSERT(rblock->bb_level != 0);
|
||||
dblock->bb_level = rblock->bb_level;
|
||||
dblock->bb_numrecs = rblock->bb_numrecs;
|
||||
dmxr = xfs_bmdr_maxrecs(dblocklen, 0);
|
||||
fkp = XFS_BMBT_KEY_ADDR(mp, rblock, 1);
|
||||
tkp = XFS_BMDR_KEY_ADDR(dblock, 1);
|
||||
fpp = XFS_BMAP_BROOT_PTR_ADDR(mp, rblock, 1, rblocklen);
|
||||
tpp = XFS_BMDR_PTR_ADDR(dblock, 1, dmxr);
|
||||
dmxr = be16_to_cpu(dblock->bb_numrecs);
|
||||
memcpy(tkp, fkp, sizeof(*fkp) * dmxr);
|
||||
memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check extent records, which have just been read, for
|
||||
* any bit in the extent flag field. ASSERT on debug
|
||||
* kernels, as this condition should not occur.
|
||||
* Return an error condition (1) if any flags found,
|
||||
* otherwise return 0.
|
||||
*/
|
||||
|
||||
int
|
||||
xfs_check_nostate_extents(
|
||||
xfs_ifork_t *ifp,
|
||||
xfs_extnum_t idx,
|
||||
xfs_extnum_t num)
|
||||
{
|
||||
for (; num > 0; num--, idx++) {
|
||||
xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, idx);
|
||||
if ((ep->l0 >>
|
||||
(64 - BMBT_EXNTFLAG_BITLEN)) != 0) {
|
||||
ASSERT(0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
STATIC struct xfs_btree_cur *
|
||||
xfs_bmbt_dup_cursor(
|
||||
struct xfs_btree_cur *cur)
|
||||
{
|
||||
struct xfs_btree_cur *new;
|
||||
|
||||
new = xfs_bmbt_init_cursor(cur->bc_mp, cur->bc_tp,
|
||||
cur->bc_private.b.ip, cur->bc_private.b.whichfork);
|
||||
|
||||
/*
|
||||
* Copy the firstblock, flist, and flags values,
|
||||
* since init cursor doesn't get them.
|
||||
*/
|
||||
new->bc_private.b.firstblock = cur->bc_private.b.firstblock;
|
||||
new->bc_private.b.flist = cur->bc_private.b.flist;
|
||||
new->bc_private.b.flags = cur->bc_private.b.flags;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_bmbt_update_cursor(
|
||||
struct xfs_btree_cur *src,
|
||||
struct xfs_btree_cur *dst)
|
||||
{
|
||||
ASSERT((dst->bc_private.b.firstblock != NULLFSBLOCK) ||
|
||||
(dst->bc_private.b.ip->i_d.di_flags & XFS_DIFLAG_REALTIME));
|
||||
ASSERT(dst->bc_private.b.flist == src->bc_private.b.flist);
|
||||
|
||||
dst->bc_private.b.allocated += src->bc_private.b.allocated;
|
||||
dst->bc_private.b.firstblock = src->bc_private.b.firstblock;
|
||||
|
||||
src->bc_private.b.allocated = 0;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_bmbt_alloc_block(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *start,
|
||||
union xfs_btree_ptr *new,
|
||||
int *stat)
|
||||
{
|
||||
xfs_alloc_arg_t args; /* block allocation args */
|
||||
int error; /* error return value */
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.tp = cur->bc_tp;
|
||||
args.mp = cur->bc_mp;
|
||||
args.fsbno = cur->bc_private.b.firstblock;
|
||||
args.firstblock = args.fsbno;
|
||||
|
||||
if (args.fsbno == NULLFSBLOCK) {
|
||||
args.fsbno = be64_to_cpu(start->l);
|
||||
args.type = XFS_ALLOCTYPE_START_BNO;
|
||||
/*
|
||||
* Make sure there is sufficient room left in the AG to
|
||||
* complete a full tree split for an extent insert. If
|
||||
* we are converting the middle part of an extent then
|
||||
* we may need space for two tree splits.
|
||||
*
|
||||
* We are relying on the caller to make the correct block
|
||||
* reservation for this operation to succeed. If the
|
||||
* reservation amount is insufficient then we may fail a
|
||||
* block allocation here and corrupt the filesystem.
|
||||
*/
|
||||
args.minleft = xfs_trans_get_block_res(args.tp);
|
||||
} else if (cur->bc_private.b.flist->xbf_low) {
|
||||
args.type = XFS_ALLOCTYPE_START_BNO;
|
||||
} else {
|
||||
args.type = XFS_ALLOCTYPE_NEAR_BNO;
|
||||
}
|
||||
|
||||
args.minlen = args.maxlen = args.prod = 1;
|
||||
args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
|
||||
if (!args.wasdel && xfs_trans_get_block_res(args.tp) == 0) {
|
||||
error = ENOSPC;
|
||||
goto error0;
|
||||
}
|
||||
error = xfs_alloc_vextent(&args);
|
||||
if (error)
|
||||
goto error0;
|
||||
|
||||
if (args.fsbno == NULLFSBLOCK && args.minleft) {
|
||||
/*
|
||||
* Could not find an AG with enough free space to satisfy
|
||||
* a full btree split. Try again without minleft and if
|
||||
* successful activate the lowspace algorithm.
|
||||
*/
|
||||
args.fsbno = 0;
|
||||
args.type = XFS_ALLOCTYPE_FIRST_AG;
|
||||
args.minleft = 0;
|
||||
error = xfs_alloc_vextent(&args);
|
||||
if (error)
|
||||
goto error0;
|
||||
cur->bc_private.b.flist->xbf_low = 1;
|
||||
}
|
||||
if (args.fsbno == NULLFSBLOCK) {
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
|
||||
*stat = 0;
|
||||
return 0;
|
||||
}
|
||||
ASSERT(args.len == 1);
|
||||
cur->bc_private.b.firstblock = args.fsbno;
|
||||
cur->bc_private.b.allocated++;
|
||||
cur->bc_private.b.ip->i_d.di_nblocks++;
|
||||
xfs_trans_log_inode(args.tp, cur->bc_private.b.ip, XFS_ILOG_CORE);
|
||||
xfs_trans_mod_dquot_byino(args.tp, cur->bc_private.b.ip,
|
||||
XFS_TRANS_DQ_BCOUNT, 1L);
|
||||
|
||||
new->l = cpu_to_be64(args.fsbno);
|
||||
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
|
||||
*stat = 1;
|
||||
return 0;
|
||||
|
||||
error0:
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
|
||||
return error;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_bmbt_free_block(
|
||||
struct xfs_btree_cur *cur,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = cur->bc_mp;
|
||||
struct xfs_inode *ip = cur->bc_private.b.ip;
|
||||
struct xfs_trans *tp = cur->bc_tp;
|
||||
xfs_fsblock_t fsbno = XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(bp));
|
||||
|
||||
xfs_bmap_add_free(fsbno, 1, cur->bc_private.b.flist, mp);
|
||||
ip->i_d.di_nblocks--;
|
||||
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
|
||||
xfs_trans_binval(tp, bp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_bmbt_get_minrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
if (level == cur->bc_nlevels - 1) {
|
||||
struct xfs_ifork *ifp;
|
||||
|
||||
ifp = XFS_IFORK_PTR(cur->bc_private.b.ip,
|
||||
cur->bc_private.b.whichfork);
|
||||
|
||||
return xfs_bmbt_maxrecs(cur->bc_mp,
|
||||
ifp->if_broot_bytes, level == 0) / 2;
|
||||
}
|
||||
|
||||
return cur->bc_mp->m_bmap_dmnr[level != 0];
|
||||
}
|
||||
|
||||
int
|
||||
xfs_bmbt_get_maxrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
if (level == cur->bc_nlevels - 1) {
|
||||
struct xfs_ifork *ifp;
|
||||
|
||||
ifp = XFS_IFORK_PTR(cur->bc_private.b.ip,
|
||||
cur->bc_private.b.whichfork);
|
||||
|
||||
return xfs_bmbt_maxrecs(cur->bc_mp,
|
||||
ifp->if_broot_bytes, level == 0);
|
||||
}
|
||||
|
||||
return cur->bc_mp->m_bmap_dmxr[level != 0];
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the maximum records we could store in the on-disk format.
|
||||
*
|
||||
* For non-root nodes this is equivalent to xfs_bmbt_get_maxrecs, but
|
||||
* for the root node this checks the available space in the dinode fork
|
||||
* so that we can resize the in-memory buffer to match it. After a
|
||||
* resize to the maximum size this function returns the same value
|
||||
* as xfs_bmbt_get_maxrecs for the root node, too.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_bmbt_get_dmaxrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
if (level != cur->bc_nlevels - 1)
|
||||
return cur->bc_mp->m_bmap_dmxr[level != 0];
|
||||
return xfs_bmdr_maxrecs(cur->bc_private.b.forksize, level == 0);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_bmbt_init_key_from_rec(
|
||||
union xfs_btree_key *key,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
key->bmbt.br_startoff =
|
||||
cpu_to_be64(xfs_bmbt_disk_get_startoff(&rec->bmbt));
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_bmbt_init_rec_from_key(
|
||||
union xfs_btree_key *key,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
ASSERT(key->bmbt.br_startoff != 0);
|
||||
|
||||
xfs_bmbt_disk_set_allf(&rec->bmbt, be64_to_cpu(key->bmbt.br_startoff),
|
||||
0, 0, XFS_EXT_NORM);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_bmbt_init_rec_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
xfs_bmbt_disk_set_all(&rec->bmbt, &cur->bc_rec.b);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_bmbt_init_ptr_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *ptr)
|
||||
{
|
||||
ptr->l = 0;
|
||||
}
|
||||
|
||||
STATIC __int64_t
|
||||
xfs_bmbt_key_diff(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_key *key)
|
||||
{
|
||||
return (__int64_t)be64_to_cpu(key->bmbt.br_startoff) -
|
||||
cur->bc_rec.b.br_startoff;
|
||||
}
|
||||
|
||||
static bool
|
||||
xfs_bmbt_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
|
||||
unsigned int level;
|
||||
|
||||
switch (block->bb_magic) {
|
||||
case cpu_to_be32(XFS_BMAP_CRC_MAGIC):
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (!uuid_equal(&block->bb_u.l.bb_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
if (be64_to_cpu(block->bb_u.l.bb_blkno) != bp->b_bn)
|
||||
return false;
|
||||
/*
|
||||
* XXX: need a better way of verifying the owner here. Right now
|
||||
* just make sure there has been one set.
|
||||
*/
|
||||
if (be64_to_cpu(block->bb_u.l.bb_owner) == 0)
|
||||
return false;
|
||||
/* fall through */
|
||||
case cpu_to_be32(XFS_BMAP_MAGIC):
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* numrecs and level verification.
|
||||
*
|
||||
* We don't know what fork we belong to, so just verify that the level
|
||||
* is less than the maximum of the two. Later checks will be more
|
||||
* precise.
|
||||
*/
|
||||
level = be16_to_cpu(block->bb_level);
|
||||
if (level > max(mp->m_bm_maxlevels[0], mp->m_bm_maxlevels[1]))
|
||||
return false;
|
||||
if (be16_to_cpu(block->bb_numrecs) > mp->m_bmap_dmxr[level != 0])
|
||||
return false;
|
||||
|
||||
/* sibling pointer verification */
|
||||
if (!block->bb_u.l.bb_leftsib ||
|
||||
(block->bb_u.l.bb_leftsib != cpu_to_be64(NULLDFSBNO) &&
|
||||
!XFS_FSB_SANITY_CHECK(mp, be64_to_cpu(block->bb_u.l.bb_leftsib))))
|
||||
return false;
|
||||
if (!block->bb_u.l.bb_rightsib ||
|
||||
(block->bb_u.l.bb_rightsib != cpu_to_be64(NULLDFSBNO) &&
|
||||
!XFS_FSB_SANITY_CHECK(mp, be64_to_cpu(block->bb_u.l.bb_rightsib))))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_bmbt_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
if (!xfs_btree_lblock_verify_crc(bp))
|
||||
xfs_buf_ioerror(bp, EFSBADCRC);
|
||||
else if (!xfs_bmbt_verify(bp))
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
|
||||
if (bp->b_error) {
|
||||
trace_xfs_btree_corrupt(bp, _RET_IP_);
|
||||
xfs_verifier_error(bp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_bmbt_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
if (!xfs_bmbt_verify(bp)) {
|
||||
trace_xfs_btree_corrupt(bp, _RET_IP_);
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
return;
|
||||
}
|
||||
xfs_btree_lblock_calc_crc(bp);
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_bmbt_buf_ops = {
|
||||
.verify_read = xfs_bmbt_read_verify,
|
||||
.verify_write = xfs_bmbt_write_verify,
|
||||
};
|
||||
|
||||
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
STATIC int
|
||||
xfs_bmbt_keys_inorder(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_key *k1,
|
||||
union xfs_btree_key *k2)
|
||||
{
|
||||
return be64_to_cpu(k1->bmbt.br_startoff) <
|
||||
be64_to_cpu(k2->bmbt.br_startoff);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_bmbt_recs_inorder(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec *r1,
|
||||
union xfs_btree_rec *r2)
|
||||
{
|
||||
return xfs_bmbt_disk_get_startoff(&r1->bmbt) +
|
||||
xfs_bmbt_disk_get_blockcount(&r1->bmbt) <=
|
||||
xfs_bmbt_disk_get_startoff(&r2->bmbt);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
static const struct xfs_btree_ops xfs_bmbt_ops = {
|
||||
.rec_len = sizeof(xfs_bmbt_rec_t),
|
||||
.key_len = sizeof(xfs_bmbt_key_t),
|
||||
|
||||
.dup_cursor = xfs_bmbt_dup_cursor,
|
||||
.update_cursor = xfs_bmbt_update_cursor,
|
||||
.alloc_block = xfs_bmbt_alloc_block,
|
||||
.free_block = xfs_bmbt_free_block,
|
||||
.get_maxrecs = xfs_bmbt_get_maxrecs,
|
||||
.get_minrecs = xfs_bmbt_get_minrecs,
|
||||
.get_dmaxrecs = xfs_bmbt_get_dmaxrecs,
|
||||
.init_key_from_rec = xfs_bmbt_init_key_from_rec,
|
||||
.init_rec_from_key = xfs_bmbt_init_rec_from_key,
|
||||
.init_rec_from_cur = xfs_bmbt_init_rec_from_cur,
|
||||
.init_ptr_from_cur = xfs_bmbt_init_ptr_from_cur,
|
||||
.key_diff = xfs_bmbt_key_diff,
|
||||
.buf_ops = &xfs_bmbt_buf_ops,
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
.keys_inorder = xfs_bmbt_keys_inorder,
|
||||
.recs_inorder = xfs_bmbt_recs_inorder,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocate a new bmap btree cursor.
|
||||
*/
|
||||
struct xfs_btree_cur * /* new bmap btree cursor */
|
||||
xfs_bmbt_init_cursor(
|
||||
struct xfs_mount *mp, /* file system mount point */
|
||||
struct xfs_trans *tp, /* transaction pointer */
|
||||
struct xfs_inode *ip, /* inode owning the btree */
|
||||
int whichfork) /* data or attr fork */
|
||||
{
|
||||
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
|
||||
struct xfs_btree_cur *cur;
|
||||
|
||||
cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
|
||||
|
||||
cur->bc_tp = tp;
|
||||
cur->bc_mp = mp;
|
||||
cur->bc_nlevels = be16_to_cpu(ifp->if_broot->bb_level) + 1;
|
||||
cur->bc_btnum = XFS_BTNUM_BMAP;
|
||||
cur->bc_blocklog = mp->m_sb.sb_blocklog;
|
||||
|
||||
cur->bc_ops = &xfs_bmbt_ops;
|
||||
cur->bc_flags = XFS_BTREE_LONG_PTRS | XFS_BTREE_ROOT_IN_INODE;
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
|
||||
|
||||
cur->bc_private.b.forksize = XFS_IFORK_SIZE(ip, whichfork);
|
||||
cur->bc_private.b.ip = ip;
|
||||
cur->bc_private.b.firstblock = NULLFSBLOCK;
|
||||
cur->bc_private.b.flist = NULL;
|
||||
cur->bc_private.b.allocated = 0;
|
||||
cur->bc_private.b.flags = 0;
|
||||
cur->bc_private.b.whichfork = whichfork;
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate number of records in a bmap btree block.
|
||||
*/
|
||||
int
|
||||
xfs_bmbt_maxrecs(
|
||||
struct xfs_mount *mp,
|
||||
int blocklen,
|
||||
int leaf)
|
||||
{
|
||||
blocklen -= XFS_BMBT_BLOCK_LEN(mp);
|
||||
|
||||
if (leaf)
|
||||
return blocklen / sizeof(xfs_bmbt_rec_t);
|
||||
return blocklen / (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate number of records in a bmap btree inode root.
|
||||
*/
|
||||
int
|
||||
xfs_bmdr_maxrecs(
|
||||
int blocklen,
|
||||
int leaf)
|
||||
{
|
||||
blocklen -= sizeof(xfs_bmdr_block_t);
|
||||
|
||||
if (leaf)
|
||||
return blocklen / sizeof(xfs_bmdr_rec_t);
|
||||
return blocklen / (sizeof(xfs_bmdr_key_t) + sizeof(xfs_bmdr_ptr_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the owner of a btree format fork fo the inode passed in. Change it to
|
||||
* the owner of that is passed in so that we can change owners before or after
|
||||
* we switch forks between inodes. The operation that the caller is doing will
|
||||
* determine whether is needs to change owner before or after the switch.
|
||||
*
|
||||
* For demand paged transactional modification, the fork switch should be done
|
||||
* after reading in all the blocks, modifying them and pinning them in the
|
||||
* transaction. For modification when the buffers are already pinned in memory,
|
||||
* the fork switch can be done before changing the owner as we won't need to
|
||||
* validate the owner until the btree buffers are unpinned and writes can occur
|
||||
* again.
|
||||
*
|
||||
* For recovery based ownership change, there is no transactional context and
|
||||
* so a buffer list must be supplied so that we can record the buffers that we
|
||||
* modified for the caller to issue IO on.
|
||||
*/
|
||||
int
|
||||
xfs_bmbt_change_owner(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode *ip,
|
||||
int whichfork,
|
||||
xfs_ino_t new_owner,
|
||||
struct list_head *buffer_list)
|
||||
{
|
||||
struct xfs_btree_cur *cur;
|
||||
int error;
|
||||
|
||||
ASSERT(tp || buffer_list);
|
||||
ASSERT(!(tp && buffer_list));
|
||||
if (whichfork == XFS_DATA_FORK)
|
||||
ASSERT(ip->i_d.di_format == XFS_DINODE_FMT_BTREE);
|
||||
else
|
||||
ASSERT(ip->i_d.di_aformat == XFS_DINODE_FMT_BTREE);
|
||||
|
||||
cur = xfs_bmbt_init_cursor(ip->i_mount, tp, ip, whichfork);
|
||||
if (!cur)
|
||||
return ENOMEM;
|
||||
|
||||
error = xfs_btree_change_owner(cur, new_owner, buffer_list);
|
||||
xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
|
||||
return error;
|
||||
}
|
3989
fs/xfs/libxfs/xfs_btree.c
Normal file
3989
fs/xfs/libxfs/xfs_btree.c
Normal file
File diff suppressed because it is too large
Load Diff
2665
fs/xfs/libxfs/xfs_da_btree.c
Normal file
2665
fs/xfs/libxfs/xfs_da_btree.c
Normal file
File diff suppressed because it is too large
Load Diff
911
fs/xfs/libxfs/xfs_da_format.c
Normal file
911
fs/xfs/libxfs/xfs_da_format.c
Normal file
@@ -0,0 +1,911 @@
|
||||
/*
|
||||
* Copyright (c) 2000,2002,2005 Silicon Graphics, Inc.
|
||||
* Copyright (c) 2013 Red Hat, 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_da_format.h"
|
||||
#include "xfs_da_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_dir2.h"
|
||||
#include "xfs_dir2_priv.h"
|
||||
|
||||
/*
|
||||
* Shortform directory ops
|
||||
*/
|
||||
static int
|
||||
xfs_dir2_sf_entsize(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
int len)
|
||||
{
|
||||
int count = sizeof(struct xfs_dir2_sf_entry); /* namelen + offset */
|
||||
|
||||
count += len; /* name */
|
||||
count += hdr->i8count ? sizeof(xfs_dir2_ino8_t) :
|
||||
sizeof(xfs_dir2_ino4_t); /* ino # */
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_dir3_sf_entsize(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
int len)
|
||||
{
|
||||
return xfs_dir2_sf_entsize(hdr, len) + sizeof(__uint8_t);
|
||||
}
|
||||
|
||||
static struct xfs_dir2_sf_entry *
|
||||
xfs_dir2_sf_nextentry(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
struct xfs_dir2_sf_entry *sfep)
|
||||
{
|
||||
return (struct xfs_dir2_sf_entry *)
|
||||
((char *)sfep + xfs_dir2_sf_entsize(hdr, sfep->namelen));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_sf_entry *
|
||||
xfs_dir3_sf_nextentry(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
struct xfs_dir2_sf_entry *sfep)
|
||||
{
|
||||
return (struct xfs_dir2_sf_entry *)
|
||||
((char *)sfep + xfs_dir3_sf_entsize(hdr, sfep->namelen));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For filetype enabled shortform directories, the file type field is stored at
|
||||
* the end of the name. Because it's only a single byte, endian conversion is
|
||||
* not necessary. For non-filetype enable directories, the type is always
|
||||
* unknown and we never store the value.
|
||||
*/
|
||||
static __uint8_t
|
||||
xfs_dir2_sfe_get_ftype(
|
||||
struct xfs_dir2_sf_entry *sfep)
|
||||
{
|
||||
return XFS_DIR3_FT_UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_sfe_put_ftype(
|
||||
struct xfs_dir2_sf_entry *sfep,
|
||||
__uint8_t ftype)
|
||||
{
|
||||
ASSERT(ftype < XFS_DIR3_FT_MAX);
|
||||
}
|
||||
|
||||
static __uint8_t
|
||||
xfs_dir3_sfe_get_ftype(
|
||||
struct xfs_dir2_sf_entry *sfep)
|
||||
{
|
||||
__uint8_t ftype;
|
||||
|
||||
ftype = sfep->name[sfep->namelen];
|
||||
if (ftype >= XFS_DIR3_FT_MAX)
|
||||
return XFS_DIR3_FT_UNKNOWN;
|
||||
return ftype;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_sfe_put_ftype(
|
||||
struct xfs_dir2_sf_entry *sfep,
|
||||
__uint8_t ftype)
|
||||
{
|
||||
ASSERT(ftype < XFS_DIR3_FT_MAX);
|
||||
|
||||
sfep->name[sfep->namelen] = ftype;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inode numbers in short-form directories can come in two versions,
|
||||
* either 4 bytes or 8 bytes wide. These helpers deal with the
|
||||
* two forms transparently by looking at the headers i8count field.
|
||||
*
|
||||
* For 64-bit inode number the most significant byte must be zero.
|
||||
*/
|
||||
static xfs_ino_t
|
||||
xfs_dir2_sf_get_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
xfs_dir2_inou_t *from)
|
||||
{
|
||||
if (hdr->i8count)
|
||||
return get_unaligned_be64(&from->i8.i) & 0x00ffffffffffffffULL;
|
||||
else
|
||||
return get_unaligned_be32(&from->i4.i);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_sf_put_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
xfs_dir2_inou_t *to,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
ASSERT((ino & 0xff00000000000000ULL) == 0);
|
||||
|
||||
if (hdr->i8count)
|
||||
put_unaligned_be64(ino, &to->i8.i);
|
||||
else
|
||||
put_unaligned_be32(ino, &to->i4.i);
|
||||
}
|
||||
|
||||
static xfs_ino_t
|
||||
xfs_dir2_sf_get_parent_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr)
|
||||
{
|
||||
return xfs_dir2_sf_get_ino(hdr, &hdr->parent);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_sf_put_parent_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
xfs_dir2_sf_put_ino(hdr, &hdr->parent, ino);
|
||||
}
|
||||
|
||||
/*
|
||||
* In short-form directory entries the inode numbers are stored at variable
|
||||
* offset behind the entry name. If the entry stores a filetype value, then it
|
||||
* sits between the name and the inode number. Hence the inode numbers may only
|
||||
* be accessed through the helpers below.
|
||||
*/
|
||||
static xfs_ino_t
|
||||
xfs_dir2_sfe_get_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
struct xfs_dir2_sf_entry *sfep)
|
||||
{
|
||||
return xfs_dir2_sf_get_ino(hdr,
|
||||
(xfs_dir2_inou_t *)&sfep->name[sfep->namelen]);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_sfe_put_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
struct xfs_dir2_sf_entry *sfep,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
xfs_dir2_sf_put_ino(hdr,
|
||||
(xfs_dir2_inou_t *)&sfep->name[sfep->namelen], ino);
|
||||
}
|
||||
|
||||
static xfs_ino_t
|
||||
xfs_dir3_sfe_get_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
struct xfs_dir2_sf_entry *sfep)
|
||||
{
|
||||
return xfs_dir2_sf_get_ino(hdr,
|
||||
(xfs_dir2_inou_t *)&sfep->name[sfep->namelen + 1]);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_sfe_put_ino(
|
||||
struct xfs_dir2_sf_hdr *hdr,
|
||||
struct xfs_dir2_sf_entry *sfep,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
xfs_dir2_sf_put_ino(hdr,
|
||||
(xfs_dir2_inou_t *)&sfep->name[sfep->namelen + 1], ino);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Directory data block operations
|
||||
*/
|
||||
|
||||
/*
|
||||
* For special situations, the dirent size ends up fixed because we always know
|
||||
* what the size of the entry is. That's true for the "." and "..", and
|
||||
* therefore we know that they are a fixed size and hence their offsets are
|
||||
* constant, as is the first entry.
|
||||
*
|
||||
* Hence, this calculation is written as a macro to be able to be calculated at
|
||||
* compile time and so certain offsets can be calculated directly in the
|
||||
* structure initaliser via the macro. There are two macros - one for dirents
|
||||
* with ftype and without so there are no unresolvable conditionals in the
|
||||
* calculations. We also use round_up() as XFS_DIR2_DATA_ALIGN is always a power
|
||||
* of 2 and the compiler doesn't reject it (unlike roundup()).
|
||||
*/
|
||||
#define XFS_DIR2_DATA_ENTSIZE(n) \
|
||||
round_up((offsetof(struct xfs_dir2_data_entry, name[0]) + (n) + \
|
||||
sizeof(xfs_dir2_data_off_t)), XFS_DIR2_DATA_ALIGN)
|
||||
|
||||
#define XFS_DIR3_DATA_ENTSIZE(n) \
|
||||
round_up((offsetof(struct xfs_dir2_data_entry, name[0]) + (n) + \
|
||||
sizeof(xfs_dir2_data_off_t) + sizeof(__uint8_t)), \
|
||||
XFS_DIR2_DATA_ALIGN)
|
||||
|
||||
static int
|
||||
xfs_dir2_data_entsize(
|
||||
int n)
|
||||
{
|
||||
return XFS_DIR2_DATA_ENTSIZE(n);
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_dir3_data_entsize(
|
||||
int n)
|
||||
{
|
||||
return XFS_DIR3_DATA_ENTSIZE(n);
|
||||
}
|
||||
|
||||
static __uint8_t
|
||||
xfs_dir2_data_get_ftype(
|
||||
struct xfs_dir2_data_entry *dep)
|
||||
{
|
||||
return XFS_DIR3_FT_UNKNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_data_put_ftype(
|
||||
struct xfs_dir2_data_entry *dep,
|
||||
__uint8_t ftype)
|
||||
{
|
||||
ASSERT(ftype < XFS_DIR3_FT_MAX);
|
||||
}
|
||||
|
||||
static __uint8_t
|
||||
xfs_dir3_data_get_ftype(
|
||||
struct xfs_dir2_data_entry *dep)
|
||||
{
|
||||
__uint8_t ftype = dep->name[dep->namelen];
|
||||
|
||||
ASSERT(ftype < XFS_DIR3_FT_MAX);
|
||||
if (ftype >= XFS_DIR3_FT_MAX)
|
||||
return XFS_DIR3_FT_UNKNOWN;
|
||||
return ftype;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_data_put_ftype(
|
||||
struct xfs_dir2_data_entry *dep,
|
||||
__uint8_t type)
|
||||
{
|
||||
ASSERT(type < XFS_DIR3_FT_MAX);
|
||||
ASSERT(dep->namelen != 0);
|
||||
|
||||
dep->name[dep->namelen] = type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pointer to an entry's tag word.
|
||||
*/
|
||||
static __be16 *
|
||||
xfs_dir2_data_entry_tag_p(
|
||||
struct xfs_dir2_data_entry *dep)
|
||||
{
|
||||
return (__be16 *)((char *)dep +
|
||||
xfs_dir2_data_entsize(dep->namelen) - sizeof(__be16));
|
||||
}
|
||||
|
||||
static __be16 *
|
||||
xfs_dir3_data_entry_tag_p(
|
||||
struct xfs_dir2_data_entry *dep)
|
||||
{
|
||||
return (__be16 *)((char *)dep +
|
||||
xfs_dir3_data_entsize(dep->namelen) - sizeof(__be16));
|
||||
}
|
||||
|
||||
/*
|
||||
* location of . and .. in data space (always block 0)
|
||||
*/
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir2_data_dot_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir2_data_dotdot_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR2_DATA_ENTSIZE(1));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir2_data_first_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR2_DATA_ENTSIZE(1) +
|
||||
XFS_DIR2_DATA_ENTSIZE(2));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir2_ftype_data_dotdot_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir2_ftype_data_first_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1) +
|
||||
XFS_DIR3_DATA_ENTSIZE(2));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir3_data_dot_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir3_data_dotdot_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir3_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir3_data_first_entry_p(
|
||||
struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir3_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1) +
|
||||
XFS_DIR3_DATA_ENTSIZE(2));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_free *
|
||||
xfs_dir2_data_bestfree_p(struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return hdr->bestfree;
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_free *
|
||||
xfs_dir3_data_bestfree_p(struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return ((struct xfs_dir3_data_hdr *)hdr)->best_free;
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir2_data_entry_p(struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_unused *
|
||||
xfs_dir2_data_unused_p(struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_unused *)
|
||||
((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_entry *
|
||||
xfs_dir3_data_entry_p(struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_entry *)
|
||||
((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
|
||||
}
|
||||
|
||||
static struct xfs_dir2_data_unused *
|
||||
xfs_dir3_data_unused_p(struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return (struct xfs_dir2_data_unused *)
|
||||
((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Directory Leaf block operations
|
||||
*/
|
||||
static int
|
||||
xfs_dir2_max_leaf_ents(struct xfs_da_geometry *geo)
|
||||
{
|
||||
return (geo->blksize - sizeof(struct xfs_dir2_leaf_hdr)) /
|
||||
(uint)sizeof(struct xfs_dir2_leaf_entry);
|
||||
}
|
||||
|
||||
static struct xfs_dir2_leaf_entry *
|
||||
xfs_dir2_leaf_ents_p(struct xfs_dir2_leaf *lp)
|
||||
{
|
||||
return lp->__ents;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_dir3_max_leaf_ents(struct xfs_da_geometry *geo)
|
||||
{
|
||||
return (geo->blksize - sizeof(struct xfs_dir3_leaf_hdr)) /
|
||||
(uint)sizeof(struct xfs_dir2_leaf_entry);
|
||||
}
|
||||
|
||||
static struct xfs_dir2_leaf_entry *
|
||||
xfs_dir3_leaf_ents_p(struct xfs_dir2_leaf *lp)
|
||||
{
|
||||
return ((struct xfs_dir3_leaf *)lp)->__ents;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_leaf_hdr_from_disk(
|
||||
struct xfs_dir3_icleaf_hdr *to,
|
||||
struct xfs_dir2_leaf *from)
|
||||
{
|
||||
to->forw = be32_to_cpu(from->hdr.info.forw);
|
||||
to->back = be32_to_cpu(from->hdr.info.back);
|
||||
to->magic = be16_to_cpu(from->hdr.info.magic);
|
||||
to->count = be16_to_cpu(from->hdr.count);
|
||||
to->stale = be16_to_cpu(from->hdr.stale);
|
||||
|
||||
ASSERT(to->magic == XFS_DIR2_LEAF1_MAGIC ||
|
||||
to->magic == XFS_DIR2_LEAFN_MAGIC);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_leaf_hdr_to_disk(
|
||||
struct xfs_dir2_leaf *to,
|
||||
struct xfs_dir3_icleaf_hdr *from)
|
||||
{
|
||||
ASSERT(from->magic == XFS_DIR2_LEAF1_MAGIC ||
|
||||
from->magic == XFS_DIR2_LEAFN_MAGIC);
|
||||
|
||||
to->hdr.info.forw = cpu_to_be32(from->forw);
|
||||
to->hdr.info.back = cpu_to_be32(from->back);
|
||||
to->hdr.info.magic = cpu_to_be16(from->magic);
|
||||
to->hdr.count = cpu_to_be16(from->count);
|
||||
to->hdr.stale = cpu_to_be16(from->stale);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_leaf_hdr_from_disk(
|
||||
struct xfs_dir3_icleaf_hdr *to,
|
||||
struct xfs_dir2_leaf *from)
|
||||
{
|
||||
struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)from;
|
||||
|
||||
to->forw = be32_to_cpu(hdr3->info.hdr.forw);
|
||||
to->back = be32_to_cpu(hdr3->info.hdr.back);
|
||||
to->magic = be16_to_cpu(hdr3->info.hdr.magic);
|
||||
to->count = be16_to_cpu(hdr3->count);
|
||||
to->stale = be16_to_cpu(hdr3->stale);
|
||||
|
||||
ASSERT(to->magic == XFS_DIR3_LEAF1_MAGIC ||
|
||||
to->magic == XFS_DIR3_LEAFN_MAGIC);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_leaf_hdr_to_disk(
|
||||
struct xfs_dir2_leaf *to,
|
||||
struct xfs_dir3_icleaf_hdr *from)
|
||||
{
|
||||
struct xfs_dir3_leaf_hdr *hdr3 = (struct xfs_dir3_leaf_hdr *)to;
|
||||
|
||||
ASSERT(from->magic == XFS_DIR3_LEAF1_MAGIC ||
|
||||
from->magic == XFS_DIR3_LEAFN_MAGIC);
|
||||
|
||||
hdr3->info.hdr.forw = cpu_to_be32(from->forw);
|
||||
hdr3->info.hdr.back = cpu_to_be32(from->back);
|
||||
hdr3->info.hdr.magic = cpu_to_be16(from->magic);
|
||||
hdr3->count = cpu_to_be16(from->count);
|
||||
hdr3->stale = cpu_to_be16(from->stale);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Directory/Attribute Node block operations
|
||||
*/
|
||||
static struct xfs_da_node_entry *
|
||||
xfs_da2_node_tree_p(struct xfs_da_intnode *dap)
|
||||
{
|
||||
return dap->__btree;
|
||||
}
|
||||
|
||||
static struct xfs_da_node_entry *
|
||||
xfs_da3_node_tree_p(struct xfs_da_intnode *dap)
|
||||
{
|
||||
return ((struct xfs_da3_intnode *)dap)->__btree;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_da2_node_hdr_from_disk(
|
||||
struct xfs_da3_icnode_hdr *to,
|
||||
struct xfs_da_intnode *from)
|
||||
{
|
||||
ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA_NODE_MAGIC));
|
||||
to->forw = be32_to_cpu(from->hdr.info.forw);
|
||||
to->back = be32_to_cpu(from->hdr.info.back);
|
||||
to->magic = be16_to_cpu(from->hdr.info.magic);
|
||||
to->count = be16_to_cpu(from->hdr.__count);
|
||||
to->level = be16_to_cpu(from->hdr.__level);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_da2_node_hdr_to_disk(
|
||||
struct xfs_da_intnode *to,
|
||||
struct xfs_da3_icnode_hdr *from)
|
||||
{
|
||||
ASSERT(from->magic == XFS_DA_NODE_MAGIC);
|
||||
to->hdr.info.forw = cpu_to_be32(from->forw);
|
||||
to->hdr.info.back = cpu_to_be32(from->back);
|
||||
to->hdr.info.magic = cpu_to_be16(from->magic);
|
||||
to->hdr.__count = cpu_to_be16(from->count);
|
||||
to->hdr.__level = cpu_to_be16(from->level);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_da3_node_hdr_from_disk(
|
||||
struct xfs_da3_icnode_hdr *to,
|
||||
struct xfs_da_intnode *from)
|
||||
{
|
||||
struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)from;
|
||||
|
||||
ASSERT(from->hdr.info.magic == cpu_to_be16(XFS_DA3_NODE_MAGIC));
|
||||
to->forw = be32_to_cpu(hdr3->info.hdr.forw);
|
||||
to->back = be32_to_cpu(hdr3->info.hdr.back);
|
||||
to->magic = be16_to_cpu(hdr3->info.hdr.magic);
|
||||
to->count = be16_to_cpu(hdr3->__count);
|
||||
to->level = be16_to_cpu(hdr3->__level);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_da3_node_hdr_to_disk(
|
||||
struct xfs_da_intnode *to,
|
||||
struct xfs_da3_icnode_hdr *from)
|
||||
{
|
||||
struct xfs_da3_node_hdr *hdr3 = (struct xfs_da3_node_hdr *)to;
|
||||
|
||||
ASSERT(from->magic == XFS_DA3_NODE_MAGIC);
|
||||
hdr3->info.hdr.forw = cpu_to_be32(from->forw);
|
||||
hdr3->info.hdr.back = cpu_to_be32(from->back);
|
||||
hdr3->info.hdr.magic = cpu_to_be16(from->magic);
|
||||
hdr3->__count = cpu_to_be16(from->count);
|
||||
hdr3->__level = cpu_to_be16(from->level);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Directory free space block operations
|
||||
*/
|
||||
static int
|
||||
xfs_dir2_free_max_bests(struct xfs_da_geometry *geo)
|
||||
{
|
||||
return (geo->blksize - sizeof(struct xfs_dir2_free_hdr)) /
|
||||
sizeof(xfs_dir2_data_off_t);
|
||||
}
|
||||
|
||||
static __be16 *
|
||||
xfs_dir2_free_bests_p(struct xfs_dir2_free *free)
|
||||
{
|
||||
return (__be16 *)((char *)free + sizeof(struct xfs_dir2_free_hdr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert data space db to the corresponding free db.
|
||||
*/
|
||||
static xfs_dir2_db_t
|
||||
xfs_dir2_db_to_fdb(struct xfs_da_geometry *geo, xfs_dir2_db_t db)
|
||||
{
|
||||
return xfs_dir2_byte_to_db(geo, XFS_DIR2_FREE_OFFSET) +
|
||||
(db / xfs_dir2_free_max_bests(geo));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert data space db to the corresponding index in a free db.
|
||||
*/
|
||||
static int
|
||||
xfs_dir2_db_to_fdindex(struct xfs_da_geometry *geo, xfs_dir2_db_t db)
|
||||
{
|
||||
return db % xfs_dir2_free_max_bests(geo);
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_dir3_free_max_bests(struct xfs_da_geometry *geo)
|
||||
{
|
||||
return (geo->blksize - sizeof(struct xfs_dir3_free_hdr)) /
|
||||
sizeof(xfs_dir2_data_off_t);
|
||||
}
|
||||
|
||||
static __be16 *
|
||||
xfs_dir3_free_bests_p(struct xfs_dir2_free *free)
|
||||
{
|
||||
return (__be16 *)((char *)free + sizeof(struct xfs_dir3_free_hdr));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert data space db to the corresponding free db.
|
||||
*/
|
||||
static xfs_dir2_db_t
|
||||
xfs_dir3_db_to_fdb(struct xfs_da_geometry *geo, xfs_dir2_db_t db)
|
||||
{
|
||||
return xfs_dir2_byte_to_db(geo, XFS_DIR2_FREE_OFFSET) +
|
||||
(db / xfs_dir3_free_max_bests(geo));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert data space db to the corresponding index in a free db.
|
||||
*/
|
||||
static int
|
||||
xfs_dir3_db_to_fdindex(struct xfs_da_geometry *geo, xfs_dir2_db_t db)
|
||||
{
|
||||
return db % xfs_dir3_free_max_bests(geo);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_free_hdr_from_disk(
|
||||
struct xfs_dir3_icfree_hdr *to,
|
||||
struct xfs_dir2_free *from)
|
||||
{
|
||||
to->magic = be32_to_cpu(from->hdr.magic);
|
||||
to->firstdb = be32_to_cpu(from->hdr.firstdb);
|
||||
to->nvalid = be32_to_cpu(from->hdr.nvalid);
|
||||
to->nused = be32_to_cpu(from->hdr.nused);
|
||||
ASSERT(to->magic == XFS_DIR2_FREE_MAGIC);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir2_free_hdr_to_disk(
|
||||
struct xfs_dir2_free *to,
|
||||
struct xfs_dir3_icfree_hdr *from)
|
||||
{
|
||||
ASSERT(from->magic == XFS_DIR2_FREE_MAGIC);
|
||||
|
||||
to->hdr.magic = cpu_to_be32(from->magic);
|
||||
to->hdr.firstdb = cpu_to_be32(from->firstdb);
|
||||
to->hdr.nvalid = cpu_to_be32(from->nvalid);
|
||||
to->hdr.nused = cpu_to_be32(from->nused);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_free_hdr_from_disk(
|
||||
struct xfs_dir3_icfree_hdr *to,
|
||||
struct xfs_dir2_free *from)
|
||||
{
|
||||
struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)from;
|
||||
|
||||
to->magic = be32_to_cpu(hdr3->hdr.magic);
|
||||
to->firstdb = be32_to_cpu(hdr3->firstdb);
|
||||
to->nvalid = be32_to_cpu(hdr3->nvalid);
|
||||
to->nused = be32_to_cpu(hdr3->nused);
|
||||
|
||||
ASSERT(to->magic == XFS_DIR3_FREE_MAGIC);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dir3_free_hdr_to_disk(
|
||||
struct xfs_dir2_free *to,
|
||||
struct xfs_dir3_icfree_hdr *from)
|
||||
{
|
||||
struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)to;
|
||||
|
||||
ASSERT(from->magic == XFS_DIR3_FREE_MAGIC);
|
||||
|
||||
hdr3->hdr.magic = cpu_to_be32(from->magic);
|
||||
hdr3->firstdb = cpu_to_be32(from->firstdb);
|
||||
hdr3->nvalid = cpu_to_be32(from->nvalid);
|
||||
hdr3->nused = cpu_to_be32(from->nused);
|
||||
}
|
||||
|
||||
static const struct xfs_dir_ops xfs_dir2_ops = {
|
||||
.sf_entsize = xfs_dir2_sf_entsize,
|
||||
.sf_nextentry = xfs_dir2_sf_nextentry,
|
||||
.sf_get_ftype = xfs_dir2_sfe_get_ftype,
|
||||
.sf_put_ftype = xfs_dir2_sfe_put_ftype,
|
||||
.sf_get_ino = xfs_dir2_sfe_get_ino,
|
||||
.sf_put_ino = xfs_dir2_sfe_put_ino,
|
||||
.sf_get_parent_ino = xfs_dir2_sf_get_parent_ino,
|
||||
.sf_put_parent_ino = xfs_dir2_sf_put_parent_ino,
|
||||
|
||||
.data_entsize = xfs_dir2_data_entsize,
|
||||
.data_get_ftype = xfs_dir2_data_get_ftype,
|
||||
.data_put_ftype = xfs_dir2_data_put_ftype,
|
||||
.data_entry_tag_p = xfs_dir2_data_entry_tag_p,
|
||||
.data_bestfree_p = xfs_dir2_data_bestfree_p,
|
||||
|
||||
.data_dot_offset = sizeof(struct xfs_dir2_data_hdr),
|
||||
.data_dotdot_offset = sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR2_DATA_ENTSIZE(1),
|
||||
.data_first_offset = sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR2_DATA_ENTSIZE(1) +
|
||||
XFS_DIR2_DATA_ENTSIZE(2),
|
||||
.data_entry_offset = sizeof(struct xfs_dir2_data_hdr),
|
||||
|
||||
.data_dot_entry_p = xfs_dir2_data_dot_entry_p,
|
||||
.data_dotdot_entry_p = xfs_dir2_data_dotdot_entry_p,
|
||||
.data_first_entry_p = xfs_dir2_data_first_entry_p,
|
||||
.data_entry_p = xfs_dir2_data_entry_p,
|
||||
.data_unused_p = xfs_dir2_data_unused_p,
|
||||
|
||||
.leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr),
|
||||
.leaf_hdr_to_disk = xfs_dir2_leaf_hdr_to_disk,
|
||||
.leaf_hdr_from_disk = xfs_dir2_leaf_hdr_from_disk,
|
||||
.leaf_max_ents = xfs_dir2_max_leaf_ents,
|
||||
.leaf_ents_p = xfs_dir2_leaf_ents_p,
|
||||
|
||||
.node_hdr_size = sizeof(struct xfs_da_node_hdr),
|
||||
.node_hdr_to_disk = xfs_da2_node_hdr_to_disk,
|
||||
.node_hdr_from_disk = xfs_da2_node_hdr_from_disk,
|
||||
.node_tree_p = xfs_da2_node_tree_p,
|
||||
|
||||
.free_hdr_size = sizeof(struct xfs_dir2_free_hdr),
|
||||
.free_hdr_to_disk = xfs_dir2_free_hdr_to_disk,
|
||||
.free_hdr_from_disk = xfs_dir2_free_hdr_from_disk,
|
||||
.free_max_bests = xfs_dir2_free_max_bests,
|
||||
.free_bests_p = xfs_dir2_free_bests_p,
|
||||
.db_to_fdb = xfs_dir2_db_to_fdb,
|
||||
.db_to_fdindex = xfs_dir2_db_to_fdindex,
|
||||
};
|
||||
|
||||
static const struct xfs_dir_ops xfs_dir2_ftype_ops = {
|
||||
.sf_entsize = xfs_dir3_sf_entsize,
|
||||
.sf_nextentry = xfs_dir3_sf_nextentry,
|
||||
.sf_get_ftype = xfs_dir3_sfe_get_ftype,
|
||||
.sf_put_ftype = xfs_dir3_sfe_put_ftype,
|
||||
.sf_get_ino = xfs_dir3_sfe_get_ino,
|
||||
.sf_put_ino = xfs_dir3_sfe_put_ino,
|
||||
.sf_get_parent_ino = xfs_dir2_sf_get_parent_ino,
|
||||
.sf_put_parent_ino = xfs_dir2_sf_put_parent_ino,
|
||||
|
||||
.data_entsize = xfs_dir3_data_entsize,
|
||||
.data_get_ftype = xfs_dir3_data_get_ftype,
|
||||
.data_put_ftype = xfs_dir3_data_put_ftype,
|
||||
.data_entry_tag_p = xfs_dir3_data_entry_tag_p,
|
||||
.data_bestfree_p = xfs_dir2_data_bestfree_p,
|
||||
|
||||
.data_dot_offset = sizeof(struct xfs_dir2_data_hdr),
|
||||
.data_dotdot_offset = sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1),
|
||||
.data_first_offset = sizeof(struct xfs_dir2_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1) +
|
||||
XFS_DIR3_DATA_ENTSIZE(2),
|
||||
.data_entry_offset = sizeof(struct xfs_dir2_data_hdr),
|
||||
|
||||
.data_dot_entry_p = xfs_dir2_data_dot_entry_p,
|
||||
.data_dotdot_entry_p = xfs_dir2_ftype_data_dotdot_entry_p,
|
||||
.data_first_entry_p = xfs_dir2_ftype_data_first_entry_p,
|
||||
.data_entry_p = xfs_dir2_data_entry_p,
|
||||
.data_unused_p = xfs_dir2_data_unused_p,
|
||||
|
||||
.leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr),
|
||||
.leaf_hdr_to_disk = xfs_dir2_leaf_hdr_to_disk,
|
||||
.leaf_hdr_from_disk = xfs_dir2_leaf_hdr_from_disk,
|
||||
.leaf_max_ents = xfs_dir2_max_leaf_ents,
|
||||
.leaf_ents_p = xfs_dir2_leaf_ents_p,
|
||||
|
||||
.node_hdr_size = sizeof(struct xfs_da_node_hdr),
|
||||
.node_hdr_to_disk = xfs_da2_node_hdr_to_disk,
|
||||
.node_hdr_from_disk = xfs_da2_node_hdr_from_disk,
|
||||
.node_tree_p = xfs_da2_node_tree_p,
|
||||
|
||||
.free_hdr_size = sizeof(struct xfs_dir2_free_hdr),
|
||||
.free_hdr_to_disk = xfs_dir2_free_hdr_to_disk,
|
||||
.free_hdr_from_disk = xfs_dir2_free_hdr_from_disk,
|
||||
.free_max_bests = xfs_dir2_free_max_bests,
|
||||
.free_bests_p = xfs_dir2_free_bests_p,
|
||||
.db_to_fdb = xfs_dir2_db_to_fdb,
|
||||
.db_to_fdindex = xfs_dir2_db_to_fdindex,
|
||||
};
|
||||
|
||||
static const struct xfs_dir_ops xfs_dir3_ops = {
|
||||
.sf_entsize = xfs_dir3_sf_entsize,
|
||||
.sf_nextentry = xfs_dir3_sf_nextentry,
|
||||
.sf_get_ftype = xfs_dir3_sfe_get_ftype,
|
||||
.sf_put_ftype = xfs_dir3_sfe_put_ftype,
|
||||
.sf_get_ino = xfs_dir3_sfe_get_ino,
|
||||
.sf_put_ino = xfs_dir3_sfe_put_ino,
|
||||
.sf_get_parent_ino = xfs_dir2_sf_get_parent_ino,
|
||||
.sf_put_parent_ino = xfs_dir2_sf_put_parent_ino,
|
||||
|
||||
.data_entsize = xfs_dir3_data_entsize,
|
||||
.data_get_ftype = xfs_dir3_data_get_ftype,
|
||||
.data_put_ftype = xfs_dir3_data_put_ftype,
|
||||
.data_entry_tag_p = xfs_dir3_data_entry_tag_p,
|
||||
.data_bestfree_p = xfs_dir3_data_bestfree_p,
|
||||
|
||||
.data_dot_offset = sizeof(struct xfs_dir3_data_hdr),
|
||||
.data_dotdot_offset = sizeof(struct xfs_dir3_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1),
|
||||
.data_first_offset = sizeof(struct xfs_dir3_data_hdr) +
|
||||
XFS_DIR3_DATA_ENTSIZE(1) +
|
||||
XFS_DIR3_DATA_ENTSIZE(2),
|
||||
.data_entry_offset = sizeof(struct xfs_dir3_data_hdr),
|
||||
|
||||
.data_dot_entry_p = xfs_dir3_data_dot_entry_p,
|
||||
.data_dotdot_entry_p = xfs_dir3_data_dotdot_entry_p,
|
||||
.data_first_entry_p = xfs_dir3_data_first_entry_p,
|
||||
.data_entry_p = xfs_dir3_data_entry_p,
|
||||
.data_unused_p = xfs_dir3_data_unused_p,
|
||||
|
||||
.leaf_hdr_size = sizeof(struct xfs_dir3_leaf_hdr),
|
||||
.leaf_hdr_to_disk = xfs_dir3_leaf_hdr_to_disk,
|
||||
.leaf_hdr_from_disk = xfs_dir3_leaf_hdr_from_disk,
|
||||
.leaf_max_ents = xfs_dir3_max_leaf_ents,
|
||||
.leaf_ents_p = xfs_dir3_leaf_ents_p,
|
||||
|
||||
.node_hdr_size = sizeof(struct xfs_da3_node_hdr),
|
||||
.node_hdr_to_disk = xfs_da3_node_hdr_to_disk,
|
||||
.node_hdr_from_disk = xfs_da3_node_hdr_from_disk,
|
||||
.node_tree_p = xfs_da3_node_tree_p,
|
||||
|
||||
.free_hdr_size = sizeof(struct xfs_dir3_free_hdr),
|
||||
.free_hdr_to_disk = xfs_dir3_free_hdr_to_disk,
|
||||
.free_hdr_from_disk = xfs_dir3_free_hdr_from_disk,
|
||||
.free_max_bests = xfs_dir3_free_max_bests,
|
||||
.free_bests_p = xfs_dir3_free_bests_p,
|
||||
.db_to_fdb = xfs_dir3_db_to_fdb,
|
||||
.db_to_fdindex = xfs_dir3_db_to_fdindex,
|
||||
};
|
||||
|
||||
static const struct xfs_dir_ops xfs_dir2_nondir_ops = {
|
||||
.node_hdr_size = sizeof(struct xfs_da_node_hdr),
|
||||
.node_hdr_to_disk = xfs_da2_node_hdr_to_disk,
|
||||
.node_hdr_from_disk = xfs_da2_node_hdr_from_disk,
|
||||
.node_tree_p = xfs_da2_node_tree_p,
|
||||
};
|
||||
|
||||
static const struct xfs_dir_ops xfs_dir3_nondir_ops = {
|
||||
.node_hdr_size = sizeof(struct xfs_da3_node_hdr),
|
||||
.node_hdr_to_disk = xfs_da3_node_hdr_to_disk,
|
||||
.node_hdr_from_disk = xfs_da3_node_hdr_from_disk,
|
||||
.node_tree_p = xfs_da3_node_tree_p,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return the ops structure according to the current config. If we are passed
|
||||
* an inode, then that overrides the default config we use which is based on
|
||||
* feature bits.
|
||||
*/
|
||||
const struct xfs_dir_ops *
|
||||
xfs_dir_get_ops(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_inode *dp)
|
||||
{
|
||||
if (dp)
|
||||
return dp->d_ops;
|
||||
if (mp->m_dir_inode_ops)
|
||||
return mp->m_dir_inode_ops;
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return &xfs_dir3_ops;
|
||||
if (xfs_sb_version_hasftype(&mp->m_sb))
|
||||
return &xfs_dir2_ftype_ops;
|
||||
return &xfs_dir2_ops;
|
||||
}
|
||||
|
||||
const struct xfs_dir_ops *
|
||||
xfs_nondir_get_ops(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_inode *dp)
|
||||
{
|
||||
if (dp)
|
||||
return dp->d_ops;
|
||||
if (mp->m_nondir_inode_ops)
|
||||
return mp->m_nondir_inode_ops;
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return &xfs_dir3_nondir_ops;
|
||||
return &xfs_dir2_nondir_ops;
|
||||
}
|
762
fs/xfs/libxfs/xfs_dir2.c
Normal file
762
fs/xfs/libxfs/xfs_dir2.c
Normal file
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
* 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_inum.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_da_format.h"
|
||||
#include "xfs_da_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_inode_item.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "xfs_dir2.h"
|
||||
#include "xfs_dir2_priv.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_dinode.h"
|
||||
|
||||
struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };
|
||||
|
||||
|
||||
/*
|
||||
* ASCII case-insensitive (ie. A-Z) support for directories that was
|
||||
* used in IRIX.
|
||||
*/
|
||||
STATIC xfs_dahash_t
|
||||
xfs_ascii_ci_hashname(
|
||||
struct xfs_name *name)
|
||||
{
|
||||
xfs_dahash_t hash;
|
||||
int i;
|
||||
|
||||
for (i = 0, hash = 0; i < name->len; i++)
|
||||
hash = tolower(name->name[i]) ^ rol32(hash, 7);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
STATIC enum xfs_dacmp
|
||||
xfs_ascii_ci_compname(
|
||||
struct xfs_da_args *args,
|
||||
const unsigned char *name,
|
||||
int len)
|
||||
{
|
||||
enum xfs_dacmp result;
|
||||
int i;
|
||||
|
||||
if (args->namelen != len)
|
||||
return XFS_CMP_DIFFERENT;
|
||||
|
||||
result = XFS_CMP_EXACT;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (args->name[i] == name[i])
|
||||
continue;
|
||||
if (tolower(args->name[i]) != tolower(name[i]))
|
||||
return XFS_CMP_DIFFERENT;
|
||||
result = XFS_CMP_CASE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct xfs_nameops xfs_ascii_ci_nameops = {
|
||||
.hashname = xfs_ascii_ci_hashname,
|
||||
.compname = xfs_ascii_ci_compname,
|
||||
};
|
||||
|
||||
int
|
||||
xfs_da_mount(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
struct xfs_da_geometry *dageo;
|
||||
int nodehdr_size;
|
||||
|
||||
|
||||
ASSERT(mp->m_sb.sb_versionnum & XFS_SB_VERSION_DIRV2BIT);
|
||||
ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <=
|
||||
XFS_MAX_BLOCKSIZE);
|
||||
|
||||
mp->m_dir_inode_ops = xfs_dir_get_ops(mp, NULL);
|
||||
mp->m_nondir_inode_ops = xfs_nondir_get_ops(mp, NULL);
|
||||
|
||||
nodehdr_size = mp->m_dir_inode_ops->node_hdr_size;
|
||||
mp->m_dir_geo = kmem_zalloc(sizeof(struct xfs_da_geometry),
|
||||
KM_SLEEP | KM_MAYFAIL);
|
||||
mp->m_attr_geo = kmem_zalloc(sizeof(struct xfs_da_geometry),
|
||||
KM_SLEEP | KM_MAYFAIL);
|
||||
if (!mp->m_dir_geo || !mp->m_attr_geo) {
|
||||
kmem_free(mp->m_dir_geo);
|
||||
kmem_free(mp->m_attr_geo);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* set up directory geometry */
|
||||
dageo = mp->m_dir_geo;
|
||||
dageo->blklog = mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog;
|
||||
dageo->fsblog = mp->m_sb.sb_blocklog;
|
||||
dageo->blksize = 1 << dageo->blklog;
|
||||
dageo->fsbcount = 1 << mp->m_sb.sb_dirblklog;
|
||||
|
||||
/*
|
||||
* Now we've set up the block conversion variables, we can calculate the
|
||||
* segment block constants using the geometry structure.
|
||||
*/
|
||||
dageo->datablk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_DATA_OFFSET);
|
||||
dageo->leafblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_LEAF_OFFSET);
|
||||
dageo->freeblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_FREE_OFFSET);
|
||||
dageo->node_ents = (dageo->blksize - nodehdr_size) /
|
||||
(uint)sizeof(xfs_da_node_entry_t);
|
||||
dageo->magicpct = (dageo->blksize * 37) / 100;
|
||||
|
||||
/* set up attribute geometry - single fsb only */
|
||||
dageo = mp->m_attr_geo;
|
||||
dageo->blklog = mp->m_sb.sb_blocklog;
|
||||
dageo->fsblog = mp->m_sb.sb_blocklog;
|
||||
dageo->blksize = 1 << dageo->blklog;
|
||||
dageo->fsbcount = 1;
|
||||
dageo->node_ents = (dageo->blksize - nodehdr_size) /
|
||||
(uint)sizeof(xfs_da_node_entry_t);
|
||||
dageo->magicpct = (dageo->blksize * 37) / 100;
|
||||
|
||||
if (xfs_sb_version_hasasciici(&mp->m_sb))
|
||||
mp->m_dirnameops = &xfs_ascii_ci_nameops;
|
||||
else
|
||||
mp->m_dirnameops = &xfs_default_nameops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
xfs_da_unmount(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
kmem_free(mp->m_dir_geo);
|
||||
kmem_free(mp->m_attr_geo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1 if directory contains only "." and "..".
|
||||
*/
|
||||
int
|
||||
xfs_dir_isempty(
|
||||
xfs_inode_t *dp)
|
||||
{
|
||||
xfs_dir2_sf_hdr_t *sfp;
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
if (dp->i_d.di_size == 0) /* might happen during shutdown. */
|
||||
return 1;
|
||||
if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
|
||||
return 0;
|
||||
sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
|
||||
return !sfp->count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate a given inode number.
|
||||
*/
|
||||
int
|
||||
xfs_dir_ino_validate(
|
||||
xfs_mount_t *mp,
|
||||
xfs_ino_t ino)
|
||||
{
|
||||
xfs_agblock_t agblkno;
|
||||
xfs_agino_t agino;
|
||||
xfs_agnumber_t agno;
|
||||
int ino_ok;
|
||||
int ioff;
|
||||
|
||||
agno = XFS_INO_TO_AGNO(mp, ino);
|
||||
agblkno = XFS_INO_TO_AGBNO(mp, ino);
|
||||
ioff = XFS_INO_TO_OFFSET(mp, ino);
|
||||
agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff);
|
||||
ino_ok =
|
||||
agno < mp->m_sb.sb_agcount &&
|
||||
agblkno < mp->m_sb.sb_agblocks &&
|
||||
agblkno != 0 &&
|
||||
ioff < (1 << mp->m_sb.sb_inopblog) &&
|
||||
XFS_AGINO_TO_INO(mp, agno, agino) == ino;
|
||||
if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE,
|
||||
XFS_RANDOM_DIR_INO_VALIDATE))) {
|
||||
xfs_warn(mp, "Invalid inode number 0x%Lx",
|
||||
(unsigned long long) ino);
|
||||
XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
|
||||
return EFSCORRUPTED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a directory with its "." and ".." entries.
|
||||
*/
|
||||
int
|
||||
xfs_dir_init(
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *dp,
|
||||
xfs_inode_t *pdp)
|
||||
{
|
||||
struct xfs_da_args *args;
|
||||
int error;
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
|
||||
if (!args)
|
||||
return ENOMEM;
|
||||
|
||||
args->geo = dp->i_mount->m_dir_geo;
|
||||
args->dp = dp;
|
||||
args->trans = tp;
|
||||
error = xfs_dir2_sf_create(args, pdp->i_ino);
|
||||
kmem_free(args);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
Enter a name in a directory.
|
||||
*/
|
||||
int
|
||||
xfs_dir_createname(
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *dp,
|
||||
struct xfs_name *name,
|
||||
xfs_ino_t inum, /* new entry inode number */
|
||||
xfs_fsblock_t *first, /* bmap's firstblock */
|
||||
xfs_bmap_free_t *flist, /* bmap's freeblock list */
|
||||
xfs_extlen_t total) /* bmap's total block count */
|
||||
{
|
||||
struct xfs_da_args *args;
|
||||
int rval;
|
||||
int v; /* type-checking value */
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
rval = xfs_dir_ino_validate(tp->t_mountp, inum);
|
||||
if (rval)
|
||||
return rval;
|
||||
XFS_STATS_INC(xs_dir_create);
|
||||
|
||||
args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
|
||||
if (!args)
|
||||
return ENOMEM;
|
||||
|
||||
args->geo = dp->i_mount->m_dir_geo;
|
||||
args->name = name->name;
|
||||
args->namelen = name->len;
|
||||
args->filetype = name->type;
|
||||
args->hashval = dp->i_mount->m_dirnameops->hashname(name);
|
||||
args->inumber = inum;
|
||||
args->dp = dp;
|
||||
args->firstblock = first;
|
||||
args->flist = flist;
|
||||
args->total = total;
|
||||
args->whichfork = XFS_DATA_FORK;
|
||||
args->trans = tp;
|
||||
args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
|
||||
|
||||
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
|
||||
rval = xfs_dir2_sf_addname(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isblock(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v) {
|
||||
rval = xfs_dir2_block_addname(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isleaf(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v)
|
||||
rval = xfs_dir2_leaf_addname(args);
|
||||
else
|
||||
rval = xfs_dir2_node_addname(args);
|
||||
|
||||
out_free:
|
||||
kmem_free(args);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* If doing a CI lookup and case-insensitive match, dup actual name into
|
||||
* args.value. Return EEXIST for success (ie. name found) or an error.
|
||||
*/
|
||||
int
|
||||
xfs_dir_cilookup_result(
|
||||
struct xfs_da_args *args,
|
||||
const unsigned char *name,
|
||||
int len)
|
||||
{
|
||||
if (args->cmpresult == XFS_CMP_DIFFERENT)
|
||||
return ENOENT;
|
||||
if (args->cmpresult != XFS_CMP_CASE ||
|
||||
!(args->op_flags & XFS_DA_OP_CILOOKUP))
|
||||
return EEXIST;
|
||||
|
||||
args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL);
|
||||
if (!args->value)
|
||||
return ENOMEM;
|
||||
|
||||
memcpy(args->value, name, len);
|
||||
args->valuelen = len;
|
||||
return EEXIST;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup a name in a directory, give back the inode number.
|
||||
* If ci_name is not NULL, returns the actual name in ci_name if it differs
|
||||
* to name, or ci_name->name is set to NULL for an exact match.
|
||||
*/
|
||||
|
||||
int
|
||||
xfs_dir_lookup(
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *dp,
|
||||
struct xfs_name *name,
|
||||
xfs_ino_t *inum, /* out: inode number */
|
||||
struct xfs_name *ci_name) /* out: actual name if CI match */
|
||||
{
|
||||
struct xfs_da_args *args;
|
||||
int rval;
|
||||
int v; /* type-checking value */
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
XFS_STATS_INC(xs_dir_lookup);
|
||||
|
||||
/*
|
||||
* We need to use KM_NOFS here so that lockdep will not throw false
|
||||
* positive deadlock warnings on a non-transactional lookup path. It is
|
||||
* safe to recurse into inode recalim in that case, but lockdep can't
|
||||
* easily be taught about it. Hence KM_NOFS avoids having to add more
|
||||
* lockdep Doing this avoids having to add a bunch of lockdep class
|
||||
* annotations into the reclaim path for the ilock.
|
||||
*/
|
||||
args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
|
||||
args->geo = dp->i_mount->m_dir_geo;
|
||||
args->name = name->name;
|
||||
args->namelen = name->len;
|
||||
args->filetype = name->type;
|
||||
args->hashval = dp->i_mount->m_dirnameops->hashname(name);
|
||||
args->dp = dp;
|
||||
args->whichfork = XFS_DATA_FORK;
|
||||
args->trans = tp;
|
||||
args->op_flags = XFS_DA_OP_OKNOENT;
|
||||
if (ci_name)
|
||||
args->op_flags |= XFS_DA_OP_CILOOKUP;
|
||||
|
||||
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
|
||||
rval = xfs_dir2_sf_lookup(args);
|
||||
goto out_check_rval;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isblock(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v) {
|
||||
rval = xfs_dir2_block_lookup(args);
|
||||
goto out_check_rval;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isleaf(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v)
|
||||
rval = xfs_dir2_leaf_lookup(args);
|
||||
else
|
||||
rval = xfs_dir2_node_lookup(args);
|
||||
|
||||
out_check_rval:
|
||||
if (rval == EEXIST)
|
||||
rval = 0;
|
||||
if (!rval) {
|
||||
*inum = args->inumber;
|
||||
if (ci_name) {
|
||||
ci_name->name = args->value;
|
||||
ci_name->len = args->valuelen;
|
||||
}
|
||||
}
|
||||
out_free:
|
||||
kmem_free(args);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove an entry from a directory.
|
||||
*/
|
||||
int
|
||||
xfs_dir_removename(
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *dp,
|
||||
struct xfs_name *name,
|
||||
xfs_ino_t ino,
|
||||
xfs_fsblock_t *first, /* bmap's firstblock */
|
||||
xfs_bmap_free_t *flist, /* bmap's freeblock list */
|
||||
xfs_extlen_t total) /* bmap's total block count */
|
||||
{
|
||||
struct xfs_da_args *args;
|
||||
int rval;
|
||||
int v; /* type-checking value */
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
XFS_STATS_INC(xs_dir_remove);
|
||||
|
||||
args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
|
||||
if (!args)
|
||||
return ENOMEM;
|
||||
|
||||
args->geo = dp->i_mount->m_dir_geo;
|
||||
args->name = name->name;
|
||||
args->namelen = name->len;
|
||||
args->filetype = name->type;
|
||||
args->hashval = dp->i_mount->m_dirnameops->hashname(name);
|
||||
args->inumber = ino;
|
||||
args->dp = dp;
|
||||
args->firstblock = first;
|
||||
args->flist = flist;
|
||||
args->total = total;
|
||||
args->whichfork = XFS_DATA_FORK;
|
||||
args->trans = tp;
|
||||
|
||||
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
|
||||
rval = xfs_dir2_sf_removename(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isblock(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v) {
|
||||
rval = xfs_dir2_block_removename(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isleaf(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v)
|
||||
rval = xfs_dir2_leaf_removename(args);
|
||||
else
|
||||
rval = xfs_dir2_node_removename(args);
|
||||
out_free:
|
||||
kmem_free(args);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace the inode number of a directory entry.
|
||||
*/
|
||||
int
|
||||
xfs_dir_replace(
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *dp,
|
||||
struct xfs_name *name, /* name of entry to replace */
|
||||
xfs_ino_t inum, /* new inode number */
|
||||
xfs_fsblock_t *first, /* bmap's firstblock */
|
||||
xfs_bmap_free_t *flist, /* bmap's freeblock list */
|
||||
xfs_extlen_t total) /* bmap's total block count */
|
||||
{
|
||||
struct xfs_da_args *args;
|
||||
int rval;
|
||||
int v; /* type-checking value */
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
|
||||
rval = xfs_dir_ino_validate(tp->t_mountp, inum);
|
||||
if (rval)
|
||||
return rval;
|
||||
|
||||
args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
|
||||
if (!args)
|
||||
return ENOMEM;
|
||||
|
||||
args->geo = dp->i_mount->m_dir_geo;
|
||||
args->name = name->name;
|
||||
args->namelen = name->len;
|
||||
args->filetype = name->type;
|
||||
args->hashval = dp->i_mount->m_dirnameops->hashname(name);
|
||||
args->inumber = inum;
|
||||
args->dp = dp;
|
||||
args->firstblock = first;
|
||||
args->flist = flist;
|
||||
args->total = total;
|
||||
args->whichfork = XFS_DATA_FORK;
|
||||
args->trans = tp;
|
||||
|
||||
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
|
||||
rval = xfs_dir2_sf_replace(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isblock(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v) {
|
||||
rval = xfs_dir2_block_replace(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isleaf(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v)
|
||||
rval = xfs_dir2_leaf_replace(args);
|
||||
else
|
||||
rval = xfs_dir2_node_replace(args);
|
||||
out_free:
|
||||
kmem_free(args);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if this entry can be added to the directory without allocating space.
|
||||
* First checks that the caller couldn't reserve enough space (resblks = 0).
|
||||
*/
|
||||
int
|
||||
xfs_dir_canenter(
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *dp,
|
||||
struct xfs_name *name, /* name of entry to add */
|
||||
uint resblks)
|
||||
{
|
||||
struct xfs_da_args *args;
|
||||
int rval;
|
||||
int v; /* type-checking value */
|
||||
|
||||
if (resblks)
|
||||
return 0;
|
||||
|
||||
ASSERT(S_ISDIR(dp->i_d.di_mode));
|
||||
|
||||
args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
|
||||
if (!args)
|
||||
return ENOMEM;
|
||||
|
||||
args->geo = dp->i_mount->m_dir_geo;
|
||||
args->name = name->name;
|
||||
args->namelen = name->len;
|
||||
args->filetype = name->type;
|
||||
args->hashval = dp->i_mount->m_dirnameops->hashname(name);
|
||||
args->dp = dp;
|
||||
args->whichfork = XFS_DATA_FORK;
|
||||
args->trans = tp;
|
||||
args->op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
|
||||
XFS_DA_OP_OKNOENT;
|
||||
|
||||
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
|
||||
rval = xfs_dir2_sf_addname(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isblock(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v) {
|
||||
rval = xfs_dir2_block_addname(args);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rval = xfs_dir2_isleaf(args, &v);
|
||||
if (rval)
|
||||
goto out_free;
|
||||
if (v)
|
||||
rval = xfs_dir2_leaf_addname(args);
|
||||
else
|
||||
rval = xfs_dir2_node_addname(args);
|
||||
out_free:
|
||||
kmem_free(args);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility routines.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add a block to the directory.
|
||||
*
|
||||
* This routine is for data and free blocks, not leaf/node blocks which are
|
||||
* handled by xfs_da_grow_inode.
|
||||
*/
|
||||
int
|
||||
xfs_dir2_grow_inode(
|
||||
struct xfs_da_args *args,
|
||||
int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */
|
||||
xfs_dir2_db_t *dbp) /* out: block number added */
|
||||
{
|
||||
struct xfs_inode *dp = args->dp;
|
||||
struct xfs_mount *mp = dp->i_mount;
|
||||
xfs_fileoff_t bno; /* directory offset of new block */
|
||||
int count; /* count of filesystem blocks */
|
||||
int error;
|
||||
|
||||
trace_xfs_dir2_grow_inode(args, space);
|
||||
|
||||
/*
|
||||
* Set lowest possible block in the space requested.
|
||||
*/
|
||||
bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE);
|
||||
count = args->geo->fsbcount;
|
||||
|
||||
error = xfs_da_grow_inode_int(args, &bno, count);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
*dbp = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)bno);
|
||||
|
||||
/*
|
||||
* Update file's size if this is the data space and it grew.
|
||||
*/
|
||||
if (space == XFS_DIR2_DATA_SPACE) {
|
||||
xfs_fsize_t size; /* directory file (data) size */
|
||||
|
||||
size = XFS_FSB_TO_B(mp, bno + count);
|
||||
if (size > dp->i_d.di_size) {
|
||||
dp->i_d.di_size = size;
|
||||
xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if the directory is a single-block form directory.
|
||||
*/
|
||||
int
|
||||
xfs_dir2_isblock(
|
||||
struct xfs_da_args *args,
|
||||
int *vp) /* out: 1 is block, 0 is not block */
|
||||
{
|
||||
xfs_fileoff_t last; /* last file offset */
|
||||
int rval;
|
||||
|
||||
if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
|
||||
return rval;
|
||||
rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize;
|
||||
ASSERT(rval == 0 || args->dp->i_d.di_size == args->geo->blksize);
|
||||
*vp = rval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if the directory is a single-leaf form directory.
|
||||
*/
|
||||
int
|
||||
xfs_dir2_isleaf(
|
||||
struct xfs_da_args *args,
|
||||
int *vp) /* out: 1 is block, 0 is not block */
|
||||
{
|
||||
xfs_fileoff_t last; /* last file offset */
|
||||
int rval;
|
||||
|
||||
if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
|
||||
return rval;
|
||||
*vp = last == args->geo->leafblk + args->geo->fsbcount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the given block from the directory.
|
||||
* This routine is used for data and free blocks, leaf/node are done
|
||||
* by xfs_da_shrink_inode.
|
||||
*/
|
||||
int
|
||||
xfs_dir2_shrink_inode(
|
||||
xfs_da_args_t *args,
|
||||
xfs_dir2_db_t db,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
xfs_fileoff_t bno; /* directory file offset */
|
||||
xfs_dablk_t da; /* directory file offset */
|
||||
int done; /* bunmap is finished */
|
||||
xfs_inode_t *dp;
|
||||
int error;
|
||||
xfs_mount_t *mp;
|
||||
xfs_trans_t *tp;
|
||||
|
||||
trace_xfs_dir2_shrink_inode(args, db);
|
||||
|
||||
dp = args->dp;
|
||||
mp = dp->i_mount;
|
||||
tp = args->trans;
|
||||
da = xfs_dir2_db_to_da(args->geo, db);
|
||||
/*
|
||||
* Unmap the fsblock(s).
|
||||
*/
|
||||
if ((error = xfs_bunmapi(tp, dp, da, args->geo->fsbcount,
|
||||
XFS_BMAPI_METADATA, 0, args->firstblock, args->flist,
|
||||
&done))) {
|
||||
/*
|
||||
* ENOSPC actually can happen if we're in a removename with
|
||||
* no space reservation, and the resulting block removal
|
||||
* would cause a bmap btree split or conversion from extents
|
||||
* to btree. This can only happen for un-fragmented
|
||||
* directory blocks, since you need to be punching out
|
||||
* the middle of an extent.
|
||||
* In this case we need to leave the block in the file,
|
||||
* and not binval it.
|
||||
* So the block has to be in a consistent empty state
|
||||
* and appropriately logged.
|
||||
* We don't free up the buffer, the caller can tell it
|
||||
* hasn't happened since it got an error back.
|
||||
*/
|
||||
return error;
|
||||
}
|
||||
ASSERT(done);
|
||||
/*
|
||||
* Invalidate the buffer from the transaction.
|
||||
*/
|
||||
xfs_trans_binval(tp, bp);
|
||||
/*
|
||||
* If it's not a data block, we're done.
|
||||
*/
|
||||
if (db >= xfs_dir2_byte_to_db(args->geo, XFS_DIR2_LEAF_OFFSET))
|
||||
return 0;
|
||||
/*
|
||||
* If the block isn't the last one in the directory, we're done.
|
||||
*/
|
||||
if (dp->i_d.di_size > xfs_dir2_db_off_to_byte(args->geo, db + 1, 0))
|
||||
return 0;
|
||||
bno = da;
|
||||
if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) {
|
||||
/*
|
||||
* This can't really happen unless there's kernel corruption.
|
||||
*/
|
||||
return error;
|
||||
}
|
||||
if (db == args->geo->datablk)
|
||||
ASSERT(bno == 0);
|
||||
else
|
||||
ASSERT(bno > 0);
|
||||
/*
|
||||
* Set the size to the new last block.
|
||||
*/
|
||||
dp->i_d.di_size = XFS_FSB_TO_B(mp, bno);
|
||||
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
|
||||
return 0;
|
||||
}
|
1265
fs/xfs/libxfs/xfs_dir2_block.c
Normal file
1265
fs/xfs/libxfs/xfs_dir2_block.c
Normal file
File diff suppressed because it is too large
Load Diff
1050
fs/xfs/libxfs/xfs_dir2_data.c
Normal file
1050
fs/xfs/libxfs/xfs_dir2_data.c
Normal file
File diff suppressed because it is too large
Load Diff
1831
fs/xfs/libxfs/xfs_dir2_leaf.c
Normal file
1831
fs/xfs/libxfs/xfs_dir2_leaf.c
Normal file
File diff suppressed because it is too large
Load Diff
2284
fs/xfs/libxfs/xfs_dir2_node.c
Normal file
2284
fs/xfs/libxfs/xfs_dir2_node.c
Normal file
File diff suppressed because it is too large
Load Diff
274
fs/xfs/libxfs/xfs_dir2_priv.h
Normal file
274
fs/xfs/libxfs/xfs_dir2_priv.h
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
#ifndef __XFS_DIR2_PRIV_H__
|
||||
#define __XFS_DIR2_PRIV_H__
|
||||
|
||||
struct dir_context;
|
||||
|
||||
/*
|
||||
* Directory offset/block conversion functions.
|
||||
*
|
||||
* DB blocks here are logical directory block numbers, not filesystem blocks.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convert dataptr to byte in file space
|
||||
*/
|
||||
static inline xfs_dir2_off_t
|
||||
xfs_dir2_dataptr_to_byte(xfs_dir2_dataptr_t dp)
|
||||
{
|
||||
return (xfs_dir2_off_t)dp << XFS_DIR2_DATA_ALIGN_LOG;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert byte in file space to dataptr. It had better be aligned.
|
||||
*/
|
||||
static inline xfs_dir2_dataptr_t
|
||||
xfs_dir2_byte_to_dataptr(xfs_dir2_off_t by)
|
||||
{
|
||||
return (xfs_dir2_dataptr_t)(by >> XFS_DIR2_DATA_ALIGN_LOG);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert byte in space to (DB) block
|
||||
*/
|
||||
static inline xfs_dir2_db_t
|
||||
xfs_dir2_byte_to_db(struct xfs_da_geometry *geo, xfs_dir2_off_t by)
|
||||
{
|
||||
return (xfs_dir2_db_t)(by >> geo->blklog);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert dataptr to a block number
|
||||
*/
|
||||
static inline xfs_dir2_db_t
|
||||
xfs_dir2_dataptr_to_db(struct xfs_da_geometry *geo, xfs_dir2_dataptr_t dp)
|
||||
{
|
||||
return xfs_dir2_byte_to_db(geo, xfs_dir2_dataptr_to_byte(dp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert byte in space to offset in a block
|
||||
*/
|
||||
static inline xfs_dir2_data_aoff_t
|
||||
xfs_dir2_byte_to_off(struct xfs_da_geometry *geo, xfs_dir2_off_t by)
|
||||
{
|
||||
return (xfs_dir2_data_aoff_t)(by & (geo->blksize - 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert dataptr to a byte offset in a block
|
||||
*/
|
||||
static inline xfs_dir2_data_aoff_t
|
||||
xfs_dir2_dataptr_to_off(struct xfs_da_geometry *geo, xfs_dir2_dataptr_t dp)
|
||||
{
|
||||
return xfs_dir2_byte_to_off(geo, xfs_dir2_dataptr_to_byte(dp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert block and offset to byte in space
|
||||
*/
|
||||
static inline xfs_dir2_off_t
|
||||
xfs_dir2_db_off_to_byte(struct xfs_da_geometry *geo, xfs_dir2_db_t db,
|
||||
xfs_dir2_data_aoff_t o)
|
||||
{
|
||||
return ((xfs_dir2_off_t)db << geo->blklog) + o;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert block (DB) to block (dablk)
|
||||
*/
|
||||
static inline xfs_dablk_t
|
||||
xfs_dir2_db_to_da(struct xfs_da_geometry *geo, xfs_dir2_db_t db)
|
||||
{
|
||||
return (xfs_dablk_t)(db << (geo->blklog - geo->fsblog));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert byte in space to (DA) block
|
||||
*/
|
||||
static inline xfs_dablk_t
|
||||
xfs_dir2_byte_to_da(struct xfs_da_geometry *geo, xfs_dir2_off_t by)
|
||||
{
|
||||
return xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, by));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert block and offset to dataptr
|
||||
*/
|
||||
static inline xfs_dir2_dataptr_t
|
||||
xfs_dir2_db_off_to_dataptr(struct xfs_da_geometry *geo, xfs_dir2_db_t db,
|
||||
xfs_dir2_data_aoff_t o)
|
||||
{
|
||||
return xfs_dir2_byte_to_dataptr(xfs_dir2_db_off_to_byte(geo, db, o));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert block (dablk) to block (DB)
|
||||
*/
|
||||
static inline xfs_dir2_db_t
|
||||
xfs_dir2_da_to_db(struct xfs_da_geometry *geo, xfs_dablk_t da)
|
||||
{
|
||||
return (xfs_dir2_db_t)(da >> (geo->blklog - geo->fsblog));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert block (dablk) to byte offset in space
|
||||
*/
|
||||
static inline xfs_dir2_off_t
|
||||
xfs_dir2_da_to_byte(struct xfs_da_geometry *geo, xfs_dablk_t da)
|
||||
{
|
||||
return xfs_dir2_db_off_to_byte(geo, xfs_dir2_da_to_db(geo, da), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Directory tail pointer accessor functions. Based on block geometry.
|
||||
*/
|
||||
static inline struct xfs_dir2_block_tail *
|
||||
xfs_dir2_block_tail_p(struct xfs_da_geometry *geo, struct xfs_dir2_data_hdr *hdr)
|
||||
{
|
||||
return ((struct xfs_dir2_block_tail *)
|
||||
((char *)hdr + geo->blksize)) - 1;
|
||||
}
|
||||
|
||||
static inline struct xfs_dir2_leaf_tail *
|
||||
xfs_dir2_leaf_tail_p(struct xfs_da_geometry *geo, struct xfs_dir2_leaf *lp)
|
||||
{
|
||||
return (struct xfs_dir2_leaf_tail *)
|
||||
((char *)lp + geo->blksize -
|
||||
sizeof(struct xfs_dir2_leaf_tail));
|
||||
}
|
||||
|
||||
/* xfs_dir2.c */
|
||||
extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
|
||||
extern int xfs_dir2_grow_inode(struct xfs_da_args *args, int space,
|
||||
xfs_dir2_db_t *dbp);
|
||||
extern int xfs_dir_cilookup_result(struct xfs_da_args *args,
|
||||
const unsigned char *name, int len);
|
||||
|
||||
#define S_SHIFT 12
|
||||
extern const unsigned char xfs_mode_to_ftype[];
|
||||
|
||||
extern unsigned char xfs_dir3_get_dtype(struct xfs_mount *mp,
|
||||
__uint8_t filetype);
|
||||
|
||||
|
||||
/* xfs_dir2_block.c */
|
||||
extern int xfs_dir3_block_read(struct xfs_trans *tp, struct xfs_inode *dp,
|
||||
struct xfs_buf **bpp);
|
||||
extern int xfs_dir2_block_addname(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_block_lookup(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_block_removename(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_block_replace(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_leaf_to_block(struct xfs_da_args *args,
|
||||
struct xfs_buf *lbp, struct xfs_buf *dbp);
|
||||
|
||||
/* xfs_dir2_data.c */
|
||||
#ifdef DEBUG
|
||||
#define xfs_dir3_data_check(dp,bp) __xfs_dir3_data_check(dp, bp);
|
||||
#else
|
||||
#define xfs_dir3_data_check(dp,bp)
|
||||
#endif
|
||||
|
||||
extern int __xfs_dir3_data_check(struct xfs_inode *dp, struct xfs_buf *bp);
|
||||
extern int xfs_dir3_data_read(struct xfs_trans *tp, struct xfs_inode *dp,
|
||||
xfs_dablk_t bno, xfs_daddr_t mapped_bno, struct xfs_buf **bpp);
|
||||
extern int xfs_dir3_data_readahead(struct xfs_inode *dp, xfs_dablk_t bno,
|
||||
xfs_daddr_t mapped_bno);
|
||||
|
||||
extern struct xfs_dir2_data_free *
|
||||
xfs_dir2_data_freeinsert(struct xfs_dir2_data_hdr *hdr,
|
||||
struct xfs_dir2_data_free *bf, struct xfs_dir2_data_unused *dup,
|
||||
int *loghead);
|
||||
extern int xfs_dir3_data_init(struct xfs_da_args *args, xfs_dir2_db_t blkno,
|
||||
struct xfs_buf **bpp);
|
||||
|
||||
/* xfs_dir2_leaf.c */
|
||||
extern int xfs_dir3_leafn_read(struct xfs_trans *tp, struct xfs_inode *dp,
|
||||
xfs_dablk_t fbno, xfs_daddr_t mappedbno, struct xfs_buf **bpp);
|
||||
extern int xfs_dir2_block_to_leaf(struct xfs_da_args *args,
|
||||
struct xfs_buf *dbp);
|
||||
extern int xfs_dir2_leaf_addname(struct xfs_da_args *args);
|
||||
extern void xfs_dir3_leaf_compact(struct xfs_da_args *args,
|
||||
struct xfs_dir3_icleaf_hdr *leafhdr, struct xfs_buf *bp);
|
||||
extern void xfs_dir3_leaf_compact_x1(struct xfs_dir3_icleaf_hdr *leafhdr,
|
||||
struct xfs_dir2_leaf_entry *ents, int *indexp,
|
||||
int *lowstalep, int *highstalep, int *lowlogp, int *highlogp);
|
||||
extern int xfs_dir3_leaf_get_buf(struct xfs_da_args *args, xfs_dir2_db_t bno,
|
||||
struct xfs_buf **bpp, __uint16_t magic);
|
||||
extern void xfs_dir3_leaf_log_ents(struct xfs_da_args *args,
|
||||
struct xfs_buf *bp, int first, int last);
|
||||
extern void xfs_dir3_leaf_log_header(struct xfs_da_args *args,
|
||||
struct xfs_buf *bp);
|
||||
extern int xfs_dir2_leaf_lookup(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_leaf_removename(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_leaf_replace(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_leaf_search_hash(struct xfs_da_args *args,
|
||||
struct xfs_buf *lbp);
|
||||
extern int xfs_dir2_leaf_trim_data(struct xfs_da_args *args,
|
||||
struct xfs_buf *lbp, xfs_dir2_db_t db);
|
||||
extern struct xfs_dir2_leaf_entry *
|
||||
xfs_dir3_leaf_find_entry(struct xfs_dir3_icleaf_hdr *leafhdr,
|
||||
struct xfs_dir2_leaf_entry *ents, int index, int compact,
|
||||
int lowstale, int highstale, int *lfloglow, int *lfloghigh);
|
||||
extern int xfs_dir2_node_to_leaf(struct xfs_da_state *state);
|
||||
|
||||
extern bool xfs_dir3_leaf_check_int(struct xfs_mount *mp, struct xfs_inode *dp,
|
||||
struct xfs_dir3_icleaf_hdr *hdr, struct xfs_dir2_leaf *leaf);
|
||||
|
||||
/* xfs_dir2_node.c */
|
||||
extern int xfs_dir2_leaf_to_node(struct xfs_da_args *args,
|
||||
struct xfs_buf *lbp);
|
||||
extern xfs_dahash_t xfs_dir2_leafn_lasthash(struct xfs_inode *dp,
|
||||
struct xfs_buf *bp, int *count);
|
||||
extern int xfs_dir2_leafn_lookup_int(struct xfs_buf *bp,
|
||||
struct xfs_da_args *args, int *indexp,
|
||||
struct xfs_da_state *state);
|
||||
extern int xfs_dir2_leafn_order(struct xfs_inode *dp, struct xfs_buf *leaf1_bp,
|
||||
struct xfs_buf *leaf2_bp);
|
||||
extern int xfs_dir2_leafn_split(struct xfs_da_state *state,
|
||||
struct xfs_da_state_blk *oldblk, struct xfs_da_state_blk *newblk);
|
||||
extern int xfs_dir2_leafn_toosmall(struct xfs_da_state *state, int *action);
|
||||
extern void xfs_dir2_leafn_unbalance(struct xfs_da_state *state,
|
||||
struct xfs_da_state_blk *drop_blk,
|
||||
struct xfs_da_state_blk *save_blk);
|
||||
extern int xfs_dir2_node_addname(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_node_lookup(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_node_removename(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_node_replace(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_node_trim_free(struct xfs_da_args *args, xfs_fileoff_t fo,
|
||||
int *rvalp);
|
||||
extern int xfs_dir2_free_read(struct xfs_trans *tp, struct xfs_inode *dp,
|
||||
xfs_dablk_t fbno, struct xfs_buf **bpp);
|
||||
|
||||
/* xfs_dir2_sf.c */
|
||||
extern int xfs_dir2_block_sfsize(struct xfs_inode *dp,
|
||||
struct xfs_dir2_data_hdr *block, struct xfs_dir2_sf_hdr *sfhp);
|
||||
extern int xfs_dir2_block_to_sf(struct xfs_da_args *args, struct xfs_buf *bp,
|
||||
int size, xfs_dir2_sf_hdr_t *sfhp);
|
||||
extern int xfs_dir2_sf_addname(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_sf_create(struct xfs_da_args *args, xfs_ino_t pino);
|
||||
extern int xfs_dir2_sf_lookup(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_sf_removename(struct xfs_da_args *args);
|
||||
extern int xfs_dir2_sf_replace(struct xfs_da_args *args);
|
||||
|
||||
/* xfs_dir2_readdir.c */
|
||||
extern int xfs_readdir(struct xfs_inode *dp, struct dir_context *ctx,
|
||||
size_t bufsize);
|
||||
|
||||
#endif /* __XFS_DIR2_PRIV_H__ */
|
1184
fs/xfs/libxfs/xfs_dir2_sf.c
Normal file
1184
fs/xfs/libxfs/xfs_dir2_sf.c
Normal file
File diff suppressed because it is too large
Load Diff
290
fs/xfs/libxfs/xfs_dquot_buf.c
Normal file
290
fs/xfs/libxfs/xfs_dquot_buf.c
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2006 Silicon Graphics, Inc.
|
||||
* Copyright (c) 2013 Red Hat, 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_qm.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_trace.h"
|
||||
|
||||
int
|
||||
xfs_calc_dquots_per_chunk(
|
||||
unsigned int nbblks) /* basic block units */
|
||||
{
|
||||
unsigned int ndquots;
|
||||
|
||||
ASSERT(nbblks > 0);
|
||||
ndquots = BBTOB(nbblks);
|
||||
do_div(ndquots, sizeof(xfs_dqblk_t));
|
||||
|
||||
return ndquots;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do some primitive error checking on ondisk dquot data structures.
|
||||
*/
|
||||
int
|
||||
xfs_dqcheck(
|
||||
struct xfs_mount *mp,
|
||||
xfs_disk_dquot_t *ddq,
|
||||
xfs_dqid_t id,
|
||||
uint type, /* used only when IO_dorepair is true */
|
||||
uint flags,
|
||||
char *str)
|
||||
{
|
||||
xfs_dqblk_t *d = (xfs_dqblk_t *)ddq;
|
||||
int errs = 0;
|
||||
|
||||
/*
|
||||
* We can encounter an uninitialized dquot buffer for 2 reasons:
|
||||
* 1. If we crash while deleting the quotainode(s), and those blks got
|
||||
* used for user data. This is because we take the path of regular
|
||||
* file deletion; however, the size field of quotainodes is never
|
||||
* updated, so all the tricks that we play in itruncate_finish
|
||||
* don't quite matter.
|
||||
*
|
||||
* 2. We don't play the quota buffers when there's a quotaoff logitem.
|
||||
* But the allocation will be replayed so we'll end up with an
|
||||
* uninitialized quota block.
|
||||
*
|
||||
* This is all fine; things are still consistent, and we haven't lost
|
||||
* any quota information. Just don't complain about bad dquot blks.
|
||||
*/
|
||||
if (ddq->d_magic != cpu_to_be16(XFS_DQUOT_MAGIC)) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : XFS dquot ID 0x%x, magic 0x%x != 0x%x",
|
||||
str, id, be16_to_cpu(ddq->d_magic), XFS_DQUOT_MAGIC);
|
||||
errs++;
|
||||
}
|
||||
if (ddq->d_version != XFS_DQUOT_VERSION) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : XFS dquot ID 0x%x, version 0x%x != 0x%x",
|
||||
str, id, ddq->d_version, XFS_DQUOT_VERSION);
|
||||
errs++;
|
||||
}
|
||||
|
||||
if (ddq->d_flags != XFS_DQ_USER &&
|
||||
ddq->d_flags != XFS_DQ_PROJ &&
|
||||
ddq->d_flags != XFS_DQ_GROUP) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : XFS dquot ID 0x%x, unknown flags 0x%x",
|
||||
str, id, ddq->d_flags);
|
||||
errs++;
|
||||
}
|
||||
|
||||
if (id != -1 && id != be32_to_cpu(ddq->d_id)) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : ondisk-dquot 0x%p, ID mismatch: "
|
||||
"0x%x expected, found id 0x%x",
|
||||
str, ddq, id, be32_to_cpu(ddq->d_id));
|
||||
errs++;
|
||||
}
|
||||
|
||||
if (!errs && ddq->d_id) {
|
||||
if (ddq->d_blk_softlimit &&
|
||||
be64_to_cpu(ddq->d_bcount) >
|
||||
be64_to_cpu(ddq->d_blk_softlimit)) {
|
||||
if (!ddq->d_btimer) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : Dquot ID 0x%x (0x%p) BLK TIMER NOT STARTED",
|
||||
str, (int)be32_to_cpu(ddq->d_id), ddq);
|
||||
errs++;
|
||||
}
|
||||
}
|
||||
if (ddq->d_ino_softlimit &&
|
||||
be64_to_cpu(ddq->d_icount) >
|
||||
be64_to_cpu(ddq->d_ino_softlimit)) {
|
||||
if (!ddq->d_itimer) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : Dquot ID 0x%x (0x%p) INODE TIMER NOT STARTED",
|
||||
str, (int)be32_to_cpu(ddq->d_id), ddq);
|
||||
errs++;
|
||||
}
|
||||
}
|
||||
if (ddq->d_rtb_softlimit &&
|
||||
be64_to_cpu(ddq->d_rtbcount) >
|
||||
be64_to_cpu(ddq->d_rtb_softlimit)) {
|
||||
if (!ddq->d_rtbtimer) {
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_alert(mp,
|
||||
"%s : Dquot ID 0x%x (0x%p) RTBLK TIMER NOT STARTED",
|
||||
str, (int)be32_to_cpu(ddq->d_id), ddq);
|
||||
errs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!errs || !(flags & XFS_QMOPT_DQREPAIR))
|
||||
return errs;
|
||||
|
||||
if (flags & XFS_QMOPT_DOWARN)
|
||||
xfs_notice(mp, "Re-initializing dquot ID 0x%x", id);
|
||||
|
||||
/*
|
||||
* Typically, a repair is only requested by quotacheck.
|
||||
*/
|
||||
ASSERT(id != -1);
|
||||
ASSERT(flags & XFS_QMOPT_DQREPAIR);
|
||||
memset(d, 0, sizeof(xfs_dqblk_t));
|
||||
|
||||
d->dd_diskdq.d_magic = cpu_to_be16(XFS_DQUOT_MAGIC);
|
||||
d->dd_diskdq.d_version = XFS_DQUOT_VERSION;
|
||||
d->dd_diskdq.d_flags = type;
|
||||
d->dd_diskdq.d_id = cpu_to_be32(id);
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
uuid_copy(&d->dd_uuid, &mp->m_sb.sb_uuid);
|
||||
xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk),
|
||||
XFS_DQUOT_CRC_OFF);
|
||||
}
|
||||
|
||||
return errs;
|
||||
}
|
||||
|
||||
STATIC bool
|
||||
xfs_dquot_buf_verify_crc(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
|
||||
int ndquots;
|
||||
int i;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* if we are in log recovery, the quota subsystem has not been
|
||||
* initialised so we have no quotainfo structure. In that case, we need
|
||||
* to manually calculate the number of dquots in the buffer.
|
||||
*/
|
||||
if (mp->m_quotainfo)
|
||||
ndquots = mp->m_quotainfo->qi_dqperchunk;
|
||||
else
|
||||
ndquots = xfs_calc_dquots_per_chunk(
|
||||
XFS_BB_TO_FSB(mp, bp->b_length));
|
||||
|
||||
for (i = 0; i < ndquots; i++, d++) {
|
||||
if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
|
||||
XFS_DQUOT_CRC_OFF))
|
||||
return false;
|
||||
if (!uuid_equal(&d->dd_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
STATIC bool
|
||||
xfs_dquot_buf_verify(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_dqblk *d = (struct xfs_dqblk *)bp->b_addr;
|
||||
xfs_dqid_t id = 0;
|
||||
int ndquots;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* if we are in log recovery, the quota subsystem has not been
|
||||
* initialised so we have no quotainfo structure. In that case, we need
|
||||
* to manually calculate the number of dquots in the buffer.
|
||||
*/
|
||||
if (mp->m_quotainfo)
|
||||
ndquots = mp->m_quotainfo->qi_dqperchunk;
|
||||
else
|
||||
ndquots = xfs_calc_dquots_per_chunk(bp->b_length);
|
||||
|
||||
/*
|
||||
* On the first read of the buffer, verify that each dquot is valid.
|
||||
* We don't know what the id of the dquot is supposed to be, just that
|
||||
* they should be increasing monotonically within the buffer. If the
|
||||
* first id is corrupt, then it will fail on the second dquot in the
|
||||
* buffer so corruptions could point to the wrong dquot in this case.
|
||||
*/
|
||||
for (i = 0; i < ndquots; i++) {
|
||||
struct xfs_disk_dquot *ddq;
|
||||
int error;
|
||||
|
||||
ddq = &d[i].dd_diskdq;
|
||||
|
||||
if (i == 0)
|
||||
id = be32_to_cpu(ddq->d_id);
|
||||
|
||||
error = xfs_dqcheck(mp, ddq, id + i, 0, XFS_QMOPT_DOWARN,
|
||||
"xfs_dquot_buf_verify");
|
||||
if (error)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_dquot_buf_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
|
||||
if (!xfs_dquot_buf_verify_crc(mp, bp))
|
||||
xfs_buf_ioerror(bp, EFSBADCRC);
|
||||
else if (!xfs_dquot_buf_verify(mp, bp))
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
|
||||
if (bp->b_error)
|
||||
xfs_verifier_error(bp);
|
||||
}
|
||||
|
||||
/*
|
||||
* we don't calculate the CRC here as that is done when the dquot is flushed to
|
||||
* the buffer after the update is done. This ensures that the dquot in the
|
||||
* buffer always has an up-to-date CRC value.
|
||||
*/
|
||||
static void
|
||||
xfs_dquot_buf_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
|
||||
if (!xfs_dquot_buf_verify(mp, bp)) {
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_dquot_buf_ops = {
|
||||
.verify_read = xfs_dquot_buf_read_verify,
|
||||
.verify_write = xfs_dquot_buf_write_verify,
|
||||
};
|
||||
|
2189
fs/xfs/libxfs/xfs_ialloc.c
Normal file
2189
fs/xfs/libxfs/xfs_ialloc.c
Normal file
File diff suppressed because it is too large
Load Diff
422
fs/xfs/libxfs/xfs_ialloc_btree.c
Normal file
422
fs/xfs/libxfs/xfs_ialloc_btree.c
Normal file
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_bit.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_btree.h"
|
||||
#include "xfs_ialloc.h"
|
||||
#include "xfs_ialloc_btree.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_trans.h"
|
||||
|
||||
|
||||
STATIC int
|
||||
xfs_inobt_get_minrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
return cur->bc_mp->m_inobt_mnr[level != 0];
|
||||
}
|
||||
|
||||
STATIC struct xfs_btree_cur *
|
||||
xfs_inobt_dup_cursor(
|
||||
struct xfs_btree_cur *cur)
|
||||
{
|
||||
return xfs_inobt_init_cursor(cur->bc_mp, cur->bc_tp,
|
||||
cur->bc_private.a.agbp, cur->bc_private.a.agno,
|
||||
cur->bc_btnum);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_inobt_set_root(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *nptr,
|
||||
int inc) /* level change */
|
||||
{
|
||||
struct xfs_buf *agbp = cur->bc_private.a.agbp;
|
||||
struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
|
||||
|
||||
agi->agi_root = nptr->s;
|
||||
be32_add_cpu(&agi->agi_level, inc);
|
||||
xfs_ialloc_log_agi(cur->bc_tp, agbp, XFS_AGI_ROOT | XFS_AGI_LEVEL);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_finobt_set_root(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *nptr,
|
||||
int inc) /* level change */
|
||||
{
|
||||
struct xfs_buf *agbp = cur->bc_private.a.agbp;
|
||||
struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
|
||||
|
||||
agi->agi_free_root = nptr->s;
|
||||
be32_add_cpu(&agi->agi_free_level, inc);
|
||||
xfs_ialloc_log_agi(cur->bc_tp, agbp,
|
||||
XFS_AGI_FREE_ROOT | XFS_AGI_FREE_LEVEL);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_inobt_alloc_block(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *start,
|
||||
union xfs_btree_ptr *new,
|
||||
int *stat)
|
||||
{
|
||||
xfs_alloc_arg_t args; /* block allocation args */
|
||||
int error; /* error return value */
|
||||
xfs_agblock_t sbno = be32_to_cpu(start->s);
|
||||
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
args.tp = cur->bc_tp;
|
||||
args.mp = cur->bc_mp;
|
||||
args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.a.agno, sbno);
|
||||
args.minlen = 1;
|
||||
args.maxlen = 1;
|
||||
args.prod = 1;
|
||||
args.type = XFS_ALLOCTYPE_NEAR_BNO;
|
||||
|
||||
error = xfs_alloc_vextent(&args);
|
||||
if (error) {
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
|
||||
return error;
|
||||
}
|
||||
if (args.fsbno == NULLFSBLOCK) {
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
|
||||
*stat = 0;
|
||||
return 0;
|
||||
}
|
||||
ASSERT(args.len == 1);
|
||||
XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
|
||||
|
||||
new->s = cpu_to_be32(XFS_FSB_TO_AGBNO(args.mp, args.fsbno));
|
||||
*stat = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_inobt_free_block(
|
||||
struct xfs_btree_cur *cur,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
xfs_fsblock_t fsbno;
|
||||
int error;
|
||||
|
||||
fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, XFS_BUF_ADDR(bp));
|
||||
error = xfs_free_extent(cur->bc_tp, fsbno, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_trans_binval(cur->bc_tp, bp);
|
||||
return error;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_inobt_get_maxrecs(
|
||||
struct xfs_btree_cur *cur,
|
||||
int level)
|
||||
{
|
||||
return cur->bc_mp->m_inobt_mxr[level != 0];
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_inobt_init_key_from_rec(
|
||||
union xfs_btree_key *key,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
key->inobt.ir_startino = rec->inobt.ir_startino;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_inobt_init_rec_from_key(
|
||||
union xfs_btree_key *key,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
rec->inobt.ir_startino = key->inobt.ir_startino;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_inobt_init_rec_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec *rec)
|
||||
{
|
||||
rec->inobt.ir_startino = cpu_to_be32(cur->bc_rec.i.ir_startino);
|
||||
rec->inobt.ir_freecount = cpu_to_be32(cur->bc_rec.i.ir_freecount);
|
||||
rec->inobt.ir_free = cpu_to_be64(cur->bc_rec.i.ir_free);
|
||||
}
|
||||
|
||||
/*
|
||||
* initial value of ptr for lookup
|
||||
*/
|
||||
STATIC void
|
||||
xfs_inobt_init_ptr_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *ptr)
|
||||
{
|
||||
struct xfs_agi *agi = XFS_BUF_TO_AGI(cur->bc_private.a.agbp);
|
||||
|
||||
ASSERT(cur->bc_private.a.agno == be32_to_cpu(agi->agi_seqno));
|
||||
|
||||
ptr->s = agi->agi_root;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_finobt_init_ptr_from_cur(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_ptr *ptr)
|
||||
{
|
||||
struct xfs_agi *agi = XFS_BUF_TO_AGI(cur->bc_private.a.agbp);
|
||||
|
||||
ASSERT(cur->bc_private.a.agno == be32_to_cpu(agi->agi_seqno));
|
||||
ptr->s = agi->agi_free_root;
|
||||
}
|
||||
|
||||
STATIC __int64_t
|
||||
xfs_inobt_key_diff(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_key *key)
|
||||
{
|
||||
return (__int64_t)be32_to_cpu(key->inobt.ir_startino) -
|
||||
cur->bc_rec.i.ir_startino;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_inobt_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
|
||||
struct xfs_perag *pag = bp->b_pag;
|
||||
unsigned int level;
|
||||
|
||||
/*
|
||||
* During growfs operations, we can't verify the exact owner as the
|
||||
* perag is not fully initialised and hence not attached to the buffer.
|
||||
*
|
||||
* Similarly, during log recovery we will have a perag structure
|
||||
* attached, but the agi information will not yet have been initialised
|
||||
* from the on disk AGI. We don't currently use any of this information,
|
||||
* but beware of the landmine (i.e. need to check pag->pagi_init) if we
|
||||
* ever do.
|
||||
*/
|
||||
switch (block->bb_magic) {
|
||||
case cpu_to_be32(XFS_IBT_CRC_MAGIC):
|
||||
case cpu_to_be32(XFS_FIBT_CRC_MAGIC):
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
if (block->bb_u.s.bb_blkno != cpu_to_be64(bp->b_bn))
|
||||
return false;
|
||||
if (pag &&
|
||||
be32_to_cpu(block->bb_u.s.bb_owner) != pag->pag_agno)
|
||||
return false;
|
||||
/* fall through */
|
||||
case cpu_to_be32(XFS_IBT_MAGIC):
|
||||
case cpu_to_be32(XFS_FIBT_MAGIC):
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* numrecs and level verification */
|
||||
level = be16_to_cpu(block->bb_level);
|
||||
if (level >= mp->m_in_maxlevels)
|
||||
return false;
|
||||
if (be16_to_cpu(block->bb_numrecs) > mp->m_inobt_mxr[level != 0])
|
||||
return false;
|
||||
|
||||
/* sibling pointer verification */
|
||||
if (!block->bb_u.s.bb_leftsib ||
|
||||
(be32_to_cpu(block->bb_u.s.bb_leftsib) >= mp->m_sb.sb_agblocks &&
|
||||
block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK)))
|
||||
return false;
|
||||
if (!block->bb_u.s.bb_rightsib ||
|
||||
(be32_to_cpu(block->bb_u.s.bb_rightsib) >= mp->m_sb.sb_agblocks &&
|
||||
block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_inobt_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
if (!xfs_btree_sblock_verify_crc(bp))
|
||||
xfs_buf_ioerror(bp, EFSBADCRC);
|
||||
else if (!xfs_inobt_verify(bp))
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
|
||||
if (bp->b_error) {
|
||||
trace_xfs_btree_corrupt(bp, _RET_IP_);
|
||||
xfs_verifier_error(bp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_inobt_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
if (!xfs_inobt_verify(bp)) {
|
||||
trace_xfs_btree_corrupt(bp, _RET_IP_);
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
return;
|
||||
}
|
||||
xfs_btree_sblock_calc_crc(bp);
|
||||
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_inobt_buf_ops = {
|
||||
.verify_read = xfs_inobt_read_verify,
|
||||
.verify_write = xfs_inobt_write_verify,
|
||||
};
|
||||
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
STATIC int
|
||||
xfs_inobt_keys_inorder(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_key *k1,
|
||||
union xfs_btree_key *k2)
|
||||
{
|
||||
return be32_to_cpu(k1->inobt.ir_startino) <
|
||||
be32_to_cpu(k2->inobt.ir_startino);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_inobt_recs_inorder(
|
||||
struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec *r1,
|
||||
union xfs_btree_rec *r2)
|
||||
{
|
||||
return be32_to_cpu(r1->inobt.ir_startino) + XFS_INODES_PER_CHUNK <=
|
||||
be32_to_cpu(r2->inobt.ir_startino);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
static const struct xfs_btree_ops xfs_inobt_ops = {
|
||||
.rec_len = sizeof(xfs_inobt_rec_t),
|
||||
.key_len = sizeof(xfs_inobt_key_t),
|
||||
|
||||
.dup_cursor = xfs_inobt_dup_cursor,
|
||||
.set_root = xfs_inobt_set_root,
|
||||
.alloc_block = xfs_inobt_alloc_block,
|
||||
.free_block = xfs_inobt_free_block,
|
||||
.get_minrecs = xfs_inobt_get_minrecs,
|
||||
.get_maxrecs = xfs_inobt_get_maxrecs,
|
||||
.init_key_from_rec = xfs_inobt_init_key_from_rec,
|
||||
.init_rec_from_key = xfs_inobt_init_rec_from_key,
|
||||
.init_rec_from_cur = xfs_inobt_init_rec_from_cur,
|
||||
.init_ptr_from_cur = xfs_inobt_init_ptr_from_cur,
|
||||
.key_diff = xfs_inobt_key_diff,
|
||||
.buf_ops = &xfs_inobt_buf_ops,
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
.keys_inorder = xfs_inobt_keys_inorder,
|
||||
.recs_inorder = xfs_inobt_recs_inorder,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct xfs_btree_ops xfs_finobt_ops = {
|
||||
.rec_len = sizeof(xfs_inobt_rec_t),
|
||||
.key_len = sizeof(xfs_inobt_key_t),
|
||||
|
||||
.dup_cursor = xfs_inobt_dup_cursor,
|
||||
.set_root = xfs_finobt_set_root,
|
||||
.alloc_block = xfs_inobt_alloc_block,
|
||||
.free_block = xfs_inobt_free_block,
|
||||
.get_minrecs = xfs_inobt_get_minrecs,
|
||||
.get_maxrecs = xfs_inobt_get_maxrecs,
|
||||
.init_key_from_rec = xfs_inobt_init_key_from_rec,
|
||||
.init_rec_from_key = xfs_inobt_init_rec_from_key,
|
||||
.init_rec_from_cur = xfs_inobt_init_rec_from_cur,
|
||||
.init_ptr_from_cur = xfs_finobt_init_ptr_from_cur,
|
||||
.key_diff = xfs_inobt_key_diff,
|
||||
.buf_ops = &xfs_inobt_buf_ops,
|
||||
#if defined(DEBUG) || defined(XFS_WARN)
|
||||
.keys_inorder = xfs_inobt_keys_inorder,
|
||||
.recs_inorder = xfs_inobt_recs_inorder,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Allocate a new inode btree cursor.
|
||||
*/
|
||||
struct xfs_btree_cur * /* new inode btree cursor */
|
||||
xfs_inobt_init_cursor(
|
||||
struct xfs_mount *mp, /* file system mount point */
|
||||
struct xfs_trans *tp, /* transaction pointer */
|
||||
struct xfs_buf *agbp, /* buffer for agi structure */
|
||||
xfs_agnumber_t agno, /* allocation group number */
|
||||
xfs_btnum_t btnum) /* ialloc or free ino btree */
|
||||
{
|
||||
struct xfs_agi *agi = XFS_BUF_TO_AGI(agbp);
|
||||
struct xfs_btree_cur *cur;
|
||||
|
||||
cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
|
||||
|
||||
cur->bc_tp = tp;
|
||||
cur->bc_mp = mp;
|
||||
cur->bc_btnum = btnum;
|
||||
if (btnum == XFS_BTNUM_INO) {
|
||||
cur->bc_nlevels = be32_to_cpu(agi->agi_level);
|
||||
cur->bc_ops = &xfs_inobt_ops;
|
||||
} else {
|
||||
cur->bc_nlevels = be32_to_cpu(agi->agi_free_level);
|
||||
cur->bc_ops = &xfs_finobt_ops;
|
||||
}
|
||||
|
||||
cur->bc_blocklog = mp->m_sb.sb_blocklog;
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
|
||||
|
||||
cur->bc_private.a.agbp = agbp;
|
||||
cur->bc_private.a.agno = agno;
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate number of records in an inobt btree block.
|
||||
*/
|
||||
int
|
||||
xfs_inobt_maxrecs(
|
||||
struct xfs_mount *mp,
|
||||
int blocklen,
|
||||
int leaf)
|
||||
{
|
||||
blocklen -= XFS_INOBT_BLOCK_LEN(mp);
|
||||
|
||||
if (leaf)
|
||||
return blocklen / sizeof(xfs_inobt_rec_t);
|
||||
return blocklen / (sizeof(xfs_inobt_key_t) + sizeof(xfs_inobt_ptr_t));
|
||||
}
|
479
fs/xfs/libxfs/xfs_inode_buf.c
Normal file
479
fs/xfs/libxfs/xfs_inode_buf.c
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2006 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_icache.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_ialloc.h"
|
||||
#include "xfs_dinode.h"
|
||||
|
||||
/*
|
||||
* Check that none of the inode's in the buffer have a next
|
||||
* unlinked field of 0.
|
||||
*/
|
||||
#if defined(DEBUG)
|
||||
void
|
||||
xfs_inobp_check(
|
||||
xfs_mount_t *mp,
|
||||
xfs_buf_t *bp)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
xfs_dinode_t *dip;
|
||||
|
||||
j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog;
|
||||
|
||||
for (i = 0; i < j; i++) {
|
||||
dip = (xfs_dinode_t *)xfs_buf_offset(bp,
|
||||
i * mp->m_sb.sb_inodesize);
|
||||
if (!dip->di_next_unlinked) {
|
||||
xfs_alert(mp,
|
||||
"Detected bogus zero next_unlinked field in inode %d buffer 0x%llx.",
|
||||
i, (long long)bp->b_bn);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If we are doing readahead on an inode buffer, we might be in log recovery
|
||||
* reading an inode allocation buffer that hasn't yet been replayed, and hence
|
||||
* has not had the inode cores stamped into it. Hence for readahead, the buffer
|
||||
* may be potentially invalid.
|
||||
*
|
||||
* If the readahead buffer is invalid, we don't want to mark it with an error,
|
||||
* but we do want to clear the DONE status of the buffer so that a followup read
|
||||
* will re-read it from disk. This will ensure that we don't get an unnecessary
|
||||
* warnings during log recovery and we don't get unnecssary panics on debug
|
||||
* kernels.
|
||||
*/
|
||||
static void
|
||||
xfs_inode_buf_verify(
|
||||
struct xfs_buf *bp,
|
||||
bool readahead)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
int i;
|
||||
int ni;
|
||||
|
||||
/*
|
||||
* Validate the magic number and version of every inode in the buffer
|
||||
*/
|
||||
ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock;
|
||||
for (i = 0; i < ni; i++) {
|
||||
int di_ok;
|
||||
xfs_dinode_t *dip;
|
||||
|
||||
dip = (struct xfs_dinode *)xfs_buf_offset(bp,
|
||||
(i << mp->m_sb.sb_inodelog));
|
||||
di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) &&
|
||||
XFS_DINODE_GOOD_VERSION(dip->di_version);
|
||||
if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
|
||||
XFS_ERRTAG_ITOBP_INOTOBP,
|
||||
XFS_RANDOM_ITOBP_INOTOBP))) {
|
||||
if (readahead) {
|
||||
bp->b_flags &= ~XBF_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
#ifdef DEBUG
|
||||
xfs_alert(mp,
|
||||
"bad inode magic/vsn daddr %lld #%d (magic=%x)",
|
||||
(unsigned long long)bp->b_bn, i,
|
||||
be16_to_cpu(dip->di_magic));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
xfs_inobp_check(mp, bp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
xfs_inode_buf_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
xfs_inode_buf_verify(bp, false);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_inode_buf_readahead_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
xfs_inode_buf_verify(bp, true);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_inode_buf_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
xfs_inode_buf_verify(bp, false);
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_inode_buf_ops = {
|
||||
.verify_read = xfs_inode_buf_read_verify,
|
||||
.verify_write = xfs_inode_buf_write_verify,
|
||||
};
|
||||
|
||||
const struct xfs_buf_ops xfs_inode_buf_ra_ops = {
|
||||
.verify_read = xfs_inode_buf_readahead_verify,
|
||||
.verify_write = xfs_inode_buf_write_verify,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This routine is called to map an inode to the buffer containing the on-disk
|
||||
* version of the inode. It returns a pointer to the buffer containing the
|
||||
* on-disk inode in the bpp parameter, and in the dipp parameter it returns a
|
||||
* pointer to the on-disk inode within that buffer.
|
||||
*
|
||||
* If a non-zero error is returned, then the contents of bpp and dipp are
|
||||
* undefined.
|
||||
*/
|
||||
int
|
||||
xfs_imap_to_bp(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_imap *imap,
|
||||
struct xfs_dinode **dipp,
|
||||
struct xfs_buf **bpp,
|
||||
uint buf_flags,
|
||||
uint iget_flags)
|
||||
{
|
||||
struct xfs_buf *bp;
|
||||
int error;
|
||||
|
||||
buf_flags |= XBF_UNMAPPED;
|
||||
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap->im_blkno,
|
||||
(int)imap->im_len, buf_flags, &bp,
|
||||
&xfs_inode_buf_ops);
|
||||
if (error) {
|
||||
if (error == EAGAIN) {
|
||||
ASSERT(buf_flags & XBF_TRYLOCK);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error == EFSCORRUPTED &&
|
||||
(iget_flags & XFS_IGET_UNTRUSTED))
|
||||
return EINVAL;
|
||||
|
||||
xfs_warn(mp, "%s: xfs_trans_read_buf() returned error %d.",
|
||||
__func__, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
*bpp = bp;
|
||||
*dipp = (struct xfs_dinode *)xfs_buf_offset(bp, imap->im_boffset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
xfs_dinode_from_disk(
|
||||
xfs_icdinode_t *to,
|
||||
xfs_dinode_t *from)
|
||||
{
|
||||
to->di_magic = be16_to_cpu(from->di_magic);
|
||||
to->di_mode = be16_to_cpu(from->di_mode);
|
||||
to->di_version = from ->di_version;
|
||||
to->di_format = from->di_format;
|
||||
to->di_onlink = be16_to_cpu(from->di_onlink);
|
||||
to->di_uid = be32_to_cpu(from->di_uid);
|
||||
to->di_gid = be32_to_cpu(from->di_gid);
|
||||
to->di_nlink = be32_to_cpu(from->di_nlink);
|
||||
to->di_projid_lo = be16_to_cpu(from->di_projid_lo);
|
||||
to->di_projid_hi = be16_to_cpu(from->di_projid_hi);
|
||||
memcpy(to->di_pad, from->di_pad, sizeof(to->di_pad));
|
||||
to->di_flushiter = be16_to_cpu(from->di_flushiter);
|
||||
to->di_atime.t_sec = be32_to_cpu(from->di_atime.t_sec);
|
||||
to->di_atime.t_nsec = be32_to_cpu(from->di_atime.t_nsec);
|
||||
to->di_mtime.t_sec = be32_to_cpu(from->di_mtime.t_sec);
|
||||
to->di_mtime.t_nsec = be32_to_cpu(from->di_mtime.t_nsec);
|
||||
to->di_ctime.t_sec = be32_to_cpu(from->di_ctime.t_sec);
|
||||
to->di_ctime.t_nsec = be32_to_cpu(from->di_ctime.t_nsec);
|
||||
to->di_size = be64_to_cpu(from->di_size);
|
||||
to->di_nblocks = be64_to_cpu(from->di_nblocks);
|
||||
to->di_extsize = be32_to_cpu(from->di_extsize);
|
||||
to->di_nextents = be32_to_cpu(from->di_nextents);
|
||||
to->di_anextents = be16_to_cpu(from->di_anextents);
|
||||
to->di_forkoff = from->di_forkoff;
|
||||
to->di_aformat = from->di_aformat;
|
||||
to->di_dmevmask = be32_to_cpu(from->di_dmevmask);
|
||||
to->di_dmstate = be16_to_cpu(from->di_dmstate);
|
||||
to->di_flags = be16_to_cpu(from->di_flags);
|
||||
to->di_gen = be32_to_cpu(from->di_gen);
|
||||
|
||||
if (to->di_version == 3) {
|
||||
to->di_changecount = be64_to_cpu(from->di_changecount);
|
||||
to->di_crtime.t_sec = be32_to_cpu(from->di_crtime.t_sec);
|
||||
to->di_crtime.t_nsec = be32_to_cpu(from->di_crtime.t_nsec);
|
||||
to->di_flags2 = be64_to_cpu(from->di_flags2);
|
||||
to->di_ino = be64_to_cpu(from->di_ino);
|
||||
to->di_lsn = be64_to_cpu(from->di_lsn);
|
||||
memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2));
|
||||
uuid_copy(&to->di_uuid, &from->di_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xfs_dinode_to_disk(
|
||||
xfs_dinode_t *to,
|
||||
xfs_icdinode_t *from)
|
||||
{
|
||||
to->di_magic = cpu_to_be16(from->di_magic);
|
||||
to->di_mode = cpu_to_be16(from->di_mode);
|
||||
to->di_version = from ->di_version;
|
||||
to->di_format = from->di_format;
|
||||
to->di_onlink = cpu_to_be16(from->di_onlink);
|
||||
to->di_uid = cpu_to_be32(from->di_uid);
|
||||
to->di_gid = cpu_to_be32(from->di_gid);
|
||||
to->di_nlink = cpu_to_be32(from->di_nlink);
|
||||
to->di_projid_lo = cpu_to_be16(from->di_projid_lo);
|
||||
to->di_projid_hi = cpu_to_be16(from->di_projid_hi);
|
||||
memcpy(to->di_pad, from->di_pad, sizeof(to->di_pad));
|
||||
to->di_atime.t_sec = cpu_to_be32(from->di_atime.t_sec);
|
||||
to->di_atime.t_nsec = cpu_to_be32(from->di_atime.t_nsec);
|
||||
to->di_mtime.t_sec = cpu_to_be32(from->di_mtime.t_sec);
|
||||
to->di_mtime.t_nsec = cpu_to_be32(from->di_mtime.t_nsec);
|
||||
to->di_ctime.t_sec = cpu_to_be32(from->di_ctime.t_sec);
|
||||
to->di_ctime.t_nsec = cpu_to_be32(from->di_ctime.t_nsec);
|
||||
to->di_size = cpu_to_be64(from->di_size);
|
||||
to->di_nblocks = cpu_to_be64(from->di_nblocks);
|
||||
to->di_extsize = cpu_to_be32(from->di_extsize);
|
||||
to->di_nextents = cpu_to_be32(from->di_nextents);
|
||||
to->di_anextents = cpu_to_be16(from->di_anextents);
|
||||
to->di_forkoff = from->di_forkoff;
|
||||
to->di_aformat = from->di_aformat;
|
||||
to->di_dmevmask = cpu_to_be32(from->di_dmevmask);
|
||||
to->di_dmstate = cpu_to_be16(from->di_dmstate);
|
||||
to->di_flags = cpu_to_be16(from->di_flags);
|
||||
to->di_gen = cpu_to_be32(from->di_gen);
|
||||
|
||||
if (from->di_version == 3) {
|
||||
to->di_changecount = cpu_to_be64(from->di_changecount);
|
||||
to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
|
||||
to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
|
||||
to->di_flags2 = cpu_to_be64(from->di_flags2);
|
||||
to->di_ino = cpu_to_be64(from->di_ino);
|
||||
to->di_lsn = cpu_to_be64(from->di_lsn);
|
||||
memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2));
|
||||
uuid_copy(&to->di_uuid, &from->di_uuid);
|
||||
to->di_flushiter = 0;
|
||||
} else {
|
||||
to->di_flushiter = cpu_to_be16(from->di_flushiter);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
xfs_dinode_verify(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_dinode *dip)
|
||||
{
|
||||
if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
|
||||
return false;
|
||||
|
||||
/* only version 3 or greater inodes are extensively verified here */
|
||||
if (dip->di_version < 3)
|
||||
return true;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (!xfs_verify_cksum((char *)dip, mp->m_sb.sb_inodesize,
|
||||
XFS_DINODE_CRC_OFF))
|
||||
return false;
|
||||
if (be64_to_cpu(dip->di_ino) != ip->i_ino)
|
||||
return false;
|
||||
if (!uuid_equal(&dip->di_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
xfs_dinode_calc_crc(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_dinode *dip)
|
||||
{
|
||||
__uint32_t crc;
|
||||
|
||||
if (dip->di_version < 3)
|
||||
return;
|
||||
|
||||
ASSERT(xfs_sb_version_hascrc(&mp->m_sb));
|
||||
crc = xfs_start_cksum((char *)dip, mp->m_sb.sb_inodesize,
|
||||
XFS_DINODE_CRC_OFF);
|
||||
dip->di_crc = xfs_end_cksum(crc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the disk inode attributes into the in-core inode structure.
|
||||
*
|
||||
* For version 5 superblocks, if we are initialising a new inode and we are not
|
||||
* utilising the XFS_MOUNT_IKEEP inode cluster mode, we can simple build the new
|
||||
* inode core with a random generation number. If we are keeping inodes around,
|
||||
* we need to read the inode cluster to get the existing generation number off
|
||||
* disk. Further, if we are using version 4 superblocks (i.e. v1/v2 inode
|
||||
* format) then log recovery is dependent on the di_flushiter field being
|
||||
* initialised from the current on-disk value and hence we must also read the
|
||||
* inode off disk.
|
||||
*/
|
||||
int
|
||||
xfs_iread(
|
||||
xfs_mount_t *mp,
|
||||
xfs_trans_t *tp,
|
||||
xfs_inode_t *ip,
|
||||
uint iget_flags)
|
||||
{
|
||||
xfs_buf_t *bp;
|
||||
xfs_dinode_t *dip;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Fill in the location information in the in-core inode.
|
||||
*/
|
||||
error = xfs_imap(mp, tp, ip->i_ino, &ip->i_imap, iget_flags);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* shortcut IO on inode allocation if possible */
|
||||
if ((iget_flags & XFS_IGET_CREATE) &&
|
||||
xfs_sb_version_hascrc(&mp->m_sb) &&
|
||||
!(mp->m_flags & XFS_MOUNT_IKEEP)) {
|
||||
/* initialise the on-disk inode core */
|
||||
memset(&ip->i_d, 0, sizeof(ip->i_d));
|
||||
ip->i_d.di_magic = XFS_DINODE_MAGIC;
|
||||
ip->i_d.di_gen = prandom_u32();
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
ip->i_d.di_version = 3;
|
||||
ip->i_d.di_ino = ip->i_ino;
|
||||
uuid_copy(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid);
|
||||
} else
|
||||
ip->i_d.di_version = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get pointers to the on-disk inode and the buffer containing it.
|
||||
*/
|
||||
error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &bp, 0, iget_flags);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* even unallocated inodes are verified */
|
||||
if (!xfs_dinode_verify(mp, ip, dip)) {
|
||||
xfs_alert(mp, "%s: validation failed for inode %lld failed",
|
||||
__func__, ip->i_ino);
|
||||
|
||||
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, dip);
|
||||
error = EFSCORRUPTED;
|
||||
goto out_brelse;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the on-disk inode is already linked to a directory
|
||||
* entry, copy all of the inode into the in-core inode.
|
||||
* xfs_iformat_fork() handles copying in the inode format
|
||||
* specific information.
|
||||
* Otherwise, just get the truly permanent information.
|
||||
*/
|
||||
if (dip->di_mode) {
|
||||
xfs_dinode_from_disk(&ip->i_d, dip);
|
||||
error = xfs_iformat_fork(ip, dip);
|
||||
if (error) {
|
||||
#ifdef DEBUG
|
||||
xfs_alert(mp, "%s: xfs_iformat() returned error %d",
|
||||
__func__, error);
|
||||
#endif /* DEBUG */
|
||||
goto out_brelse;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Partial initialisation of the in-core inode. Just the bits
|
||||
* that xfs_ialloc won't overwrite or relies on being correct.
|
||||
*/
|
||||
ip->i_d.di_magic = be16_to_cpu(dip->di_magic);
|
||||
ip->i_d.di_version = dip->di_version;
|
||||
ip->i_d.di_gen = be32_to_cpu(dip->di_gen);
|
||||
ip->i_d.di_flushiter = be16_to_cpu(dip->di_flushiter);
|
||||
|
||||
if (dip->di_version == 3) {
|
||||
ip->i_d.di_ino = be64_to_cpu(dip->di_ino);
|
||||
uuid_copy(&ip->i_d.di_uuid, &dip->di_uuid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure to pull in the mode here as well in
|
||||
* case the inode is released without being used.
|
||||
* This ensures that xfs_inactive() will see that
|
||||
* the inode is already free and not try to mess
|
||||
* with the uninitialized part of it.
|
||||
*/
|
||||
ip->i_d.di_mode = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Automatically convert version 1 inode formats in memory to version 2
|
||||
* inode format. If the inode is modified, it will get logged and
|
||||
* rewritten as a version 2 inode. We can do this because we set the
|
||||
* superblock feature bit for v2 inodes unconditionally during mount
|
||||
* and it means the reast of the code can assume the inode version is 2
|
||||
* or higher.
|
||||
*/
|
||||
if (ip->i_d.di_version == 1) {
|
||||
ip->i_d.di_version = 2;
|
||||
memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
|
||||
ip->i_d.di_nlink = ip->i_d.di_onlink;
|
||||
ip->i_d.di_onlink = 0;
|
||||
xfs_set_projid(ip, 0);
|
||||
}
|
||||
|
||||
ip->i_delayed_blks = 0;
|
||||
|
||||
/*
|
||||
* Mark the buffer containing the inode as something to keep
|
||||
* around for a while. This helps to keep recently accessed
|
||||
* meta-data in-core longer.
|
||||
*/
|
||||
xfs_buf_set_ref(bp, XFS_INO_REF);
|
||||
|
||||
/*
|
||||
* Use xfs_trans_brelse() to release the buffer containing the on-disk
|
||||
* inode, because it was acquired with xfs_trans_read_buf() in
|
||||
* xfs_imap_to_bp() above. If tp is NULL, this is just a normal
|
||||
* brelse(). If we're within a transaction, then xfs_trans_brelse()
|
||||
* will only release the buffer if it is not dirty within the
|
||||
* transaction. It will be OK to release the buffer in this case,
|
||||
* because inodes on disk are never destroyed and we will be locking the
|
||||
* new in-core inode before putting it in the cache where other
|
||||
* processes can find it. Thus we don't have to worry about the inode
|
||||
* being changed just because we released the buffer.
|
||||
*/
|
||||
out_brelse:
|
||||
xfs_trans_brelse(tp, bp);
|
||||
return error;
|
||||
}
|
1906
fs/xfs/libxfs/xfs_inode_fork.c
Normal file
1906
fs/xfs/libxfs/xfs_inode_fork.c
Normal file
File diff suppressed because it is too large
Load Diff
150
fs/xfs/libxfs/xfs_log_rlimit.c
Normal file
150
fs/xfs/libxfs/xfs_log_rlimit.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Jie Liu.
|
||||
* 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_da_format.h"
|
||||
#include "xfs_trans_space.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_da_btree.h"
|
||||
#include "xfs_attr_leaf.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
|
||||
/*
|
||||
* Calculate the maximum length in bytes that would be required for a local
|
||||
* attribute value as large attributes out of line are not logged.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_log_calc_max_attrsetm_res(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
int size;
|
||||
int nblks;
|
||||
|
||||
size = xfs_attr_leaf_entsize_local_max(mp->m_attr_geo->blksize) -
|
||||
MAXNAMELEN - 1;
|
||||
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
|
||||
nblks += XFS_B_TO_FSB(mp, size);
|
||||
nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK);
|
||||
|
||||
return M_RES(mp)->tr_attrsetm.tr_logres +
|
||||
M_RES(mp)->tr_attrsetrt.tr_logres * nblks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over the log space reservation table to figure out and return
|
||||
* the maximum one in terms of the pre-calculated values which were done
|
||||
* at mount time.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_log_get_max_trans_res(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_trans_res *max_resp)
|
||||
{
|
||||
struct xfs_trans_res *resp;
|
||||
struct xfs_trans_res *end_resp;
|
||||
int log_space = 0;
|
||||
int attr_space;
|
||||
|
||||
attr_space = xfs_log_calc_max_attrsetm_res(mp);
|
||||
|
||||
resp = (struct xfs_trans_res *)M_RES(mp);
|
||||
end_resp = (struct xfs_trans_res *)(M_RES(mp) + 1);
|
||||
for (; resp < end_resp; resp++) {
|
||||
int tmp = resp->tr_logcount > 1 ?
|
||||
resp->tr_logres * resp->tr_logcount :
|
||||
resp->tr_logres;
|
||||
if (log_space < tmp) {
|
||||
log_space = tmp;
|
||||
*max_resp = *resp; /* struct copy */
|
||||
}
|
||||
}
|
||||
|
||||
if (attr_space > log_space) {
|
||||
*max_resp = M_RES(mp)->tr_attrsetm; /* struct copy */
|
||||
max_resp->tr_logres = attr_space;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the minimum valid log size for the given superblock configuration.
|
||||
* Used to calculate the minimum log size at mkfs time, and to determine if
|
||||
* the log is large enough or not at mount time. Returns the minimum size in
|
||||
* filesystem block size units.
|
||||
*/
|
||||
int
|
||||
xfs_log_calc_minimum_size(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
struct xfs_trans_res tres = {0};
|
||||
int max_logres;
|
||||
int min_logblks = 0;
|
||||
int lsunit = 0;
|
||||
|
||||
xfs_log_get_max_trans_res(mp, &tres);
|
||||
|
||||
max_logres = xfs_log_calc_unit_res(mp, tres.tr_logres);
|
||||
if (tres.tr_logcount > 1)
|
||||
max_logres *= tres.tr_logcount;
|
||||
|
||||
if (xfs_sb_version_haslogv2(&mp->m_sb) && mp->m_sb.sb_logsunit > 1)
|
||||
lsunit = BTOBB(mp->m_sb.sb_logsunit);
|
||||
|
||||
/*
|
||||
* Two factors should be taken into account for calculating the minimum
|
||||
* log space.
|
||||
* 1) The fundamental limitation is that no single transaction can be
|
||||
* larger than half size of the log.
|
||||
*
|
||||
* From mkfs.xfs, this is considered by the XFS_MIN_LOG_FACTOR
|
||||
* define, which is set to 3. That means we can definitely fit
|
||||
* maximally sized 2 transactions in the log. We'll use this same
|
||||
* value here.
|
||||
*
|
||||
* 2) If the lsunit option is specified, a transaction requires 2 LSU
|
||||
* for the reservation because there are two log writes that can
|
||||
* require padding - the transaction data and the commit record which
|
||||
* are written separately and both can require padding to the LSU.
|
||||
* Consider that we can have an active CIL reservation holding 2*LSU,
|
||||
* but the CIL is not over a push threshold, in this case, if we
|
||||
* don't have enough log space for at one new transaction, which
|
||||
* includes another 2*LSU in the reservation, we will run into dead
|
||||
* loop situation in log space grant procedure. i.e.
|
||||
* xlog_grant_head_wait().
|
||||
*
|
||||
* Hence the log size needs to be able to contain two maximally sized
|
||||
* and padded transactions, which is (2 * (2 * LSU + maxlres)).
|
||||
*
|
||||
* Also, the log size should be a multiple of the log stripe unit, round
|
||||
* it up to lsunit boundary if lsunit is specified.
|
||||
*/
|
||||
if (lsunit) {
|
||||
min_logblks = roundup_64(BTOBB(max_logres), lsunit) +
|
||||
2 * lsunit;
|
||||
} else
|
||||
min_logblks = BTOBB(max_logres) + 2 * BBSIZE;
|
||||
min_logblks *= XFS_MIN_LOG_FACTOR;
|
||||
|
||||
return XFS_BB_TO_FSB(mp, min_logblks);
|
||||
}
|
973
fs/xfs/libxfs/xfs_rtbitmap.c
Normal file
973
fs/xfs/libxfs/xfs_rtbitmap.c
Normal file
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
* 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_bit.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_bmap.h"
|
||||
#include "xfs_bmap_util.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_trans_space.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_buf.h"
|
||||
#include "xfs_icache.h"
|
||||
#include "xfs_dinode.h"
|
||||
#include "xfs_rtalloc.h"
|
||||
|
||||
|
||||
/*
|
||||
* Realtime allocator bitmap functions shared with userspace.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Get a buffer for the bitmap or summary file block specified.
|
||||
* The buffer is returned read and locked.
|
||||
*/
|
||||
int
|
||||
xfs_rtbuf_get(
|
||||
xfs_mount_t *mp, /* file system mount structure */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t block, /* block number in bitmap or summary */
|
||||
int issum, /* is summary not bitmap */
|
||||
xfs_buf_t **bpp) /* output: buffer for the block */
|
||||
{
|
||||
xfs_buf_t *bp; /* block buffer, result */
|
||||
xfs_inode_t *ip; /* bitmap or summary inode */
|
||||
xfs_bmbt_irec_t map;
|
||||
int nmap = 1;
|
||||
int error; /* error value */
|
||||
|
||||
ip = issum ? mp->m_rsumip : mp->m_rbmip;
|
||||
|
||||
error = xfs_bmapi_read(ip, block, 1, &map, &nmap, XFS_DATA_FORK);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ASSERT(map.br_startblock != NULLFSBLOCK);
|
||||
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
|
||||
XFS_FSB_TO_DADDR(mp, map.br_startblock),
|
||||
mp->m_bsize, 0, &bp, NULL);
|
||||
if (error)
|
||||
return error;
|
||||
*bpp = bp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Searching backward from start to limit, find the first block whose
|
||||
* allocated/free state is different from start's.
|
||||
*/
|
||||
int
|
||||
xfs_rtfind_back(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t start, /* starting block to look at */
|
||||
xfs_rtblock_t limit, /* last block to look at */
|
||||
xfs_rtblock_t *rtblock) /* out: start block found */
|
||||
{
|
||||
xfs_rtword_t *b; /* current word in buffer */
|
||||
int bit; /* bit number in the word */
|
||||
xfs_rtblock_t block; /* bitmap block number */
|
||||
xfs_buf_t *bp; /* buf for the block */
|
||||
xfs_rtword_t *bufp; /* starting word in buffer */
|
||||
int error; /* error value */
|
||||
xfs_rtblock_t firstbit; /* first useful bit in the word */
|
||||
xfs_rtblock_t i; /* current bit number rel. to start */
|
||||
xfs_rtblock_t len; /* length of inspected area */
|
||||
xfs_rtword_t mask; /* mask of relevant bits for value */
|
||||
xfs_rtword_t want; /* mask for "good" values */
|
||||
xfs_rtword_t wdiff; /* difference from wanted value */
|
||||
int word; /* word number in the buffer */
|
||||
|
||||
/*
|
||||
* Compute and read in starting bitmap block for starting block.
|
||||
*/
|
||||
block = XFS_BITTOBLOCK(mp, start);
|
||||
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
bufp = bp->b_addr;
|
||||
/*
|
||||
* Get the first word's index & point to it.
|
||||
*/
|
||||
word = XFS_BITTOWORD(mp, start);
|
||||
b = &bufp[word];
|
||||
bit = (int)(start & (XFS_NBWORD - 1));
|
||||
len = start - limit + 1;
|
||||
/*
|
||||
* Compute match value, based on the bit at start: if 1 (free)
|
||||
* then all-ones, else all-zeroes.
|
||||
*/
|
||||
want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
|
||||
/*
|
||||
* If the starting position is not word-aligned, deal with the
|
||||
* partial word.
|
||||
*/
|
||||
if (bit < XFS_NBWORD - 1) {
|
||||
/*
|
||||
* Calculate first (leftmost) bit number to look at,
|
||||
* and mask for all the relevant bits in this word.
|
||||
*/
|
||||
firstbit = XFS_RTMAX((xfs_srtblock_t)(bit - len + 1), 0);
|
||||
mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
|
||||
firstbit;
|
||||
/*
|
||||
* Calculate the difference between the value there
|
||||
* and what we're looking for.
|
||||
*/
|
||||
if ((wdiff = (*b ^ want) & mask)) {
|
||||
/*
|
||||
* Different. Mark where we are and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i = bit - XFS_RTHIBIT(wdiff);
|
||||
*rtblock = start - i + 1;
|
||||
return 0;
|
||||
}
|
||||
i = bit - firstbit + 1;
|
||||
/*
|
||||
* Go on to previous block if that's where the previous word is
|
||||
* and we need the previous word.
|
||||
*/
|
||||
if (--word == -1 && i < len) {
|
||||
/*
|
||||
* If done with this block, get the previous one.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
bufp = bp->b_addr;
|
||||
word = XFS_BLOCKWMASK(mp);
|
||||
b = &bufp[word];
|
||||
} else {
|
||||
/*
|
||||
* Go on to the previous word in the buffer.
|
||||
*/
|
||||
b--;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Starting on a word boundary, no partial word.
|
||||
*/
|
||||
i = 0;
|
||||
}
|
||||
/*
|
||||
* Loop over whole words in buffers. When we use up one buffer
|
||||
* we move on to the previous one.
|
||||
*/
|
||||
while (len - i >= XFS_NBWORD) {
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = *b ^ want)) {
|
||||
/*
|
||||
* Different, mark where we are and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
|
||||
*rtblock = start - i + 1;
|
||||
return 0;
|
||||
}
|
||||
i += XFS_NBWORD;
|
||||
/*
|
||||
* Go on to previous block if that's where the previous word is
|
||||
* and we need the previous word.
|
||||
*/
|
||||
if (--word == -1 && i < len) {
|
||||
/*
|
||||
* If done with this block, get the previous one.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
bufp = bp->b_addr;
|
||||
word = XFS_BLOCKWMASK(mp);
|
||||
b = &bufp[word];
|
||||
} else {
|
||||
/*
|
||||
* Go on to the previous word in the buffer.
|
||||
*/
|
||||
b--;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If not ending on a word boundary, deal with the last
|
||||
* (partial) word.
|
||||
*/
|
||||
if (len - i) {
|
||||
/*
|
||||
* Calculate first (leftmost) bit number to look at,
|
||||
* and mask for all the relevant bits in this word.
|
||||
*/
|
||||
firstbit = XFS_NBWORD - (len - i);
|
||||
mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = (*b ^ want) & mask)) {
|
||||
/*
|
||||
* Different, mark where we are and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
|
||||
*rtblock = start - i + 1;
|
||||
return 0;
|
||||
} else
|
||||
i = len;
|
||||
}
|
||||
/*
|
||||
* No match, return that we scanned the whole area.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
*rtblock = start - i + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Searching forward from start to limit, find the first block whose
|
||||
* allocated/free state is different from start's.
|
||||
*/
|
||||
int
|
||||
xfs_rtfind_forw(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t start, /* starting block to look at */
|
||||
xfs_rtblock_t limit, /* last block to look at */
|
||||
xfs_rtblock_t *rtblock) /* out: start block found */
|
||||
{
|
||||
xfs_rtword_t *b; /* current word in buffer */
|
||||
int bit; /* bit number in the word */
|
||||
xfs_rtblock_t block; /* bitmap block number */
|
||||
xfs_buf_t *bp; /* buf for the block */
|
||||
xfs_rtword_t *bufp; /* starting word in buffer */
|
||||
int error; /* error value */
|
||||
xfs_rtblock_t i; /* current bit number rel. to start */
|
||||
xfs_rtblock_t lastbit; /* last useful bit in the word */
|
||||
xfs_rtblock_t len; /* length of inspected area */
|
||||
xfs_rtword_t mask; /* mask of relevant bits for value */
|
||||
xfs_rtword_t want; /* mask for "good" values */
|
||||
xfs_rtword_t wdiff; /* difference from wanted value */
|
||||
int word; /* word number in the buffer */
|
||||
|
||||
/*
|
||||
* Compute and read in starting bitmap block for starting block.
|
||||
*/
|
||||
block = XFS_BITTOBLOCK(mp, start);
|
||||
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
bufp = bp->b_addr;
|
||||
/*
|
||||
* Get the first word's index & point to it.
|
||||
*/
|
||||
word = XFS_BITTOWORD(mp, start);
|
||||
b = &bufp[word];
|
||||
bit = (int)(start & (XFS_NBWORD - 1));
|
||||
len = limit - start + 1;
|
||||
/*
|
||||
* Compute match value, based on the bit at start: if 1 (free)
|
||||
* then all-ones, else all-zeroes.
|
||||
*/
|
||||
want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
|
||||
/*
|
||||
* If the starting position is not word-aligned, deal with the
|
||||
* partial word.
|
||||
*/
|
||||
if (bit) {
|
||||
/*
|
||||
* Calculate last (rightmost) bit number to look at,
|
||||
* and mask for all the relevant bits in this word.
|
||||
*/
|
||||
lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
|
||||
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
|
||||
/*
|
||||
* Calculate the difference between the value there
|
||||
* and what we're looking for.
|
||||
*/
|
||||
if ((wdiff = (*b ^ want) & mask)) {
|
||||
/*
|
||||
* Different. Mark where we are and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i = XFS_RTLOBIT(wdiff) - bit;
|
||||
*rtblock = start + i - 1;
|
||||
return 0;
|
||||
}
|
||||
i = lastbit - bit;
|
||||
/*
|
||||
* Go on to next block if that's where the next word is
|
||||
* and we need the next word.
|
||||
*/
|
||||
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
|
||||
/*
|
||||
* If done with this block, get the previous one.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
b = bufp = bp->b_addr;
|
||||
word = 0;
|
||||
} else {
|
||||
/*
|
||||
* Go on to the previous word in the buffer.
|
||||
*/
|
||||
b++;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Starting on a word boundary, no partial word.
|
||||
*/
|
||||
i = 0;
|
||||
}
|
||||
/*
|
||||
* Loop over whole words in buffers. When we use up one buffer
|
||||
* we move on to the next one.
|
||||
*/
|
||||
while (len - i >= XFS_NBWORD) {
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = *b ^ want)) {
|
||||
/*
|
||||
* Different, mark where we are and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i += XFS_RTLOBIT(wdiff);
|
||||
*rtblock = start + i - 1;
|
||||
return 0;
|
||||
}
|
||||
i += XFS_NBWORD;
|
||||
/*
|
||||
* Go on to next block if that's where the next word is
|
||||
* and we need the next word.
|
||||
*/
|
||||
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
|
||||
/*
|
||||
* If done with this block, get the next one.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
b = bufp = bp->b_addr;
|
||||
word = 0;
|
||||
} else {
|
||||
/*
|
||||
* Go on to the next word in the buffer.
|
||||
*/
|
||||
b++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If not ending on a word boundary, deal with the last
|
||||
* (partial) word.
|
||||
*/
|
||||
if ((lastbit = len - i)) {
|
||||
/*
|
||||
* Calculate mask for all the relevant bits in this word.
|
||||
*/
|
||||
mask = ((xfs_rtword_t)1 << lastbit) - 1;
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = (*b ^ want) & mask)) {
|
||||
/*
|
||||
* Different, mark where we are and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i += XFS_RTLOBIT(wdiff);
|
||||
*rtblock = start + i - 1;
|
||||
return 0;
|
||||
} else
|
||||
i = len;
|
||||
}
|
||||
/*
|
||||
* No match, return that we scanned the whole area.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
*rtblock = start + i - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and modify the summary information for a given extent size,
|
||||
* bitmap block combination.
|
||||
* Keeps track of a current summary block, so we don't keep reading
|
||||
* it from the buffer cache.
|
||||
*/
|
||||
int
|
||||
xfs_rtmodify_summary(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
int log, /* log2 of extent size */
|
||||
xfs_rtblock_t bbno, /* bitmap block number */
|
||||
int delta, /* change to make to summary info */
|
||||
xfs_buf_t **rbpp, /* in/out: summary block buffer */
|
||||
xfs_fsblock_t *rsb) /* in/out: summary block number */
|
||||
{
|
||||
xfs_buf_t *bp; /* buffer for the summary block */
|
||||
int error; /* error value */
|
||||
xfs_fsblock_t sb; /* summary fsblock */
|
||||
int so; /* index into the summary file */
|
||||
xfs_suminfo_t *sp; /* pointer to returned data */
|
||||
|
||||
/*
|
||||
* Compute entry number in the summary file.
|
||||
*/
|
||||
so = XFS_SUMOFFS(mp, log, bbno);
|
||||
/*
|
||||
* Compute the block number in the summary file.
|
||||
*/
|
||||
sb = XFS_SUMOFFSTOBLOCK(mp, so);
|
||||
/*
|
||||
* If we have an old buffer, and the block number matches, use that.
|
||||
*/
|
||||
if (rbpp && *rbpp && *rsb == sb)
|
||||
bp = *rbpp;
|
||||
/*
|
||||
* Otherwise we have to get the buffer.
|
||||
*/
|
||||
else {
|
||||
/*
|
||||
* If there was an old one, get rid of it first.
|
||||
*/
|
||||
if (rbpp && *rbpp)
|
||||
xfs_trans_brelse(tp, *rbpp);
|
||||
error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
/*
|
||||
* Remember this buffer and block for the next call.
|
||||
*/
|
||||
if (rbpp) {
|
||||
*rbpp = bp;
|
||||
*rsb = sb;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Point to the summary information, modify and log it.
|
||||
*/
|
||||
sp = XFS_SUMPTR(mp, bp, so);
|
||||
*sp += delta;
|
||||
xfs_trans_log_buf(tp, bp, (uint)((char *)sp - (char *)bp->b_addr),
|
||||
(uint)((char *)sp - (char *)bp->b_addr + sizeof(*sp) - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the given range of bitmap bits to the given value.
|
||||
* Do whatever I/O and logging is required.
|
||||
*/
|
||||
int
|
||||
xfs_rtmodify_range(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t start, /* starting block to modify */
|
||||
xfs_extlen_t len, /* length of extent to modify */
|
||||
int val) /* 1 for free, 0 for allocated */
|
||||
{
|
||||
xfs_rtword_t *b; /* current word in buffer */
|
||||
int bit; /* bit number in the word */
|
||||
xfs_rtblock_t block; /* bitmap block number */
|
||||
xfs_buf_t *bp; /* buf for the block */
|
||||
xfs_rtword_t *bufp; /* starting word in buffer */
|
||||
int error; /* error value */
|
||||
xfs_rtword_t *first; /* first used word in the buffer */
|
||||
int i; /* current bit number rel. to start */
|
||||
int lastbit; /* last useful bit in word */
|
||||
xfs_rtword_t mask; /* mask o frelevant bits for value */
|
||||
int word; /* word number in the buffer */
|
||||
|
||||
/*
|
||||
* Compute starting bitmap block number.
|
||||
*/
|
||||
block = XFS_BITTOBLOCK(mp, start);
|
||||
/*
|
||||
* Read the bitmap block, and point to its data.
|
||||
*/
|
||||
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
bufp = bp->b_addr;
|
||||
/*
|
||||
* Compute the starting word's address, and starting bit.
|
||||
*/
|
||||
word = XFS_BITTOWORD(mp, start);
|
||||
first = b = &bufp[word];
|
||||
bit = (int)(start & (XFS_NBWORD - 1));
|
||||
/*
|
||||
* 0 (allocated) => all zeroes; 1 (free) => all ones.
|
||||
*/
|
||||
val = -val;
|
||||
/*
|
||||
* If not starting on a word boundary, deal with the first
|
||||
* (partial) word.
|
||||
*/
|
||||
if (bit) {
|
||||
/*
|
||||
* Compute first bit not changed and mask of relevant bits.
|
||||
*/
|
||||
lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
|
||||
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
|
||||
/*
|
||||
* Set/clear the active bits.
|
||||
*/
|
||||
if (val)
|
||||
*b |= mask;
|
||||
else
|
||||
*b &= ~mask;
|
||||
i = lastbit - bit;
|
||||
/*
|
||||
* Go on to the next block if that's where the next word is
|
||||
* and we need the next word.
|
||||
*/
|
||||
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
|
||||
/*
|
||||
* Log the changed part of this block.
|
||||
* Get the next one.
|
||||
*/
|
||||
xfs_trans_log_buf(tp, bp,
|
||||
(uint)((char *)first - (char *)bufp),
|
||||
(uint)((char *)b - (char *)bufp));
|
||||
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
first = b = bufp = bp->b_addr;
|
||||
word = 0;
|
||||
} else {
|
||||
/*
|
||||
* Go on to the next word in the buffer
|
||||
*/
|
||||
b++;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Starting on a word boundary, no partial word.
|
||||
*/
|
||||
i = 0;
|
||||
}
|
||||
/*
|
||||
* Loop over whole words in buffers. When we use up one buffer
|
||||
* we move on to the next one.
|
||||
*/
|
||||
while (len - i >= XFS_NBWORD) {
|
||||
/*
|
||||
* Set the word value correctly.
|
||||
*/
|
||||
*b = val;
|
||||
i += XFS_NBWORD;
|
||||
/*
|
||||
* Go on to the next block if that's where the next word is
|
||||
* and we need the next word.
|
||||
*/
|
||||
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
|
||||
/*
|
||||
* Log the changed part of this block.
|
||||
* Get the next one.
|
||||
*/
|
||||
xfs_trans_log_buf(tp, bp,
|
||||
(uint)((char *)first - (char *)bufp),
|
||||
(uint)((char *)b - (char *)bufp));
|
||||
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
first = b = bufp = bp->b_addr;
|
||||
word = 0;
|
||||
} else {
|
||||
/*
|
||||
* Go on to the next word in the buffer
|
||||
*/
|
||||
b++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If not ending on a word boundary, deal with the last
|
||||
* (partial) word.
|
||||
*/
|
||||
if ((lastbit = len - i)) {
|
||||
/*
|
||||
* Compute a mask of relevant bits.
|
||||
*/
|
||||
bit = 0;
|
||||
mask = ((xfs_rtword_t)1 << lastbit) - 1;
|
||||
/*
|
||||
* Set/clear the active bits.
|
||||
*/
|
||||
if (val)
|
||||
*b |= mask;
|
||||
else
|
||||
*b &= ~mask;
|
||||
b++;
|
||||
}
|
||||
/*
|
||||
* Log any remaining changed bytes.
|
||||
*/
|
||||
if (b > first)
|
||||
xfs_trans_log_buf(tp, bp, (uint)((char *)first - (char *)bufp),
|
||||
(uint)((char *)b - (char *)bufp - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark an extent specified by start and len freed.
|
||||
* Updates all the summary information as well as the bitmap.
|
||||
*/
|
||||
int
|
||||
xfs_rtfree_range(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t start, /* starting block to free */
|
||||
xfs_extlen_t len, /* length to free */
|
||||
xfs_buf_t **rbpp, /* in/out: summary block buffer */
|
||||
xfs_fsblock_t *rsb) /* in/out: summary block number */
|
||||
{
|
||||
xfs_rtblock_t end; /* end of the freed extent */
|
||||
int error; /* error value */
|
||||
xfs_rtblock_t postblock; /* first block freed > end */
|
||||
xfs_rtblock_t preblock; /* first block freed < start */
|
||||
|
||||
end = start + len - 1;
|
||||
/*
|
||||
* Modify the bitmap to mark this extent freed.
|
||||
*/
|
||||
error = xfs_rtmodify_range(mp, tp, start, len, 1);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
/*
|
||||
* Assume we're freeing out of the middle of an allocated extent.
|
||||
* We need to find the beginning and end of the extent so we can
|
||||
* properly update the summary.
|
||||
*/
|
||||
error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
/*
|
||||
* Find the next allocated block (end of allocated extent).
|
||||
*/
|
||||
error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
|
||||
&postblock);
|
||||
if (error)
|
||||
return error;
|
||||
/*
|
||||
* If there are blocks not being freed at the front of the
|
||||
* old extent, add summary data for them to be allocated.
|
||||
*/
|
||||
if (preblock < start) {
|
||||
error = xfs_rtmodify_summary(mp, tp,
|
||||
XFS_RTBLOCKLOG(start - preblock),
|
||||
XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If there are blocks not being freed at the end of the
|
||||
* old extent, add summary data for them to be allocated.
|
||||
*/
|
||||
if (postblock > end) {
|
||||
error = xfs_rtmodify_summary(mp, tp,
|
||||
XFS_RTBLOCKLOG(postblock - end),
|
||||
XFS_BITTOBLOCK(mp, end + 1), -1, rbpp, rsb);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Increment the summary information corresponding to the entire
|
||||
* (new) free extent.
|
||||
*/
|
||||
error = xfs_rtmodify_summary(mp, tp,
|
||||
XFS_RTBLOCKLOG(postblock + 1 - preblock),
|
||||
XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the given range is either all allocated (val = 0) or
|
||||
* all free (val = 1).
|
||||
*/
|
||||
int
|
||||
xfs_rtcheck_range(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t start, /* starting block number of extent */
|
||||
xfs_extlen_t len, /* length of extent */
|
||||
int val, /* 1 for free, 0 for allocated */
|
||||
xfs_rtblock_t *new, /* out: first block not matching */
|
||||
int *stat) /* out: 1 for matches, 0 for not */
|
||||
{
|
||||
xfs_rtword_t *b; /* current word in buffer */
|
||||
int bit; /* bit number in the word */
|
||||
xfs_rtblock_t block; /* bitmap block number */
|
||||
xfs_buf_t *bp; /* buf for the block */
|
||||
xfs_rtword_t *bufp; /* starting word in buffer */
|
||||
int error; /* error value */
|
||||
xfs_rtblock_t i; /* current bit number rel. to start */
|
||||
xfs_rtblock_t lastbit; /* last useful bit in word */
|
||||
xfs_rtword_t mask; /* mask of relevant bits for value */
|
||||
xfs_rtword_t wdiff; /* difference from wanted value */
|
||||
int word; /* word number in the buffer */
|
||||
|
||||
/*
|
||||
* Compute starting bitmap block number
|
||||
*/
|
||||
block = XFS_BITTOBLOCK(mp, start);
|
||||
/*
|
||||
* Read the bitmap block.
|
||||
*/
|
||||
error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
bufp = bp->b_addr;
|
||||
/*
|
||||
* Compute the starting word's address, and starting bit.
|
||||
*/
|
||||
word = XFS_BITTOWORD(mp, start);
|
||||
b = &bufp[word];
|
||||
bit = (int)(start & (XFS_NBWORD - 1));
|
||||
/*
|
||||
* 0 (allocated) => all zero's; 1 (free) => all one's.
|
||||
*/
|
||||
val = -val;
|
||||
/*
|
||||
* If not starting on a word boundary, deal with the first
|
||||
* (partial) word.
|
||||
*/
|
||||
if (bit) {
|
||||
/*
|
||||
* Compute first bit not examined.
|
||||
*/
|
||||
lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
|
||||
/*
|
||||
* Mask of relevant bits.
|
||||
*/
|
||||
mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = (*b ^ val) & mask)) {
|
||||
/*
|
||||
* Different, compute first wrong bit and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i = XFS_RTLOBIT(wdiff) - bit;
|
||||
*new = start + i;
|
||||
*stat = 0;
|
||||
return 0;
|
||||
}
|
||||
i = lastbit - bit;
|
||||
/*
|
||||
* Go on to next block if that's where the next word is
|
||||
* and we need the next word.
|
||||
*/
|
||||
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
|
||||
/*
|
||||
* If done with this block, get the next one.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
b = bufp = bp->b_addr;
|
||||
word = 0;
|
||||
} else {
|
||||
/*
|
||||
* Go on to the next word in the buffer.
|
||||
*/
|
||||
b++;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Starting on a word boundary, no partial word.
|
||||
*/
|
||||
i = 0;
|
||||
}
|
||||
/*
|
||||
* Loop over whole words in buffers. When we use up one buffer
|
||||
* we move on to the next one.
|
||||
*/
|
||||
while (len - i >= XFS_NBWORD) {
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = *b ^ val)) {
|
||||
/*
|
||||
* Different, compute first wrong bit and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i += XFS_RTLOBIT(wdiff);
|
||||
*new = start + i;
|
||||
*stat = 0;
|
||||
return 0;
|
||||
}
|
||||
i += XFS_NBWORD;
|
||||
/*
|
||||
* Go on to next block if that's where the next word is
|
||||
* and we need the next word.
|
||||
*/
|
||||
if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
|
||||
/*
|
||||
* If done with this block, get the next one.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
b = bufp = bp->b_addr;
|
||||
word = 0;
|
||||
} else {
|
||||
/*
|
||||
* Go on to the next word in the buffer.
|
||||
*/
|
||||
b++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If not ending on a word boundary, deal with the last
|
||||
* (partial) word.
|
||||
*/
|
||||
if ((lastbit = len - i)) {
|
||||
/*
|
||||
* Mask of relevant bits.
|
||||
*/
|
||||
mask = ((xfs_rtword_t)1 << lastbit) - 1;
|
||||
/*
|
||||
* Compute difference between actual and desired value.
|
||||
*/
|
||||
if ((wdiff = (*b ^ val) & mask)) {
|
||||
/*
|
||||
* Different, compute first wrong bit and return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
i += XFS_RTLOBIT(wdiff);
|
||||
*new = start + i;
|
||||
*stat = 0;
|
||||
return 0;
|
||||
} else
|
||||
i = len;
|
||||
}
|
||||
/*
|
||||
* Successful, return.
|
||||
*/
|
||||
xfs_trans_brelse(tp, bp);
|
||||
*new = start + i;
|
||||
*stat = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Check that the given extent (block range) is allocated already.
|
||||
*/
|
||||
STATIC int /* error */
|
||||
xfs_rtcheck_alloc_range(
|
||||
xfs_mount_t *mp, /* file system mount point */
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t bno, /* starting block number of extent */
|
||||
xfs_extlen_t len) /* length of extent */
|
||||
{
|
||||
xfs_rtblock_t new; /* dummy for xfs_rtcheck_range */
|
||||
int stat;
|
||||
int error;
|
||||
|
||||
error = xfs_rtcheck_range(mp, tp, bno, len, 0, &new, &stat);
|
||||
if (error)
|
||||
return error;
|
||||
ASSERT(stat);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define xfs_rtcheck_alloc_range(m,t,b,l) (0)
|
||||
#endif
|
||||
/*
|
||||
* Free an extent in the realtime subvolume. Length is expressed in
|
||||
* realtime extents, as is the block number.
|
||||
*/
|
||||
int /* error */
|
||||
xfs_rtfree_extent(
|
||||
xfs_trans_t *tp, /* transaction pointer */
|
||||
xfs_rtblock_t bno, /* starting block number to free */
|
||||
xfs_extlen_t len) /* length of extent freed */
|
||||
{
|
||||
int error; /* error value */
|
||||
xfs_mount_t *mp; /* file system mount structure */
|
||||
xfs_fsblock_t sb; /* summary file block number */
|
||||
xfs_buf_t *sumbp = NULL; /* summary file block buffer */
|
||||
|
||||
mp = tp->t_mountp;
|
||||
|
||||
ASSERT(mp->m_rbmip->i_itemp != NULL);
|
||||
ASSERT(xfs_isilocked(mp->m_rbmip, XFS_ILOCK_EXCL));
|
||||
|
||||
error = xfs_rtcheck_alloc_range(mp, tp, bno, len);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Free the range of realtime blocks.
|
||||
*/
|
||||
error = xfs_rtfree_range(mp, tp, bno, len, &sumbp, &sb);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
/*
|
||||
* Mark more blocks free in the superblock.
|
||||
*/
|
||||
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);
|
||||
/*
|
||||
* If we've now freed all the blocks, reset the file sequence
|
||||
* number to 0.
|
||||
*/
|
||||
if (tp->t_frextents_delta + mp->m_sb.sb_frextents ==
|
||||
mp->m_sb.sb_rextents) {
|
||||
if (!(mp->m_rbmip->i_d.di_flags & XFS_DIFLAG_NEWRTBM))
|
||||
mp->m_rbmip->i_d.di_flags |= XFS_DIFLAG_NEWRTBM;
|
||||
*(__uint64_t *)&mp->m_rbmip->i_d.di_atime = 0;
|
||||
xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
201
fs/xfs/libxfs/xfs_symlink_remote.c
Normal file
201
fs/xfs/libxfs/xfs_symlink_remote.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2006 Silicon Graphics, Inc.
|
||||
* Copyright (c) 2012-2013 Red Hat, 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_trace.h"
|
||||
#include "xfs_symlink.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_buf_item.h"
|
||||
|
||||
|
||||
/*
|
||||
* Each contiguous block has a header, so it is not just a simple pathlen
|
||||
* to FSB conversion.
|
||||
*/
|
||||
int
|
||||
xfs_symlink_blocks(
|
||||
struct xfs_mount *mp,
|
||||
int pathlen)
|
||||
{
|
||||
int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
|
||||
|
||||
return (pathlen + buflen - 1) / buflen;
|
||||
}
|
||||
|
||||
int
|
||||
xfs_symlink_hdr_set(
|
||||
struct xfs_mount *mp,
|
||||
xfs_ino_t ino,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_dsymlink_hdr *dsl = bp->b_addr;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return 0;
|
||||
|
||||
dsl->sl_magic = cpu_to_be32(XFS_SYMLINK_MAGIC);
|
||||
dsl->sl_offset = cpu_to_be32(offset);
|
||||
dsl->sl_bytes = cpu_to_be32(size);
|
||||
uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_uuid);
|
||||
dsl->sl_owner = cpu_to_be64(ino);
|
||||
dsl->sl_blkno = cpu_to_be64(bp->b_bn);
|
||||
bp->b_ops = &xfs_symlink_buf_ops;
|
||||
|
||||
return sizeof(struct xfs_dsymlink_hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checking of the symlink header is split into two parts. the verifier does
|
||||
* CRC, location and bounds checking, the unpacking function checks the path
|
||||
* parameters and owner.
|
||||
*/
|
||||
bool
|
||||
xfs_symlink_hdr_ok(
|
||||
xfs_ino_t ino,
|
||||
uint32_t offset,
|
||||
uint32_t size,
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_dsymlink_hdr *dsl = bp->b_addr;
|
||||
|
||||
if (offset != be32_to_cpu(dsl->sl_offset))
|
||||
return false;
|
||||
if (size != be32_to_cpu(dsl->sl_bytes))
|
||||
return false;
|
||||
if (ino != be64_to_cpu(dsl->sl_owner))
|
||||
return false;
|
||||
|
||||
/* ok */
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
xfs_symlink_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
struct xfs_dsymlink_hdr *dsl = bp->b_addr;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return false;
|
||||
if (dsl->sl_magic != cpu_to_be32(XFS_SYMLINK_MAGIC))
|
||||
return false;
|
||||
if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_uuid))
|
||||
return false;
|
||||
if (bp->b_bn != be64_to_cpu(dsl->sl_blkno))
|
||||
return false;
|
||||
if (be32_to_cpu(dsl->sl_offset) +
|
||||
be32_to_cpu(dsl->sl_bytes) >= MAXPATHLEN)
|
||||
return false;
|
||||
if (dsl->sl_owner == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_symlink_read_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
|
||||
/* no verification of non-crc buffers */
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return;
|
||||
|
||||
if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF))
|
||||
xfs_buf_ioerror(bp, EFSBADCRC);
|
||||
else if (!xfs_symlink_verify(bp))
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
|
||||
if (bp->b_error)
|
||||
xfs_verifier_error(bp);
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_symlink_write_verify(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_mount *mp = bp->b_target->bt_mount;
|
||||
struct xfs_buf_log_item *bip = bp->b_fspriv;
|
||||
|
||||
/* no verification of non-crc buffers */
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return;
|
||||
|
||||
if (!xfs_symlink_verify(bp)) {
|
||||
xfs_buf_ioerror(bp, EFSCORRUPTED);
|
||||
xfs_verifier_error(bp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bip) {
|
||||
struct xfs_dsymlink_hdr *dsl = bp->b_addr;
|
||||
dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn);
|
||||
}
|
||||
xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF);
|
||||
}
|
||||
|
||||
const struct xfs_buf_ops xfs_symlink_buf_ops = {
|
||||
.verify_read = xfs_symlink_read_verify,
|
||||
.verify_write = xfs_symlink_write_verify,
|
||||
};
|
||||
|
||||
void
|
||||
xfs_symlink_local_to_remote(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_buf *bp,
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_ifork *ifp)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
char *buf;
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
bp->b_ops = NULL;
|
||||
memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* As this symlink fits in an inode literal area, it must also fit in
|
||||
* the smallest buffer the filesystem supports.
|
||||
*/
|
||||
ASSERT(BBTOB(bp->b_length) >=
|
||||
ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr));
|
||||
|
||||
bp->b_ops = &xfs_symlink_buf_ops;
|
||||
|
||||
buf = bp->b_addr;
|
||||
buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp);
|
||||
memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes);
|
||||
}
|
894
fs/xfs/libxfs/xfs_trans_resv.c
Normal file
894
fs/xfs/libxfs/xfs_trans_resv.c
Normal file
@@ -0,0 +1,894 @@
|
||||
/*
|
||||
* Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
|
||||
* Copyright (C) 2010 Red Hat, 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 "xfs.h"
|
||||
#include "xfs_fs.h"
|
||||
#include "xfs_shared.h"
|
||||
#include "xfs_format.h"
|
||||
#include "xfs_log_format.h"
|
||||
#include "xfs_trans_resv.h"
|
||||
#include "xfs_sb.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_da_format.h"
|
||||
#include "xfs_da_btree.h"
|
||||
#include "xfs_inode.h"
|
||||
#include "xfs_bmap_btree.h"
|
||||
#include "xfs_ialloc.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_qm.h"
|
||||
#include "xfs_trans_space.h"
|
||||
#include "xfs_trace.h"
|
||||
|
||||
/*
|
||||
* A buffer has a format structure overhead in the log in addition
|
||||
* to the data, so we need to take this into account when reserving
|
||||
* space in a transaction for a buffer. Round the space required up
|
||||
* to a multiple of 128 bytes so that we don't change the historical
|
||||
* reservation that has been used for this overhead.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_buf_log_overhead(void)
|
||||
{
|
||||
return round_up(sizeof(struct xlog_op_header) +
|
||||
sizeof(struct xfs_buf_log_format), 128);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate out transaction log reservation per item in bytes.
|
||||
*
|
||||
* The nbufs argument is used to indicate the number of items that
|
||||
* will be changed in a transaction. size is used to tell how many
|
||||
* bytes should be reserved per item.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_buf_res(
|
||||
uint nbufs,
|
||||
uint size)
|
||||
{
|
||||
return nbufs * (size + xfs_buf_log_overhead());
|
||||
}
|
||||
|
||||
/*
|
||||
* Logging inodes is really tricksy. They are logged in memory format,
|
||||
* which means that what we write into the log doesn't directly translate into
|
||||
* the amount of space they use on disk.
|
||||
*
|
||||
* Case in point - btree format forks in memory format use more space than the
|
||||
* on-disk format. In memory, the buffer contains a normal btree block header so
|
||||
* the btree code can treat it as though it is just another generic buffer.
|
||||
* However, when we write it to the inode fork, we don't write all of this
|
||||
* header as it isn't needed. e.g. the root is only ever in the inode, so
|
||||
* there's no need for sibling pointers which would waste 16 bytes of space.
|
||||
*
|
||||
* Hence when we have an inode with a maximally sized btree format fork, then
|
||||
* amount of information we actually log is greater than the size of the inode
|
||||
* on disk. Hence we need an inode reservation function that calculates all this
|
||||
* correctly. So, we log:
|
||||
*
|
||||
* - 4 log op headers for object
|
||||
* - for the ilf, the inode core and 2 forks
|
||||
* - inode log format object
|
||||
* - the inode core
|
||||
* - two inode forks containing bmap btree root blocks.
|
||||
* - the btree data contained by both forks will fit into the inode size,
|
||||
* hence when combined with the inode core above, we have a total of the
|
||||
* actual inode size.
|
||||
* - the BMBT headers need to be accounted separately, as they are
|
||||
* additional to the records and pointers that fit inside the inode
|
||||
* forks.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_inode_res(
|
||||
struct xfs_mount *mp,
|
||||
uint ninodes)
|
||||
{
|
||||
return ninodes *
|
||||
(4 * sizeof(struct xlog_op_header) +
|
||||
sizeof(struct xfs_inode_log_format) +
|
||||
mp->m_sb.sb_inodesize +
|
||||
2 * XFS_BMBT_BLOCK_LEN(mp));
|
||||
}
|
||||
|
||||
/*
|
||||
* The free inode btree is a conditional feature and the log reservation
|
||||
* requirements differ slightly from that of the traditional inode allocation
|
||||
* btree. The finobt tracks records for inode chunks with at least one free
|
||||
* inode. A record can be removed from the tree for an inode allocation
|
||||
* or free and thus the finobt reservation is unconditional across:
|
||||
*
|
||||
* - inode allocation
|
||||
* - inode free
|
||||
* - inode chunk allocation
|
||||
*
|
||||
* The 'modify' param indicates to include the record modification scenario. The
|
||||
* 'alloc' param indicates to include the reservation for free space btree
|
||||
* modifications on behalf of finobt modifications. This is required only for
|
||||
* transactions that do not already account for free space btree modifications.
|
||||
*
|
||||
* the free inode btree: max depth * block size
|
||||
* the allocation btrees: 2 trees * (max depth - 1) * block size
|
||||
* the free inode btree entry: block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_finobt_res(
|
||||
struct xfs_mount *mp,
|
||||
int alloc,
|
||||
int modify)
|
||||
{
|
||||
uint res;
|
||||
|
||||
if (!xfs_sb_version_hasfinobt(&mp->m_sb))
|
||||
return 0;
|
||||
|
||||
res = xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1));
|
||||
if (alloc)
|
||||
res += xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
if (modify)
|
||||
res += (uint)XFS_FSB_TO_B(mp, 1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Various log reservation values.
|
||||
*
|
||||
* These are based on the size of the file system block because that is what
|
||||
* most transactions manipulate. Each adds in an additional 128 bytes per
|
||||
* item logged to try to account for the overhead of the transaction mechanism.
|
||||
*
|
||||
* Note: Most of the reservations underestimate the number of allocation
|
||||
* groups into which they could free extents in the xfs_bmap_finish() call.
|
||||
* This is because the number in the worst case is quite high and quite
|
||||
* unusual. In order to fix this we need to change xfs_bmap_finish() to free
|
||||
* extents in only a single AG at a time. This will require changes to the
|
||||
* EFI code as well, however, so that the EFI for the extents not freed is
|
||||
* logged again in each transaction. See SGI PV #261917.
|
||||
*
|
||||
* Reservation functions here avoid a huge stack in xfs_trans_init due to
|
||||
* register overflow from temporaries in the calculations.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* In a write transaction we can allocate a maximum of 2
|
||||
* extents. This gives:
|
||||
* the inode getting the new extents: inode size
|
||||
* the inode's bmap btree: max depth * block size
|
||||
* the agfs of the ags from which the extents are allocated: 2 * sector
|
||||
* the superblock free block counter: sector size
|
||||
* the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
* And the bmap_finish transaction can free bmap blocks in a join:
|
||||
* the agfs of the ags containing the blocks: 2 * sector size
|
||||
* the agfls of the ags containing the blocks: 2 * sector size
|
||||
* the super block free block counter: sector size
|
||||
* the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_write_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
MAX((xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK),
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),
|
||||
XFS_FSB_TO_B(mp, 1))),
|
||||
(xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),
|
||||
XFS_FSB_TO_B(mp, 1))));
|
||||
}
|
||||
|
||||
/*
|
||||
* In truncating a file we free up to two extents at once. We can modify:
|
||||
* the inode being truncated: inode size
|
||||
* the inode's bmap btree: (max depth + 1) * block size
|
||||
* And the bmap_finish transaction can free the blocks and bmap blocks:
|
||||
* the agf for each of the ags: 4 * sector size
|
||||
* the agfl for each of the ags: 4 * sector size
|
||||
* the super block to reflect the freed blocks: sector size
|
||||
* worst case split in allocation btrees per extent assuming 4 extents:
|
||||
* 4 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
* the inode btree: max depth * blocksize
|
||||
* the allocation btrees: 2 trees * (max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_itruncate_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
MAX((xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1,
|
||||
XFS_FSB_TO_B(mp, 1))),
|
||||
(xfs_calc_buf_res(9, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 4),
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(5, 0) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(2 + mp->m_ialloc_blks +
|
||||
mp->m_in_maxlevels, 0)));
|
||||
}
|
||||
|
||||
/*
|
||||
* In renaming a files we can modify:
|
||||
* the four inodes involved: 4 * inode size
|
||||
* the two directory btrees: 2 * (max depth + v2) * dir block size
|
||||
* the two directory bmap btrees: 2 * max depth * block size
|
||||
* And the bmap_finish transaction can free dir and bmap blocks (two sets
|
||||
* of bmap blocks) giving:
|
||||
* the agf for the ags in which the blocks live: 3 * sector size
|
||||
* the agfl for the ags in which the blocks live: 3 * sector size
|
||||
* the superblock for the free block count: sector size
|
||||
* the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_rename_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
MAX((xfs_calc_inode_res(mp, 4) +
|
||||
xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
|
||||
XFS_FSB_TO_B(mp, 1))),
|
||||
(xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 3),
|
||||
XFS_FSB_TO_B(mp, 1))));
|
||||
}
|
||||
|
||||
/*
|
||||
* For removing an inode from unlinked list at first, we can modify:
|
||||
* the agi hash list and counters: sector size
|
||||
* the on disk inode before ours in the agi hash list: inode cluster size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_iunlink_remove_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
max_t(uint, XFS_FSB_TO_B(mp, 1), mp->m_inode_cluster_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* For creating a link to an inode:
|
||||
* the parent directory inode: inode size
|
||||
* the linked inode: inode size
|
||||
* the directory btree could split: (max depth + v2) * dir block size
|
||||
* the directory bmap btree could join or split: (max depth + v2) * blocksize
|
||||
* And the bmap_finish transaction can free some bmap blocks giving:
|
||||
* the agf for the ag in which the blocks live: sector size
|
||||
* the agfl for the ag in which the blocks live: sector size
|
||||
* the superblock for the free block count: sector size
|
||||
* the allocation btrees: 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_link_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
xfs_calc_iunlink_remove_reservation(mp) +
|
||||
MAX((xfs_calc_inode_res(mp, 2) +
|
||||
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
|
||||
XFS_FSB_TO_B(mp, 1))),
|
||||
(xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1))));
|
||||
}
|
||||
|
||||
/*
|
||||
* For adding an inode to unlinked list we can modify:
|
||||
* the agi hash list: sector size
|
||||
* the unlinked inode: inode size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_inode_res(mp, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* For removing a directory entry we can modify:
|
||||
* the parent directory inode: inode size
|
||||
* the removed inode: inode size
|
||||
* the directory btree could join: (max depth + v2) * dir block size
|
||||
* the directory bmap btree could join or split: (max depth + v2) * blocksize
|
||||
* And the bmap_finish transaction can free the dir and bmap blocks giving:
|
||||
* the agf for the ag in which the blocks live: 2 * sector size
|
||||
* the agfl for the ag in which the blocks live: 2 * sector size
|
||||
* the superblock for the free block count: sector size
|
||||
* the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_remove_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
xfs_calc_iunlink_add_reservation(mp) +
|
||||
MAX((xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
|
||||
XFS_FSB_TO_B(mp, 1))),
|
||||
(xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),
|
||||
XFS_FSB_TO_B(mp, 1))));
|
||||
}
|
||||
|
||||
/*
|
||||
* For create, break it in to the two cases that the transaction
|
||||
* covers. We start with the modify case - allocation done by modification
|
||||
* of the state of existing inodes - and the allocation case.
|
||||
*/
|
||||
|
||||
/*
|
||||
* For create we can modify:
|
||||
* the parent directory inode: inode size
|
||||
* the new inode: inode size
|
||||
* the inode btree entry: block size
|
||||
* the superblock for the nlink flag: sector size
|
||||
* the directory btree: (max depth + v2) * dir block size
|
||||
* the directory inode's bmap btree: (max depth + v2) * block size
|
||||
* the finobt (record modification and allocation btrees)
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_create_resv_modify(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_inode_res(mp, 2) +
|
||||
xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
(uint)XFS_FSB_TO_B(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_finobt_res(mp, 1, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* For create we can allocate some inodes giving:
|
||||
* the agi and agf of the ag getting the new inodes: 2 * sectorsize
|
||||
* the superblock for the nlink flag: sector size
|
||||
* the inode blocks allocated: mp->m_ialloc_blks * blocksize
|
||||
* the inode btree: max depth * blocksize
|
||||
* the allocation btrees: 2 trees * (max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_create_resv_alloc(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
|
||||
mp->m_sb.sb_sectsize +
|
||||
xfs_calc_buf_res(mp->m_ialloc_blks, XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
}
|
||||
|
||||
STATIC uint
|
||||
__xfs_calc_create_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
MAX(xfs_calc_create_resv_alloc(mp),
|
||||
xfs_calc_create_resv_modify(mp));
|
||||
}
|
||||
|
||||
/*
|
||||
* For icreate we can allocate some inodes giving:
|
||||
* the agi and agf of the ag getting the new inodes: 2 * sectorsize
|
||||
* the superblock for the nlink flag: sector size
|
||||
* the inode btree: max depth * blocksize
|
||||
* the allocation btrees: 2 trees * (max depth - 1) * block size
|
||||
* the finobt (record insertion)
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_icreate_resv_alloc(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
|
||||
mp->m_sb.sb_sectsize +
|
||||
xfs_calc_buf_res(mp->m_in_maxlevels, XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_finobt_res(mp, 0, 0);
|
||||
}
|
||||
|
||||
STATIC uint
|
||||
xfs_calc_icreate_reservation(xfs_mount_t *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
MAX(xfs_calc_icreate_resv_alloc(mp),
|
||||
xfs_calc_create_resv_modify(mp));
|
||||
}
|
||||
|
||||
STATIC uint
|
||||
xfs_calc_create_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
return xfs_calc_icreate_reservation(mp);
|
||||
return __xfs_calc_create_reservation(mp);
|
||||
|
||||
}
|
||||
|
||||
STATIC uint
|
||||
xfs_calc_create_tmpfile_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
uint res = XFS_DQUOT_LOGRES(mp);
|
||||
|
||||
if (xfs_sb_version_hascrc(&mp->m_sb))
|
||||
res += xfs_calc_icreate_resv_alloc(mp);
|
||||
else
|
||||
res += xfs_calc_create_resv_alloc(mp);
|
||||
|
||||
return res + xfs_calc_iunlink_add_reservation(mp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Making a new directory is the same as creating a new file.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_mkdir_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_create_reservation(mp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Making a new symplink is the same as creating a new file, but
|
||||
* with the added blocks for remote symlink data which can be up to 1kB in
|
||||
* length (MAXPATHLEN).
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_symlink_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_create_reservation(mp) +
|
||||
xfs_calc_buf_res(1, MAXPATHLEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* In freeing an inode we can modify:
|
||||
* the inode being freed: inode size
|
||||
* the super block free inode counter: sector size
|
||||
* the agi hash list and counters: sector size
|
||||
* the inode btree entry: block size
|
||||
* the on disk inode before ours in the agi hash list: inode cluster size
|
||||
* the inode btree: max depth * blocksize
|
||||
* the allocation btrees: 2 trees * (max depth - 1) * block size
|
||||
* the finobt (record insertion, removal or modification)
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_ifree_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(1, XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_iunlink_remove_reservation(mp) +
|
||||
xfs_calc_buf_res(1, 0) +
|
||||
xfs_calc_buf_res(2 + mp->m_ialloc_blks +
|
||||
mp->m_in_maxlevels, 0) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_finobt_res(mp, 0, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* When only changing the inode we log the inode and possibly the superblock
|
||||
* We also add a bit of slop for the transaction stuff.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_ichange_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Growing the data section of the filesystem.
|
||||
* superblock
|
||||
* agi and agf
|
||||
* allocation btrees
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_growdata_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Growing the rt section of the filesystem.
|
||||
* In the first set of transactions (ALLOC) we allocate space to the
|
||||
* bitmap or summary files.
|
||||
* superblock: sector size
|
||||
* agf of the ag from which the extent is allocated: sector size
|
||||
* bmap btree for bitmap/summary inode: max depth * blocksize
|
||||
* bitmap/summary inode: inode size
|
||||
* allocation btrees for 1 block alloc: 2 * (2 * maxdepth - 1) * blocksize
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_growrtalloc_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK),
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Growing the rt section of the filesystem.
|
||||
* In the second set of transactions (ZERO) we zero the new metadata blocks.
|
||||
* one bitmap/summary block: blocksize
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_growrtzero_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Growing the rt section of the filesystem.
|
||||
* In the third set of transactions (FREE) we update metadata without
|
||||
* allocating any new blocks.
|
||||
* superblock: sector size
|
||||
* bitmap inode: inode size
|
||||
* summary inode: inode size
|
||||
* one bitmap block: blocksize
|
||||
* summary blocks: new summary size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_growrtfree_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_inode_res(mp, 2) +
|
||||
xfs_calc_buf_res(1, mp->m_sb.sb_blocksize) +
|
||||
xfs_calc_buf_res(1, mp->m_rsumsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Logging the inode modification timestamp on a synchronous write.
|
||||
* inode
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_swrite_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_inode_res(mp, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Logging the inode mode bits when writing a setuid/setgid file
|
||||
* inode
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_writeid_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_inode_res(mp, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converting the inode from non-attributed to attributed.
|
||||
* the inode being converted: inode size
|
||||
* agf block and superblock (for block allocation)
|
||||
* the new block (directory sized)
|
||||
* bmap blocks for the new directory block
|
||||
* allocation btrees
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_addafork_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(1, mp->m_dir_geo->blksize) +
|
||||
xfs_calc_buf_res(XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1,
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 1),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Removing the attribute fork of a file
|
||||
* the inode being truncated: inode size
|
||||
* the inode's bmap btree: max depth * block size
|
||||
* And the bmap_finish transaction can free the blocks and bmap blocks:
|
||||
* the agf for each of the ags: 4 * sector size
|
||||
* the agfl for each of the ags: 4 * sector size
|
||||
* the super block to reflect the freed blocks: sector size
|
||||
* worst case split in allocation btrees per extent assuming 4 extents:
|
||||
* 4 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_attrinval_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return MAX((xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK),
|
||||
XFS_FSB_TO_B(mp, 1))),
|
||||
(xfs_calc_buf_res(9, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 4),
|
||||
XFS_FSB_TO_B(mp, 1))));
|
||||
}
|
||||
|
||||
/*
|
||||
* Setting an attribute at mount time.
|
||||
* the inode getting the attribute
|
||||
* the superblock for allocations
|
||||
* the agfs extents are allocated from
|
||||
* the attribute btree * max depth
|
||||
* the inode allocation btree
|
||||
* Since attribute transaction space is dependent on the size of the attribute,
|
||||
* the calculation is done partially at mount time and partially at runtime(see
|
||||
* below).
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_attrsetm_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_DA_NODE_MAXDEPTH, XFS_FSB_TO_B(mp, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Setting an attribute at runtime, transaction space unit per block.
|
||||
* the superblock for allocations: sector size
|
||||
* the inode bmap btree could join or split: max depth * block size
|
||||
* Since the runtime attribute transaction space is dependent on the total
|
||||
* blocks needed for the 1st bmap, here we calculate out the space unit for
|
||||
* one block so that the caller could figure out the total space according
|
||||
* to the attibute extent length in blocks by:
|
||||
* ext * M_RES(mp)->tr_attrsetrt.tr_logres
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_attrsetrt_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK),
|
||||
XFS_FSB_TO_B(mp, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Removing an attribute.
|
||||
* the inode: inode size
|
||||
* the attribute btree could join: max depth * block size
|
||||
* the inode bmap btree could join or split: max depth * block size
|
||||
* And the bmap_finish transaction can free the attr blocks freed giving:
|
||||
* the agf for the ag in which the blocks live: 2 * sector size
|
||||
* the agfl for the ag in which the blocks live: 2 * sector size
|
||||
* the superblock for the free block count: sector size
|
||||
* the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_attrrm_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return XFS_DQUOT_LOGRES(mp) +
|
||||
MAX((xfs_calc_inode_res(mp, 1) +
|
||||
xfs_calc_buf_res(XFS_DA_NODE_MAXDEPTH,
|
||||
XFS_FSB_TO_B(mp, 1)) +
|
||||
(uint)XFS_FSB_TO_B(mp,
|
||||
XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) +
|
||||
xfs_calc_buf_res(XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK), 0)),
|
||||
(xfs_calc_buf_res(5, mp->m_sb.sb_sectsize) +
|
||||
xfs_calc_buf_res(XFS_ALLOCFREE_LOG_COUNT(mp, 2),
|
||||
XFS_FSB_TO_B(mp, 1))));
|
||||
}
|
||||
|
||||
/*
|
||||
* Clearing a bad agino number in an agi hash bucket.
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_clear_agi_bucket_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clearing the quotaflags in the superblock.
|
||||
* the super block for changing quota flags: sector size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_qm_sbchange_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjusting quota limits.
|
||||
* the xfs_disk_dquot_t: sizeof(struct xfs_disk_dquot)
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_qm_setqlim_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, sizeof(struct xfs_disk_dquot));
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocating quota on disk if needed.
|
||||
* the write transaction log space for quota file extent allocation
|
||||
* the unit of quota allocation: one system block size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_qm_dqalloc_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_write_reservation(mp) +
|
||||
xfs_calc_buf_res(1,
|
||||
XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB) - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turning off quotas.
|
||||
* the xfs_qoff_logitem_t: sizeof(struct xfs_qoff_logitem) * 2
|
||||
* the superblock for the quota flags: sector size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_qm_quotaoff_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return sizeof(struct xfs_qoff_logitem) * 2 +
|
||||
xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* End of turning off quotas.
|
||||
* the xfs_qoff_logitem_t: sizeof(struct xfs_qoff_logitem) * 2
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_qm_quotaoff_end_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return sizeof(struct xfs_qoff_logitem) * 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Syncing the incore super block changes to disk.
|
||||
* the super block to reflect the changes: sector size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_sb_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
}
|
||||
|
||||
void
|
||||
xfs_trans_resv_calc(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_trans_resv *resp)
|
||||
{
|
||||
/*
|
||||
* The following transactions are logged in physical format and
|
||||
* require a permanent reservation on space.
|
||||
*/
|
||||
resp->tr_write.tr_logres = xfs_calc_write_reservation(mp);
|
||||
resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
|
||||
resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp);
|
||||
resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
|
||||
resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
|
||||
resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
|
||||
resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
|
||||
resp->tr_link.tr_logcount = XFS_LINK_LOG_COUNT;
|
||||
resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
|
||||
resp->tr_remove.tr_logcount = XFS_REMOVE_LOG_COUNT;
|
||||
resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
|
||||
resp->tr_symlink.tr_logcount = XFS_SYMLINK_LOG_COUNT;
|
||||
resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_create.tr_logres = xfs_calc_create_reservation(mp);
|
||||
resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
|
||||
resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_create_tmpfile.tr_logres =
|
||||
xfs_calc_create_tmpfile_reservation(mp);
|
||||
resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
|
||||
resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
|
||||
resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
|
||||
resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
|
||||
resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
|
||||
resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_addafork.tr_logres = xfs_calc_addafork_reservation(mp);
|
||||
resp->tr_addafork.tr_logcount = XFS_ADDAFORK_LOG_COUNT;
|
||||
resp->tr_addafork.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_attrinval.tr_logres = xfs_calc_attrinval_reservation(mp);
|
||||
resp->tr_attrinval.tr_logcount = XFS_ATTRINVAL_LOG_COUNT;
|
||||
resp->tr_attrinval.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_attrsetm.tr_logres = xfs_calc_attrsetm_reservation(mp);
|
||||
resp->tr_attrsetm.tr_logcount = XFS_ATTRSET_LOG_COUNT;
|
||||
resp->tr_attrsetm.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_attrrm.tr_logres = xfs_calc_attrrm_reservation(mp);
|
||||
resp->tr_attrrm.tr_logcount = XFS_ATTRRM_LOG_COUNT;
|
||||
resp->tr_attrrm.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_growrtalloc.tr_logres = xfs_calc_growrtalloc_reservation(mp);
|
||||
resp->tr_growrtalloc.tr_logcount = XFS_DEFAULT_PERM_LOG_COUNT;
|
||||
resp->tr_growrtalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
resp->tr_qm_dqalloc.tr_logres = xfs_calc_qm_dqalloc_reservation(mp);
|
||||
resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
|
||||
resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
|
||||
|
||||
/*
|
||||
* The following transactions are logged in logical format with
|
||||
* a default log count.
|
||||
*/
|
||||
resp->tr_qm_sbchange.tr_logres = xfs_calc_qm_sbchange_reservation(mp);
|
||||
resp->tr_qm_sbchange.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
resp->tr_qm_setqlim.tr_logres = xfs_calc_qm_setqlim_reservation(mp);
|
||||
resp->tr_qm_setqlim.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
resp->tr_qm_quotaoff.tr_logres = xfs_calc_qm_quotaoff_reservation(mp);
|
||||
resp->tr_qm_quotaoff.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
resp->tr_qm_equotaoff.tr_logres =
|
||||
xfs_calc_qm_quotaoff_end_reservation(mp);
|
||||
resp->tr_qm_equotaoff.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
resp->tr_sb.tr_logres = xfs_calc_sb_reservation(mp);
|
||||
resp->tr_sb.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
/* The following transaction are logged in logical format */
|
||||
resp->tr_ichange.tr_logres = xfs_calc_ichange_reservation(mp);
|
||||
resp->tr_growdata.tr_logres = xfs_calc_growdata_reservation(mp);
|
||||
resp->tr_fsyncts.tr_logres = xfs_calc_swrite_reservation(mp);
|
||||
resp->tr_writeid.tr_logres = xfs_calc_writeid_reservation(mp);
|
||||
resp->tr_attrsetrt.tr_logres = xfs_calc_attrsetrt_reservation(mp);
|
||||
resp->tr_clearagi.tr_logres = xfs_calc_clear_agi_bucket_reservation(mp);
|
||||
resp->tr_growrtzero.tr_logres = xfs_calc_growrtzero_reservation(mp);
|
||||
resp->tr_growrtfree.tr_logres = xfs_calc_growrtfree_reservation(mp);
|
||||
}
|
Reference in New Issue
Block a user