/*
- * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
- *
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 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.
- *
+ *
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
- *
+ *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
- *
+ *
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
- *
- * http://www.sgi.com
- *
- * For further information regarding this notice, see:
- *
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
* http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
*/
-#include <libxlog.h>
+#include <xfs/libxlog.h>
/*
* This routine finds (to an approximation) the first block in the physical
* necessarily be perfect.
*/
int
-xlog_find_cycle_start(xlog_t *log,
- xfs_buf_t *bp,
+xlog_find_cycle_start(xlog_t *log,
+ xfs_buf_t *bp,
xfs_daddr_t first_blk,
xfs_daddr_t *last_blk,
- uint cycle)
+ uint cycle)
{
- xfs_daddr_t mid_blk;
- uint mid_cycle;
- int error;
+ xfs_daddr_t mid_blk;
+ uint mid_cycle;
+ int error;
mid_blk = BLK_AVG(first_blk, *last_blk);
while (mid_blk != first_blk && mid_blk != *last_blk) {
*/
STATIC int
-xlog_find_verify_cycle( xlog_t *log,
- xfs_daddr_t start_blk,
- int nbblks,
- uint stop_on_cycle_no,
+xlog_find_verify_cycle( xlog_t *log,
+ xfs_daddr_t start_blk,
+ int nbblks,
+ uint stop_on_cycle_no,
xfs_daddr_t *new_blk)
{
xfs_daddr_t i, j;
uint cycle;
- xfs_buf_t *bp;
- char *buf = NULL;
+ xfs_buf_t *bp;
+ char *buf = NULL;
int error = 0;
- xfs_daddr_t bufblks = nbblks;
+ xfs_daddr_t bufblks;
+
+ bufblks = 1 << ffs(nbblks);
while (!(bp = xlog_get_bp(bufblks, log->l_mp))) {
- /* can't get enough memory to do everything in one big buffer */
+ /* can't get enough memory to do everything in one big buffer */
bufblks >>= 1;
- if (!bufblks)
- return ENOMEM;
- }
-
+ if (!bufblks)
+ return ENOMEM;
+ }
+
+ for (i = start_blk; i < start_blk + nbblks; i += bufblks) {
+ int bcount;
- for (i = start_blk; i < start_blk + nbblks; i += bufblks) {
- int bcount = min(bufblks, (start_blk + nbblks - i));
+ bcount = min(bufblks, (start_blk + nbblks - i));
- if ((error = xlog_bread(log, i, bcount, bp)))
- goto out;
+ if ((error = xlog_bread(log, i, bcount, bp)))
+ goto out;
buf = XFS_BUF_PTR(bp);
for (j = 0; j < bcount; j++) {
cycle = GET_CYCLE(buf, ARCH_CONVERT);
if (cycle == stop_on_cycle_no) {
- *new_blk = i;
+ *new_blk = i+j;
goto out;
}
-
- buf += BBSIZE;
+
+ buf += BBSIZE;
}
}
int error = 0;
int smallmem = 0;
int num_blks = *last_blk - start_blk;
+ int xhdrs;
ASSERT(start_blk != 0 || *last_blk != start_blk);
if (!(bp = xlog_get_bp(num_blks, log->l_mp))) {
- if (!(bp = xlog_get_bp(1, log->l_mp)))
- return ENOMEM;
- smallmem = 1;
- buf = XFS_BUF_PTR(bp);
+ if (!(bp = xlog_get_bp(1, log->l_mp)))
+ return ENOMEM;
+ smallmem = 1;
+ buf = XFS_BUF_PTR(bp);
} else {
if ((error = xlog_bread(log, start_blk, num_blks, bp)))
goto out;
- buf = XFS_BUF_PTR(bp) + (num_blks - 1) * BBSIZE;
+ buf = XFS_BUF_PTR(bp) + ((num_blks - 1) << BBSHIFT);
}
-
- for (i=(*last_blk)-1; i>=0; i--) {
+ for (i = (*last_blk) - 1; i >= 0; i--) {
if (i < start_blk) {
/* legal log record not found */
xlog_warn("XFS: Log inconsistent (didn't find previous header)");
if (smallmem && (error = xlog_bread(log, i, 1, bp)))
goto out;
- head = (xlog_rec_header_t*)buf;
-
+ head = (xlog_rec_header_t*)buf;
+
if (INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM)
break;
-
- if (!smallmem)
- buf -= BBSIZE;
+
+ if (!smallmem)
+ buf -= BBSIZE;
}
/*
* will be called again for the end of the physical log.
*/
if (i == -1) {
- error = -1;
+ error = -1;
goto out;
}
/* we have the final block of the good log (the first block
* of the log record _before_ the head. So we check the uuid.
*/
-
+
if ((error = xlog_header_check_mount(log->l_mp, head)))
- goto out;
-
+ goto out;
+
/*
* We may have found a log record header before we expected one.
* last_blk will be the 1st block # with a given cycle #. We may end
* reset last_blk. Only when last_blk points in the middle of a log
* record do we update last_blk.
*/
- if (*last_blk - i + extra_bblks
- != BTOBB(INT_GET(head->h_len, ARCH_CONVERT))+1)
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ uint h_size = INT_GET(head->h_size, ARCH_CONVERT);
+
+ xhdrs = h_size / XLOG_HEADER_CYCLE_SIZE;
+ if (h_size % XLOG_HEADER_CYCLE_SIZE)
+ xhdrs++;
+ } else {
+ xhdrs = 1;
+ }
+
+ if (*last_blk - i + extra_bblks
+ != BTOBB(INT_GET(head->h_len, ARCH_CONVERT))+xhdrs)
*last_blk = i;
out:
* Head is defined to be the point of the log where the next log write
* write could go. This means that incomplete LR writes at the end are
* eliminated when calculating the head. We aren't guaranteed that previous
- * LR have complete transactions. We only know that a cycle number of
+ * LR have complete transactions. We only know that a cycle number of
* current cycle number -1 won't be present in the log if we start writing
* from our current block number.
*
/* Is the end of the log device zeroed? */
if ((error = xlog_find_zeroed(log, &first_blk)) == -1) {
*return_head_blk = first_blk;
-
- /* is the whole lot zeroed? */
- if (!first_blk) {
- /* Linux XFS shouldn't generate totally zeroed logs -
- * mkfs etc write a dummy unmount record to a fresh
- * log so we can store the uuid in there
- */
- xlog_warn("XFS: totally zeroed log\n");
- }
-
+
+ /* is the whole lot zeroed? */
+ if (!first_blk) {
+ /* Linux XFS shouldn't generate totally zeroed logs -
+ * mkfs etc write a dummy unmount record to a fresh
+ * log so we can store the uuid in there
+ */
+ xlog_warn("XFS: totally zeroed log");
+ }
+
return 0;
} else if (error) {
- xlog_warn("XFS: empty log check failed");
+ xlog_warn("XFS: empty log check failed");
return error;
}
* This is really the combination of the above two cases, and the
* head has to end up at the start of the x-1 hole at the end of
* the log.
- *
+ *
* In the 256k log case, we will read from the beginning to the
* end of the log and search for cycle numbers equal to x-1. We
* don't worry about the x+1 blocks that we encounter, because
* in the in-core log. The following number can be made tighter if
* we actually look at the block size of the filesystem.
*/
- num_scan_bblks = BTOBB(XLOG_MAX_ICLOGS<<XLOG_MAX_RECORD_BSHIFT);
+ num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log);
if (head_blk >= num_scan_bblks) {
/*
* We are guaranteed that the entire check can be performed
if (new_blk != -1)
head_blk = new_blk;
} else { /* need to read 2 parts of log */
- /*
+ /*
* We are going to scan backwards in the log in two parts. First
* we scan the physical end of the log. In this part of the log,
* we are looking for blocks with cycle number last_half_cycle - 1.
* occurrence of last_half_cycle is wrong and we move back to the
* hole we've found. This case looks like
* x + 1 ... | x | x + 1 | x ...
- * ^ binary search stopped here
+ * ^ binary search stopped here
* Another case we need to handle that only occurs in 256k logs is
* x + 1 ... | x ... | x+1 | x ...
* ^ binary search stops here
if ((error = xlog_find_verify_cycle(log, start_blk,
num_scan_bblks-(int)head_blk, (stop_on_cycle - 1),
&new_blk)))
- goto bp_err;
+ goto bp_err;
if (new_blk != -1) {
head_blk = new_blk;
goto bad_blk;
bp_err:
xlog_put_bp(bp);
- if (error)
- xlog_warn("XFS: failed to find log head");
-
+ if (error)
+ xlog_warn("XFS: failed to find log head");
+
return error;
} /* xlog_find_head */
* available.
*/
int
-xlog_find_tail(xlog_t *log,
- xfs_daddr_t *head_blk,
- xfs_daddr_t *tail_blk,
- int readonly)
+xlog_find_tail(xlog_t *log,
+ xfs_daddr_t *head_blk,
+ xfs_daddr_t *tail_blk,
+ int readonly)
{
xlog_rec_header_t *rhead;
xlog_op_header_t *op_head;
xfs_daddr_t umount_data_blk;
xfs_daddr_t after_umount_blk;
xfs_lsn_t tail_lsn;
-
- found = error = 0;
+ int hblks;
+
+ found = 0;
/*
- * Find previous log record
+ * Find previous log record
*/
if ((error = xlog_find_head(log, head_blk)))
return error;
* Search backwards looking for log record header block
*/
ASSERT(*head_blk < INT_MAX);
- for (i=(int)(*head_blk)-1; i>=0; i--) {
+ for (i = (int)(*head_blk) - 1; i >= 0; i--) {
if ((error = xlog_bread(log, i, 1, bp)))
goto bread_err;
- if (INT_GET(*(uint *)(XFS_BUF_PTR(bp)), ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM) {
+ if (XLOG_HEADER_MAGIC_NUM ==
+ INT_GET(*(uint *)(XFS_BUF_PTR(bp)), ARCH_CONVERT)) {
found = 1;
break;
}
* the previous code.
*/
if (!found) {
- for (i=log->l_logBBsize-1; i>=(int)(*head_blk); i--) {
+ for (i = log->l_logBBsize - 1; i >= (int)(*head_blk); i--) {
if ((error = xlog_bread(log, i, 1, bp)))
goto bread_err;
- if (INT_GET(*(uint*)(XFS_BUF_PTR(bp)), ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM) {
+ if (XLOG_HEADER_MAGIC_NUM ==
+ INT_GET(*(uint*)(XFS_BUF_PTR(bp)), ARCH_CONVERT)) {
found = 2;
break;
}
* unmount record if there is one, so we pass the lsn of the
* unmount record rather than the block after it.
*/
- after_umount_blk = (i + 2) % log->l_logBBsize;
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ int h_size = INT_GET(rhead->h_size, ARCH_CONVERT);
+ int h_version = INT_GET(rhead->h_version, ARCH_CONVERT);
+
+ if ((h_version & XLOG_VERSION_2) &&
+ (h_size > XLOG_HEADER_CYCLE_SIZE)) {
+ hblks = h_size / XLOG_HEADER_CYCLE_SIZE;
+ if (h_size % XLOG_HEADER_CYCLE_SIZE)
+ hblks++;
+ } else {
+ hblks = 1;
+ }
+ } else {
+ hblks = 1;
+ }
+ after_umount_blk = (i + hblks + (int)
+ BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT))) % log->l_logBBsize;
tail_lsn = log->l_tail_lsn;
- if (*head_blk == after_umount_blk && INT_GET(rhead->h_num_logops, ARCH_CONVERT) == 1) {
- umount_data_blk = (i + 1) % log->l_logBBsize;
+ if (*head_blk == after_umount_blk &&
+ INT_GET(rhead->h_num_logops, ARCH_CONVERT) == 1) {
+ umount_data_blk = (i + hblks) % log->l_logBBsize;
if ((error = xlog_bread(log, umount_data_blk, 1, bp))) {
goto bread_err;
}
* overwrite the unmount record after a clean unmount.
*
* Do this only if we are going to recover the filesystem
+ *
+ * NOTE: This used to say "if (!readonly)"
+ * However on Linux, we can & do recover a read-only filesystem.
+ * We only skip recovery if NORECOVERY is specified on mount,
+ * in which case we would not be here.
+ *
+ * But... if the -device- itself is readonly, just skip this.
+ * We can't recover this device anyway, so it won't matter.
*/
- if (!readonly)
+
+ if (!is_read_only(log->l_mp->m_logdev_targp->pbr_kdev)) {
error = xlog_clear_stale_blocks(log, tail_lsn);
+ }
#endif
bread_err:
exit:
xlog_put_bp(bp);
- if (error)
- xlog_warn("XFS: failed to locate log tail");
+ if (error)
+ xlog_warn("XFS: failed to locate log tail");
return error;
} /* xlog_find_tail */
*/
int
xlog_find_zeroed(struct log *log,
- xfs_daddr_t *blk_no)
+ xfs_daddr_t *blk_no)
{
xfs_buf_t *bp;
uint first_cycle, last_cycle;
xfs_daddr_t num_scan_bblks;
int error, log_bbnum = log->l_logBBsize;
- error = 0;
/* check totally zeroed log */
bp = xlog_get_bp(1,log->l_mp);
if (!bp)
} else if (first_cycle != 1) {
/*
* If the cycle of the last block is zero, the cycle of
- * the first block must be 1. If it's not, maybe we're
- * not looking at a log... Bail out.
+ * the first block must be 1. If it's not, maybe we're
+ * not looking at a log... Bail out.
*/
- xlog_warn("XFS: Log inconsistent or not a log (last==0, first!=1)");
+ xlog_warn("XFS: Log inconsistent or not a log (last==0, first!=1)");
return XFS_ERROR(EINVAL);
}
-
+
/* we have a partially zeroed log */
last_blk = log_bbnum-1;
if ((error = xlog_find_cycle_start(log, bp, 0, &last_blk, 0)))
* we scan over the defined maximum blocks. At this point, the maximum
* is not chosen to mean anything special. XXXmiken
*/
- num_scan_bblks = BTOBB(XLOG_MAX_ICLOGS<<XLOG_MAX_RECORD_BSHIFT);
+ num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log);
ASSERT(num_scan_bblks <= INT_MAX);
-
+
if (last_blk < num_scan_bblks)
num_scan_bblks = last_blk;
start_blk = last_blk - num_scan_bblks;
-
+
/*
* We search for any instances of cycle number 0 that occur before
* our current estimate of the head. What we're trying to detect is
* Potentially backup over partial log record write. We don't need
* to search the end of the log because we know it is zero.
*/
- if ((error = xlog_find_verify_log_record(log, start_blk,
- &last_blk, 0)))
+ if ((error = xlog_find_verify_log_record(log, start_blk,
+ &last_blk, 0)) == -1) {
+ error = XFS_ERROR(EIO);
+ goto bp_err;
+ } else if (error)
goto bp_err;
*blk_no = last_blk;
xfs_caddr_t dp,
xlog_t *log)
{
- int i;
+ int i, j, k;
+ union ich {
+ xlog_rec_header_t hic_header;
+ xlog_rec_ext_header_t hic_xheader;
+ char hic_sector[XLOG_HEADER_SIZE];
+ } *xhdr;
+
#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
uint *up = (uint *)dp;
uint chksum = 0;
#endif
- for (i=0; i<BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); i++) {
- /* these are both on-disk, so don't endian flip twice */
+ for (i=0; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)) &&
+ i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
*(uint *)dp = *(uint *)&rhead->h_cycle_data[i];
dp += BBSIZE;
}
+
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ xhdr = (union ich*)rhead;
+ for ( ; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); i++) {
+ j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ *(uint *)dp = xhdr[j].hic_xheader.xh_cycle_data[k];
+ dp += BBSIZE;
+ }
+ }
+
#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
/* divide length by 4 to get # words */
for (i=0; i < INT_GET(rhead->h_len, ARCH_CONVERT) >> 2; i++) {
if (!INT_ISZERO(rhead->h_chksum, ARCH_CONVERT) ||
((log->l_flags & XLOG_CHKSUM_MISMATCH) == 0)) {
cmn_err(CE_DEBUG,
- "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)",
+ "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)",
INT_GET(rhead->h_chksum, ARCH_CONVERT), chksum);
cmn_err(CE_DEBUG,
"XFS: Disregard message if filesystem was created with non-DEBUG kernel");
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ cmn_err(CE_DEBUG,
+ "XFS: LogR this is a LogV2 filesystem");
+ }
log->l_flags |= XLOG_CHKSUM_MISMATCH;
}
- }
+ }
#endif /* DEBUG && XFS_LOUD_RECOVERY */
} /* xlog_unpack_data */
-
STATIC xlog_recover_t *
xlog_recover_find_tid(xlog_recover_t *q,
xlog_tid_t tid)
return p;
} /* xlog_recover_find_tid */
+
STATIC void
xlog_recover_put_hashq(xlog_recover_t **q,
xlog_recover_t *trans)
*q = trans;
} /* xlog_recover_put_hashq */
+
STATIC void
xlog_recover_new_tid(xlog_recover_t **q,
xlog_tid_t tid,
xlog_recover_insert_item_backq(itemq, item);
} /* xlog_recover_add_item */
-/* The next region to add is the start of a new region. It could be
+/* The next region to add is the start of a new region. It could be
* a whole region or it could be the first part of a new region. Because
* of this, the assumption here is that the type and size fields of all
* format structures fit into the first 32 bits of the structure.
* will appear in the current log item.
*/
STATIC int
-xlog_recover_add_to_trans(xlog_recover_t *trans,
+ xlog_recover_add_to_trans(xlog_recover_t *trans,
xfs_caddr_t dp,
int len)
{
if (!len)
return 0;
ptr = kmem_zalloc(len, 0);
- bcopy(dp, ptr, len);
-
+ memcpy(ptr, dp, len);
+
in_f = (xfs_inode_log_format_t *)ptr;
item = trans->r_itemq;
if (item == 0) {
ASSERT(*(uint *)dp == XFS_TRANS_HEADER_MAGIC);
if (len == sizeof(xfs_trans_header_t))
xlog_recover_add_item(&trans->r_itemq);
- bcopy(dp, &trans->r_theader, len); /* s, d, l */
+ memcpy(&trans->r_theader, dp, len); /* d, s, l */
return 0;
}
if (item->ri_prev->ri_total != 0 &&
xlog_recover_item_t *item;
xfs_caddr_t ptr, old_ptr;
int old_len;
-
+
item = trans->r_itemq;
if (item == 0) {
/* finish copying rest of trans header */
xlog_recover_add_item(&trans->r_itemq);
ptr = (xfs_caddr_t)&trans->r_theader+sizeof(xfs_trans_header_t)-len;
- bcopy(dp, ptr, len); /* s, d, l */
+ memcpy(ptr, dp, len); /* d, s, l */
return 0;
}
item = item->ri_prev;
old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
old_len = item->ri_buf[item->ri_cnt-1].i_len;
- ptr = kmem_realloc(old_ptr, len+old_len, old_len, 0);
- bcopy(dp , &ptr[old_len], len); /* s, d, l */
+ ptr = kmem_realloc(old_ptr, len+old_len, old_len, 0);
+ memcpy(&ptr[old_len], dp, len); /* d, s, l */
item->ri_buf[item->ri_cnt-1].i_len += len;
item->ri_buf[item->ri_cnt-1].i_addr = ptr;
return 0;
int error;
unsigned long hash;
uint flags;
-
+
/* check the log format matches our own - else we can't recover */
if (xlog_header_check_recover(log->l_mp, rhead))
return (XFS_ERROR(EIO));
-
- while (dp < lp) {
+
+ while ((dp < lp) && num_logops) {
ASSERT(dp + sizeof(xlog_op_header_t) <= lp);
ohead = (xlog_op_header_t *)dp;
dp += sizeof(xlog_op_header_t);
xlog_warn("XFS: xlog_recover_process_data: bad clientid");
ASSERT(0);
return (XFS_ERROR(EIO));
- }
+ }
tid = INT_GET(ohead->oh_tid, ARCH_CONVERT);
hash = XLOG_RHASH(tid);
trans = xlog_recover_find_tid(rhash[hash], tid);
xfs_daddr_t blk_no;
xfs_caddr_t bufaddr;
xfs_buf_t *hbp, *dbp;
- int error;
- int bblks, split_bblks;
+ int error, h_size;
+ int bblks, split_bblks;
+ int hblks, split_hblks, wrapped_hblks;
xlog_recover_t *rhash[XLOG_RHASH_SIZE];
error = 0;
- hbp = xlog_get_bp(1,log->l_mp);
+
+
+ /*
+ * Read the header of the tail block and get the iclog buffer size from
+ * h_size. Use this to tell how many sectors make up the log header.
+ */
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ /*
+ * When using variable length iclogs, read first sector of iclog
+ * header and extract the header size from it. Get a new hbp that
+ * is the correct size.
+ */
+ hbp = xlog_get_bp(1, log->l_mp);
+ if (!hbp)
+ return ENOMEM;
+ if ((error = xlog_bread(log, tail_blk, 1, hbp)))
+ goto bread_err1;
+ rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp);
+ ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) ==
+ XLOG_HEADER_MAGIC_NUM);
+ if ((INT_GET(rhead->h_version, ARCH_CONVERT) & (~XLOG_VERSION_OKBITS)) != 0) {
+ xlog_warn("XFS: xlog_do_recovery_pass: unrecognised log version number.");
+ error = XFS_ERROR(EIO);
+ goto bread_err1;
+ }
+ h_size = INT_GET(rhead->h_size, ARCH_CONVERT);
+
+ if ((INT_GET(rhead->h_version, ARCH_CONVERT) & XLOG_VERSION_2) &&
+ (h_size > XLOG_HEADER_CYCLE_SIZE)) {
+ hblks = h_size / XLOG_HEADER_CYCLE_SIZE;
+ if (h_size % XLOG_HEADER_CYCLE_SIZE)
+ hblks++;
+ xlog_put_bp(hbp);
+ hbp = xlog_get_bp(hblks, log->l_mp);
+ } else {
+ hblks=1;
+ }
+ } else {
+ hblks=1;
+ hbp = xlog_get_bp(1, log->l_mp);
+ h_size = XLOG_BIG_RECORD_BSIZE;
+ }
+
if (!hbp)
return ENOMEM;
- dbp = xlog_get_bp(BTOBB(XLOG_MAX_RECORD_BSIZE),log->l_mp);
+ dbp = xlog_get_bp(BTOBB(h_size),log->l_mp);
if (!dbp) {
xlog_put_bp(hbp);
return ENOMEM;
}
- bzero(rhash, sizeof(rhash));
+
+ memset(rhash, 0, sizeof(rhash));
if (tail_blk <= head_blk) {
for (blk_no = tail_blk; blk_no < head_blk; ) {
- if ((error = xlog_bread(log, blk_no, 1, hbp)))
- goto bread_err;
+ if ((error = xlog_bread(log, blk_no, hblks, hbp)))
+ goto bread_err2;
rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp);
ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM);
ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX));
bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); /* blocks in data section */
+
+ if (unlikely((INT_GET(rhead->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) ||
+ (BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) > INT_MAX)) ||
+ (bblks <= 0) ||
+ (blk_no > log->l_logBBsize))) {
+ XFS_ERROR_REPORT("xlog_do_recovery_pass(1)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ error = EFSCORRUPTED;
+ goto bread_err2;
+ }
+
+ if ((INT_GET(rhead->h_version, ARCH_CONVERT) & (~XLOG_VERSION_OKBITS)) != 0) {
+ xlog_warn("XFS: xlog_do_recovery_pass: unrecognised log version number.");
+ error = XFS_ERROR(EIO);
+ goto bread_err2;
+ }
+ bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); /* blocks in data section */
if (bblks > 0) {
- if ((error = xlog_bread(log, blk_no+1, bblks, dbp)))
- goto bread_err;
+ if ((error = xlog_bread(log, blk_no+hblks, bblks, dbp)))
+ goto bread_err2;
xlog_unpack_data(rhead, XFS_BUF_PTR(dbp), log);
if ((error = xlog_recover_process_data(log, rhash,
rhead, XFS_BUF_PTR(dbp),
pass)))
- goto bread_err;
+ goto bread_err2;
}
- blk_no += (bblks+1);
+ blk_no += (bblks+hblks);
}
} else {
/*
*/
blk_no = tail_blk;
while (blk_no < log->l_logBBsize) {
-
- /* Read header of one block */
- if ((error = xlog_bread(log, blk_no, 1, hbp)))
- goto bread_err;
+ /*
+ * Check for header wrapping around physical end-of-log
+ */
+ wrapped_hblks = 0;
+ if (blk_no+hblks <= log->l_logBBsize) {
+ /* Read header in one read */
+ if ((error = xlog_bread(log, blk_no, hblks, hbp)))
+ goto bread_err2;
+ } else {
+ /* This log record is split across physical end of log */
+ split_hblks = 0;
+ if (blk_no != log->l_logBBsize) {
+ /* some data is before physical end of log */
+ ASSERT(blk_no <= INT_MAX);
+ split_hblks = log->l_logBBsize - (int)blk_no;
+ ASSERT(split_hblks > 0);
+ if ((error = xlog_bread(log, blk_no, split_hblks, hbp)))
+ goto bread_err2;
+ }
+ bufaddr = XFS_BUF_PTR(hbp);
+ XFS_BUF_SET_PTR(hbp, bufaddr + BBTOB(split_hblks),
+ BBTOB(hblks - split_hblks));
+ wrapped_hblks = hblks - split_hblks;
+ if ((error = xlog_bread(log, 0, wrapped_hblks, hbp)))
+ goto bread_err2;
+ XFS_BUF_SET_PTR(hbp, bufaddr, hblks);
+ }
rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp);
ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM);
- ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX));
+ ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX));
bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT));
/* LR body must have data or it wouldn't have been written */
ASSERT(bblks > 0);
- blk_no++; /* successfully read header */
- ASSERT(blk_no <= log->l_logBBsize);
+ blk_no += hblks; /* successfully read header */
- if ((INT_GET(rhead->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) ||
+ if (unlikely((INT_GET(rhead->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) ||
(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) > INT_MAX)) ||
- (bblks <= 0) ||
- (blk_no > log->l_logBBsize)) {
+ (bblks <= 0))) {
+ XFS_ERROR_REPORT("xlog_do_recovery_pass(2)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
error = EFSCORRUPTED;
- goto bread_err;
+ goto bread_err2;
}
-
+
/* Read in data for log record */
if (blk_no+bblks <= log->l_logBBsize) {
if ((error = xlog_bread(log, blk_no, bblks, dbp)))
- goto bread_err;
+ goto bread_err2;
} else {
/* This log record is split across physical end of log */
split_bblks = 0;
split_bblks = log->l_logBBsize - (int)blk_no;
ASSERT(split_bblks > 0);
if ((error = xlog_bread(log, blk_no, split_bblks, dbp)))
- goto bread_err;
+ goto bread_err2;
}
bufaddr = XFS_BUF_PTR(dbp);
XFS_BUF_SET_PTR(dbp, bufaddr + BBTOB(split_bblks),
BBTOB(bblks - split_bblks));
- if ((error = xlog_bread(log, 0, bblks - split_bblks, dbp)))
- goto bread_err;
- XFS_BUF_SET_PTR(dbp, bufaddr, XLOG_MAX_RECORD_BSIZE);
+ if ((error = xlog_bread(log, wrapped_hblks,
+ bblks - split_bblks, dbp)))
+ goto bread_err2;
+ XFS_BUF_SET_PTR(dbp, bufaddr, XLOG_BIG_RECORD_BSIZE);
}
xlog_unpack_data(rhead, XFS_BUF_PTR(dbp), log);
if ((error = xlog_recover_process_data(log, rhash,
rhead, XFS_BUF_PTR(dbp),
pass)))
- goto bread_err;
+ goto bread_err2;
blk_no += bblks;
}
/* read first part of physical log */
while (blk_no < head_blk) {
- if ((error = xlog_bread(log, blk_no, 1, hbp)))
- goto bread_err;
+ if ((error = xlog_bread(log, blk_no, hblks, hbp)))
+ goto bread_err2;
rhead = (xlog_rec_header_t *)XFS_BUF_PTR(hbp);
ASSERT(INT_GET(rhead->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM);
ASSERT(BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT) <= INT_MAX));
bblks = (int) BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT));
ASSERT(bblks > 0);
- if ((error = xlog_bread(log, blk_no+1, bblks, dbp)))
- goto bread_err;
+ if ((error = xlog_bread(log, blk_no+hblks, bblks, dbp)))
+ goto bread_err2;
xlog_unpack_data(rhead, XFS_BUF_PTR(dbp), log);
if ((error = xlog_recover_process_data(log, rhash,
rhead, XFS_BUF_PTR(dbp),
pass)))
- goto bread_err;
- blk_no += (bblks+1);
- }
+ goto bread_err2;
+ blk_no += (bblks+hblks);
+ }
}
-bread_err:
+bread_err2:
xlog_put_bp(dbp);
+bread_err1:
xlog_put_bp(hbp);
return error;