1113db2ddSJeff Roberson /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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 38d485c77fSKonstantin Belousov #include <ufs/ufs/extattr.h> 39d485c77fSKonstantin 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); 18640647558SChuck Silvers if (!check_cgmagic(cgx, cgbp)) 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 324460ed610SKirk McKusick * we're concerned with. If any fragments have been 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 /* 382460ed610SKirk McKusick * Check to see if the requested block is available. 383460ed610SKirk McKusick * We can just check in the cylinder-group maps as 384460ed610SKirk McKusick * they will only have usable blocks in them. 385460ed610SKirk McKusick */ 386460ed610SKirk McKusick ufs2_daddr_t 387e5d0d1c5SKirk McKusick suj_checkblkavail(ufs2_daddr_t blkno, long frags) 388460ed610SKirk McKusick { 389460ed610SKirk McKusick struct bufarea *cgbp; 390460ed610SKirk McKusick struct cg *cgp; 391460ed610SKirk McKusick ufs2_daddr_t j, k, baseblk; 392460ed610SKirk McKusick long cg; 393460ed610SKirk McKusick 39452f97104SKirk McKusick if ((u_int64_t)blkno > sblock.fs_size) 39552f97104SKirk McKusick return (0); 396460ed610SKirk McKusick cg = dtog(&sblock, blkno); 397460ed610SKirk McKusick cgbp = cglookup(cg); 398460ed610SKirk McKusick cgp = cgbp->b_un.b_cg; 39940647558SChuck Silvers if (!check_cgmagic(cg, cgbp)) 400460ed610SKirk McKusick return (-((cg + 1) * sblock.fs_fpg - sblock.fs_frag)); 401460ed610SKirk McKusick baseblk = dtogd(&sblock, blkno); 402460ed610SKirk McKusick for (j = 0; j <= sblock.fs_frag - frags; j++) { 403460ed610SKirk McKusick if (!isset(cg_blksfree(cgp), baseblk + j)) 404460ed610SKirk McKusick continue; 405460ed610SKirk McKusick for (k = 1; k < frags; k++) 406460ed610SKirk McKusick if (!isset(cg_blksfree(cgp), baseblk + j + k)) 407460ed610SKirk McKusick break; 408460ed610SKirk McKusick if (k < frags) { 409460ed610SKirk McKusick j += k; 410460ed610SKirk McKusick continue; 411460ed610SKirk McKusick } 412460ed610SKirk McKusick for (k = 0; k < frags; k++) 413460ed610SKirk McKusick clrbit(cg_blksfree(cgp), baseblk + j + k); 414460ed610SKirk McKusick n_blks += frags; 415460ed610SKirk McKusick if (frags == sblock.fs_frag) 416460ed610SKirk McKusick cgp->cg_cs.cs_nbfree--; 417460ed610SKirk McKusick else 418460ed610SKirk McKusick cgp->cg_cs.cs_nffree -= frags; 419460ed610SKirk McKusick cgdirty(cgbp); 420460ed610SKirk McKusick return ((cg * sblock.fs_fpg) + baseblk + j); 421460ed610SKirk McKusick } 422460ed610SKirk McKusick return (0); 423460ed610SKirk McKusick } 424460ed610SKirk McKusick 425460ed610SKirk McKusick /* 426113db2ddSJeff Roberson * Clear an inode from the cg bitmap. If the inode was already clear return 427113db2ddSJeff Roberson * 0 so the caller knows it does not have to check the inode contents. 428113db2ddSJeff Roberson */ 429113db2ddSJeff Roberson static int 430113db2ddSJeff Roberson ino_free(ino_t ino, int mode) 431113db2ddSJeff Roberson { 432113db2ddSJeff Roberson struct suj_cg *sc; 433113db2ddSJeff Roberson uint8_t *inosused; 434113db2ddSJeff Roberson struct cg *cgp; 435113db2ddSJeff Roberson int cg; 436113db2ddSJeff Roberson 437113db2ddSJeff Roberson cg = ino_to_cg(fs, ino); 438113db2ddSJeff Roberson ino = ino % fs->fs_ipg; 439113db2ddSJeff Roberson sc = cg_lookup(cg); 440113db2ddSJeff Roberson cgp = sc->sc_cgp; 441113db2ddSJeff Roberson inosused = cg_inosused(cgp); 442113db2ddSJeff Roberson /* 443113db2ddSJeff Roberson * The bitmap may never have made it to the disk so we have to 444113db2ddSJeff Roberson * conditionally clear. We can avoid writing the cg in this case. 445113db2ddSJeff Roberson */ 446113db2ddSJeff Roberson if (isclr(inosused, ino)) 447113db2ddSJeff Roberson return (0); 448113db2ddSJeff Roberson freeinos++; 449113db2ddSJeff Roberson clrbit(inosused, ino); 450113db2ddSJeff Roberson if (ino < cgp->cg_irotor) 451113db2ddSJeff Roberson cgp->cg_irotor = ino; 452113db2ddSJeff Roberson cgp->cg_cs.cs_nifree++; 453d8ba45e2SEd Maste if ((mode & IFMT) == IFDIR) { 454113db2ddSJeff Roberson freedir++; 455113db2ddSJeff Roberson cgp->cg_cs.cs_ndir--; 456113db2ddSJeff Roberson } 4575cc52631SKirk McKusick cgdirty(sc->sc_cgbp); 458113db2ddSJeff Roberson 459113db2ddSJeff Roberson return (1); 460113db2ddSJeff Roberson } 461113db2ddSJeff Roberson 462113db2ddSJeff Roberson /* 463113db2ddSJeff Roberson * Free 'frags' frags starting at filesystem block 'bno' skipping any frags 464113db2ddSJeff Roberson * set in the mask. 465113db2ddSJeff Roberson */ 466113db2ddSJeff Roberson static void 467460ed610SKirk McKusick blk_free(ino_t ino, ufs2_daddr_t bno, int mask, int frags) 468113db2ddSJeff Roberson { 469113db2ddSJeff Roberson ufs1_daddr_t fragno, cgbno; 470113db2ddSJeff Roberson struct suj_cg *sc; 471113db2ddSJeff Roberson struct cg *cgp; 472113db2ddSJeff Roberson int i, cg; 473113db2ddSJeff Roberson uint8_t *blksfree; 474113db2ddSJeff Roberson 475113db2ddSJeff Roberson if (debug) 4762db62a6bSJeff Roberson printf("Freeing %d frags at blk %jd mask 0x%x\n", 4772db62a6bSJeff Roberson frags, bno, mask); 478460ed610SKirk McKusick /* 479460ed610SKirk McKusick * Check to see if the block needs to be claimed by a snapshot. 480460ed610SKirk McKusick * If wanted, the snapshot references it. Otherwise we free it. 481460ed610SKirk McKusick */ 482460ed610SKirk McKusick if (snapblkfree(fs, bno, lfragtosize(fs, frags), ino, 483460ed610SKirk McKusick suj_checkblkavail)) 484460ed610SKirk McKusick return; 485113db2ddSJeff Roberson cg = dtog(fs, bno); 486113db2ddSJeff Roberson sc = cg_lookup(cg); 487113db2ddSJeff Roberson cgp = sc->sc_cgp; 488113db2ddSJeff Roberson cgbno = dtogd(fs, bno); 489113db2ddSJeff Roberson blksfree = cg_blksfree(cgp); 490113db2ddSJeff Roberson 491113db2ddSJeff Roberson /* 492113db2ddSJeff Roberson * If it's not allocated we only wrote the journal entry 493113db2ddSJeff Roberson * and never the bitmaps. Here we unconditionally clear and 494113db2ddSJeff Roberson * resolve the cg summary later. 495113db2ddSJeff Roberson */ 496113db2ddSJeff Roberson if (frags == fs->fs_frag && mask == 0) { 497113db2ddSJeff Roberson fragno = fragstoblks(fs, cgbno); 498113db2ddSJeff Roberson ffs_setblock(fs, blksfree, fragno); 499113db2ddSJeff Roberson freeblocks++; 500113db2ddSJeff Roberson } else { 501113db2ddSJeff Roberson /* 502113db2ddSJeff Roberson * deallocate the fragment 503113db2ddSJeff Roberson */ 504113db2ddSJeff Roberson for (i = 0; i < frags; i++) 505113db2ddSJeff Roberson if ((mask & (1 << i)) == 0 && isclr(blksfree, cgbno +i)) { 506113db2ddSJeff Roberson freefrags++; 507113db2ddSJeff Roberson setbit(blksfree, cgbno + i); 508113db2ddSJeff Roberson } 509113db2ddSJeff Roberson } 5105cc52631SKirk McKusick cgdirty(sc->sc_cgbp); 511113db2ddSJeff Roberson } 512113db2ddSJeff Roberson 513113db2ddSJeff Roberson /* 514113db2ddSJeff Roberson * Returns 1 if the whole block starting at 'bno' is marked free and 0 515113db2ddSJeff Roberson * otherwise. 516113db2ddSJeff Roberson */ 517113db2ddSJeff Roberson static int 518113db2ddSJeff Roberson blk_isfree(ufs2_daddr_t bno) 519113db2ddSJeff Roberson { 520113db2ddSJeff Roberson struct suj_cg *sc; 521113db2ddSJeff Roberson 522113db2ddSJeff Roberson sc = cg_lookup(dtog(fs, bno)); 523113db2ddSJeff Roberson return ffs_isblock(fs, cg_blksfree(sc->sc_cgp), dtogd(fs, bno)); 524113db2ddSJeff Roberson } 525113db2ddSJeff Roberson 526113db2ddSJeff Roberson /* 527113db2ddSJeff Roberson * Determine whether a block exists at a particular lbn in an inode. 528113db2ddSJeff Roberson * Returns 1 if found, 0 if not. lbn may be negative for indirects 529113db2ddSJeff Roberson * or ext blocks. 530113db2ddSJeff Roberson */ 531113db2ddSJeff Roberson static int 532113db2ddSJeff Roberson blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int *frags) 533113db2ddSJeff Roberson { 5345cc52631SKirk McKusick struct inode ip; 5355cc52631SKirk McKusick union dinode *dp; 536113db2ddSJeff Roberson ufs2_daddr_t nblk; 537113db2ddSJeff Roberson 5385cc52631SKirk McKusick ginode(ino, &ip); 5395cc52631SKirk McKusick dp = ip.i_dp; 5405cc52631SKirk McKusick if (DIP(dp, di_nlink) == 0 || DIP(dp, di_mode) == 0) { 5415cc52631SKirk McKusick irelse(&ip); 542113db2ddSJeff Roberson return (0); 5435cc52631SKirk McKusick } 5445cc52631SKirk McKusick nblk = ino_blkatoff(dp, ino, lbn, frags, NULL); 5455cc52631SKirk McKusick irelse(&ip); 546113db2ddSJeff Roberson return (nblk == blk); 547113db2ddSJeff Roberson } 548113db2ddSJeff Roberson 549113db2ddSJeff Roberson /* 55024d37c1eSJeff Roberson * Clear the directory entry at diroff that should point to child. Minimal 55124d37c1eSJeff Roberson * checking is done and it is assumed that this path was verified with isat. 55224d37c1eSJeff Roberson */ 55324d37c1eSJeff Roberson static void 55424d37c1eSJeff Roberson ino_clrat(ino_t parent, off_t diroff, ino_t child) 55524d37c1eSJeff Roberson { 55624d37c1eSJeff Roberson union dinode *dip; 55724d37c1eSJeff Roberson struct direct *dp; 5585cc52631SKirk McKusick struct inode ip; 55924d37c1eSJeff Roberson ufs2_daddr_t blk; 5605cc52631SKirk McKusick struct bufarea *bp; 56124d37c1eSJeff Roberson ufs_lbn_t lbn; 56224d37c1eSJeff Roberson int blksize; 56324d37c1eSJeff Roberson int frags; 56424d37c1eSJeff Roberson int doff; 56524d37c1eSJeff Roberson 56624d37c1eSJeff Roberson if (debug) 567623d7cb6SMatthew D Fleming printf("Clearing inode %ju from parent %ju at offset %jd\n", 568623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff); 56924d37c1eSJeff Roberson 57024d37c1eSJeff Roberson lbn = lblkno(fs, diroff); 57124d37c1eSJeff Roberson doff = blkoff(fs, diroff); 5725cc52631SKirk McKusick ginode(parent, &ip); 5735cc52631SKirk McKusick dip = ip.i_dp; 5745cc52631SKirk McKusick blk = ino_blkatoff(dip, parent, lbn, &frags, NULL); 57524d37c1eSJeff Roberson blksize = sblksize(fs, DIP(dip, di_size), lbn); 5765cc52631SKirk McKusick irelse(&ip); 5775cc52631SKirk McKusick bp = getdatablk(blk, blksize, BT_DIRDATA); 5785cc52631SKirk McKusick if (bp->b_errs != 0) 5795cc52631SKirk McKusick err_suj("ino_clrat: UNRECOVERABLE I/O ERROR"); 5805cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[doff]; 58124d37c1eSJeff Roberson if (dp->d_ino != child) 582623d7cb6SMatthew D Fleming errx(1, "Inode %ju does not exist in %ju at %jd", 583623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff); 58424d37c1eSJeff Roberson dp->d_ino = 0; 5855cc52631SKirk McKusick dirty(bp); 5865cc52631SKirk McKusick brelse(bp); 58724d37c1eSJeff Roberson /* 58824d37c1eSJeff Roberson * The actual .. reference count will already have been removed 58924d37c1eSJeff Roberson * from the parent by the .. remref record. 59024d37c1eSJeff Roberson */ 59124d37c1eSJeff Roberson } 59224d37c1eSJeff Roberson 59324d37c1eSJeff Roberson /* 594113db2ddSJeff Roberson * Determines whether a pointer to an inode exists within a directory 595113db2ddSJeff Roberson * at a specified offset. Returns the mode of the found entry. 596113db2ddSJeff Roberson */ 597113db2ddSJeff Roberson static int 598113db2ddSJeff Roberson ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot) 599113db2ddSJeff Roberson { 6005cc52631SKirk McKusick struct inode ip; 601113db2ddSJeff Roberson union dinode *dip; 6025cc52631SKirk McKusick struct bufarea *bp; 603113db2ddSJeff Roberson struct direct *dp; 604113db2ddSJeff Roberson ufs2_daddr_t blk; 605113db2ddSJeff Roberson ufs_lbn_t lbn; 606113db2ddSJeff Roberson int blksize; 607113db2ddSJeff Roberson int frags; 608113db2ddSJeff Roberson int dpoff; 609113db2ddSJeff Roberson int doff; 610113db2ddSJeff Roberson 611113db2ddSJeff Roberson *isdot = 0; 6125cc52631SKirk McKusick ginode(parent, &ip); 6135cc52631SKirk McKusick dip = ip.i_dp; 614113db2ddSJeff Roberson *mode = DIP(dip, di_mode); 615d8ba45e2SEd Maste if ((*mode & IFMT) != IFDIR) { 616113db2ddSJeff Roberson if (debug) { 617113db2ddSJeff Roberson /* 618113db2ddSJeff Roberson * This can happen if the parent inode 619113db2ddSJeff Roberson * was reallocated. 620113db2ddSJeff Roberson */ 621113db2ddSJeff Roberson if (*mode != 0) 622623d7cb6SMatthew D Fleming printf("Directory %ju has bad mode %o\n", 623623d7cb6SMatthew D Fleming (uintmax_t)parent, *mode); 624113db2ddSJeff Roberson else 625623d7cb6SMatthew D Fleming printf("Directory %ju has zero mode\n", 626623d7cb6SMatthew D Fleming (uintmax_t)parent); 627113db2ddSJeff Roberson } 6285cc52631SKirk McKusick irelse(&ip); 629113db2ddSJeff Roberson return (0); 630113db2ddSJeff Roberson } 631113db2ddSJeff Roberson lbn = lblkno(fs, diroff); 632113db2ddSJeff Roberson doff = blkoff(fs, diroff); 633113db2ddSJeff Roberson blksize = sblksize(fs, DIP(dip, di_size), lbn); 634113db2ddSJeff Roberson if (diroff + DIRECTSIZ(1) > DIP(dip, di_size) || doff >= blksize) { 635113db2ddSJeff Roberson if (debug) 636623d7cb6SMatthew D Fleming printf("ino %ju absent from %ju due to offset %jd" 637113db2ddSJeff Roberson " exceeding size %jd\n", 638623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, diroff, 639623d7cb6SMatthew D Fleming DIP(dip, di_size)); 6405cc52631SKirk McKusick irelse(&ip); 641113db2ddSJeff Roberson return (0); 642113db2ddSJeff Roberson } 6435cc52631SKirk McKusick blk = ino_blkatoff(dip, parent, lbn, &frags, NULL); 6445cc52631SKirk McKusick irelse(&ip); 645113db2ddSJeff Roberson if (blk <= 0) { 646113db2ddSJeff Roberson if (debug) 647623d7cb6SMatthew D Fleming printf("Sparse directory %ju", (uintmax_t)parent); 648113db2ddSJeff Roberson return (0); 649113db2ddSJeff Roberson } 6505cc52631SKirk McKusick bp = getdatablk(blk, blksize, BT_DIRDATA); 6515cc52631SKirk McKusick if (bp->b_errs != 0) 6525cc52631SKirk McKusick err_suj("ino_isat: UNRECOVERABLE I/O ERROR"); 653113db2ddSJeff Roberson /* 654113db2ddSJeff Roberson * Walk through the records from the start of the block to be 655113db2ddSJeff Roberson * certain we hit a valid record and not some junk in the middle 656113db2ddSJeff Roberson * of a file name. Stop when we reach or pass the expected offset. 657113db2ddSJeff Roberson */ 658f32d2926SPedro F. Giffuni dpoff = rounddown(doff, DIRBLKSIZ); 659113db2ddSJeff Roberson do { 6605cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[dpoff]; 661113db2ddSJeff Roberson if (dpoff == doff) 662113db2ddSJeff Roberson break; 663113db2ddSJeff Roberson if (dp->d_reclen == 0) 664113db2ddSJeff Roberson break; 665113db2ddSJeff Roberson dpoff += dp->d_reclen; 666113db2ddSJeff Roberson } while (dpoff <= doff); 667113db2ddSJeff Roberson if (dpoff > fs->fs_bsize) 668623d7cb6SMatthew D Fleming err_suj("Corrupt directory block in dir ino %ju\n", 669623d7cb6SMatthew D Fleming (uintmax_t)parent); 670113db2ddSJeff Roberson /* Not found. */ 671113db2ddSJeff Roberson if (dpoff != doff) { 672113db2ddSJeff Roberson if (debug) 673623d7cb6SMatthew D Fleming printf("ino %ju not found in %ju, lbn %jd, dpoff %d\n", 674623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)parent, lbn, dpoff); 6755cc52631SKirk McKusick brelse(bp); 676113db2ddSJeff Roberson return (0); 677113db2ddSJeff Roberson } 678113db2ddSJeff Roberson /* 679113db2ddSJeff Roberson * We found the item in question. Record the mode and whether it's 680113db2ddSJeff Roberson * a . or .. link for the caller. 681113db2ddSJeff Roberson */ 682113db2ddSJeff Roberson if (dp->d_ino == child) { 683113db2ddSJeff Roberson if (child == parent) 684113db2ddSJeff Roberson *isdot = 1; 685113db2ddSJeff Roberson else if (dp->d_namlen == 2 && 686113db2ddSJeff Roberson dp->d_name[0] == '.' && dp->d_name[1] == '.') 687113db2ddSJeff Roberson *isdot = 1; 688113db2ddSJeff Roberson *mode = DTTOIF(dp->d_type); 6895cc52631SKirk McKusick brelse(bp); 690113db2ddSJeff Roberson return (1); 691113db2ddSJeff Roberson } 692113db2ddSJeff Roberson if (debug) 693623d7cb6SMatthew D Fleming printf("ino %ju doesn't match dirent ino %ju in parent %ju\n", 694623d7cb6SMatthew D Fleming (uintmax_t)child, (uintmax_t)dp->d_ino, (uintmax_t)parent); 6955cc52631SKirk McKusick brelse(bp); 696113db2ddSJeff Roberson return (0); 697113db2ddSJeff Roberson } 698113db2ddSJeff Roberson 699113db2ddSJeff Roberson #define VISIT_INDIR 0x0001 700113db2ddSJeff Roberson #define VISIT_EXT 0x0002 701113db2ddSJeff Roberson #define VISIT_ROOT 0x0004 /* Operation came via root & valid pointers. */ 702113db2ddSJeff Roberson 703113db2ddSJeff Roberson /* 704113db2ddSJeff Roberson * Read an indirect level which may or may not be linked into an inode. 705113db2ddSJeff Roberson */ 706113db2ddSJeff Roberson static void 707113db2ddSJeff Roberson indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags, 708113db2ddSJeff Roberson ino_visitor visitor, int flags) 709113db2ddSJeff Roberson { 7105cc52631SKirk McKusick struct bufarea *bp; 711113db2ddSJeff Roberson ufs_lbn_t lbnadd; 712113db2ddSJeff Roberson ufs2_daddr_t nblk; 713113db2ddSJeff Roberson ufs_lbn_t nlbn; 714113db2ddSJeff Roberson int level; 715113db2ddSJeff Roberson int i; 716113db2ddSJeff Roberson 717113db2ddSJeff Roberson /* 718113db2ddSJeff Roberson * Don't visit indirect blocks with contents we can't trust. This 719113db2ddSJeff Roberson * should only happen when indir_visit() is called to complete a 720113db2ddSJeff Roberson * truncate that never finished and not when a pointer is found via 721113db2ddSJeff Roberson * an inode. 722113db2ddSJeff Roberson */ 723113db2ddSJeff Roberson if (blk == 0) 724113db2ddSJeff Roberson return; 725113db2ddSJeff Roberson level = lbn_level(lbn); 726113db2ddSJeff Roberson if (level == -1) 727edad6026SXin LI err_suj("Invalid level for lbn %jd\n", lbn); 728113db2ddSJeff Roberson if ((flags & VISIT_ROOT) == 0 && blk_isindir(blk, ino, lbn) == 0) { 729113db2ddSJeff Roberson if (debug) 730623d7cb6SMatthew D Fleming printf("blk %jd ino %ju lbn %jd(%d) is not indir.\n", 731623d7cb6SMatthew D Fleming blk, (uintmax_t)ino, lbn, level); 732113db2ddSJeff Roberson goto out; 733113db2ddSJeff Roberson } 734113db2ddSJeff Roberson lbnadd = 1; 735113db2ddSJeff Roberson for (i = level; i > 0; i--) 736113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 7375cc52631SKirk McKusick bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level); 7385cc52631SKirk McKusick if (bp->b_errs != 0) 739b796bfceSKirk McKusick err_suj("indir_visit: UNRECOVERABLE I/O ERROR\n"); 740113db2ddSJeff Roberson for (i = 0; i < NINDIR(fs); i++) { 7415cc52631SKirk McKusick if ((nblk = IBLK(bp, i)) == 0) 742113db2ddSJeff Roberson continue; 743113db2ddSJeff Roberson if (level == 0) { 744113db2ddSJeff Roberson nlbn = -lbn + i * lbnadd; 745113db2ddSJeff Roberson (*frags) += fs->fs_frag; 746113db2ddSJeff Roberson visitor(ino, nlbn, nblk, fs->fs_frag); 747113db2ddSJeff Roberson } else { 748113db2ddSJeff Roberson nlbn = (lbn + 1) - (i * lbnadd); 749113db2ddSJeff Roberson indir_visit(ino, nlbn, nblk, frags, visitor, flags); 750113db2ddSJeff Roberson } 751113db2ddSJeff Roberson } 7525cc52631SKirk McKusick brelse(bp); 753113db2ddSJeff Roberson out: 754113db2ddSJeff Roberson if (flags & VISIT_INDIR) { 755113db2ddSJeff Roberson (*frags) += fs->fs_frag; 756113db2ddSJeff Roberson visitor(ino, lbn, blk, fs->fs_frag); 757113db2ddSJeff Roberson } 758113db2ddSJeff Roberson } 759113db2ddSJeff Roberson 760113db2ddSJeff Roberson /* 761113db2ddSJeff Roberson * Visit each block in an inode as specified by 'flags' and call a 762113db2ddSJeff Roberson * callback function. The callback may inspect or free blocks. The 763113db2ddSJeff Roberson * count of frags found according to the size in the file is returned. 764113db2ddSJeff Roberson * This is not valid for sparse files but may be used to determine 765113db2ddSJeff Roberson * the correct di_blocks for a file. 766113db2ddSJeff Roberson */ 767113db2ddSJeff Roberson static uint64_t 7685cc52631SKirk McKusick ino_visit(union dinode *dp, ino_t ino, ino_visitor visitor, int flags) 769113db2ddSJeff Roberson { 770113db2ddSJeff Roberson ufs_lbn_t nextlbn; 771113db2ddSJeff Roberson ufs_lbn_t tmpval; 772113db2ddSJeff Roberson ufs_lbn_t lbn; 773113db2ddSJeff Roberson uint64_t size; 774113db2ddSJeff Roberson uint64_t fragcnt; 775113db2ddSJeff Roberson int mode; 776113db2ddSJeff Roberson int frags; 777113db2ddSJeff Roberson int i; 778113db2ddSJeff Roberson 7795cc52631SKirk McKusick size = DIP(dp, di_size); 7805cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 781113db2ddSJeff Roberson fragcnt = 0; 782113db2ddSJeff Roberson if ((flags & VISIT_EXT) && 7835cc52631SKirk McKusick fs->fs_magic == FS_UFS2_MAGIC && dp->dp2.di_extsize) { 7841dc349abSEd Maste for (i = 0; i < UFS_NXADDR; i++) { 7855cc52631SKirk McKusick if (dp->dp2.di_extb[i] == 0) 786113db2ddSJeff Roberson continue; 7875cc52631SKirk McKusick frags = sblksize(fs, dp->dp2.di_extsize, i); 788113db2ddSJeff Roberson frags = numfrags(fs, frags); 789113db2ddSJeff Roberson fragcnt += frags; 7905cc52631SKirk McKusick visitor(ino, -1 - i, dp->dp2.di_extb[i], frags); 791113db2ddSJeff Roberson } 792113db2ddSJeff Roberson } 793113db2ddSJeff Roberson /* Skip datablocks for short links and devices. */ 794d8ba45e2SEd Maste if (mode == IFBLK || mode == IFCHR || 795d8ba45e2SEd Maste (mode == IFLNK && size < fs->fs_maxsymlinklen)) 796113db2ddSJeff Roberson return (fragcnt); 7971dc349abSEd Maste for (i = 0; i < UFS_NDADDR; i++) { 7985cc52631SKirk McKusick if (DIP(dp, di_db[i]) == 0) 799113db2ddSJeff Roberson continue; 800113db2ddSJeff Roberson frags = sblksize(fs, size, i); 801113db2ddSJeff Roberson frags = numfrags(fs, frags); 802113db2ddSJeff Roberson fragcnt += frags; 8035cc52631SKirk McKusick visitor(ino, i, DIP(dp, di_db[i]), frags); 804113db2ddSJeff Roberson } 805113db2ddSJeff Roberson /* 806113db2ddSJeff Roberson * We know the following indirects are real as we're following 807113db2ddSJeff Roberson * real pointers to them. 808113db2ddSJeff Roberson */ 809113db2ddSJeff Roberson flags |= VISIT_ROOT; 8101dc349abSEd Maste for (i = 0, tmpval = NINDIR(fs), lbn = UFS_NDADDR; i < UFS_NIADDR; i++, 811113db2ddSJeff Roberson lbn = nextlbn) { 812113db2ddSJeff Roberson nextlbn = lbn + tmpval; 813113db2ddSJeff Roberson tmpval *= NINDIR(fs); 8145cc52631SKirk McKusick if (DIP(dp, di_ib[i]) == 0) 815113db2ddSJeff Roberson continue; 8165cc52631SKirk McKusick indir_visit(ino, -lbn - i, DIP(dp, di_ib[i]), &fragcnt, visitor, 817113db2ddSJeff Roberson flags); 818113db2ddSJeff Roberson } 819113db2ddSJeff Roberson return (fragcnt); 820113db2ddSJeff Roberson } 821113db2ddSJeff Roberson 822113db2ddSJeff Roberson /* 823113db2ddSJeff Roberson * Null visitor function used when we just want to count blocks and 824113db2ddSJeff Roberson * record the lbn. 825113db2ddSJeff Roberson */ 826113db2ddSJeff Roberson ufs_lbn_t visitlbn; 827113db2ddSJeff Roberson static void 828113db2ddSJeff Roberson null_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 829113db2ddSJeff Roberson { 830113db2ddSJeff Roberson if (lbn > 0) 831113db2ddSJeff Roberson visitlbn = lbn; 832113db2ddSJeff Roberson } 833113db2ddSJeff Roberson 834113db2ddSJeff Roberson /* 835113db2ddSJeff Roberson * Recalculate di_blocks when we discover that a block allocation or 836113db2ddSJeff Roberson * free was not successfully completed. The kernel does not roll this back 837113db2ddSJeff Roberson * because it would be too expensive to compute which indirects were 838113db2ddSJeff Roberson * reachable at the time the inode was written. 839113db2ddSJeff Roberson */ 840113db2ddSJeff Roberson static void 841113db2ddSJeff Roberson ino_adjblks(struct suj_ino *sino) 842113db2ddSJeff Roberson { 8435cc52631SKirk McKusick struct inode ip; 8445cc52631SKirk McKusick union dinode *dp; 845113db2ddSJeff Roberson uint64_t blocks; 846113db2ddSJeff Roberson uint64_t frags; 847113db2ddSJeff Roberson off_t isize; 848113db2ddSJeff Roberson off_t size; 849113db2ddSJeff Roberson ino_t ino; 850113db2ddSJeff Roberson 851113db2ddSJeff Roberson ino = sino->si_ino; 8525cc52631SKirk McKusick ginode(ino, &ip); 8535cc52631SKirk McKusick dp = ip.i_dp; 854113db2ddSJeff Roberson /* No need to adjust zero'd inodes. */ 8555cc52631SKirk McKusick if (DIP(dp, di_mode) == 0) { 8565cc52631SKirk McKusick irelse(&ip); 857113db2ddSJeff Roberson return; 8585cc52631SKirk McKusick } 859113db2ddSJeff Roberson /* 860113db2ddSJeff Roberson * Visit all blocks and count them as well as recording the last 861113db2ddSJeff Roberson * valid lbn in the file. If the file size doesn't agree with the 862113db2ddSJeff Roberson * last lbn we need to truncate to fix it. Otherwise just adjust 863113db2ddSJeff Roberson * the blocks count. 864113db2ddSJeff Roberson */ 865113db2ddSJeff Roberson visitlbn = 0; 8665cc52631SKirk McKusick frags = ino_visit(dp, ino, null_visit, VISIT_INDIR | VISIT_EXT); 867113db2ddSJeff Roberson blocks = fsbtodb(fs, frags); 868113db2ddSJeff Roberson /* 869113db2ddSJeff Roberson * We assume the size and direct block list is kept coherent by 870113db2ddSJeff Roberson * softdep. For files that have extended into indirects we truncate 871113db2ddSJeff Roberson * to the size in the inode or the maximum size permitted by 872113db2ddSJeff Roberson * populated indirects. 873113db2ddSJeff Roberson */ 8741dc349abSEd Maste if (visitlbn >= UFS_NDADDR) { 8755cc52631SKirk McKusick isize = DIP(dp, di_size); 876113db2ddSJeff Roberson size = lblktosize(fs, visitlbn + 1); 877113db2ddSJeff Roberson if (isize > size) 878113db2ddSJeff Roberson isize = size; 879113db2ddSJeff Roberson /* Always truncate to free any unpopulated indirects. */ 8805cc52631SKirk McKusick ino_trunc(ino, isize); 8815cc52631SKirk McKusick irelse(&ip); 882113db2ddSJeff Roberson return; 883113db2ddSJeff Roberson } 8845cc52631SKirk McKusick if (blocks == DIP(dp, di_blocks)) { 8855cc52631SKirk McKusick irelse(&ip); 886113db2ddSJeff Roberson return; 8875cc52631SKirk McKusick } 888113db2ddSJeff Roberson if (debug) 889623d7cb6SMatthew D Fleming printf("ino %ju adjusting block count from %jd to %jd\n", 8905cc52631SKirk McKusick (uintmax_t)ino, DIP(dp, di_blocks), blocks); 8915cc52631SKirk McKusick DIP_SET(dp, di_blocks, blocks); 8925cc52631SKirk McKusick inodirty(&ip); 8935cc52631SKirk McKusick irelse(&ip); 894113db2ddSJeff Roberson } 895113db2ddSJeff Roberson 896113db2ddSJeff Roberson static void 897113db2ddSJeff Roberson blk_free_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 898113db2ddSJeff Roberson { 899113db2ddSJeff Roberson 900460ed610SKirk McKusick blk_free(ino, blk, blk_freemask(blk, ino, lbn, frags), frags); 901113db2ddSJeff Roberson } 902113db2ddSJeff Roberson 903113db2ddSJeff Roberson /* 904113db2ddSJeff Roberson * Free a block or tree of blocks that was previously rooted in ino at 905113db2ddSJeff Roberson * the given lbn. If the lbn is an indirect all children are freed 906113db2ddSJeff Roberson * recursively. 907113db2ddSJeff Roberson */ 908113db2ddSJeff Roberson static void 909113db2ddSJeff Roberson blk_free_lbn(ufs2_daddr_t blk, ino_t ino, ufs_lbn_t lbn, int frags, int follow) 910113db2ddSJeff Roberson { 911113db2ddSJeff Roberson uint64_t resid; 912113db2ddSJeff Roberson int mask; 913113db2ddSJeff Roberson 914113db2ddSJeff Roberson mask = blk_freemask(blk, ino, lbn, frags); 915113db2ddSJeff Roberson resid = 0; 9161dc349abSEd Maste if (lbn <= -UFS_NDADDR && follow && mask == 0) 917113db2ddSJeff Roberson indir_visit(ino, lbn, blk, &resid, blk_free_visit, VISIT_INDIR); 918113db2ddSJeff Roberson else 919460ed610SKirk McKusick blk_free(ino, blk, mask, frags); 920113db2ddSJeff Roberson } 921113db2ddSJeff Roberson 922113db2ddSJeff Roberson static void 923113db2ddSJeff Roberson ino_setskip(struct suj_ino *sino, ino_t parent) 924113db2ddSJeff Roberson { 925113db2ddSJeff Roberson int isdot; 926113db2ddSJeff Roberson int mode; 927113db2ddSJeff Roberson 928113db2ddSJeff Roberson if (ino_isat(sino->si_ino, DOTDOT_OFFSET, parent, &mode, &isdot)) 929113db2ddSJeff Roberson sino->si_skipparent = 1; 930113db2ddSJeff Roberson } 931113db2ddSJeff Roberson 93224d37c1eSJeff Roberson static void 93324d37c1eSJeff Roberson ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot) 93424d37c1eSJeff Roberson { 93524d37c1eSJeff Roberson struct suj_ino *sino; 93624d37c1eSJeff Roberson struct suj_rec *srec; 93724d37c1eSJeff Roberson struct jrefrec *rrec; 93824d37c1eSJeff Roberson 93924d37c1eSJeff Roberson /* 94024d37c1eSJeff Roberson * Lookup this inode to see if we have a record for it. 94124d37c1eSJeff Roberson */ 94224d37c1eSJeff Roberson sino = ino_lookup(child, 0); 94324d37c1eSJeff Roberson /* 94424d37c1eSJeff Roberson * Tell any child directories we've already removed their 94524d37c1eSJeff Roberson * parent link cnt. Don't try to adjust our link down again. 94624d37c1eSJeff Roberson */ 94724d37c1eSJeff Roberson if (sino != NULL && isdotdot == 0) 94824d37c1eSJeff Roberson ino_setskip(sino, parent); 94924d37c1eSJeff Roberson /* 95024d37c1eSJeff Roberson * No valid record for this inode. Just drop the on-disk 95124d37c1eSJeff Roberson * link by one. 95224d37c1eSJeff Roberson */ 95324d37c1eSJeff Roberson if (sino == NULL || sino->si_hasrecs == 0) { 95424d37c1eSJeff Roberson ino_decr(child); 95524d37c1eSJeff Roberson return; 95624d37c1eSJeff Roberson } 95724d37c1eSJeff Roberson /* 95824d37c1eSJeff Roberson * Use ino_adjust() if ino_check() has already processed this 95924d37c1eSJeff Roberson * child. If we lose the last non-dot reference to a 96024d37c1eSJeff Roberson * directory it will be discarded. 96124d37c1eSJeff Roberson */ 96224d37c1eSJeff Roberson if (sino->si_linkadj) { 9635cc52631SKirk McKusick if (sino->si_nlink == 0) 9645cc52631SKirk McKusick err_suj("ino_remref: ino %ld mode 0%o about to go " 9655cc52631SKirk McKusick "negative\n", sino->si_ino, sino->si_mode); 96624d37c1eSJeff Roberson sino->si_nlink--; 96724d37c1eSJeff Roberson if (isdotdot) 96824d37c1eSJeff Roberson sino->si_dotlinks--; 96924d37c1eSJeff Roberson ino_adjust(sino); 97024d37c1eSJeff Roberson return; 97124d37c1eSJeff Roberson } 97224d37c1eSJeff Roberson /* 97324d37c1eSJeff Roberson * If we haven't yet processed this inode we need to make 97424d37c1eSJeff Roberson * sure we will successfully discover the lost path. If not 97524d37c1eSJeff Roberson * use nlinkadj to remember. 97624d37c1eSJeff Roberson */ 97724d37c1eSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 97824d37c1eSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 97924d37c1eSJeff Roberson if (rrec->jr_parent == parent && 98024d37c1eSJeff Roberson rrec->jr_diroff == diroff) 98124d37c1eSJeff Roberson return; 98224d37c1eSJeff Roberson } 98324d37c1eSJeff Roberson sino->si_nlinkadj++; 98424d37c1eSJeff Roberson } 98524d37c1eSJeff Roberson 986113db2ddSJeff Roberson /* 987113db2ddSJeff Roberson * Free the children of a directory when the directory is discarded. 988113db2ddSJeff Roberson */ 989113db2ddSJeff Roberson static void 990113db2ddSJeff Roberson ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 991113db2ddSJeff Roberson { 992113db2ddSJeff Roberson struct suj_ino *sino; 9935cc52631SKirk McKusick struct bufarea *bp; 994113db2ddSJeff Roberson struct direct *dp; 995113db2ddSJeff Roberson off_t diroff; 996113db2ddSJeff Roberson int skipparent; 99724d37c1eSJeff Roberson int isdotdot; 998113db2ddSJeff Roberson int dpoff; 999113db2ddSJeff Roberson int size; 1000113db2ddSJeff Roberson 1001113db2ddSJeff Roberson sino = ino_lookup(ino, 0); 1002113db2ddSJeff Roberson if (sino) 1003113db2ddSJeff Roberson skipparent = sino->si_skipparent; 1004113db2ddSJeff Roberson else 1005113db2ddSJeff Roberson skipparent = 0; 1006113db2ddSJeff Roberson size = lfragtosize(fs, frags); 10075cc52631SKirk McKusick bp = getdatablk(blk, size, BT_DIRDATA); 10085cc52631SKirk McKusick if (bp->b_errs != 0) 10095cc52631SKirk McKusick err_suj("ino_free_children: UNRECOVERABLE I/O ERROR"); 10105cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[0]; 1011113db2ddSJeff Roberson for (dpoff = 0; dpoff < size && dp->d_reclen; dpoff += dp->d_reclen) { 10125cc52631SKirk McKusick dp = (struct direct *)&bp->b_un.b_buf[dpoff]; 10131dc349abSEd Maste if (dp->d_ino == 0 || dp->d_ino == UFS_WINO) 1014113db2ddSJeff Roberson continue; 1015113db2ddSJeff Roberson if (dp->d_namlen == 1 && dp->d_name[0] == '.') 1016113db2ddSJeff Roberson continue; 101724d37c1eSJeff Roberson isdotdot = dp->d_namlen == 2 && dp->d_name[0] == '.' && 1018113db2ddSJeff Roberson dp->d_name[1] == '.'; 101924d37c1eSJeff Roberson if (isdotdot && skipparent == 1) 1020113db2ddSJeff Roberson continue; 1021113db2ddSJeff Roberson if (debug) 1022623d7cb6SMatthew D Fleming printf("Directory %ju removing ino %ju name %s\n", 1023623d7cb6SMatthew D Fleming (uintmax_t)ino, (uintmax_t)dp->d_ino, dp->d_name); 1024113db2ddSJeff Roberson diroff = lblktosize(fs, lbn) + dpoff; 102524d37c1eSJeff Roberson ino_remref(ino, dp->d_ino, diroff, isdotdot); 1026113db2ddSJeff Roberson } 10275cc52631SKirk McKusick brelse(bp); 1028113db2ddSJeff Roberson } 1029113db2ddSJeff Roberson 1030113db2ddSJeff Roberson /* 1031113db2ddSJeff Roberson * Reclaim an inode, freeing all blocks and decrementing all children's 1032113db2ddSJeff Roberson * link counts. Free the inode back to the cg. 1033113db2ddSJeff Roberson */ 1034113db2ddSJeff Roberson static void 10355cc52631SKirk McKusick ino_reclaim(struct inode *ip, ino_t ino, int mode) 1036113db2ddSJeff Roberson { 10375cc52631SKirk McKusick union dinode *dp; 1038113db2ddSJeff Roberson uint32_t gen; 1039113db2ddSJeff Roberson 10405cc52631SKirk McKusick dp = ip->i_dp; 10411dc349abSEd Maste if (ino == UFS_ROOTINO) 10421dc349abSEd Maste err_suj("Attempting to free UFS_ROOTINO\n"); 1043113db2ddSJeff Roberson if (debug) 1044623d7cb6SMatthew D Fleming printf("Truncating and freeing ino %ju, nlink %d, mode %o\n", 10455cc52631SKirk McKusick (uintmax_t)ino, DIP(dp, di_nlink), DIP(dp, di_mode)); 1046113db2ddSJeff Roberson 1047113db2ddSJeff Roberson /* We are freeing an inode or directory. */ 10485cc52631SKirk McKusick if ((DIP(dp, di_mode) & IFMT) == IFDIR) 10495cc52631SKirk McKusick ino_visit(dp, ino, ino_free_children, 0); 10505cc52631SKirk McKusick DIP_SET(dp, di_nlink, 0); 1051460ed610SKirk McKusick if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) 1052460ed610SKirk McKusick snapremove(ino); 10535cc52631SKirk McKusick ino_visit(dp, ino, blk_free_visit, VISIT_EXT | VISIT_INDIR); 1054113db2ddSJeff Roberson /* Here we have to clear the inode and release any blocks it holds. */ 10555cc52631SKirk McKusick gen = DIP(dp, di_gen); 1056113db2ddSJeff Roberson if (fs->fs_magic == FS_UFS1_MAGIC) 10575cc52631SKirk McKusick bzero(dp, sizeof(struct ufs1_dinode)); 1058113db2ddSJeff Roberson else 10595cc52631SKirk McKusick bzero(dp, sizeof(struct ufs2_dinode)); 10605cc52631SKirk McKusick DIP_SET(dp, di_gen, gen); 10615cc52631SKirk McKusick inodirty(ip); 1062113db2ddSJeff Roberson ino_free(ino, mode); 1063113db2ddSJeff Roberson return; 1064113db2ddSJeff Roberson } 1065113db2ddSJeff Roberson 1066113db2ddSJeff Roberson /* 1067113db2ddSJeff Roberson * Adjust an inode's link count down by one when a directory goes away. 1068113db2ddSJeff Roberson */ 1069113db2ddSJeff Roberson static void 1070113db2ddSJeff Roberson ino_decr(ino_t ino) 1071113db2ddSJeff Roberson { 10725cc52631SKirk McKusick struct inode ip; 10735cc52631SKirk McKusick union dinode *dp; 1074113db2ddSJeff Roberson int reqlink; 1075113db2ddSJeff Roberson int nlink; 1076113db2ddSJeff Roberson int mode; 1077113db2ddSJeff Roberson 10785cc52631SKirk McKusick ginode(ino, &ip); 10795cc52631SKirk McKusick dp = ip.i_dp; 10805cc52631SKirk McKusick nlink = DIP(dp, di_nlink); 10815cc52631SKirk McKusick mode = DIP(dp, di_mode); 1082113db2ddSJeff Roberson if (nlink < 1) 1083edad6026SXin LI err_suj("Inode %d link count %d invalid\n", ino, nlink); 1084113db2ddSJeff Roberson if (mode == 0) 1085edad6026SXin LI err_suj("Inode %d has a link of %d with 0 mode\n", ino, nlink); 1086113db2ddSJeff Roberson nlink--; 1087d8ba45e2SEd Maste if ((mode & IFMT) == IFDIR) 1088113db2ddSJeff Roberson reqlink = 2; 1089113db2ddSJeff Roberson else 1090113db2ddSJeff Roberson reqlink = 1; 1091113db2ddSJeff Roberson if (nlink < reqlink) { 1092113db2ddSJeff Roberson if (debug) 1093623d7cb6SMatthew D Fleming printf("ino %ju not enough links to live %d < %d\n", 1094623d7cb6SMatthew D Fleming (uintmax_t)ino, nlink, reqlink); 10955cc52631SKirk McKusick ino_reclaim(&ip, ino, mode); 10965cc52631SKirk McKusick irelse(&ip); 1097113db2ddSJeff Roberson return; 1098113db2ddSJeff Roberson } 10995cc52631SKirk McKusick DIP_SET(dp, di_nlink, nlink); 11005cc52631SKirk McKusick inodirty(&ip); 11015cc52631SKirk McKusick irelse(&ip); 1102113db2ddSJeff Roberson } 1103113db2ddSJeff Roberson 1104113db2ddSJeff Roberson /* 1105113db2ddSJeff Roberson * Adjust the inode link count to 'nlink'. If the count reaches zero 1106113db2ddSJeff Roberson * free it. 1107113db2ddSJeff Roberson */ 1108113db2ddSJeff Roberson static void 1109113db2ddSJeff Roberson ino_adjust(struct suj_ino *sino) 1110113db2ddSJeff Roberson { 1111113db2ddSJeff Roberson struct jrefrec *rrec; 1112113db2ddSJeff Roberson struct suj_rec *srec; 1113113db2ddSJeff Roberson struct suj_ino *stmp; 11145cc52631SKirk McKusick union dinode *dp; 11155cc52631SKirk McKusick struct inode ip; 1116113db2ddSJeff Roberson nlink_t nlink; 111769921123SKonstantin Belousov nlink_t reqlink; 111824d37c1eSJeff Roberson int recmode; 111924d37c1eSJeff Roberson int isdot; 1120113db2ddSJeff Roberson int mode; 1121113db2ddSJeff Roberson ino_t ino; 1122113db2ddSJeff Roberson 1123113db2ddSJeff Roberson nlink = sino->si_nlink; 1124113db2ddSJeff Roberson ino = sino->si_ino; 1125d8ba45e2SEd Maste mode = sino->si_mode & IFMT; 112624d37c1eSJeff Roberson /* 112724d37c1eSJeff Roberson * If it's a directory with no dot links, it was truncated before 112824d37c1eSJeff Roberson * the name was cleared. We need to clear the dirent that 112924d37c1eSJeff Roberson * points at it. 113024d37c1eSJeff Roberson */ 1131d8ba45e2SEd Maste if (mode == IFDIR && nlink == 1 && sino->si_dotlinks == 0) { 113224d37c1eSJeff Roberson sino->si_nlink = nlink = 0; 113324d37c1eSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 113424d37c1eSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 113524d37c1eSJeff Roberson if (ino_isat(rrec->jr_parent, rrec->jr_diroff, ino, 113624d37c1eSJeff Roberson &recmode, &isdot) == 0) 113724d37c1eSJeff Roberson continue; 113824d37c1eSJeff Roberson ino_clrat(rrec->jr_parent, rrec->jr_diroff, ino); 113924d37c1eSJeff Roberson break; 114024d37c1eSJeff Roberson } 114124d37c1eSJeff Roberson if (srec == NULL) 1142623d7cb6SMatthew D Fleming errx(1, "Directory %ju name not found", (uintmax_t)ino); 114324d37c1eSJeff Roberson } 1144113db2ddSJeff Roberson /* 1145113db2ddSJeff Roberson * If it's a directory with no real names pointing to it go ahead 1146113db2ddSJeff Roberson * and truncate it. This will free any children. 1147113db2ddSJeff Roberson */ 1148d8ba45e2SEd Maste if (mode == IFDIR && nlink - sino->si_dotlinks == 0) { 1149113db2ddSJeff Roberson sino->si_nlink = nlink = 0; 1150113db2ddSJeff Roberson /* 1151113db2ddSJeff Roberson * Mark any .. links so they know not to free this inode 1152113db2ddSJeff Roberson * when they are removed. 1153113db2ddSJeff Roberson */ 1154113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 1155113db2ddSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 1156113db2ddSJeff Roberson if (rrec->jr_diroff == DOTDOT_OFFSET) { 1157113db2ddSJeff Roberson stmp = ino_lookup(rrec->jr_parent, 0); 1158113db2ddSJeff Roberson if (stmp) 1159113db2ddSJeff Roberson ino_setskip(stmp, ino); 1160113db2ddSJeff Roberson } 1161113db2ddSJeff Roberson } 1162113db2ddSJeff Roberson } 11635cc52631SKirk McKusick ginode(ino, &ip); 11645cc52631SKirk McKusick dp = ip.i_dp; 11655cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 1166ed8d06aaSJohn Baldwin if (nlink > UFS_LINK_MAX) 11671c324569SKonstantin Belousov err_suj("ino %ju nlink manipulation error, new %ju, old %d\n", 11685cc52631SKirk McKusick (uintmax_t)ino, (uintmax_t)nlink, DIP(dp, di_nlink)); 1169113db2ddSJeff Roberson if (debug) 11701c324569SKonstantin Belousov printf("Adjusting ino %ju, nlink %ju, old link %d lastmode %o\n", 11715cc52631SKirk McKusick (uintmax_t)ino, (uintmax_t)nlink, DIP(dp, di_nlink), 11721c324569SKonstantin Belousov sino->si_mode); 1173113db2ddSJeff Roberson if (mode == 0) { 1174113db2ddSJeff Roberson if (debug) 1175623d7cb6SMatthew D Fleming printf("ino %ju, zero inode freeing bitmap\n", 1176623d7cb6SMatthew D Fleming (uintmax_t)ino); 1177113db2ddSJeff Roberson ino_free(ino, sino->si_mode); 11785cc52631SKirk McKusick irelse(&ip); 1179113db2ddSJeff Roberson return; 1180113db2ddSJeff Roberson } 1181113db2ddSJeff Roberson /* XXX Should be an assert? */ 1182113db2ddSJeff Roberson if (mode != sino->si_mode && debug) 1183623d7cb6SMatthew D Fleming printf("ino %ju, mode %o != %o\n", 1184623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, sino->si_mode); 1185d8ba45e2SEd Maste if ((mode & IFMT) == IFDIR) 1186113db2ddSJeff Roberson reqlink = 2; 1187113db2ddSJeff Roberson else 1188113db2ddSJeff Roberson reqlink = 1; 1189113db2ddSJeff Roberson /* If the inode doesn't have enough links to live, free it. */ 1190113db2ddSJeff Roberson if (nlink < reqlink) { 1191113db2ddSJeff Roberson if (debug) 11921c324569SKonstantin Belousov printf("ino %ju not enough links to live %ju < %ju\n", 11931c324569SKonstantin Belousov (uintmax_t)ino, (uintmax_t)nlink, 11941c324569SKonstantin Belousov (uintmax_t)reqlink); 11955cc52631SKirk McKusick ino_reclaim(&ip, ino, mode); 11965cc52631SKirk McKusick irelse(&ip); 1197113db2ddSJeff Roberson return; 1198113db2ddSJeff Roberson } 1199113db2ddSJeff Roberson /* If required write the updated link count. */ 12005cc52631SKirk McKusick if (DIP(dp, di_nlink) == nlink) { 1201113db2ddSJeff Roberson if (debug) 1202623d7cb6SMatthew D Fleming printf("ino %ju, link matches, skipping.\n", 1203623d7cb6SMatthew D Fleming (uintmax_t)ino); 12045cc52631SKirk McKusick irelse(&ip); 1205113db2ddSJeff Roberson return; 1206113db2ddSJeff Roberson } 12075cc52631SKirk McKusick DIP_SET(dp, di_nlink, nlink); 12085cc52631SKirk McKusick inodirty(&ip); 12095cc52631SKirk McKusick irelse(&ip); 1210113db2ddSJeff Roberson } 1211113db2ddSJeff Roberson 1212113db2ddSJeff Roberson /* 1213113db2ddSJeff Roberson * Truncate some or all blocks in an indirect, freeing any that are required 1214113db2ddSJeff Roberson * and zeroing the indirect. 1215113db2ddSJeff Roberson */ 1216113db2ddSJeff Roberson static void 12175cc52631SKirk McKusick indir_trunc(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, ufs_lbn_t lastlbn, 12185cc52631SKirk McKusick union dinode *dp) 1219113db2ddSJeff Roberson { 12205cc52631SKirk McKusick struct bufarea *bp; 1221113db2ddSJeff Roberson ufs_lbn_t lbnadd; 1222113db2ddSJeff Roberson ufs2_daddr_t nblk; 1223113db2ddSJeff Roberson ufs_lbn_t next; 1224113db2ddSJeff Roberson ufs_lbn_t nlbn; 12255cc52631SKirk McKusick int isdirty; 1226113db2ddSJeff Roberson int level; 1227113db2ddSJeff Roberson int i; 1228113db2ddSJeff Roberson 1229113db2ddSJeff Roberson if (blk == 0) 1230113db2ddSJeff Roberson return; 12315cc52631SKirk McKusick isdirty = 0; 1232113db2ddSJeff Roberson level = lbn_level(lbn); 1233113db2ddSJeff Roberson if (level == -1) 1234edad6026SXin LI err_suj("Invalid level for lbn %jd\n", lbn); 1235113db2ddSJeff Roberson lbnadd = 1; 1236113db2ddSJeff Roberson for (i = level; i > 0; i--) 1237113db2ddSJeff Roberson lbnadd *= NINDIR(fs); 12385cc52631SKirk McKusick bp = getdatablk(blk, fs->fs_bsize, BT_LEVEL1 + level); 12395cc52631SKirk McKusick if (bp->b_errs != 0) 12405cc52631SKirk McKusick err_suj("indir_trunc: UNRECOVERABLE I/O ERROR"); 1241113db2ddSJeff Roberson for (i = 0; i < NINDIR(fs); i++) { 12425cc52631SKirk McKusick if ((nblk = IBLK(bp, i)) == 0) 1243113db2ddSJeff Roberson continue; 1244113db2ddSJeff Roberson if (level != 0) { 1245113db2ddSJeff Roberson nlbn = (lbn + 1) - (i * lbnadd); 1246113db2ddSJeff Roberson /* 1247113db2ddSJeff Roberson * Calculate the lbn of the next indirect to 1248113db2ddSJeff Roberson * determine if any of this indirect must be 1249113db2ddSJeff Roberson * reclaimed. 1250113db2ddSJeff Roberson */ 1251113db2ddSJeff Roberson next = -(lbn + level) + ((i+1) * lbnadd); 1252113db2ddSJeff Roberson if (next <= lastlbn) 1253113db2ddSJeff Roberson continue; 12545cc52631SKirk McKusick indir_trunc(ino, nlbn, nblk, lastlbn, dp); 1255113db2ddSJeff Roberson /* If all of this indirect was reclaimed, free it. */ 1256113db2ddSJeff Roberson nlbn = next - lbnadd; 1257113db2ddSJeff Roberson if (nlbn < lastlbn) 1258113db2ddSJeff Roberson continue; 1259113db2ddSJeff Roberson } else { 1260113db2ddSJeff Roberson nlbn = -lbn + i * lbnadd; 1261113db2ddSJeff Roberson if (nlbn < lastlbn) 1262113db2ddSJeff Roberson continue; 1263113db2ddSJeff Roberson } 12645cc52631SKirk McKusick isdirty = 1; 1265460ed610SKirk McKusick blk_free(ino, nblk, 0, fs->fs_frag); 12665cc52631SKirk McKusick IBLK_SET(bp, i, 0); 1267113db2ddSJeff Roberson } 12685cc52631SKirk McKusick if (isdirty) 12695cc52631SKirk McKusick dirty(bp); 12705cc52631SKirk McKusick brelse(bp); 1271113db2ddSJeff Roberson } 1272113db2ddSJeff Roberson 1273113db2ddSJeff Roberson /* 1274113db2ddSJeff Roberson * Truncate an inode to the minimum of the given size or the last populated 1275113db2ddSJeff Roberson * block after any over size have been discarded. The kernel would allocate 1276113db2ddSJeff Roberson * the last block in the file but fsck does not and neither do we. This 1277113db2ddSJeff Roberson * code never extends files, only shrinks them. 1278113db2ddSJeff Roberson */ 1279113db2ddSJeff Roberson static void 1280113db2ddSJeff Roberson ino_trunc(ino_t ino, off_t size) 1281113db2ddSJeff Roberson { 12825cc52631SKirk McKusick struct inode ip; 12835cc52631SKirk McKusick union dinode *dp; 12845cc52631SKirk McKusick struct bufarea *bp; 1285113db2ddSJeff Roberson ufs2_daddr_t bn; 1286113db2ddSJeff Roberson uint64_t totalfrags; 1287113db2ddSJeff Roberson ufs_lbn_t nextlbn; 1288113db2ddSJeff Roberson ufs_lbn_t lastlbn; 1289113db2ddSJeff Roberson ufs_lbn_t tmpval; 1290113db2ddSJeff Roberson ufs_lbn_t lbn; 1291113db2ddSJeff Roberson ufs_lbn_t i; 12925cc52631SKirk McKusick int blksize, frags; 1293113db2ddSJeff Roberson off_t cursize; 1294113db2ddSJeff Roberson off_t off; 1295113db2ddSJeff Roberson int mode; 1296113db2ddSJeff Roberson 12975cc52631SKirk McKusick ginode(ino, &ip); 12985cc52631SKirk McKusick dp = ip.i_dp; 12995cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 13005cc52631SKirk McKusick cursize = DIP(dp, di_size); 1301460ed610SKirk McKusick /* If no size change, nothing to do */ 1302460ed610SKirk McKusick if (size == cursize) { 1303460ed610SKirk McKusick irelse(&ip); 1304460ed610SKirk McKusick return; 1305460ed610SKirk McKusick } 1306113db2ddSJeff Roberson if (debug) 1307623d7cb6SMatthew D Fleming printf("Truncating ino %ju, mode %o to size %jd from size %jd\n", 1308623d7cb6SMatthew D Fleming (uintmax_t)ino, mode, size, cursize); 1309113db2ddSJeff Roberson 1310113db2ddSJeff Roberson /* Skip datablocks for short links and devices. */ 1311d8ba45e2SEd Maste if (mode == 0 || mode == IFBLK || mode == IFCHR || 13125cc52631SKirk McKusick (mode == IFLNK && cursize < fs->fs_maxsymlinklen)) { 13135cc52631SKirk McKusick irelse(&ip); 1314113db2ddSJeff Roberson return; 13155cc52631SKirk McKusick } 1316113db2ddSJeff Roberson /* Don't extend. */ 13175cc52631SKirk McKusick if (size > cursize) { 13185cc52631SKirk McKusick irelse(&ip); 13195cc52631SKirk McKusick return; 13205cc52631SKirk McKusick } 13215cc52631SKirk McKusick if ((DIP(dp, di_flags) & SF_SNAPSHOT) != 0) { 13225cc52631SKirk McKusick if (size > 0) 13235cc52631SKirk McKusick err_suj("Partial truncation of ino %ju snapshot file\n", 13245cc52631SKirk McKusick (uintmax_t)ino); 1325460ed610SKirk McKusick snapremove(ino); 13265cc52631SKirk McKusick } 1327113db2ddSJeff Roberson lastlbn = lblkno(fs, blkroundup(fs, size)); 13281dc349abSEd Maste for (i = lastlbn; i < UFS_NDADDR; i++) { 13295cc52631SKirk McKusick if ((bn = DIP(dp, di_db[i])) == 0) 1330113db2ddSJeff Roberson continue; 13315cc52631SKirk McKusick blksize = sblksize(fs, cursize, i); 1332460ed610SKirk McKusick blk_free(ino, bn, 0, numfrags(fs, blksize)); 13335cc52631SKirk McKusick DIP_SET(dp, di_db[i], 0); 1334113db2ddSJeff Roberson } 1335113db2ddSJeff Roberson /* 1336113db2ddSJeff Roberson * Follow indirect blocks, freeing anything required. 1337113db2ddSJeff Roberson */ 13381dc349abSEd Maste for (i = 0, tmpval = NINDIR(fs), lbn = UFS_NDADDR; i < UFS_NIADDR; i++, 1339113db2ddSJeff Roberson lbn = nextlbn) { 1340113db2ddSJeff Roberson nextlbn = lbn + tmpval; 1341113db2ddSJeff Roberson tmpval *= NINDIR(fs); 1342113db2ddSJeff Roberson /* If we're not freeing any in this indirect range skip it. */ 1343113db2ddSJeff Roberson if (lastlbn >= nextlbn) 1344113db2ddSJeff Roberson continue; 1345460ed610SKirk McKusick if ((bn = DIP(dp, di_ib[i])) == 0) 1346113db2ddSJeff Roberson continue; 1347460ed610SKirk McKusick indir_trunc(ino, -lbn - i, bn, lastlbn, dp); 1348113db2ddSJeff Roberson /* If we freed everything in this indirect free the indir. */ 1349113db2ddSJeff Roberson if (lastlbn > lbn) 1350113db2ddSJeff Roberson continue; 1351460ed610SKirk McKusick blk_free(ino, bn, 0, fs->fs_frag); 13525cc52631SKirk McKusick DIP_SET(dp, di_ib[i], 0); 1353113db2ddSJeff Roberson } 1354113db2ddSJeff Roberson /* 1355113db2ddSJeff Roberson * Now that we've freed any whole blocks that exceed the desired 1356113db2ddSJeff Roberson * truncation size, figure out how many blocks remain and what the 1357113db2ddSJeff Roberson * last populated lbn is. We will set the size to this last lbn 1358113db2ddSJeff Roberson * rather than worrying about allocating the final lbn as the kernel 1359113db2ddSJeff Roberson * would've done. This is consistent with normal fsck behavior. 1360113db2ddSJeff Roberson */ 1361113db2ddSJeff Roberson visitlbn = 0; 13625cc52631SKirk McKusick totalfrags = ino_visit(dp, ino, null_visit, VISIT_INDIR | VISIT_EXT); 1363113db2ddSJeff Roberson if (size > lblktosize(fs, visitlbn + 1)) 1364113db2ddSJeff Roberson size = lblktosize(fs, visitlbn + 1); 1365113db2ddSJeff Roberson /* 1366113db2ddSJeff Roberson * If we're truncating direct blocks we have to adjust frags 1367113db2ddSJeff Roberson * accordingly. 1368113db2ddSJeff Roberson */ 13691dc349abSEd Maste if (visitlbn < UFS_NDADDR && totalfrags) { 1370113db2ddSJeff Roberson long oldspace, newspace; 1371113db2ddSJeff Roberson 13725cc52631SKirk McKusick bn = DIP(dp, di_db[visitlbn]); 1373113db2ddSJeff Roberson if (bn == 0) 1374623d7cb6SMatthew D Fleming err_suj("Bad blk at ino %ju lbn %jd\n", 1375623d7cb6SMatthew D Fleming (uintmax_t)ino, visitlbn); 1376113db2ddSJeff Roberson oldspace = sblksize(fs, cursize, visitlbn); 1377113db2ddSJeff Roberson newspace = sblksize(fs, size, visitlbn); 1378113db2ddSJeff Roberson if (oldspace != newspace) { 1379113db2ddSJeff Roberson bn += numfrags(fs, newspace); 1380113db2ddSJeff Roberson frags = numfrags(fs, oldspace - newspace); 1381460ed610SKirk McKusick blk_free(ino, bn, 0, frags); 1382113db2ddSJeff Roberson totalfrags -= frags; 1383113db2ddSJeff Roberson } 1384113db2ddSJeff Roberson } 13855cc52631SKirk McKusick DIP_SET(dp, di_blocks, fsbtodb(fs, totalfrags)); 13865cc52631SKirk McKusick DIP_SET(dp, di_size, size); 13875cc52631SKirk McKusick inodirty(&ip); 1388113db2ddSJeff Roberson /* 1389113db2ddSJeff Roberson * If we've truncated into the middle of a block or frag we have 1390113db2ddSJeff Roberson * to zero it here. Otherwise the file could extend into 1391113db2ddSJeff Roberson * uninitialized space later. 1392113db2ddSJeff Roberson */ 1393113db2ddSJeff Roberson off = blkoff(fs, size); 13945cc52631SKirk McKusick if (off && DIP(dp, di_mode) != IFDIR) { 1395113db2ddSJeff Roberson long clrsize; 1396113db2ddSJeff Roberson 13975cc52631SKirk McKusick bn = ino_blkatoff(dp, ino, visitlbn, &frags, NULL); 1398113db2ddSJeff Roberson if (bn == 0) 1399623d7cb6SMatthew D Fleming err_suj("Block missing from ino %ju at lbn %jd\n", 1400623d7cb6SMatthew D Fleming (uintmax_t)ino, visitlbn); 1401113db2ddSJeff Roberson clrsize = frags * fs->fs_fsize; 14025cc52631SKirk McKusick bp = getdatablk(bn, clrsize, BT_DATA); 14035cc52631SKirk McKusick if (bp->b_errs != 0) 14045cc52631SKirk McKusick err_suj("ino_trunc: UNRECOVERABLE I/O ERROR"); 1405113db2ddSJeff Roberson clrsize -= off; 14065cc52631SKirk McKusick bzero(&bp->b_un.b_buf[off], clrsize); 14075cc52631SKirk McKusick dirty(bp); 14085cc52631SKirk McKusick brelse(bp); 1409113db2ddSJeff Roberson } 14105cc52631SKirk McKusick irelse(&ip); 1411113db2ddSJeff Roberson return; 1412113db2ddSJeff Roberson } 1413113db2ddSJeff Roberson 1414113db2ddSJeff Roberson /* 1415113db2ddSJeff Roberson * Process records available for one inode and determine whether the 1416113db2ddSJeff Roberson * link count is correct or needs adjusting. 1417113db2ddSJeff Roberson */ 1418113db2ddSJeff Roberson static void 1419113db2ddSJeff Roberson ino_check(struct suj_ino *sino) 1420113db2ddSJeff Roberson { 1421113db2ddSJeff Roberson struct suj_rec *srec; 1422113db2ddSJeff Roberson struct jrefrec *rrec; 1423113db2ddSJeff Roberson nlink_t dotlinks; 142469921123SKonstantin Belousov nlink_t newlinks; 142569921123SKonstantin Belousov nlink_t removes; 142669921123SKonstantin Belousov nlink_t nlink; 1427113db2ddSJeff Roberson ino_t ino; 1428113db2ddSJeff Roberson int isdot; 1429113db2ddSJeff Roberson int isat; 1430113db2ddSJeff Roberson int mode; 1431113db2ddSJeff Roberson 1432113db2ddSJeff Roberson if (sino->si_hasrecs == 0) 1433113db2ddSJeff Roberson return; 1434113db2ddSJeff Roberson ino = sino->si_ino; 1435113db2ddSJeff Roberson rrec = (struct jrefrec *)TAILQ_FIRST(&sino->si_recs)->sr_rec; 1436113db2ddSJeff Roberson nlink = rrec->jr_nlink; 1437113db2ddSJeff Roberson newlinks = 0; 1438113db2ddSJeff Roberson dotlinks = 0; 1439113db2ddSJeff Roberson removes = sino->si_nlinkadj; 1440113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sino->si_recs, sr_next) { 1441113db2ddSJeff Roberson rrec = (struct jrefrec *)srec->sr_rec; 1442113db2ddSJeff Roberson isat = ino_isat(rrec->jr_parent, rrec->jr_diroff, 1443113db2ddSJeff Roberson rrec->jr_ino, &mode, &isdot); 1444d8ba45e2SEd Maste if (isat && (mode & IFMT) != (rrec->jr_mode & IFMT)) 1445edad6026SXin LI err_suj("Inode mode/directory type mismatch %o != %o\n", 1446113db2ddSJeff Roberson mode, rrec->jr_mode); 1447113db2ddSJeff Roberson if (debug) 14481c324569SKonstantin Belousov printf("jrefrec: op %d ino %ju, nlink %ju, parent %ju, " 1449113db2ddSJeff Roberson "diroff %jd, mode %o, isat %d, isdot %d\n", 1450623d7cb6SMatthew D Fleming rrec->jr_op, (uintmax_t)rrec->jr_ino, 14511c324569SKonstantin Belousov (uintmax_t)rrec->jr_nlink, 14521c324569SKonstantin Belousov (uintmax_t)rrec->jr_parent, 14531c324569SKonstantin Belousov (uintmax_t)rrec->jr_diroff, 1454623d7cb6SMatthew D Fleming rrec->jr_mode, isat, isdot); 1455d8ba45e2SEd Maste mode = rrec->jr_mode & IFMT; 1456113db2ddSJeff Roberson if (rrec->jr_op == JOP_REMREF) 1457113db2ddSJeff Roberson removes++; 1458113db2ddSJeff Roberson newlinks += isat; 1459113db2ddSJeff Roberson if (isdot) 1460113db2ddSJeff Roberson dotlinks += isat; 1461113db2ddSJeff Roberson } 1462113db2ddSJeff Roberson /* 1463113db2ddSJeff Roberson * The number of links that remain are the starting link count 1464113db2ddSJeff Roberson * subtracted by the total number of removes with the total 1465113db2ddSJeff Roberson * links discovered back in. An incomplete remove thus 1466113db2ddSJeff Roberson * makes no change to the link count but an add increases 1467113db2ddSJeff Roberson * by one. 1468113db2ddSJeff Roberson */ 1469113db2ddSJeff Roberson if (debug) 14701c324569SKonstantin Belousov printf( 14711c324569SKonstantin Belousov "ino %ju nlink %ju newlinks %ju removes %ju dotlinks %ju\n", 14721c324569SKonstantin Belousov (uintmax_t)ino, (uintmax_t)nlink, (uintmax_t)newlinks, 14731c324569SKonstantin Belousov (uintmax_t)removes, (uintmax_t)dotlinks); 1474113db2ddSJeff Roberson nlink += newlinks; 1475113db2ddSJeff Roberson nlink -= removes; 1476113db2ddSJeff Roberson sino->si_linkadj = 1; 1477113db2ddSJeff Roberson sino->si_nlink = nlink; 1478113db2ddSJeff Roberson sino->si_dotlinks = dotlinks; 1479113db2ddSJeff Roberson sino->si_mode = mode; 1480113db2ddSJeff Roberson ino_adjust(sino); 1481113db2ddSJeff Roberson } 1482113db2ddSJeff Roberson 1483113db2ddSJeff Roberson /* 1484113db2ddSJeff Roberson * Process records available for one block and determine whether it is 1485113db2ddSJeff Roberson * still allocated and whether the owning inode needs to be updated or 1486113db2ddSJeff Roberson * a free completed. 1487113db2ddSJeff Roberson */ 1488113db2ddSJeff Roberson static void 1489113db2ddSJeff Roberson blk_check(struct suj_blk *sblk) 1490113db2ddSJeff Roberson { 1491113db2ddSJeff Roberson struct suj_rec *srec; 1492113db2ddSJeff Roberson struct jblkrec *brec; 1493113db2ddSJeff Roberson struct suj_ino *sino; 1494113db2ddSJeff Roberson ufs2_daddr_t blk; 1495113db2ddSJeff Roberson int mask; 1496113db2ddSJeff Roberson int frags; 1497113db2ddSJeff Roberson int isat; 1498113db2ddSJeff Roberson 1499113db2ddSJeff Roberson /* 1500113db2ddSJeff Roberson * Each suj_blk actually contains records for any fragments in that 1501113db2ddSJeff Roberson * block. As a result we must evaluate each record individually. 1502113db2ddSJeff Roberson */ 1503113db2ddSJeff Roberson sino = NULL; 1504113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 1505113db2ddSJeff Roberson brec = (struct jblkrec *)srec->sr_rec; 1506113db2ddSJeff Roberson frags = brec->jb_frags; 1507113db2ddSJeff Roberson blk = brec->jb_blkno + brec->jb_oldfrags; 1508113db2ddSJeff Roberson isat = blk_isat(brec->jb_ino, brec->jb_lbn, blk, &frags); 1509113db2ddSJeff Roberson if (sino == NULL || sino->si_ino != brec->jb_ino) { 1510113db2ddSJeff Roberson sino = ino_lookup(brec->jb_ino, 1); 1511113db2ddSJeff Roberson sino->si_blkadj = 1; 1512113db2ddSJeff Roberson } 1513113db2ddSJeff Roberson if (debug) 1514623d7cb6SMatthew D Fleming printf("op %d blk %jd ino %ju lbn %jd frags %d isat %d (%d)\n", 1515623d7cb6SMatthew D Fleming brec->jb_op, blk, (uintmax_t)brec->jb_ino, 1516623d7cb6SMatthew D Fleming brec->jb_lbn, brec->jb_frags, isat, frags); 1517113db2ddSJeff Roberson /* 1518113db2ddSJeff Roberson * If we found the block at this address we still have to 1519113db2ddSJeff Roberson * determine if we need to free the tail end that was 1520113db2ddSJeff Roberson * added by adding contiguous fragments from the same block. 1521113db2ddSJeff Roberson */ 1522113db2ddSJeff Roberson if (isat == 1) { 1523113db2ddSJeff Roberson if (frags == brec->jb_frags) 1524113db2ddSJeff Roberson continue; 1525113db2ddSJeff Roberson mask = blk_freemask(blk, brec->jb_ino, brec->jb_lbn, 1526113db2ddSJeff Roberson brec->jb_frags); 1527113db2ddSJeff Roberson mask >>= frags; 1528113db2ddSJeff Roberson blk += frags; 1529113db2ddSJeff Roberson frags = brec->jb_frags - frags; 1530460ed610SKirk McKusick blk_free(brec->jb_ino, blk, mask, frags); 1531113db2ddSJeff Roberson continue; 1532113db2ddSJeff Roberson } 1533113db2ddSJeff Roberson /* 1534113db2ddSJeff Roberson * The block wasn't found, attempt to free it. It won't be 1535113db2ddSJeff Roberson * freed if it was actually reallocated. If this was an 1536113db2ddSJeff Roberson * allocation we don't want to follow indirects as they 1537113db2ddSJeff Roberson * may not be written yet. Any children of the indirect will 1538113db2ddSJeff Roberson * have their own records. If it's a free we need to 1539113db2ddSJeff Roberson * recursively free children. 1540113db2ddSJeff Roberson */ 1541113db2ddSJeff Roberson blk_free_lbn(blk, brec->jb_ino, brec->jb_lbn, brec->jb_frags, 1542113db2ddSJeff Roberson brec->jb_op == JOP_FREEBLK); 1543113db2ddSJeff Roberson } 1544113db2ddSJeff Roberson } 1545113db2ddSJeff Roberson 1546113db2ddSJeff Roberson /* 1547113db2ddSJeff Roberson * Walk the list of inode records for this cg and resolve moved and duplicate 1548113db2ddSJeff Roberson * inode references now that we have a complete picture. 1549113db2ddSJeff Roberson */ 1550113db2ddSJeff Roberson static void 1551113db2ddSJeff Roberson cg_build(struct suj_cg *sc) 1552113db2ddSJeff Roberson { 1553113db2ddSJeff Roberson struct suj_ino *sino; 1554113db2ddSJeff Roberson int i; 1555113db2ddSJeff Roberson 15565cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1557113db2ddSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) 1558113db2ddSJeff Roberson ino_build(sino); 1559113db2ddSJeff Roberson } 1560113db2ddSJeff Roberson 1561113db2ddSJeff Roberson /* 1562113db2ddSJeff Roberson * Handle inodes requiring truncation. This must be done prior to 1563113db2ddSJeff Roberson * looking up any inodes in directories. 1564113db2ddSJeff Roberson */ 1565113db2ddSJeff Roberson static void 1566113db2ddSJeff Roberson cg_trunc(struct suj_cg *sc) 1567113db2ddSJeff Roberson { 1568113db2ddSJeff Roberson struct suj_ino *sino; 1569113db2ddSJeff Roberson int i; 1570113db2ddSJeff Roberson 15715cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) { 1572280e091aSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) { 1573113db2ddSJeff Roberson if (sino->si_trunc) { 1574113db2ddSJeff Roberson ino_trunc(sino->si_ino, 1575113db2ddSJeff Roberson sino->si_trunc->jt_size); 1576280e091aSJeff Roberson sino->si_blkadj = 0; 1577113db2ddSJeff Roberson sino->si_trunc = NULL; 1578113db2ddSJeff Roberson } 1579280e091aSJeff Roberson if (sino->si_blkadj) 1580280e091aSJeff Roberson ino_adjblks(sino); 1581280e091aSJeff Roberson } 1582280e091aSJeff Roberson } 1583113db2ddSJeff Roberson } 1584113db2ddSJeff Roberson 1585364e7245SKonstantin Belousov static void 1586364e7245SKonstantin Belousov cg_adj_blk(struct suj_cg *sc) 1587364e7245SKonstantin Belousov { 1588364e7245SKonstantin Belousov struct suj_ino *sino; 1589364e7245SKonstantin Belousov int i; 1590364e7245SKonstantin Belousov 15915cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) { 1592364e7245SKonstantin Belousov LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) { 1593364e7245SKonstantin Belousov if (sino->si_blkadj) 1594364e7245SKonstantin Belousov ino_adjblks(sino); 1595364e7245SKonstantin Belousov } 1596364e7245SKonstantin Belousov } 1597364e7245SKonstantin Belousov } 1598364e7245SKonstantin Belousov 1599113db2ddSJeff Roberson /* 1600113db2ddSJeff Roberson * Free any partially allocated blocks and then resolve inode block 1601113db2ddSJeff Roberson * counts. 1602113db2ddSJeff Roberson */ 1603113db2ddSJeff Roberson static void 1604113db2ddSJeff Roberson cg_check_blk(struct suj_cg *sc) 1605113db2ddSJeff Roberson { 1606113db2ddSJeff Roberson struct suj_blk *sblk; 1607113db2ddSJeff Roberson int i; 1608113db2ddSJeff Roberson 1609113db2ddSJeff Roberson 16105cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1611113db2ddSJeff Roberson LIST_FOREACH(sblk, &sc->sc_blkhash[i], sb_next) 1612113db2ddSJeff Roberson blk_check(sblk); 1613113db2ddSJeff Roberson } 1614113db2ddSJeff Roberson 1615113db2ddSJeff Roberson /* 1616113db2ddSJeff Roberson * Walk the list of inode records for this cg, recovering any 1617113db2ddSJeff Roberson * changes which were not complete at the time of crash. 1618113db2ddSJeff Roberson */ 1619113db2ddSJeff Roberson static void 1620113db2ddSJeff Roberson cg_check_ino(struct suj_cg *sc) 1621113db2ddSJeff Roberson { 1622113db2ddSJeff Roberson struct suj_ino *sino; 1623113db2ddSJeff Roberson int i; 1624113db2ddSJeff Roberson 16255cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1626113db2ddSJeff Roberson LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) 1627113db2ddSJeff Roberson ino_check(sino); 1628113db2ddSJeff Roberson } 1629113db2ddSJeff Roberson 1630113db2ddSJeff Roberson static void 1631113db2ddSJeff Roberson cg_apply(void (*apply)(struct suj_cg *)) 1632113db2ddSJeff Roberson { 1633113db2ddSJeff Roberson struct suj_cg *scg; 1634113db2ddSJeff Roberson int i; 1635113db2ddSJeff Roberson 16365cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 1637113db2ddSJeff Roberson LIST_FOREACH(scg, &cghash[i], sc_next) 1638113db2ddSJeff Roberson apply(scg); 1639113db2ddSJeff Roberson } 1640113db2ddSJeff Roberson 1641113db2ddSJeff Roberson /* 1642113db2ddSJeff Roberson * Process the unlinked but referenced file list. Freeing all inodes. 1643113db2ddSJeff Roberson */ 1644113db2ddSJeff Roberson static void 1645113db2ddSJeff Roberson ino_unlinked(void) 1646113db2ddSJeff Roberson { 16475cc52631SKirk McKusick struct inode ip; 16485cc52631SKirk McKusick union dinode *dp; 1649113db2ddSJeff Roberson uint16_t mode; 1650113db2ddSJeff Roberson ino_t inon; 1651113db2ddSJeff Roberson ino_t ino; 1652113db2ddSJeff Roberson 1653113db2ddSJeff Roberson ino = fs->fs_sujfree; 1654113db2ddSJeff Roberson fs->fs_sujfree = 0; 1655113db2ddSJeff Roberson while (ino != 0) { 16565cc52631SKirk McKusick ginode(ino, &ip); 16575cc52631SKirk McKusick dp = ip.i_dp; 16585cc52631SKirk McKusick mode = DIP(dp, di_mode) & IFMT; 16595cc52631SKirk McKusick inon = DIP(dp, di_freelink); 16605cc52631SKirk McKusick DIP_SET(dp, di_freelink, 0); 16615cc52631SKirk McKusick inodirty(&ip); 1662113db2ddSJeff Roberson /* 1663113db2ddSJeff Roberson * XXX Should this be an errx? 1664113db2ddSJeff Roberson */ 16655cc52631SKirk McKusick if (DIP(dp, di_nlink) == 0) { 1666113db2ddSJeff Roberson if (debug) 1667623d7cb6SMatthew D Fleming printf("Freeing unlinked ino %ju mode %o\n", 1668e25a029eSMatthew D Fleming (uintmax_t)ino, mode); 16695cc52631SKirk McKusick ino_reclaim(&ip, ino, mode); 1670113db2ddSJeff Roberson } else if (debug) 1671623d7cb6SMatthew D Fleming printf("Skipping ino %ju mode %o with link %d\n", 16725cc52631SKirk McKusick (uintmax_t)ino, mode, DIP(dp, di_nlink)); 1673113db2ddSJeff Roberson ino = inon; 16745cc52631SKirk McKusick irelse(&ip); 1675113db2ddSJeff Roberson } 1676113db2ddSJeff Roberson } 1677113db2ddSJeff Roberson 1678113db2ddSJeff Roberson /* 1679113db2ddSJeff Roberson * Append a new record to the list of records requiring processing. 1680113db2ddSJeff Roberson */ 1681113db2ddSJeff Roberson static void 1682113db2ddSJeff Roberson ino_append(union jrec *rec) 1683113db2ddSJeff Roberson { 1684113db2ddSJeff Roberson struct jrefrec *refrec; 1685113db2ddSJeff Roberson struct jmvrec *mvrec; 1686113db2ddSJeff Roberson struct suj_ino *sino; 1687113db2ddSJeff Roberson struct suj_rec *srec; 1688113db2ddSJeff Roberson 1689113db2ddSJeff Roberson mvrec = &rec->rec_jmvrec; 1690113db2ddSJeff Roberson refrec = &rec->rec_jrefrec; 1691113db2ddSJeff Roberson if (debug && mvrec->jm_op == JOP_MVREF) 16921c324569SKonstantin Belousov printf("ino move: ino %ju, parent %ju, " 16931c324569SKonstantin Belousov "diroff %jd, oldoff %jd\n", 16941c324569SKonstantin Belousov (uintmax_t)mvrec->jm_ino, (uintmax_t)mvrec->jm_parent, 16951c324569SKonstantin Belousov (uintmax_t)mvrec->jm_newoff, (uintmax_t)mvrec->jm_oldoff); 1696113db2ddSJeff Roberson else if (debug && 1697113db2ddSJeff Roberson (refrec->jr_op == JOP_ADDREF || refrec->jr_op == JOP_REMREF)) 16981c324569SKonstantin Belousov printf("ino ref: op %d, ino %ju, nlink %ju, " 16991c324569SKonstantin Belousov "parent %ju, diroff %jd\n", 17001c324569SKonstantin Belousov refrec->jr_op, (uintmax_t)refrec->jr_ino, 17011c324569SKonstantin Belousov (uintmax_t)refrec->jr_nlink, 17021c324569SKonstantin Belousov (uintmax_t)refrec->jr_parent, (uintmax_t)refrec->jr_diroff); 1703113db2ddSJeff Roberson sino = ino_lookup(((struct jrefrec *)rec)->jr_ino, 1); 1704113db2ddSJeff Roberson sino->si_hasrecs = 1; 1705113db2ddSJeff Roberson srec = errmalloc(sizeof(*srec)); 1706113db2ddSJeff Roberson srec->sr_rec = rec; 1707113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_newrecs, srec, sr_next); 1708113db2ddSJeff Roberson } 1709113db2ddSJeff Roberson 1710113db2ddSJeff Roberson /* 1711113db2ddSJeff Roberson * Add a reference adjustment to the sino list and eliminate dups. The 1712113db2ddSJeff Roberson * primary loop in ino_build_ref() checks for dups but new ones may be 1713113db2ddSJeff Roberson * created as a result of offset adjustments. 1714113db2ddSJeff Roberson */ 1715113db2ddSJeff Roberson static void 1716113db2ddSJeff Roberson ino_add_ref(struct suj_ino *sino, struct suj_rec *srec) 1717113db2ddSJeff Roberson { 1718113db2ddSJeff Roberson struct jrefrec *refrec; 1719113db2ddSJeff Roberson struct suj_rec *srn; 1720113db2ddSJeff Roberson struct jrefrec *rrn; 1721113db2ddSJeff Roberson 1722113db2ddSJeff Roberson refrec = (struct jrefrec *)srec->sr_rec; 1723113db2ddSJeff Roberson /* 1724113db2ddSJeff Roberson * We walk backwards so that the oldest link count is preserved. If 1725113db2ddSJeff Roberson * an add record conflicts with a remove keep the remove. Redundant 1726113db2ddSJeff Roberson * removes are eliminated in ino_build_ref. Otherwise we keep the 1727113db2ddSJeff Roberson * oldest record at a given location. 1728113db2ddSJeff Roberson */ 1729113db2ddSJeff Roberson for (srn = TAILQ_LAST(&sino->si_recs, srechd); srn; 1730113db2ddSJeff Roberson srn = TAILQ_PREV(srn, srechd, sr_next)) { 1731113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 1732113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 1733113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 1734113db2ddSJeff Roberson continue; 1735113db2ddSJeff Roberson if (rrn->jr_op == JOP_REMREF || refrec->jr_op == JOP_ADDREF) { 1736113db2ddSJeff Roberson rrn->jr_mode = refrec->jr_mode; 1737113db2ddSJeff Roberson return; 1738113db2ddSJeff Roberson } 1739113db2ddSJeff Roberson /* 1740113db2ddSJeff Roberson * Adding a remove. 1741113db2ddSJeff Roberson * 1742113db2ddSJeff Roberson * Replace the record in place with the old nlink in case 1743113db2ddSJeff Roberson * we replace the head of the list. Abandon srec as a dup. 1744113db2ddSJeff Roberson */ 1745113db2ddSJeff Roberson refrec->jr_nlink = rrn->jr_nlink; 1746113db2ddSJeff Roberson srn->sr_rec = srec->sr_rec; 1747113db2ddSJeff Roberson return; 1748113db2ddSJeff Roberson } 1749113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_recs, srec, sr_next); 1750113db2ddSJeff Roberson } 1751113db2ddSJeff Roberson 1752113db2ddSJeff Roberson /* 1753113db2ddSJeff Roberson * Create a duplicate of a reference at a previous location. 1754113db2ddSJeff Roberson */ 1755113db2ddSJeff Roberson static void 1756113db2ddSJeff Roberson ino_dup_ref(struct suj_ino *sino, struct jrefrec *refrec, off_t diroff) 1757113db2ddSJeff Roberson { 1758113db2ddSJeff Roberson struct jrefrec *rrn; 1759113db2ddSJeff Roberson struct suj_rec *srn; 1760113db2ddSJeff Roberson 1761113db2ddSJeff Roberson rrn = errmalloc(sizeof(*refrec)); 1762113db2ddSJeff Roberson *rrn = *refrec; 1763113db2ddSJeff Roberson rrn->jr_op = JOP_ADDREF; 1764113db2ddSJeff Roberson rrn->jr_diroff = diroff; 1765113db2ddSJeff Roberson srn = errmalloc(sizeof(*srn)); 1766113db2ddSJeff Roberson srn->sr_rec = (union jrec *)rrn; 1767113db2ddSJeff Roberson ino_add_ref(sino, srn); 1768113db2ddSJeff Roberson } 1769113db2ddSJeff Roberson 1770113db2ddSJeff Roberson /* 1771113db2ddSJeff Roberson * Add a reference to the list at all known locations. We follow the offset 1772113db2ddSJeff Roberson * changes for a single instance and create duplicate add refs at each so 1773113db2ddSJeff Roberson * that we can tolerate any version of the directory block. Eliminate 1774113db2ddSJeff Roberson * removes which collide with adds that are seen in the journal. They should 1775113db2ddSJeff Roberson * not adjust the link count down. 1776113db2ddSJeff Roberson */ 1777113db2ddSJeff Roberson static void 1778113db2ddSJeff Roberson ino_build_ref(struct suj_ino *sino, struct suj_rec *srec) 1779113db2ddSJeff Roberson { 1780113db2ddSJeff Roberson struct jrefrec *refrec; 1781113db2ddSJeff Roberson struct jmvrec *mvrec; 1782113db2ddSJeff Roberson struct suj_rec *srp; 1783113db2ddSJeff Roberson struct suj_rec *srn; 1784113db2ddSJeff Roberson struct jrefrec *rrn; 1785113db2ddSJeff Roberson off_t diroff; 1786113db2ddSJeff Roberson 1787113db2ddSJeff Roberson refrec = (struct jrefrec *)srec->sr_rec; 1788113db2ddSJeff Roberson /* 1789113db2ddSJeff Roberson * Search for a mvrec that matches this offset. Whether it's an add 1790113db2ddSJeff Roberson * or a remove we can delete the mvref after creating a dup record in 1791113db2ddSJeff Roberson * the old location. 1792113db2ddSJeff Roberson */ 1793113db2ddSJeff Roberson if (!TAILQ_EMPTY(&sino->si_movs)) { 1794113db2ddSJeff Roberson diroff = refrec->jr_diroff; 1795113db2ddSJeff Roberson for (srn = TAILQ_LAST(&sino->si_movs, srechd); srn; srn = srp) { 1796113db2ddSJeff Roberson srp = TAILQ_PREV(srn, srechd, sr_next); 1797113db2ddSJeff Roberson mvrec = (struct jmvrec *)srn->sr_rec; 1798113db2ddSJeff Roberson if (mvrec->jm_parent != refrec->jr_parent || 1799113db2ddSJeff Roberson mvrec->jm_newoff != diroff) 1800113db2ddSJeff Roberson continue; 1801113db2ddSJeff Roberson diroff = mvrec->jm_oldoff; 1802113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_movs, srn, sr_next); 1803edad6026SXin LI free(srn); 1804113db2ddSJeff Roberson ino_dup_ref(sino, refrec, diroff); 1805113db2ddSJeff Roberson } 1806113db2ddSJeff Roberson } 1807113db2ddSJeff Roberson /* 1808113db2ddSJeff Roberson * If a remove wasn't eliminated by an earlier add just append it to 1809113db2ddSJeff Roberson * the list. 1810113db2ddSJeff Roberson */ 1811113db2ddSJeff Roberson if (refrec->jr_op == JOP_REMREF) { 1812113db2ddSJeff Roberson ino_add_ref(sino, srec); 1813113db2ddSJeff Roberson return; 1814113db2ddSJeff Roberson } 1815113db2ddSJeff Roberson /* 1816113db2ddSJeff Roberson * Walk the list of records waiting to be added to the list. We 1817113db2ddSJeff Roberson * must check for moves that apply to our current offset and remove 1818113db2ddSJeff Roberson * them from the list. Remove any duplicates to eliminate removes 1819113db2ddSJeff Roberson * with corresponding adds. 1820113db2ddSJeff Roberson */ 1821113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(srn, &sino->si_newrecs, sr_next, srp) { 1822113db2ddSJeff Roberson switch (srn->sr_rec->rec_jrefrec.jr_op) { 1823113db2ddSJeff Roberson case JOP_ADDREF: 1824113db2ddSJeff Roberson /* 1825113db2ddSJeff Roberson * This should actually be an error we should 1826113db2ddSJeff Roberson * have a remove for every add journaled. 1827113db2ddSJeff Roberson */ 1828113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 1829113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 1830113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 1831113db2ddSJeff Roberson break; 1832113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 1833113db2ddSJeff Roberson break; 1834113db2ddSJeff Roberson case JOP_REMREF: 1835113db2ddSJeff Roberson /* 1836113db2ddSJeff Roberson * Once we remove the current iteration of the 1837113db2ddSJeff Roberson * record at this address we're done. 1838113db2ddSJeff Roberson */ 1839113db2ddSJeff Roberson rrn = (struct jrefrec *)srn->sr_rec; 1840113db2ddSJeff Roberson if (rrn->jr_parent != refrec->jr_parent || 1841113db2ddSJeff Roberson rrn->jr_diroff != refrec->jr_diroff) 1842113db2ddSJeff Roberson break; 1843113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 1844113db2ddSJeff Roberson ino_add_ref(sino, srec); 1845113db2ddSJeff Roberson return; 1846113db2ddSJeff Roberson case JOP_MVREF: 1847113db2ddSJeff Roberson /* 1848113db2ddSJeff Roberson * Update our diroff based on any moves that match 1849113db2ddSJeff Roberson * and remove the move. 1850113db2ddSJeff Roberson */ 1851113db2ddSJeff Roberson mvrec = (struct jmvrec *)srn->sr_rec; 1852113db2ddSJeff Roberson if (mvrec->jm_parent != refrec->jr_parent || 1853113db2ddSJeff Roberson mvrec->jm_oldoff != refrec->jr_diroff) 1854113db2ddSJeff Roberson break; 1855113db2ddSJeff Roberson ino_dup_ref(sino, refrec, mvrec->jm_oldoff); 1856113db2ddSJeff Roberson refrec->jr_diroff = mvrec->jm_newoff; 1857113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srn, sr_next); 1858113db2ddSJeff Roberson break; 1859113db2ddSJeff Roberson default: 1860edad6026SXin LI err_suj("ino_build_ref: Unknown op %d\n", 1861113db2ddSJeff Roberson srn->sr_rec->rec_jrefrec.jr_op); 1862113db2ddSJeff Roberson } 1863113db2ddSJeff Roberson } 1864113db2ddSJeff Roberson ino_add_ref(sino, srec); 1865113db2ddSJeff Roberson } 1866113db2ddSJeff Roberson 1867113db2ddSJeff Roberson /* 1868113db2ddSJeff Roberson * Walk the list of new records and add them in-order resolving any 1869113db2ddSJeff Roberson * dups and adjusted offsets. 1870113db2ddSJeff Roberson */ 1871113db2ddSJeff Roberson static void 1872113db2ddSJeff Roberson ino_build(struct suj_ino *sino) 1873113db2ddSJeff Roberson { 1874113db2ddSJeff Roberson struct suj_rec *srec; 1875113db2ddSJeff Roberson 1876113db2ddSJeff Roberson while ((srec = TAILQ_FIRST(&sino->si_newrecs)) != NULL) { 1877113db2ddSJeff Roberson TAILQ_REMOVE(&sino->si_newrecs, srec, sr_next); 1878113db2ddSJeff Roberson switch (srec->sr_rec->rec_jrefrec.jr_op) { 1879113db2ddSJeff Roberson case JOP_ADDREF: 1880113db2ddSJeff Roberson case JOP_REMREF: 1881113db2ddSJeff Roberson ino_build_ref(sino, srec); 1882113db2ddSJeff Roberson break; 1883113db2ddSJeff Roberson case JOP_MVREF: 1884113db2ddSJeff Roberson /* 1885113db2ddSJeff Roberson * Add this mvrec to the queue of pending mvs. 1886113db2ddSJeff Roberson */ 1887113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sino->si_movs, srec, sr_next); 1888113db2ddSJeff Roberson break; 1889113db2ddSJeff Roberson default: 1890edad6026SXin LI err_suj("ino_build: Unknown op %d\n", 1891113db2ddSJeff Roberson srec->sr_rec->rec_jrefrec.jr_op); 1892113db2ddSJeff Roberson } 1893113db2ddSJeff Roberson } 1894113db2ddSJeff Roberson if (TAILQ_EMPTY(&sino->si_recs)) 1895113db2ddSJeff Roberson sino->si_hasrecs = 0; 1896113db2ddSJeff Roberson } 1897113db2ddSJeff Roberson 1898113db2ddSJeff Roberson /* 1899113db2ddSJeff Roberson * Modify journal records so they refer to the base block number 1900113db2ddSJeff Roberson * and a start and end frag range. This is to facilitate the discovery 1901113db2ddSJeff Roberson * of overlapping fragment allocations. 1902113db2ddSJeff Roberson */ 1903113db2ddSJeff Roberson static void 1904113db2ddSJeff Roberson blk_build(struct jblkrec *blkrec) 1905113db2ddSJeff Roberson { 1906113db2ddSJeff Roberson struct suj_rec *srec; 1907113db2ddSJeff Roberson struct suj_blk *sblk; 1908113db2ddSJeff Roberson struct jblkrec *blkrn; 1909113db2ddSJeff Roberson ufs2_daddr_t blk; 1910113db2ddSJeff Roberson int frag; 1911113db2ddSJeff Roberson 1912113db2ddSJeff Roberson if (debug) 1913113db2ddSJeff Roberson printf("blk_build: op %d blkno %jd frags %d oldfrags %d " 19141c324569SKonstantin Belousov "ino %ju lbn %jd\n", 19151c324569SKonstantin Belousov blkrec->jb_op, (uintmax_t)blkrec->jb_blkno, 19161c324569SKonstantin Belousov blkrec->jb_frags, blkrec->jb_oldfrags, 19171c324569SKonstantin Belousov (uintmax_t)blkrec->jb_ino, (uintmax_t)blkrec->jb_lbn); 1918113db2ddSJeff Roberson 1919113db2ddSJeff Roberson blk = blknum(fs, blkrec->jb_blkno); 1920113db2ddSJeff Roberson frag = fragnum(fs, blkrec->jb_blkno); 1921b796bfceSKirk McKusick if (blkrec->jb_blkno < 0 || blk + fs->fs_frag - frag > fs->fs_size) 1922b796bfceSKirk McKusick err_suj("Out-of-bounds journal block number %jd\n", 1923b796bfceSKirk McKusick blkrec->jb_blkno); 1924113db2ddSJeff Roberson sblk = blk_lookup(blk, 1); 1925113db2ddSJeff Roberson /* 1926113db2ddSJeff Roberson * Rewrite the record using oldfrags to indicate the offset into 1927113db2ddSJeff Roberson * the block. Leave jb_frags as the actual allocated count. 1928113db2ddSJeff Roberson */ 1929113db2ddSJeff Roberson blkrec->jb_blkno -= frag; 1930113db2ddSJeff Roberson blkrec->jb_oldfrags = frag; 1931113db2ddSJeff Roberson if (blkrec->jb_oldfrags + blkrec->jb_frags > fs->fs_frag) 1932edad6026SXin LI err_suj("Invalid fragment count %d oldfrags %d\n", 1933113db2ddSJeff Roberson blkrec->jb_frags, frag); 1934113db2ddSJeff Roberson /* 1935113db2ddSJeff Roberson * Detect dups. If we detect a dup we always discard the oldest 1936113db2ddSJeff Roberson * record as it is superseded by the new record. This speeds up 1937113db2ddSJeff Roberson * later stages but also eliminates free records which are used 1938113db2ddSJeff Roberson * to indicate that the contents of indirects can be trusted. 1939113db2ddSJeff Roberson */ 1940113db2ddSJeff Roberson TAILQ_FOREACH(srec, &sblk->sb_recs, sr_next) { 1941113db2ddSJeff Roberson blkrn = (struct jblkrec *)srec->sr_rec; 1942113db2ddSJeff Roberson if (blkrn->jb_ino != blkrec->jb_ino || 1943113db2ddSJeff Roberson blkrn->jb_lbn != blkrec->jb_lbn || 1944113db2ddSJeff Roberson blkrn->jb_blkno != blkrec->jb_blkno || 1945113db2ddSJeff Roberson blkrn->jb_frags != blkrec->jb_frags || 1946113db2ddSJeff Roberson blkrn->jb_oldfrags != blkrec->jb_oldfrags) 1947113db2ddSJeff Roberson continue; 1948113db2ddSJeff Roberson if (debug) 1949113db2ddSJeff Roberson printf("Removed dup.\n"); 1950113db2ddSJeff Roberson /* Discard the free which is a dup with an alloc. */ 1951113db2ddSJeff Roberson if (blkrec->jb_op == JOP_FREEBLK) 1952113db2ddSJeff Roberson return; 1953113db2ddSJeff Roberson TAILQ_REMOVE(&sblk->sb_recs, srec, sr_next); 1954113db2ddSJeff Roberson free(srec); 1955113db2ddSJeff Roberson break; 1956113db2ddSJeff Roberson } 1957113db2ddSJeff Roberson srec = errmalloc(sizeof(*srec)); 1958113db2ddSJeff Roberson srec->sr_rec = (union jrec *)blkrec; 1959113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&sblk->sb_recs, srec, sr_next); 1960113db2ddSJeff Roberson } 1961113db2ddSJeff Roberson 1962113db2ddSJeff Roberson static void 1963113db2ddSJeff Roberson ino_build_trunc(struct jtrncrec *rec) 1964113db2ddSJeff Roberson { 1965113db2ddSJeff Roberson struct suj_ino *sino; 1966113db2ddSJeff Roberson 1967113db2ddSJeff Roberson if (debug) 19681c324569SKonstantin Belousov printf("ino_build_trunc: op %d ino %ju, size %jd\n", 19691c324569SKonstantin Belousov rec->jt_op, (uintmax_t)rec->jt_ino, 19701c324569SKonstantin Belousov (uintmax_t)rec->jt_size); 1971101a9ac0SKirk McKusick if (chkfilesize(IFREG, rec->jt_size) == 0) 1972101a9ac0SKirk McKusick err_suj("ino_build: truncation size too large %ju\n", 1973101a9ac0SKirk McKusick (intmax_t)rec->jt_size); 1974113db2ddSJeff Roberson sino = ino_lookup(rec->jt_ino, 1); 1975280e091aSJeff Roberson if (rec->jt_op == JOP_SYNC) { 1976280e091aSJeff Roberson sino->si_trunc = NULL; 1977280e091aSJeff Roberson return; 1978280e091aSJeff Roberson } 1979280e091aSJeff Roberson if (sino->si_trunc == NULL || sino->si_trunc->jt_size > rec->jt_size) 1980113db2ddSJeff Roberson sino->si_trunc = rec; 1981113db2ddSJeff Roberson } 1982113db2ddSJeff Roberson 1983113db2ddSJeff Roberson /* 1984113db2ddSJeff Roberson * Build up tables of the operations we need to recover. 1985113db2ddSJeff Roberson */ 1986113db2ddSJeff Roberson static void 1987113db2ddSJeff Roberson suj_build(void) 1988113db2ddSJeff Roberson { 1989113db2ddSJeff Roberson struct suj_seg *seg; 1990113db2ddSJeff Roberson union jrec *rec; 1991113db2ddSJeff Roberson int off; 19924235bafaSPedro F. Giffuni int i; 1993113db2ddSJeff Roberson 1994113db2ddSJeff Roberson TAILQ_FOREACH(seg, &allsegs, ss_next) { 1995113db2ddSJeff Roberson if (debug) 1996113db2ddSJeff Roberson printf("seg %jd has %d records, oldseq %jd.\n", 1997113db2ddSJeff Roberson seg->ss_rec.jsr_seq, seg->ss_rec.jsr_cnt, 1998113db2ddSJeff Roberson seg->ss_rec.jsr_oldest); 1999113db2ddSJeff Roberson off = 0; 2000113db2ddSJeff Roberson rec = (union jrec *)seg->ss_blk; 2001113db2ddSJeff Roberson for (i = 0; i < seg->ss_rec.jsr_cnt; off += JREC_SIZE, rec++) { 2002113db2ddSJeff Roberson /* skip the segrec. */ 20030947d19aSKonstantin Belousov if ((off % real_dev_bsize) == 0) 2004113db2ddSJeff Roberson continue; 2005113db2ddSJeff Roberson switch (rec->rec_jrefrec.jr_op) { 2006113db2ddSJeff Roberson case JOP_ADDREF: 2007113db2ddSJeff Roberson case JOP_REMREF: 2008113db2ddSJeff Roberson case JOP_MVREF: 2009113db2ddSJeff Roberson ino_append(rec); 2010113db2ddSJeff Roberson break; 2011113db2ddSJeff Roberson case JOP_NEWBLK: 2012113db2ddSJeff Roberson case JOP_FREEBLK: 2013113db2ddSJeff Roberson blk_build((struct jblkrec *)rec); 2014113db2ddSJeff Roberson break; 2015113db2ddSJeff Roberson case JOP_TRUNC: 201685e9da38SJeff Roberson case JOP_SYNC: 2017113db2ddSJeff Roberson ino_build_trunc((struct jtrncrec *)rec); 2018113db2ddSJeff Roberson break; 2019113db2ddSJeff Roberson default: 2020edad6026SXin LI err_suj("Unknown journal operation %d (%d)\n", 2021113db2ddSJeff Roberson rec->rec_jrefrec.jr_op, off); 2022113db2ddSJeff Roberson } 2023113db2ddSJeff Roberson i++; 2024113db2ddSJeff Roberson } 2025113db2ddSJeff Roberson } 2026113db2ddSJeff Roberson } 2027113db2ddSJeff Roberson 2028113db2ddSJeff Roberson /* 2029113db2ddSJeff Roberson * Prune the journal segments to those we care about based on the 2030113db2ddSJeff Roberson * oldest sequence in the newest segment. Order the segment list 2031113db2ddSJeff Roberson * based on sequence number. 2032113db2ddSJeff Roberson */ 2033113db2ddSJeff Roberson static void 2034113db2ddSJeff Roberson suj_prune(void) 2035113db2ddSJeff Roberson { 2036113db2ddSJeff Roberson struct suj_seg *seg; 2037113db2ddSJeff Roberson struct suj_seg *segn; 2038113db2ddSJeff Roberson uint64_t newseq; 2039113db2ddSJeff Roberson int discard; 2040113db2ddSJeff Roberson 2041113db2ddSJeff Roberson if (debug) 2042113db2ddSJeff Roberson printf("Pruning up to %jd\n", oldseq); 2043113db2ddSJeff Roberson /* First free the expired segments. */ 2044113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2045113db2ddSJeff Roberson if (seg->ss_rec.jsr_seq >= oldseq) 2046113db2ddSJeff Roberson continue; 2047113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2048113db2ddSJeff Roberson free(seg->ss_blk); 2049113db2ddSJeff Roberson free(seg); 2050113db2ddSJeff Roberson } 2051113db2ddSJeff Roberson /* Next ensure that segments are ordered properly. */ 2052113db2ddSJeff Roberson seg = TAILQ_FIRST(&allsegs); 2053113db2ddSJeff Roberson if (seg == NULL) { 2054113db2ddSJeff Roberson if (debug) 2055113db2ddSJeff Roberson printf("Empty journal\n"); 2056113db2ddSJeff Roberson return; 2057113db2ddSJeff Roberson } 2058113db2ddSJeff Roberson newseq = seg->ss_rec.jsr_seq; 2059113db2ddSJeff Roberson for (;;) { 2060113db2ddSJeff Roberson seg = TAILQ_LAST(&allsegs, seghd); 2061113db2ddSJeff Roberson if (seg->ss_rec.jsr_seq >= newseq) 2062113db2ddSJeff Roberson break; 2063113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2064113db2ddSJeff Roberson TAILQ_INSERT_HEAD(&allsegs, seg, ss_next); 2065113db2ddSJeff Roberson newseq = seg->ss_rec.jsr_seq; 2066113db2ddSJeff Roberson 2067113db2ddSJeff Roberson } 2068edad6026SXin LI if (newseq != oldseq) { 20692db62a6bSJeff Roberson TAILQ_FOREACH(seg, &allsegs, ss_next) { 20702db62a6bSJeff Roberson printf("%jd, ", seg->ss_rec.jsr_seq); 20712db62a6bSJeff Roberson } 20722db62a6bSJeff Roberson printf("\n"); 2073edad6026SXin LI err_suj("Journal file sequence mismatch %jd != %jd\n", 2074113db2ddSJeff Roberson newseq, oldseq); 2075edad6026SXin LI } 2076113db2ddSJeff Roberson /* 2077113db2ddSJeff Roberson * The kernel may asynchronously write segments which can create 2078113db2ddSJeff Roberson * gaps in the sequence space. Throw away any segments after the 2079113db2ddSJeff Roberson * gap as the kernel guarantees only those that are contiguously 2080113db2ddSJeff Roberson * reachable are marked as completed. 2081113db2ddSJeff Roberson */ 2082113db2ddSJeff Roberson discard = 0; 2083113db2ddSJeff Roberson TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2084113db2ddSJeff Roberson if (!discard && newseq++ == seg->ss_rec.jsr_seq) { 2085113db2ddSJeff Roberson jrecs += seg->ss_rec.jsr_cnt; 20860947d19aSKonstantin Belousov jbytes += seg->ss_rec.jsr_blocks * real_dev_bsize; 2087113db2ddSJeff Roberson continue; 2088113db2ddSJeff Roberson } 2089113db2ddSJeff Roberson discard = 1; 2090113db2ddSJeff Roberson if (debug) 2091113db2ddSJeff Roberson printf("Journal order mismatch %jd != %jd pruning\n", 2092113db2ddSJeff Roberson newseq-1, seg->ss_rec.jsr_seq); 2093113db2ddSJeff Roberson TAILQ_REMOVE(&allsegs, seg, ss_next); 2094113db2ddSJeff Roberson free(seg->ss_blk); 2095113db2ddSJeff Roberson free(seg); 2096113db2ddSJeff Roberson } 2097113db2ddSJeff Roberson if (debug) 2098113db2ddSJeff Roberson printf("Processing journal segments from %jd to %jd\n", 2099113db2ddSJeff Roberson oldseq, newseq-1); 2100113db2ddSJeff Roberson } 2101113db2ddSJeff Roberson 2102113db2ddSJeff Roberson /* 2103113db2ddSJeff Roberson * Verify the journal inode before attempting to read records. 2104113db2ddSJeff Roberson */ 2105113db2ddSJeff Roberson static int 21065cc52631SKirk McKusick suj_verifyino(union dinode *dp) 2107113db2ddSJeff Roberson { 2108113db2ddSJeff Roberson 21095cc52631SKirk McKusick if (DIP(dp, di_nlink) != 1) { 2110623d7cb6SMatthew D Fleming printf("Invalid link count %d for journal inode %ju\n", 21115cc52631SKirk McKusick DIP(dp, di_nlink), (uintmax_t)sujino); 2112113db2ddSJeff Roberson return (-1); 2113113db2ddSJeff Roberson } 2114113db2ddSJeff Roberson 21155cc52631SKirk McKusick if ((DIP(dp, di_flags) & (SF_IMMUTABLE | SF_NOUNLINK)) != 2116113db2ddSJeff Roberson (SF_IMMUTABLE | SF_NOUNLINK)) { 2117623d7cb6SMatthew D Fleming printf("Invalid flags 0x%X for journal inode %ju\n", 21185cc52631SKirk McKusick DIP(dp, di_flags), (uintmax_t)sujino); 2119113db2ddSJeff Roberson return (-1); 2120113db2ddSJeff Roberson } 2121113db2ddSJeff Roberson 21225cc52631SKirk McKusick if (DIP(dp, di_mode) != (IFREG | IREAD)) { 2123623d7cb6SMatthew D Fleming printf("Invalid mode %o for journal inode %ju\n", 21245cc52631SKirk McKusick DIP(dp, di_mode), (uintmax_t)sujino); 2125113db2ddSJeff Roberson return (-1); 2126113db2ddSJeff Roberson } 2127113db2ddSJeff Roberson 21285cc52631SKirk McKusick if (DIP(dp, di_size) < SUJ_MIN) { 2129623d7cb6SMatthew D Fleming printf("Invalid size %jd for journal inode %ju\n", 21305cc52631SKirk McKusick DIP(dp, di_size), (uintmax_t)sujino); 2131113db2ddSJeff Roberson return (-1); 2132113db2ddSJeff Roberson } 2133113db2ddSJeff Roberson 21345cc52631SKirk McKusick if (DIP(dp, di_modrev) != fs->fs_mtime) { 2135113db2ddSJeff Roberson printf("Journal timestamp does not match fs mount time\n"); 2136113db2ddSJeff Roberson return (-1); 2137113db2ddSJeff Roberson } 2138113db2ddSJeff Roberson 2139113db2ddSJeff Roberson return (0); 2140113db2ddSJeff Roberson } 2141113db2ddSJeff Roberson 2142113db2ddSJeff Roberson struct jblocks { 2143113db2ddSJeff Roberson struct jextent *jb_extent; /* Extent array. */ 2144113db2ddSJeff Roberson int jb_avail; /* Available extents. */ 2145113db2ddSJeff Roberson int jb_used; /* Last used extent. */ 2146113db2ddSJeff Roberson int jb_head; /* Allocator head. */ 2147113db2ddSJeff Roberson int jb_off; /* Allocator extent offset. */ 2148113db2ddSJeff Roberson }; 2149113db2ddSJeff Roberson struct jextent { 2150113db2ddSJeff Roberson ufs2_daddr_t je_daddr; /* Disk block address. */ 2151113db2ddSJeff Roberson int je_blocks; /* Disk block count. */ 2152113db2ddSJeff Roberson }; 2153113db2ddSJeff Roberson 21547703a6ffSScott Long static struct jblocks *suj_jblocks; 2155113db2ddSJeff Roberson 2156113db2ddSJeff Roberson static struct jblocks * 2157113db2ddSJeff Roberson jblocks_create(void) 2158113db2ddSJeff Roberson { 2159113db2ddSJeff Roberson struct jblocks *jblocks; 2160113db2ddSJeff Roberson int size; 2161113db2ddSJeff Roberson 2162113db2ddSJeff Roberson jblocks = errmalloc(sizeof(*jblocks)); 2163113db2ddSJeff Roberson jblocks->jb_avail = 10; 2164113db2ddSJeff Roberson jblocks->jb_used = 0; 2165113db2ddSJeff Roberson jblocks->jb_head = 0; 2166113db2ddSJeff Roberson jblocks->jb_off = 0; 2167113db2ddSJeff Roberson size = sizeof(struct jextent) * jblocks->jb_avail; 2168113db2ddSJeff Roberson jblocks->jb_extent = errmalloc(size); 2169113db2ddSJeff Roberson bzero(jblocks->jb_extent, size); 2170113db2ddSJeff Roberson 2171113db2ddSJeff Roberson return (jblocks); 2172113db2ddSJeff Roberson } 2173113db2ddSJeff Roberson 2174113db2ddSJeff Roberson /* 2175113db2ddSJeff Roberson * Return the next available disk block and the amount of contiguous 2176113db2ddSJeff Roberson * free space it contains. 2177113db2ddSJeff Roberson */ 2178113db2ddSJeff Roberson static ufs2_daddr_t 2179113db2ddSJeff Roberson jblocks_next(struct jblocks *jblocks, int bytes, int *actual) 2180113db2ddSJeff Roberson { 2181113db2ddSJeff Roberson struct jextent *jext; 2182113db2ddSJeff Roberson ufs2_daddr_t daddr; 2183113db2ddSJeff Roberson int freecnt; 2184113db2ddSJeff Roberson int blocks; 2185113db2ddSJeff Roberson 21865cc52631SKirk McKusick blocks = btodb(bytes); 2187113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_head]; 2188113db2ddSJeff Roberson freecnt = jext->je_blocks - jblocks->jb_off; 2189113db2ddSJeff Roberson if (freecnt == 0) { 2190113db2ddSJeff Roberson jblocks->jb_off = 0; 2191113db2ddSJeff Roberson if (++jblocks->jb_head > jblocks->jb_used) 2192113db2ddSJeff Roberson return (0); 2193113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_head]; 2194113db2ddSJeff Roberson freecnt = jext->je_blocks; 2195113db2ddSJeff Roberson } 2196113db2ddSJeff Roberson if (freecnt > blocks) 2197113db2ddSJeff Roberson freecnt = blocks; 21985cc52631SKirk McKusick *actual = dbtob(freecnt); 2199113db2ddSJeff Roberson daddr = jext->je_daddr + jblocks->jb_off; 2200113db2ddSJeff Roberson 2201113db2ddSJeff Roberson return (daddr); 2202113db2ddSJeff Roberson } 2203113db2ddSJeff Roberson 2204113db2ddSJeff Roberson /* 2205113db2ddSJeff Roberson * Advance the allocation head by a specified number of bytes, consuming 2206113db2ddSJeff Roberson * one journal segment. 2207113db2ddSJeff Roberson */ 2208113db2ddSJeff Roberson static void 2209113db2ddSJeff Roberson jblocks_advance(struct jblocks *jblocks, int bytes) 2210113db2ddSJeff Roberson { 2211113db2ddSJeff Roberson 22125cc52631SKirk McKusick jblocks->jb_off += btodb(bytes); 2213113db2ddSJeff Roberson } 2214113db2ddSJeff Roberson 2215113db2ddSJeff Roberson static void 2216113db2ddSJeff Roberson jblocks_destroy(struct jblocks *jblocks) 2217113db2ddSJeff Roberson { 2218113db2ddSJeff Roberson 2219113db2ddSJeff Roberson free(jblocks->jb_extent); 2220113db2ddSJeff Roberson free(jblocks); 2221113db2ddSJeff Roberson } 2222113db2ddSJeff Roberson 2223113db2ddSJeff Roberson static void 2224113db2ddSJeff Roberson jblocks_add(struct jblocks *jblocks, ufs2_daddr_t daddr, int blocks) 2225113db2ddSJeff Roberson { 2226113db2ddSJeff Roberson struct jextent *jext; 2227113db2ddSJeff Roberson int size; 2228113db2ddSJeff Roberson 2229113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_used]; 2230113db2ddSJeff Roberson /* Adding the first block. */ 2231113db2ddSJeff Roberson if (jext->je_daddr == 0) { 2232113db2ddSJeff Roberson jext->je_daddr = daddr; 2233113db2ddSJeff Roberson jext->je_blocks = blocks; 2234113db2ddSJeff Roberson return; 2235113db2ddSJeff Roberson } 2236113db2ddSJeff Roberson /* Extending the last extent. */ 2237113db2ddSJeff Roberson if (jext->je_daddr + jext->je_blocks == daddr) { 2238113db2ddSJeff Roberson jext->je_blocks += blocks; 2239113db2ddSJeff Roberson return; 2240113db2ddSJeff Roberson } 2241113db2ddSJeff Roberson /* Adding a new extent. */ 2242113db2ddSJeff Roberson if (++jblocks->jb_used == jblocks->jb_avail) { 2243113db2ddSJeff Roberson jblocks->jb_avail *= 2; 2244113db2ddSJeff Roberson size = sizeof(struct jextent) * jblocks->jb_avail; 2245113db2ddSJeff Roberson jext = errmalloc(size); 2246113db2ddSJeff Roberson bzero(jext, size); 2247113db2ddSJeff Roberson bcopy(jblocks->jb_extent, jext, 2248113db2ddSJeff Roberson sizeof(struct jextent) * jblocks->jb_used); 2249113db2ddSJeff Roberson free(jblocks->jb_extent); 2250113db2ddSJeff Roberson jblocks->jb_extent = jext; 2251113db2ddSJeff Roberson } 2252113db2ddSJeff Roberson jext = &jblocks->jb_extent[jblocks->jb_used]; 2253113db2ddSJeff Roberson jext->je_daddr = daddr; 2254113db2ddSJeff Roberson jext->je_blocks = blocks; 2255113db2ddSJeff Roberson 2256113db2ddSJeff Roberson return; 2257113db2ddSJeff Roberson } 2258113db2ddSJeff Roberson 2259113db2ddSJeff Roberson /* 2260113db2ddSJeff Roberson * Add a file block from the journal to the extent map. We can't read 2261113db2ddSJeff Roberson * each file block individually because the kernel treats it as a circular 2262113db2ddSJeff Roberson * buffer and segments may span mutliple contiguous blocks. 2263113db2ddSJeff Roberson */ 2264113db2ddSJeff Roberson static void 2265113db2ddSJeff Roberson suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) 2266113db2ddSJeff Roberson { 2267113db2ddSJeff Roberson 2268113db2ddSJeff Roberson jblocks_add(suj_jblocks, fsbtodb(fs, blk), fsbtodb(fs, frags)); 2269113db2ddSJeff Roberson } 2270113db2ddSJeff Roberson 2271113db2ddSJeff Roberson static void 2272113db2ddSJeff Roberson suj_read(void) 2273113db2ddSJeff Roberson { 2274113db2ddSJeff Roberson uint8_t block[1 * 1024 * 1024]; 2275113db2ddSJeff Roberson struct suj_seg *seg; 2276113db2ddSJeff Roberson struct jsegrec *recn; 2277113db2ddSJeff Roberson struct jsegrec *rec; 2278113db2ddSJeff Roberson ufs2_daddr_t blk; 2279113db2ddSJeff Roberson int readsize; 22804235bafaSPedro F. Giffuni int blocks; 2281113db2ddSJeff Roberson int recsize; 2282113db2ddSJeff Roberson int size; 228311ec5dd0SPedro F. Giffuni int i; 2284113db2ddSJeff Roberson 2285113db2ddSJeff Roberson /* 2286113db2ddSJeff Roberson * Read records until we exhaust the journal space. If we find 2287113db2ddSJeff Roberson * an invalid record we start searching for a valid segment header 2288113db2ddSJeff Roberson * at the next block. This is because we don't have a head/tail 2289113db2ddSJeff Roberson * pointer and must recover the information indirectly. At the gap 2290113db2ddSJeff Roberson * between the head and tail we won't necessarily have a valid 2291113db2ddSJeff Roberson * segment. 2292113db2ddSJeff Roberson */ 2293113db2ddSJeff Roberson restart: 2294113db2ddSJeff Roberson for (;;) { 2295113db2ddSJeff Roberson size = sizeof(block); 2296113db2ddSJeff Roberson blk = jblocks_next(suj_jblocks, size, &readsize); 2297113db2ddSJeff Roberson if (blk == 0) 2298113db2ddSJeff Roberson return; 2299113db2ddSJeff Roberson size = readsize; 2300113db2ddSJeff Roberson /* 2301113db2ddSJeff Roberson * Read 1MB at a time and scan for records within this block. 2302113db2ddSJeff Roberson */ 23035cc52631SKirk McKusick if (pread(fsreadfd, &block, size, dbtob(blk)) != size) { 2304edad6026SXin LI err_suj("Error reading journal block %jd\n", 2305113db2ddSJeff Roberson (intmax_t)blk); 2306edad6026SXin LI } 2307113db2ddSJeff Roberson for (rec = (void *)block; size; size -= recsize, 2308113db2ddSJeff Roberson rec = (struct jsegrec *)((uintptr_t)rec + recsize)) { 23090947d19aSKonstantin Belousov recsize = real_dev_bsize; 2310113db2ddSJeff Roberson if (rec->jsr_time != fs->fs_mtime) { 23115cc52631SKirk McKusick #ifdef notdef 2312113db2ddSJeff Roberson if (debug) 2313113db2ddSJeff Roberson printf("Rec time %jd != fs mtime %jd\n", 2314113db2ddSJeff Roberson rec->jsr_time, fs->fs_mtime); 23155cc52631SKirk McKusick #endif 2316113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2317113db2ddSJeff Roberson continue; 2318113db2ddSJeff Roberson } 2319113db2ddSJeff Roberson if (rec->jsr_cnt == 0) { 2320113db2ddSJeff Roberson if (debug) 2321113db2ddSJeff Roberson printf("Found illegal count %d\n", 2322113db2ddSJeff Roberson rec->jsr_cnt); 2323113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2324113db2ddSJeff Roberson continue; 2325113db2ddSJeff Roberson } 2326113db2ddSJeff Roberson blocks = rec->jsr_blocks; 23270947d19aSKonstantin Belousov recsize = blocks * real_dev_bsize; 2328113db2ddSJeff Roberson if (recsize > size) { 2329113db2ddSJeff Roberson /* 2330113db2ddSJeff Roberson * We may just have run out of buffer, restart 2331113db2ddSJeff Roberson * the loop to re-read from this spot. 2332113db2ddSJeff Roberson */ 2333113db2ddSJeff Roberson if (size < fs->fs_bsize && 2334113db2ddSJeff Roberson size != readsize && 2335113db2ddSJeff Roberson recsize <= fs->fs_bsize) 2336113db2ddSJeff Roberson goto restart; 2337113db2ddSJeff Roberson if (debug) 2338113db2ddSJeff Roberson printf("Found invalid segsize %d > %d\n", 2339113db2ddSJeff Roberson recsize, size); 23400947d19aSKonstantin Belousov recsize = real_dev_bsize; 2341113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2342113db2ddSJeff Roberson continue; 2343113db2ddSJeff Roberson } 2344113db2ddSJeff Roberson /* 2345113db2ddSJeff Roberson * Verify that all blocks in the segment are present. 2346113db2ddSJeff Roberson */ 2347113db2ddSJeff Roberson for (i = 1; i < blocks; i++) { 23480947d19aSKonstantin Belousov recn = (void *)((uintptr_t)rec) + i * 23490947d19aSKonstantin Belousov real_dev_bsize; 2350113db2ddSJeff Roberson if (recn->jsr_seq == rec->jsr_seq && 2351113db2ddSJeff Roberson recn->jsr_time == rec->jsr_time) 2352113db2ddSJeff Roberson continue; 2353113db2ddSJeff Roberson if (debug) 2354113db2ddSJeff Roberson printf("Incomplete record %jd (%d)\n", 2355113db2ddSJeff Roberson rec->jsr_seq, i); 23560947d19aSKonstantin Belousov recsize = i * real_dev_bsize; 2357113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2358113db2ddSJeff Roberson goto restart; 2359113db2ddSJeff Roberson } 2360113db2ddSJeff Roberson seg = errmalloc(sizeof(*seg)); 2361113db2ddSJeff Roberson seg->ss_blk = errmalloc(recsize); 2362113db2ddSJeff Roberson seg->ss_rec = *rec; 2363113db2ddSJeff Roberson bcopy((void *)rec, seg->ss_blk, recsize); 2364113db2ddSJeff Roberson if (rec->jsr_oldest > oldseq) 2365113db2ddSJeff Roberson oldseq = rec->jsr_oldest; 2366113db2ddSJeff Roberson TAILQ_INSERT_TAIL(&allsegs, seg, ss_next); 2367113db2ddSJeff Roberson jblocks_advance(suj_jblocks, recsize); 2368113db2ddSJeff Roberson } 2369113db2ddSJeff Roberson } 2370113db2ddSJeff Roberson } 2371113db2ddSJeff Roberson 2372113db2ddSJeff Roberson /* 2373113db2ddSJeff Roberson * Orchestrate the verification of a filesystem via the softupdates journal. 2374113db2ddSJeff Roberson */ 2375113db2ddSJeff Roberson int 2376113db2ddSJeff Roberson suj_check(const char *filesys) 2377113db2ddSJeff Roberson { 23785cc52631SKirk McKusick struct inodesc idesc; 23795cc52631SKirk McKusick struct csum *cgsum; 2380*6a71277cSKirk McKusick union dinode *dp, *jip; 23815cc52631SKirk McKusick struct inode ip; 2382113db2ddSJeff Roberson uint64_t blocks; 23835cc52631SKirk McKusick int i, retval; 2384edad6026SXin LI struct suj_seg *seg; 2385edad6026SXin LI struct suj_seg *segn; 2386113db2ddSJeff Roberson 23877703a6ffSScott Long initsuj(); 2388a6bbdf81SKirk McKusick fs = &sblock; 23895cc52631SKirk McKusick if (real_dev_bsize == 0 && ioctl(fsreadfd, DIOCGSECTORSIZE, 2390a6bbdf81SKirk McKusick &real_dev_bsize) == -1) 2391a6bbdf81SKirk McKusick real_dev_bsize = secsize; 2392a6bbdf81SKirk McKusick if (debug) 2393a6bbdf81SKirk McKusick printf("dev_bsize %u\n", real_dev_bsize); 2394edad6026SXin LI 2395edad6026SXin LI /* 2396edad6026SXin LI * Set an exit point when SUJ check failed 2397edad6026SXin LI */ 2398edad6026SXin LI retval = setjmp(jmpbuf); 2399edad6026SXin LI if (retval != 0) { 2400edad6026SXin LI pwarn("UNEXPECTED SU+J INCONSISTENCY\n"); 2401edad6026SXin LI TAILQ_FOREACH_SAFE(seg, &allsegs, ss_next, segn) { 2402edad6026SXin LI TAILQ_REMOVE(&allsegs, seg, ss_next); 2403edad6026SXin LI free(seg->ss_blk); 2404edad6026SXin LI free(seg); 2405edad6026SXin LI } 2406edad6026SXin LI if (reply("FALLBACK TO FULL FSCK") == 0) { 2407edad6026SXin LI ckfini(0); 2408edad6026SXin LI exit(EEXIT); 2409edad6026SXin LI } else 2410edad6026SXin LI return (-1); 2411edad6026SXin LI } 2412edad6026SXin LI 2413113db2ddSJeff Roberson /* 24145cc52631SKirk McKusick * Search the root directory for the SUJ_FILE. 2415113db2ddSJeff Roberson */ 24165cc52631SKirk McKusick idesc.id_type = DATA; 24175cc52631SKirk McKusick idesc.id_fix = IGNORE; 24185cc52631SKirk McKusick idesc.id_number = UFS_ROOTINO; 24195cc52631SKirk McKusick idesc.id_func = findino; 24205cc52631SKirk McKusick idesc.id_name = SUJ_FILE; 24215cc52631SKirk McKusick ginode(UFS_ROOTINO, &ip); 2422*6a71277cSKirk McKusick dp = ip.i_dp; 2423*6a71277cSKirk McKusick if ((DIP(dp, di_mode) & IFMT) != IFDIR) { 2424*6a71277cSKirk McKusick irelse(&ip); 2425*6a71277cSKirk McKusick err_suj("root inode is not a directory\n"); 2426*6a71277cSKirk McKusick } 2427*6a71277cSKirk McKusick if (DIP(dp, di_size) < 0 || DIP(dp, di_size) > MAXDIRSIZE) { 2428*6a71277cSKirk McKusick irelse(&ip); 2429*6a71277cSKirk McKusick err_suj("negative or oversized root directory %jd\n", 2430*6a71277cSKirk McKusick (uintmax_t)DIP(dp, di_size)); 2431*6a71277cSKirk McKusick } 2432*6a71277cSKirk McKusick if ((ckinode(dp, &idesc) & FOUND) == FOUND) { 24335cc52631SKirk McKusick sujino = idesc.id_parent; 24345cc52631SKirk McKusick irelse(&ip); 24355cc52631SKirk McKusick } else { 2436edad6026SXin LI printf("Journal inode removed. Use tunefs to re-create.\n"); 2437edad6026SXin LI sblock.fs_flags &= ~FS_SUJ; 2438edad6026SXin LI sblock.fs_sujfree = 0; 24395cc52631SKirk McKusick irelse(&ip); 2440edad6026SXin LI return (-1); 2441edad6026SXin LI } 2442113db2ddSJeff Roberson /* 2443113db2ddSJeff Roberson * Fetch the journal inode and verify it. 2444113db2ddSJeff Roberson */ 24455cc52631SKirk McKusick ginode(sujino, &ip); 24465cc52631SKirk McKusick jip = ip.i_dp; 2447113db2ddSJeff Roberson printf("** SU+J Recovering %s\n", filesys); 24485cc52631SKirk McKusick if (suj_verifyino(jip) != 0 || (!preen && !reply("USE JOURNAL"))) { 24495cc52631SKirk McKusick irelse(&ip); 2450113db2ddSJeff Roberson return (-1); 24515cc52631SKirk McKusick } 2452113db2ddSJeff Roberson /* 2453113db2ddSJeff Roberson * Build a list of journal blocks in jblocks before parsing the 2454113db2ddSJeff Roberson * available journal blocks in with suj_read(). 2455113db2ddSJeff Roberson */ 2456623d7cb6SMatthew D Fleming printf("** Reading %jd byte journal from inode %ju.\n", 2457623d7cb6SMatthew D Fleming DIP(jip, di_size), (uintmax_t)sujino); 2458113db2ddSJeff Roberson suj_jblocks = jblocks_create(); 2459113db2ddSJeff Roberson blocks = ino_visit(jip, sujino, suj_add_block, 0); 2460edad6026SXin LI if (blocks != numfrags(fs, DIP(jip, di_size))) { 2461623d7cb6SMatthew D Fleming printf("Sparse journal inode %ju.\n", (uintmax_t)sujino); 24625cc52631SKirk McKusick irelse(&ip); 2463edad6026SXin LI return (-1); 2464edad6026SXin LI } 24655cc52631SKirk McKusick irelse(&ip); 2466113db2ddSJeff Roberson suj_read(); 2467113db2ddSJeff Roberson jblocks_destroy(suj_jblocks); 2468113db2ddSJeff Roberson suj_jblocks = NULL; 2469113db2ddSJeff Roberson if (preen || reply("RECOVER")) { 2470113db2ddSJeff Roberson printf("** Building recovery table.\n"); 2471113db2ddSJeff Roberson suj_prune(); 2472113db2ddSJeff Roberson suj_build(); 2473113db2ddSJeff Roberson cg_apply(cg_build); 2474113db2ddSJeff Roberson printf("** Resolving unreferenced inode list.\n"); 2475113db2ddSJeff Roberson ino_unlinked(); 2476113db2ddSJeff Roberson printf("** Processing journal entries.\n"); 2477113db2ddSJeff Roberson cg_apply(cg_trunc); 2478113db2ddSJeff Roberson cg_apply(cg_check_blk); 2479364e7245SKonstantin Belousov cg_apply(cg_adj_blk); 2480113db2ddSJeff Roberson cg_apply(cg_check_ino); 2481113db2ddSJeff Roberson } 2482edad6026SXin LI if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0) 2483113db2ddSJeff Roberson return (0); 2484113db2ddSJeff Roberson /* 2485460ed610SKirk McKusick * Check block counts of snapshot inodes and 2486460ed610SKirk McKusick * make copies of any needed snapshot blocks. 2487460ed610SKirk McKusick */ 2488460ed610SKirk McKusick for (i = 0; i < snapcnt; i++) 2489460ed610SKirk McKusick check_blkcnt(&snaplist[i]); 2490460ed610SKirk McKusick snapflush(suj_checkblkavail); 2491460ed610SKirk McKusick /* 24925cc52631SKirk McKusick * Recompute the fs summary info from correct cs summaries. 2493113db2ddSJeff Roberson */ 24945cc52631SKirk McKusick bzero(&fs->fs_cstotal, sizeof(struct csum_total)); 24955cc52631SKirk McKusick for (i = 0; i < fs->fs_ncg; i++) { 24965cc52631SKirk McKusick cgsum = &fs->fs_cs(fs, i); 24975cc52631SKirk McKusick fs->fs_cstotal.cs_nffree += cgsum->cs_nffree; 24985cc52631SKirk McKusick fs->fs_cstotal.cs_nbfree += cgsum->cs_nbfree; 24995cc52631SKirk McKusick fs->fs_cstotal.cs_nifree += cgsum->cs_nifree; 25005cc52631SKirk McKusick fs->fs_cstotal.cs_ndir += cgsum->cs_ndir; 25015cc52631SKirk McKusick } 25025cc52631SKirk McKusick fs->fs_pendinginodes = 0; 25035cc52631SKirk McKusick fs->fs_pendingblocks = 0; 25045cc52631SKirk McKusick fs->fs_clean = 1; 25055cc52631SKirk McKusick fs->fs_time = time(NULL); 25065cc52631SKirk McKusick fs->fs_mtime = time(NULL); 25075cc52631SKirk McKusick sbdirty(); 25085cc52631SKirk McKusick ckfini(1); 2509edad6026SXin LI if (jrecs > 0 || jbytes > 0) { 2510113db2ddSJeff Roberson printf("** %jd journal records in %jd bytes for %.2f%% utilization\n", 2511113db2ddSJeff Roberson jrecs, jbytes, ((float)jrecs / (float)(jbytes / JREC_SIZE)) * 100); 2512113db2ddSJeff Roberson printf("** Freed %jd inodes (%jd dirs) %jd blocks, and %jd frags.\n", 2513113db2ddSJeff Roberson freeinos, freedir, freeblocks, freefrags); 2514edad6026SXin LI } 2515113db2ddSJeff Roberson 2516113db2ddSJeff Roberson return (0); 2517113db2ddSJeff Roberson } 25187703a6ffSScott Long 25197703a6ffSScott Long static void 25207703a6ffSScott Long initsuj(void) 25217703a6ffSScott Long { 25227703a6ffSScott Long int i; 25237703a6ffSScott Long 25245cc52631SKirk McKusick for (i = 0; i < HASHSIZE; i++) 25257703a6ffSScott Long LIST_INIT(&cghash[i]); 25267703a6ffSScott Long lastcg = NULL; 25277703a6ffSScott Long TAILQ_INIT(&allsegs); 25287703a6ffSScott Long oldseq = 0; 25297703a6ffSScott Long fs = NULL; 25307703a6ffSScott Long sujino = 0; 25317703a6ffSScott Long freefrags = 0; 25327703a6ffSScott Long freeblocks = 0; 25337703a6ffSScott Long freeinos = 0; 25347703a6ffSScott Long freedir = 0; 25357703a6ffSScott Long jbytes = 0; 25367703a6ffSScott Long jrecs = 0; 25377703a6ffSScott Long suj_jblocks = NULL; 25387703a6ffSScott Long } 2539