Commit f5f3d9b0 authored by Dave Chinner's avatar Dave Chinner Committed by Ben Myers

xfs: add CRC checks to block format directory blocks

Now that directory buffers are made from a single struct xfs_buf, we
can add CRC calculation and checking callbacks. While there, add all
the fields to the on disk structures for future functionality such
as d_type support, uuids, block numbers, owner inode, etc.

To distinguish between the different on disk formats, change the
magic numbers for the new format directory blocks.
Signed-off-by: 's avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: 's avatarBen Myers <bpm@sgi.com>
Signed-off-by: 's avatarBen Myers <bpm@sgi.com>
parent f948dd76
/*
* Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
* Copyright (c) 2013 Red Hat, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
......@@ -28,11 +29,13 @@
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_inode_item.h"
#include "xfs_buf_item.h"
#include "xfs_dir2.h"
#include "xfs_dir2_format.h"
#include "xfs_dir2_priv.h"
#include "xfs_error.h"
#include "xfs_trace.h"
#include "xfs_cksum.h"
/*
* Local function prototypes.
......@@ -56,44 +59,74 @@ xfs_dir_startup(void)
xfs_dir_hash_dotdot = xfs_da_hashname((unsigned char *)"..", 2);
}
static void
xfs_dir2_block_verify(
static bool
xfs_dir3_block_verify(
struct xfs_buf *bp)
{
struct xfs_mount *mp = bp->b_target->bt_mount;
struct xfs_dir2_data_hdr *hdr = bp->b_addr;
int block_ok = 0;
block_ok = hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
block_ok = block_ok && __xfs_dir2_data_check(NULL, bp) == 0;
if (!block_ok) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, hdr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
if (xfs_sb_version_hascrc(&mp->m_sb)) {
if (hdr3->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
return false;
if (!uuid_equal(&hdr3->uuid, &mp->m_sb.sb_uuid))
return false;
if (be64_to_cpu(hdr3->blkno) != bp->b_bn)
return false;
} else {
if (hdr3->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC))
return false;
}
if (__xfs_dir2_data_check(NULL, bp))
return false;
return true;
}
static void
xfs_dir2_block_read_verify(
xfs_dir3_block_read_verify(
struct xfs_buf *bp)
{
xfs_dir2_block_verify(bp);
struct xfs_mount *mp = bp->b_target->bt_mount;
if ((xfs_sb_version_hascrc(&mp->m_sb) &&
!xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
XFS_DIR3_DATA_CRC_OFF)) ||
!xfs_dir3_block_verify(bp)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
}
}
static void
xfs_dir2_block_write_verify(
xfs_dir3_block_write_verify(
struct xfs_buf *bp)
{
xfs_dir2_block_verify(bp);
struct xfs_mount *mp = bp->b_target->bt_mount;
struct xfs_buf_log_item *bip = bp->b_fspriv;
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
if (!xfs_dir3_block_verify(bp)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
xfs_buf_ioerror(bp, EFSCORRUPTED);
return;
}
if (!xfs_sb_version_hascrc(&mp->m_sb))
return;
if (bip)
hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn);
xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_DATA_CRC_OFF);
}
const struct xfs_buf_ops xfs_dir2_block_buf_ops = {
.verify_read = xfs_dir2_block_read_verify,
.verify_write = xfs_dir2_block_write_verify,
const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
.verify_read = xfs_dir3_block_read_verify,
.verify_write = xfs_dir3_block_write_verify,
};
static int
xfs_dir2_block_read(
xfs_dir3_block_read(
struct xfs_trans *tp,
struct xfs_inode *dp,
struct xfs_buf **bpp)
......@@ -101,7 +134,29 @@ xfs_dir2_block_read(
struct xfs_mount *mp = dp->i_mount;
return xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, bpp,
XFS_DATA_FORK, &xfs_dir2_block_buf_ops);
XFS_DATA_FORK, &xfs_dir3_block_buf_ops);
}
static void
xfs_dir3_block_init(
struct xfs_mount *mp,
struct xfs_buf *bp,
struct xfs_inode *dp)
{
struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
bp->b_ops = &xfs_dir3_block_buf_ops;
if (xfs_sb_version_hascrc(&mp->m_sb)) {
memset(hdr3, 0, sizeof(*hdr3));
hdr3->magic = cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
hdr3->blkno = cpu_to_be64(bp->b_bn);
hdr3->owner = cpu_to_be64(dp->i_ino);
uuid_copy(&hdr3->uuid, &mp->m_sb.sb_uuid);
return;
}
hdr3->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
}
static void
......@@ -121,7 +176,7 @@ xfs_dir2_block_need_space(
struct xfs_dir2_data_unused *enddup = NULL;
*compact = 0;
bf = hdr->bestfree;
bf = xfs_dir3_data_bestfree_p(hdr);
/*
* If there are stale entries we'll use one for the leaf.
......@@ -303,7 +358,7 @@ xfs_dir2_block_addname(
mp = dp->i_mount;
/* Read the (one and only) directory block into bp. */
error = xfs_dir2_block_read(tp, dp, &bp);
error = xfs_dir3_block_read(tp, dp, &bp);
if (error)
return error;
......@@ -531,7 +586,7 @@ xfs_dir2_block_getdents(
if (xfs_dir2_dataptr_to_db(mp, *offset) > mp->m_dirdatablk)
return 0;
error = xfs_dir2_block_read(NULL, dp, &bp);
error = xfs_dir3_block_read(NULL, dp, &bp);
if (error)
return error;
......@@ -546,7 +601,7 @@ xfs_dir2_block_getdents(
* Set up values for the loop.
*/
btp = xfs_dir2_block_tail_p(mp, hdr);
ptr = (char *)(hdr + 1);
ptr = (char *)xfs_dir3_data_entry_p(hdr);
endptr = (char *)xfs_dir2_block_leaf_p(btp);
/*
......@@ -711,7 +766,7 @@ xfs_dir2_block_lookup_int(
tp = args->trans;
mp = dp->i_mount;
error = xfs_dir2_block_read(tp, dp, &bp);
error = xfs_dir3_block_read(tp, dp, &bp);
if (error)
return error;
......@@ -974,9 +1029,12 @@ xfs_dir2_leaf_to_block(
* These will show up in the leaf bests table.
*/
while (dp->i_d.di_size > mp->m_dirblksize) {
int hdrsz;
hdrsz = xfs_dir3_data_hdr_size(xfs_sb_version_hascrc(&mp->m_sb));
bestsp = xfs_dir2_leaf_bests_p(ltp);
if (be16_to_cpu(bestsp[be32_to_cpu(ltp->bestcount) - 1]) ==
mp->m_dirblksize - (uint)sizeof(*hdr)) {
mp->m_dirblksize - hdrsz) {
if ((error =
xfs_dir2_leaf_trim_data(args, lbp,
(xfs_dir2_db_t)(be32_to_cpu(ltp->bestcount) - 1))))
......@@ -1014,8 +1072,8 @@ xfs_dir2_leaf_to_block(
/*
* Start converting it to block form.
*/
dbp->b_ops = &xfs_dir2_block_buf_ops;
hdr->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
xfs_dir3_block_init(mp, dbp, dp);
needlog = 1;
needscan = 0;
/*
......@@ -1137,16 +1195,16 @@ xfs_dir2_sf_to_block(
return error;
}
/*
* Initialize the data block.
* Initialize the data block, then convert it to block format.
*/
error = xfs_dir2_data_init(args, blkno, &bp);
error = xfs_dir3_data_init(args, blkno, &bp);
if (error) {
kmem_free(sfp);
return error;
}
bp->b_ops = &xfs_dir2_block_buf_ops;
xfs_dir3_block_init(mp, bp, dp);
hdr = bp->b_addr;
hdr->magic = cpu_to_be32(XFS_DIR2_BLOCK_MAGIC);
/*
* Compute size of block "tail" area.
*/
......@@ -1156,7 +1214,7 @@ xfs_dir2_sf_to_block(
* The whole thing is initialized to free by the init routine.
* Say we're using the leaf and tail area.
*/
dup = (xfs_dir2_data_unused_t *)(hdr + 1);
dup = xfs_dir3_data_unused_p(hdr);
needlog = needscan = 0;
xfs_dir2_data_use_free(tp, bp, dup, mp->m_dirblksize - i, i, &needlog,
&needscan);
......@@ -1178,8 +1236,7 @@ xfs_dir2_sf_to_block(
/*
* Create entry for .
*/
dep = (xfs_dir2_data_entry_t *)
((char *)hdr + XFS_DIR2_DATA_DOT_OFFSET);
dep = xfs_dir3_data_dot_entry_p(hdr);
dep->inumber = cpu_to_be64(dp->i_ino);
dep->namelen = 1;
dep->name[0] = '.';
......@@ -1192,8 +1249,7 @@ xfs_dir2_sf_to_block(
/*
* Create entry for ..
*/
dep = (xfs_dir2_data_entry_t *)
((char *)hdr + XFS_DIR2_DATA_DOTDOT_OFFSET);
dep = xfs_dir3_data_dotdot_entry_p(hdr);
dep->inumber = cpu_to_be64(xfs_dir2_sf_get_parent_ino(sfp));
dep->namelen = 2;
dep->name[0] = dep->name[1] = '.';
......@@ -1203,7 +1259,7 @@ xfs_dir2_sf_to_block(
blp[1].hashval = cpu_to_be32(xfs_dir_hash_dotdot);
blp[1].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
(char *)dep - (char *)hdr));
offset = XFS_DIR2_DATA_FIRST_OFFSET;
offset = xfs_dir3_data_first_offset(hdr);
/*
* Loop over existing entries, stuff them in.
*/
......
This diff is collapsed.
/*
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
* Copyright (c) 2013 Red Hat, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
......@@ -35,6 +36,37 @@
#define XFS_DIR2_DATA_MAGIC 0x58443244 /* XD2D: multiblock dirs */
#define XFS_DIR2_FREE_MAGIC 0x58443246 /* XD2F: free index blocks */
/*
* Directory Version 3 With CRCs.
*
* The tree formats are the same as for version 2 directories. The difference
* is in the block header and dirent formats. In many cases the v3 structures
* use v2 definitions as they are no different and this makes code sharing much
* easier.
*
* Also, the xfs_dir3_*() functions handle both v2 and v3 formats - if the
* format is v2 then they switch to the existing v2 code, or the format is v3
* they implement the v3 functionality. This means the existing dir2 is a mix of
* xfs_dir2/xfs_dir3 calls and functions. The xfs_dir3 functions are called
* where there is a difference in the formats, otherwise the code is unchanged.
*
* Where it is possible, the code decides what to do based on the magic numbers
* in the blocks rather than feature bits in the superblock. This means the code
* is as independent of the external XFS code as possible as doesn't require
* passing struct xfs_mount pointers into places where it isn't really
* necessary.
*
* Version 3 includes:
*
* - a larger block header for CRC and identification purposes and so the
* offsets of all the structures inside the blocks are different.
*
* - new magic numbers to be able to detect the v2/v3 types on the fly.
*/
#define XFS_DIR3_BLOCK_MAGIC 0x58444233 /* XDB3: single block dirs */
#define XFS_DIR3_DATA_MAGIC 0x58444433 /* XDD3: multiblock dirs */
/*
* Byte offset in data block and shortform entry.
*/
......@@ -225,6 +257,38 @@ typedef struct xfs_dir2_data_hdr {
xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT];
} xfs_dir2_data_hdr_t;
/*
* define a structure for all the verification fields we are adding to the
* directory block structures. This will be used in several structures.
* The magic number must be the first entry to align with all the dir2
* structures so we determine how to decode them just by the magic number.
*/
struct xfs_dir3_blk_hdr {
__be32 magic; /* magic number */
__be32 crc; /* CRC of block */
__be64 blkno; /* first block of the buffer */
__be64 lsn; /* sequence number of last write */
uuid_t uuid; /* filesystem we belong to */
__be64 owner; /* inode that owns the block */
};
struct xfs_dir3_data_hdr {
struct xfs_dir3_blk_hdr hdr;
xfs_dir2_data_free_t best_free[XFS_DIR2_DATA_FD_COUNT];
};
#define XFS_DIR3_DATA_CRC_OFF offsetof(struct xfs_dir3_data_hdr, hdr.crc)
static inline struct xfs_dir2_data_free *
xfs_dir3_data_bestfree_p(struct xfs_dir2_data_hdr *hdr)
{
if (hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC)) {
struct xfs_dir3_data_hdr *hdr3 = (struct xfs_dir3_data_hdr *)hdr;
return hdr3->best_free;
}
return hdr->bestfree;
}
/*
* Active entry in a data block.
*
......@@ -280,6 +344,85 @@ xfs_dir2_data_unused_tag_p(struct xfs_dir2_data_unused *dup)
be16_to_cpu(dup->length) - sizeof(__be16));
}
static inline struct xfs_dir2_data_unused *
xfs_dir3_data_unused_p(struct xfs_dir2_data_hdr *hdr)
{
if (hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC)) {
return (struct xfs_dir2_data_unused *)
((char *)hdr + sizeof(struct xfs_dir3_data_hdr));
}
return (struct xfs_dir2_data_unused *)
((char *)hdr + sizeof(struct xfs_dir2_data_hdr));
}
static inline size_t
xfs_dir3_data_hdr_size(bool dir3)
{
if (dir3)
return sizeof(struct xfs_dir3_data_hdr);
return sizeof(struct xfs_dir2_data_hdr);
}
static inline size_t
xfs_dir3_data_entry_offset(struct xfs_dir2_data_hdr *hdr)
{
bool dir3 = hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC);
return xfs_dir3_data_hdr_size(dir3);
}
static inline struct xfs_dir2_data_entry *
xfs_dir3_data_entry_p(struct xfs_dir2_data_hdr *hdr)
{
return (struct xfs_dir2_data_entry *)
((char *)hdr + xfs_dir3_data_entry_offset(hdr));
}
/*
* Offsets of . and .. in data space (always block 0)
*/
static inline xfs_dir2_data_aoff_t
xfs_dir3_data_dot_offset(struct xfs_dir2_data_hdr *hdr)
{
return xfs_dir3_data_entry_offset(hdr);
}
static inline xfs_dir2_data_aoff_t
xfs_dir3_data_dotdot_offset(struct xfs_dir2_data_hdr *hdr)
{
return xfs_dir3_data_dot_offset(hdr) + xfs_dir2_data_entsize(1);
}
static inline xfs_dir2_data_aoff_t
xfs_dir3_data_first_offset(struct xfs_dir2_data_hdr *hdr)
{
return xfs_dir3_data_dotdot_offset(hdr) + xfs_dir2_data_entsize(2);
}
/*
* location of . and .. in data space (always block 0)
*/
static inline 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 + xfs_dir3_data_dot_offset(hdr));
}
static inline 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 + xfs_dir3_data_dotdot_offset(hdr));
}
static inline 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 + xfs_dir3_data_first_offset(hdr));
}
/*
* Leaf block structures.
*
......
......@@ -149,6 +149,7 @@ xfs_dir2_block_to_leaf(
int needlog; /* need to log block header */
int needscan; /* need to rescan bestfree */
xfs_trans_t *tp; /* transaction pointer */
struct xfs_dir2_data_free *bf;
trace_xfs_dir2_block_to_leaf(args);
......@@ -177,6 +178,7 @@ xfs_dir2_block_to_leaf(
xfs_dir2_data_check(dp, dbp);
btp = xfs_dir2_block_tail_p(mp, hdr);
blp = xfs_dir2_block_leaf_p(btp);
bf = xfs_dir3_data_bestfree_p(hdr);
/*
* Set the counts in the leaf header.
*/
......@@ -212,7 +214,7 @@ xfs_dir2_block_to_leaf(
ltp = xfs_dir2_leaf_tail_p(mp, leaf);
ltp->bestcount = cpu_to_be32(1);
bestsp = xfs_dir2_leaf_bests_p(ltp);
bestsp[0] = hdr->bestfree[0].length;
bestsp[0] = bf[0].length;
/*
* Log the data header and leaf bests table.
*/
......@@ -544,7 +546,7 @@ xfs_dir2_leaf_addname(
/*
* Initialize the block.
*/
if ((error = xfs_dir2_data_init(args, use_block, &dbp))) {
if ((error = xfs_dir3_data_init(args, use_block, &dbp))) {
xfs_trans_brelse(tp, lbp);
return error;
}
......
......@@ -1588,7 +1588,7 @@ xfs_dir2_node_addname_int(
if (unlikely((error = xfs_dir2_grow_inode(args,
XFS_DIR2_DATA_SPACE,
&dbno)) ||
(error = xfs_dir2_data_init(args, dbno, &dbp))))
(error = xfs_dir3_data_init(args, dbno, &dbp))))
return error;
/*
......
......@@ -30,7 +30,7 @@ extern int xfs_dir_cilookup_result(struct xfs_da_args *args,
const unsigned char *name, int len);
/* xfs_dir2_block.c */
extern const struct xfs_buf_ops xfs_dir2_block_buf_ops;
extern const struct xfs_buf_ops xfs_dir3_block_buf_ops;
extern int xfs_dir2_block_addname(struct xfs_da_args *args);
extern int xfs_dir2_block_getdents(struct xfs_inode *dp, void *dirent,
......@@ -61,7 +61,7 @@ xfs_dir2_data_freeinsert(struct xfs_dir2_data_hdr *hdr,
struct xfs_dir2_data_unused *dup, int *loghead);
extern void xfs_dir2_data_freescan(struct xfs_mount *mp,
struct xfs_dir2_data_hdr *hdr, int *loghead);
extern int xfs_dir2_data_init(struct xfs_da_args *args, xfs_dir2_db_t blkno,
extern int xfs_dir3_data_init(struct xfs_da_args *args, xfs_dir2_db_t blkno,
struct xfs_buf **bpp);
extern void xfs_dir2_data_log_entry(struct xfs_trans *tp, struct xfs_buf *bp,
struct xfs_dir2_data_entry *dep);
......
......@@ -278,7 +278,7 @@ xfs_dir2_block_to_sf(
* Set up to loop over the block's entries.
*/
btp = xfs_dir2_block_tail_p(mp, hdr);
ptr = (char *)(hdr + 1);
ptr = (char *)xfs_dir3_data_entry_p(hdr);
endptr = (char *)xfs_dir2_block_leaf_p(btp);
sfep = xfs_dir2_sf_firstentry(sfp);
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment