From: Nathan Scott Date: Wed, 17 Oct 2001 11:00:32 +0000 (+0000) Subject: add libxlog directory. X-Git-Tag: v2.0.0~35 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d321ceac8da6c31790d860ec9d25673244ff66ec;p=thirdparty%2Fxfsprogs-dev.git add libxlog directory. --- diff --git a/Makefile b/Makefile index 55dde91c5..c877f251e 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ LSRCFILES = configure configure.in Makepkgs install-sh README VERSION LDIRT = config.log config.status config.cache confdefs.h conftest* \ Logs/* install.* install-dev.* *.gz -SUBDIRS = include libxfs libhandle libdisk \ +SUBDIRS = include libxfs libxlog libhandle libdisk \ bmap db freeze fsck growfs imap logprint mkfile mkfs repair rtcp \ man doc debian build diff --git a/VERSION b/VERSION index e8de20afd..ebaef32ca 100644 --- a/VERSION +++ b/VERSION @@ -3,5 +3,5 @@ # PKG_MAJOR=1 PKG_MINOR=3 -PKG_REVISION=11 +PKG_REVISION=12 PKG_BUILD=0 diff --git a/doc/CHANGES b/doc/CHANGES index 633774543..cce19dde7 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -1,3 +1,9 @@ +xfsprogs-1.3.12 (17 October 2001) + - implement the -f (file) option to xfs_logprint + - rework the xlog code into libxlog for code sharing + - xfs_repair now detects a dirty log and, without -L, will + no longer blindly zero it (which prevents any recovery) + xfsprogs-1.3.11 (17 October 2001) - tidy up some (benign) compiler warnings from libxfs - fixed 64-bit pointer alignment issues in xfs_check diff --git a/include/Makefile b/include/Makefile index e54a8e3e4..af4946126 100644 --- a/include/Makefile +++ b/include/Makefile @@ -33,7 +33,7 @@ TOPDIR = .. include $(TOPDIR)/include/builddefs -HFILES = arch.h handle.h jdm.h libxfs.h xqm.h \ +HFILES = arch.h handle.h jdm.h libxfs.h libxlog.h xqm.h \ xfs_ag.h xfs_alloc.h xfs_alloc_btree.h xfs_arch.h xfs_attr_leaf.h \ xfs_attr_sf.h xfs_bit.h xfs_bmap.h xfs_bmap_btree.h xfs_btree.h \ xfs_buf_item.h xfs_cred.h xfs_da_btree.h xfs_dfrag.h xfs_dinode.h \ diff --git a/include/builddefs.in b/include/builddefs.in index eac435946..0aa78849e 100644 --- a/include/builddefs.in +++ b/include/builddefs.in @@ -41,6 +41,7 @@ MALLOCLIB = @malloc_lib@ LIBUUID = @libuuid@ LIBXFS = $(TOPDIR)/libxfs/libxfs.la +LIBXLOG = $(TOPDIR)/libxlog/libxlog.la LIBDISK = $(TOPDIR)/libdisk/libdisk.la LIBHANDLE = $(TOPDIR)/libhandle/libhandle.la diff --git a/include/libxlog.h b/include/libxlog.h new file mode 100644 index 000000000..4f0c7f215 --- /dev/null +++ b/include/libxlog.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2000 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://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ +#ifndef LIBXLOG_H +#define LIBXLOG_H + +#include + +/* + * define the userlevel xlog_t to be the subset of the kernel's + * xlog_t that we actually need to get our work done, avoiding + * the need to define any exotic kernel types in userland. + */ +typedef struct log { + xfs_lsn_t l_tail_lsn; /* lsn of 1st LR w/ unflush buffers */ + xfs_lsn_t l_last_sync_lsn;/* lsn of last LR on disk */ + xfs_mount_t *l_mp; /* mount point */ + dev_t l_dev; /* dev_t of log */ + xfs_daddr_t l_logBBstart; /* start block of log */ + int l_logsize; /* size of log in bytes */ + int l_logBBsize; /* size of log in 512 byte chunks */ + int l_curr_cycle; /* Cycle number of log writes */ + int l_prev_cycle; /* Cycle # b4 last block increment */ + int l_curr_block; /* current logical block of log */ + int l_prev_block; /* previous logical block of log */ + int l_iclog_size; /* size of log in bytes */ + int l_iclog_size_log;/* log power size of log */ + int l_iclog_bufs; /* number of iclog buffers */ + int l_grant_reserve_cycle; /* */ + int l_grant_reserve_bytes; /* */ + int l_grant_write_cycle; /* */ + int l_grant_write_bytes; /* */ +} xlog_t; + +#include +#include +#include +#include +#include + + +/* + * macros mapping kernel code to user code + */ +#define STATIC static +#define EFSCORRUPTED 990 +#define XFS_ERROR(e) (e) +#define min(a,b) ((a) < (b) ? (a) : (b)) + +#if (__GNUC__ < 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ <= 95)) +# define xlog_warn(fmt,args...) \ + ( fprintf(stderr,fmt,## args), fputc('\n', stderr) ) +# define cmn_err(sev,fmt,args...) \ + xlog_warn(fmt,## args) +# define xlog_exit(fmt,args...) \ + ( xlog_warn(fmt,## args), exit(1) ) +# define xlog_panic(fmt,args...) \ + xlog_exit(fmt,## args) +#else +# define xlog_warn(...) \ + ( fprintf(stderr,__VA_ARGS__), fputc('\n', stderr) ) +# define cmn_err(sev,...) \ + xlog_warn(__VA_ARGS__) +# define xlog_exit(...) \ + ( xlog_warn(__VA_ARGS__), exit(1) ) +# define xlog_panic(...) \ + xlog_exit(__VA_ARGS__) +#endif + +#define xlog_get_bp(nbblks, mp) libxfs_getbuf(x.logdev, 0, (nbblks)) +#define xlog_put_bp(bp) libxfs_putbuf(bp) +#define xlog_bread(log,blkno,nbblks,bp) \ + (libxfs_readbufr(x.logdev, \ + (log)->l_logBBstart+(blkno), bp, (nbblks), 1), 0) + +#define kmem_zalloc(size, foo) calloc(size,1) +#define kmem_free(ptr, foo) free(ptr) +#define kmem_realloc(ptr, len, old, foo) realloc(ptr, len) + +/* exports */ +extern int print_record_header; +extern int print_exit; + +/* libxfs parameters */ +extern libxfs_init_t x; + +extern void xfs_log_print_trans(xlog_t *log, + int print_block_start); + +extern void xfs_log_print( xlog_t *log, + int fd, + int print_block_start); + +extern int xlog_find_zeroed(xlog_t *log, xfs_daddr_t *blk_no); +extern int xlog_find_cycle_start(xlog_t *log, xfs_buf_t *bp, + xfs_daddr_t first_blk, xfs_daddr_t *last_blk, uint cycle); +extern int xlog_find_tail(xlog_t *log, xfs_daddr_t *head_blk, + xfs_daddr_t *tail_blk, int readonly); + +extern int xlog_test_footer(xlog_t *log); +extern int xlog_recover(xlog_t *log, int readonly); +extern void xlog_recover_print_data(xfs_caddr_t p, int len); +extern void xlog_recover_print_logitem(xlog_recover_item_t *item); +extern void xlog_recover_print_trans_head(xlog_recover_t *tr); +extern int xlog_print_find_oldest(xlog_t *log, xfs_daddr_t *last_blk); + +extern void print_xlog_op_line(void); +extern void print_xlog_record_line(void); +extern void print_stars(void); + +/* for transactional view */ +extern void xlog_recover_print_trans_head(xlog_recover_t *tr); + +extern void xlog_recover_print_trans( xlog_recover_t *trans, + xlog_recover_item_t *itemq, + int print); + +extern int xlog_do_recovery_pass( xlog_t *log, + xfs_daddr_t head_blk, + xfs_daddr_t tail_blk, + int pass); +extern int xlog_recover_do_trans( xlog_t *log, + xlog_recover_t *trans, + int pass); +extern int xlog_header_check_recover( xfs_mount_t *mp, + xlog_rec_header_t *head); +extern int xlog_header_check_mount( xfs_mount_t *mp, + xlog_rec_header_t *head); + +#endif /* LIBXLOG_H */ diff --git a/libxlog/Makefile b/libxlog/Makefile new file mode 100644 index 000000000..85250bfc4 --- /dev/null +++ b/libxlog/Makefile @@ -0,0 +1,50 @@ +# +# Copyright (c) 2000 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://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ +# + +TOPDIR = .. +include $(TOPDIR)/include/builddefs + +LTLIBRARY = libxlog.la +LT_CURRENT = 0 +LT_REVISION = 0 +LT_AGE = 0 + +CFILES = xfs_log_recover.c util.c + +default: $(LTLIBRARY) + +include $(BUILDRULES) + +install: default + +install-dev: default + $(INSTALL_LTLIB_STATIC) diff --git a/libxlog/util.c b/libxlog/util.c new file mode 100644 index 000000000..1d0592a37 --- /dev/null +++ b/libxlog/util.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2000 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://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include + +int print_exit; +int print_record_header; +libxfs_init_t x; + +static int +header_check_uuid(xfs_mount_t *mp, xlog_rec_header_t *head) +{ + char uu_log[64], uu_sb[64]; + + if (!uuid_compare(mp->m_sb.sb_uuid, head->h_fs_uuid)) return 0; + + uuid_unparse(mp->m_sb.sb_uuid, uu_sb); + uuid_unparse(head->h_fs_uuid, uu_log); + + printf("* ERROR: mismatched uuid in log\n" + "* SB : %s\n* log: %s\n", + uu_sb, uu_log); + + return 1; +} + +int +xlog_header_check_recover(xfs_mount_t *mp, xlog_rec_header_t *head) +{ + if (print_record_header) + printf("\nLOG REC AT LSN cycle %d block %d (0x%x, 0x%x)\n", + CYCLE_LSN(head->h_lsn, ARCH_CONVERT), + BLOCK_LSN(head->h_lsn, ARCH_CONVERT), + CYCLE_LSN(head->h_lsn, ARCH_CONVERT), + BLOCK_LSN(head->h_lsn, ARCH_CONVERT)); + + if (INT_GET(head->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) { + + printf("* ERROR: bad magic number in log header: 0x%x\n", + INT_GET(head->h_magicno, ARCH_CONVERT)); + + } else if (header_check_uuid(mp, head)) { + + /* failed - fall through */ + + } else if (INT_GET(head->h_fmt, ARCH_CONVERT) != XLOG_FMT) { + + printf("* ERROR: log format incompatible (log=%d, ours=%d)\n", + INT_GET(head->h_fmt, ARCH_CONVERT), XLOG_FMT); + + } else { + /* everything is ok */ + return 0; + } + + /* bail out now or just carry on regardless */ + if (print_exit) + xlog_exit("Bad log"); + + return 0; +} + +int +xlog_header_check_mount(xfs_mount_t *mp, xlog_rec_header_t *head) +{ + if (uuid_is_null(head->h_fs_uuid)) return 0; + if (header_check_uuid(mp, head)) { + /* bail out now or just carry on regardless */ + if (print_exit) + xlog_exit("Bad log"); + } + return 0; +} diff --git a/libxlog/xfs_log_recover.c b/libxlog/xfs_log_recover.c new file mode 100644 index 000000000..be7e6c272 --- /dev/null +++ b/libxlog/xfs_log_recover.c @@ -0,0 +1,1247 @@ +/* + * Copyright (c) 2000 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://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include + +/* + * This routine finds (to an approximation) the first block in the physical + * log which contains the given cycle. It uses a binary search algorithm. + * Note that the algorithm can not be perfect because the disk will not + * necessarily be perfect. + */ +int +xlog_find_cycle_start(xlog_t *log, + xfs_buf_t *bp, + xfs_daddr_t first_blk, + xfs_daddr_t *last_blk, + uint cycle) +{ + 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) { + if ((error = xlog_bread(log, mid_blk, 1, bp))) + return error; + mid_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + if (mid_cycle == cycle) { + *last_blk = mid_blk; + /* last_half_cycle == mid_cycle */ + } else { + first_blk = mid_blk; + /* first_half_cycle == mid_cycle */ + } + mid_blk = BLK_AVG(first_blk, *last_blk); + } + ASSERT((mid_blk == first_blk && mid_blk+1 == *last_blk) || + (mid_blk == *last_blk && mid_blk-1 == first_blk)); + + return 0; +} /* xlog_find_cycle_start */ + + +/* + * Check that the range of blocks does not contain the cycle number + * given. The scan needs to occur from front to back and the ptr into the + * region must be updated since a later routine will need to perform another + * test. If the region is completely good, we end up returning the same + * last block number. + * + * Return -1 if we encounter no errors. This is an invalid block number + * since we don't ever expect logs to get this large. + */ + +STATIC xfs_daddr_t +xlog_find_verify_cycle( xlog_t *log, + xfs_daddr_t start_blk, + int nbblks, + uint stop_on_cycle_no) +{ + int i, j; + uint cycle; + xfs_buf_t *bp; + char *buf = NULL; + int error = 0; + xfs_daddr_t bufblks = nbblks; + + while (!(bp = xlog_get_bp(bufblks, log->l_mp))) { + /* can't get enough memory to do everything in one big buffer */ + bufblks >>= 1; + if (!bufblks) + return -ENOMEM; + } + + + for (i = start_blk; i < start_blk + nbblks; i += bufblks) { + int bcount = min(bufblks, (start_blk + nbblks - i)); + + 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) { + error = i; + goto out; + } + + buf += BBSIZE; + } + } + + error = -1; + +out: + xlog_put_bp(bp); + + return error; +} /* xlog_find_verify_cycle */ + + +/* + * Potentially backup over partial log record write. + * + * In the typical case, last_blk is the number of the block directly after + * a good log record. Therefore, we subtract one to get the block number + * of the last block in the given buffer. extra_bblks contains the number + * of blocks we would have read on a previous read. This happens when the + * last log record is split over the end of the physical log. + * + * extra_bblks is the number of blocks potentially verified on a previous + * call to this routine. + */ + +STATIC int +xlog_find_verify_log_record(xlog_t *log, + xfs_daddr_t start_blk, + xfs_daddr_t *last_blk, + int extra_bblks) +{ + xfs_daddr_t i; + xfs_buf_t *bp; + char *buf = NULL; + xlog_rec_header_t *head = NULL; + int error = 0; + int smallmem = 0; + int num_blks = *last_blk - start_blk; + + 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); + } else { + if ((error = xlog_bread(log, start_blk, num_blks, bp))) + goto out; + buf = XFS_BUF_PTR(bp) + (num_blks - 1) * BBSIZE; + } + + + 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)"); +#ifdef __KERNEL__ + ASSERT(0); +#endif + error = XFS_ERROR(EIO); + goto out; + } + + if (smallmem && (error = xlog_bread(log, i, 1, bp))) + goto out; + head = (xlog_rec_header_t*)buf; + + if (INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM) + break; + + if (!smallmem) + buf -= BBSIZE; + } + + /* + * We hit the beginning of the physical log & still no header. Return + * to caller. If caller can handle a return of -1, then this routine + * will be called again for the end of the physical log. + */ + if (i == -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; + + /* + * 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 + * up reading an entire log record. In this case, we don't want to + * 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) + *last_blk = i; + +out: + xlog_put_bp(bp); + + return error; +} /* xlog_find_verify_log_record */ + +/* + * 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 + * current cycle number -1 won't be present in the log if we start writing + * from our current block number. + * + * last_blk contains the block number of the first block with a given + * cycle number. + * + * Also called from xfs_log_print.c + * + * Return: zero if normal, non-zero if error. + */ +int +xlog_find_head(xlog_t *log, + xfs_daddr_t *return_head_blk) +{ + xfs_buf_t *bp; + xfs_daddr_t new_blk, first_blk, start_blk, last_blk, head_blk; + int num_scan_bblks; + uint first_half_cycle, last_half_cycle; + uint stop_on_cycle; + int error, log_bbnum = log->l_logBBsize; + + /* 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"); + } + + return 0; + } else if (error) { + xlog_warn("XFS: empty log check failed"); + return error; + } + + first_blk = 0; /* get cycle # of 1st block */ + bp = xlog_get_bp(1,log->l_mp); + if (!bp) + return -ENOMEM; + if ((error = xlog_bread(log, 0, 1, bp))) + goto bp_err; + first_half_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + + last_blk = head_blk = log_bbnum-1; /* get cycle # of last block */ + if ((error = xlog_bread(log, last_blk, 1, bp))) + goto bp_err; + last_half_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + ASSERT(last_half_cycle != 0); + + /* + * If the 1st half cycle number is equal to the last half cycle number, + * then the entire log is stamped with the same cycle number. In this + * case, head_blk can't be set to zero (which makes sense). The below + * math doesn't work out properly with head_blk equal to zero. Instead, + * we set it to log_bbnum which is an illegal block number, but this + * value makes the math correct. If head_blk doesn't changed through + * all the tests below, *head_blk is set to zero at the very end rather + * than log_bbnum. In a sense, log_bbnum and zero are the same block + * in a circular file. + */ + if (first_half_cycle == last_half_cycle) { + /* + * In this case we believe that the entire log should have cycle + * number last_half_cycle. We need to scan backwards from the + * end verifying that there are no holes still containing + * last_half_cycle - 1. If we find such a hole, then the start + * of that hole will be the new head. The simple case looks like + * x | x ... | x - 1 | x + * Another case that fits this picture would be + * x | x + 1 | x ... | x + * In this case the head really is somwhere at the end of the + * log, as one of the latest writes at the beginning was incomplete. + * One more case is + * x | x + 1 | x ... | x - 1 | x + * 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 + * we know that they cannot be the head since the log started with + * x. + */ + head_blk = log_bbnum; + stop_on_cycle = last_half_cycle - 1; + } else { + /* + * In this case we want to find the first block with cycle number + * matching last_half_cycle. We expect the log to be some + * variation on + * x + 1 ... | x ... + * The first block with cycle number x (last_half_cycle) will be + * where the new head belongs. First we do a binary search for + * the first occurrence of last_half_cycle. The binary search + * may not be totally accurate, so then we scan back from there + * looking for occurrences of last_half_cycle before us. If + * that backwards scan wraps around the beginning of the log, + * then we look for occurrences of last_half_cycle - 1 at the + * end of the log. The cases we're looking for look like + * x + 1 ... | x | x + 1 | x ... + * ^ binary search stopped here + * or + * x + 1 ... | x ... | x - 1 | x + * <---------> less than scan distance + */ + stop_on_cycle = last_half_cycle; + if ((error = xlog_find_cycle_start(log, bp, first_blk, + &head_blk, last_half_cycle))) + goto bp_err; + } + + /* + * Now validate the answer. Scan back some number of maximum possible + * blocks and make sure each one has the expected cycle number. The + * maximum is determined by the total possible amount of buffering + * 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<= num_scan_bblks) { + /* + * We are guaranteed that the entire check can be performed + * in one buffer. + */ + start_blk = head_blk - num_scan_bblks; + new_blk = xlog_find_verify_cycle(log, start_blk, num_scan_bblks, + stop_on_cycle); + 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. + * If we find one, then we know that the log starts there, as we've + * found a hole that didn't get written in going around the end + * of the physical log. The simple case for this is + * x + 1 ... | x ... | x - 1 | x + * <---------> less than scan distance + * If all of the blocks at the end of the log have cycle number + * last_half_cycle, then we check the blocks at the start of the + * log looking for occurrences of last_half_cycle. If we find one, + * then our current estimate for the location of the first + * 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 + * Another case we need to handle that only occurs in 256k logs is + * x + 1 ... | x ... | x+1 | x ... + * ^ binary search stops here + * In a 256k log, the scan at the end of the log will see the x+1 + * blocks. We need to skip past those since that is certainly not + * the head of the log. By searching for last_half_cycle-1 we + * accomplish that. + */ + start_blk = log_bbnum - num_scan_bblks + head_blk; + ASSERT(head_blk <= INT_MAX && (xfs_daddr_t) num_scan_bblks-head_blk >= 0); + new_blk= xlog_find_verify_cycle(log, start_blk, + num_scan_bblks-(int)head_blk, (stop_on_cycle - 1)); + if (new_blk != -1) { + head_blk = new_blk; + goto bad_blk; + } + + /* + * Scan beginning of log now. The last part of the physical log + * is good. This scan needs to verify that it doesn't find the + * last_half_cycle. + */ + start_blk = 0; + ASSERT(head_blk <= INT_MAX); + new_blk = xlog_find_verify_cycle(log, start_blk, (int) head_blk, + stop_on_cycle); + if (new_blk != -1) + head_blk = new_blk; + } + +bad_blk: + /* + * Now we need to make sure head_blk is not pointing to a block in + * the middle of a log record. + */ + num_scan_bblks = BTOBB(XLOG_MAX_RECORD_BSIZE); + if (head_blk >= num_scan_bblks) { + start_blk = head_blk - num_scan_bblks; /* don't read head_blk */ + + /* start ptr at last block ptr before head_blk */ + if ((error = xlog_find_verify_log_record(log, + start_blk, + &head_blk, + 0)) == -1) { + error = XFS_ERROR(EIO); + goto bp_err; + } else if (error) + goto bp_err; + } else { + start_blk = 0; + ASSERT(head_blk <= INT_MAX); + if ((error = xlog_find_verify_log_record(log, + start_blk, + &head_blk, + 0)) == -1) { + /* We hit the beginning of the log during our search */ + start_blk = log_bbnum - num_scan_bblks + head_blk; + new_blk = log_bbnum; + ASSERT(start_blk <= INT_MAX && (xfs_daddr_t) log_bbnum-start_blk >= 0); + ASSERT(head_blk <= INT_MAX); + if ((error = xlog_find_verify_log_record(log, + start_blk, + &new_blk, + (int)head_blk)) == -1) { + error = XFS_ERROR(EIO); + goto bp_err; + } else if (error) + goto bp_err; + if (new_blk != log_bbnum) + head_blk = new_blk; + } else if (error) + goto bp_err; + } + + xlog_put_bp(bp); + if (head_blk == log_bbnum) + *return_head_blk = 0; + else + *return_head_blk = head_blk; + /* + * When returning here, we have a good block number. Bad block + * means that during a previous crash, we didn't have a clean break + * from cycle number N to cycle number N-1. In this case, we need + * to find the first block with cycle number N-1. + */ + return 0; + +bp_err: + xlog_put_bp(bp); + + if (error) + xlog_warn("XFS: failed to find log head"); + + return error; +} /* xlog_find_head */ + +/* + * Find the sync block number or the tail of the log. + * + * This will be the block number of the last record to have its + * associated buffers synced to disk. Every log record header has + * a sync lsn embedded in it. LSNs hold block numbers, so it is easy + * to get a sync block number. The only concern is to figure out which + * log record header to believe. + * + * The following algorithm uses the log record header with the largest + * lsn. The entire log record does not need to be valid. We only care + * that the header is valid. + * + * We could speed up search by using current head_blk buffer, but it is not + * available. + */ +int +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_buf_t *bp; + int error, i, found; + xfs_daddr_t umount_data_blk; + xfs_daddr_t after_umount_blk; + xfs_lsn_t tail_lsn; + + found = error = 0; + + /* + * Find previous log record + */ + if ((error = xlog_find_head(log, head_blk))) + return error; + + bp = xlog_get_bp(1,log->l_mp); + if (!bp) + return -ENOMEM; + if (*head_blk == 0) { /* special case */ + if ((error = xlog_bread(log, 0, 1, bp))) + goto bread_err; + if (GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT) == 0) { + *tail_blk = 0; + /* leave all other log inited values alone */ + goto exit; + } + } + + /* + * Search backwards looking for log record header block + */ + ASSERT(*head_blk < INT_MAX); + 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) { + found = 1; + break; + } + } + /* + * If we haven't found the log record header block, start looking + * again from the end of the physical log. XXXmiken: There should be + * a check here to make sure we didn't search more than N blocks in + * the previous code. + */ + if (!found) { + 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) { + found = 2; + break; + } + } + } + if (!found) { + xlog_warn("XFS: xlog_find_tail: couldn't find sync record"); + ASSERT(0); + return XFS_ERROR(EIO); + } + + /* find blk_no of tail of log */ + rhead = (xlog_rec_header_t *)XFS_BUF_PTR(bp); + *tail_blk = BLOCK_LSN(rhead->h_tail_lsn, ARCH_CONVERT); + + /* + * Reset log values according to the state of the log when we + * crashed. In the case where head_blk == 0, we bump curr_cycle + * one because the next write starts a new cycle rather than + * continuing the cycle of the last good log record. At this + * point we have guaranteed that all partial log records have been + * accounted for. Therefore, we know that the last good log record + * written was complete and ended exactly on the end boundary + * of the physical log. + */ + log->l_prev_block = i; + log->l_curr_block = (int)*head_blk; + log->l_curr_cycle = INT_GET(rhead->h_cycle, ARCH_CONVERT); + if (found == 2) + log->l_curr_cycle++; + log->l_tail_lsn = INT_GET(rhead->h_tail_lsn, ARCH_CONVERT); + log->l_last_sync_lsn = INT_GET(rhead->h_lsn, ARCH_CONVERT); + log->l_grant_reserve_cycle = log->l_curr_cycle; + log->l_grant_reserve_bytes = BBTOB(log->l_curr_block); + log->l_grant_write_cycle = log->l_curr_cycle; + log->l_grant_write_bytes = BBTOB(log->l_curr_block); + + /* + * Look for unmount record. If we find it, then we know there + * was a clean unmount. Since 'i' could be the last block in + * the physical log, we convert to a log block before comparing + * to the head_blk. + * + * Save the current tail lsn to use to pass to + * xlog_clear_stale_blocks() below. We won't want to clear the + * 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; + 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 ((error = xlog_bread(log, umount_data_blk, 1, bp))) { + goto bread_err; + } + op_head = (xlog_op_header_t *)XFS_BUF_PTR(bp); + if (op_head->oh_flags & XLOG_UNMOUNT_TRANS) { + /* + * Set tail and last sync so that newly written + * log records will point recovery to after the + * current unmount record. + */ + ASSIGN_ANY_LSN(log->l_tail_lsn, log->l_curr_cycle, + after_umount_blk, ARCH_NOCONVERT); + ASSIGN_ANY_LSN(log->l_last_sync_lsn, log->l_curr_cycle, + after_umount_blk, ARCH_NOCONVERT); + *tail_blk = after_umount_blk; + } + } + +#ifdef __KERNEL__ + /* + * Make sure that there are no blocks in front of the head + * with the same cycle number as the head. This can happen + * because we allow multiple outstanding log writes concurrently, + * and the later writes might make it out before earlier ones. + * + * We use the lsn from before modifying it so that we'll never + * overwrite the unmount record after a clean unmount. + * + * Do this only if we are going to recover the filesystem + */ + if (!readonly) + 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"); + + return error; +} /* xlog_find_tail */ + + +/* + * Is the log zeroed at all? + * + * The last binary search should be changed to perform an X block read + * once X becomes small enough. You can then search linearly through + * the X blocks. This will cut down on the number of reads we need to do. + * + * If the log is partially zeroed, this routine will pass back the blkno + * of the first block with cycle number 0. It won't have a complete LR + * preceding it. + * + * Return: + * 0 => the log is completely written to + * -1 => use *blk_no as the first block of the log + * >0 => error has occurred + */ +int +xlog_find_zeroed(struct log *log, + xfs_daddr_t *blk_no) +{ + xfs_buf_t *bp; + uint first_cycle, last_cycle; + xfs_daddr_t new_blk, last_blk, start_blk; + 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) + return -ENOMEM; + if ((error = xlog_bread(log, 0, 1, bp))) + goto bp_err; + first_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + if (first_cycle == 0) { /* completely zeroed log */ + *blk_no = 0; + xlog_put_bp(bp); + return -1; + } + + /* check partially zeroed log */ + if ((error = xlog_bread(log, log_bbnum-1, 1, bp))) + goto bp_err; + last_cycle = GET_CYCLE(XFS_BUF_PTR(bp), ARCH_CONVERT); + if (last_cycle != 0) { /* log completely written to */ + xlog_put_bp(bp); + return 0; + } 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. + */ + 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))) + goto bp_err; + + /* + * Validate the answer. Because there is no way to guarantee that + * the entire log is made up of log records which are the same size, + * 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<h_len, ARCH_CONVERT)); i++) { + INT_SET(*(uint *)dp, ARCH_CONVERT, INT_GET(*(uint *)&rhead->h_cycle_data[i], ARCH_CONVERT)); + 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++) { + chksum ^= INT_GET(*up, ARCH_CONVERT); + up++; + } + if (chksum != INT_GET(rhead->h_chksum, ARCH_CONVERT)) { + 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)", + INT_GET(rhead->h_chksum, ARCH_CONVERT), chksum); + cmn_err(CE_DEBUG, +"XFS: Disregard message if filesystem was created with non-DEBUG kernel"); + 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) +{ + xlog_recover_t *p = q; + + while (p != NULL) { + if (p->r_log_tid == tid) + break; + p = p->r_next; + } + return p; +} /* xlog_recover_find_tid */ + +STATIC void +xlog_recover_put_hashq(xlog_recover_t **q, + xlog_recover_t *trans) +{ + trans->r_next = *q; + *q = trans; +} /* xlog_recover_put_hashq */ + +STATIC void +xlog_recover_new_tid(xlog_recover_t **q, + xlog_tid_t tid, + xfs_lsn_t lsn) +{ + xlog_recover_t *trans; + + trans = kmem_zalloc(sizeof(xlog_recover_t), 0); + trans->r_log_tid = tid; + trans->r_lsn = lsn; + xlog_recover_put_hashq(q, trans); +} /* xlog_recover_new_tid */ + + +STATIC int +xlog_recover_unlink_tid(xlog_recover_t **q, + xlog_recover_t *trans) +{ + xlog_recover_t *tp; + int found = 0; + + ASSERT(trans != 0); + if (trans == *q) { + *q = (*q)->r_next; + } else { + tp = *q; + while (tp != 0) { + if (tp->r_next == trans) { + found = 1; + break; + } + tp = tp->r_next; + } + if (!found) { + xlog_warn( + "XFS: xlog_recover_unlink_tid: trans not found"); + ASSERT(0); + return XFS_ERROR(EIO); + } + tp->r_next = tp->r_next->r_next; + } + return 0; +} /* xlog_recover_unlink_tid */ + +/* + * Free up any resources allocated by the transaction + * + * Remember that EFIs, EFDs, and IUNLINKs are handled later. + */ +STATIC void +xlog_recover_free_trans(xlog_recover_t *trans) +{ + xlog_recover_item_t *first_item, *item, *free_item; + int i; + + item = first_item = trans->r_itemq; + do { + free_item = item; + item = item->ri_next; + /* Free the regions in the item. */ + for (i = 0; i < free_item->ri_cnt; i++) { + kmem_free(free_item->ri_buf[i].i_addr, + free_item->ri_buf[i].i_len); + } + /* Free the item itself */ + kmem_free(free_item->ri_buf, + (free_item->ri_total * sizeof(xfs_log_iovec_t))); + kmem_free(free_item, sizeof(xlog_recover_item_t)); + } while (first_item != item); + /* Free the transaction recover structure */ + kmem_free(trans, sizeof(xlog_recover_t)); +} /* xlog_recover_free_trans */ + + +STATIC int +xlog_recover_commit_trans(xlog_t *log, + xlog_recover_t **q, + xlog_recover_t *trans, + int pass) +{ + int error; + + if ((error = xlog_recover_unlink_tid(q, trans))) + return error; + if ((error = xlog_recover_do_trans(log, trans, pass))) + return error; + xlog_recover_free_trans(trans); /* no error */ + return 0; +} /* xlog_recover_commit_trans */ + +STATIC void +xlog_recover_insert_item_backq(xlog_recover_item_t **q, + xlog_recover_item_t *item) +{ + if (*q == 0) { + item->ri_prev = item->ri_next = item; + *q = item; + } else { + item->ri_next = *q; + item->ri_prev = (*q)->ri_prev; + (*q)->ri_prev = item; + item->ri_prev->ri_next = item; + } +} /* xlog_recover_insert_item_backq */ + +STATIC void +xlog_recover_add_item(xlog_recover_item_t **itemq) +{ + xlog_recover_item_t *item; + + item = kmem_zalloc(sizeof(xlog_recover_item_t), 0); + 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 + * 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. + * + * This works because all regions must be 32 bit aligned. Therefore, we + * either have both fields or we have neither field. In the case we have + * neither field, the data part of the region is zero length. We only have + * a log_op_header and can throw away the header since a new one will appear + * later. If we have at least 4 bytes, then we can determine how many regions + * will appear in the current log item. + */ +STATIC int +xlog_recover_add_to_trans(xlog_recover_t *trans, + xfs_caddr_t dp, + int len) +{ + xfs_inode_log_format_t *in_f; /* any will do */ + xlog_recover_item_t *item; + xfs_caddr_t ptr; + + if (!len) + return 0; + ptr = kmem_zalloc(len, 0); + bcopy(dp, ptr, 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 */ + return 0; + } + if (item->ri_prev->ri_total != 0 && + item->ri_prev->ri_total == item->ri_prev->ri_cnt) { + xlog_recover_add_item(&trans->r_itemq); + } + item = trans->r_itemq; + item = item->ri_prev; + + if (item->ri_total == 0) { /* first region to be added */ + item->ri_total = in_f->ilf_size; + ASSERT(item->ri_total <= XLOG_MAX_REGIONS_IN_ITEM); + item->ri_buf = kmem_zalloc((item->ri_total * + sizeof(xfs_log_iovec_t)), 0); + } + ASSERT(item->ri_total > item->ri_cnt); + /* Description region is ri_buf[0] */ + item->ri_buf[item->ri_cnt].i_addr = ptr; + item->ri_buf[item->ri_cnt].i_len = len; + item->ri_cnt++; + return 0; +} /* xlog_recover_add_to_trans */ + +STATIC int +xlog_recover_add_to_cont_trans(xlog_recover_t *trans, + xfs_caddr_t dp, + int len) +{ + 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 */ + 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 */ + item->ri_buf[item->ri_cnt-1].i_len += len; + item->ri_buf[item->ri_cnt-1].i_addr = ptr; + return 0; +} /* xlog_recover_add_to_cont_trans */ + +STATIC int +xlog_recover_unmount_trans(xlog_recover_t *trans) +{ + /* Do nothing now */ + xlog_warn("XFS: xlog_recover_unmount_trans: Unmount LR"); + return( 0 ); +} /* xlog_recover_unmount_trans */ + + +STATIC int +xlog_recover_process_data(xlog_t *log, + xlog_recover_t *rhash[], + xlog_rec_header_t *rhead, + xfs_caddr_t dp, + int pass) +{ + xfs_caddr_t lp = dp+INT_GET(rhead->h_len, ARCH_CONVERT); + int num_logops = INT_GET(rhead->h_num_logops, ARCH_CONVERT); + xlog_op_header_t *ohead; + xlog_recover_t *trans; + xlog_tid_t tid; + 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) { + ASSERT(dp + sizeof(xlog_op_header_t) <= lp); + ohead = (xlog_op_header_t *)dp; + dp += sizeof(xlog_op_header_t); + if (ohead->oh_clientid != XFS_TRANSACTION && + ohead->oh_clientid != XFS_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); + if (trans == NULL) { /* not found; add new tid */ + if (ohead->oh_flags & XLOG_START_TRANS) + xlog_recover_new_tid(&rhash[hash], tid, INT_GET(rhead->h_lsn, ARCH_CONVERT)); + } else { + ASSERT(dp+INT_GET(ohead->oh_len, ARCH_CONVERT) <= lp); + flags = ohead->oh_flags & ~XLOG_END_TRANS; + if (flags & XLOG_WAS_CONT_TRANS) + flags &= ~XLOG_CONTINUE_TRANS; + switch (flags) { + case XLOG_COMMIT_TRANS: { + error = xlog_recover_commit_trans(log, &rhash[hash], + trans, pass); + break; + } + case XLOG_UNMOUNT_TRANS: { + error = xlog_recover_unmount_trans(trans); + break; + } + case XLOG_WAS_CONT_TRANS: { + error = xlog_recover_add_to_cont_trans(trans, dp, + INT_GET(ohead->oh_len, ARCH_CONVERT)); + break; + } + case XLOG_START_TRANS : { + xlog_warn("XFS: xlog_recover_process_data: bad transaction"); + ASSERT(0); + error = XFS_ERROR(EIO); + break; + } + case 0: + case XLOG_CONTINUE_TRANS: { + error = xlog_recover_add_to_trans(trans, dp, + INT_GET(ohead->oh_len, ARCH_CONVERT)); + break; + } + default: { + xlog_warn("XFS: xlog_recover_process_data: bad flag"); + ASSERT(0); + error = XFS_ERROR(EIO); + break; + } + } /* switch */ + if (error) + return error; + } /* if */ + dp += INT_GET(ohead->oh_len, ARCH_CONVERT); + num_logops--; + } + return( 0 ); +} /* xlog_recover_process_data */ + +/* + * Read the log from tail to head and process the log records found. + * Handle the two cases where the tail and head are in the same cycle + * and where the active portion of the log wraps around the end of + * the physical log separately. The pass parameter is passed through + * to the routines called to process the data and is not looked at + * here. + */ +int +xlog_do_recovery_pass(xlog_t *log, + xfs_daddr_t head_blk, + xfs_daddr_t tail_blk, + int pass) +{ + xlog_rec_header_t *rhead; + xfs_daddr_t blk_no; + xfs_caddr_t bufaddr; + xfs_buf_t *hbp, *dbp; + int error; + int bblks, split_bblks; + xlog_recover_t *rhash[XLOG_RHASH_SIZE]; + + error = 0; + hbp = xlog_get_bp(1,log->l_mp); + if (!hbp) + return -ENOMEM; + dbp = xlog_get_bp(BTOBB(XLOG_MAX_RECORD_BSIZE),log->l_mp); + if (!dbp) { + xlog_put_bp(hbp); + return -ENOMEM; + } + bzero(rhash, 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; + 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 (bblks > 0) { + if ((error = xlog_bread(log, blk_no+1, bblks, dbp))) + goto bread_err; + 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); + } + } else { + /* + * Perform recovery around the end of the physical log. When the head + * is not on the same cycle number as the tail, we can't do a sequential + * recovery as above. + */ + 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; + 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)); + + /* 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); + + if ((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)) { + error = EFSCORRUPTED; + goto bread_err; + } + + /* 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; + } else { + /* This log record is split across physical end of log */ + split_bblks = 0; + if (blk_no != log->l_logBBsize) { + + /* some data is before physical end of log */ + ASSERT(blk_no <= INT_MAX); + 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; + } + 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); + } + 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; + } + + ASSERT(blk_no >= log->l_logBBsize); + blk_no -= log->l_logBBsize; + + /* read first part of physical log */ + while (blk_no < head_blk) { + if ((error = xlog_bread(log, blk_no, 1, hbp))) + goto bread_err; + 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; + 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); + } + } + +bread_err: + xlog_put_bp(dbp); + xlog_put_bp(hbp); + + return error; +} diff --git a/logprint/Makefile b/logprint/Makefile index 2867d9f4e..05c5a8f1d 100644 --- a/logprint/Makefile +++ b/logprint/Makefile @@ -35,11 +35,10 @@ include $(TOPDIR)/include/builddefs LTCOMMAND = xfs_logprint -CFILES = log_print_trans.c log_print_all.c log_misc.c logprint.c \ - xfs_log_recover.c HFILES = logprint.h -LLDLIBS = $(LIBXFS) $(LIBUUID) -LTDEPENDENCIES = $(LIBXFS) +CFILES = log_print_trans.c log_print_all.c log_misc.c logprint.c +LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) +LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) LLDFLAGS = -static default: $(LTCOMMAND) diff --git a/logprint/log_print_trans.c b/logprint/log_print_trans.c index 02baefccd..625896e2b 100644 --- a/logprint/log_print_trans.c +++ b/logprint/log_print_trans.c @@ -51,7 +51,6 @@ xlog_recover_do_trans(xlog_t *log, return 0; } /* xlog_recover_do_trans */ -static int print_record_header=0; void xfs_log_print_trans(xlog_t *log, @@ -79,68 +78,3 @@ xfs_log_print_trans(xlog_t *log, exit(1); } /* xfs_log_print_trans */ - -static int -header_check_uuid(xfs_mount_t *mp, xlog_rec_header_t *head) -{ - char uu_log[64], uu_sb[64]; - - if (!uuid_compare(mp->m_sb.sb_uuid, head->h_fs_uuid)) return 0; - - uuid_unparse(mp->m_sb.sb_uuid, uu_sb); - uuid_unparse(head->h_fs_uuid, uu_log); - - printf("* ERROR: mismatched uuid in log\n" - "* SB : %s\n* log: %s\n", - uu_sb, uu_log); - - return 1; -} - -int -xlog_header_check_recover(xfs_mount_t *mp, xlog_rec_header_t *head) -{ - if (print_record_header) - printf("\nLOG REC AT LSN cycle %d block %d (0x%x, 0x%x)\n", - CYCLE_LSN(head->h_lsn, ARCH_CONVERT), - BLOCK_LSN(head->h_lsn, ARCH_CONVERT), - CYCLE_LSN(head->h_lsn, ARCH_CONVERT), - BLOCK_LSN(head->h_lsn, ARCH_CONVERT)); - - if (INT_GET(head->h_magicno, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM) { - - printf("* ERROR: bad magic number in log header: 0x%x\n", - INT_GET(head->h_magicno, ARCH_CONVERT)); - - } else if (header_check_uuid(mp, head)) { - - /* failed - fall through */ - - } else if (INT_GET(head->h_fmt, ARCH_CONVERT) != XLOG_FMT) { - - printf("* ERROR: log format incompatible (log=%d, ours=%d)\n", - INT_GET(head->h_fmt, ARCH_CONVERT), XLOG_FMT); - - } else { - /* everything is ok */ - return 0; - } - - /* bail out now or just carry on regardless */ - if (print_exit) - xlog_exit("Bad log"); - - return 0; -} - -int -xlog_header_check_mount(xfs_mount_t *mp, xlog_rec_header_t *head) -{ - if (uuid_is_null(head->h_fs_uuid)) return 0; - if (header_check_uuid(mp, head)) { - /* bail out now or just carry on regardless */ - if (print_exit) - xlog_exit("Bad log"); - } - return 0; -} diff --git a/logprint/logprint.c b/logprint/logprint.c index 18aad49bd..a6a381925 100644 --- a/logprint/logprint.c +++ b/logprint/logprint.c @@ -43,7 +43,6 @@ int print_no_data; int print_no_print; int print_exit = 1; /* -e is now default. specify -c to override */ -libxfs_init_t x; xfs_mount_t mp; void diff --git a/logprint/logprint.h b/logprint/logprint.h index ba2675060..8a750b9de 100644 --- a/logprint/logprint.h +++ b/logprint/logprint.h @@ -29,82 +29,10 @@ * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */ -#ifndef XFS_LOGPRINT_H -#define XFS_LOGPRINT_H +#ifndef LOGPRINT_H +#define LOGPRINT_H -#include - -/* - * define the userlevel xlog_t to be the subset of the kernel's - * xlog_t that we actually need to get our work done, avoiding - * the need to define any exotic kernel types in userland. - */ -typedef struct log { - xfs_lsn_t l_tail_lsn; /* lsn of 1st LR w/ unflush buffers */ - xfs_lsn_t l_last_sync_lsn;/* lsn of last LR on disk */ - xfs_mount_t *l_mp; /* mount point */ - dev_t l_dev; /* dev_t of log */ - xfs_daddr_t l_logBBstart; /* start block of log */ - int l_logsize; /* size of log in bytes */ - int l_logBBsize; /* size of log in 512 byte chunks */ - int l_roundoff; /* round off error of all iclogs */ - int l_curr_cycle; /* Cycle number of log writes */ - int l_prev_cycle; /* Cycle # b4 last block increment */ - int l_curr_block; /* current logical block of log */ - int l_prev_block; /* previous logical block of log */ - int l_iclog_size; /* size of log in bytes */ - int l_iclog_size_log;/* log power size of log */ - int l_iclog_bufs; /* number of iclog buffers */ - int l_grant_reserve_cycle; /* */ - int l_grant_reserve_bytes; /* */ - int l_grant_write_cycle; /* */ - int l_grant_write_bytes; /* */ -} xlog_t; - -#include -#include -#include -#include -#include - - -/* - * macros mapping kernel code to user code - */ -#define STATIC static -#define EFSCORRUPTED 990 -#define XFS_ERROR(e) (e) -#define min(a,b) ((a) < (b) ? (a) : (b)) - -#if (__GNUC__ < 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ <= 95)) -# define xlog_warn(fmt,args...) \ - ( fprintf(stderr,fmt,## args), fputc('\n', stderr) ) -# define cmn_err(sev,fmt,args...) \ - xlog_warn(fmt,## args) -# define xlog_exit(fmt,args...) \ - ( xlog_warn(fmt,## args), exit(1) ) -# define xlog_panic(fmt,args...) \ - xlog_exit(fmt,## args) -#else -# define xlog_warn(...) \ - ( fprintf(stderr,__VA_ARGS__), fputc('\n', stderr) ) -# define cmn_err(sev,...) \ - xlog_warn(__VA_ARGS__) -# define xlog_exit(...) \ - ( xlog_warn(__VA_ARGS__), exit(1) ) -# define xlog_panic(...) \ - xlog_exit(__VA_ARGS__) -#endif - -#define xlog_get_bp(nbblks, mp) libxfs_getbuf(x.logdev, 0, (nbblks)) -#define xlog_put_bp(bp) libxfs_putbuf(bp) -#define xlog_bread(log,blkno,nbblks,bp) \ - (libxfs_readbufr(x.logdev, \ - (log)->l_logBBstart+(blkno), bp, (nbblks), 1), 0) - -#define kmem_zalloc(size, foo) calloc(size,1) -#define kmem_free(ptr, foo) free(ptr) -#define kmem_realloc(ptr, len, old, foo) realloc(ptr, len) +#include /* command line flags */ extern int print_data; @@ -114,59 +42,10 @@ extern int print_quota; extern int print_buffer; extern int print_transactions; extern int print_overwrite; - -extern int print_exit; extern int print_no_data; extern int print_no_print; /* exports */ - extern char *trans_type[]; -/* libxfs parameters */ -extern libxfs_init_t x; - -extern void xfs_log_print_trans(xlog_t *log, - int print_block_start); - -extern void xfs_log_print( xlog_t *log, - int fd, - int print_block_start); - -extern int xlog_find_zeroed(xlog_t *log, xfs_daddr_t *blk_no); -extern int xlog_find_cycle_start(xlog_t *log, xfs_buf_t *bp, - xfs_daddr_t first_blk, xfs_daddr_t *last_blk, uint cycle); -extern int xlog_find_tail(xlog_t *log, xfs_daddr_t *head_blk, - xfs_daddr_t *tail_blk, int readonly); - -extern int xlog_test_footer(xlog_t *log); -extern int xlog_recover(xlog_t *log, int readonly); -extern void xlog_recover_print_data(xfs_caddr_t p, int len); -extern void xlog_recover_print_logitem(xlog_recover_item_t *item); -extern void xlog_recover_print_trans_head(xlog_recover_t *tr); -extern int xlog_print_find_oldest(xlog_t *log, xfs_daddr_t *last_blk); - -extern void print_xlog_op_line(void); -extern void print_xlog_record_line(void); -extern void print_stars(void); - -/* for transactional view */ -extern void xlog_recover_print_trans_head(xlog_recover_t *tr); - -extern void xlog_recover_print_trans( xlog_recover_t *trans, - xlog_recover_item_t *itemq, - int print); - -extern int xlog_do_recovery_pass( xlog_t *log, - xfs_daddr_t head_blk, - xfs_daddr_t tail_blk, - int pass); -extern int xlog_recover_do_trans( xlog_t *log, - xlog_recover_t *trans, - int pass); -extern int xlog_header_check_recover( xfs_mount_t *mp, - xlog_rec_header_t *head); -extern int xlog_header_check_mount( xfs_mount_t *mp, - xlog_rec_header_t *head); - -#endif /* XFS_LOGPRINT_H */ +#endif /* LOGPRINT_H */ diff --git a/man/man8/xfs_repair.8 b/man/man8/xfs_repair.8 index 14a286cba..c671c761c 100644 --- a/man/man8/xfs_repair.8 +++ b/man/man8/xfs_repair.8 @@ -3,9 +3,9 @@ xfs_repair \- repair an XFS filesystem .SH SYNOPSIS .nf -\f3xfs_repair\f1 [ \f3\-n\f1 ] [ \f3\-o\f1 subopt[=value] ] xfs_special +\f3xfs_repair\f1 [ \f3\-nL\f1 ] [ \f3\-o\f1 subopt[=value] ] xfs_special .sp .8v -\f3xfs_repair\f1 \f3\-f\f1 [ \f3\-n\f1 ] [ \f3\-o\f1 subopt[=value] ] ... file +\f3xfs_repair\f1 \f3\-f\f1 [ \f3\-nL\f1 ] [ \f3\-o\f1 subopt[=value] ] ... file .fi .SH DESCRIPTION .I xfs_repair @@ -35,6 +35,14 @@ Specifies that the special device is actually a file (see the This might happen if an image copy of a filesystem has been copied or written into an ordinary file. .TP +.B \-L +Force Log Zeroing. +Forces +.I xfs_repair +to zero the log even if it is dirty (contains metadata changes). +When using this option the filesystem will likely appear to be corrupt, +and can cause the loss of user files and/or data. +.TP .B \-l Specifies the device special file where the filesystems external log resides. @@ -340,6 +348,15 @@ will return a status of 1 if filesystem corruption was detected and .I xfs_repair run without the -n option will always return a status code of 0. .SH BUGS +The filesystem to be checked and repaired must have been +unmounted cleanly using normal system administration procedures +(the +.IR umount (8) +command or system shutdown), not as a result of a crash or system reset. +If the filesystem has not been unmounted cleanly, mount it and unmount +it cleanly before running +.IR xfs_repair . +.PP .I xfs_repair does not do a thorough job on XFS extended attributes. The structure of the attribute fork will be consistent, @@ -358,5 +375,6 @@ are encountered. .SH SEE ALSO dd(1), mkfs.xfs(8), +umount(8), xfs_check(8), xfs(5). diff --git a/repair/Makefile b/repair/Makefile index 6537f299f..b6d4feb71 100644 --- a/repair/Makefile +++ b/repair/Makefile @@ -45,8 +45,8 @@ CFILES = agheader.c attr_repair.c avl.c avl64.c bmap.c dino_chunks.c \ phase2.c phase3.c phase4.c phase5.c phase6.c phase7.c rt.c sb.c \ scan.c versions.c xfs_repair.c -LLDLIBS = $(LIBXFS) $(LIBUUID) -LTDEPENDENCIES = $(LIBXFS) +LLDLIBS = $(LIBXFS) $(LIBXLOG) $(LIBUUID) +LTDEPENDENCIES = $(LIBXFS) $(LIBXLOG) LLDFLAGS = -static default: $(LTCOMMAND) diff --git a/repair/globals.h b/repair/globals.h index 708c69ec5..71548d3c5 100644 --- a/repair/globals.h +++ b/repair/globals.h @@ -121,6 +121,7 @@ EXTERN int fs_fd; /* filesystem fd */ EXTERN int verbose; EXTERN int no_modify; EXTERN int isa_file; +EXTERN int zap_log; EXTERN int dumpcore; /* abort, not exit on fatal errs */ EXTERN int delete_attr_ok; /* can clear attrs w/o clearing files */ EXTERN int force_geo; /* can set geo on low confidence info */ diff --git a/repair/phase2.c b/repair/phase2.c index a906892b1..eda5806c9 100644 --- a/repair/phase2.c +++ b/repair/phase2.c @@ -30,7 +30,7 @@ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */ -#include +#include #include "avl.h" #include "globals.h" #include "agheader.h" @@ -41,16 +41,60 @@ void set_mp(xfs_mount_t *mpp); void scan_ag(xfs_agnumber_t agno); +/* workaround craziness in the xlog routines */ +int xlog_recover_do_trans(xlog_t *log, xlog_recover_t *t, int p) { return 0; } + static void -zero_log(xfs_mount_t *mp, libxfs_init_t *args) +zero_log(xfs_mount_t *mp) { - int logdev = (mp->m_sb.sb_logstart == 0) ? args->logdev : args->ddev; - + int error; + xlog_t log; + xfs_daddr_t head_blk, tail_blk; + dev_t logdev = (mp->m_sb.sb_logstart == 0) ? x.logdev : x.ddev; + + memset(&log, 0, sizeof(log)); + if (!x.logdev) + x.logdev = x.ddev; + x.logBBsize = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); + x.logBBstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart); + + log.l_dev = logdev; + log.l_logsize = BBTOB(x.logBBsize); + log.l_logBBsize = x.logBBsize; + log.l_logBBstart = x.logBBstart; + log.l_mp = mp; + + if ((error = xlog_find_tail(&log, &head_blk, &tail_blk, 0))) { + do_error("xlog_find_tail returned error %d\n", error); + return; + } else { + if (verbose) { + do_warn("zero_log: head block %lld tail block %lld\n", + head_blk, tail_blk); + } + if (head_blk != tail_blk) { + if (zap_log) { + do_warn( +"ALERT: The filesystem has valuable metadata changes in a log which is being\n" +"destroyed because the -L option was used.\n"); + } else { + do_warn( +"ERROR: The filesystem has valuable metadata changes in a log which needs to\n" +"be replayed. Mount the filesystem to replay the log, and unmount it before\n" +"re-running xfs_repair. If you are unable to mount the filesystem, then use\n" +"the -L option to destroy the log and attempt a repair.\n" +"Note that destroying the log may cause corruption -- please attempt a mount\n" +"of the filesystem before doing this.\n"); + exit(2); + } + } + } + libxfs_log_clear(logdev, XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart), (xfs_extlen_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks), - &mp->m_sb.sb_uuid, - XLOG_FMT); + &mp->m_sb.sb_uuid, + XLOG_FMT); } /* @@ -63,7 +107,7 @@ zero_log(xfs_mount_t *mp, libxfs_init_t *args) */ void -phase2(xfs_mount_t *mp, libxfs_init_t *args) +phase2(xfs_mount_t *mp) { xfs_agnumber_t i; xfs_agblock_t b; @@ -75,7 +119,7 @@ phase2(xfs_mount_t *mp, libxfs_init_t *args) /* Check whether this fs has internal or external log */ if (mp->m_sb.sb_logstart == 0) { - if (!args->logname) { + if (!x.logname) { fprintf (stderr, "This filesystem has an external log. " "Specify log device with the -l option.\n"); @@ -83,14 +127,14 @@ phase2(xfs_mount_t *mp, libxfs_init_t *args) } fprintf (stderr, "Phase 2 - using external log on %s\n", - args->logname); + x.logname); } else fprintf (stderr, "Phase 2 - using internal log\n"); /* Zero log if applicable */ if (!no_modify) { do_log(" - zero log...\n"); - zero_log(mp, args); + zero_log(mp); } do_log(" - scan filesystem freespace and inode maps...\n"); diff --git a/repair/xfs_repair.c b/repair/xfs_repair.c index 951e3b930..a93da1be7 100644 --- a/repair/xfs_repair.c +++ b/repair/xfs_repair.c @@ -30,7 +30,7 @@ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */ -#include +#include #include "avl.h" #include "avl64.h" #include "globals.h" @@ -43,7 +43,7 @@ #define rounddown(x, y) (((x)/(y))*(y)) extern void phase1(xfs_mount_t *); -extern void phase2(xfs_mount_t *, libxfs_init_t *); +extern void phase2(xfs_mount_t *); extern void phase3(xfs_mount_t *); extern void phase4(xfs_mount_t *); extern void phase5(xfs_mount_t *); @@ -147,6 +147,7 @@ process_args(int argc, char **argv) verbose = 0; no_modify = 0; isa_file = 0; + zap_log = 0; dumpcore = 0; full_backptrs = 0; delete_attr_ok = 1; @@ -169,7 +170,7 @@ process_args(int argc, char **argv) * XXX have to add suboption processing here * attributes, quotas, nlinks, aligned_inos, sb_fbits */ - while ((c = getopt(argc, argv, "o:fnDvVl:")) != EOF) { + while ((c = getopt(argc, argv, "o:flLnDvV:")) != EOF) { switch (c) { case 'D': dumpcore = 1; @@ -208,6 +209,9 @@ process_args(int argc, char **argv) case 'f': isa_file = 1; break; + case 'L': + zap_log = 1; + break; case 'n': no_modify = 1; break; @@ -401,7 +405,6 @@ calc_mkfs(xfs_mount_t *mp) int main(int argc, char **argv) { - libxfs_init_t args; xfs_mount_t *temp_mp; xfs_mount_t *mp; xfs_sb_t *sb; @@ -414,7 +417,7 @@ main(int argc, char **argv) setbuf(stdout, NULL); process_args(argc, argv); - xfs_init(&args); + xfs_init(&x); /* do phase1 to make sure we have a superblock */ phase1(temp_mp); @@ -427,12 +430,12 @@ main(int argc, char **argv) } /* prepare the mount structure */ - sbp = libxfs_readbuf(args.ddev, XFS_SB_DADDR, 1, 0); + sbp = libxfs_readbuf(x.ddev, XFS_SB_DADDR, 1, 0); memset(&xfs_m, 0, sizeof(xfs_mount_t)); sb = &xfs_m.m_sb; libxfs_xlate_sb(XFS_BUF_PTR(sbp), sb, 1, ARCH_CONVERT, XFS_SB_ALL_BITS); - mp = libxfs_mount(&xfs_m, sb, args.ddev, args.logdev, args.rtdev, 0); + mp = libxfs_mount(&xfs_m, sb, x.ddev, x.logdev, x.rtdev, 0); if (!mp) { fprintf(stderr, "%s: cannot repair this filesystem. Sorry.\n", @@ -468,7 +471,7 @@ main(int argc, char **argv) /* make sure the per-ag freespace maps are ok so we can mount the fs */ - phase2(mp, &args); + phase2(mp); phase3(mp); @@ -570,11 +573,11 @@ main(int argc, char **argv) libxfs_writebuf(sbp, 0); libxfs_umount(mp); - if (args.rtdev) - libxfs_device_close(args.rtdev); - if (args.logdev) - libxfs_device_close(args.logdev); - libxfs_device_close(args.ddev); + if (x.rtdev) + libxfs_device_close(x.rtdev); + if (x.logdev && x.logdev != x.ddev) + libxfs_device_close(x.logdev); + libxfs_device_close(x.ddev); do_log("done\n");