From 6d192a9b82212abf1e0e89da6e3a952afba7e4d6 Mon Sep 17 00:00:00 2001 From: Tim Shimmin Date: Fri, 9 Jun 2006 14:55:38 +1000 Subject: [XFS] inode items and EFI/EFDs have different ondisk format for 32bit and 64bit kernels allow recovery to handle both versions and do the necessary decoding SGI-PV: 952214 SGI-Modid: xfs-linux-melb:xfs-kern:26011a Signed-off-by: Tim Shimmin Signed-off-by: Nathan Scott diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index f19282e..8b028f1 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -294,6 +294,62 @@ xfs_efi_init(xfs_mount_t *mp, } /* + * Copy an EFI format buffer from the given buf, and into the destination + * EFI format structure. + * The given buffer can be in 32 bit or 64 bit form (which has different padding), + * one of which will be the native format for this kernel. + * It will handle the conversion of formats if necessary. + */ +int +xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) +{ + xfs_efi_log_format_t *src_efi_fmt = (xfs_efi_log_format_t *)buf->i_addr; + uint i; + uint len = sizeof(xfs_efi_log_format_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t); + uint len32 = sizeof(xfs_efi_log_format_32_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t); + uint len64 = sizeof(xfs_efi_log_format_64_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t); + + if (buf->i_len == len) { + memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len); + return 0; + } else if (buf->i_len == len32) { + xfs_efi_log_format_32_t *src_efi_fmt_32 = + (xfs_efi_log_format_32_t *)buf->i_addr; + + dst_efi_fmt->efi_type = src_efi_fmt_32->efi_type; + dst_efi_fmt->efi_size = src_efi_fmt_32->efi_size; + dst_efi_fmt->efi_nextents = src_efi_fmt_32->efi_nextents; + dst_efi_fmt->efi_id = src_efi_fmt_32->efi_id; + for (i = 0; i < dst_efi_fmt->efi_nextents; i++) { + dst_efi_fmt->efi_extents[i].ext_start = + src_efi_fmt_32->efi_extents[i].ext_start; + dst_efi_fmt->efi_extents[i].ext_len = + src_efi_fmt_32->efi_extents[i].ext_len; + } + return 0; + } else if (buf->i_len == len64) { + xfs_efi_log_format_64_t *src_efi_fmt_64 = + (xfs_efi_log_format_64_t *)buf->i_addr; + + dst_efi_fmt->efi_type = src_efi_fmt_64->efi_type; + dst_efi_fmt->efi_size = src_efi_fmt_64->efi_size; + dst_efi_fmt->efi_nextents = src_efi_fmt_64->efi_nextents; + dst_efi_fmt->efi_id = src_efi_fmt_64->efi_id; + for (i = 0; i < dst_efi_fmt->efi_nextents; i++) { + dst_efi_fmt->efi_extents[i].ext_start = + src_efi_fmt_64->efi_extents[i].ext_start; + dst_efi_fmt->efi_extents[i].ext_len = + src_efi_fmt_64->efi_extents[i].ext_len; + } + return 0; + } + return EFSCORRUPTED; +} + +/* * This is called by the efd item code below to release references to * the given efi item. Each efd calls this with the number of * extents that it has logged, and when the sum of these reaches diff --git a/fs/xfs/xfs_extfree_item.h b/fs/xfs/xfs_extfree_item.h index 5bf6817..0ea45ed 100644 --- a/fs/xfs/xfs_extfree_item.h +++ b/fs/xfs/xfs_extfree_item.h @@ -27,6 +27,24 @@ typedef struct xfs_extent { } xfs_extent_t; /* + * Since an xfs_extent_t has types (start:64, len: 32) + * there are different alignments on 32 bit and 64 bit kernels. + * So we provide the different variants for use by a + * conversion routine. + */ + +typedef struct xfs_extent_32 { + xfs_dfsbno_t ext_start; + xfs_extlen_t ext_len; +} __attribute__((packed)) xfs_extent_32_t; + +typedef struct xfs_extent_64 { + xfs_dfsbno_t ext_start; + xfs_extlen_t ext_len; + __uint32_t ext_pad; +} xfs_extent_64_t; + +/* * This is the structure used to lay out an efi log item in the * log. The efi_extents field is a variable size array whose * size is given by efi_nextents. @@ -39,6 +57,22 @@ typedef struct xfs_efi_log_format { xfs_extent_t efi_extents[1]; /* array of extents to free */ } xfs_efi_log_format_t; +typedef struct xfs_efi_log_format_32 { + unsigned short efi_type; /* efi log item type */ + unsigned short efi_size; /* size of this item */ + uint efi_nextents; /* # extents to free */ + __uint64_t efi_id; /* efi identifier */ + xfs_extent_32_t efi_extents[1]; /* array of extents to free */ +} __attribute__((packed)) xfs_efi_log_format_32_t; + +typedef struct xfs_efi_log_format_64 { + unsigned short efi_type; /* efi log item type */ + unsigned short efi_size; /* size of this item */ + uint efi_nextents; /* # extents to free */ + __uint64_t efi_id; /* efi identifier */ + xfs_extent_64_t efi_extents[1]; /* array of extents to free */ +} xfs_efi_log_format_64_t; + /* * This is the structure used to lay out an efd log item in the * log. The efd_extents array is a variable size array whose @@ -52,6 +86,22 @@ typedef struct xfs_efd_log_format { xfs_extent_t efd_extents[1]; /* array of extents freed */ } xfs_efd_log_format_t; +typedef struct xfs_efd_log_format_32 { + unsigned short efd_type; /* efd log item type */ + unsigned short efd_size; /* size of this item */ + uint efd_nextents; /* # of extents freed */ + __uint64_t efd_efi_id; /* id of corresponding efi */ + xfs_extent_32_t efd_extents[1]; /* array of extents freed */ +} __attribute__((packed)) xfs_efd_log_format_32_t; + +typedef struct xfs_efd_log_format_64 { + unsigned short efd_type; /* efd log item type */ + unsigned short efd_size; /* size of this item */ + uint efd_nextents; /* # of extents freed */ + __uint64_t efd_efi_id; /* id of corresponding efi */ + xfs_extent_64_t efd_extents[1]; /* array of extents freed */ +} xfs_efd_log_format_64_t; + #ifdef __KERNEL__ @@ -103,7 +153,8 @@ extern struct kmem_zone *xfs_efd_zone; xfs_efi_log_item_t *xfs_efi_init(struct xfs_mount *, uint); xfs_efd_log_item_t *xfs_efd_init(struct xfs_mount *, xfs_efi_log_item_t *, uint); - +int xfs_efi_copy_format(xfs_log_iovec_t *buf, + xfs_efi_log_format_t *dst_efi_fmt); void xfs_efi_item_free(xfs_efi_log_item_t *); #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 7497a48..cd65a56 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -1084,3 +1084,52 @@ xfs_istale_done( { xfs_iflush_abort(iip->ili_inode); } + +/* + * convert an xfs_inode_log_format struct from either 32 or 64 bit versions + * (which can have different field alignments) to the native version + */ +int +xfs_inode_item_format_convert( + xfs_log_iovec_t *buf, + xfs_inode_log_format_t *in_f) +{ + if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) { + xfs_inode_log_format_32_t *in_f32; + + in_f32 = (xfs_inode_log_format_32_t *)buf->i_addr; + in_f->ilf_type = in_f32->ilf_type; + in_f->ilf_size = in_f32->ilf_size; + in_f->ilf_fields = in_f32->ilf_fields; + in_f->ilf_asize = in_f32->ilf_asize; + in_f->ilf_dsize = in_f32->ilf_dsize; + in_f->ilf_ino = in_f32->ilf_ino; + /* copy biggest field of ilf_u */ + memcpy(in_f->ilf_u.ilfu_uuid.__u_bits, + in_f32->ilf_u.ilfu_uuid.__u_bits, + sizeof(uuid_t)); + in_f->ilf_blkno = in_f32->ilf_blkno; + in_f->ilf_len = in_f32->ilf_len; + in_f->ilf_boffset = in_f32->ilf_boffset; + return 0; + } else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){ + xfs_inode_log_format_64_t *in_f64; + + in_f64 = (xfs_inode_log_format_64_t *)buf->i_addr; + in_f->ilf_type = in_f64->ilf_type; + in_f->ilf_size = in_f64->ilf_size; + in_f->ilf_fields = in_f64->ilf_fields; + in_f->ilf_asize = in_f64->ilf_asize; + in_f->ilf_dsize = in_f64->ilf_dsize; + in_f->ilf_ino = in_f64->ilf_ino; + /* copy biggest field of ilf_u */ + memcpy(in_f->ilf_u.ilfu_uuid.__u_bits, + in_f64->ilf_u.ilfu_uuid.__u_bits, + sizeof(uuid_t)); + in_f->ilf_blkno = in_f64->ilf_blkno; + in_f->ilf_len = in_f64->ilf_len; + in_f->ilf_boffset = in_f64->ilf_boffset; + return 0; + } + return EFSCORRUPTED; +} diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index c5dbf93..5db6cd1 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -23,25 +23,6 @@ * log. The size of the inline data/extents/b-tree root to be logged * (if any) is indicated in the ilf_dsize field. Changes to this structure * must be added on to the end. - * - * Convention for naming inode log item versions : The current version - * is always named XFS_LI_INODE. When an inode log item gets superseded, - * add the latest version of IRIX that will generate logs with that item - * to the version name. - * - * -Version 1 of this structure (XFS_LI_5_3_INODE) included up to the first - * union (ilf_u) field. This was released with IRIX 5.3-XFS. - * -Version 2 of this structure (XFS_LI_6_1_INODE) is currently the entire - * structure. This was released with IRIX 6.0.1-XFS and IRIX 6.1. - * -Version 3 of this structure (XFS_LI_INODE) is the same as version 2 - * so a new structure definition wasn't necessary. However, we had - * to add a new type because the inode cluster size changed from 4K - * to 8K and the version number had to be rev'ved to keep older kernels - * from trying to recover logs with the 8K buffers in them. The logging - * code can handle recovery on different-sized clusters now so hopefully - * this'll be the last time we need to change the inode log item just - * for a change in the inode cluster size. This new version was - * released with IRIX 6.2. */ typedef struct xfs_inode_log_format { unsigned short ilf_type; /* inode log item type */ @@ -59,18 +40,38 @@ typedef struct xfs_inode_log_format { int ilf_boffset; /* off of inode in buffer */ } xfs_inode_log_format_t; -/* Initial version shipped with IRIX 5.3-XFS */ -typedef struct xfs_inode_log_format_v1 { - unsigned short ilf_type; /* inode log item type */ - unsigned short ilf_size; /* size of this item */ - uint ilf_fields; /* flags for fields logged */ - uint ilf_dsize; /* size of data/ext/root */ - xfs_ino_t ilf_ino; /* inode number */ +typedef struct xfs_inode_log_format_32 { + unsigned short ilf_type; /* 16: inode log item type */ + unsigned short ilf_size; /* 16: size of this item */ + uint ilf_fields; /* 32: flags for fields logged */ + ushort ilf_asize; /* 32: size of attr d/ext/root */ + ushort ilf_dsize; /* 32: size of data/ext/root */ + xfs_ino_t ilf_ino; /* 64: inode number */ union { - xfs_dev_t ilfu_rdev; /* rdev value for dev inode*/ - uuid_t ilfu_uuid; /* mount point value */ + xfs_dev_t ilfu_rdev; /* 32: rdev value for dev inode*/ + uuid_t ilfu_uuid; /* 128: mount point value */ + } ilf_u; + __int64_t ilf_blkno; /* 64: blkno of inode buffer */ + int ilf_len; /* 32: len of inode buffer */ + int ilf_boffset; /* 32: off of inode in buffer */ +} __attribute__((packed)) xfs_inode_log_format_32_t; + +typedef struct xfs_inode_log_format_64 { + unsigned short ilf_type; /* 16: inode log item type */ + unsigned short ilf_size; /* 16: size of this item */ + uint ilf_fields; /* 32: flags for fields logged */ + ushort ilf_asize; /* 32: size of attr d/ext/root */ + ushort ilf_dsize; /* 32: size of data/ext/root */ + __uint32_t ilf_pad; /* 32: pad for 64 bit boundary */ + xfs_ino_t ilf_ino; /* 64: inode number */ + union { + xfs_dev_t ilfu_rdev; /* 32: rdev value for dev inode*/ + uuid_t ilfu_uuid; /* 128: mount point value */ } ilf_u; -} xfs_inode_log_format_t_v1; + __int64_t ilf_blkno; /* 64: blkno of inode buffer */ + int ilf_len; /* 32: len of inode buffer */ + int ilf_boffset; /* 32: off of inode in buffer */ +} xfs_inode_log_format_64_t; /* * Flags for xfs_trans_log_inode flags field. @@ -172,6 +173,8 @@ extern void xfs_inode_item_destroy(struct xfs_inode *); extern void xfs_iflush_done(struct xfs_buf *, xfs_inode_log_item_t *); extern void xfs_istale_done(struct xfs_buf *, xfs_inode_log_item_t *); extern void xfs_iflush_abort(struct xfs_inode *); +extern int xfs_inode_item_format_convert(xfs_log_iovec_t *, + xfs_inode_log_format_t *); #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 1f0016b..efffa75 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2292,12 +2292,22 @@ xlog_recover_do_inode_trans( int attr_index; uint fields; xfs_dinode_core_t *dicp; + int need_free = 0; if (pass == XLOG_RECOVER_PASS1) { return 0; } - in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr; + if (item->ri_buf[0].i_len == sizeof(xfs_inode_log_format_t)) { + in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr; + } else { + in_f = (xfs_inode_log_format_t *)kmem_alloc( + sizeof(xfs_inode_log_format_t), KM_SLEEP); + need_free = 1; + error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f); + if (error) + goto error; + } ino = in_f->ilf_ino; mp = log->l_mp; if (ITEM_TYPE(item) == XFS_LI_INODE) { @@ -2323,8 +2333,10 @@ xlog_recover_do_inode_trans( * Inode buffers can be freed, look out for it, * and do not replay the inode. */ - if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0)) - return 0; + if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0)) { + error = 0; + goto error; + } bp = xfs_buf_read_flags(mp->m_ddev_targp, imap.im_blkno, imap.im_len, XFS_BUF_LOCK); @@ -2333,7 +2345,7 @@ xlog_recover_do_inode_trans( bp, imap.im_blkno); error = XFS_BUF_GETERROR(bp); xfs_buf_relse(bp); - return error; + goto error; } error = 0; ASSERT(in_f->ilf_fields & XFS_ILOG_CORE); @@ -2350,7 +2362,8 @@ xlog_recover_do_inode_trans( dip, bp, ino); XFS_ERROR_REPORT("xlog_recover_do_inode_trans(1)", XFS_ERRLEVEL_LOW, mp); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } dicp = (xfs_dinode_core_t*)(item->ri_buf[1].i_addr); if (unlikely(dicp->di_magic != XFS_DINODE_MAGIC)) { @@ -2360,7 +2373,8 @@ xlog_recover_do_inode_trans( item, ino); XFS_ERROR_REPORT("xlog_recover_do_inode_trans(2)", XFS_ERRLEVEL_LOW, mp); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } /* Skip replay when the on disk inode is newer than the log one */ @@ -2376,7 +2390,8 @@ xlog_recover_do_inode_trans( /* do nothing */ } else { xfs_buf_relse(bp); - return 0; + error = 0; + goto error; } } /* Take the opportunity to reset the flush iteration count */ @@ -2391,7 +2406,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad regular inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld", item, dip, bp, ino); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } } else if (unlikely((dicp->di_mode & S_IFMT) == S_IFDIR)) { if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) && @@ -2403,7 +2419,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad dir inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld", item, dip, bp, ino); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } } if (unlikely(dicp->di_nextents + dicp->di_anextents > dicp->di_nblocks)){ @@ -2415,7 +2432,8 @@ xlog_recover_do_inode_trans( item, dip, bp, ino, dicp->di_nextents + dicp->di_anextents, dicp->di_nblocks); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } if (unlikely(dicp->di_forkoff > mp->m_sb.sb_inodesize)) { XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(6)", @@ -2424,7 +2442,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad inode log rec ptr 0x%p, dino ptr 0x%p, dino bp 0x%p, ino %Ld, forkoff 0x%x", item, dip, bp, ino, dicp->di_forkoff); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } if (unlikely(item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t))) { XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(7)", @@ -2433,7 +2452,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad inode log record length %d, rec ptr 0x%p", item->ri_buf[1].i_len, item); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } /* The core is in in-core format */ @@ -2521,7 +2541,8 @@ xlog_recover_do_inode_trans( xlog_warn("XFS: xlog_recover_do_inode_trans: Invalid flag"); ASSERT(0); xfs_buf_relse(bp); - return XFS_ERROR(EIO); + error = EIO; + goto error; } } @@ -2537,7 +2558,10 @@ write_inode_buffer: error = xfs_bwrite(mp, bp); } - return (error); +error: + if (need_free) + kmem_free(in_f, sizeof(*in_f)); + return XFS_ERROR(error); } /* @@ -2674,32 +2698,32 @@ xlog_recover_do_dquot_trans( * structure into it, and adds the efi to the AIL with the given * LSN. */ -STATIC void +STATIC int xlog_recover_do_efi_trans( xlog_t *log, xlog_recover_item_t *item, xfs_lsn_t lsn, int pass) { + int error; xfs_mount_t *mp; xfs_efi_log_item_t *efip; xfs_efi_log_format_t *efi_formatp; SPLDECL(s); if (pass == XLOG_RECOVER_PASS1) { - return; + return 0; } efi_formatp = (xfs_efi_log_format_t *)item->ri_buf[0].i_addr; - ASSERT(item->ri_buf[0].i_len == - (sizeof(xfs_efi_log_format_t) + - ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t)))); mp = log->l_mp; efip = xfs_efi_init(mp, efi_formatp->efi_nextents); - memcpy((char *)&(efip->efi_format), (char *)efi_formatp, - sizeof(xfs_efi_log_format_t) + - ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t))); + if ((error = xfs_efi_copy_format(&(item->ri_buf[0]), + &(efip->efi_format)))) { + xfs_efi_item_free(efip); + return error; + } efip->efi_next_extent = efi_formatp->efi_nextents; efip->efi_flags |= XFS_EFI_COMMITTED; @@ -2708,6 +2732,7 @@ xlog_recover_do_efi_trans( * xfs_trans_update_ail() drops the AIL lock. */ xfs_trans_update_ail(mp, (xfs_log_item_t *)efip, lsn, s); + return 0; } @@ -2738,9 +2763,10 @@ xlog_recover_do_efd_trans( } efd_formatp = (xfs_efd_log_format_t *)item->ri_buf[0].i_addr; - ASSERT(item->ri_buf[0].i_len == - (sizeof(xfs_efd_log_format_t) + - ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_t)))); + ASSERT((item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_32_t) + + ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_32_t)))) || + (item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_64_t) + + ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_64_t))))); efi_id = efd_formatp->efd_efi_id; /* @@ -2810,15 +2836,14 @@ xlog_recover_do_trans( if ((error = xlog_recover_do_buffer_trans(log, item, pass))) break; - } else if ((ITEM_TYPE(item) == XFS_LI_INODE) || - (ITEM_TYPE(item) == XFS_LI_6_1_INODE) || - (ITEM_TYPE(item) == XFS_LI_5_3_INODE)) { + } else if ((ITEM_TYPE(item) == XFS_LI_INODE)) { if ((error = xlog_recover_do_inode_trans(log, item, pass))) break; } else if (ITEM_TYPE(item) == XFS_LI_EFI) { - xlog_recover_do_efi_trans(log, item, trans->r_lsn, - pass); + if ((error = xlog_recover_do_efi_trans(log, item, trans->r_lsn, + pass))) + break; } else if (ITEM_TYPE(item) == XFS_LI_EFD) { xlog_recover_do_efd_trans(log, item, pass); } else if (ITEM_TYPE(item) == XFS_LI_DQUOT) { @@ -3798,7 +3823,7 @@ xlog_do_log_recovery( error = xlog_do_recovery_pass(log, head_blk, tail_blk, XLOG_RECOVER_PASS2); #ifdef DEBUG - { + if (!error) { int i; for (i = 0; i < XLOG_BC_TABLE_SIZE; i++) -- cgit v0.10.2