]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxlog/xfs_log_recover.c
[contributed] FreeBSD update from Alexander Kabaev
[thirdparty/xfsprogs-dev.git] / libxlog / xfs_log_recover.c
index 25adab221bd4da5ea10f5ee4ca45af6e8a0a3785..a43a99404cf59e3c07328f1ecc79a1819c283498 100644 (file)
@@ -1,36 +1,36 @@
 /*
- * 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) {
@@ -82,42 +82,45 @@ xlog_find_cycle_start(xlog_t        *log,
  */
 
 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;
                }
        }
 
@@ -156,22 +159,22 @@ xlog_find_verify_log_record(xlog_t        *log,
     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)");
@@ -182,13 +185,13 @@ xlog_find_verify_log_record(xlog_t        *log,
 
        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;
     }
 
     /*
@@ -197,17 +200,17 @@ xlog_find_verify_log_record(xlog_t        *log,
      * 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
@@ -215,8 +218,18 @@ xlog_find_verify_log_record(xlog_t *log,
      * 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:
@@ -229,7 +242,7 @@ 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.
  *
@@ -254,19 +267,19 @@ xlog_find_head(xlog_t  *log,
     /* 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;
     }
 
@@ -312,7 +325,7 @@ xlog_find_head(xlog_t  *log,
         * 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
@@ -354,7 +367,7 @@ xlog_find_head(xlog_t  *log,
      * 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
@@ -367,7 +380,7 @@ xlog_find_head(xlog_t  *log,
        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.
@@ -383,7 +396,7 @@ xlog_find_head(xlog_t  *log,
         * 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
@@ -397,7 +410,7 @@ xlog_find_head(xlog_t  *log,
        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;
@@ -477,9 +490,9 @@ 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 */
 
@@ -500,10 +513,10 @@ bp_err:
  * 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;
@@ -512,11 +525,12 @@ xlog_find_tail(xlog_t  *log,
        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;
@@ -538,10 +552,11 @@ xlog_find_tail(xlog_t  *log,
         * 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;
                }
@@ -553,10 +568,11 @@ xlog_find_tail(xlog_t  *log,
         * 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;
                        }
@@ -605,10 +621,27 @@ xlog_find_tail(xlog_t  *log,
         * 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;
                }
@@ -638,17 +671,27 @@ xlog_find_tail(xlog_t  *log,
         * 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 */
@@ -672,7 +715,7 @@ exit:
  */
 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;
@@ -680,7 +723,6 @@ xlog_find_zeroed(struct log *log,
        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)
@@ -704,13 +746,13 @@ xlog_find_zeroed(struct log       *log,
        } 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)))
@@ -722,13 +764,13 @@ xlog_find_zeroed(struct log       *log,
         * 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
@@ -745,8 +787,11 @@ xlog_find_zeroed(struct log        *log,
         * 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;
@@ -763,17 +808,34 @@ xlog_unpack_data(xlog_rec_header_t *rhead,
                 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++) {
@@ -784,17 +846,20 @@ xlog_unpack_data(xlog_rec_header_t *rhead,
            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)
@@ -809,6 +874,7 @@ xlog_recover_find_tid(xlog_recover_t *q,
        return p;
 }      /* xlog_recover_find_tid */
 
+
 STATIC void
 xlog_recover_put_hashq(xlog_recover_t **q,
                       xlog_recover_t *trans)
@@ -817,6 +883,7 @@ xlog_recover_put_hashq(xlog_recover_t **q,
        *q = trans;
 }      /* xlog_recover_put_hashq */
 
+
 STATIC void
 xlog_recover_new_tid(xlog_recover_t    **q,
                     xlog_tid_t         tid,
@@ -931,7 +998,7 @@ xlog_recover_add_item(xlog_recover_item_t **itemq)
        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.
@@ -944,7 +1011,7 @@ xlog_recover_add_item(xlog_recover_item_t **itemq)
  * 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)
 {
@@ -955,15 +1022,15 @@ xlog_recover_add_to_trans(xlog_recover_t *trans,
        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 &&
@@ -995,13 +1062,13 @@ xlog_recover_add_to_cont_trans(xlog_recover_t    *trans,
        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;
@@ -1009,8 +1076,8 @@ xlog_recover_add_to_cont_trans(xlog_recover_t     *trans,
        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;
@@ -1040,12 +1107,12 @@ xlog_recover_process_data(xlog_t            *log,
     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);
@@ -1054,7 +1121,7 @@ xlog_recover_process_data(xlog_t      *log,
            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);
@@ -1127,38 +1194,99 @@ xlog_do_recovery_pass(xlog_t    *log,
     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 {
        /*
@@ -1168,32 +1296,55 @@ xlog_do_recovery_pass(xlog_t    *log,
         */
        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;
@@ -1204,20 +1355,21 @@ xlog_do_recovery_pass(xlog_t    *log,
                    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;
        }
 
@@ -1226,26 +1378,27 @@ xlog_do_recovery_pass(xlog_t    *log,
 
        /* 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;