1 /* $NetBSD: lfs_rfw.c,v 1.12 2009/02/22 20:28:07 ad Exp $ */
4 * Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Konrad E. Schroder <perseant@hhhh.org>.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: lfs_rfw.c,v 1.12 2009/02/22 20:28:07 ad Exp $");
35 #if defined(_KERNEL_OPT)
36 #include "opt_quota.h"
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/namei.h>
43 #include <sys/kernel.h>
44 #include <sys/vnode.h>
45 #include <sys/mount.h>
46 #include <sys/kthread.h>
48 #include <sys/device.h>
51 #include <sys/disklabel.h>
52 #include <sys/ioctl.h>
53 #include <sys/errno.h>
54 #include <sys/malloc.h>
56 #include <sys/socket.h>
57 #include <sys/syslog.h>
58 #include <uvm/uvm_extern.h>
59 #include <sys/sysctl.h>
61 #include <sys/kauth.h>
63 #include <miscfs/specfs/specdev.h>
65 #include <ufs/ufs/quota.h>
66 #include <ufs/ufs/inode.h>
67 #include <ufs/ufs/ufsmount.h>
68 #include <ufs/ufs/ufs_extern.h>
71 #include <uvm/uvm_stat.h>
72 #include <uvm/uvm_pager.h>
73 #include <uvm/uvm_pdaemon.h>
75 #include <ufs/lfs/lfs.h>
76 #include <ufs/lfs/lfs_extern.h>
78 #include <miscfs/genfs/genfs.h>
79 #include <miscfs/genfs/genfs_node.h>
84 static daddr_t
check_segsum(struct lfs
*, daddr_t
, u_int64_t
,
85 kauth_cred_t
, int, int *, struct lwp
*);
87 extern int lfs_do_rfw
;
90 * Allocate a particular inode with a particular version number, freeing
91 * any previous versions of this inode that may have gone before.
92 * Used by the roll-forward code.
94 * XXX this function does not have appropriate locking to be used on a live fs;
95 * XXX but something similar could probably be used for an "undelete" call.
97 * Called with the Ifile inode locked.
100 lfs_rf_valloc(struct lfs
*fs
, ino_t ino
, int vers
, struct lwp
*l
,
104 struct buf
*bp
, *cbp
;
111 ASSERT_SEGLOCK(fs
); /* XXX it doesn't, really */
114 * First, just try a vget. If the version number is the one we want,
115 * we don't have to do anything else. If the version number is wrong,
116 * take appropriate action.
118 error
= VFS_VGET(fs
->lfs_ivnode
->v_mount
, ino
, &vp
);
120 DLOG((DLOG_RF
, "lfs_rf_valloc[1]: ino %d vp %p\n", ino
, vp
));
124 if (ip
->i_gen
== vers
)
126 else if (ip
->i_gen
< vers
) {
127 lfs_truncate(vp
, (off_t
)0, 0, NOCRED
);
128 ip
->i_gen
= ip
->i_ffs1_gen
= vers
;
129 LFS_SET_UINO(ip
, IN_CHANGE
| IN_UPDATE
);
132 DLOG((DLOG_RF
, "ino %d: sought version %d, got %d\n",
133 ino
, vers
, ip
->i_ffs1_gen
));
141 * The inode is not in use. Find it on the free list.
143 /* If the Ifile is too short to contain this inum, extend it */
144 while (VTOI(fs
->lfs_ivnode
)->i_size
<= (ino
/
145 fs
->lfs_ifpb
+ fs
->lfs_cleansz
+ fs
->lfs_segtabsz
)
147 lfs_extend_ifile(fs
, NOCRED
);
150 LFS_IENTRY(ifp
, fs
, ino
, bp
);
151 oldnext
= ifp
->if_nextfree
;
152 ifp
->if_version
= vers
;
155 LFS_GET_HEADFREE(fs
, cip
, cbp
, &ino
);
157 LFS_PUT_HEADFREE(fs
, cip
, cbp
, oldnext
);
161 LFS_IENTRY(ifp
, fs
, tino
, bp
);
162 if (ifp
->if_nextfree
== ino
||
163 ifp
->if_nextfree
== LFS_UNUSED_INUM
)
165 tino
= ifp
->if_nextfree
;
168 if (ifp
->if_nextfree
== LFS_UNUSED_INUM
) {
172 ifp
->if_nextfree
= oldnext
;
176 error
= lfs_ialloc(fs
, fs
->lfs_ivnode
, ino
, vers
, &vp
);
179 * Make it VREG so we can put blocks on it. We will change
180 * this later if it turns out to be some other kind of file.
183 ip
->i_mode
= ip
->i_ffs1_mode
= IFREG
;
184 ip
->i_nlink
= ip
->i_ffs1_nlink
= 1;
185 ufs_vinit(vp
->v_mount
, lfs_specop_p
, lfs_fifoop_p
, &vp
);
188 DLOG((DLOG_RF
, "lfs_rf_valloc: ino %d vp %p\n", ino
, vp
));
190 /* The dirop-nature of this vnode is past */
191 lfs_unmark_vnode(vp
);
192 (void)lfs_vunref(vp
);
193 vp
->v_uflag
&= ~VU_DIROP
;
194 mutex_enter(&lfs_lock
);
197 TAILQ_REMOVE(&fs
->lfs_dchainhd
, ip
, i_lfs_dchain
);
198 wakeup(&lfs_dirvcount
);
199 wakeup(&fs
->lfs_dirvcount
);
200 mutex_exit(&lfs_lock
);
207 * Load the appropriate indirect block, and change the appropriate pointer.
208 * Mark the block dirty. Do segment and avail accounting.
211 update_meta(struct lfs
*fs
, ino_t ino
, int vers
, daddr_t lbn
,
212 daddr_t ndaddr
, size_t size
, struct lwp
*l
)
219 struct indir a
[NIADDR
];
226 KASSERT(lbn
>= 0); /* no indirect blocks */
228 if ((error
= lfs_rf_valloc(fs
, ino
, vers
, l
, &vp
)) != 0) {
229 DLOG((DLOG_RF
, "update_meta: ino %d: lfs_rf_valloc"
230 " returned %d\n", ino
, error
));
234 if ((error
= lfs_balloc(vp
, (lbn
<< fs
->lfs_bshift
), size
,
235 NOCRED
, 0, &bp
)) != 0) {
239 /* No need to write, the block is already on disk */
240 if (bp
->b_oflags
& BO_DELWRI
) {
242 fs
->lfs_avail
+= btofsb(fs
, bp
->b_bcount
);
244 brelse(bp
, BC_INVAL
);
247 * Extend the file, if it is not large enough already.
248 * XXX this is not exactly right, we don't know how much of the
249 * XXX last block is actually used. We hope that an inode will
250 * XXX appear later to give the correct size.
253 if (ip
->i_size
<= (lbn
<< fs
->lfs_bshift
)) {
257 newsize
= ip
->i_ffs1_size
= (lbn
<< fs
->lfs_bshift
) +
258 (size
- fs
->lfs_fsize
) + 1;
260 newsize
= ip
->i_ffs1_size
= (lbn
<< fs
->lfs_bshift
) + 1;
262 if (ip
->i_size
< newsize
) {
263 ip
->i_size
= newsize
;
265 * tell vm our new size for the case the inode won't
268 uvm_vnp_setsize(vp
, newsize
);
272 lfs_update_single(fs
, NULL
, vp
, lbn
, ndaddr
, size
);
274 LFS_SEGENTRY(sup
, fs
, dtosn(fs
, ndaddr
), bp
);
275 sup
->su_nbytes
+= size
;
276 LFS_WRITESEGENTRY(sup
, fs
, dtosn(fs
, ndaddr
), bp
);
278 /* differences here should be due to UNWRITTEN indirect blocks. */
279 KASSERT((lblkno(fs
, ip
->i_size
) > NDADDR
&&
280 ip
->i_lfs_effnblks
== ip
->i_ffs1_blocks
) ||
281 ip
->i_lfs_effnblks
>= ip
->i_ffs1_blocks
);
284 /* Now look again to make sure it worked */
285 ufs_bmaparray(vp
, lbn
, &odaddr
, &a
[0], &num
, NULL
, NULL
);
286 for (i
= num
; i
> 0; i
--) {
288 panic("update_meta: absent %d lv indirect block", i
);
290 if (dbtofsb(fs
, odaddr
) != ndaddr
)
291 DLOG((DLOG_RF
, "update_meta: failed setting ino %d lbn %"
292 PRId64
" to %" PRId64
"\n", ino
, lbn
, ndaddr
));
299 update_inoblk(struct lfs
*fs
, daddr_t offset
, kauth_cred_t cred
,
302 struct vnode
*devvp
, *vp
;
304 struct ufs1_dinode
*dip
;
305 struct buf
*dbp
, *ibp
;
311 devvp
= VTOI(fs
->lfs_ivnode
)->i_devvp
;
314 * Get the inode, update times and perms.
315 * DO NOT update disk blocks, we do that separately.
317 error
= bread(devvp
, fsbtodb(fs
, offset
), fs
->lfs_ibsize
,
320 DLOG((DLOG_RF
, "update_inoblk: bread returned %d\n", error
));
323 dip
= ((struct ufs1_dinode
*)(dbp
->b_data
)) + INOPB(fs
);
324 while (--dip
>= (struct ufs1_dinode
*)dbp
->b_data
) {
325 if (dip
->di_inumber
> LFS_IFILE_INUM
) {
326 error
= lfs_rf_valloc(fs
, dip
->di_inumber
, dip
->di_gen
,
329 DLOG((DLOG_RF
, "update_inoblk: lfs_rf_valloc"
330 " returned %d\n", error
));
334 if (dip
->di_size
!= ip
->i_size
)
335 lfs_truncate(vp
, dip
->di_size
, 0, NOCRED
);
336 /* Get mode, link count, size, and times */
337 memcpy(ip
->i_din
.ffs1_din
, dip
,
338 offsetof(struct ufs1_dinode
, di_db
[0]));
340 /* Then the rest, except di_blocks */
341 ip
->i_flags
= ip
->i_ffs1_flags
= dip
->di_flags
;
342 ip
->i_gen
= ip
->i_ffs1_gen
= dip
->di_gen
;
343 ip
->i_uid
= ip
->i_ffs1_uid
= dip
->di_uid
;
344 ip
->i_gid
= ip
->i_ffs1_gid
= dip
->di_gid
;
346 ip
->i_mode
= ip
->i_ffs1_mode
;
347 ip
->i_nlink
= ip
->i_ffs1_nlink
;
348 ip
->i_size
= ip
->i_ffs1_size
;
350 LFS_SET_UINO(ip
, IN_CHANGE
| IN_UPDATE
);
352 /* Re-initialize to get type right */
353 ufs_vinit(vp
->v_mount
, lfs_specop_p
, lfs_fifoop_p
,
357 /* Record change in location */
358 LFS_IENTRY(ifp
, fs
, dip
->di_inumber
, ibp
);
359 daddr
= ifp
->if_daddr
;
360 ifp
->if_daddr
= dbtofsb(fs
, dbp
->b_blkno
);
361 error
= LFS_BWRITE_LOG(ibp
); /* Ifile */
362 /* And do segment accounting */
363 if (dtosn(fs
, daddr
) != dtosn(fs
, dbtofsb(fs
, dbp
->b_blkno
))) {
365 LFS_SEGENTRY(sup
, fs
, dtosn(fs
, daddr
),
367 sup
->su_nbytes
-= sizeof (struct ufs1_dinode
);
368 LFS_WRITESEGENTRY(sup
, fs
,
372 LFS_SEGENTRY(sup
, fs
, dtosn(fs
, dbtofsb(fs
, dbp
->b_blkno
)),
374 sup
->su_nbytes
+= sizeof (struct ufs1_dinode
);
375 LFS_WRITESEGENTRY(sup
, fs
,
376 dtosn(fs
, dbtofsb(fs
, dbp
->b_blkno
)),
386 #define CHECK_CKSUM 0x0001 /* Check the checksum to make sure it's valid */
387 #define CHECK_UPDATE 0x0002 /* Update Ifile for new data blocks / inodes */
390 check_segsum(struct lfs
*fs
, daddr_t offset
, u_int64_t nextserial
,
391 kauth_cred_t cred
, int flags
, int *pseg_flags
, struct lwp
*l
)
394 struct buf
*bp
, *dbp
;
395 int error
, nblocks
= 0, ninos
, i
, j
; /* XXX: gcc */
397 u_long
*dp
= NULL
, *datap
= NULL
; /* XXX u_int32_t */
399 int32_t *iaddr
; /* XXX ondisk32 */
404 devvp
= VTOI(fs
->lfs_ivnode
)->i_devvp
;
406 * If the segment has a superblock and we're at the top
407 * of the segment, skip the superblock.
409 if (sntod(fs
, dtosn(fs
, offset
)) == offset
) {
410 LFS_SEGENTRY(sup
, fs
, dtosn(fs
, offset
), bp
);
411 if (sup
->su_flags
& SEGUSE_SUPERBLOCK
)
412 offset
+= btofsb(fs
, LFS_SBPAD
);
416 /* Read in the segment summary */
417 error
= bread(devvp
, fsbtodb(fs
, offset
), fs
->lfs_sumsize
,
422 /* Check summary checksum */
423 ssp
= (SEGSUM
*)bp
->b_data
;
424 if (flags
& CHECK_CKSUM
) {
425 if (ssp
->ss_sumsum
!= cksum(&ssp
->ss_datasum
,
427 sizeof(ssp
->ss_sumsum
))) {
428 DLOG((DLOG_RF
, "Sumsum error at 0x%" PRIx64
"\n", offset
));
432 if (ssp
->ss_nfinfo
== 0 && ssp
->ss_ninos
== 0) {
433 DLOG((DLOG_RF
, "Empty pseg at 0x%" PRIx64
"\n", offset
));
437 if (ssp
->ss_create
< fs
->lfs_tstamp
) {
438 DLOG((DLOG_RF
, "Old data at 0x%" PRIx64
"\n", offset
));
443 if (fs
->lfs_version
> 1) {
444 if (ssp
->ss_serial
!= nextserial
) {
445 DLOG((DLOG_RF
, "Unexpected serial number at 0x%" PRIx64
450 if (ssp
->ss_ident
!= fs
->lfs_ident
) {
451 DLOG((DLOG_RF
, "Incorrect fsid (0x%x vs 0x%x) at 0x%"
452 PRIx64
"\n", ssp
->ss_ident
, fs
->lfs_ident
, offset
));
458 *pseg_flags
= ssp
->ss_flags
;
460 offset
+= btofsb(fs
, fs
->lfs_sumsize
);
462 ninos
= howmany(ssp
->ss_ninos
, INOPB(fs
));
464 iaddr
= (int32_t *)((char*)bp
->b_data
+ fs
->lfs_sumsize
- sizeof(int32_t));
465 if (flags
& CHECK_CKSUM
) {
468 fip
= (FINFO
*)((char*)bp
->b_data
+ SEGSUM_SIZE(fs
));
469 for (i
= 0; i
< ssp
->ss_nfinfo
; ++i
) {
470 nblocks
+= fip
->fi_nblocks
;
471 if (fip
->fi_nblocks
<= 0)
474 fip
= (FINFO
*)(((char *)fip
) + FINFOSIZE
+
475 (fip
->fi_nblocks
* sizeof(int32_t)));
478 /* Create the sum array */
479 datap
= dp
= (u_long
*)malloc(nblocks
* sizeof(u_long
),
480 M_SEGMENT
, M_WAITOK
);
483 /* Handle individual blocks */
484 fip
= (FINFO
*)((char*)bp
->b_data
+ SEGSUM_SIZE(fs
));
485 for (i
= 0; i
< ssp
->ss_nfinfo
|| ninos
; ++i
) {
487 if (ninos
&& *iaddr
== offset
) {
488 if (flags
& CHECK_CKSUM
) {
489 /* Read in the head and add to the buffer */
490 error
= bread(devvp
, fsbtodb(fs
, offset
), fs
->lfs_bsize
,
496 (*dp
++) = ((u_long
*)(dbp
->b_data
))[0];
499 if (flags
& CHECK_UPDATE
) {
500 if ((error
= update_inoblk(fs
, offset
, cred
, l
))
506 offset
+= btofsb(fs
, fs
->lfs_ibsize
);
509 --i
; /* compensate */
512 size
= fs
->lfs_bsize
;
513 for (j
= 0; j
< fip
->fi_nblocks
; ++j
) {
514 if (j
== fip
->fi_nblocks
- 1)
515 size
= fip
->fi_lastlength
;
516 if (flags
& CHECK_CKSUM
) {
517 error
= bread(devvp
, fsbtodb(fs
, offset
), size
,
523 (*dp
++) = ((u_long
*)(dbp
->b_data
))[0];
526 /* Account for and update any direct blocks */
527 if ((flags
& CHECK_UPDATE
) &&
528 fip
->fi_ino
> LFS_IFILE_INUM
&&
529 fip
->fi_blocks
[j
] >= 0) {
530 update_meta(fs
, fip
->fi_ino
, fip
->fi_version
,
531 fip
->fi_blocks
[j
], offset
, size
, l
);
533 offset
+= btofsb(fs
, size
);
536 fip
= (FINFO
*)(((char *)fip
) + FINFOSIZE
537 + fip
->fi_nblocks
* sizeof(int32_t));
539 /* Checksum the array, compare */
540 if ((flags
& CHECK_CKSUM
) &&
541 ssp
->ss_datasum
!= cksum(datap
, nblocks
* sizeof(u_long
)))
543 DLOG((DLOG_RF
, "Datasum error at 0x%" PRIx64
544 " (wanted %x got %x)\n",
545 offset
, ssp
->ss_datasum
, cksum(datap
, nblocks
*
551 /* If we're at the end of the segment, move to the next */
552 if (dtosn(fs
, offset
+ btofsb(fs
, fs
->lfs_sumsize
+ fs
->lfs_bsize
)) !=
554 if (dtosn(fs
, offset
) == dtosn(fs
, ssp
->ss_next
)) {
558 offset
= ssp
->ss_next
;
559 DLOG((DLOG_RF
, "LFS roll forward: moving to offset 0x%" PRIx64
560 " -> segment %d\n", offset
, dtosn(fs
,offset
)));
563 if (flags
& CHECK_UPDATE
) {
564 fs
->lfs_avail
-= (offset
- oldoffset
);
565 /* Don't clog the buffer queue */
566 mutex_enter(&lfs_lock
);
567 if (locked_queue_count
> LFS_MAX_BUFS
||
568 locked_queue_bytes
> LFS_MAX_BYTES
) {
569 lfs_flush(fs
, SEGM_CKP
, 0);
571 mutex_exit(&lfs_lock
);
575 if (flags
& CHECK_CKSUM
)
576 free(datap
, M_SEGMENT
);
580 /* XXX should we update the serial number even for bad psegs? */
581 if ((flags
& CHECK_UPDATE
) && offset
> 0 && fs
->lfs_version
> 1)
582 fs
->lfs_serial
= nextserial
;
587 lfs_roll_forward(struct lfs
*fs
, struct mount
*mp
, struct lwp
*l
)
590 daddr_t offset
, oldoffset
, lastgoodpseg
;
591 int sn
, curseg
, do_rollforward
;
597 p
= l
? l
->l_proc
: NULL
;
598 cred
= p
? p
->p_cred
: NOCRED
;
603 * We don't roll forward for v1 filesystems, because
604 * of the danger that the clock was turned back between the last
605 * checkpoint and crash. This would roll forward garbage.
607 * v2 filesystems don't have this problem because they use a
608 * monotonically increasing serial number instead of a timestamp.
610 do_rollforward
= (!(fs
->lfs_pflags
& LFS_PF_CLEAN
) &&
611 lfs_do_rfw
&& fs
->lfs_version
> 1 && p
!= NULL
);
612 if (do_rollforward
) {
613 u_int64_t nextserial
;
615 * Phase I: Find the address of the last good partial
616 * segment that was written after the checkpoint. Mark
617 * the segments in question dirty, so they won't be
620 lastgoodpseg
= oldoffset
= offset
= fs
->lfs_offset
;
622 DLOG((DLOG_RF
, "LFS roll forward phase 1: start at offset 0x%"
623 PRIx64
"\n", offset
));
624 LFS_SEGENTRY(sup
, fs
, dtosn(fs
, offset
), bp
);
625 if (!(sup
->su_flags
& SEGUSE_DIRTY
))
627 sup
->su_flags
|= SEGUSE_DIRTY
;
628 LFS_WRITESEGENTRY(sup
, fs
, dtosn(fs
, offset
), bp
);
629 nextserial
= fs
->lfs_serial
+ 1;
630 while ((offset
= check_segsum(fs
, offset
, nextserial
,
631 cred
, CHECK_CKSUM
, &flags
, l
)) > 0) {
633 if (sntod(fs
, oldoffset
) != sntod(fs
, offset
)) {
634 LFS_SEGENTRY(sup
, fs
, dtosn(fs
, oldoffset
),
636 if (!(sup
->su_flags
& SEGUSE_DIRTY
))
638 sup
->su_flags
|= SEGUSE_DIRTY
;
639 LFS_WRITESEGENTRY(sup
, fs
, dtosn(fs
, oldoffset
),
643 DLOG((DLOG_RF
, "LFS roll forward phase 1: offset=0x%"
644 PRIx64
"\n", offset
));
645 if (flags
& SS_DIROP
) {
646 DLOG((DLOG_RF
, "lfs_mountfs: dirops at 0x%"
647 PRIx64
"\n", oldoffset
));
648 if (!(flags
& SS_CONT
)) {
649 DLOG((DLOG_RF
, "lfs_mountfs: dirops end "
650 "at 0x%" PRIx64
"\n", oldoffset
));
653 if (!(flags
& SS_CONT
))
654 lastgoodpseg
= offset
;
657 if (flags
& SS_CONT
) {
658 DLOG((DLOG_RF
, "LFS roll forward: warning: incomplete "
659 "dirops discarded\n"));
661 DLOG((DLOG_RF
, "LFS roll forward phase 1: completed: "
662 "lastgoodpseg=0x%" PRIx64
"\n", lastgoodpseg
));
663 oldoffset
= fs
->lfs_offset
;
664 if (fs
->lfs_offset
!= lastgoodpseg
) {
665 /* Don't overwrite what we're trying to preserve */
666 offset
= fs
->lfs_offset
;
667 fs
->lfs_offset
= lastgoodpseg
;
668 fs
->lfs_curseg
= sntod(fs
, dtosn(fs
, fs
->lfs_offset
));
669 for (sn
= curseg
= dtosn(fs
, fs
->lfs_curseg
);;) {
670 sn
= (sn
+ 1) % fs
->lfs_nseg
;
672 panic("lfs_mountfs: no clean segments");
673 LFS_SEGENTRY(sup
, fs
, sn
, bp
);
674 dirty
= (sup
->su_flags
& SEGUSE_DIRTY
);
679 fs
->lfs_nextseg
= sntod(fs
, sn
);
682 * Phase II: Roll forward from the first superblock.
684 while (offset
!= lastgoodpseg
) {
685 DLOG((DLOG_RF
, "LFS roll forward phase 2: 0x%"
686 PRIx64
"\n", offset
));
687 offset
= check_segsum(fs
, offset
,
688 fs
->lfs_serial
+ 1, cred
, CHECK_UPDATE
,
693 * Finish: flush our changes to disk.
695 lfs_segwrite(mp
, SEGM_CKP
| SEGM_SYNC
);
696 DLOG((DLOG_RF
, "lfs_mountfs: roll forward ",
697 "recovered %lld blocks\n",
698 (long long)(lastgoodpseg
- oldoffset
)));
700 DLOG((DLOG_RF
, "LFS roll forward complete\n"));