1113db2ddSJeff Roberson /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31de7b4b8SPedro F. Giffuni * 4113db2ddSJeff Roberson * Copyright 2009, 2010 Jeffrey W. Roberson <jeff@FreeBSD.org> 5113db2ddSJeff Roberson * All rights reserved. 6113db2ddSJeff Roberson * 7113db2ddSJeff Roberson * Redistribution and use in source and binary forms, with or without 8113db2ddSJeff Roberson * modification, are permitted provided that the following conditions 9113db2ddSJeff Roberson * are met: 10113db2ddSJeff Roberson * 1. Redistributions of source code must retain the above copyright 11113db2ddSJeff Roberson * notice, this list of conditions and the following disclaimer. 12113db2ddSJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 13113db2ddSJeff Roberson * notice, this list of conditions and the following disclaimer in the 14113db2ddSJeff Roberson * documentation and/or other materials provided with the distribution. 15113db2ddSJeff Roberson * 16113db2ddSJeff Roberson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17113db2ddSJeff Roberson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18113db2ddSJeff Roberson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19113db2ddSJeff Roberson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20113db2ddSJeff Roberson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21113db2ddSJeff Roberson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22113db2ddSJeff Roberson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23113db2ddSJeff Roberson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24113db2ddSJeff Roberson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25113db2ddSJeff Roberson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26113db2ddSJeff Roberson * SUCH DAMAGE. 27113db2ddSJeff Roberson */ 28113db2ddSJeff Roberson 29113db2ddSJeff Roberson #include <sys/cdefs.h> 30113db2ddSJeff Roberson __FBSDID("$FreeBSD$"); 31113db2ddSJeff Roberson 32113db2ddSJeff Roberson #include <sys/param.h> 330947d19aSKonstantin Belousov #include <sys/disk.h> 34113db2ddSJeff Roberson #include <sys/disklabel.h> 35113db2ddSJeff Roberson #include <sys/mount.h> 36113db2ddSJeff Roberson #include <sys/stat.h> 37113db2ddSJeff Roberson 38*d485c77fSKonstantin Belousov #include <ufs/ufs/extattr.h> 39*d485c77fSKonstantin Belousov #include <ufs/ufs/quota.h> 40113db2ddSJeff Roberson #include <ufs/ufs/ufsmount.h> 41113db2ddSJeff Roberson #include <ufs/ufs/dinode.h> 42113db2ddSJeff Roberson #include <ufs/ufs/dir.h> 43113db2ddSJeff Roberson #include <ufs/ffs/fs.h> 44113db2ddSJeff Roberson 457649cb00SKirk McKusick #include <assert.h> 467649cb00SKirk McKusick #include <err.h> 47edad6026SXin LI #include <setjmp.h> 48edad6026SXin LI #include <stdarg.h> 49113db2ddSJeff Roberson #include <stdio.h> 50113db2ddSJeff Roberson #include <stdlib.h> 51113db2ddSJeff Roberson #include <stdint.h> 52113db2ddSJeff Roberson #include <libufs.h> 53113db2ddSJeff Roberson #include <string.h> 54113db2ddSJeff Roberson #include <strings.h> 55edad6026SXin LI #include <sysexits.h> 567649cb00SKirk McKusick #include <time.h> 57113db2ddSJeff Roberson 58113db2ddSJeff Roberson #include "fsck.h" 59113db2ddSJeff Roberson 60113db2ddSJeff Roberson #define DOTDOT_OFFSET DIRECTSIZ(1) 61113db2ddSJeff Roberson 62113db2ddSJeff Roberson struct suj_seg { 63113db2ddSJeff Roberson TAILQ_ENTRY(suj_seg) ss_next; 64113db2ddSJeff Roberson struct jsegrec ss_rec; 65113db2ddSJeff Roberson uint8_t *ss_blk; 66113db2ddSJeff Roberson }; 67113db2ddSJeff Roberson 68113db2ddSJeff Roberson struct suj_rec { 69113db2ddSJeff Roberson TAILQ_ENTRY(suj_rec) sr_next; 70113db2ddSJeff Roberson union jrec *sr_rec; 71113db2ddSJeff Roberson }; 72113db2ddSJeff Roberson TAILQ_HEAD(srechd, suj_rec); 73113db2ddSJeff Roberson 74113db2ddSJeff Roberson struct suj_ino { 75113db2ddSJeff Roberson LIST_ENTRY(suj_ino) si_next; 76113db2ddSJeff Roberson struct srechd si_recs; 77113db2ddSJeff Roberson struct srechd si_newrecs; 78113db2ddSJeff Roberson struct srechd si_movs; 79113db2ddSJeff Roberson struct jtrncrec *si_trunc; 80113db2ddSJeff Roberson ino_t si_ino; 81113db2ddSJeff Roberson char si_skipparent; 82113db2ddSJeff Roberson char si_hasrecs; 83113db2ddSJeff Roberson char si_blkadj; 84113db2ddSJeff Roberson char si_linkadj; 85113db2ddSJeff Roberson int si_mode; 86113db2ddSJeff Roberson nlink_t si_nlinkadj; 87113db2ddSJeff Roberson nlink_t si_nlink; 88113db2ddSJeff Roberson nlink_t si_dotlinks; 89113db2ddSJeff Roberson }; 90113db2ddSJeff Roberson LIST_HEAD(inohd, suj_ino); 91113db2ddSJeff Roberson 92113db2ddSJeff Roberson struct suj_blk { 93113db2ddSJeff Roberson LIST_ENTRY(suj_blk) sb_next; 94113db2ddSJeff Roberson struct srechd sb_recs; 95113db2ddSJeff Roberson ufs2_daddr_t sb_blk; 96113db2ddSJeff Roberson }; 97113db2ddSJeff Roberson LIST_HEAD(blkhd, suj_blk); 98113db2ddSJeff Roberson 99113db2ddSJeff Roberson struct suj_cg { 100113db2ddSJeff Roberson LIST_ENTRY(suj_cg) sc_next; 1015cc52631SKirk McKusick struct blkhd sc_blkhash[HASHSIZE]; 1025cc52631SKirk McKusick struct inohd sc_inohash[HASHSIZE]; 103113db2ddSJeff Roberson struct ino_blk *sc_lastiblk; 104113db2ddSJeff Roberson struct suj_ino *sc_lastino; 105113db2ddSJeff Roberson struct suj_blk *sc_lastblk; 1065cc52631SKirk McKusick struct bufarea *sc_cgbp; 107113db2ddSJeff Roberson struct cg *sc_cgp; 108113db2ddSJeff Roberson int sc_cgx; 109113db2ddSJeff Roberson }; 110113db2ddSJeff Roberson 1115cc52631SKirk McKusick static LIST_HEAD(cghd, suj_cg) cghash[HASHSIZE]; 1127703a6ffSScott Long static struct suj_cg *lastcg; 113113db2ddSJeff Roberson 1147703a6ffSScott Long static TAILQ_HEAD(seghd, suj_seg) allsegs; 1157703a6ffSScott Long static uint64_t oldseq; 116113db2ddSJeff Roberson static struct fs *fs = NULL; 1177703a6ffSScott Long static ino_t sujino; 118113db2ddSJeff Roberson 119113db2ddSJeff Roberson /* 120113db2ddSJeff Roberson * Summary statistics. 121113db2ddSJeff Roberson */ 1227703a6ffSScott Long static uint64_t freefrags; 1237703a6ffSScott Long static uint64_t freeblocks; 1247703a6ffSScott Long static uint64_t freeinos; 1257703a6ffSScott Long static uint64_t freedir; 1267703a6ffSScott Long static uint64_t jbytes; 1277703a6ffSScott Long static uint64_t jrecs; 128113db2ddSJeff Roberson 129edad6026SXin LI static jmp_buf jmpbuf; 130edad6026SXin LI 131113db2ddSJeff Roberson typedef void (*ino_visitor)(ino_t, ufs_lbn_t, ufs2_daddr_t, int); 132edad6026SXin LI static void err_suj(const char *, ...) __dead2; 133113db2ddSJeff Roberson static void ino_trunc(ino_t, off_t); 134113db2ddSJeff Roberson static void ino_decr(ino_t); 135113db2ddSJeff Roberson static void ino_adjust(struct suj_ino *); 136113db2ddSJeff Roberson static void ino_build(struct suj_ino *); 137113db2ddSJeff Roberson static int blk_isfree(ufs2_daddr_t); 1387703a6ffSScott Long static void initsuj(void); 139113db2ddSJeff Roberson 140113db2ddSJeff Roberson static void * 141113db2ddSJeff Roberson errmalloc(size_t n) 142113db2ddSJeff Roberson { 143113db2ddSJeff Roberson void *a; 144113db2ddSJeff Roberson 14581fbded2SKirk McKusick a = Malloc(n); 146113db2ddSJeff Roberson if (a == NULL) 147edad6026SXin LI err(EX_OSERR, "malloc(%zu)", n); 148113db2ddSJeff Roberson return (a); 149113db2ddSJeff Roberson } 150113db2ddSJeff Roberson 151113db2ddSJeff Roberson /* 152edad6026SXin LI * When hit a fatal error in journalling check, print out 153edad6026SXin LI * the error and then offer to fallback to normal fsck. 154edad6026SXin LI */ 155edad6026SXin LI static void 156edad6026SXin LI err_suj(const char * restrict fmt, ...) 157edad6026SXin LI { 158edad6026SXin LI va_list ap; 159edad6026SXin LI 160edad6026SXin LI if (preen) 161edad6026SXin LI (void)fprintf(stdout, "%s: ", cdevname); 162edad6026SXin LI 163edad6026SXin LI va_start(ap, fmt); 164edad6026SXin LI (void)vfprintf(stdout, fmt, ap); 165edad6026SXin LI va_end(ap); 166edad6026SXin LI 167edad6026SXin LI longjmp(jmpbuf, -1); 168edad6026SXin LI } 169edad6026SXin LI 170edad6026SXin LI /* 171113db2ddSJeff Roberson * Lookup a cg by number in the hash so we can keep track of which cgs 172113db2ddSJeff Roberson * need stats rebuilt. 173113db2ddSJeff Roberson */ 174113db2ddSJeff Roberson static struct suj_cg * 175113db2ddSJeff Roberson cg_lookup(int cgx) 176113db2ddSJeff Roberson { 177113db2ddSJeff Roberson struct cghd *hd; 178113db2ddSJeff Roberson struct suj_cg *sc; 1795cc52631SKirk McKusick struct bufarea *cgbp; 180113db2ddSJeff Roberson 181edad6026SXin LI if (cgx < 0 || cgx >= fs->fs_ncg) 182edad6026SXin LI err_suj("Bad cg number %d\n", cgx); 183113db2ddSJeff Roberson if (lastcg && lastcg->sc_cgx == cgx) 184113db2ddSJeff Roberson return (lastcg); 1855cc52631SKirk McKusick cgbp = cglookup(cgx); 1865cc52631SKirk McKusick if (!check_cgmagic(cgx, cgbp, 0)) 1875cc52631SKirk McKusick err_suj("UNABLE TO REBUILD CYLINDER GROUP %d", cgx); 1885cc52631SKirk McKusick hd = &cghash[HASH(cgx)]; 189113db2ddSJeff Roberson LIST_FOREACH(sc, hd, sc_next) 190113db2ddSJeff Roberson if (sc->sc_cgx == cgx) { 1915cc52631SKirk McKusick sc->sc_cgbp = cgbp; 1925cc52631SKirk McKusick sc->sc_cgp = sc->sc_cgbp->b_un.b_cg; 193113db2ddSJeff Roberson lastcg = sc; 194113db2ddSJeff Roberson return (sc); 195113db2ddSJeff Roberson } 196113db2ddSJeff Roberson sc = errmalloc(sizeof(*sc)); 197113db2ddSJeff Roberson bzero(sc, sizeof(*sc)); 1985cc52631SKirk McKusick sc->sc_cgbp = cgbp; 1995cc52631SKirk McKusick sc->sc_cgp = sc->sc_cgbp->b_un.b_cg; 200113db2ddSJeff Roberson sc->sc_cgx = cgx; 201113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, sc, sc_next); 202113db2ddSJeff Roberson return (sc); 203113db2ddSJeff Roberson } 204113db2ddSJeff Roberson 205113db2ddSJeff Roberson /* 206113db2ddSJeff Roberson * Lookup an inode number in the hash and allocate a suj_ino if it does 207113db2ddSJeff Roberson * not exist. 208113db2ddSJeff Roberson */ 209113db2ddSJeff Roberson static struct suj_ino * 210113db2ddSJeff Roberson ino_lookup(ino_t ino, int creat) 211113db2ddSJeff Roberson { 212113db2ddSJeff Roberson struct suj_ino *sino; 213113db2ddSJeff Roberson struct inohd *hd; 214113db2ddSJeff Roberson struct suj_cg *sc; 215113db2ddSJeff Roberson 216113db2ddSJeff Roberson sc = cg_lookup(ino_to_cg(fs, ino)); 217113db2ddSJeff Roberson if (sc->sc_lastino && sc->sc_lastino->si_ino == ino) 218113db2ddSJeff Roberson return (sc->sc_lastino); 2195cc52631SKirk McKusick hd = &sc->sc_inohash[HASH(ino)]; 220113db2ddSJeff Roberson LIST_FOREACH(sino, hd, si_next) 221113db2ddSJeff Roberson if (sino->si_ino == ino) 222113db2ddSJeff Roberson return (sino); 223113db2ddSJeff Roberson if (creat == 0) 224113db2ddSJeff Roberson return (NULL); 225113db2ddSJeff Roberson sino = errmalloc(sizeof(*sino)); 226113db2ddSJeff Roberson bzero(sino, sizeof(*sino)); 227113db2ddSJeff Roberson sino->si_ino = ino; 228113db2ddSJeff Roberson TAILQ_INIT(&sino->si_recs); 229113db2ddSJeff Roberson TAILQ_INIT(&sino->si_newrecs); 230113db2ddSJeff Roberson TAILQ_INIT(&sino->si_movs); 231113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, sino, si_next); 232113db2ddSJeff Roberson 233113db2ddSJeff Roberson return (sino); 234113db2ddSJeff Roberson } 235113db2ddSJeff Roberson 236113db2ddSJeff Roberson /* 237113db2ddSJeff Roberson * Lookup a block number in the hash and allocate a suj_blk if it does 238113db2ddSJeff Roberson * not exist. 239113db2ddSJeff Roberson */ 240113db2ddSJeff Roberson static struct suj_blk * 241113db2ddSJeff Roberson blk_lookup(ufs2_daddr_t blk, int creat) 242113db2ddSJeff Roberson { 243113db2ddSJeff Roberson struct suj_blk *sblk; 244113db2ddSJeff Roberson struct suj_cg *sc; 245113db2ddSJeff Roberson struct blkhd *hd; 246113db2ddSJeff Roberson 247113db2ddSJeff Roberson sc = cg_lookup(dtog(fs, blk)); 248113db2ddSJeff Roberson if (sc->sc_lastblk && sc->sc_lastblk->sb_blk == blk) 249113db2ddSJeff Roberson return (sc->sc_lastblk); 2505cc52631SKirk McKusick hd = &sc->sc_blkhash[HASH(fragstoblks(fs, blk))]; 251113db2ddSJeff Roberson LIST_FOREACH(sblk, hd, sb_next) 252113db2ddSJeff Roberson if (sblk->sb_blk == blk) 253113db2ddSJeff Roberson return (sblk); 254113db2ddSJeff Roberson if (creat == 0) 255113db2ddSJeff Roberson return (NULL); 256113db2ddSJeff Roberson sblk = errmalloc(sizeof(*sblk)); 257113db2ddSJeff Roberson bzero(sblk, sizeof(*sblk)); 258113db2ddSJeff Roberson sblk->sb_blk = blk; 259113db2ddSJeff Roberson TAILQ_INIT(&sblk->sb_recs); 260113db2ddSJeff Roberson LIST_INSERT_HEAD(hd, sblk, sb_next); 261113db2ddSJeff Roberson 262113db2ddSJeff Roberson return (sblk); 263113db2ddSJeff Roberson } 264113db2ddSJeff Roberson 265113db2ddSJeff Roberson static int 266113db2ddSJeff Roberson blk_overlaps(struct jblkrec *brec, ufs2_daddr_t start, int frags) 267113db2ddSJeff Roberson { 268113db2ddSJeff Roberson ufs2_daddr_t bstart; 269113db2ddSJeff Roberson ufs2_daddr_t bend; 270113db2ddSJeff Roberson ufs2_daddr_t end; 271113db2ddSJeff Roberson 272113db2ddSJeff Roberson end = start + frags; 273113db2ddSJeff Roberson bstart = brec->jb_blkno + brec->jb_oldfrags; 274113db2ddSJeff Roberson bend = bstart + brec->jb_frags; 275113db2ddSJeff Roberson if (start < bend && end > bstart) 276113db2ddSJeff Roberson return (1); 277113db2ddSJeff Roberson return (0); 278113db2ddSJeff Roberson } 279113db2ddSJeff Roberson 280113db2ddSJeff Roberson static int 281113db2ddSJeff Roberson blk_equals(struct jblkrec *brec, ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t start, 282113db2ddSJeff Roberson int frags) 283113db2ddSJeff Roberson { 284113db2ddSJeff Roberson 285113db2ddSJeff Roberson if (brec->jb_ino != ino || brec->jb_lbn != lbn) 286113db2ddSJeff Roberson return (0); 287113db2ddSJeff Roberson if (brec->jb_blkno + brec->jb_oldfrags != start) 288113db2ddSJeff Roberson return (0); 2892db62a6bSJeff Roberson if (brec->jb_frags < frags) 290113db2ddSJeff Roberson return (0); 291113db2ddSJeff Roberson return (1); 292113db2ddSJeff Roberson } 293113db2ddSJeff Roberson 294113db2ddSJeff Roberson static void 295113db2ddSJeff Roberson blk_setmask(struct jblkrec *brec, int *mask) 296113db2ddSJeff Roberson { 297113db2ddSJeff Roberson int i; 298113db2ddSJeff Roberson 299113db2ddSJeff Roberson for (i = brec->jb_oldfrags; i < brec->jb_oldfrags + brec->jb_frags; i++) 300113db2ddSJeff Roberson *mask |= 1 << i; 301113db2ddSJeff Roberson } 302113db2ddSJeff Roberson 303113db2ddSJeff Roberson /* 304113db2ddSJeff Roberson * Determine whether a given block has been reallocated to a new location. 305113db2ddSJeff Roberson * Returns a mask of overlapping bits if any frags have been reused or 306113db2ddSJeff Roberson * zero if the block has not been re-used and the contents can be trusted. 307113db2ddSJeff Roberson * 308113db2ddSJeff Roberson * This is used to ensure that an orphaned pointer due to truncate is safe 309113db2ddSJeff Roberson * to be freed. The mask value can be used to free partial blocks. 310113db2ddSJeff Roberson */ 311113db2ddSJeff Roberson static int 312113db2ddSJeff Roberson blk_freemask(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags) 313113db2ddSJeff Roberson { 314113db2ddSJeff Roberson struct suj_blk *sblk; 315113db2ddSJeff Roberson struct suj_rec *srec; 316113db2ddSJeff Roberson struct jblkrec *brec; 317113db2ddSJeff Roberson int mask; 318113db2ddSJeff Roberson int off; 319113db2ddSJeff Roberson 320113db2ddSJeff Roberson /* 321113db2ddSJeff Roberson * To be certain we're not freeing a reallocated block we lookup 322113db2ddSJeff Roberson * this block in the blk hash and see if there is an allocation 323113db2ddSJeff Roberson * journal record that overlaps with any fragments in the block 324113db2ddSJeff Roberson * we're concerned with. If any fragments have ben reallocated 325113db2ddSJeff Roberson * the block has already been freed and re-used for another purpose. 326113db2ddSJeff Roberson */ 327113db2ddSJeff Roberson mask = 0; 328113db2ddSJeff Roberson sblk = blk_lookup(blknum(fs, blk), 0); 329113db2ddSJeff Roberson if (sblk == NULL) 330113db2ddSJeff Roberson return (0); 331113db2ddSJeff Roberson off = blk - sblk->sb_blk; 332113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 333113db2ddSJeff Roberson brec = (struct jblkrec *)srec->sr_rec; 334113db2ddSJeff Roberson /* 335113db2ddSJeff Roberson * If the block overlaps but does not match 336113db2ddSJeff Roberson * exactly this record refers to the current 337113db2ddSJeff Roberson * location. 338113db2ddSJeff Roberson */ 339113db2ddSJeff Roberson if (blk_overlaps(brec, blk, frags) == 0) 340113db2ddSJeff Roberson continue; 341113db2ddSJeff Roberson if (blk_equals(brec, ino, lbn, blk, frags) == 1) 342113db2ddSJeff Roberson mask = 0; 343113db2ddSJeff Roberson else 344113db2ddSJeff Roberson blk_setmask(brec, &mask); 345113db2ddSJeff Roberson } 346113db2ddSJeff Roberson if (debug) 347113db2ddSJeff Roberson printf("blk_freemask: blk %jd sblk %jd off %d mask 0x%X\n", 348113db2ddSJeff Roberson blk, sblk->sb_blk, off, mask); 349113db2ddSJeff Roberson return (mask >> off); 350113db2ddSJeff Roberson } 351113db2ddSJeff Roberson 352113db2ddSJeff Roberson /* 353113db2ddSJeff Roberson * Determine whether it is safe to follow an indirect. It is not safe 354113db2ddSJeff Roberson * if any part of the indirect has been reallocated or the last journal 355113db2ddSJeff Roberson * entry was an allocation. Just allocated indirects may not have valid 356113db2ddSJeff Roberson * pointers yet and all of their children will have their own records. 357113db2ddSJeff Roberson * It is also not safe to follow an indirect if the cg bitmap has been 358113db2ddSJeff Roberson * cleared as a new allocation may write to the block prior to the journal 359113db2ddSJeff Roberson * being written. 360113db2ddSJeff Roberson * 361113db2ddSJeff Roberson * Returns 1 if it's safe to follow the indirect and 0 otherwise. 362113db2ddSJeff Roberson */ 363113db2ddSJeff Roberson static int 364113db2ddSJeff Roberson blk_isindir(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn) 365113db2ddSJeff Roberson { 366113db2ddSJeff Roberson struct suj_blk *sblk; 367113db2ddSJeff Roberson struct jblkrec *brec; 368113db2ddSJeff Roberson 369113db2ddSJeff Roberson sblk = blk_lookup(blk, 0); 370113db2ddSJeff Roberson if (sblk == NULL) 371113db2ddSJeff Roberson return (1); 372113db2ddSJeff Roberson if (TAILQ_EMPTY(&sblk->sb_recs)) 373113db2ddSJeff Roberson return (1); 374113db2ddSJeff Roberson brec = (struct jblkrec *)TAILQ_LAST(&sblk->sb_recs, srechd)->sr_rec; 375113db2ddSJeff Roberson if (blk_equals(brec, ino, lbn, blk, fs->fs_frag)) 376113db2ddSJeff Roberson if (brec->jb_op == JOP_FREEBLK) 377113db2ddSJeff Roberson return (!blk_isfree(blk)); 378113db2ddSJeff Roberson return (0); 379113db2ddSJeff Roberson } 380113db2ddSJeff Roberson 381113db2ddSJeff Roberson /* 382113db2ddSJeff Roberson * Clear an inode from the cg bitmap. If the inode was already clear return 383113db2ddSJeff Roberson * 0 so the caller knows it does not have to check the inode contents. 384113db2ddSJeff Roberson */ 385113db2ddSJeff Roberson static int 386113db2ddSJeff Roberson ino_free(ino_t ino, int mode) 387113db2ddSJeff Roberson { 388113db2ddSJeff Roberson struct suj_cg *sc; 389113db2ddSJeff Roberson uint8_t *inosused; 390113db2ddSJeff Roberson struct cg *cgp; 391113db2ddSJeff Roberson int cg; 392113db2ddSJeff Roberson 393113db2ddSJeff Roberson cg = ino_to_cg(fs, ino); 394113db2ddSJeff Roberson ino = ino % fs->fs_ipg; 395113db2ddSJeff Roberson sc = cg_lookup(cg); 396113db2ddSJeff Roberson cgp = sc->sc_cgp; 397113db2ddSJeff Roberson inosused = cg_inosused(cgp); 398113db2ddSJeff Roberson /* 399113db2ddSJeff Roberson * The bitmap may never have made it to the disk so we have to 400113db2ddSJeff Roberson * conditionally clear. We can avoid writing the cg in this case. 401113db2ddSJeff Roberson */ 402113db2ddSJeff Roberson if (isclr(inosused, ino)) 403113db2ddSJeff Roberson return (0); 404113db2ddSJeff Roberson freeinos++; 405113db2ddSJeff Roberson clrbit(inosused, ino); 406113db2ddSJeff Roberson if (ino < cgp->cg_irotor) 407113db2ddSJeff Roberson cgp->cg_irotor = ino; 408113db2ddSJeff Roberson cgp->cg_cs.cs_nifree++; 409d8ba45e2SEd Maste if ((mode & IFMT) == IFDIR) { 410113db2ddSJeff Roberson freedir++; 411113db2ddSJeff Roberson cgp->cg_cs.cs_ndir--; 412113db2ddSJeff Roberson } 4135cc52631SKirk McKusick cgdirty(sc->sc_cgbp); 414113db2ddSJeff Roberson 415113db2ddSJeff Roberson return (1); 416113db2ddSJeff Roberson } 417113db2ddSJeff Roberson 418113db2ddSJeff Roberson /* 419113db2ddSJeff Roberson * Free 'frags' frags starting at filesystem block 'bno' skipping any frags 420113db2ddSJeff Roberson * set in the mask. 421113db2ddSJeff Roberson */ 422113db2ddSJeff Roberson static void 423113db2ddSJeff Roberson blk_free(ufs2_daddr_t bno, int mask, int frags) 424113db2ddSJeff Roberson { 425113db2ddSJeff Roberson ufs1_daddr_t fragno, cgbno; 426113db2ddSJeff Roberson struct suj_cg *sc; 427113db2ddSJeff Roberson struct cg *cgp; 428113db2ddSJeff Roberson int i, cg; 429113db2ddSJeff Roberson uint8_t *blksfree; 430113db2ddSJeff Roberson 431113db2ddSJeff Roberson if (debug) 4322db62a6bSJeff Roberson printf("Freeing %d frags at blk %jd mask 0x%x\n", 4332db62a6bSJeff Roberson frags, bno, mask); 434113db2ddSJeff Roberson cg = dtog(fs, bno); 435113db2ddSJeff Roberson sc = cg_lookup(cg); 436113db2ddSJeff Roberson cgp = sc->sc_cgp; 437113db2ddSJeff Roberson cgbno = dtogd(fs, bno); 438113db2ddSJeff Roberson blksfree = cg_blksfree(cgp); 439113db2ddSJeff Roberson 440113db2ddSJeff Roberson /* 441113db2ddSJeff Roberson * If it's not allocated we only wrote the journal entry 442113db2ddSJeff Roberson * and never the bitmaps. Here we unconditionally clear and 443113db2ddSJeff Roberson * resolve the cg summary later. 444113db2ddSJeff Roberson */ 445113db2ddSJeff Roberson if (frags == fs->fs_frag && mask == 0) { 446113db2ddSJeff Roberson fragno = fragstoblks(fs, cgbno); 447113db2ddSJeff Roberson ffs_setblock(fs, blksfree, fragno); 448113db2ddSJeff Roberson freeblocks++; 449113db2ddSJeff Roberson } else { 450113db2ddSJeff Roberson /* 451113db2ddSJeff Roberson * deallocate the fragment 452113db2ddSJeff Roberson */ 453113db2ddSJeff Roberson for (i = 0; i < frags; i++) 454113db2ddSJeff Roberson if ((mask & (1 << i)) == 0 && isclr(blksfree, cgbno +i)) { 455113db2ddSJeff Roberson freefrags++; 456113db2ddSJeff Roberson setbit(blksfree, cgbno + i); 457113db2ddSJeff Roberson } 458113db2ddSJeff Roberson } 4595cc52631SKirk McKusick cgdirty(sc->sc_cgbp); 460113db2ddSJeff Roberson } 461113db2ddSJeff Roberson 462113db2ddSJeff Roberson /* 463113db2ddSJeff Roberson * Returns 1 if the whole block starting at 'bno' is marked free and 0 464113db2ddSJeff Roberson * otherwise. 465113db2ddSJeff Roberson */ 466113db2ddSJeff Roberson static int 467113db2ddSJeff Roberson blk_isfree(ufs2_daddr_t bno) 468113db2ddSJeff Roberson { 469113db2ddSJeff Roberson struct suj_cg *sc; 470113db2ddSJeff Roberson 471113db2ddSJeff Roberson sc = cg_lookup(dtog(fs, bno)); 472113db2ddSJeff Roberson return ffs_isblock(fs, cg_blksfree(sc->sc_cgp), dtogd(fs, bno)); 473113db2ddSJeff Roberson } 474113db2ddSJeff Roberson 475113db2ddSJeff Roberson /* 476113db2ddSJeff Roberson * Determine whether a block exists at a particular lbn in an inode. 477113db2ddSJeff Roberson * Returns 1 if found, 0 if not. lbn may be negative for indirects 478113db2ddSJeff Roberson * or ext blocks. 479113db2ddSJeff Roberson */ 480113db2ddSJeff Roberson static int 481113db2ddSJeff Roberson blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int *frags) 482113db2ddSJeff Roberson { 4835cc52631SKirk McKusick struct inode ip; 4845cc52631SKirk McKusick union dinode *dp; 485113db2ddSJeff Roberson ufs2_daddr_t nblk; 486113db2ddSJeff Roberson 4875cc52631SKirk McKusick ginode(ino, &ip); 4885cc52631SKirk McKusick dp = ip.i_dp; 4895cc52631SKirk McKusick if (DIP(dp, di_nlink) == 0 || DIP(dp, di_mode) == 0) { 4905cc52631SKirk McKusick irelse(&ip); 491113db2ddSJeff Roberson return (0); 4925cc52631SKirk McKusick } 4935cc52631SKirk McKusick nblk = ino_blkatoff(dp, ino, lbn, frags, NULL); 4945cc52631SKirk McKusick irelse(&ip); 495113db2ddSJeff Roberson return (nblk == blk); 496113db2ddSJeff Roberson } 497113db2ddSJeff Roberson 498113db2ddSJeff Roberson /* 49924d37c1eSJeff Roberson * Clear the directory entry at diroff that should point to child. Minimal 50024d37c1eSJeff Roberson * checking is done and it is assumed that this path was verified with isat. 50124d37c1eSJeff Roberson */ 50224d37c1eSJeff Roberson static void 50324d37c1eSJeff Roberson ino_clrat(ino_t parent, off_t diroff, ino_t child) 50424d37c1eSJeff Roberson { 50524d37c1eSJeff Roberson union dinode *dip; 50624d37c1eSJeff Roberson struct direct *dp; 5075cc52631SKirk McKusick struct inode ip; 50824d37c1eSJeff Roberson ufs2_daddr_t blk; 5095cc52631SKirk McKusick struct bufarea *bp; 51024d37c1eSJeff Roberson ufs_lbn_t lbn; 51124d37c1eSJeff Roberson int blksize; 51224d37c1eSJeff Roberson int frags; 51324d37c1eSJeff Roberson int doff; 51424d37c1eSJeff Roberson 51524d37c1eSJeff Roberson if (debug) 516623d7cb6SMatthew D Fleming printf("Clearing inode %ju from parent %ju at offset %jd\n", 517623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff); 51824d37c1eSJeff Roberson 51924d37c1eSJeff Roberson lbn = lblkno(fs, diroff); 52024d37c1eSJeff Roberson doff = blkoff(fs, diroff); 5215cc52631SKirk McKusick ginode(parent, &ip); 5225cc52631SKirk McKusick dip = ip.i_dp; 5235cc52631SKirk McKusick blk = ino_blkatoff(dip, parent, lbn, &frags, NULL); 52424d37c1eSJeff Roberson blksize = sblksize(fs, DIP(dip, di_size), lbn); 5255cc52631SKirk McKusick irelse(&ip); 5265cc52631SKirk McKusick bp = getdatablk(blk, blksize, BT_DIRDATA); 5275cc52631SKirk McKusick if (bp->b_errs != 0) 5285cc52631SKirk McKusick err_suj("ino_clrat: UNRECOVERABLE I/O ERROR"); 5295cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[doff]; 53024d37c1eSJeff Roberson if (dp->d_ino != child) 531623d7cb6SMatthew D Fleming errx(1, "Inode %ju does not exist in %ju at %jd", 532623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff); 53324d37c1eSJeff Roberson dp->d_ino = 0; 5345cc52631SKirk McKusick dirty(bp); 5355cc52631SKirk McKusick brelse(bp); 53624d37c1eSJeff Roberson /* 53724d37c1eSJeff Roberson * The actual .. reference count will already have been removed 53824d37c1eSJeff Roberson * from the parent by the .. remref record. 53924d37c1eSJeff Roberson */ 54024d37c1eSJeff Roberson } 54124d37c1eSJeff Roberson 54224d37c1eSJeff Roberson /* 543113db2ddSJeff Roberson * Determines whether a pointer to an inode exists within a directory 544113db2ddSJeff Roberson * at a specified offset. Returns the mode of the found entry. 545113db2ddSJeff Roberson */ 546113db2ddSJeff Roberson static int 547113db2ddSJeff Roberson ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot) 548113db2ddSJeff Roberson { 5495cc52631SKirk McKusick struct inode ip; 550113db2ddSJeff Roberson union dinode *dip; 5515cc52631SKirk McKusick struct bufarea *bp; 552113db2ddSJeff Roberson struct direct *dp; 553113db2ddSJeff Roberson ufs2_daddr_t blk; 554113db2ddSJeff Roberson ufs_lbn_t lbn; 555113db2ddSJeff Roberson int blksize; 556113db2ddSJeff Roberson int frags; 557113db2ddSJeff Roberson int dpoff; 558113db2ddSJeff Roberson int doff; 559113db2ddSJeff Roberson 560113db2ddSJeff Roberson *isdot = 0; 5615cc52631SKirk McKusick ginode(parent, &ip); 5625cc52631SKirk McKusick dip = ip.i_dp; 563113db2ddSJeff Roberson *mode = DIP(dip, di_mode); 564d8ba45e2SEd Maste if ((*mode & IFMT) != IFDIR) { 565113db2ddSJeff Roberson if (debug) { 566113db2ddSJeff Roberson /* 567113db2ddSJeff Roberson * This can happen if the parent inode 568113db2ddSJeff Roberson * was reallocated. 569113db2ddSJeff Roberson */ 570113db2ddSJeff Roberson if (*mode != 0) 571623d7cb6SMatthew D Fleming printf("Directory %ju has bad mode %o\n", 572623d7cb6SMatthew D Fleming (uintmax_t)parent, *mode); 573113db2ddSJeff Roberson else 574623d7cb6SMatthew D Fleming printf("Directory %ju has zero mode\n", 575623d7cb6SMatthew D Fleming (uintmax_t)parent); 576113db2ddSJeff Roberson } 5775cc52631SKirk McKusick irelse(&ip); 578113db2ddSJeff Roberson return (0); 579113db2ddSJeff Roberson } 580113db2ddSJeff Roberson lbn = lblkno(fs, diroff); 581113db2ddSJeff Roberson doff = blkoff(fs, diroff); 582113db2ddSJeff Roberson blksize = sblksize(fs, DIP(dip, di_size), lbn); 583113db2ddSJeff Roberson if (diroff + DIRECTSIZ(1) > DIP(dip, di_size) || doff >= blksize) { 584113db2ddSJeff Roberson if (debug) 585623d7cb6SMatthew D Fleming printf("ino %ju absent from %ju due to offset %jd" 586113db2ddSJeff Roberson " exceeding size %jd\n", 587623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff, 588623d7cb6SMatthew D Fleming DIP(dip, di_size)); 5895cc52631SKirk McKusick irelse(&ip); 590113db2ddSJeff Roberson return (0); 591113db2ddSJeff Roberson } 5925cc52631SKirk McKusick blk = ino_blkatoff(dip, parent, lbn, &frags, NULL); 5935cc52631SKirk McKusick irelse(&ip); 594113db2ddSJeff Roberson if (blk <= 0) { 595113db2ddSJeff Roberson if (debug) 596623d7cb6SMatthew D Fleming printf("Sparse directory %ju", (uintmax_t)parent); 597113db2ddSJeff Roberson return (0); 598113db2ddSJeff Roberson } 5995cc52631SKirk McKusick bp = getdatablk(blk, blksize, BT_DIRDATA); 6005cc52631SKirk McKusick if (bp->b_errs != 0) 6015cc52631SKirk McKusick err_suj("ino_isat: UNRECOVERABLE I/O ERROR"); 602113db2ddSJeff Roberson /* 603113db2ddSJeff Roberson * Walk through the records from the start of the block to be 604113db2ddSJeff Roberson * certain we hit a valid record and not some junk in the middle 605113db2ddSJeff Roberson * of a file name. Stop when we reach or pass the expected offset. 606113db2ddSJeff Roberson */ 607f32d2926SPedro F. Giffuni dpoff = rounddown(doff, DIRBLKSIZ); 608113db2ddSJeff Roberson do { 6095cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[dpoff]; 610113db2ddSJeff Roberson if (dpoff == doff) 611113db2ddSJeff Roberson break; 612113db2ddSJeff Roberson if (dp->d_reclen == 0) 613113db2ddSJeff Roberson break; 614113db2ddSJeff Roberson dpoff += dp->d_reclen; 615113db2ddSJeff Roberson } while (dpoff <= doff); 616113db2ddSJeff Roberson if (dpoff > fs->fs_bsize) 617623d7cb6SMatthew D Fleming err_suj("Corrupt directory block in dir ino %ju\n", 618623d7cb6SMatthew D Fleming (uintmax_t)parent); 619113db2ddSJeff Roberson /* Not found. */ 620113db2ddSJeff Roberson if (dpoff != doff) { 621113db2ddSJeff Roberson if (debug) 622623d7cb6SMatthew D Fleming printf("ino %ju not found in %ju, lbn %jd, dpoff %d\n", 623623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, lbn, dpoff); 6245cc52631SKirk McKusick brelse(bp); 625113db2ddSJeff Roberson return (0); 626113db2ddSJeff Roberson } 627113db2ddSJeff Roberson /* 628113db2ddSJeff Roberson * We found the item in question. Record the mode and whether it's 629113db2ddSJeff Roberson * a . or .. link for the caller. 630113db2ddSJeff Roberson */ 631113db2ddSJeff Roberson if (dp->d_ino == child) { 632113db2ddSJeff Roberson if (child == parent) 633113db2ddSJeff Roberson *isdot = 1; 634113db2ddSJeff Roberson else if (dp->d_namlen == 2 && 635113db2ddSJeff Roberson dp->d_name[0] == '.' && dp->d_name[1] == '.') 636113db2ddSJeff Roberson *isdot = 1; 637113db2ddSJeff Roberson *mode = DTTOIF(dp->d_type); 6385cc52631SKirk McKusick brelse(bp); 639113db2ddSJeff Roberson return (1); 640113db2ddSJeff Roberson } 641113db2ddSJeff Roberson if (debug) 642623d7cb6SMatthew D Fleming printf("ino %ju doesn't match dirent ino %ju in parent %ju\n", 643623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)dp->d_ino, (uintmax_t)parent); 6445cc52631SKirk McKusick brelse(bp); 645113db2ddSJeff Roberson return (0); 646113db2ddSJeff Roberson } 647113db2ddSJeff Roberson 648113db2ddSJeff Roberson #define VISIT_INDIR 0x0001 649113db2ddSJeff Roberson #define VISIT_EXT 0x0002 650113db2ddSJeff Roberson #define VISIT_ROOT 0x0004 /* Operation came via root & valid pointers. */ 651113db2ddSJeff Roberson 652113db2ddSJeff Roberson /* 653113db2ddSJeff Roberson * Read an indirect level which may or may not be linked into an inode. 654113db2ddSJeff Roberson */ 655113db2ddSJeff Roberson static void 656113db2ddSJeff Roberson indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags, 657113db2ddSJeff Roberson ino_visitor visitor, int flags) 658113db2ddSJeff Roberson { 6595cc52631SKirk McKusick struct bufarea *bp; 660113db2ddSJeff Roberson ufs_lbn_t lbnadd; 661113db2ddSJeff Roberson ufs2_daddr_t nblk; 662113db2ddSJeff Roberson ufs_lbn_t nlbn; 663113db2ddSJeff Roberson int level; 664113db2ddSJeff Roberson int i; 665113db2ddSJeff Roberson 666113db2ddSJeff Roberson /* 667113db2ddSJeff Roberson * Don't visit indirect blocks with contents we can't trust. This 668113db2ddSJeff Roberson * should only happen when indir_visit() is called to complete a 669113db2ddSJeff Roberson * truncate that never finished and not when a pointer is found via 670113db2ddSJeff Roberson * an inode. 671113db2ddSJeff Roberson */ 672113db2ddSJeff Roberson if (blk == 0) 673113db2ddSJeff Roberson return; 674113db2ddSJeff Roberson level = lbn_level(lbn); 675113db2ddSJeff Roberson if (level == -1) 676edad6026SXin LI err_suj("Invalid level for lbn %jd\n", lbn); 677113db2ddSJeff Roberson if ((flags & VISIT_ROOT) == 0 && blk_isindir(blk, ino, lbn) == 0) { 678113db2ddSJeff Roberson if (debug) 679623d7cb6SMatthew D Fleming printf("blk %jd ino %ju lbn %jd(%d) is not indir.\n", 680623d7cb6SMatthew D Fleming blk, (uintmax_t)ino, lbn, level); 681113db2ddSJeff Roberson goto out; 682113db2ddSJeff Roberson } 683113db2ddSJeff Roberson lbnadd = 1; 684113db2ddSJeff Roberson for (i = level; i > 0; i--) 685113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 6865cc52631SKirk McKusick bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level); 6875cc52631SKirk McKusick if (bp->b_errs != 0) 6885cc52631SKirk McKusick err_suj("indir_visit: UNRECOVERABLE I/O ERROR"); 689113db2ddSJeff Roberson for (i = 0; i < NINDIR(fs); i++) { 6905cc52631SKirk McKusick if ((nblk = IBLK(bp, i)) == 0) 691113db2ddSJeff Roberson continue; 692113db2ddSJeff Roberson if (level == 0) { 693113db2ddSJeff Roberson nlbn = -lbn + i * lbnadd; 694113db2ddSJeff Roberson (*frags) += fs->fs_frag; 695113db2ddSJeff Roberson visitor(ino, nlbn, nblk, fs->fs_frag); 696113db2ddSJeff Roberson } else { 697113db2ddSJeff Roberson nlbn = (lbn + 1) - (i * lbnadd); 698113db2ddSJeff Roberson indir_visit(ino, nlbn, nblk, frags, visitor, flags); 699113db2ddSJeff Roberson } 700113db2ddSJeff Roberson } 7015cc52631SKirk McKusick brelse(bp); 702113db2ddSJeff Roberson out: 703113db2ddSJeff Roberson if (flags & VISIT_INDIR) { 704113db2ddSJeff Roberson (*frags) += fs->fs_frag; 705113db2ddSJeff Roberson visitor(ino, lbn, blk, fs->fs_frag); 706113db2ddSJeff Roberson } 707113db2ddSJeff Roberson } 708113db2ddSJeff Roberson 709113db2ddSJeff Roberson /* 710113db2ddSJeff Roberson * Visit each block in an inode as specified by 'flags' and call a 711113db2ddSJeff Roberson * callback function. The callback may inspect or free blocks. The 712113db2ddSJeff Roberson * count of frags found according to the size in the file is returned. 713113db2ddSJeff Roberson * This is not valid for sparse files but may be used to determine 714113db2ddSJeff Roberson * the correct di_blocks for a file. 715113db2ddSJeff Roberson */ 716113db2ddSJeff Roberson static uint64_t 7175cc52631SKirk McKusick ino_visit(union dinode *dp, ino_t ino, ino_visitor visitor, int flags) 718113db2ddSJeff Roberson { 719113db2ddSJeff Roberson ufs_lbn_t nextlbn; 720113db2ddSJeff Roberson ufs_lbn_t tmpval; 721113db2ddSJeff Roberson ufs_lbn_t lbn; 722113db2ddSJeff Roberson uint64_t size; 723113db2ddSJeff Roberson uint64_t fragcnt; 724113db2ddSJeff Roberson int mode; 725113db2ddSJeff Roberson int frags; 726113db2ddSJeff Roberson int i; 727113db2ddSJeff Roberson 7285cc52631SKirk McKusick size = DIP(dp, di_size); 7295cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 730113db2ddSJeff Roberson fragcnt = 0; 731113db2ddSJeff Roberson if ((flags & VISIT_EXT) && 7325cc52631SKirk McKusick fs->fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize) { 7331dc349abSEd Maste for (i = 0; i < UFS_NXADDR; i++) { 7345cc52631SKirk McKusick if (dp->dp2.di_extb[i] == 0) 735113db2ddSJeff Roberson continue; 7365cc52631SKirk McKusick frags = sblksize(fs, dp->dp2.di_extsize, i); 737113db2ddSJeff Roberson frags = numfrags(fs, frags); 738113db2ddSJeff Roberson fragcnt += frags; 7395cc52631SKirk McKusick visitor(ino, -1 - i, dp->dp2.di_extb[i], frags); 740113db2ddSJeff Roberson } 741113db2ddSJeff Roberson } 742113db2ddSJeff Roberson /* Skip datablocks for short links and devices. */ 743d8ba45e2SEd Maste if (mode == IFBLK || mode == IFCHR || 744d8ba45e2SEd Maste (mode == IFLNK && size < fs->fs_maxsymlinklen)) 745113db2ddSJeff Roberson return (fragcnt); 7461dc349abSEd Maste for (i = 0; i < UFS_NDADDR; i++) { 7475cc52631SKirk McKusick if (DIP(dp, di_db[i]) == 0) 748113db2ddSJeff Roberson continue; 749113db2ddSJeff Roberson frags = sblksize(fs, size, i); 750113db2ddSJeff Roberson frags = numfrags(fs, frags); 751113db2ddSJeff Roberson fragcnt += frags; 7525cc52631SKirk McKusick visitor(ino, i, DIP(dp, di_db[i]), frags); 753113db2ddSJeff Roberson } 754113db2ddSJeff Roberson /* 755113db2ddSJeff Roberson * We know the following indirects are real as we're following 756113db2ddSJeff Roberson * real pointers to them. 757113db2ddSJeff Roberson */ 758113db2ddSJeff Roberson flags |= VISIT_ROOT; 7591dc349abSEd Maste for (i = 0, tmpval = NINDIR(fs), lbn = UFS_NDADDR; i < UFS_NIADDR; i++, 760113db2ddSJeff Roberson lbn = nextlbn) { 761113db2ddSJeff Roberson nextlbn = lbn + tmpval; 762113db2ddSJeff Roberson tmpval *= NINDIR(fs); 7635cc52631SKirk McKusick if (DIP(dp, di_ib[i]) == 0) 764113db2ddSJeff Roberson continue; 7655cc52631SKirk McKusick indir_visit(ino, -lbn - i, DIP(dp, di_ib[i]), &fragcnt, visitor, 766113db2ddSJeff Roberson flags); 767113db2ddSJeff Roberson } 768113db2ddSJeff Roberson return (fragcnt); 769113db2ddSJeff Roberson } 770113db2ddSJeff Roberson 771113db2ddSJeff Roberson /* 772113db2ddSJeff Roberson * Null visitor function used when we just want to count blocks and 773113db2ddSJeff Roberson * record the lbn. 774113db2ddSJeff Roberson */ 775113db2ddSJeff Roberson ufs_lbn_t visitlbn; 776113db2ddSJeff Roberson static void 777113db2ddSJeff Roberson null_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 778113db2ddSJeff Roberson { 779113db2ddSJeff Roberson if (lbn > 0) 780113db2ddSJeff Roberson visitlbn = lbn; 781113db2ddSJeff Roberson } 782113db2ddSJeff Roberson 783113db2ddSJeff Roberson /* 784113db2ddSJeff Roberson * Recalculate di_blocks when we discover that a block allocation or 785113db2ddSJeff Roberson * free was not successfully completed. The kernel does not roll this back 786113db2ddSJeff Roberson * because it would be too expensive to compute which indirects were 787113db2ddSJeff Roberson * reachable at the time the inode was written. 788113db2ddSJeff Roberson */ 789113db2ddSJeff Roberson static void 790113db2ddSJeff Roberson ino_adjblks(struct suj_ino *sino) 791113db2ddSJeff Roberson { 7925cc52631SKirk McKusick struct inode ip; 7935cc52631SKirk McKusick union dinode *dp; 794113db2ddSJeff Roberson uint64_t blocks; 795113db2ddSJeff Roberson uint64_t frags; 796113db2ddSJeff Roberson off_t isize; 797113db2ddSJeff Roberson off_t size; 798113db2ddSJeff Roberson ino_t ino; 799113db2ddSJeff Roberson 800113db2ddSJeff Roberson ino = sino->si_ino; 8015cc52631SKirk McKusick ginode(ino, &ip); 8025cc52631SKirk McKusick dp = ip.i_dp; 803113db2ddSJeff Roberson /* No need to adjust zero'd inodes. */ 8045cc52631SKirk McKusick if (DIP(dp, di_mode) == 0) { 8055cc52631SKirk McKusick irelse(&ip); 806113db2ddSJeff Roberson return; 8075cc52631SKirk McKusick } 808113db2ddSJeff Roberson /* 809113db2ddSJeff Roberson * Visit all blocks and count them as well as recording the last 810113db2ddSJeff Roberson * valid lbn in the file. If the file size doesn't agree with the 811113db2ddSJeff Roberson * last lbn we need to truncate to fix it. Otherwise just adjust 812113db2ddSJeff Roberson * the blocks count. 813113db2ddSJeff Roberson */ 814113db2ddSJeff Roberson visitlbn = 0; 8155cc52631SKirk McKusick frags = ino_visit(dp, ino, null_visit, VISIT_INDIR | VISIT_EXT); 816113db2ddSJeff Roberson blocks = fsbtodb(fs, frags); 817113db2ddSJeff Roberson /* 818113db2ddSJeff Roberson * We assume the size and direct block list is kept coherent by 819113db2ddSJeff Roberson * softdep. For files that have extended into indirects we truncate 820113db2ddSJeff Roberson * to the size in the inode or the maximum size permitted by 821113db2ddSJeff Roberson * populated indirects. 822113db2ddSJeff Roberson */ 8231dc349abSEd Maste if (visitlbn >= UFS_NDADDR) { 8245cc52631SKirk McKusick isize = DIP(dp, di_size); 825113db2ddSJeff Roberson size = lblktosize(fs, visitlbn + 1); 826113db2ddSJeff Roberson if (isize > size) 827113db2ddSJeff Roberson isize = size; 828113db2ddSJeff Roberson /* Always truncate to free any unpopulated indirects. */ 8295cc52631SKirk McKusick ino_trunc(ino, isize); 8305cc52631SKirk McKusick irelse(&ip); 831113db2ddSJeff Roberson return; 832113db2ddSJeff Roberson } 8335cc52631SKirk McKusick if (blocks == DIP(dp, di_blocks)) { 8345cc52631SKirk McKusick irelse(&ip); 835113db2ddSJeff Roberson return; 8365cc52631SKirk McKusick } 837113db2ddSJeff Roberson if (debug) 838623d7cb6SMatthew D Fleming printf("ino %ju adjusting block count from %jd to %jd\n", 8395cc52631SKirk McKusick (uintmax_t)ino, DIP(dp, di_blocks), blocks); 8405cc52631SKirk McKusick DIP_SET(dp, di_blocks, blocks); 8415cc52631SKirk McKusick inodirty(&ip); 8425cc52631SKirk McKusick irelse(&ip); 843113db2ddSJeff Roberson } 844113db2ddSJeff Roberson 845113db2ddSJeff Roberson static void 846113db2ddSJeff Roberson blk_free_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 847113db2ddSJeff Roberson { 848113db2ddSJeff Roberson 8492db62a6bSJeff Roberson blk_free(blk, blk_freemask(blk, ino, lbn, frags), frags); 850113db2ddSJeff Roberson } 851113db2ddSJeff Roberson 852113db2ddSJeff Roberson /* 853113db2ddSJeff Roberson * Free a block or tree of blocks that was previously rooted in ino at 854113db2ddSJeff Roberson * the given lbn. If the lbn is an indirect all children are freed 855113db2ddSJeff Roberson * recursively. 856113db2ddSJeff Roberson */ 857113db2ddSJeff Roberson static void 858113db2ddSJeff Roberson blk_free_lbn(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags, int follow) 859113db2ddSJeff Roberson { 860113db2ddSJeff Roberson uint64_t resid; 861113db2ddSJeff Roberson int mask; 862113db2ddSJeff Roberson 863113db2ddSJeff Roberson mask = blk_freemask(blk, ino, lbn, frags); 864113db2ddSJeff Roberson resid = 0; 8651dc349abSEd Maste if (lbn <= -UFS_NDADDR && follow && mask == 0) 866113db2ddSJeff Roberson indir_visit(ino, lbn, blk, &resid, blk_free_visit, VISIT_INDIR); 867113db2ddSJeff Roberson else 868113db2ddSJeff Roberson blk_free(blk, mask, frags); 869113db2ddSJeff Roberson } 870113db2ddSJeff Roberson 871113db2ddSJeff Roberson static void 872113db2ddSJeff Roberson ino_setskip(struct suj_ino *sino, ino_t parent) 873113db2ddSJeff Roberson { 874113db2ddSJeff Roberson int isdot; 875113db2ddSJeff Roberson int mode; 876113db2ddSJeff Roberson 877113db2ddSJeff Roberson if (ino_isat(sino->si_ino, DOTDOT_OFFSET, parent, &mode, &isdot)) 878113db2ddSJeff Roberson sino->si_skipparent = 1; 879113db2ddSJeff Roberson } 880113db2ddSJeff Roberson 88124d37c1eSJeff Roberson static void 88224d37c1eSJeff Roberson ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot) 88324d37c1eSJeff Roberson { 88424d37c1eSJeff Roberson struct suj_ino *sino; 88524d37c1eSJeff Roberson struct suj_rec *srec; 88624d37c1eSJeff Roberson struct jrefrec *rrec; 88724d37c1eSJeff Roberson 88824d37c1eSJeff Roberson /* 88924d37c1eSJeff Roberson * Lookup this inode to see if we have a record for it. 89024d37c1eSJeff Roberson */ 89124d37c1eSJeff Roberson sino = ino_lookup(child, 0); 89224d37c1eSJeff Roberson /* 89324d37c1eSJeff Roberson * Tell any child directories we've already removed their 89424d37c1eSJeff Roberson * parent link cnt. Don't try to adjust our link down again. 89524d37c1eSJeff Roberson */ 89624d37c1eSJeff Roberson if (sino != NULL && isdotdot == 0) 89724d37c1eSJeff Roberson ino_setskip(sino, parent); 89824d37c1eSJeff Roberson /* 89924d37c1eSJeff Roberson * No valid record for this inode. Just drop the on-disk 90024d37c1eSJeff Roberson * link by one. 90124d37c1eSJeff Roberson */ 90224d37c1eSJeff Roberson if (sino == NULL || sino->si_hasrecs == 0) { 90324d37c1eSJeff Roberson ino_decr(child); 90424d37c1eSJeff Roberson return; 90524d37c1eSJeff Roberson } 90624d37c1eSJeff Roberson /* 90724d37c1eSJeff Roberson * Use ino_adjust() if ino_check() has already processed this 90824d37c1eSJeff Roberson * child. If we lose the last non-dot reference to a 90924d37c1eSJeff Roberson * directory it will be discarded. 91024d37c1eSJeff Roberson */ 91124d37c1eSJeff Roberson if (sino->si_linkadj) { 9125cc52631SKirk McKusick if (sino->si_nlink == 0) 9135cc52631SKirk McKusick err_suj("ino_remref: ino %ld mode 0%o about to go " 9145cc52631SKirk McKusick "negative\n", sino->si_ino, sino->si_mode); 91524d37c1eSJeff Roberson sino->si_nlink--; 91624d37c1eSJeff Roberson if (isdotdot) 91724d37c1eSJeff Roberson sino->si_dotlinks--; 91824d37c1eSJeff Roberson ino_adjust(sino); 91924d37c1eSJeff Roberson return; 92024d37c1eSJeff Roberson } 92124d37c1eSJeff Roberson /* 92224d37c1eSJeff Roberson * If we haven't yet processed this inode we need to make 92324d37c1eSJeff Roberson * sure we will successfully discover the lost path. If not 92424d37c1eSJeff Roberson * use nlinkadj to remember. 92524d37c1eSJeff Roberson */ 92624d37c1eSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 92724d37c1eSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 92824d37c1eSJeff Roberson if (rrec->jr_parent == parent && 92924d37c1eSJeff Roberson rrec->jr_diroff == diroff) 93024d37c1eSJeff Roberson return; 93124d37c1eSJeff Roberson } 93224d37c1eSJeff Roberson sino->si_nlinkadj++; 93324d37c1eSJeff Roberson } 93424d37c1eSJeff Roberson 935113db2ddSJeff Roberson /* 936113db2ddSJeff Roberson * Free the children of a directory when the directory is discarded. 937113db2ddSJeff Roberson */ 938113db2ddSJeff Roberson static void 939113db2ddSJeff Roberson ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 940113db2ddSJeff Roberson { 941113db2ddSJeff Roberson struct suj_ino *sino; 9425cc52631SKirk McKusick struct bufarea *bp; 943113db2ddSJeff Roberson struct direct *dp; 944113db2ddSJeff Roberson off_t diroff; 945113db2ddSJeff Roberson int skipparent; 94624d37c1eSJeff Roberson int isdotdot; 947113db2ddSJeff Roberson int dpoff; 948113db2ddSJeff Roberson int size; 949113db2ddSJeff Roberson 950113db2ddSJeff Roberson sino = ino_lookup(ino, 0); 951113db2ddSJeff Roberson if (sino) 952113db2ddSJeff Roberson skipparent = sino->si_skipparent; 953113db2ddSJeff Roberson else 954113db2ddSJeff Roberson skipparent = 0; 955113db2ddSJeff Roberson size = lfragtosize(fs, frags); 9565cc52631SKirk McKusick bp = getdatablk(blk, size, BT_DIRDATA); 9575cc52631SKirk McKusick if (bp->b_errs != 0) 9585cc52631SKirk McKusick err_suj("ino_free_children: UNRECOVERABLE I/O ERROR"); 9595cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[0]; 960113db2ddSJeff Roberson for (dpoff = 0; dpoff < size && dp->d_reclen; dpoff += dp->d_reclen) { 9615cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[dpoff]; 9621dc349abSEd Maste if (dp->d_ino == 0 || dp->d_ino == UFS_WINO) 963113db2ddSJeff Roberson continue; 964113db2ddSJeff Roberson if (dp->d_namlen == 1 && dp->d_name[0] == '.') 965113db2ddSJeff Roberson continue; 96624d37c1eSJeff Roberson isdotdot = dp->d_namlen == 2 && dp->d_name[0] == '.' && 967113db2ddSJeff Roberson dp->d_name[1] == '.'; 96824d37c1eSJeff Roberson if (isdotdot && skipparent == 1) 969113db2ddSJeff Roberson continue; 970113db2ddSJeff Roberson if (debug) 971623d7cb6SMatthew D Fleming printf("Directory %ju removing ino %ju name %s\n", 972623d7cb6SMatthew D Fleming (uintmax_t)ino, (uintmax_t)dp->d_ino, dp->d_name); 973113db2ddSJeff Roberson diroff = lblktosize(fs, lbn) + dpoff; 97424d37c1eSJeff Roberson ino_remref(ino, dp->d_ino, diroff, isdotdot); 975113db2ddSJeff Roberson } 9765cc52631SKirk McKusick brelse(bp); 977113db2ddSJeff Roberson } 978113db2ddSJeff Roberson 979113db2ddSJeff Roberson /* 980113db2ddSJeff Roberson * Reclaim an inode, freeing all blocks and decrementing all children's 981113db2ddSJeff Roberson * link counts. Free the inode back to the cg. 982113db2ddSJeff Roberson */ 983113db2ddSJeff Roberson static void 9845cc52631SKirk McKusick ino_reclaim(struct inode *ip, ino_t ino, int mode) 985113db2ddSJeff Roberson { 9865cc52631SKirk McKusick union dinode *dp; 987113db2ddSJeff Roberson uint32_t gen; 988113db2ddSJeff Roberson 9895cc52631SKirk McKusick dp = ip->i_dp; 9901dc349abSEd Maste if (ino == UFS_ROOTINO) 9911dc349abSEd Maste err_suj("Attempting to free UFS_ROOTINO\n"); 992113db2ddSJeff Roberson if (debug) 993623d7cb6SMatthew D Fleming printf("Truncating and freeing ino %ju, nlink %d, mode %o\n", 9945cc52631SKirk McKusick (uintmax_t)ino, DIP(dp, di_nlink), DIP(dp, di_mode)); 995113db2ddSJeff Roberson 996113db2ddSJeff Roberson /* We are freeing an inode or directory. */ 9975cc52631SKirk McKusick if ((DIP(dp, di_mode) & IFMT) == IFDIR) 9985cc52631SKirk McKusick ino_visit(dp, ino, ino_free_children, 0); 9995cc52631SKirk McKusick DIP_SET(dp, di_nlink, 0); 10005cc52631SKirk McKusick ino_visit(dp, ino, blk_free_visit, VISIT_EXT | VISIT_INDIR); 1001113db2ddSJeff Roberson /* Here we have to clear the inode and release any blocks it holds. */ 10025cc52631SKirk McKusick gen = DIP(dp, di_gen); 1003113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 10045cc52631SKirk McKusick bzero(dp, sizeof(struct ufs1_dinode)); 1005113db2ddSJeff Roberson else 10065cc52631SKirk McKusick bzero(dp, sizeof(struct ufs2_dinode)); 10075cc52631SKirk McKusick DIP_SET(dp, di_gen, gen); 10085cc52631SKirk McKusick inodirty(ip); 1009113db2ddSJeff Roberson ino_free(ino, mode); 1010113db2ddSJeff Roberson return; 1011113db2ddSJeff Roberson } 1012113db2ddSJeff Roberson 1013113db2ddSJeff Roberson /* 1014113db2ddSJeff Roberson * Adjust an inode's link count down by one when a directory goes away. 1015113db2ddSJeff Roberson */ 1016113db2ddSJeff Roberson static void 1017113db2ddSJeff Roberson ino_decr(ino_t ino) 1018113db2ddSJeff Roberson { 10195cc52631SKirk McKusick struct inode ip; 10205cc52631SKirk McKusick union dinode *dp; 1021113db2ddSJeff Roberson int reqlink; 1022113db2ddSJeff Roberson int nlink; 1023113db2ddSJeff Roberson int mode; 1024113db2ddSJeff Roberson 10255cc52631SKirk McKusick ginode(ino, &ip); 10265cc52631SKirk McKusick dp = ip.i_dp; 10275cc52631SKirk McKusick nlink = DIP(dp, di_nlink); 10285cc52631SKirk McKusick mode = DIP(dp, di_mode); 1029113db2ddSJeff Roberson if (nlink < 1) 1030edad6026SXin LI err_suj("Inode %d link count %d invalid\n", ino, nlink); 1031113db2ddSJeff Roberson if (mode == 0) 1032edad6026SXin LI err_suj("Inode %d has a link of %d with 0 mode\n", ino, nlink); 1033113db2ddSJeff Roberson nlink--; 1034d8ba45e2SEd Maste if ((mode & IFMT) == IFDIR) 1035113db2ddSJeff Roberson reqlink = 2; 1036113db2ddSJeff Roberson else 1037113db2ddSJeff Roberson reqlink = 1; 1038113db2ddSJeff Roberson if (nlink < reqlink) { 1039113db2ddSJeff Roberson if (debug) 1040623d7cb6SMatthew D Fleming printf("ino %ju not enough links to live %d < %d\n", 1041623d7cb6SMatthew D Fleming (uintmax_t)ino, nlink, reqlink); 10425cc52631SKirk McKusick ino_reclaim(&ip, ino, mode); 10435cc52631SKirk McKusick irelse(&ip); 1044113db2ddSJeff Roberson return; 1045113db2ddSJeff Roberson } 10465cc52631SKirk McKusick DIP_SET(dp, di_nlink, nlink); 10475cc52631SKirk McKusick inodirty(&ip); 10485cc52631SKirk McKusick irelse(&ip); 1049113db2ddSJeff Roberson } 1050113db2ddSJeff Roberson 1051113db2ddSJeff Roberson /* 1052113db2ddSJeff Roberson * Adjust the inode link count to 'nlink'. If the count reaches zero 1053113db2ddSJeff Roberson * free it. 1054113db2ddSJeff Roberson */ 1055113db2ddSJeff Roberson static void 1056113db2ddSJeff Roberson ino_adjust(struct suj_ino *sino) 1057113db2ddSJeff Roberson { 1058113db2ddSJeff Roberson struct jrefrec *rrec; 1059113db2ddSJeff Roberson struct suj_rec *srec; 1060113db2ddSJeff Roberson struct suj_ino *stmp; 10615cc52631SKirk McKusick union dinode *dp; 10625cc52631SKirk McKusick struct inode ip; 1063113db2ddSJeff Roberson nlink_t nlink; 106469921123SKonstantin Belousov nlink_t reqlink; 106524d37c1eSJeff Roberson int recmode; 106624d37c1eSJeff Roberson int isdot; 1067113db2ddSJeff Roberson int mode; 1068113db2ddSJeff Roberson ino_t ino; 1069113db2ddSJeff Roberson 1070113db2ddSJeff Roberson nlink = sino->si_nlink; 1071113db2ddSJeff Roberson ino = sino->si_ino; 1072d8ba45e2SEd Maste mode = sino->si_mode & IFMT; 107324d37c1eSJeff Roberson /* 107424d37c1eSJeff Roberson * If it's a directory with no dot links, it was truncated before 107524d37c1eSJeff Roberson * the name was cleared. We need to clear the dirent that 107624d37c1eSJeff Roberson * points at it. 107724d37c1eSJeff Roberson */ 1078d8ba45e2SEd Maste if (mode == IFDIR && nlink == 1 && sino->si_dotlinks == 0) { 107924d37c1eSJeff Roberson sino->si_nlink = nlink = 0; 108024d37c1eSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 108124d37c1eSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 108224d37c1eSJeff Roberson if (ino_isat(rrec->jr_parent, rrec->jr_diroff, ino, 108324d37c1eSJeff Roberson &recmode, &isdot) == 0) 108424d37c1eSJeff Roberson continue; 108524d37c1eSJeff Roberson ino_clrat(rrec->jr_parent, rrec->jr_diroff, ino); 108624d37c1eSJeff Roberson break; 108724d37c1eSJeff Roberson } 108824d37c1eSJeff Roberson if (srec == NULL) 1089623d7cb6SMatthew D Fleming errx(1, "Directory %ju name not found", (uintmax_t)ino); 109024d37c1eSJeff Roberson } 1091113db2ddSJeff Roberson /* 1092113db2ddSJeff Roberson * If it's a directory with no real names pointing to it go ahead 1093113db2ddSJeff Roberson * and truncate it. This will free any children. 1094113db2ddSJeff Roberson */ 1095d8ba45e2SEd Maste if (mode == IFDIR && nlink - sino->si_dotlinks == 0) { 1096113db2ddSJeff Roberson sino->si_nlink = nlink = 0; 1097113db2ddSJeff Roberson /* 1098113db2ddSJeff Roberson * Mark any .. links so they know not to free this inode 1099113db2ddSJeff Roberson * when they are removed. 1100113db2ddSJeff Roberson */ 1101113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 1102113db2ddSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 1103113db2ddSJeff Roberson if (rrec->jr_diroff == DOTDOT_OFFSET) { 1104113db2ddSJeff Roberson stmp = ino_lookup(rrec->jr_parent, 0); 1105113db2ddSJeff Roberson if (stmp) 1106113db2ddSJeff Roberson ino_setskip(stmp, ino); 1107113db2ddSJeff Roberson } 1108113db2ddSJeff Roberson } 1109113db2ddSJeff Roberson } 11105cc52631SKirk McKusick ginode(ino, &ip); 11115cc52631SKirk McKusick dp = ip.i_dp; 11125cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 1113ed8d06aaSJohn Baldwin if (nlink > UFS_LINK_MAX) 11141c324569SKonstantin Belousov err_suj("ino %ju nlink manipulation error, new %ju, old %d\n", 11155cc52631SKirk McKusick (uintmax_t)ino, (uintmax_t)nlink, DIP(dp, di_nlink)); 1116113db2ddSJeff Roberson if (debug) 11171c324569SKonstantin Belousov printf("Adjusting ino %ju, nlink %ju, old link %d lastmode %o\n", 11185cc52631SKirk McKusick (uintmax_t)ino, (uintmax_t)nlink, DIP(dp, di_nlink), 11191c324569SKonstantin Belousov sino->si_mode); 1120113db2ddSJeff Roberson if (mode == 0) { 1121113db2ddSJeff Roberson if (debug) 1122623d7cb6SMatthew D Fleming printf("ino %ju, zero inode freeing bitmap\n", 1123623d7cb6SMatthew D Fleming (uintmax_t)ino); 1124113db2ddSJeff Roberson ino_free(ino, sino->si_mode); 11255cc52631SKirk McKusick irelse(&ip); 1126113db2ddSJeff Roberson return; 1127113db2ddSJeff Roberson } 1128113db2ddSJeff Roberson /* XXX Should be an assert? */ 1129113db2ddSJeff Roberson if (mode != sino->si_mode && debug) 1130623d7cb6SMatthew D Fleming printf("ino %ju, mode %o != %o\n", 1131623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, sino->si_mode); 1132d8ba45e2SEd Maste if ((mode & IFMT) == IFDIR) 1133113db2ddSJeff Roberson reqlink = 2; 1134113db2ddSJeff Roberson else 1135113db2ddSJeff Roberson reqlink = 1; 1136113db2ddSJeff Roberson /* If the inode doesn't have enough links to live, free it. */ 1137113db2ddSJeff Roberson if (nlink < reqlink) { 1138113db2ddSJeff Roberson if (debug) 11391c324569SKonstantin Belousov printf("ino %ju not enough links to live %ju < %ju\n", 11401c324569SKonstantin Belousov (uintmax_t)ino, (uintmax_t)nlink, 11411c324569SKonstantin Belousov (uintmax_t)reqlink); 11425cc52631SKirk McKusick ino_reclaim(&ip, ino, mode); 11435cc52631SKirk McKusick irelse(&ip); 1144113db2ddSJeff Roberson return; 1145113db2ddSJeff Roberson } 1146113db2ddSJeff Roberson /* If required write the updated link count. */ 11475cc52631SKirk McKusick if (DIP(dp, di_nlink) == nlink) { 1148113db2ddSJeff Roberson if (debug) 1149623d7cb6SMatthew D Fleming printf("ino %ju, link matches, skipping.\n", 1150623d7cb6SMatthew D Fleming (uintmax_t)ino); 11515cc52631SKirk McKusick irelse(&ip); 1152113db2ddSJeff Roberson return; 1153113db2ddSJeff Roberson } 11545cc52631SKirk McKusick DIP_SET(dp, di_nlink, nlink); 11555cc52631SKirk McKusick inodirty(&ip); 11565cc52631SKirk McKusick irelse(&ip); 1157113db2ddSJeff Roberson } 1158113db2ddSJeff Roberson 1159113db2ddSJeff Roberson /* 1160113db2ddSJeff Roberson * Truncate some or all blocks in an indirect, freeing any that are required 1161113db2ddSJeff Roberson * and zeroing the indirect. 1162113db2ddSJeff Roberson */ 1163113db2ddSJeff Roberson static void 11645cc52631SKirk McKusick indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn, 11655cc52631SKirk McKusick union dinode *dp) 1166113db2ddSJeff Roberson { 11675cc52631SKirk McKusick struct bufarea *bp; 1168113db2ddSJeff Roberson ufs_lbn_t lbnadd; 1169113db2ddSJeff Roberson ufs2_daddr_t nblk; 1170113db2ddSJeff Roberson ufs_lbn_t next; 1171113db2ddSJeff Roberson ufs_lbn_t nlbn; 11725cc52631SKirk McKusick int isdirty; 1173113db2ddSJeff Roberson int level; 1174113db2ddSJeff Roberson int i; 1175113db2ddSJeff Roberson 1176113db2ddSJeff Roberson if (blk == 0) 1177113db2ddSJeff Roberson return; 11785cc52631SKirk McKusick isdirty = 0; 1179113db2ddSJeff Roberson level = lbn_level(lbn); 1180113db2ddSJeff Roberson if (level == -1) 1181edad6026SXin LI err_suj("Invalid level for lbn %jd\n", lbn); 1182113db2ddSJeff Roberson lbnadd = 1; 1183113db2ddSJeff Roberson for (i = level; i > 0; i--) 1184113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 11855cc52631SKirk McKusick bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level); 11865cc52631SKirk McKusick if (bp->b_errs != 0) 11875cc52631SKirk McKusick err_suj("indir_trunc: UNRECOVERABLE I/O ERROR"); 1188113db2ddSJeff Roberson for (i = 0; i < NINDIR(fs); i++) { 11895cc52631SKirk McKusick if ((nblk = IBLK(bp, i)) == 0) 1190113db2ddSJeff Roberson continue; 1191113db2ddSJeff Roberson if (level != 0) { 1192113db2ddSJeff Roberson nlbn = (lbn + 1) - (i * lbnadd); 1193113db2ddSJeff Roberson /* 1194113db2ddSJeff Roberson * Calculate the lbn of the next indirect to 1195113db2ddSJeff Roberson * determine if any of this indirect must be 1196113db2ddSJeff Roberson * reclaimed. 1197113db2ddSJeff Roberson */ 1198113db2ddSJeff Roberson next = -(lbn + level) + ((i+1) * lbnadd); 1199113db2ddSJeff Roberson if (next <= lastlbn) 1200113db2ddSJeff Roberson continue; 12015cc52631SKirk McKusick indir_trunc(ino, nlbn, nblk, lastlbn, dp); 1202113db2ddSJeff Roberson /* If all of this indirect was reclaimed, free it. */ 1203113db2ddSJeff Roberson nlbn = next - lbnadd; 1204113db2ddSJeff Roberson if (nlbn < lastlbn) 1205113db2ddSJeff Roberson continue; 1206113db2ddSJeff Roberson } else { 1207113db2ddSJeff Roberson nlbn = -lbn + i * lbnadd; 1208113db2ddSJeff Roberson if (nlbn < lastlbn) 1209113db2ddSJeff Roberson continue; 1210113db2ddSJeff Roberson } 12115cc52631SKirk McKusick isdirty = 1; 1212113db2ddSJeff Roberson blk_free(nblk, 0, fs->fs_frag); 12135cc52631SKirk McKusick IBLK_SET(bp, i, 0); 1214113db2ddSJeff Roberson } 12155cc52631SKirk McKusick if (isdirty) 12165cc52631SKirk McKusick dirty(bp); 12175cc52631SKirk McKusick brelse(bp); 1218113db2ddSJeff Roberson } 1219113db2ddSJeff Roberson 1220113db2ddSJeff Roberson /* 1221113db2ddSJeff Roberson * Truncate an inode to the minimum of the given size or the last populated 1222113db2ddSJeff Roberson * block after any over size have been discarded. The kernel would allocate 1223113db2ddSJeff Roberson * the last block in the file but fsck does not and neither do we. This 1224113db2ddSJeff Roberson * code never extends files, only shrinks them. 1225113db2ddSJeff Roberson */ 1226113db2ddSJeff Roberson static void 1227113db2ddSJeff Roberson ino_trunc(ino_t ino, off_t size) 1228113db2ddSJeff Roberson { 12295cc52631SKirk McKusick struct inode ip; 12305cc52631SKirk McKusick union dinode *dp; 12315cc52631SKirk McKusick struct bufarea *bp; 1232113db2ddSJeff Roberson ufs2_daddr_t bn; 1233113db2ddSJeff Roberson uint64_t totalfrags; 1234113db2ddSJeff Roberson ufs_lbn_t nextlbn; 1235113db2ddSJeff Roberson ufs_lbn_t lastlbn; 1236113db2ddSJeff Roberson ufs_lbn_t tmpval; 1237113db2ddSJeff Roberson ufs_lbn_t lbn; 1238113db2ddSJeff Roberson ufs_lbn_t i; 12395cc52631SKirk McKusick int blksize, frags; 1240113db2ddSJeff Roberson off_t cursize; 1241113db2ddSJeff Roberson off_t off; 1242113db2ddSJeff Roberson int mode; 1243113db2ddSJeff Roberson 12445cc52631SKirk McKusick ginode(ino, &ip); 12455cc52631SKirk McKusick dp = ip.i_dp; 12465cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 12475cc52631SKirk McKusick cursize = DIP(dp, di_size); 1248113db2ddSJeff Roberson if (debug) 1249623d7cb6SMatthew D Fleming printf("Truncating ino %ju, mode %o to size %jd from size %jd\n", 1250623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, size, cursize); 1251113db2ddSJeff Roberson 1252113db2ddSJeff Roberson /* Skip datablocks for short links and devices. */ 1253d8ba45e2SEd Maste if (mode == 0 || mode == IFBLK || mode == IFCHR || 12545cc52631SKirk McKusick (mode == IFLNK && cursize < fs->fs_maxsymlinklen)) { 12555cc52631SKirk McKusick irelse(&ip); 1256113db2ddSJeff Roberson return; 12575cc52631SKirk McKusick } 1258113db2ddSJeff Roberson /* Don't extend. */ 12595cc52631SKirk McKusick if (size > cursize) { 12605cc52631SKirk McKusick irelse(&ip); 12615cc52631SKirk McKusick return; 12625cc52631SKirk McKusick } 12635cc52631SKirk McKusick if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) { 12645cc52631SKirk McKusick if (size > 0) 12655cc52631SKirk McKusick err_suj("Partial truncation of ino %ju snapshot file\n", 12665cc52631SKirk McKusick (uintmax_t)ino); 12675cc52631SKirk McKusick } 1268113db2ddSJeff Roberson lastlbn = lblkno(fs, blkroundup(fs, size)); 12691dc349abSEd Maste for (i = lastlbn; i < UFS_NDADDR; i++) { 12705cc52631SKirk McKusick if ((bn = DIP(dp, di_db[i])) == 0) 1271113db2ddSJeff Roberson continue; 12725cc52631SKirk McKusick blksize = sblksize(fs, cursize, i); 12735cc52631SKirk McKusick blk_free(bn, 0, numfrags(fs, blksize)); 12745cc52631SKirk McKusick DIP_SET(dp, di_db[i], 0); 1275113db2ddSJeff Roberson } 1276113db2ddSJeff Roberson /* 1277113db2ddSJeff Roberson * Follow indirect blocks, freeing anything required. 1278113db2ddSJeff Roberson */ 12791dc349abSEd Maste for (i = 0, tmpval = NINDIR(fs), lbn = UFS_NDADDR; i < UFS_NIADDR; i++, 1280113db2ddSJeff Roberson lbn = nextlbn) { 1281113db2ddSJeff Roberson nextlbn = lbn + tmpval; 1282113db2ddSJeff Roberson tmpval *= NINDIR(fs); 1283113db2ddSJeff Roberson /* If we're not freeing any in this indirect range skip it. */ 1284113db2ddSJeff Roberson if (lastlbn >= nextlbn) 1285113db2ddSJeff Roberson continue; 12865cc52631SKirk McKusick if (DIP(dp, di_ib[i]) == 0) 1287113db2ddSJeff Roberson continue; 12885cc52631SKirk McKusick indir_trunc(ino, -lbn - i, DIP(dp, di_ib[i]), lastlbn, dp); 1289113db2ddSJeff Roberson /* If we freed everything in this indirect free the indir. */ 1290113db2ddSJeff Roberson if (lastlbn > lbn) 1291113db2ddSJeff Roberson continue; 12925cc52631SKirk McKusick blk_free(DIP(dp, di_ib[i]), 0, fs->fs_frag); 12935cc52631SKirk McKusick DIP_SET(dp, di_ib[i], 0); 1294113db2ddSJeff Roberson } 1295113db2ddSJeff Roberson /* 1296113db2ddSJeff Roberson * Now that we've freed any whole blocks that exceed the desired 1297113db2ddSJeff Roberson * truncation size, figure out how many blocks remain and what the 1298113db2ddSJeff Roberson * last populated lbn is. We will set the size to this last lbn 1299113db2ddSJeff Roberson * rather than worrying about allocating the final lbn as the kernel 1300113db2ddSJeff Roberson * would've done. This is consistent with normal fsck behavior. 1301113db2ddSJeff Roberson */ 1302113db2ddSJeff Roberson visitlbn = 0; 13035cc52631SKirk McKusick totalfrags = ino_visit(dp, ino, null_visit, VISIT_INDIR | VISIT_EXT); 1304113db2ddSJeff Roberson if (size > lblktosize(fs, visitlbn + 1)) 1305113db2ddSJeff Roberson size = lblktosize(fs, visitlbn + 1); 1306113db2ddSJeff Roberson /* 1307113db2ddSJeff Roberson * If we're truncating direct blocks we have to adjust frags 1308113db2ddSJeff Roberson * accordingly. 1309113db2ddSJeff Roberson */ 13101dc349abSEd Maste if (visitlbn < UFS_NDADDR && totalfrags) { 1311113db2ddSJeff Roberson long oldspace, newspace; 1312113db2ddSJeff Roberson 13135cc52631SKirk McKusick bn = DIP(dp, di_db[visitlbn]); 1314113db2ddSJeff Roberson if (bn == 0) 1315623d7cb6SMatthew D Fleming err_suj("Bad blk at ino %ju lbn %jd\n", 1316623d7cb6SMatthew D Fleming (uintmax_t)ino, visitlbn); 1317113db2ddSJeff Roberson oldspace = sblksize(fs, cursize, visitlbn); 1318113db2ddSJeff Roberson newspace = sblksize(fs, size, visitlbn); 1319113db2ddSJeff Roberson if (oldspace != newspace) { 1320113db2ddSJeff Roberson bn += numfrags(fs, newspace); 1321113db2ddSJeff Roberson frags = numfrags(fs, oldspace - newspace); 1322113db2ddSJeff Roberson blk_free(bn, 0, frags); 1323113db2ddSJeff Roberson totalfrags -= frags; 1324113db2ddSJeff Roberson } 1325113db2ddSJeff Roberson } 13265cc52631SKirk McKusick DIP_SET(dp, di_blocks, fsbtodb(fs, totalfrags)); 13275cc52631SKirk McKusick DIP_SET(dp, di_size, size); 13285cc52631SKirk McKusick inodirty(&ip); 1329113db2ddSJeff Roberson /* 1330113db2ddSJeff Roberson * If we've truncated into the middle of a block or frag we have 1331113db2ddSJeff Roberson * to zero it here. Otherwise the file could extend into 1332113db2ddSJeff Roberson * uninitialized space later. 1333113db2ddSJeff Roberson */ 1334113db2ddSJeff Roberson off = blkoff(fs, size); 13355cc52631SKirk McKusick if (off && DIP(dp, di_mode) != IFDIR) { 1336113db2ddSJeff Roberson long clrsize; 1337113db2ddSJeff Roberson 13385cc52631SKirk McKusick bn = ino_blkatoff(dp, ino, visitlbn, &frags, NULL); 1339113db2ddSJeff Roberson if (bn == 0) 1340623d7cb6SMatthew D Fleming err_suj("Block missing from ino %ju at lbn %jd\n", 1341623d7cb6SMatthew D Fleming (uintmax_t)ino, visitlbn); 1342113db2ddSJeff Roberson clrsize = frags * fs->fs_fsize; 13435cc52631SKirk McKusick bp = getdatablk(bn, clrsize, BT_DATA); 13445cc52631SKirk McKusick if (bp->b_errs != 0) 13455cc52631SKirk McKusick err_suj("ino_trunc: UNRECOVERABLE I/O ERROR"); 1346113db2ddSJeff Roberson clrsize -= off; 13475cc52631SKirk McKusick bzero(&bp->b_un.b_buf[off], clrsize); 13485cc52631SKirk McKusick dirty(bp); 13495cc52631SKirk McKusick brelse(bp); 1350113db2ddSJeff Roberson } 13515cc52631SKirk McKusick irelse(&ip); 1352113db2ddSJeff Roberson return; 1353113db2ddSJeff Roberson } 1354113db2ddSJeff Roberson 1355113db2ddSJeff Roberson /* 1356113db2ddSJeff Roberson * Process records available for one inode and determine whether the 1357113db2ddSJeff Roberson * link count is correct or needs adjusting. 1358113db2ddSJeff Roberson */ 1359113db2ddSJeff Roberson static void 1360113db2ddSJeff Roberson ino_check(struct suj_ino *sino) 1361113db2ddSJeff Roberson { 1362113db2ddSJeff Roberson struct suj_rec *srec; 1363113db2ddSJeff Roberson struct jrefrec *rrec; 1364113db2ddSJeff Roberson nlink_t dotlinks; 136569921123SKonstantin Belousov nlink_t newlinks; 136669921123SKonstantin Belousov nlink_t removes; 136769921123SKonstantin Belousov nlink_t nlink; 1368113db2ddSJeff Roberson ino_t ino; 1369113db2ddSJeff Roberson int isdot; 1370113db2ddSJeff Roberson int isat; 1371113db2ddSJeff Roberson int mode; 1372113db2ddSJeff Roberson 1373113db2ddSJeff Roberson if (sino->si_hasrecs == 0) 1374113db2ddSJeff Roberson return; 1375113db2ddSJeff Roberson ino = sino->si_ino; 1376113db2ddSJeff Roberson rrec = (struct jrefrec *)TAILQ_FIRST(&sino->si_recs)->sr_rec; 1377113db2ddSJeff Roberson nlink = rrec->jr_nlink; 1378113db2ddSJeff Roberson newlinks = 0; 1379113db2ddSJeff Roberson dotlinks = 0; 1380113db2ddSJeff Roberson removes = sino->si_nlinkadj; 1381113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 1382113db2ddSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 1383113db2ddSJeff Roberson isat = ino_isat(rrec->jr_parent, rrec->jr_diroff, 1384113db2ddSJeff Roberson rrec->jr_ino, &mode, &isdot); 1385d8ba45e2SEd Maste if (isat && (mode & IFMT) != (rrec->jr_mode & IFMT)) 1386edad6026SXin LI err_suj("Inode mode/directory type mismatch %o != %o\n", 1387113db2ddSJeff Roberson mode, rrec->jr_mode); 1388113db2ddSJeff Roberson if (debug) 13891c324569SKonstantin Belousov printf("jrefrec: op %d ino %ju, nlink %ju, parent %ju, " 1390113db2ddSJeff Roberson "diroff %jd, mode %o, isat %d, isdot %d\n", 1391623d7cb6SMatthew D Fleming rrec->jr_op, (uintmax_t)rrec->jr_ino, 13921c324569SKonstantin Belousov (uintmax_t)rrec->jr_nlink, 13931c324569SKonstantin Belousov (uintmax_t)rrec->jr_parent, 13941c324569SKonstantin Belousov (uintmax_t)rrec->jr_diroff, 1395623d7cb6SMatthew D Fleming rrec->jr_mode, isat, isdot); 1396d8ba45e2SEd Maste mode = rrec->jr_mode & IFMT; 1397113db2ddSJeff Roberson if (rrec->jr_op == JOP_REMREF) 1398113db2ddSJeff Roberson removes++; 1399113db2ddSJeff Roberson newlinks += isat; 1400113db2ddSJeff Roberson if (isdot) 1401113db2ddSJeff Roberson dotlinks += isat; 1402113db2ddSJeff Roberson } 1403113db2ddSJeff Roberson /* 1404113db2ddSJeff Roberson * The number of links that remain are the starting link count 1405113db2ddSJeff Roberson * subtracted by the total number of removes with the total 1406113db2ddSJeff Roberson * links discovered back in. An incomplete remove thus 1407113db2ddSJeff Roberson * makes no change to the link count but an add increases 1408113db2ddSJeff Roberson * by one. 1409113db2ddSJeff Roberson */ 1410113db2ddSJeff Roberson if (debug) 14111c324569SKonstantin Belousov printf( 14121c324569SKonstantin Belousov "ino %ju nlink %ju newlinks %ju removes %ju dotlinks %ju\n", 14131c324569SKonstantin Belousov (uintmax_t)ino, (uintmax_t)nlink, (uintmax_t)newlinks, 14141c324569SKonstantin Belousov (uintmax_t)removes, (uintmax_t)dotlinks); 1415113db2ddSJeff Roberson nlink += newlinks; 1416113db2ddSJeff Roberson nlink -= removes; 1417113db2ddSJeff Roberson sino->si_linkadj = 1; 1418113db2ddSJeff Roberson sino->si_nlink = nlink; 1419113db2ddSJeff Roberson sino->si_dotlinks = dotlinks; 1420113db2ddSJeff Roberson sino->si_mode = mode; 1421113db2ddSJeff Roberson ino_adjust(sino); 1422113db2ddSJeff Roberson } 1423113db2ddSJeff Roberson 1424113db2ddSJeff Roberson /* 1425113db2ddSJeff Roberson * Process records available for one block and determine whether it is 1426113db2ddSJeff Roberson * still allocated and whether the owning inode needs to be updated or 1427113db2ddSJeff Roberson * a free completed. 1428113db2ddSJeff Roberson */ 1429113db2ddSJeff Roberson static void 1430113db2ddSJeff Roberson blk_check(struct suj_blk *sblk) 1431113db2ddSJeff Roberson { 1432113db2ddSJeff Roberson struct suj_rec *srec; 1433113db2ddSJeff Roberson struct jblkrec *brec; 1434113db2ddSJeff Roberson struct suj_ino *sino; 1435113db2ddSJeff Roberson ufs2_daddr_t blk; 1436113db2ddSJeff Roberson int mask; 1437113db2ddSJeff Roberson int frags; 1438113db2ddSJeff Roberson int isat; 1439113db2ddSJeff Roberson 1440113db2ddSJeff Roberson /* 1441113db2ddSJeff Roberson * Each suj_blk actually contains records for any fragments in that 1442113db2ddSJeff Roberson * block. As a result we must evaluate each record individually. 1443113db2ddSJeff Roberson */ 1444113db2ddSJeff Roberson sino = NULL; 1445113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 1446113db2ddSJeff Roberson brec = (struct jblkrec *)srec->sr_rec; 1447113db2ddSJeff Roberson frags = brec->jb_frags; 1448113db2ddSJeff Roberson blk = brec->jb_blkno + brec->jb_oldfrags; 1449113db2ddSJeff Roberson isat = blk_isat(brec->jb_ino, brec->jb_lbn, blk, &frags); 1450113db2ddSJeff Roberson if (sino == NULL || sino->si_ino != brec->jb_ino) { 1451113db2ddSJeff Roberson sino = ino_lookup(brec->jb_ino, 1); 1452113db2ddSJeff Roberson sino->si_blkadj = 1; 1453113db2ddSJeff Roberson } 1454113db2ddSJeff Roberson if (debug) 1455623d7cb6SMatthew D Fleming printf("op %d blk %jd ino %ju lbn %jd frags %d isat %d (%d)\n", 1456623d7cb6SMatthew D Fleming brec->jb_op, blk, (uintmax_t)brec->jb_ino, 1457623d7cb6SMatthew D Fleming brec->jb_lbn, brec->jb_frags, isat, frags); 1458113db2ddSJeff Roberson /* 1459113db2ddSJeff Roberson * If we found the block at this address we still have to 1460113db2ddSJeff Roberson * determine if we need to free the tail end that was 1461113db2ddSJeff Roberson * added by adding contiguous fragments from the same block. 1462113db2ddSJeff Roberson */ 1463113db2ddSJeff Roberson if (isat == 1) { 1464113db2ddSJeff Roberson if (frags == brec->jb_frags) 1465113db2ddSJeff Roberson continue; 1466113db2ddSJeff Roberson mask = blk_freemask(blk, brec->jb_ino, brec->jb_lbn, 1467113db2ddSJeff Roberson brec->jb_frags); 1468113db2ddSJeff Roberson mask >>= frags; 1469113db2ddSJeff Roberson blk += frags; 1470113db2ddSJeff Roberson frags = brec->jb_frags - frags; 1471113db2ddSJeff Roberson blk_free(blk, mask, frags); 1472113db2ddSJeff Roberson continue; 1473113db2ddSJeff Roberson } 1474113db2ddSJeff Roberson /* 1475113db2ddSJeff Roberson * The block wasn't found, attempt to free it. It won't be 1476113db2ddSJeff Roberson * freed if it was actually reallocated. If this was an 1477113db2ddSJeff Roberson * allocation we don't want to follow indirects as they 1478113db2ddSJeff Roberson * may not be written yet. Any children of the indirect will 1479113db2ddSJeff Roberson * have their own records. If it's a free we need to 1480113db2ddSJeff Roberson * recursively free children. 1481113db2ddSJeff Roberson */ 1482113db2ddSJeff Roberson blk_free_lbn(blk, brec->jb_ino, brec->jb_lbn, brec->jb_frags, 1483113db2ddSJeff Roberson brec->jb_op == JOP_FREEBLK); 1484113db2ddSJeff Roberson } 1485113db2ddSJeff Roberson } 1486113db2ddSJeff Roberson 1487113db2ddSJeff Roberson /* 1488113db2ddSJeff Roberson * Walk the list of inode records for this cg and resolve moved and duplicate 1489113db2ddSJeff Roberson * inode references now that we have a complete picture. 1490113db2ddSJeff Roberson */ 1491113db2ddSJeff Roberson static void 1492113db2ddSJeff Roberson cg_build(struct suj_cg *sc) 1493113db2ddSJeff Roberson { 1494113db2ddSJeff Roberson struct suj_ino *sino; 1495113db2ddSJeff Roberson int i; 1496113db2ddSJeff Roberson 14975cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1498113db2ddSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) 1499113db2ddSJeff Roberson ino_build(sino); 1500113db2ddSJeff Roberson } 1501113db2ddSJeff Roberson 1502113db2ddSJeff Roberson /* 1503113db2ddSJeff Roberson * Handle inodes requiring truncation. This must be done prior to 1504113db2ddSJeff Roberson * looking up any inodes in directories. 1505113db2ddSJeff Roberson */ 1506113db2ddSJeff Roberson static void 1507113db2ddSJeff Roberson cg_trunc(struct suj_cg *sc) 1508113db2ddSJeff Roberson { 1509113db2ddSJeff Roberson struct suj_ino *sino; 1510113db2ddSJeff Roberson int i; 1511113db2ddSJeff Roberson 15125cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) { 1513280e091aSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) { 1514113db2ddSJeff Roberson if (sino->si_trunc) { 1515113db2ddSJeff Roberson ino_trunc(sino->si_ino, 1516113db2ddSJeff Roberson sino->si_trunc->jt_size); 1517280e091aSJeff Roberson sino->si_blkadj = 0; 1518113db2ddSJeff Roberson sino->si_trunc = NULL; 1519113db2ddSJeff Roberson } 1520280e091aSJeff Roberson if (sino->si_blkadj) 1521280e091aSJeff Roberson ino_adjblks(sino); 1522280e091aSJeff Roberson } 1523280e091aSJeff Roberson } 1524113db2ddSJeff Roberson } 1525113db2ddSJeff Roberson 1526364e7245SKonstantin Belousov static void 1527364e7245SKonstantin Belousov cg_adj_blk(struct suj_cg *sc) 1528364e7245SKonstantin Belousov { 1529364e7245SKonstantin Belousov struct suj_ino *sino; 1530364e7245SKonstantin Belousov int i; 1531364e7245SKonstantin Belousov 15325cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) { 1533364e7245SKonstantin Belousov LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) { 1534364e7245SKonstantin Belousov if (sino->si_blkadj) 1535364e7245SKonstantin Belousov ino_adjblks(sino); 1536364e7245SKonstantin Belousov } 1537364e7245SKonstantin Belousov } 1538364e7245SKonstantin Belousov } 1539364e7245SKonstantin Belousov 1540113db2ddSJeff Roberson /* 1541113db2ddSJeff Roberson * Free any partially allocated blocks and then resolve inode block 1542113db2ddSJeff Roberson * counts. 1543113db2ddSJeff Roberson */ 1544113db2ddSJeff Roberson static void 1545113db2ddSJeff Roberson cg_check_blk(struct suj_cg *sc) 1546113db2ddSJeff Roberson { 1547113db2ddSJeff Roberson struct suj_blk *sblk; 1548113db2ddSJeff Roberson int i; 1549113db2ddSJeff Roberson 1550113db2ddSJeff Roberson 15515cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1552113db2ddSJeff Roberson LIST_FOREACH(sblk, &sc->sc_blkhash[i], sb_next) 1553113db2ddSJeff Roberson blk_check(sblk); 1554113db2ddSJeff Roberson } 1555113db2ddSJeff Roberson 1556113db2ddSJeff Roberson /* 1557113db2ddSJeff Roberson * Walk the list of inode records for this cg, recovering any 1558113db2ddSJeff Roberson * changes which were not complete at the time of crash. 1559113db2ddSJeff Roberson */ 1560113db2ddSJeff Roberson static void 1561113db2ddSJeff Roberson cg_check_ino(struct suj_cg *sc) 1562113db2ddSJeff Roberson { 1563113db2ddSJeff Roberson struct suj_ino *sino; 1564113db2ddSJeff Roberson int i; 1565113db2ddSJeff Roberson 15665cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1567113db2ddSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) 1568113db2ddSJeff Roberson ino_check(sino); 1569113db2ddSJeff Roberson } 1570113db2ddSJeff Roberson 1571113db2ddSJeff Roberson static void 1572113db2ddSJeff Roberson cg_apply(void (*apply)(struct suj_cg *)) 1573113db2ddSJeff Roberson { 1574113db2ddSJeff Roberson struct suj_cg *scg; 1575113db2ddSJeff Roberson int i; 1576113db2ddSJeff Roberson 15775cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1578113db2ddSJeff Roberson LIST_FOREACH(scg, &cghash[i], sc_next) 1579113db2ddSJeff Roberson apply(scg); 1580113db2ddSJeff Roberson } 1581113db2ddSJeff Roberson 1582113db2ddSJeff Roberson /* 1583113db2ddSJeff Roberson * Process the unlinked but referenced file list. Freeing all inodes. 1584113db2ddSJeff Roberson */ 1585113db2ddSJeff Roberson static void 1586113db2ddSJeff Roberson ino_unlinked(void) 1587113db2ddSJeff Roberson { 15885cc52631SKirk McKusick struct inode ip; 15895cc52631SKirk McKusick union dinode *dp; 1590113db2ddSJeff Roberson uint16_t mode; 1591113db2ddSJeff Roberson ino_t inon; 1592113db2ddSJeff Roberson ino_t ino; 1593113db2ddSJeff Roberson 1594113db2ddSJeff Roberson ino = fs->fs_sujfree; 1595113db2ddSJeff Roberson fs->fs_sujfree = 0; 1596113db2ddSJeff Roberson while (ino != 0) { 15975cc52631SKirk McKusick ginode(ino, &ip); 15985cc52631SKirk McKusick dp = ip.i_dp; 15995cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 16005cc52631SKirk McKusick inon = DIP(dp, di_freelink); 16015cc52631SKirk McKusick DIP_SET(dp, di_freelink, 0); 16025cc52631SKirk McKusick inodirty(&ip); 1603113db2ddSJeff Roberson /* 1604113db2ddSJeff Roberson * XXX Should this be an errx? 1605113db2ddSJeff Roberson */ 16065cc52631SKirk McKusick if (DIP(dp, di_nlink) == 0) { 1607113db2ddSJeff Roberson if (debug) 1608623d7cb6SMatthew D Fleming printf("Freeing unlinked ino %ju mode %o\n", 1609e25a029eSMatthew D Fleming (uintmax_t)ino, mode); 16105cc52631SKirk McKusick ino_reclaim(&ip, ino, mode); 1611113db2ddSJeff Roberson } else if (debug) 1612623d7cb6SMatthew D Fleming printf("Skipping ino %ju mode %o with link %d\n", 16135cc52631SKirk McKusick (uintmax_t)ino, mode, DIP(dp, di_nlink)); 1614113db2ddSJeff Roberson ino = inon; 16155cc52631SKirk McKusick irelse(&ip); 1616113db2ddSJeff Roberson } 1617113db2ddSJeff Roberson } 1618113db2ddSJeff Roberson 1619113db2ddSJeff Roberson /* 1620113db2ddSJeff Roberson * Append a new record to the list of records requiring processing. 1621113db2ddSJeff Roberson */ 1622113db2ddSJeff Roberson static void 1623113db2ddSJeff Roberson ino_append(union jrec *rec) 1624113db2ddSJeff Roberson { 1625113db2ddSJeff Roberson struct jrefrec *refrec; 1626113db2ddSJeff Roberson struct jmvrec *mvrec; 1627113db2ddSJeff Roberson struct suj_ino *sino; 1628113db2ddSJeff Roberson struct suj_rec *srec; 1629113db2ddSJeff Roberson 1630113db2ddSJeff Roberson mvrec = &rec->rec_jmvrec; 1631113db2ddSJeff Roberson refrec = &rec->rec_jrefrec; 1632113db2ddSJeff Roberson if (debug && mvrec->jm_op == JOP_MVREF) 16331c324569SKonstantin Belousov printf("ino move: ino %ju, parent %ju, " 16341c324569SKonstantin Belousov "diroff %jd, oldoff %jd\n", 16351c324569SKonstantin Belousov (uintmax_t)mvrec->jm_ino, (uintmax_t)mvrec->jm_parent, 16361c324569SKonstantin Belousov (uintmax_t)mvrec->jm_newoff, (uintmax_t)mvrec->jm_oldoff); 1637113db2ddSJeff Roberson else if (debug && 1638113db2ddSJeff Roberson (refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF)) 16391c324569SKonstantin Belousov printf("ino ref: op %d, ino %ju, nlink %ju, " 16401c324569SKonstantin Belousov "parent %ju, diroff %jd\n", 16411c324569SKonstantin Belousov refrec->jr_op, (uintmax_t)refrec->jr_ino, 16421c324569SKonstantin Belousov (uintmax_t)refrec->jr_nlink, 16431c324569SKonstantin Belousov (uintmax_t)refrec->jr_parent, (uintmax_t)refrec->jr_diroff); 1644113db2ddSJeff Roberson sino = ino_lookup(((struct jrefrec *)rec)->jr_ino, 1); 1645113db2ddSJeff Roberson sino->si_hasrecs = 1; 1646113db2ddSJeff Roberson srec = errmalloc(sizeof(*srec)); 1647113db2ddSJeff Roberson srec->sr_rec = rec; 1648113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_newrecs, srec, sr_next); 1649113db2ddSJeff Roberson } 1650113db2ddSJeff Roberson 1651113db2ddSJeff Roberson /* 1652113db2ddSJeff Roberson * Add a reference adjustment to the sino list and eliminate dups. The 1653113db2ddSJeff Roberson * primary loop in ino_build_ref() checks for dups but new ones may be 1654113db2ddSJeff Roberson * created as a result of offset adjustments. 1655113db2ddSJeff Roberson */ 1656113db2ddSJeff Roberson static void 1657113db2ddSJeff Roberson ino_add_ref(struct suj_ino *sino, struct suj_rec *srec) 1658113db2ddSJeff Roberson { 1659113db2ddSJeff Roberson struct jrefrec *refrec; 1660113db2ddSJeff Roberson struct suj_rec *srn; 1661113db2ddSJeff Roberson struct jrefrec *rrn; 1662113db2ddSJeff Roberson 1663113db2ddSJeff Roberson refrec = (struct jrefrec *)srec->sr_rec; 1664113db2ddSJeff Roberson /* 1665113db2ddSJeff Roberson * We walk backwards so that the oldest link count is preserved. If 1666113db2ddSJeff Roberson * an add record conflicts with a remove keep the remove. Redundant 1667113db2ddSJeff Roberson * removes are eliminated in ino_build_ref. Otherwise we keep the 1668113db2ddSJeff Roberson * oldest record at a given location. 1669113db2ddSJeff Roberson */ 1670113db2ddSJeff Roberson for (srn = TAILQ_LAST(&sino->si_recs, srechd); srn; 1671113db2ddSJeff Roberson srn = TAILQ_PREV(srn, srechd, sr_next)) { 1672113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 1673113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 1674113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 1675113db2ddSJeff Roberson continue; 1676113db2ddSJeff Roberson if (rrn->jr_op == JOP_REMREF || refrec->jr_op == JOP_ADDREF) { 1677113db2ddSJeff Roberson rrn->jr_mode = refrec->jr_mode; 1678113db2ddSJeff Roberson return; 1679113db2ddSJeff Roberson } 1680113db2ddSJeff Roberson /* 1681113db2ddSJeff Roberson * Adding a remove. 1682113db2ddSJeff Roberson * 1683113db2ddSJeff Roberson * Replace the record in place with the old nlink in case 1684113db2ddSJeff Roberson * we replace the head of the list. Abandon srec as a dup. 1685113db2ddSJeff Roberson */ 1686113db2ddSJeff Roberson refrec->jr_nlink = rrn->jr_nlink; 1687113db2ddSJeff Roberson srn->sr_rec = srec->sr_rec; 1688113db2ddSJeff Roberson return; 1689113db2ddSJeff Roberson } 1690113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_recs, srec, sr_next); 1691113db2ddSJeff Roberson } 1692113db2ddSJeff Roberson 1693113db2ddSJeff Roberson /* 1694113db2ddSJeff Roberson * Create a duplicate of a reference at a previous location. 1695113db2ddSJeff Roberson */ 1696113db2ddSJeff Roberson static void 1697113db2ddSJeff Roberson ino_dup_ref(struct suj_ino *sino, struct jrefrec *refrec, off_t diroff) 1698113db2ddSJeff Roberson { 1699113db2ddSJeff Roberson struct jrefrec *rrn; 1700113db2ddSJeff Roberson struct suj_rec *srn; 1701113db2ddSJeff Roberson 1702113db2ddSJeff Roberson rrn = errmalloc(sizeof(*refrec)); 1703113db2ddSJeff Roberson *rrn = *refrec; 1704113db2ddSJeff Roberson rrn->jr_op = JOP_ADDREF; 1705113db2ddSJeff Roberson rrn->jr_diroff = diroff; 1706113db2ddSJeff Roberson srn = errmalloc(sizeof(*srn)); 1707113db2ddSJeff Roberson srn->sr_rec = (union jrec *)rrn; 1708113db2ddSJeff Roberson ino_add_ref(sino, srn); 1709113db2ddSJeff Roberson } 1710113db2ddSJeff Roberson 1711113db2ddSJeff Roberson /* 1712113db2ddSJeff Roberson * Add a reference to the list at all known locations. We follow the offset 1713113db2ddSJeff Roberson * changes for a single instance and create duplicate add refs at each so 1714113db2ddSJeff Roberson * that we can tolerate any version of the directory block. Eliminate 1715113db2ddSJeff Roberson * removes which collide with adds that are seen in the journal. They should 1716113db2ddSJeff Roberson * not adjust the link count down. 1717113db2ddSJeff Roberson */ 1718113db2ddSJeff Roberson static void 1719113db2ddSJeff Roberson ino_build_ref(struct suj_ino *sino, struct suj_rec *srec) 1720113db2ddSJeff Roberson { 1721113db2ddSJeff Roberson struct jrefrec *refrec; 1722113db2ddSJeff Roberson struct jmvrec *mvrec; 1723113db2ddSJeff Roberson struct suj_rec *srp; 1724113db2ddSJeff Roberson struct suj_rec *srn; 1725113db2ddSJeff Roberson struct jrefrec *rrn; 1726113db2ddSJeff Roberson off_t diroff; 1727113db2ddSJeff Roberson 1728113db2ddSJeff Roberson refrec = (struct jrefrec *)srec->sr_rec; 1729113db2ddSJeff Roberson /* 1730113db2ddSJeff Roberson * Search for a mvrec that matches this offset. Whether it's an add 1731113db2ddSJeff Roberson * or a remove we can delete the mvref after creating a dup record in 1732113db2ddSJeff Roberson * the old location. 1733113db2ddSJeff Roberson */ 1734113db2ddSJeff Roberson if (!TAILQ_EMPTY(&sino->si_movs)) { 1735113db2ddSJeff Roberson diroff = refrec->jr_diroff; 1736113db2ddSJeff Roberson for (srn = TAILQ_LAST(&sino->si_movs, srechd); srn; srn = srp) { 1737113db2ddSJeff Roberson srp = TAILQ_PREV(srn, srechd, sr_next); 1738113db2ddSJeff Roberson mvrec = (struct jmvrec *)srn->sr_rec; 1739113db2ddSJeff Roberson if (mvrec->jm_parent != refrec->jr_parent || 1740113db2ddSJeff Roberson mvrec->jm_newoff != diroff) 1741113db2ddSJeff Roberson continue; 1742113db2ddSJeff Roberson diroff = mvrec->jm_oldoff; 1743113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_movs, srn, sr_next); 1744edad6026SXin LI free(srn); 1745113db2ddSJeff Roberson ino_dup_ref(sino, refrec, diroff); 1746113db2ddSJeff Roberson } 1747113db2ddSJeff Roberson } 1748113db2ddSJeff Roberson /* 1749113db2ddSJeff Roberson * If a remove wasn't eliminated by an earlier add just append it to 1750113db2ddSJeff Roberson * the list. 1751113db2ddSJeff Roberson */ 1752113db2ddSJeff Roberson if (refrec->jr_op == JOP_REMREF) { 1753113db2ddSJeff Roberson ino_add_ref(sino, srec); 1754113db2ddSJeff Roberson return; 1755113db2ddSJeff Roberson } 1756113db2ddSJeff Roberson /* 1757113db2ddSJeff Roberson * Walk the list of records waiting to be added to the list. We 1758113db2ddSJeff Roberson * must check for moves that apply to our current offset and remove 1759113db2ddSJeff Roberson * them from the list. Remove any duplicates to eliminate removes 1760113db2ddSJeff Roberson * with corresponding adds. 1761113db2ddSJeff Roberson */ 1762113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(srn, &sino->si_newrecs, sr_next, srp) { 1763113db2ddSJeff Roberson switch (srn->sr_rec->rec_jrefrec.jr_op) { 1764113db2ddSJeff Roberson case JOP_ADDREF: 1765113db2ddSJeff Roberson /* 1766113db2ddSJeff Roberson * This should actually be an error we should 1767113db2ddSJeff Roberson * have a remove for every add journaled. 1768113db2ddSJeff Roberson */ 1769113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 1770113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 1771113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 1772113db2ddSJeff Roberson break; 1773113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 1774113db2ddSJeff Roberson break; 1775113db2ddSJeff Roberson case JOP_REMREF: 1776113db2ddSJeff Roberson /* 1777113db2ddSJeff Roberson * Once we remove the current iteration of the 1778113db2ddSJeff Roberson * record at this address we're done. 1779113db2ddSJeff Roberson */ 1780113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 1781113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 1782113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 1783113db2ddSJeff Roberson break; 1784113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 1785113db2ddSJeff Roberson ino_add_ref(sino, srec); 1786113db2ddSJeff Roberson return; 1787113db2ddSJeff Roberson case JOP_MVREF: 1788113db2ddSJeff Roberson /* 1789113db2ddSJeff Roberson * Update our diroff based on any moves that match 1790113db2ddSJeff Roberson * and remove the move. 1791113db2ddSJeff Roberson */ 1792113db2ddSJeff Roberson mvrec = (struct jmvrec *)srn->sr_rec; 1793113db2ddSJeff Roberson if (mvrec->jm_parent != refrec->jr_parent || 1794113db2ddSJeff Roberson mvrec->jm_oldoff != refrec->jr_diroff) 1795113db2ddSJeff Roberson break; 1796113db2ddSJeff Roberson ino_dup_ref(sino, refrec, mvrec->jm_oldoff); 1797113db2ddSJeff Roberson refrec->jr_diroff = mvrec->jm_newoff; 1798113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 1799113db2ddSJeff Roberson break; 1800113db2ddSJeff Roberson default: 1801edad6026SXin LI err_suj("ino_build_ref: Unknown op %d\n", 1802113db2ddSJeff Roberson srn->sr_rec->rec_jrefrec.jr_op); 1803113db2ddSJeff Roberson } 1804113db2ddSJeff Roberson } 1805113db2ddSJeff Roberson ino_add_ref(sino, srec); 1806113db2ddSJeff Roberson } 1807113db2ddSJeff Roberson 1808113db2ddSJeff Roberson /* 1809113db2ddSJeff Roberson * Walk the list of new records and add them in-order resolving any 1810113db2ddSJeff Roberson * dups and adjusted offsets. 1811113db2ddSJeff Roberson */ 1812113db2ddSJeff Roberson static void 1813113db2ddSJeff Roberson ino_build(struct suj_ino *sino) 1814113db2ddSJeff Roberson { 1815113db2ddSJeff Roberson struct suj_rec *srec; 1816113db2ddSJeff Roberson 1817113db2ddSJeff Roberson while ((srec = TAILQ_FIRST(&sino->si_newrecs)) != NULL) { 1818113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srec, sr_next); 1819113db2ddSJeff Roberson switch (srec->sr_rec->rec_jrefrec.jr_op) { 1820113db2ddSJeff Roberson case JOP_ADDREF: 1821113db2ddSJeff Roberson case JOP_REMREF: 1822113db2ddSJeff Roberson ino_build_ref(sino, srec); 1823113db2ddSJeff Roberson break; 1824113db2ddSJeff Roberson case JOP_MVREF: 1825113db2ddSJeff Roberson /* 1826113db2ddSJeff Roberson * Add this mvrec to the queue of pending mvs. 1827113db2ddSJeff Roberson */ 1828113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next); 1829113db2ddSJeff Roberson break; 1830113db2ddSJeff Roberson default: 1831edad6026SXin LI err_suj("ino_build: Unknown op %d\n", 1832113db2ddSJeff Roberson srec->sr_rec->rec_jrefrec.jr_op); 1833113db2ddSJeff Roberson } 1834113db2ddSJeff Roberson } 1835113db2ddSJeff Roberson if (TAILQ_EMPTY(&sino->si_recs)) 1836113db2ddSJeff Roberson sino->si_hasrecs = 0; 1837113db2ddSJeff Roberson } 1838113db2ddSJeff Roberson 1839113db2ddSJeff Roberson /* 1840113db2ddSJeff Roberson * Modify journal records so they refer to the base block number 1841113db2ddSJeff Roberson * and a start and end frag range. This is to facilitate the discovery 1842113db2ddSJeff Roberson * of overlapping fragment allocations. 1843113db2ddSJeff Roberson */ 1844113db2ddSJeff Roberson static void 1845113db2ddSJeff Roberson blk_build(struct jblkrec *blkrec) 1846113db2ddSJeff Roberson { 1847113db2ddSJeff Roberson struct suj_rec *srec; 1848113db2ddSJeff Roberson struct suj_blk *sblk; 1849113db2ddSJeff Roberson struct jblkrec *blkrn; 1850113db2ddSJeff Roberson ufs2_daddr_t blk; 1851113db2ddSJeff Roberson int frag; 1852113db2ddSJeff Roberson 1853113db2ddSJeff Roberson if (debug) 1854113db2ddSJeff Roberson printf("blk_build: op %d blkno %jd frags %d oldfrags %d " 18551c324569SKonstantin Belousov "ino %ju lbn %jd\n", 18561c324569SKonstantin Belousov blkrec->jb_op, (uintmax_t)blkrec->jb_blkno, 18571c324569SKonstantin Belousov blkrec->jb_frags, blkrec->jb_oldfrags, 18581c324569SKonstantin Belousov (uintmax_t)blkrec->jb_ino, (uintmax_t)blkrec->jb_lbn); 1859113db2ddSJeff Roberson 1860113db2ddSJeff Roberson blk = blknum(fs, blkrec->jb_blkno); 1861113db2ddSJeff Roberson frag = fragnum(fs, blkrec->jb_blkno); 1862113db2ddSJeff Roberson sblk = blk_lookup(blk, 1); 1863113db2ddSJeff Roberson /* 1864113db2ddSJeff Roberson * Rewrite the record using oldfrags to indicate the offset into 1865113db2ddSJeff Roberson * the block. Leave jb_frags as the actual allocated count. 1866113db2ddSJeff Roberson */ 1867113db2ddSJeff Roberson blkrec->jb_blkno -= frag; 1868113db2ddSJeff Roberson blkrec->jb_oldfrags = frag; 1869113db2ddSJeff Roberson if (blkrec->jb_oldfrags + blkrec->jb_frags > fs->fs_frag) 1870edad6026SXin LI err_suj("Invalid fragment count %d oldfrags %d\n", 1871113db2ddSJeff Roberson blkrec->jb_frags, frag); 1872113db2ddSJeff Roberson /* 1873113db2ddSJeff Roberson * Detect dups. If we detect a dup we always discard the oldest 1874113db2ddSJeff Roberson * record as it is superseded by the new record. This speeds up 1875113db2ddSJeff Roberson * later stages but also eliminates free records which are used 1876113db2ddSJeff Roberson * to indicate that the contents of indirects can be trusted. 1877113db2ddSJeff Roberson */ 1878113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 1879113db2ddSJeff Roberson blkrn = (struct jblkrec *)srec->sr_rec; 1880113db2ddSJeff Roberson if (blkrn->jb_ino != blkrec->jb_ino || 1881113db2ddSJeff Roberson blkrn->jb_lbn != blkrec->jb_lbn || 1882113db2ddSJeff Roberson blkrn->jb_blkno != blkrec->jb_blkno || 1883113db2ddSJeff Roberson blkrn->jb_frags != blkrec->jb_frags || 1884113db2ddSJeff Roberson blkrn->jb_oldfrags != blkrec->jb_oldfrags) 1885113db2ddSJeff Roberson continue; 1886113db2ddSJeff Roberson if (debug) 1887113db2ddSJeff Roberson printf("Removed dup.\n"); 1888113db2ddSJeff Roberson /* Discard the free which is a dup with an alloc. */ 1889113db2ddSJeff Roberson if (blkrec->jb_op == JOP_FREEBLK) 1890113db2ddSJeff Roberson return; 1891113db2ddSJeff Roberson TAILQ_REMOVE(&sblk->sb_recs, srec, sr_next); 1892113db2ddSJeff Roberson free(srec); 1893113db2ddSJeff Roberson break; 1894113db2ddSJeff Roberson } 1895113db2ddSJeff Roberson srec = errmalloc(sizeof(*srec)); 1896113db2ddSJeff Roberson srec->sr_rec = (union jrec *)blkrec; 1897113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sblk->sb_recs, srec, sr_next); 1898113db2ddSJeff Roberson } 1899113db2ddSJeff Roberson 1900113db2ddSJeff Roberson static void 1901113db2ddSJeff Roberson ino_build_trunc(struct jtrncrec *rec) 1902113db2ddSJeff Roberson { 1903113db2ddSJeff Roberson struct suj_ino *sino; 1904113db2ddSJeff Roberson 1905113db2ddSJeff Roberson if (debug) 19061c324569SKonstantin Belousov printf("ino_build_trunc: op %d ino %ju, size %jd\n", 19071c324569SKonstantin Belousov rec->jt_op, (uintmax_t)rec->jt_ino, 19081c324569SKonstantin Belousov (uintmax_t)rec->jt_size); 1909113db2ddSJeff Roberson sino = ino_lookup(rec->jt_ino, 1); 1910280e091aSJeff Roberson if (rec->jt_op == JOP_SYNC) { 1911280e091aSJeff Roberson sino->si_trunc = NULL; 1912280e091aSJeff Roberson return; 1913280e091aSJeff Roberson } 1914280e091aSJeff Roberson if (sino->si_trunc == NULL || sino->si_trunc->jt_size > rec->jt_size) 1915113db2ddSJeff Roberson sino->si_trunc = rec; 1916113db2ddSJeff Roberson } 1917113db2ddSJeff Roberson 1918113db2ddSJeff Roberson /* 1919113db2ddSJeff Roberson * Build up tables of the operations we need to recover. 1920113db2ddSJeff Roberson */ 1921113db2ddSJeff Roberson static void 1922113db2ddSJeff Roberson suj_build(void) 1923113db2ddSJeff Roberson { 1924113db2ddSJeff Roberson struct suj_seg *seg; 1925113db2ddSJeff Roberson union jrec *rec; 1926113db2ddSJeff Roberson int off; 19274235bafaSPedro F. Giffuni int i; 1928113db2ddSJeff Roberson 1929113db2ddSJeff Roberson TAILQ_FOREACH(seg, &allsegs, ss_next) { 1930113db2ddSJeff Roberson if (debug) 1931113db2ddSJeff Roberson printf("seg %jd has %d records, oldseq %jd.\n", 1932113db2ddSJeff Roberson seg->ss_rec.jsr_seq, seg->ss_rec.jsr_cnt, 1933113db2ddSJeff Roberson seg->ss_rec.jsr_oldest); 1934113db2ddSJeff Roberson off = 0; 1935113db2ddSJeff Roberson rec = (union jrec *)seg->ss_blk; 1936113db2ddSJeff Roberson for (i = 0; i < seg->ss_rec.jsr_cnt; off += JREC_SIZE, rec++) { 1937113db2ddSJeff Roberson /* skip the segrec. */ 19380947d19aSKonstantin Belousov if ((off % real_dev_bsize) == 0) 1939113db2ddSJeff Roberson continue; 1940113db2ddSJeff Roberson switch (rec->rec_jrefrec.jr_op) { 1941113db2ddSJeff Roberson case JOP_ADDREF: 1942113db2ddSJeff Roberson case JOP_REMREF: 1943113db2ddSJeff Roberson case JOP_MVREF: 1944113db2ddSJeff Roberson ino_append(rec); 1945113db2ddSJeff Roberson break; 1946113db2ddSJeff Roberson case JOP_NEWBLK: 1947113db2ddSJeff Roberson case JOP_FREEBLK: 1948113db2ddSJeff Roberson blk_build((struct jblkrec *)rec); 1949113db2ddSJeff Roberson break; 1950113db2ddSJeff Roberson case JOP_TRUNC: 195185e9da38SJeff Roberson case JOP_SYNC: 1952113db2ddSJeff Roberson ino_build_trunc((struct jtrncrec *)rec); 1953113db2ddSJeff Roberson break; 1954113db2ddSJeff Roberson default: 1955edad6026SXin LI err_suj("Unknown journal operation %d (%d)\n", 1956113db2ddSJeff Roberson rec->rec_jrefrec.jr_op, off); 1957113db2ddSJeff Roberson } 1958113db2ddSJeff Roberson i++; 1959113db2ddSJeff Roberson } 1960113db2ddSJeff Roberson } 1961113db2ddSJeff Roberson } 1962113db2ddSJeff Roberson 1963113db2ddSJeff Roberson /* 1964113db2ddSJeff Roberson * Prune the journal segments to those we care about based on the 1965113db2ddSJeff Roberson * oldest sequence in the newest segment. Order the segment list 1966113db2ddSJeff Roberson * based on sequence number. 1967113db2ddSJeff Roberson */ 1968113db2ddSJeff Roberson static void 1969113db2ddSJeff Roberson suj_prune(void) 1970113db2ddSJeff Roberson { 1971113db2ddSJeff Roberson struct suj_seg *seg; 1972113db2ddSJeff Roberson struct suj_seg *segn; 1973113db2ddSJeff Roberson uint64_t newseq; 1974113db2ddSJeff Roberson int discard; 1975113db2ddSJeff Roberson 1976113db2ddSJeff Roberson if (debug) 1977113db2ddSJeff Roberson printf("Pruning up to %jd\n", oldseq); 1978113db2ddSJeff Roberson /* First free the expired segments. */ 1979113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 1980113db2ddSJeff Roberson if (seg->ss_rec.jsr_seq >= oldseq) 1981113db2ddSJeff Roberson continue; 1982113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 1983113db2ddSJeff Roberson free(seg->ss_blk); 1984113db2ddSJeff Roberson free(seg); 1985113db2ddSJeff Roberson } 1986113db2ddSJeff Roberson /* Next ensure that segments are ordered properly. */ 1987113db2ddSJeff Roberson seg = TAILQ_FIRST(&allsegs); 1988113db2ddSJeff Roberson if (seg == NULL) { 1989113db2ddSJeff Roberson if (debug) 1990113db2ddSJeff Roberson printf("Empty journal\n"); 1991113db2ddSJeff Roberson return; 1992113db2ddSJeff Roberson } 1993113db2ddSJeff Roberson newseq = seg->ss_rec.jsr_seq; 1994113db2ddSJeff Roberson for (;;) { 1995113db2ddSJeff Roberson seg = TAILQ_LAST(&allsegs, seghd); 1996113db2ddSJeff Roberson if (seg->ss_rec.jsr_seq >= newseq) 1997113db2ddSJeff Roberson break; 1998113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 1999113db2ddSJeff Roberson TAILQ_INSERT_HEAD(&allsegs, seg, ss_next); 2000113db2ddSJeff Roberson newseq = seg->ss_rec.jsr_seq; 2001113db2ddSJeff Roberson 2002113db2ddSJeff Roberson } 2003edad6026SXin LI if (newseq != oldseq) { 20042db62a6bSJeff Roberson TAILQ_FOREACH(seg, &allsegs, ss_next) { 20052db62a6bSJeff Roberson printf("%jd, ", seg->ss_rec.jsr_seq); 20062db62a6bSJeff Roberson } 20072db62a6bSJeff Roberson printf("\n"); 2008edad6026SXin LI err_suj("Journal file sequence mismatch %jd != %jd\n", 2009113db2ddSJeff Roberson newseq, oldseq); 2010edad6026SXin LI } 2011113db2ddSJeff Roberson /* 2012113db2ddSJeff Roberson * The kernel may asynchronously write segments which can create 2013113db2ddSJeff Roberson * gaps in the sequence space. Throw away any segments after the 2014113db2ddSJeff Roberson * gap as the kernel guarantees only those that are contiguously 2015113db2ddSJeff Roberson * reachable are marked as completed. 2016113db2ddSJeff Roberson */ 2017113db2ddSJeff Roberson discard = 0; 2018113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2019113db2ddSJeff Roberson if (!discard && newseq++ == seg->ss_rec.jsr_seq) { 2020113db2ddSJeff Roberson jrecs += seg->ss_rec.jsr_cnt; 20210947d19aSKonstantin Belousov jbytes += seg->ss_rec.jsr_blocks * real_dev_bsize; 2022113db2ddSJeff Roberson continue; 2023113db2ddSJeff Roberson } 2024113db2ddSJeff Roberson discard = 1; 2025113db2ddSJeff Roberson if (debug) 2026113db2ddSJeff Roberson printf("Journal order mismatch %jd != %jd pruning\n", 2027113db2ddSJeff Roberson newseq-1, seg->ss_rec.jsr_seq); 2028113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2029113db2ddSJeff Roberson free(seg->ss_blk); 2030113db2ddSJeff Roberson free(seg); 2031113db2ddSJeff Roberson } 2032113db2ddSJeff Roberson if (debug) 2033113db2ddSJeff Roberson printf("Processing journal segments from %jd to %jd\n", 2034113db2ddSJeff Roberson oldseq, newseq-1); 2035113db2ddSJeff Roberson } 2036113db2ddSJeff Roberson 2037113db2ddSJeff Roberson /* 2038113db2ddSJeff Roberson * Verify the journal inode before attempting to read records. 2039113db2ddSJeff Roberson */ 2040113db2ddSJeff Roberson static int 20415cc52631SKirk McKusick suj_verifyino(union dinode *dp) 2042113db2ddSJeff Roberson { 2043113db2ddSJeff Roberson 20445cc52631SKirk McKusick if (DIP(dp, di_nlink) != 1) { 2045623d7cb6SMatthew D Fleming printf("Invalid link count %d for journal inode %ju\n", 20465cc52631SKirk McKusick DIP(dp, di_nlink), (uintmax_t)sujino); 2047113db2ddSJeff Roberson return (-1); 2048113db2ddSJeff Roberson } 2049113db2ddSJeff Roberson 20505cc52631SKirk McKusick if ((DIP(dp, di_flags) & (SF_IMMUTABLE | SF_NOUNLINK)) != 2051113db2ddSJeff Roberson (SF_IMMUTABLE | SF_NOUNLINK)) { 2052623d7cb6SMatthew D Fleming printf("Invalid flags 0x%X for journal inode %ju\n", 20535cc52631SKirk McKusick DIP(dp, di_flags), (uintmax_t)sujino); 2054113db2ddSJeff Roberson return (-1); 2055113db2ddSJeff Roberson } 2056113db2ddSJeff Roberson 20575cc52631SKirk McKusick if (DIP(dp, di_mode) != (IFREG | IREAD)) { 2058623d7cb6SMatthew D Fleming printf("Invalid mode %o for journal inode %ju\n", 20595cc52631SKirk McKusick DIP(dp, di_mode), (uintmax_t)sujino); 2060113db2ddSJeff Roberson return (-1); 2061113db2ddSJeff Roberson } 2062113db2ddSJeff Roberson 20635cc52631SKirk McKusick if (DIP(dp, di_size) < SUJ_MIN) { 2064623d7cb6SMatthew D Fleming printf("Invalid size %jd for journal inode %ju\n", 20655cc52631SKirk McKusick DIP(dp, di_size), (uintmax_t)sujino); 2066113db2ddSJeff Roberson return (-1); 2067113db2ddSJeff Roberson } 2068113db2ddSJeff Roberson 20695cc52631SKirk McKusick if (DIP(dp, di_modrev) != fs->fs_mtime) { 2070113db2ddSJeff Roberson printf("Journal timestamp does not match fs mount time\n"); 2071113db2ddSJeff Roberson return (-1); 2072113db2ddSJeff Roberson } 2073113db2ddSJeff Roberson 2074113db2ddSJeff Roberson return (0); 2075113db2ddSJeff Roberson } 2076113db2ddSJeff Roberson 2077113db2ddSJeff Roberson struct jblocks { 2078113db2ddSJeff Roberson struct jextent *jb_extent; /* Extent array. */ 2079113db2ddSJeff Roberson int jb_avail; /* Available extents. */ 2080113db2ddSJeff Roberson int jb_used; /* Last used extent. */ 2081113db2ddSJeff Roberson int jb_head; /* Allocator head. */ 2082113db2ddSJeff Roberson int jb_off; /* Allocator extent offset. */ 2083113db2ddSJeff Roberson }; 2084113db2ddSJeff Roberson struct jextent { 2085113db2ddSJeff Roberson ufs2_daddr_t je_daddr; /* Disk block address. */ 2086113db2ddSJeff Roberson int je_blocks; /* Disk block count. */ 2087113db2ddSJeff Roberson }; 2088113db2ddSJeff Roberson 20897703a6ffSScott Long static struct jblocks *suj_jblocks; 2090113db2ddSJeff Roberson 2091113db2ddSJeff Roberson static struct jblocks * 2092113db2ddSJeff Roberson jblocks_create(void) 2093113db2ddSJeff Roberson { 2094113db2ddSJeff Roberson struct jblocks *jblocks; 2095113db2ddSJeff Roberson int size; 2096113db2ddSJeff Roberson 2097113db2ddSJeff Roberson jblocks = errmalloc(sizeof(*jblocks)); 2098113db2ddSJeff Roberson jblocks->jb_avail = 10; 2099113db2ddSJeff Roberson jblocks->jb_used = 0; 2100113db2ddSJeff Roberson jblocks->jb_head = 0; 2101113db2ddSJeff Roberson jblocks->jb_off = 0; 2102113db2ddSJeff Roberson size = sizeof(struct jextent) * jblocks->jb_avail; 2103113db2ddSJeff Roberson jblocks->jb_extent = errmalloc(size); 2104113db2ddSJeff Roberson bzero(jblocks->jb_extent, size); 2105113db2ddSJeff Roberson 2106113db2ddSJeff Roberson return (jblocks); 2107113db2ddSJeff Roberson } 2108113db2ddSJeff Roberson 2109113db2ddSJeff Roberson /* 2110113db2ddSJeff Roberson * Return the next available disk block and the amount of contiguous 2111113db2ddSJeff Roberson * free space it contains. 2112113db2ddSJeff Roberson */ 2113113db2ddSJeff Roberson static ufs2_daddr_t 2114113db2ddSJeff Roberson jblocks_next(struct jblocks *jblocks, int bytes, int *actual) 2115113db2ddSJeff Roberson { 2116113db2ddSJeff Roberson struct jextent *jext; 2117113db2ddSJeff Roberson ufs2_daddr_t daddr; 2118113db2ddSJeff Roberson int freecnt; 2119113db2ddSJeff Roberson int blocks; 2120113db2ddSJeff Roberson 21215cc52631SKirk McKusick blocks = btodb(bytes); 2122113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_head]; 2123113db2ddSJeff Roberson freecnt = jext->je_blocks - jblocks->jb_off; 2124113db2ddSJeff Roberson if (freecnt == 0) { 2125113db2ddSJeff Roberson jblocks->jb_off = 0; 2126113db2ddSJeff Roberson if (++jblocks->jb_head > jblocks->jb_used) 2127113db2ddSJeff Roberson return (0); 2128113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_head]; 2129113db2ddSJeff Roberson freecnt = jext->je_blocks; 2130113db2ddSJeff Roberson } 2131113db2ddSJeff Roberson if (freecnt > blocks) 2132113db2ddSJeff Roberson freecnt = blocks; 21335cc52631SKirk McKusick *actual = dbtob(freecnt); 2134113db2ddSJeff Roberson daddr = jext->je_daddr + jblocks->jb_off; 2135113db2ddSJeff Roberson 2136113db2ddSJeff Roberson return (daddr); 2137113db2ddSJeff Roberson } 2138113db2ddSJeff Roberson 2139113db2ddSJeff Roberson /* 2140113db2ddSJeff Roberson * Advance the allocation head by a specified number of bytes, consuming 2141113db2ddSJeff Roberson * one journal segment. 2142113db2ddSJeff Roberson */ 2143113db2ddSJeff Roberson static void 2144113db2ddSJeff Roberson jblocks_advance(struct jblocks *jblocks, int bytes) 2145113db2ddSJeff Roberson { 2146113db2ddSJeff Roberson 21475cc52631SKirk McKusick jblocks->jb_off += btodb(bytes); 2148113db2ddSJeff Roberson } 2149113db2ddSJeff Roberson 2150113db2ddSJeff Roberson static void 2151113db2ddSJeff Roberson jblocks_destroy(struct jblocks *jblocks) 2152113db2ddSJeff Roberson { 2153113db2ddSJeff Roberson 2154113db2ddSJeff Roberson free(jblocks->jb_extent); 2155113db2ddSJeff Roberson free(jblocks); 2156113db2ddSJeff Roberson } 2157113db2ddSJeff Roberson 2158113db2ddSJeff Roberson static void 2159113db2ddSJeff Roberson jblocks_add(struct jblocks *jblocks, ufs2_daddr_t daddr, int blocks) 2160113db2ddSJeff Roberson { 2161113db2ddSJeff Roberson struct jextent *jext; 2162113db2ddSJeff Roberson int size; 2163113db2ddSJeff Roberson 2164113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_used]; 2165113db2ddSJeff Roberson /* Adding the first block. */ 2166113db2ddSJeff Roberson if (jext->je_daddr == 0) { 2167113db2ddSJeff Roberson jext->je_daddr = daddr; 2168113db2ddSJeff Roberson jext->je_blocks = blocks; 2169113db2ddSJeff Roberson return; 2170113db2ddSJeff Roberson } 2171113db2ddSJeff Roberson /* Extending the last extent. */ 2172113db2ddSJeff Roberson if (jext->je_daddr + jext->je_blocks == daddr) { 2173113db2ddSJeff Roberson jext->je_blocks += blocks; 2174113db2ddSJeff Roberson return; 2175113db2ddSJeff Roberson } 2176113db2ddSJeff Roberson /* Adding a new extent. */ 2177113db2ddSJeff Roberson if (++jblocks->jb_used == jblocks->jb_avail) { 2178113db2ddSJeff Roberson jblocks->jb_avail *= 2; 2179113db2ddSJeff Roberson size = sizeof(struct jextent) * jblocks->jb_avail; 2180113db2ddSJeff Roberson jext = errmalloc(size); 2181113db2ddSJeff Roberson bzero(jext, size); 2182113db2ddSJeff Roberson bcopy(jblocks->jb_extent, jext, 2183113db2ddSJeff Roberson sizeof(struct jextent) * jblocks->jb_used); 2184113db2ddSJeff Roberson free(jblocks->jb_extent); 2185113db2ddSJeff Roberson jblocks->jb_extent = jext; 2186113db2ddSJeff Roberson } 2187113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_used]; 2188113db2ddSJeff Roberson jext->je_daddr = daddr; 2189113db2ddSJeff Roberson jext->je_blocks = blocks; 2190113db2ddSJeff Roberson 2191113db2ddSJeff Roberson return; 2192113db2ddSJeff Roberson } 2193113db2ddSJeff Roberson 2194113db2ddSJeff Roberson /* 2195113db2ddSJeff Roberson * Add a file block from the journal to the extent map. We can't read 2196113db2ddSJeff Roberson * each file block individually because the kernel treats it as a circular 2197113db2ddSJeff Roberson * buffer and segments may span mutliple contiguous blocks. 2198113db2ddSJeff Roberson */ 2199113db2ddSJeff Roberson static void 2200113db2ddSJeff Roberson suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 2201113db2ddSJeff Roberson { 2202113db2ddSJeff Roberson 2203113db2ddSJeff Roberson jblocks_add(suj_jblocks, fsbtodb(fs, blk), fsbtodb(fs, frags)); 2204113db2ddSJeff Roberson } 2205113db2ddSJeff Roberson 2206113db2ddSJeff Roberson static void 2207113db2ddSJeff Roberson suj_read(void) 2208113db2ddSJeff Roberson { 2209113db2ddSJeff Roberson uint8_t block[1 * 1024 * 1024]; 2210113db2ddSJeff Roberson struct suj_seg *seg; 2211113db2ddSJeff Roberson struct jsegrec *recn; 2212113db2ddSJeff Roberson struct jsegrec *rec; 2213113db2ddSJeff Roberson ufs2_daddr_t blk; 2214113db2ddSJeff Roberson int readsize; 22154235bafaSPedro F. Giffuni int blocks; 2216113db2ddSJeff Roberson int recsize; 2217113db2ddSJeff Roberson int size; 221811ec5dd0SPedro F. Giffuni int i; 2219113db2ddSJeff Roberson 2220113db2ddSJeff Roberson /* 2221113db2ddSJeff Roberson * Read records until we exhaust the journal space. If we find 2222113db2ddSJeff Roberson * an invalid record we start searching for a valid segment header 2223113db2ddSJeff Roberson * at the next block. This is because we don't have a head/tail 2224113db2ddSJeff Roberson * pointer and must recover the information indirectly. At the gap 2225113db2ddSJeff Roberson * between the head and tail we won't necessarily have a valid 2226113db2ddSJeff Roberson * segment. 2227113db2ddSJeff Roberson */ 2228113db2ddSJeff Roberson restart: 2229113db2ddSJeff Roberson for (;;) { 2230113db2ddSJeff Roberson size = sizeof(block); 2231113db2ddSJeff Roberson blk = jblocks_next(suj_jblocks, size, &readsize); 2232113db2ddSJeff Roberson if (blk == 0) 2233113db2ddSJeff Roberson return; 2234113db2ddSJeff Roberson size = readsize; 2235113db2ddSJeff Roberson /* 2236113db2ddSJeff Roberson * Read 1MB at a time and scan for records within this block. 2237113db2ddSJeff Roberson */ 22385cc52631SKirk McKusick if (pread(fsreadfd, &block, size, dbtob(blk)) != size) { 2239edad6026SXin LI err_suj("Error reading journal block %jd\n", 2240113db2ddSJeff Roberson (intmax_t)blk); 2241edad6026SXin LI } 2242113db2ddSJeff Roberson for (rec = (void *)block; size; size -= recsize, 2243113db2ddSJeff Roberson rec = (struct jsegrec *)((uintptr_t)rec + recsize)) { 22440947d19aSKonstantin Belousov recsize = real_dev_bsize; 2245113db2ddSJeff Roberson if (rec->jsr_time != fs->fs_mtime) { 22465cc52631SKirk McKusick #ifdef notdef 2247113db2ddSJeff Roberson if (debug) 2248113db2ddSJeff Roberson printf("Rec time %jd != fs mtime %jd\n", 2249113db2ddSJeff Roberson rec->jsr_time, fs->fs_mtime); 22505cc52631SKirk McKusick #endif 2251113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2252113db2ddSJeff Roberson continue; 2253113db2ddSJeff Roberson } 2254113db2ddSJeff Roberson if (rec->jsr_cnt == 0) { 2255113db2ddSJeff Roberson if (debug) 2256113db2ddSJeff Roberson printf("Found illegal count %d\n", 2257113db2ddSJeff Roberson rec->jsr_cnt); 2258113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2259113db2ddSJeff Roberson continue; 2260113db2ddSJeff Roberson } 2261113db2ddSJeff Roberson blocks = rec->jsr_blocks; 22620947d19aSKonstantin Belousov recsize = blocks * real_dev_bsize; 2263113db2ddSJeff Roberson if (recsize > size) { 2264113db2ddSJeff Roberson /* 2265113db2ddSJeff Roberson * We may just have run out of buffer, restart 2266113db2ddSJeff Roberson * the loop to re-read from this spot. 2267113db2ddSJeff Roberson */ 2268113db2ddSJeff Roberson if (size < fs->fs_bsize && 2269113db2ddSJeff Roberson size != readsize && 2270113db2ddSJeff Roberson recsize <= fs->fs_bsize) 2271113db2ddSJeff Roberson goto restart; 2272113db2ddSJeff Roberson if (debug) 2273113db2ddSJeff Roberson printf("Found invalid segsize %d > %d\n", 2274113db2ddSJeff Roberson recsize, size); 22750947d19aSKonstantin Belousov recsize = real_dev_bsize; 2276113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2277113db2ddSJeff Roberson continue; 2278113db2ddSJeff Roberson } 2279113db2ddSJeff Roberson /* 2280113db2ddSJeff Roberson * Verify that all blocks in the segment are present. 2281113db2ddSJeff Roberson */ 2282113db2ddSJeff Roberson for (i = 1; i < blocks; i++) { 22830947d19aSKonstantin Belousov recn = (void *)((uintptr_t)rec) + i * 22840947d19aSKonstantin Belousov real_dev_bsize; 2285113db2ddSJeff Roberson if (recn->jsr_seq == rec->jsr_seq && 2286113db2ddSJeff Roberson recn->jsr_time == rec->jsr_time) 2287113db2ddSJeff Roberson continue; 2288113db2ddSJeff Roberson if (debug) 2289113db2ddSJeff Roberson printf("Incomplete record %jd (%d)\n", 2290113db2ddSJeff Roberson rec->jsr_seq, i); 22910947d19aSKonstantin Belousov recsize = i * real_dev_bsize; 2292113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2293113db2ddSJeff Roberson goto restart; 2294113db2ddSJeff Roberson } 2295113db2ddSJeff Roberson seg = errmalloc(sizeof(*seg)); 2296113db2ddSJeff Roberson seg->ss_blk = errmalloc(recsize); 2297113db2ddSJeff Roberson seg->ss_rec = *rec; 2298113db2ddSJeff Roberson bcopy((void *)rec, seg->ss_blk, recsize); 2299113db2ddSJeff Roberson if (rec->jsr_oldest > oldseq) 2300113db2ddSJeff Roberson oldseq = rec->jsr_oldest; 2301113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&allsegs, seg, ss_next); 2302113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2303113db2ddSJeff Roberson } 2304113db2ddSJeff Roberson } 2305113db2ddSJeff Roberson } 2306113db2ddSJeff Roberson 2307113db2ddSJeff Roberson /* 2308113db2ddSJeff Roberson * Orchestrate the verification of a filesystem via the softupdates journal. 2309113db2ddSJeff Roberson */ 2310113db2ddSJeff Roberson int 2311113db2ddSJeff Roberson suj_check(const char *filesys) 2312113db2ddSJeff Roberson { 23135cc52631SKirk McKusick struct inodesc idesc; 23145cc52631SKirk McKusick struct csum *cgsum; 2315113db2ddSJeff Roberson union dinode *jip; 23165cc52631SKirk McKusick struct inode ip; 2317113db2ddSJeff Roberson uint64_t blocks; 23185cc52631SKirk McKusick int i, retval; 2319edad6026SXin LI struct suj_seg *seg; 2320edad6026SXin LI struct suj_seg *segn; 2321113db2ddSJeff Roberson 23227703a6ffSScott Long initsuj(); 2323a6bbdf81SKirk McKusick fs = &sblock; 23245cc52631SKirk McKusick if (real_dev_bsize == 0 && ioctl(fsreadfd, DIOCGSECTORSIZE, 2325a6bbdf81SKirk McKusick &real_dev_bsize) == -1) 2326a6bbdf81SKirk McKusick real_dev_bsize = secsize; 2327a6bbdf81SKirk McKusick if (debug) 2328a6bbdf81SKirk McKusick printf("dev_bsize %u\n", real_dev_bsize); 2329edad6026SXin LI 2330edad6026SXin LI /* 2331edad6026SXin LI * Set an exit point when SUJ check failed 2332edad6026SXin LI */ 2333edad6026SXin LI retval = setjmp(jmpbuf); 2334edad6026SXin LI if (retval != 0) { 2335edad6026SXin LI pwarn("UNEXPECTED SU+J INCONSISTENCY\n"); 2336edad6026SXin LI TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2337edad6026SXin LI TAILQ_REMOVE(&allsegs, seg, ss_next); 2338edad6026SXin LI free(seg->ss_blk); 2339edad6026SXin LI free(seg); 2340edad6026SXin LI } 2341edad6026SXin LI if (reply("FALLBACK TO FULL FSCK") == 0) { 2342edad6026SXin LI ckfini(0); 2343edad6026SXin LI exit(EEXIT); 2344edad6026SXin LI } else 2345edad6026SXin LI return (-1); 2346edad6026SXin LI } 2347edad6026SXin LI 2348113db2ddSJeff Roberson /* 23495cc52631SKirk McKusick * Search the root directory for the SUJ_FILE. 2350113db2ddSJeff Roberson */ 23515cc52631SKirk McKusick idesc.id_type = DATA; 23525cc52631SKirk McKusick idesc.id_fix = IGNORE; 23535cc52631SKirk McKusick idesc.id_number = UFS_ROOTINO; 23545cc52631SKirk McKusick idesc.id_func = findino; 23555cc52631SKirk McKusick idesc.id_name = SUJ_FILE; 23565cc52631SKirk McKusick ginode(UFS_ROOTINO, &ip); 23575cc52631SKirk McKusick if ((ckinode(ip.i_dp, &idesc) & FOUND) == FOUND) { 23585cc52631SKirk McKusick sujino = idesc.id_parent; 23595cc52631SKirk McKusick irelse(&ip); 23605cc52631SKirk McKusick } else { 2361edad6026SXin LI printf("Journal inode removed. Use tunefs to re-create.\n"); 2362edad6026SXin LI sblock.fs_flags &= ~FS_SUJ; 2363edad6026SXin LI sblock.fs_sujfree = 0; 23645cc52631SKirk McKusick irelse(&ip); 2365edad6026SXin LI return (-1); 2366edad6026SXin LI } 2367113db2ddSJeff Roberson /* 2368113db2ddSJeff Roberson * Fetch the journal inode and verify it. 2369113db2ddSJeff Roberson */ 23705cc52631SKirk McKusick ginode(sujino, &ip); 23715cc52631SKirk McKusick jip = ip.i_dp; 2372113db2ddSJeff Roberson printf("** SU+J Recovering %s\n", filesys); 23735cc52631SKirk McKusick if (suj_verifyino(jip) != 0 || (!preen && !reply("USE JOURNAL"))) { 23745cc52631SKirk McKusick irelse(&ip); 2375113db2ddSJeff Roberson return (-1); 23765cc52631SKirk McKusick } 2377113db2ddSJeff Roberson /* 2378113db2ddSJeff Roberson * Build a list of journal blocks in jblocks before parsing the 2379113db2ddSJeff Roberson * available journal blocks in with suj_read(). 2380113db2ddSJeff Roberson */ 2381623d7cb6SMatthew D Fleming printf("** Reading %jd byte journal from inode %ju.\n", 2382623d7cb6SMatthew D Fleming DIP(jip, di_size), (uintmax_t)sujino); 2383113db2ddSJeff Roberson suj_jblocks = jblocks_create(); 2384113db2ddSJeff Roberson blocks = ino_visit(jip, sujino, suj_add_block, 0); 2385edad6026SXin LI if (blocks != numfrags(fs, DIP(jip, di_size))) { 2386623d7cb6SMatthew D Fleming printf("Sparse journal inode %ju.\n", (uintmax_t)sujino); 23875cc52631SKirk McKusick irelse(&ip); 2388edad6026SXin LI return (-1); 2389edad6026SXin LI } 23905cc52631SKirk McKusick irelse(&ip); 2391113db2ddSJeff Roberson suj_read(); 2392113db2ddSJeff Roberson jblocks_destroy(suj_jblocks); 2393113db2ddSJeff Roberson suj_jblocks = NULL; 2394113db2ddSJeff Roberson if (preen || reply("RECOVER")) { 2395113db2ddSJeff Roberson printf("** Building recovery table.\n"); 2396113db2ddSJeff Roberson suj_prune(); 2397113db2ddSJeff Roberson suj_build(); 2398113db2ddSJeff Roberson cg_apply(cg_build); 2399113db2ddSJeff Roberson printf("** Resolving unreferenced inode list.\n"); 2400113db2ddSJeff Roberson ino_unlinked(); 2401113db2ddSJeff Roberson printf("** Processing journal entries.\n"); 2402113db2ddSJeff Roberson cg_apply(cg_trunc); 2403113db2ddSJeff Roberson cg_apply(cg_check_blk); 2404364e7245SKonstantin Belousov cg_apply(cg_adj_blk); 2405113db2ddSJeff Roberson cg_apply(cg_check_ino); 2406113db2ddSJeff Roberson } 2407edad6026SXin LI if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0) 2408113db2ddSJeff Roberson return (0); 2409113db2ddSJeff Roberson /* 24105cc52631SKirk McKusick * Recompute the fs summary info from correct cs summaries. 2411113db2ddSJeff Roberson */ 24125cc52631SKirk McKusick bzero(&fs->fs_cstotal, sizeof(struct csum_total)); 24135cc52631SKirk McKusick for (i = 0; i < fs->fs_ncg; i++) { 24145cc52631SKirk McKusick cgsum = &fs->fs_cs(fs, i); 24155cc52631SKirk McKusick fs->fs_cstotal.cs_nffree += cgsum->cs_nffree; 24165cc52631SKirk McKusick fs->fs_cstotal.cs_nbfree += cgsum->cs_nbfree; 24175cc52631SKirk McKusick fs->fs_cstotal.cs_nifree += cgsum->cs_nifree; 24185cc52631SKirk McKusick fs->fs_cstotal.cs_ndir += cgsum->cs_ndir; 24195cc52631SKirk McKusick } 24205cc52631SKirk McKusick fs->fs_pendinginodes = 0; 24215cc52631SKirk McKusick fs->fs_pendingblocks = 0; 24225cc52631SKirk McKusick fs->fs_clean = 1; 24235cc52631SKirk McKusick fs->fs_time = time(NULL); 24245cc52631SKirk McKusick fs->fs_mtime = time(NULL); 24255cc52631SKirk McKusick sbdirty(); 24265cc52631SKirk McKusick ckfini(1); 2427edad6026SXin LI if (jrecs > 0 || jbytes > 0) { 2428113db2ddSJeff Roberson printf("** %jd journal records in %jd bytes for %.2f%% utilization\n", 2429113db2ddSJeff Roberson jrecs, jbytes, ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100); 2430113db2ddSJeff Roberson printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd frags.\n", 2431113db2ddSJeff Roberson freeinos, freedir, freeblocks, freefrags); 2432edad6026SXin LI } 2433113db2ddSJeff Roberson 2434113db2ddSJeff Roberson return (0); 2435113db2ddSJeff Roberson } 24367703a6ffSScott Long 24377703a6ffSScott Long static void 24387703a6ffSScott Long initsuj(void) 24397703a6ffSScott Long { 24407703a6ffSScott Long int i; 24417703a6ffSScott Long 24425cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 24437703a6ffSScott Long LIST_INIT(&cghash[i]); 24447703a6ffSScott Long lastcg = NULL; 24457703a6ffSScott Long TAILQ_INIT(&allsegs); 24467703a6ffSScott Long oldseq = 0; 24477703a6ffSScott Long fs = NULL; 24487703a6ffSScott Long sujino = 0; 24497703a6ffSScott Long freefrags = 0; 24507703a6ffSScott Long freeblocks = 0; 24517703a6ffSScott Long freeinos = 0; 24527703a6ffSScott Long freedir = 0; 24537703a6ffSScott Long jbytes = 0; 24547703a6ffSScott Long jrecs = 0; 24557703a6ffSScott Long suj_jblocks = NULL; 24567703a6ffSScott Long } 2457